CreateFrameworkListFile.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Xml.Linq;
  9. using Microsoft.Build.Framework;
  10. using Microsoft.Build.Utilities;
  11. namespace RepoTasks;
  12. public class CreateFrameworkListFile : Microsoft.Build.Utilities.Task
  13. {
  14. /// <summary>
  15. /// Files to extract basic information from and include in the list.
  16. /// </summary>
  17. [Required]
  18. public ITaskItem[] Files { get; set; }
  19. [Required]
  20. public string TargetFile { get; set; }
  21. /// <summary>
  22. /// Extra attributes to place on the root node.
  23. ///
  24. /// %(Identity): Attribute name.
  25. /// %(Value): Attribute value.
  26. /// </summary>
  27. public ITaskItem[] RootAttributes { get; set; }
  28. public override bool Execute()
  29. {
  30. XAttribute[] rootAttributes = RootAttributes
  31. ?.Select(item => new XAttribute(item.ItemSpec, item.GetMetadata("Value")))
  32. .ToArray();
  33. var frameworkManifest = new XElement("FileList", rootAttributes);
  34. var usedFileProfiles = new HashSet<string>();
  35. foreach (var f in Files
  36. .Select(item => new
  37. {
  38. Item = item,
  39. Filename = Path.GetFileName(item.ItemSpec),
  40. AssemblyName = FileUtilities.GetAssemblyName(item.ItemSpec),
  41. FileVersion = FileUtilities.GetFileVersion(item.ItemSpec),
  42. IsNative = item.GetMetadata("IsNativeImage") == "true",
  43. IsSymbolFile = item.GetMetadata("IsSymbolFile") == "true",
  44. PackagePath = GetPackagePath(item)
  45. })
  46. .Where(f =>
  47. !f.IsSymbolFile &&
  48. (f.Filename.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || f.IsNative))
  49. .OrderBy(f => f.Filename, StringComparer.Ordinal))
  50. {
  51. string path = Path.Combine(f.PackagePath, f.Filename).Replace('\\', '/');
  52. string type = f.IsNative ? "Native" : "Managed";
  53. var element = new XElement("File", new XAttribute("Path", path));
  54. if (path.StartsWith("analyzers/", StringComparison.Ordinal))
  55. {
  56. type = "Analyzer";
  57. if (path.EndsWith(".resources.dll", StringComparison.Ordinal))
  58. {
  59. // omit analyzer resources
  60. continue;
  61. }
  62. var pathParts = path.Split('/');
  63. if (pathParts.Length < 3 || !pathParts[1].Equals("dotnet", StringComparison.Ordinal) || pathParts.Length > 5)
  64. {
  65. Log.LogError($"Unexpected analyzer path format {path}. Expected 'analyzers/dotnet(/roslyn<version>)(/language)/analyzer.dll");
  66. }
  67. // Check if we have enough parts for language directory and include it.
  68. // There could be a roslyn<version> folder before the language folder. Check for it.
  69. bool hasRoslynVersion = pathParts[2].StartsWith("roslyn", StringComparison.Ordinal);
  70. int languageLengthCheck = hasRoslynVersion ? 4 : 3;
  71. int potentialLanguageIndex = hasRoslynVersion ? 3 : 2;
  72. if (pathParts.Length > languageLengthCheck)
  73. {
  74. element.Add(new XAttribute("Language", pathParts[potentialLanguageIndex]));
  75. }
  76. }
  77. element.Add(new XAttribute("Type", type));
  78. if (f.AssemblyName != null)
  79. {
  80. byte[] publicKeyToken = f.AssemblyName.GetPublicKeyToken();
  81. string publicKeyTokenHex;
  82. if (publicKeyToken != null)
  83. {
  84. publicKeyTokenHex = BitConverter.ToString(publicKeyToken)
  85. .ToLowerInvariant()
  86. .Replace("-", "");
  87. }
  88. else
  89. {
  90. Log.LogError($"No public key token found for assembly {f.Item.ItemSpec}");
  91. publicKeyTokenHex = "";
  92. }
  93. element.Add(
  94. new XAttribute("AssemblyName", f.AssemblyName.Name),
  95. new XAttribute("PublicKeyToken", publicKeyTokenHex),
  96. new XAttribute("AssemblyVersion", f.AssemblyName.Version));
  97. }
  98. else if (!f.IsNative)
  99. {
  100. // This file isn't managed and isn't native. Leave it off the list.
  101. continue;
  102. }
  103. element.Add(new XAttribute("FileVersion", f.FileVersion));
  104. frameworkManifest.Add(element);
  105. }
  106. Directory.CreateDirectory(Path.GetDirectoryName(TargetFile));
  107. File.WriteAllText(TargetFile, frameworkManifest.ToString());
  108. return !Log.HasLoggedErrors;
  109. }
  110. private static string GetPackagePath(ITaskItem item)
  111. {
  112. string packagePath = item.GetMetadata("PackagePath");
  113. // replicate the logic used by PackTask https://github.com/NuGet/NuGet.Client/blob/f24bad0668193ce21a1db8cabd1ce95ba509c7f0/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs#L644-L647
  114. string recursiveDir = item.GetMetadata("RecursiveDir");
  115. recursiveDir = string.IsNullOrEmpty(recursiveDir) ? item.GetMetadata("NuGetRecursiveDir") : recursiveDir;
  116. return string.IsNullOrEmpty(recursiveDir) ? packagePath :
  117. Path.Combine(packagePath, recursiveDir);
  118. }
  119. }