Skip to content

Commit 1c19af5

Browse files
committed
Add analyzer section in project.assets.json
1 parent c8a073c commit 1c19af5

25 files changed

Lines changed: 2025 additions & 51 deletions

src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ Copyright (c) .NET Foundation. All rights reserved.
8080
AND '$(TargetFrameworkIdentifier)' == '.NETCoreApp'
8181
AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '10.0'))">all</NuGetAuditMode>
8282
<NuGetAuditMode Condition=" '$(NuGetAuditMode)' == '' ">direct</NuGetAuditMode>
83+
<!-- Analyzer assets restore is only available for projects targeting .NET 11 or greater. Force the opt-in off for older frameworks. -->
84+
<RestoreEnableAnalyzerAssets Condition="'$(RestoreEnableAnalyzerAssets)' == 'true'
85+
AND !('$(TargetFrameworkIdentifier)' == '.NETCoreApp'
86+
AND '$(TargetFrameworkVersion)' != ''
87+
AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '11.0')))">false</RestoreEnableAnalyzerAssets>
88+
<RestoreEnableAnalyzerAssets Condition="'$(RestoreEnableAnalyzerAssets)' == '' AND '$(TargetFrameworks)' == ''">false</RestoreEnableAnalyzerAssets>
8389
</PropertyGroup>
8490

8591
<!-- Package pruning is enabled for all projects targeting .NET 10 or greater, including multi-targeted projects. -->
@@ -864,6 +870,7 @@ Copyright (c) .NET Foundation. All rights reserved.
864870
<RestoreAdditionalProjectSources>$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
865871
<RestoreAdditionalProjectFallbackFolders>$(RestoreAdditionalProjectFallbackFolders)</RestoreAdditionalProjectFallbackFolders>
866872
<RestoreAdditionalProjectFallbackFoldersExcludes>$(RestoreAdditionalProjectFallbackFoldersExcludes)</RestoreAdditionalProjectFallbackFoldersExcludes>
873+
<RestoreEnableAnalyzerAssets>$(RestoreEnableAnalyzerAssets)</RestoreEnableAnalyzerAssets>
867874
</_RestoreSettingsPerFramework>
868875
</ItemGroup>
869876
</Target>
@@ -887,6 +894,18 @@ Copyright (c) .NET Foundation. All rights reserved.
887894
<Output TaskParameter="AbsolutePaths" PropertyName="RestoreOutputAbsolutePath" />
888895
</ConvertToAbsolutePath>
889896

897+
<ItemGroup Condition="'$(TargetFrameworks)' != ''">
898+
<_RestoreSettingsWithAnalyzerAssets
899+
Include="@(_RestoreSettingsPerFramework)"
900+
Condition="'%(_RestoreSettingsPerFramework.RestoreEnableAnalyzerAssets)' == 'true'" />
901+
</ItemGroup>
902+
903+
<PropertyGroup>
904+
<_RestoreEffectiveEnableAnalyzerAssets>$(RestoreEnableAnalyzerAssets)</_RestoreEffectiveEnableAnalyzerAssets>
905+
<_RestoreEffectiveEnableAnalyzerAssets Condition="'$(TargetFrameworks)' != ''">false</_RestoreEffectiveEnableAnalyzerAssets>
906+
<_RestoreEffectiveEnableAnalyzerAssets Condition="'$(TargetFrameworks)' != '' AND '@(_RestoreSettingsWithAnalyzerAssets)' != ''">true</_RestoreEffectiveEnableAnalyzerAssets>
907+
</PropertyGroup>
908+
890909
<!--
891910
Determine project name for the assets file.
892911
Highest priority: PackageId
@@ -962,6 +981,7 @@ Copyright (c) .NET Foundation. All rights reserved.
962981
<NETCoreSdkVersion>$(NETCoreSdkVersion)</NETCoreSdkVersion>
963982
<RestoreUseLegacyDependencyResolver>$(RestoreUseLegacyDependencyResolver)</RestoreUseLegacyDependencyResolver>
964983
<RestoreDoNotWriteDependencyGraphSpec>$(RestoreDoNotWriteDependencyGraphSpec)</RestoreDoNotWriteDependencyGraphSpec>
984+
<RestoreEnableAnalyzerAssets>$(_RestoreEffectiveEnableAnalyzerAssets)</RestoreEnableAnalyzerAssets>
965985
</_RestoreGraphEntry>
966986
</ItemGroup>
967987

src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ public LockFile CreateLockFile(LockFile previousLockFile,
155155

156156
var librariesWithWarnings = new HashSet<LibraryIdentity>();
157157

158-
var rootProjectStyle = project.RestoreMetadata?.ProjectStyle ?? ProjectStyle.Unknown;
158+
var restoreMetadata = project.RestoreMetadata;
159+
var rootProjectStyle = restoreMetadata?.ProjectStyle ?? ProjectStyle.Unknown;
160+
bool restoreEnableAnalyzerAssets = restoreMetadata?.RestoreEnableAnalyzerAssets == true;
159161

160162
// Add the targets
161163
foreach (var targetGraph in targetGraphs
@@ -238,6 +240,7 @@ public LockFile CreateLockFile(LockFile previousLockFile,
238240
dependencyType: includeFlags,
239241
targetFrameworkOverride: null,
240242
dependencies: graphItem.Data.Dependencies,
243+
restoreEnableAnalyzerAssets: restoreEnableAnalyzerAssets,
241244
cache: lockFileBuilderCache);
242245

243246
target.Libraries.Add(targetLibrary);
@@ -258,6 +261,7 @@ public LockFile CreateLockFile(LockFile previousLockFile,
258261
targetFrameworkOverride: nonFallbackFramework,
259262
dependencyType: includeFlags,
260263
dependencies: graphItem.Data.Dependencies,
264+
restoreEnableAnalyzerAssets: restoreEnableAnalyzerAssets,
261265
cache: lockFileBuilderCache);
262266
usedFallbackFramework = !targetLibrary.Equals(targetLibraryWithoutFallback);
263267
}

src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilderCache.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class LockFileBuilderCache
3131
private readonly ConcurrentDictionary<CriteriaKey, List<(List<SelectionCriteria>, bool)>> _criteriaSets =
3232
new();
3333

34-
private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags, int dependencyCount), Lazy<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)>> _lockFileTargetLibraryCache =
34+
private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags, int dependencyCount, bool restoreEnableAnalyzerAssets), Lazy<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)>> _lockFileTargetLibraryCache =
3535
new();
3636

3737
/// <summary>
@@ -106,7 +106,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka
106106
/// <summary>
107107
/// Try to get a LockFileTargetLibrary from the cache.
108108
/// </summary>
109-
internal (LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, List<LibraryDependency> dependencies, Func<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)> valueFactory)
109+
internal (LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, List<LibraryDependency> dependencies, bool restoreEnableAnalyzerAssets, Func<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)> valueFactory)
110110
{
111111
// Comparing RuntimeGraph for equality is very expensive,
112112
// so in case of a request where the RuntimeGraph is not empty we avoid using the cache.
@@ -116,7 +116,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka
116116
localPackageInfo = localPackageInfo ?? throw new ArgumentNullException(nameof(localPackageInfo));
117117
var criteriaKey = new CriteriaKey(graph.TargetGraphName, framework);
118118
var packagePath = localPackageInfo.ExpandedPath;
119-
return _lockFileTargetLibraryCache.GetOrAdd((criteriaKey, packagePath, aliases, libraryIncludeFlags, dependencies.Count),
119+
return _lockFileTargetLibraryCache.GetOrAdd((criteriaKey, packagePath, aliases, libraryIncludeFlags, dependencies.Count, restoreEnableAnalyzerAssets),
120120
key => new Lazy<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)>(valueFactory)).Value;
121121
}
122122

src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ private readonly Dictionary<RestoreTargetGraph, Dictionary<string, LibraryInclud
151151
private const string PackagePruningRemovablePackagesCount = "Pruning.RemovablePackages.Count";
152152
private const string PackagePruningDirectCount = "Pruning.Pruned.Direct.Count";
153153

154+
// Analyzer assets names
155+
private const string AnalyzerAssetsEnabled = "AnalyzerAssets.Enabled";
156+
private const string AnalyzerAssetsCount = "AnalyzerAssets.Count";
157+
154158
internal readonly bool _enableNewDependencyResolver;
155159
private readonly bool _isLockFileEnabled;
156160

@@ -354,6 +358,7 @@ public async Task<RestoreResult> ExecuteAsync(CancellationToken token)
354358

355359
telemetry.TelemetryEvent[UpdatedAssetsFile] = restoreResult._isAssetsFileDirty.Value;
356360
telemetry.TelemetryEvent[UpdatedMSBuildFiles] = restoreResult._dirtyMSBuildFiles.Value.Count > 0;
361+
telemetry.TelemetryEvent[AnalyzerAssetsCount] = CountAnalyzerAssets(assetsFile);
357362

358363
return restoreResult;
359364
}
@@ -408,6 +413,7 @@ private void InitializeTelemetry(TelemetryActivity telemetry, int httpSourcesCou
408413
}
409414

410415
telemetry.TelemetryEvent[AuditEnabled] = auditEnabled ? "enabled" : "disabled";
416+
telemetry.TelemetryEvent[AnalyzerAssetsEnabled] = _request.Project.RestoreMetadata?.RestoreEnableAnalyzerAssets ?? false;
411417

412418
PopulatePruningEnabledTelemetry(_request.Project, telemetry.TelemetryEvent);
413419
}
@@ -453,6 +459,37 @@ internal static void PopulatePruningEnabledTelemetry(PackageSpec project, Teleme
453459
telemetryEvent[PackagePruningFrameworksUnsupportedCount] = pruningNotApplicableCount;
454460
}
455461

462+
/// <summary>
463+
/// Counts the analyzer assets written to the assets file, excluding '_._' placeholders that
464+
/// represent analyzers excluded from the project. Reflects the depth of analyzer-assets adoption.
465+
/// </summary>
466+
private static int CountAnalyzerAssets(LockFile assetsFile)
467+
{
468+
int count = 0;
469+
470+
foreach (LockFileTarget target in assetsFile.Targets)
471+
{
472+
foreach (LockFileTargetLibrary library in target.Libraries)
473+
{
474+
IList<LockFileItem> analyzerAssets = library.AnalyzerAssets;
475+
if (analyzerAssets == null)
476+
{
477+
continue;
478+
}
479+
480+
foreach (LockFileItem asset in analyzerAssets)
481+
{
482+
if (!asset.Path.EndsWith("_._", StringComparison.Ordinal))
483+
{
484+
count++;
485+
}
486+
}
487+
}
488+
}
489+
490+
return count;
491+
}
492+
456493
private async Task<(RestoreResult, bool, CacheFile)> EvaluateNoOpAsync(TelemetryActivity telemetry, CacheFile cacheFile, Stopwatch restoreTime)
457494
{
458495
telemetry.StartIntervalMeasure();

0 commit comments

Comments
 (0)