Exemplo n.º 1
0
        /// <summary>
        /// Creates a new <see cref="SolutionBuilder"/> from an MSBuild traversal project.
        /// </summary>
        /// <param name="projectPath">
        /// The path to the traversal project from which to populate the returned <see cref="SolutionBuilder"/>.
        /// </param>
        /// <param name="solutionOutputPath">
        /// The path to the solution that the returned <see cref="SolutionBuilder"/> should
        /// represent. This path is used to compute the relative path to projects.
        /// </param>
        /// <param name="addTransitiveProjectReferences">
        /// Whether or not to add transitive `&lt;ProjectReference&gt;` projects to the solution.
        /// </param>
        /// <param name="log">
        /// An optional logger.
        /// </param>
        public static SolutionBuilder FromTraversalProject(
            string projectPath,
            string solutionOutputPath           = null,
            bool addTransitiveProjectReferences = true,
            TaskLoggingHelper log = null)
        {
            if (projectPath == null)
            {
                throw new ArgumentNullException(nameof(projectPath));
            }

            if (!File.Exists(projectPath))
            {
                throw new FileNotFoundException($"project does not exist: {projectPath}");
            }

            projectPath = ResolveFullPath(projectPath);

            if (string.IsNullOrEmpty(solutionOutputPath))
            {
                solutionOutputPath = Path.ChangeExtension(projectPath, ".sln");
            }

            log?.LogMessage(MessageImportance.High, "Generating solution from traversal project");
            log?.LogMessage(MessageImportance.Normal, $"  projectPath: {projectPath}");
            log?.LogMessage(MessageImportance.Normal, $"  solutionOutputPath: {solutionOutputPath}");
            log?.LogMessage(MessageImportance.Normal, $"  addTransitiveProjectReferences: {addTransitiveProjectReferences}");

            var solution         = new SolutionBuilder(solutionOutputPath, log);
            var traversalProject = new ProjectCollection().LoadProject(projectPath);

            // Add the explicit solution configurations
            foreach (var item in traversalProject.GetItems("SolutionConfiguration"))
            {
                solution.AddSolutionConfiguration(ConfigurationPlatform.Parse(item.EvaluatedInclude));
            }

            // For each solution configuration, re-evaluate the whole project collection
            // with the mapped solution -> project configurations. This can result in
            // different project references (e.g. cross platform projects that might build
            // on windows and not mac, etc.).
            foreach (var item in traversalProject.GetItems("SolutionConfiguration"))
            {
                var solutionConfigurationPlatform = ConfigurationPlatform.Parse(item.EvaluatedInclude);
                var projectConfigurationPlatform  = new ConfigurationPlatform(
                    item.GetMetadataValue("Configuration") ?? solutionConfigurationPlatform.Configuration,
                    item.GetMetadataValue("Platform") ?? solutionConfigurationPlatform.Platform);

                var globalProperties = new List <(string, string)> {
                    ("IsGeneratingSolution", "true"),
                    ("Configuration", projectConfigurationPlatform.Configuration),
                    ("Platform", projectConfigurationPlatform.Platform)
                };
                globalProperties.AddRange(item.Metadata.Select(m => (m.Name, m.EvaluatedValue)));

                var graph = DependencyGraph
                            .Create(projectPath, globalProperties)
                            .LoadGraph();

                // Add each project. Projects may fail to load in MSBuild, for example, if an SDK
                // is not available via the running MSBuild toolchain. If this is the case, we
                // still need to handle adding the project to the solution, we just can't infer
                // anything therein.
                foreach (var node in graph.TopologicallySortedProjects)
                {
                    // Don't add the root traversal project to the solution
                    if (node.Parents.Count == 0)
                    {
                        continue;
                    }

                    // Prefer an explicit project GUID if one exists (old style projects)
                    Guid projectGuid         = default;
                    var  explicitProjectGuid = node.Project == null
                        ? XDocument
                                               .Load(node.ProjectPath)
                                               .Root
                                               .Elements()
                                               .FirstOrDefault(e => !e.HasAttributes && string.Equals(
                                                                   e.Name.LocalName,
                                                                   "PropertyGroup",
                                                                   StringComparison.OrdinalIgnoreCase))
                                               ?.Elements()
                                               .FirstOrDefault(e => string.Equals(
                                                                   e.Name.LocalName,
                                                                   "ProjectGuid",
                                                                   StringComparison.OrdinalIgnoreCase))
                                               ?.Value
                        : node.Project.GetPropertyValue("ProjectGuid");

                    if (!string.IsNullOrEmpty(explicitProjectGuid))
                    {
                        Guid.TryParse(explicitProjectGuid, out projectGuid);
                    }

                    string solutionFolder    = null;
                    var    includeInSolution = true;

                    foreach (var projectReference in node.ProjectReferenceItems)
                    {
                        if (string.Equals(
                                projectReference.GetMetadataValue("IncludeInSolution"),
                                "false",
                                StringComparison.OrdinalIgnoreCase))
                        {
                            includeInSolution = false;
                            break;
                        }

                        projectConfigurationPlatform = projectConfigurationPlatform
                                                       .WithConfiguration(projectReference.GetMetadataValue("Configuration"))
                                                       .WithPlatform(projectReference.GetMetadataValue("Platform"));

                        var _solutionFolder = projectReference.GetMetadataValue("SolutionFolder");
                        if (!string.IsNullOrEmpty(_solutionFolder))
                        {
                            solutionFolder = _solutionFolder;
                        }
                    }

                    if (!includeInSolution)
                    {
                        continue;
                    }

                    var solutionNode = solution.AddProject(
                        node.ProjectPath,
                        solutionFolder,
                        projectGuid);

                    solutionNode.AddConfigurationMap(new SolutionConfigurationPlatformMap(
                                                         solutionConfigurationPlatform,
                                                         projectConfigurationPlatform));
                }
            }

            return(solution);
        }