Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 109 additions & 27 deletions src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,39 @@ public int GetHashCode((string, NuGetVersion) library)
}

private void WriteAnalyzers()
{
// Use the decision persisted by restore so build consumes the corresponding lock-file shape.
if (_lockFile.PackageSpec?.RestoreMetadata?.RestoreEnableAnalyzerAssets == true)
{
WriteAnalyzerAssets();
}
else
{
WriteAnalyzerPackageFiles();
}
}

private void WriteAnalyzerAssets()
{
AnalyzerResolver resolver = new(this);

foreach (LockFileTargetLibrary library in _compileTimeTarget.Libraries)
{
if (!library.IsPackage())
{
continue;
}

foreach (LockFileItem asset in library.AnalyzerAssets)
{
resolver.AddAsset(asset, library);
}

resolver.CompleteLibraryAnalyzers();
}
}

private void WriteAnalyzerPackageFiles()
{
AnalyzerResolver resolver = new(this);

Expand Down Expand Up @@ -968,10 +1001,12 @@ private void WriteAnalyzers()
private class AnalyzerResolver
{
private readonly CacheWriter _cacheWriter;
private readonly string _compilerName;
private readonly string _compilerNameSearchString;
private readonly Version _compilerVersion;
private readonly string _projectCodeLanguage;
private Dictionary<(string, NuGetVersion), LockFileTargetLibrary> _targetLibraries;
private List<(string, LockFileLibrary, Version)> _potentialAnalyzers;
private List<(string, LockFileTargetLibrary, Version)> _potentialAnalyzers;
private Version _maxApplicableVersion;

private Dictionary<(string, NuGetVersion), LockFileTargetLibrary> TargetLibraries =>
Expand All @@ -981,9 +1016,11 @@ private class AnalyzerResolver
public AnalyzerResolver(CacheWriter cacheWriter)
{
_cacheWriter = cacheWriter;
_projectCodeLanguage = NuGetUtils.GetLockFileLanguageName(_cacheWriter._task.ProjectLanguage);

if (ParseCompilerApiVersion(_cacheWriter._task.CompilerApiVersion, out ReadOnlyMemory<char> compilerName, out Version compilerVersion))
{
_compilerName = compilerName.ToString();
#if NET
_compilerNameSearchString = string.Concat("/".AsSpan(), compilerName.Span);
#else
Expand All @@ -995,32 +1032,80 @@ public AnalyzerResolver(CacheWriter cacheWriter)

public void AddFile(string file, LockFileLibrary library)
{
if (NuGetUtils.IsApplicableAnalyzer(file, _cacheWriter._task.ProjectLanguage))
if (!NuGetUtils.IsApplicableAnalyzer(file, _cacheWriter._task.ProjectLanguage)
|| !TargetLibraries.TryGetValue((library.Name, library.Version), out LockFileTargetLibrary targetLibrary))
{
if (IsFileCompilerVersionSpecific(file, out Version fileCompilerVersion))
{
if (fileCompilerVersion > _compilerVersion)
{
// version is too high - skip this file
return;
}
return;
}

_potentialAnalyzers ??= new List<(string, LockFileLibrary, Version)>();
_potentialAnalyzers.Add((file, library, fileCompilerVersion));
AddAnalyzer(
file,
targetLibrary,
IsFileCompilerVersionSpecific(file, out Version fileCompilerVersion) ? fileCompilerVersion : null);
}

if (_maxApplicableVersion == null || fileCompilerVersion > _maxApplicableVersion)
{
_maxApplicableVersion = fileCompilerVersion;
}
}
else
{
// if this file isn't specific to a compiler version, just write it directly
WriteAnalyzer(file, library);
}
public void AddAsset(LockFileItem asset, LockFileTargetLibrary library)
{
if (asset.IsPlaceholderFile() || !IsApplicableAnalyzerLanguage(asset))
{
return;
}

AddAnalyzer(asset.Path, library, GetAssetCompilerVersion(asset));
}

private Version GetAssetCompilerVersion(LockFileItem asset)
{
if (_compilerName == null
|| !asset.Properties.TryGetValue(LockFileItem.CompilerApiVersionProperty, out string compilerApiVersion)
|| !ParseCompilerApiVersion(compilerApiVersion, out ReadOnlyMemory<char> compilerName, out Version compilerVersion)
#if NET
|| !compilerName.Span.Equals(_compilerName.AsSpan(), StringComparison.Ordinal))
#else
|| !string.Equals(_compilerName, compilerName.ToString(), StringComparison.Ordinal))
#endif
{
return null;
}

return compilerVersion;
}

private void AddAnalyzer(string file, LockFileTargetLibrary library, Version compilerVersion)
{
if (compilerVersion == null)
{
WriteAnalyzer(file, library);
return;
}

if (compilerVersion > _compilerVersion)
{
return;
}

_potentialAnalyzers ??= new List<(string, LockFileTargetLibrary, Version)>();
_potentialAnalyzers.Add((file, library, compilerVersion));

if (_maxApplicableVersion == null || compilerVersion > _maxApplicableVersion)
{
_maxApplicableVersion = compilerVersion;
}
}

private bool IsApplicableAnalyzerLanguage(LockFileItem asset)
{
if (!asset.Properties.TryGetValue(LockFileContentFile.CodeLanguageProperty, out string codeLanguage)
|| string.IsNullOrEmpty(codeLanguage)
|| string.Equals(codeLanguage, "any", StringComparison.OrdinalIgnoreCase))
{
return true;
}

return _projectCodeLanguage != null
&& string.Equals(codeLanguage, _projectCodeLanguage, StringComparison.OrdinalIgnoreCase);
}

private bool IsFileCompilerVersionSpecific(string file, out Version fileCompilerVersion)
{
fileCompilerVersion = null;
Expand Down Expand Up @@ -1051,7 +1136,7 @@ public void CompleteLibraryAnalyzers()
{
if (_maxApplicableVersion != null && _potentialAnalyzers?.Count > 0)
{
foreach (var (file, library, version) in _potentialAnalyzers)
foreach ((string file, LockFileTargetLibrary library, Version version) in _potentialAnalyzers)
{
if (version == _maxApplicableVersion)
{
Expand All @@ -1065,12 +1150,9 @@ public void CompleteLibraryAnalyzers()
_potentialAnalyzers?.Clear();
}

private void WriteAnalyzer(string file, LockFileLibrary library)
private void WriteAnalyzer(string file, LockFileTargetLibrary library)
{
if (TargetLibraries.TryGetValue((library.Name, library.Version), out var targetLibrary))
{
_cacheWriter.WriteItem(_cacheWriter._packageResolver.ResolvePackageAssetPath(targetLibrary, file), targetLibrary);
}
_cacheWriter.WriteItem(_cacheWriter._packageResolver.ResolvePackageAssetPath(library, file), library);
}

/// <summary>
Expand Down
Loading
Loading