Example #1
0
        public override bool Execute()
        {
            var dependencyGraph = DependencyGraph
                                  .Create(MSBuildProjectFullPath)
                                  .LoadGraph();

            var projectPath  = ResolveFullPath(MSBuildProjectFullPath);
            var allItemSpecs = new HashSet <string> ();

            var compileItems          = new List <TaskItem> ();
            var projectReferenceItems = new List <TaskItem> ();
            var referenceItems        = new List <TaskItem> ();
            var packageReferenceItems = new List <TaskItem> ();
            var embeddedResourceItems = new List <TaskItem> ();

            var projectsToConsolidate = new List <ProjectDependencyNode> ();

            foreach (var project in dependencyGraph.TopologicallySortedProjects)
            {
                if (ResolveFullPath(project.ProjectPath) == projectPath)
                {
                    continue;
                }

                if (string.IsNullOrEmpty(ConsolidationConditionMetadataName) ||
                    project.ProjectReferenceItems.Any(
                        pr => bool.TryParse(
                            pr.GetMetadataValue(ConsolidationConditionMetadataName),
                            out var consolidate) && consolidate))
                {
                    projectsToConsolidate.Add(project);
                }
Example #2
0
        public async Task ProcessMirepoixSolution()
        {
            var solutionPath = Path.Combine(
                Git.FindRepositoryRootPathFromAssembly(),
                "mirepoix.sln");

            var dependencyGraph = await DependencyGraph
                                  .Create(solutionPath, ("Configuration", "Debug"))
                                  .LoadGraphAsync();

            Assert.All(dependencyGraph.TopologicallySortedProjects, node => {
                if (node.LoadException != null)
                {
                    throw node.LoadException;
                }
            });

            Assert.Collection(
                dependencyGraph
                .TopologicallySortedProjects
                .Select(p => Path.GetFileNameWithoutExtension(p.Project.FullPath)),
                p => Assert.Equal("Xamarin.NativeHelpers", p),
                p => Assert.Equal("Xamarin.Preferences", p),
                p => Assert.Equal("Xamarin.Preferences.Tests", p),
                p => Assert.Equal("Xamarin.Helpers", p),
                p => Assert.Equal("Xamarin.ProcessControl", p),
                p => Assert.Equal("Xamarin.XunitHelpers", p),
                p => Assert.Equal("Xamarin.ProcessControl.Tests", p),
                p => Assert.Equal("Xamarin.Downloader", p),
                p => Assert.Equal("Xamarin.Downloader.Tests", p),
                p => Assert.Equal("Xamarin.Security.Keychain", p),
                p => Assert.Equal("Xamarin.Security.Keychain.Tests", p),
                p => Assert.Equal("Xamarin.Helpers.Tests", p),
                p => Assert.Equal("Xamarin.NativeHelpers.Tests", p),
                p => Assert.Equal("Xamarin.MSBuild.Sdk", p),
                p => Assert.Equal("Xamarin.MSBuild.Sdk.Tests", p),
                p => Assert.Equal("Xamarin.Mac.Sdk", p),
                p => Assert.Equal("ILRepackPatcher", p),
                p => Assert.Equal("Xamarin.BuildConsolidator", p),
                p => Assert.Equal("Xamarin.BuildConsolidator.Tests", p),
                p => Assert.Equal("Xamarin.Cecil.Rocks", p),
                p => Assert.Equal("Xamarin.Cecil.Rocks.Tests", p),
                p => Assert.Equal("Xamarin.PropertyListDeserializer", p));
        }
Example #3
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);
        }
Example #4
0
        public override bool Execute()
        {
            var dependencyGraph = DependencyGraph
                                  .Create(MSBuildProjectFullPath)
                                  .LoadGraph();

            var projectPath  = ResolveFullPath(MSBuildProjectFullPath);
            var allItemSpecs = new HashSet <string> ();

            var compileItems          = new List <TaskItem> ();
            var projectReferenceItems = new List <TaskItem> ();
            var referenceItems        = new List <TaskItem> ();
            var embeddedResourceItems = new List <TaskItem> ();

            var projectsToConsolidate = dependencyGraph
                                        .TopologicallySortedProjects
                                        .Where(project => {
                if (ResolveFullPath(project.ProjectPath) == projectPath)
                {
                    return(false);
                }

                if (string.IsNullOrEmpty(ConsolidationConditionMetadataName))
                {
                    return(true);
                }

                return(project.ProjectReferenceItems.Any(
                           pr => bool.TryParse(
                               pr.GetMetadataValue(ConsolidationConditionMetadataName),
                               out var consolidate) && consolidate));
            })
                                        .ToList();

            foreach (var project in projectsToConsolidate)
            {
                var projectDirectory = Path.GetDirectoryName(project.ProjectPath);

                foreach (var item in project.Project.AllEvaluatedItems)
                {
                    if (ShouldExcludeItem(item))
                    {
                        continue;
                    }

                    List <TaskItem> collection;

                    var itemSpec            = item.EvaluatedInclude;
                    var useItemSpecFullPath = false;
                    var itemSpecFullPath    = ResolveFullPath(
                        projectDirectory,
                        itemSpec);

                    var itemMetadata = new Dictionary <string, string> (StringComparer.OrdinalIgnoreCase);

                    foreach (var metadata in item.Metadata)
                    {
                        itemMetadata.Add(metadata.Name, metadata.EvaluatedValue);
                    }

                    switch (item.ItemType.ToLowerInvariant())
                    {
                    case "compile":
                        collection          = compileItems;
                        useItemSpecFullPath = true;
                        break;

                    case "projectreference":
                        collection          = projectReferenceItems;
                        useItemSpecFullPath = true;

                        if (projectsToConsolidate.Any(
                                p => ResolveFullPath(p.ProjectPath) == itemSpecFullPath))
                        {
                            continue;
                        }

                        break;

                    case "reference":
                        collection = referenceItems;
                        break;

                    case "embeddedresource":
                        collection          = embeddedResourceItems;
                        useItemSpecFullPath = true;

                        foreach (var name in embeddedResourceMetadataPathNames)
                        {
                            if (itemMetadata.TryGetValue(name, out var value))
                            {
                                itemMetadata [name] = ResolveFullPath(
                                    projectDirectory,
                                    value);
                            }
                        }

                        itemMetadata.TryGetValue("LogicalResource", out var logicalResource);
                        itemMetadata ["LogicalResource"] = logicalResource = ComputeLogicalResourceName(
                            project.Project,
                            item,
                            logicalResource);

                        if (logicalResource.EndsWith(".resx", StringComparison.OrdinalIgnoreCase))
                        {
                            itemMetadata ["ManifestResourceName"] = Path.GetFileNameWithoutExtension(logicalResource);
                        }

                        break;

                    default:
                        continue;
                    }

                    if (useItemSpecFullPath)
                    {
                        itemSpec = itemSpecFullPath;
                    }

                    if (allItemSpecs.Contains(itemSpec))
                    {
                        continue;
                    }

                    allItemSpecs.Add(itemSpec);

                    collection.Add(new TaskItem(
                                       itemSpec,
                                       itemMetadata));
                }
            }

            CompileItems          = compileItems.ToArray();
            ProjectReferenceItems = projectReferenceItems.ToArray();
            ReferenceItems        = referenceItems.ToArray();
            EmbeddedResourceItems = embeddedResourceItems.ToArray();

            return(true);

            bool ShouldExcludeItem(ProjectItem item)
            => ConsolidateRemoveItemsRegex
            ?.Where(regexItem => string.Equals(
                        item.ItemType,
                        regexItem.GetMetadata("ItemType"),
                        StringComparison.OrdinalIgnoreCase))
            .Any(regexItem => Regex.IsMatch(
                     item.EvaluatedInclude,
                     regexItem.ItemSpec))
            ?? false;

            string ComputeLogicalResourceName(
                Project project,
                ProjectItem projectItem,
                string logicalResource)
            {
                if (!string.IsNullOrEmpty(logicalResource))
                {
                    return(logicalResource);
                }

                var projectDirectory = Path.GetDirectoryName(project.FullPath);
                var resourceName     = Regex.Replace(
                    MakeRelativePath(
                        projectDirectory,
                        Path.Combine(projectDirectory, projectItem.EvaluatedInclude)),
                    @"[\\\/]+",
                    ".");

                var prefix = project.GetPropertyValue("RootNamespace");

                if (string.IsNullOrEmpty(prefix) && project.FullPath != null)
                {
                    prefix = Path.GetFileNameWithoutExtension(project.FullPath);
                }

                if (string.IsNullOrEmpty(prefix))
                {
                    return(resourceName);
                }

                return($"{prefix}.{resourceName}");
            }
        }