ProjectInfoFactory.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using Microsoft.Build.Construction;
  8. using Microsoft.Build.Evaluation;
  9. using Microsoft.Build.Execution;
  10. using NuGet.Frameworks;
  11. using RepoTasks.Utilities;
  12. using Microsoft.Build.Utilities;
  13. namespace RepoTasks.ProjectModel
  14. {
  15. internal class ProjectInfoFactory
  16. {
  17. private readonly TaskLoggingHelper _logger;
  18. public ProjectInfoFactory(TaskLoggingHelper logger)
  19. {
  20. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  21. }
  22. public ProjectInfo Create(string path, ProjectCollection projectCollection)
  23. {
  24. var project = GetProject(path, projectCollection);
  25. var instance = project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup);
  26. var targetFrameworks = instance.GetPropertyValue("TargetFrameworks");
  27. var targetFramework = instance.GetPropertyValue("TargetFramework");
  28. var frameworks = new List<ProjectFrameworkInfo>();
  29. if (!string.IsNullOrEmpty(targetFrameworks) && string.IsNullOrEmpty(targetFramework))
  30. {
  31. // multi targeting
  32. foreach (var tfm in targetFrameworks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
  33. {
  34. project.SetGlobalProperty("TargetFramework", tfm);
  35. var innerBuild = project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup);
  36. var tfmInfo = new ProjectFrameworkInfo(NuGetFramework.Parse(tfm), GetDependencies(innerBuild));
  37. frameworks.Add(tfmInfo);
  38. }
  39. project.RemoveGlobalProperty("TargetFramework");
  40. }
  41. else if (!string.IsNullOrEmpty(targetFramework))
  42. {
  43. var tfmInfo = new ProjectFrameworkInfo(NuGetFramework.Parse(targetFramework), GetDependencies(instance));
  44. frameworks.Add(tfmInfo);
  45. }
  46. var projectDir = Path.GetDirectoryName(path);
  47. var tools = GetTools(instance).ToArray();
  48. bool.TryParse(instance.GetPropertyValue("IsPackable"), out var isPackable);
  49. if (isPackable)
  50. {
  51. // the default packable setting is disabled for projects referencing this package.
  52. isPackable = !frameworks.SelectMany(f => f.Dependencies.Keys).Any(d => d.Equals("Microsoft.NET.Test.Sdk", StringComparison.OrdinalIgnoreCase));
  53. }
  54. var packageId = instance.GetPropertyValue("PackageId");
  55. var packageVersion = instance.GetPropertyValue("PackageVersion");
  56. return new ProjectInfo(path,
  57. frameworks,
  58. tools,
  59. isPackable,
  60. packageId,
  61. packageVersion);
  62. }
  63. private static object _projLock = new object();
  64. private static Project GetProject(string path, ProjectCollection projectCollection)
  65. {
  66. var projects = projectCollection.GetLoadedProjects(path);
  67. foreach(var proj in projects)
  68. {
  69. if (proj.GetPropertyValue("DesignTimeBuild") == "true")
  70. {
  71. return proj;
  72. }
  73. }
  74. var xml = ProjectRootElement.Open(path, projectCollection);
  75. var globalProps = new Dictionary<string, string>()
  76. {
  77. ["DesignTimeBuild"] = "true",
  78. // Isolate the project from post-restore side effects
  79. ["ExcludeRestorePackageImports"] = "true",
  80. };
  81. var project = new Project(xml,
  82. globalProps,
  83. toolsVersion: "15.0",
  84. projectCollection: projectCollection)
  85. {
  86. IsBuildEnabled = false
  87. };
  88. return project;
  89. }
  90. private IReadOnlyDictionary<string, PackageReferenceInfo> GetDependencies(ProjectInstance project)
  91. {
  92. var references = new Dictionary<string, PackageReferenceInfo>(StringComparer.OrdinalIgnoreCase);
  93. foreach (var item in project.GetItems("PackageReference"))
  94. {
  95. bool.TryParse(item.GetMetadataValue("IsImplicitlyDefined"), out var isImplicit);
  96. var info = new PackageReferenceInfo(item.EvaluatedInclude, item.GetMetadataValue("Version"), isImplicit);
  97. if (references.ContainsKey(info.Id))
  98. {
  99. _logger.LogKoreBuildWarning(project.ProjectFileLocation.File, KoreBuildErrors.DuplicatePackageReference, $"Found a duplicate PackageReference for {info.Id}. Restore results may be unpredictable.");
  100. }
  101. references[info.Id] = info;
  102. }
  103. return references;
  104. }
  105. private static IEnumerable<DotNetCliReferenceInfo> GetTools(ProjectInstance project)
  106. {
  107. return project.GetItems("DotNetCliToolReference").Select(item =>
  108. new DotNetCliReferenceInfo(item.EvaluatedInclude, item.GetMetadataValue("Version")));
  109. }
  110. }
  111. }