Просмотр исходного кода

Add support for packaging analyzers into ref-pack (#33977)

* Add support for packaging analyzers into ref-pack
* Apply suggestions from code review
* Add tests for analyzers in refpack
* Handle empty analyzers directory

Co-authored-by: Doug Bunting <[email protected]>
Eric StJohn 4 лет назад
Родитель
Сommit
640b77f082

+ 29 - 6
eng/tools/RepoTasks/CreateFrameworkListFile.cs

@@ -57,12 +57,35 @@ namespace RepoTasks
                     (f.Filename.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || f.IsNative))
                 .OrderBy(f => f.Filename, StringComparer.Ordinal))
             {
-                var element = new XElement(
-                    "File",
-                    new XAttribute("Type", f.IsNative ? "Native" : "Managed"),
-                    new XAttribute(
-                        "Path",
-                        Path.Combine(f.PackagePath, f.Filename).Replace('\\', '/')));
+                string path = Path.Combine(f.PackagePath, f.Filename).Replace('\\', '/');
+                string type = f.IsNative ? "Native" : "Managed";
+                var element = new XElement("File", new XAttribute("Path", path));
+
+                if (path.StartsWith("analyzers/", StringComparison.Ordinal))
+                {
+                    type = "Analyzer";
+
+                    if (path.EndsWith(".resources.dll", StringComparison.Ordinal))
+                    {
+                        // omit analyzer resources
+                        continue;
+                    }
+
+                    var pathParts = path.Split('/');
+
+                    if (pathParts.Length < 3 || !pathParts[1].Equals("dotnet", StringComparison.Ordinal) || pathParts.Length > 4)
+                    {
+                        Log.LogError($"Unexpected analyzer path format {path}.  Expected  'analyzers/dotnet(/language)/analyzer.dll");
+                    }
+
+                    // Check if we have enough parts for language directory and include it
+                    if (pathParts.Length > 3)
+                    {
+                        element.Add(new XAttribute("Language", pathParts[2]));
+                    }
+                }
+
+                element.Add(new XAttribute("Type", type));
 
                 if (f.AssemblyName != null)
                 {

+ 3 - 0
src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj

@@ -164,6 +164,9 @@ This package is an internal implementation of the .NET Core SDK and is not meant
           Include="@(AspNetCoreReferenceAssemblyPath->WithMetadataValue('ExternallyResolved', 'true')->'%(RootDir)%(Directory)%(Filename).xml')"
           Condition="Exists('%(RootDir)%(Directory)%(Filename).xml')" />
 
+      <_AnalyzerContent Include="$(PkgMicrosoft_AspNetCore_Internal_Transport)\$(AnalyzersPackagePath)**\*.*" />
+      <RefPackContent Include="@(_AnalyzerContent)" PackagePath="$(AnalyzersPackagePath)%(RecursiveDir)" />
+
       <RefPackContent Include="@(AspNetCoreReferenceAssemblyPath)" PackagePath="$(RefAssemblyPackagePath)" />
       <RefPackContent Include="@(AspNetCoreReferenceDocXml)" PackagePath="$(RefAssemblyPackagePath)" />
       <RefPackContent Include="$(ReferencePackageOverridesPath)" PackagePath="$(ManifestsPackagePath)" />

+ 1 - 0
src/Framework/Directory.Build.props

@@ -3,6 +3,7 @@
 
   <PropertyGroup>
     <ManifestsPackagePath>data/</ManifestsPackagePath>
+    <AnalyzersPackagePath>analyzers/</AnalyzersPackagePath>
     <!-- Shared between targeting pack and runtime build. -->
     <PlatformManifestFileName>PlatformManifest.txt</PlatformManifestFileName>
     <PlatformManifestOutputPath>$(ArtifactsObjDir)$(PlatformManifestFileName)</PlatformManifestOutputPath>

+ 74 - 25
src/Framework/test/TargetingPackTests.cs

@@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore
                 return;
             }
 
-            IEnumerable<string> dlls = Directory.GetFiles(_targetingPackRoot, "*.dll", SearchOption.AllDirectories);
+            IEnumerable<string> dlls = Directory.GetFiles(Path.Combine(_targetingPackRoot, "ref"), "*.dll", SearchOption.AllDirectories);
             Assert.NotEmpty(dlls);
 
             Assert.All(dlls, path =>
@@ -312,32 +312,48 @@ namespace Microsoft.AspNetCore
 
             var frameworkListDoc = XDocument.Load(frameworkListPath);
             var frameworkListEntries = frameworkListDoc.Root.Descendants();
+            var managedEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Managed", StringComparison.Ordinal));
+            var analyzerEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Analyzer", StringComparison.Ordinal));
 
-            _output.WriteLine("==== file contents ====");
-            _output.WriteLine(string.Join('\n', frameworkListEntries.Select(i => i.Attribute("AssemblyName").Value).OrderBy(i => i)));
-            _output.WriteLine("==== expected assemblies ====");
-            _output.WriteLine(string.Join('\n', expectedAssemblies.OrderBy(i => i)));
-
-             var actualAssemblies = frameworkListEntries
-                .Select(i =>
-                {
-                    var fileName = i.Attribute("AssemblyName").Value;
-                    return fileName.EndsWith(".dll", StringComparison.Ordinal)
-                        ? fileName.Substring(0, fileName.Length - 4)
-                        : fileName;
-                })
-                .ToHashSet();
+            var analyzersDir = Path.Combine(_targetingPackRoot, "analyzers");
+            var expectedAnalyzers = Directory.Exists(analyzersDir) ?
+                Directory.GetFiles(analyzersDir, "*.dll", SearchOption.AllDirectories)
+                .Select(p => Path.GetFileNameWithoutExtension(p))
+                .Where(f => !f.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
+                .ToHashSet() :
+                new HashSet<string>();
 
-            var missing = expectedAssemblies.Except(actualAssemblies);
-            var unexpected = actualAssemblies.Except(expectedAssemblies);
-
-            _output.WriteLine("==== missing assemblies from the framework list ====");
-            _output.WriteLine(string.Join('\n', missing));
-            _output.WriteLine("==== unexpected assemblies in the framework list ====");
-            _output.WriteLine(string.Join('\n', unexpected));
+            CompareFrameworkElements(expectedAssemblies, managedEntries, "managed");
+            CompareFrameworkElements(expectedAnalyzers, analyzerEntries, "analyzer");
 
-            Assert.Empty(missing);
-            Assert.Empty(unexpected);
+            void CompareFrameworkElements(HashSet<string> expectedAssemblyNames, IEnumerable<XElement> actualElements, string type)
+            {
+                _output.WriteLine($"==== file contents ({type}) ====");
+                _output.WriteLine(string.Join('\n', actualElements.Select(i => i.Attribute("AssemblyName").Value).OrderBy(i => i)));
+                _output.WriteLine($"==== expected {type} assemblies ====");
+                _output.WriteLine(string.Join('\n', expectedAssemblyNames.OrderBy(i => i)));
+
+                var actualAssemblyNames = managedEntries
+                   .Select(i =>
+                   {
+                       var fileName = i.Attribute("AssemblyName").Value;
+                       return fileName.EndsWith(".dll", StringComparison.Ordinal)
+                           ? fileName.Substring(0, fileName.Length - 4)
+                           : fileName;
+                   })
+                   .ToHashSet();
+
+                var missing = actualAssemblyNames.Except(actualAssemblyNames);
+                var unexpected = actualAssemblyNames.Except(expectedAssemblies);
+
+                _output.WriteLine($"==== missing {type} assemblies from the framework list ====");
+                _output.WriteLine(string.Join('\n', missing));
+                _output.WriteLine($"==== unexpected {type} assemblies in the framework list ====");
+                _output.WriteLine(string.Join('\n', unexpected));
+
+                Assert.Empty(missing);
+                Assert.Empty(unexpected);
+            }
 
             Assert.All(frameworkListEntries, i =>
             {
@@ -370,7 +386,7 @@ namespace Microsoft.AspNetCore
             ZipArchive archive = ZipFile.OpenRead(targetingPackPath);
 
             var actualPaths = archive.Entries
-                .Where(i => i.FullName.EndsWith(".dll", StringComparison.Ordinal))
+                .Where(i => i.FullName.EndsWith(".dll", StringComparison.Ordinal) && !i.FullName.EndsWith(".resources.dll", StringComparison.Ordinal))
                 .Select(i => i.FullName).ToHashSet();
 
             var expectedPaths = frameworkListEntries.Select(i => i.Attribute("Path").Value).ToHashSet();
@@ -391,5 +407,38 @@ namespace Microsoft.AspNetCore
             Assert.Empty(missing);
             Assert.Empty(unexpected);
         }
+
+        [Fact]
+        public void FrameworkListListsContainsAnalyzerLanguage()
+        {
+            if (!_isTargetingPackBuilding || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
+            {
+                return;
+            }
+
+            var frameworkListPath = Path.Combine(_targetingPackRoot, "data", "FrameworkList.xml");
+
+            AssertEx.FileExists(frameworkListPath);
+
+            var frameworkListDoc = XDocument.Load(frameworkListPath);
+            var frameworkListEntries = frameworkListDoc.Root.Descendants();
+
+            var analyzerEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Analyzer", StringComparison.Ordinal));
+
+            Assert.All(analyzerEntries, analyzerEntry =>
+            {
+                var actualLanguage = analyzerEntry.Attribute("Language")?.Value;
+                var assemblyPath = analyzerEntry.Attribute("Path").Value;
+
+                string expectedLanguage = Path.GetFileName(Path.GetDirectoryName(assemblyPath));
+
+                if (expectedLanguage.Equals("dotnet", StringComparison.OrdinalIgnoreCase))
+                {
+                    expectedLanguage = null;
+                }
+
+                Assert.Equal(expectedLanguage, actualLanguage);
+            });
+        }
     }
 }