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); }
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)); }
/// <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 `<ProjectReference>` 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); }
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}"); } }