diff --git a/eng/tools/RepoTasks/CreateFrameworkListFile.cs b/eng/tools/RepoTasks/CreateFrameworkListFile.cs index 0ac46a87c12a..8005f81f65fe 100644 --- a/eng/tools/RepoTasks/CreateFrameworkListFile.cs +++ b/eng/tools/RepoTasks/CreateFrameworkListFile.cs @@ -57,12 +57,35 @@ public override bool Execute() (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) { diff --git a/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj b/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj index ebb7fe041e60..43eba091547f 100644 --- a/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj +++ b/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)**\*.*" /> + + diff --git a/src/Framework/Directory.Build.props b/src/Framework/Directory.Build.props index 11d96a6864b7..4533e3e5e1be 100644 --- a/src/Framework/Directory.Build.props +++ b/src/Framework/Directory.Build.props @@ -3,6 +3,7 @@ data/ + analyzers/ PlatformManifest.txt $(ArtifactsObjDir)$(PlatformManifestFileName) diff --git a/src/Framework/test/TargetingPackTests.cs b/src/Framework/test/TargetingPackTests.cs index 5f34153949c5..99b43ca08ee3 100644 --- a/src/Framework/test/TargetingPackTests.cs +++ b/src/Framework/test/TargetingPackTests.cs @@ -200,7 +200,7 @@ public void AssembliesAreReferenceAssemblies() return; } - IEnumerable dlls = Directory.GetFiles(_targetingPackRoot, "*.dll", SearchOption.AllDirectories); + IEnumerable dlls = Directory.GetFiles(Path.Combine(_targetingPackRoot, "ref"), "*.dll", SearchOption.AllDirectories); Assert.NotEmpty(dlls); Assert.All(dlls, path => @@ -312,32 +312,48 @@ public void FrameworkListListsContainsCorrectEntries() 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(); - 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 expectedAssemblyNames, IEnumerable 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 @@ public void FrameworkListListsContainsCorrectPaths() 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 @@ public void FrameworkListListsContainsCorrectPaths() 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); + }); + } } }