/// <summary>
        /// Builds a graph of dependencies between solution files, from a list of .csproj and .sln files.
        /// </summary>
        /// <param name="inputFiles">Project (.csproj) and solution (.sln) files to start the dependency search from</param>
        /// <param name="_excludedSLNs">Solution (.sln) files that should be excluded from the final dependency graph - useful for temporarily ignoring cyclic dependencies.
        /// Note that .sln files may appear in the final graph even if they are not given in the input files list, if something in the input depends on them.</param>
        /// <param name="maxRecursionLevel">How deep to resolve dependencies of the given inputs. 0 means no dependency resolution is performed. -1 means infinity.</param>
        public static BuildDependencyInfo GetDependencyInfo(IProjectFinder projectFinder, IEnumerable <string> inputFiles, IEnumerable <string> _excludedSLNs, int maxRecursionLevel) //, bool findAllDependents)
        {
            string[] projectFiles;
            string[] slnFiles;
            ProcessInputFiles(inputFiles.Select(CanonicalPath), out projectFiles, out slnFiles);

            var excludedSLNs = _excludedSLNs.Select(CanonicalPath)
                               .Select(x => x.ToLowerInvariant())
                               .ToArray();

            if (excludedSLNs.Any(x => false == SLN_EXTENSION.Equals(System.IO.Path.GetExtension(x))))
            {
                var errorMessage = "excluded files must have extension: " + SLN_EXTENSION;
                _logger.Error(errorMessage);
                throw new ArgumentException(errorMessage, "_excludedSLNs");
            }

            var csprojProjects = projectFiles.Select(Project.FromCSProj);
            var slnProjects    = slnFiles.SelectMany(projectFinder.GetProjectsOfSLN);
            var inputProjects  = csprojProjects.Union(slnProjects).ToArray();

            PrintInputInfo(excludedSLNs, projectFiles, slnFiles, inputProjects);

            //Project[] projectsForDependencyGraph = findAllDependents
            //                                     ? projectFinder.AllProjectsInPath().ToArray()
            //                                     : inputProjects;

            return(new BuildDependencyInfo(ProjectDependencyGraph(projectFinder, inputProjects, false, maxRecursionLevel),
                                           SolutionDependencyGraph(projectFinder, inputProjects, false, maxRecursionLevel),
                                           excludedSLNs));
        }
Example #2
0
 protected static string ProjectsUsingAssemblyReference(IProjectFinder projectFinder, AssemblyReference assemblyReference)
 {
     return(StringExtensions.Tabify(projectFinder.AllProjectsInPath()
                                    .Where(x => x.AssemblyReferences
                                           .Contains(assemblyReference))
                                    .Select(x => x.ToString())));
 }
Example #3
0
        protected static void AddIndirectReferences(IProjectFinder projectFinder, Regex[] assemblyNamePatterns, bool ignoreOnlyMatching,
                                                    HashSet <string> originalAssemblyReferenceNames, Queue <AssemblyReference> remainingReferences, string targetPath,
                                                    Project buildingProject, FileInfo[] projectOutputs, string explicitTargetPath, AssemblyReference assemblyReference,
                                                    List <IndirectReferenceInfo> indirectReferencesOutsideSolution)
        {
            var indirectReferences = GetIndirectReferences(originalAssemblyReferenceNames, targetPath, projectOutputs, explicitTargetPath)
                                     .Where(x => IncludeAssemblyWhenCopyingDeps(x, assemblyNamePatterns, ignoreOnlyMatching))
                                     .ToArray();

            if (false == indirectReferences.Any())
            {
                return;
            }
            var buildingSolution = projectFinder.GetSLNFileForProject(buildingProject);

            // TODO: Print the name of the project that is missing these indirect references instead of letting the user guess.
            _logger.InfoFormat("Adding indirect references listed below. The direct reference is to {0}.\n{1}\nThis probably means some project is referencing {0} but does not reference all of the listed indirect assemblies above.\nHere's a list of projects referencing {0}:\n{2}",
                               assemblyReference.Name, StringExtensions.Tabify(indirectReferences.Select(x => x.Name)),
                               StringExtensions.Tabify(ProjectsUsingAssemblyReference(projectFinder, assemblyReference)));
            foreach (var indirectReference in indirectReferences)
            {
                var indirectReferenceBuildingProject = projectFinder.FindProjectForAssemblyReference(indirectReference).SingleOrDefault();
                var indirectReferenceInfo            = new IndirectReferenceInfo(assemblyReference, buildingProject, indirectReference, indirectReferenceBuildingProject);
                if (null != indirectReferenceBuildingProject)
                {
                    if (projectFinder.GetSLNFileForProject(indirectReferenceBuildingProject).FullName.Equals(buildingSolution.FullName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        originalAssemblyReferenceNames.Add(ComparableAssemblyName(indirectReference));
                        remainingReferences.Enqueue(indirectReference);
                        continue;
                    }
                }
                indirectReferencesOutsideSolution.Add(indirectReferenceInfo);
            }
        }
Example #4
0
        protected static void WarnAboutRemainingIndirectReferences(IProjectFinder projectFinder, HashSet <string> originalAssemblyReferenceNames,
                                                                   List <IndirectReferenceInfo> indirectReferencesOutsideSolution)
        {
            foreach (var indirectReferenceInfo in
                     indirectReferencesOutsideSolution
                     .Where(x => false == originalAssemblyReferenceNames.Contains(ComparableAssemblyName(x.IndirectReference))))
            {
                Action <string> logAction = _logger.Warn;

                // Some projects are KNOWN to be executable, which means they will absolutely fail when we try to run them because if this indirect reference that is missing.
                // Other projects may also be "executable", such as Web Applications, so we still emit a warning.
                if (KnownExecutableOutputTypes.Contains(indirectReferenceInfo.DirectReferenceProject.OutputType))
                {
                    logAction = _logger.Error;
                }


                logAction(String.Format(@"Skipped indirect reference from solution other than the direct reference that caused it:
    Indirect reference:             {0}
    Indirect reference built by:    {1}
    Required by project:            {5} - {2}
    Which builds reference:         {3}
    Which is used directly by projects:
    {4}
", // TODO: Add description of motivation for this: If the project directly requires an assembly from solution A, which happens to required something from solution B, the builder will not copy assemblies from B and mix them in the same "Components" directory of assemblies from A
                                        indirectReferenceInfo.IndirectReference,
                                        indirectReferenceInfo.IndirectReferenceProject,
                                        indirectReferenceInfo.DirectReferenceProject,
                                        indirectReferenceInfo.DirectReference,
                                        StringExtensions.Tabify(ProjectsUsingAssemblyReference(projectFinder, indirectReferenceInfo.DirectReference)),
                                        indirectReferenceInfo.DirectReferenceProject.OutputType));
            }
        }
Example #5
0
 protected static void ValidateSolutionReadyForBuild(IProjectFinder projectFinder, string solutionFileName, Regex[] ignoredDependencyAssemblies, bool ignoreOnlyMatching)
 {
     foreach (var project in projectFinder.GetProjectsOfSLN(solutionFileName))
     {
         project.ValidateHintPaths(ignoredDependencyAssemblies, ignoreOnlyMatching);
         project.ValidateAssemblyReferencesAreAvailable();
     }
 }
Example #6
0
        public static void UpdateComponentsFromBuiltProjects(IProjectFinder projectFinder, string solutionFileName, Regex[] assemblyNamePatterns, bool ignoreOnlyMatching, bool ignoreMissing)
        {
            _logger.DebugFormat("Updating components: {0} (copying dependencies required for '{1}')...", Path.GetFileName(solutionFileName), solutionFileName);
            var assemblyReferences = projectFinder.GetProjectsOfSLN(solutionFileName)
                                     .SelectMany(x => x.AssemblyReferences)
                                     .Distinct();

            Builder.CopyAssemblyReferencesFromBuiltProjects(projectFinder, assemblyNamePatterns, ignoreOnlyMatching, assemblyReferences, ignoreMissing);
            _logger.InfoFormat("Updated components required by: {0} ('{1}')", Path.GetFileName(solutionFileName), solutionFileName);
        }
Example #7
0
 protected static void RunAllUnitTests(IProjectFinder projectFinder, string solutionFileName, bool ignoreFailedTests)
 {
     foreach (var project in projectFinder.GetProjectsOfSLN(solutionFileName))
     {
         if (project.AssemblyReferences.Any(x => x.AssemblyNameFromFullName().StartsWith("Microsoft.VisualStudio.QualityTools.UnitTestFramework")))
         {
             _logger.DebugFormat("\tRunning Tests: {0}", project.Name);
             MSTest(project, ignoreFailedTests);
         }
     }
 }
Example #8
0
 /// <summary>
 /// <para>Builds the given solution. Steps:</para>
 /// <para>1. Update dependencies (components) by copying all referenced assemblies from their building projects' output paths to the HintPath</para>
 /// <para>2. Clean the solution</para>
 /// <para>3. Build the solution</para>
 /// <para>Use <paramref name="ignoredDependencyAssemblies"/> to ignore system and/or third party assemblies that are not part of the build tree (those that will not be found when the <paramref name="projectFinder"/> will search for a project that builds them)</para>
 /// </summary>
 /// <param name="projectFinder"></param>
 /// <param name="solutionFileName"></param>
 /// <param name="ignoredDependencyAssemblies">Patterns for dependent assemblies to ignore when trying to find a building project to copy from.</param>
 /// <param name="ignoreOnlyMatching">Flips the meaning of the ignored assemblies so that ALL assemblies will be ignored, EXCEPT the ones matching the given patterns</param>
 public static void BuildSolution(IProjectFinder projectFinder, string solutionFileName, Regex[] ignoredDependencyAssemblies, bool ignoreOnlyMatching, bool ignoreMissing, bool cleanBeforeBuild, bool runTests, bool ignoreFailedTests)
 {
     _logger.InfoFormat("Building Solution: '{0}'", solutionFileName);
     Builder.UpdateComponentsFromBuiltProjects(projectFinder, solutionFileName, ignoredDependencyAssemblies, ignoreOnlyMatching, ignoreMissing);
     ValidateSolutionReadyForBuild(projectFinder, solutionFileName, ignoredDependencyAssemblies, ignoreOnlyMatching);
     if (cleanBeforeBuild)
     {
         _logger.DebugFormat("\tCleaning...");
         MSBuild(solutionFileName, "/t:clean");
     }
     _logger.DebugFormat("\tBuilding...");
     MSBuild(solutionFileName);
     if (runTests)
     {
         _logger.DebugFormat("\tRunning tests is enabled - looking for tests to run...");
         RunAllUnitTests(projectFinder, solutionFileName, ignoreFailedTests);
     }
     _logger.InfoFormat("Done: {0} ('{1}')\n", Path.GetFileName(solutionFileName), solutionFileName);
 }
Example #9
0
 public GitPackEngine(Options options,
                      IGitCommandHelper gitCommandHelper,
                      IChangedFilePreparer projectPreparer,
                      IVisualStudioProjectCompiler projectCompiler,
                      IPathService pathServie,
                      IFilePackService filePackService,
                      IProjectFinder projectFinder,
                      IPackageCompressService compressService,
                      ILogger logger)
 {
     this.options             = options;
     this._gitCommandHelper   = gitCommandHelper;
     this.changedFilePreparer = projectPreparer;
     this.logger          = logger;
     this.pathService     = pathServie;
     this.projectCompiler = projectCompiler;
     this.projectFinder   = projectFinder;
     this.filePackService = filePackService;
     this.compressService = compressService;
 }
 protected static IEnumerable <Project> GetAllProjectsInSolutionsOfProject(IProjectFinder projectFinder, Project project)
 {
     return(projectFinder.GetProjectsOfSLN(projectFinder.GetSLNFileForProject(project)));
 }
        /// <summary>
        /// Builds a dependency graph between solutions. The vertices in the graph are the solution full file names.
        /// </summary>
        public static AdjacencyGraph <String, SEdge <String> > SolutionDependencyGraph(IProjectFinder projectFinder, IEnumerable <Project> projects, bool reverse, int maxRecursionLevel)
        {
            var graph = DeepDependencies(projectFinder, projects, true, maxRecursionLevel)
                        .Where(x => x.Key != x.Value)
                        .Select(x => ProjectEdgeToSLNEdge(projectFinder, x))
                        .Where(x => false == SolutionNamesEqual(x))
                        .Distinct()
                        .Select(x => new SEdge <String>(reverse ? x.Key : x.Value, reverse ? x.Value : x.Key))
                        .ToAdjacencyGraph <String, SEdge <String> >(false);

            graph.AddVertexRange(projects.Select(x => SLNVertexName(projectFinder, x)));
            return(graph);
        }
Example #12
0
        public static void CopyAssemblyReferencesFromBuiltProjects(
            IProjectFinder projectFinder, Regex[] assemblyNamePatterns, bool ignoreOnlyMatching, IEnumerable <AssemblyReference> assemblyReferences, bool ignoreMissing)
        {
            // TODO: Refactor this mess, and save for each indirect reference the dependency path that caused it to be included in a unified way

            var originalAssemblyReferenceNames = new HashSet <string>(assemblyReferences.Select(ComparableAssemblyName));

            var remainingReferences = new Queue <AssemblyReference>(assemblyReferences);
            var indirectReferencesOutsideSolution = new List <IndirectReferenceInfo>();

            var ignoredAssemblies     = new List <AssemblyReference>();
            var badHintPathAssemblies = new List <AssemblyReference>();
            var missingProjects       = new List <AssemblyReference>();
            var unbuiltProjects       = new List <Project>();

            while (remainingReferences.Any())
            {
                var assemblyReference = remainingReferences.Dequeue();
                if (false == IncludeAssemblyWhenCopyingDeps(assemblyReference, assemblyNamePatterns, ignoreOnlyMatching))
                {
                    _logger.DebugFormat("Not copying ignored assembly: '{0}'", assemblyReference.ToString());
                    ignoredAssemblies.Add(assemblyReference);
                    continue;
                }

                if (String.IsNullOrWhiteSpace(assemblyReference.HintPath))
                {
                    _logger.DebugFormat("Can't copy dependency (no target path): Missing HintPath for assembly reference: '{0}', used by projects:\n{1}",
                                        assemblyReference, ProjectsUsingAssemblyReference(projectFinder, assemblyReference));
                    badHintPathAssemblies.Add(assemblyReference);
                    continue;
                }
                var targetPath = System.IO.Path.GetDirectoryName(assemblyReference.HintPath);

                var buildingProject = projectFinder.FindProjectForAssemblyReference(assemblyReference).SingleOrDefault();
                if (null == buildingProject)
                {
                    _logger.DebugFormat("Can't find dependency (no building project): No project builds assembly reference: '{0}', used by projects:\n{1}",
                                        assemblyReference,
                                        ProjectsUsingAssemblyReference(projectFinder, assemblyReference));
                    missingProjects.Add(assemblyReference);
                    continue;
                }

                if (ignoreMissing && (false == System.IO.Directory.Exists(buildingProject.GetAbsoluteOutputPath())))
                {
                    _logger.DebugFormat("Ignoring (not copying) all components from not-built project: {0}", buildingProject.ToString());
                    unbuiltProjects.Add(buildingProject);
                    continue;
                }

                var projectOutputs = buildingProject.GetBuiltProjectOutputs().ToArray();
                _logger.InfoFormat("Copy: {0,-40} -> {1}", buildingProject.Name, targetPath);
                CopyFilesToDirectory(projectOutputs, targetPath, ignoreMissing);

                // Add sub-references - the indirectly referenced assemblies, the ones used by the current assemblyReference
                var explicitTargetPath = System.IO.Path.GetDirectoryName(assemblyReference.ExplicitHintPath);
                // TODO: Refactor
                AddIndirectReferences(projectFinder, assemblyNamePatterns, ignoreOnlyMatching, originalAssemblyReferenceNames,
                                      remainingReferences, targetPath, buildingProject, projectOutputs, explicitTargetPath, assemblyReference, indirectReferencesOutsideSolution);
            }

            WarnAboutRemainingIndirectReferences(projectFinder, originalAssemblyReferenceNames, indirectReferencesOutsideSolution);
            WarnAboutUncopiedAssemblies(assemblyNamePatterns, ignoreOnlyMatching, ignoredAssemblies, badHintPathAssemblies, missingProjects, unbuiltProjects);
        }
        /// <summary>
        /// Creates a graph representing all the dependencies within the given projects.
        /// The edges will be from dependent project to dependency, unless <paramref name="reverse"/> is True,
        /// in which case the edges will be from dependency to dependent (which is more useful for topological sorting -
        /// which in this way will return the projects in build order)
        /// </summary>
        /// <param name="projects"></param>
        /// <returns></returns>
        public static AdjacencyGraph <Project, SEdge <Project> > ProjectDependencyGraph(IProjectFinder projectFinder, IEnumerable <Project> projects, bool reverse, int maxRecursionLevel)
        {
            // First add all the dependencies (edges)
            var graph = DeepDependencies(projectFinder, projects, false, maxRecursionLevel)
                        .Distinct()
                        .Select(x => new SEdge <Project>(reverse ? x.Key : x.Value, reverse ? x.Value : x.Key))
                        .ToAdjacencyGraph <Project, SEdge <Project> >(false);

            // Then make sure we have vertices for each input "root" project, regardless of if it has dependencies or not
            graph.AddVertexRange(projects);
            return(graph);
        }
 public static IEnumerable <Project> BuildOrder(IProjectFinder projectFinder, IEnumerable <Project> projects, int maxRecursionLevel)
 {
     return(ProjectDependencyGraph(projectFinder, projects, true, maxRecursionLevel).TopologicalSort());
 }
        /// <summary>
        /// Finds all pairs of dependencies source -> target of projects that depend on each other.
        /// </summary>
        /// <param name="maxRecursionLevel">How deep to resolve dependencies of the given inputs. 0 means no dependency resolution is performed. -1 means infinity.</param>
        /// <returns></returns>
        public static IEnumerable <KeyValuePair <Project, Project> > DeepDependencies(IProjectFinder projectFinder, IEnumerable <Project> projects, bool includeAllProjectsInSolution, int maxRecursionLevel)
        {
            var projectsToTraverse = new Queue <ResolvedProjectDependencyInfo>(projects.Select(x => new ResolvedProjectDependencyInfo(0, x, x)));

            var traversedProjects = new HashSet <Project>();

            while (projectsToTraverse.Any())
            {
                var projectPair = projectsToTraverse.Dequeue();
                var project     = projectPair.Target;

                if (projectPair.Source != projectPair.Target)
                {
                    yield return(new KeyValuePair <Project, Project>(projectPair.Source, projectPair.Target));
                }

                if ((0 <= maxRecursionLevel) && (projectPair.RecursionLevel >= maxRecursionLevel))
                {
                    continue;
                }

                if (traversedProjects.Contains(project))
                {
                    continue;
                }
                traversedProjects.Add(project);

                if (includeAllProjectsInSolution)
                {
                    foreach (var projectInSameSolution in GetAllProjectsInSolutionsOfProject(projectFinder, project)
                             .Where(x => false == traversedProjects.Contains(x)))
                    {
                        projectsToTraverse.Enqueue(new ResolvedProjectDependencyInfo(projectPair.RecursionLevel + 1, projectInSameSolution, projectInSameSolution));
                    }
                }
                foreach (var subProject in project.ProjectReferences)
                {
                    projectsToTraverse.Enqueue(new ResolvedProjectDependencyInfo(projectPair.RecursionLevel + 1, project, subProject));
                }
                // TODO: Why do we allow a null projectFinder at all here?
                if (null != projectFinder)
                {
                    foreach (var assemblySubProject in project.AssemblyReferences.SelectMany(projectFinder.FindProjectForAssemblyReference))
                    {
                        projectsToTraverse.Enqueue(new ResolvedProjectDependencyInfo(projectPair.RecursionLevel + 1, project, assemblySubProject));
                    }
                }
            }
        }
 private static string SLNVertexName(IProjectFinder projectFinder, Project project)
 {
     return(projectFinder.GetSLNFileForProject(project).FullName);
 }
 protected static KeyValuePair <string, string> ProjectEdgeToSLNEdge(IProjectFinder projectFinder, KeyValuePair <Project, Project> x)
 {
     return(new KeyValuePair <String, String>(SLNVertexName(projectFinder, x.Key),
                                              SLNVertexName(projectFinder, x.Value)));
 }