Skip to content

Commit 640b77f

Browse files
ericstjdougbu
andauthored
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 <6431421+dougbu@users.noreply.github.com>
1 parent 1dd00f4 commit 640b77f

File tree

4 files changed

+107
-31
lines changed

4 files changed

+107
-31
lines changed

eng/tools/RepoTasks/CreateFrameworkListFile.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,35 @@ public override bool Execute()
5757
(f.Filename.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || f.IsNative))
5858
.OrderBy(f => f.Filename, StringComparer.Ordinal))
5959
{
60-
var element = new XElement(
61-
"File",
62-
new XAttribute("Type", f.IsNative ? "Native" : "Managed"),
63-
new XAttribute(
64-
"Path",
65-
Path.Combine(f.PackagePath, f.Filename).Replace('\\', '/')));
60+
string path = Path.Combine(f.PackagePath, f.Filename).Replace('\\', '/');
61+
string type = f.IsNative ? "Native" : "Managed";
62+
var element = new XElement("File", new XAttribute("Path", path));
63+
64+
if (path.StartsWith("analyzers/", StringComparison.Ordinal))
65+
{
66+
type = "Analyzer";
67+
68+
if (path.EndsWith(".resources.dll", StringComparison.Ordinal))
69+
{
70+
// omit analyzer resources
71+
continue;
72+
}
73+
74+
var pathParts = path.Split('/');
75+
76+
if (pathParts.Length < 3 || !pathParts[1].Equals("dotnet", StringComparison.Ordinal) || pathParts.Length > 4)
77+
{
78+
Log.LogError($"Unexpected analyzer path format {path}. Expected 'analyzers/dotnet(/language)/analyzer.dll");
79+
}
80+
81+
// Check if we have enough parts for language directory and include it
82+
if (pathParts.Length > 3)
83+
{
84+
element.Add(new XAttribute("Language", pathParts[2]));
85+
}
86+
}
87+
88+
element.Add(new XAttribute("Type", type));
6689

6790
if (f.AssemblyName != null)
6891
{

src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ This package is an internal implementation of the .NET Core SDK and is not meant
164164
Include="@(AspNetCoreReferenceAssemblyPath->WithMetadataValue('ExternallyResolved', 'true')->'%(RootDir)%(Directory)%(Filename).xml')"
165165
Condition="Exists('%(RootDir)%(Directory)%(Filename).xml')" />
166166

167+
<_AnalyzerContent Include="$(PkgMicrosoft_AspNetCore_Internal_Transport)\$(AnalyzersPackagePath)**\*.*" />
168+
<RefPackContent Include="@(_AnalyzerContent)" PackagePath="$(AnalyzersPackagePath)%(RecursiveDir)" />
169+
167170
<RefPackContent Include="@(AspNetCoreReferenceAssemblyPath)" PackagePath="$(RefAssemblyPackagePath)" />
168171
<RefPackContent Include="@(AspNetCoreReferenceDocXml)" PackagePath="$(RefAssemblyPackagePath)" />
169172
<RefPackContent Include="$(ReferencePackageOverridesPath)" PackagePath="$(ManifestsPackagePath)" />

src/Framework/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
<PropertyGroup>
55
<ManifestsPackagePath>data/</ManifestsPackagePath>
6+
<AnalyzersPackagePath>analyzers/</AnalyzersPackagePath>
67
<!-- Shared between targeting pack and runtime build. -->
78
<PlatformManifestFileName>PlatformManifest.txt</PlatformManifestFileName>
89
<PlatformManifestOutputPath>$(ArtifactsObjDir)$(PlatformManifestFileName)</PlatformManifestOutputPath>

src/Framework/test/TargetingPackTests.cs

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public void AssembliesAreReferenceAssemblies()
200200
return;
201201
}
202202

203-
IEnumerable<string> dlls = Directory.GetFiles(_targetingPackRoot, "*.dll", SearchOption.AllDirectories);
203+
IEnumerable<string> dlls = Directory.GetFiles(Path.Combine(_targetingPackRoot, "ref"), "*.dll", SearchOption.AllDirectories);
204204
Assert.NotEmpty(dlls);
205205

206206
Assert.All(dlls, path =>
@@ -312,32 +312,48 @@ public void FrameworkListListsContainsCorrectEntries()
312312

313313
var frameworkListDoc = XDocument.Load(frameworkListPath);
314314
var frameworkListEntries = frameworkListDoc.Root.Descendants();
315+
var managedEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Managed", StringComparison.Ordinal));
316+
var analyzerEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Analyzer", StringComparison.Ordinal));
315317

316-
_output.WriteLine("==== file contents ====");
317-
_output.WriteLine(string.Join('\n', frameworkListEntries.Select(i => i.Attribute("AssemblyName").Value).OrderBy(i => i)));
318-
_output.WriteLine("==== expected assemblies ====");
319-
_output.WriteLine(string.Join('\n', expectedAssemblies.OrderBy(i => i)));
320-
321-
var actualAssemblies = frameworkListEntries
322-
.Select(i =>
323-
{
324-
var fileName = i.Attribute("AssemblyName").Value;
325-
return fileName.EndsWith(".dll", StringComparison.Ordinal)
326-
? fileName.Substring(0, fileName.Length - 4)
327-
: fileName;
328-
})
329-
.ToHashSet();
318+
var analyzersDir = Path.Combine(_targetingPackRoot, "analyzers");
319+
var expectedAnalyzers = Directory.Exists(analyzersDir) ?
320+
Directory.GetFiles(analyzersDir, "*.dll", SearchOption.AllDirectories)
321+
.Select(p => Path.GetFileNameWithoutExtension(p))
322+
.Where(f => !f.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
323+
.ToHashSet() :
324+
new HashSet<string>();
330325

331-
var missing = expectedAssemblies.Except(actualAssemblies);
332-
var unexpected = actualAssemblies.Except(expectedAssemblies);
333-
334-
_output.WriteLine("==== missing assemblies from the framework list ====");
335-
_output.WriteLine(string.Join('\n', missing));
336-
_output.WriteLine("==== unexpected assemblies in the framework list ====");
337-
_output.WriteLine(string.Join('\n', unexpected));
326+
CompareFrameworkElements(expectedAssemblies, managedEntries, "managed");
327+
CompareFrameworkElements(expectedAnalyzers, analyzerEntries, "analyzer");
338328

339-
Assert.Empty(missing);
340-
Assert.Empty(unexpected);
329+
void CompareFrameworkElements(HashSet<string> expectedAssemblyNames, IEnumerable<XElement> actualElements, string type)
330+
{
331+
_output.WriteLine($"==== file contents ({type}) ====");
332+
_output.WriteLine(string.Join('\n', actualElements.Select(i => i.Attribute("AssemblyName").Value).OrderBy(i => i)));
333+
_output.WriteLine($"==== expected {type} assemblies ====");
334+
_output.WriteLine(string.Join('\n', expectedAssemblyNames.OrderBy(i => i)));
335+
336+
var actualAssemblyNames = managedEntries
337+
.Select(i =>
338+
{
339+
var fileName = i.Attribute("AssemblyName").Value;
340+
return fileName.EndsWith(".dll", StringComparison.Ordinal)
341+
? fileName.Substring(0, fileName.Length - 4)
342+
: fileName;
343+
})
344+
.ToHashSet();
345+
346+
var missing = actualAssemblyNames.Except(actualAssemblyNames);
347+
var unexpected = actualAssemblyNames.Except(expectedAssemblies);
348+
349+
_output.WriteLine($"==== missing {type} assemblies from the framework list ====");
350+
_output.WriteLine(string.Join('\n', missing));
351+
_output.WriteLine($"==== unexpected {type} assemblies in the framework list ====");
352+
_output.WriteLine(string.Join('\n', unexpected));
353+
354+
Assert.Empty(missing);
355+
Assert.Empty(unexpected);
356+
}
341357

342358
Assert.All(frameworkListEntries, i =>
343359
{
@@ -370,7 +386,7 @@ public void FrameworkListListsContainsCorrectPaths()
370386
ZipArchive archive = ZipFile.OpenRead(targetingPackPath);
371387

372388
var actualPaths = archive.Entries
373-
.Where(i => i.FullName.EndsWith(".dll", StringComparison.Ordinal))
389+
.Where(i => i.FullName.EndsWith(".dll", StringComparison.Ordinal) && !i.FullName.EndsWith(".resources.dll", StringComparison.Ordinal))
374390
.Select(i => i.FullName).ToHashSet();
375391

376392
var expectedPaths = frameworkListEntries.Select(i => i.Attribute("Path").Value).ToHashSet();
@@ -391,5 +407,38 @@ public void FrameworkListListsContainsCorrectPaths()
391407
Assert.Empty(missing);
392408
Assert.Empty(unexpected);
393409
}
410+
411+
[Fact]
412+
public void FrameworkListListsContainsAnalyzerLanguage()
413+
{
414+
if (!_isTargetingPackBuilding || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
415+
{
416+
return;
417+
}
418+
419+
var frameworkListPath = Path.Combine(_targetingPackRoot, "data", "FrameworkList.xml");
420+
421+
AssertEx.FileExists(frameworkListPath);
422+
423+
var frameworkListDoc = XDocument.Load(frameworkListPath);
424+
var frameworkListEntries = frameworkListDoc.Root.Descendants();
425+
426+
var analyzerEntries = frameworkListEntries.Where(i => i.Attribute("Type").Value.Equals("Analyzer", StringComparison.Ordinal));
427+
428+
Assert.All(analyzerEntries, analyzerEntry =>
429+
{
430+
var actualLanguage = analyzerEntry.Attribute("Language")?.Value;
431+
var assemblyPath = analyzerEntry.Attribute("Path").Value;
432+
433+
string expectedLanguage = Path.GetFileName(Path.GetDirectoryName(assemblyPath));
434+
435+
if (expectedLanguage.Equals("dotnet", StringComparison.OrdinalIgnoreCase))
436+
{
437+
expectedLanguage = null;
438+
}
439+
440+
Assert.Equal(expectedLanguage, actualLanguage);
441+
});
442+
}
394443
}
395444
}

0 commit comments

Comments
 (0)