Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance GenerateDocumentationAndConfigFiles tool to generate vNext globalconfig files #6258

Merged
merged 3 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
<!-- Unshipped release -->
<ItemGroup Condition="'$(ReleaseTrackingOptOut)' != 'true' AND Exists('$(MSBuildProjectDirectory)\AnalyzerReleases.Unshipped.md')">
<AdditionalFiles Include="$(MSBuildProjectDirectory)\AnalyzerReleases.Unshipped.md" />
<!-- Copy the unshipped releases file to output directory so it can be used in 'GenerateGlobalAnalyzerConfigs' post-build target -->
<!-- Include unshipped file also as 'None' - Workaround for 'CopyToOutputDirectory' not being respected for additional files -->
<None Update="AnalyzerReleases.Unshipped.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>AnalyzerReleases\$(AssemblyName)\AnalyzerReleases.Unshipped.md</Link>
</None>
</ItemGroup>
<!-- Shipped releases -->
<ItemGroup Condition="'$(ReleaseTrackingOptOut)' != 'true' AND Exists('$(MSBuildProjectDirectory)\AnalyzerReleases.Shipped.md')">
Expand Down
74 changes: 52 additions & 22 deletions src/Tools/GenerateDocumentationAndConfigFiles/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ async Task<bool> checkHelpLinkAsync(string helpLink)
async Task<bool> createGlobalConfigFilesAsync()
{
using var shippedFilesDataBuilder = ArrayBuilder<ReleaseTrackingData>.GetInstance();
using var unshippedFilesDataBuilder = ArrayBuilder<ReleaseTrackingData>.GetInstance();
using var versionsBuilder = PooledHashSet<Version>.GetInstance();

// Validate all assemblies exist on disk and can be loaded.
Expand Down Expand Up @@ -705,7 +706,8 @@ async Task<bool> createGlobalConfigFilesAsync()

var assemblyName = Path.GetFileNameWithoutExtension(assembly);
var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName);
if (File.Exists(shippedFile))
var unshippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.UnshippedFileName);
if (File.Exists(shippedFile) && File.Exists(unshippedFile))
{
sawShippedFile = true;

Expand All @@ -717,6 +719,7 @@ async Task<bool> createGlobalConfigFilesAsync()

try
{
// Read shipped file
using var fileStream = File.OpenRead(shippedFile);
var sourceText = SourceText.From(fileStream);
var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText,
Expand All @@ -725,6 +728,15 @@ async Task<bool> createGlobalConfigFilesAsync()
isShippedFile: true);
shippedFilesDataBuilder.Add(releaseTrackingData);
versionsBuilder.AddRange(releaseTrackingData.Versions);

// Read unshipped file
using var fileStreamUnshipped = File.OpenRead(unshippedFile);
var sourceTextUnshipped = SourceText.From(fileStreamUnshipped);
var releaseTrackingDataUnshipped = ReleaseTrackingHelper.ReadReleaseTrackingData(unshippedFile, sourceTextUnshipped,
onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new Exception($"Duplicate entry in {unshippedFile} at {line.LineNumber}: '{line}'"),
onInvalidEntry: (line, _2, _3, _4) => throw new Exception($"Invalid entry in {unshippedFile} at {line.LineNumber}: '{line}'"),
isShippedFile: false);
unshippedFilesDataBuilder.Add(releaseTrackingDataUnshipped);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
Expand All @@ -744,33 +756,49 @@ async Task<bool> createGlobalConfigFilesAsync()

if (versionsBuilder.Count > 0)
{
var shippedFilesData = shippedFilesDataBuilder.ToImmutable();
var releaseTrackingData = shippedFilesDataBuilder.Concat(unshippedFilesDataBuilder).ToImmutableArray();

// Generate global analyzer config files for each shipped version, if required.
// Generate global analyzer config files for each shipped version.
foreach (var version in versionsBuilder)
{
var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix(version);

foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode)))
{
CreateGlobalConfig(version, analysisLevelVersionString, (AnalysisMode)analysisMode!, shippedFilesData, category: null);
foreach (var category in categories)
{
CreateGlobalConfig(version, analysisLevelVersionString, (AnalysisMode)analysisMode!, shippedFilesData, category);
}
}
CreateGlobalConfigsForVersion(version, isShippedVersion: true, releaseTrackingData);
}

// Generate global analyzer config files for unshipped version.
// See https://github.com/dotnet/roslyn-analyzers/issues/6247 for details.

// Use 'unshippedVersion = maxShippedVersion + 1' for unshipped data.
var maxShippedVersion = versionsBuilder.Max();
var unshippedVersion = new Version(maxShippedVersion!.Major + 1, maxShippedVersion.Minor);
CreateGlobalConfigsForVersion(unshippedVersion, isShippedVersion: false, releaseTrackingData);
}

return true;

// Local functions.
void CreateGlobalConfigsForVersion(
Version version,
bool isShippedVersion,
ImmutableArray<ReleaseTrackingData> releaseTrackingData)
{
var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix(version);

foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode)))
{
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, releaseTrackingData, category: null);
foreach (var category in categories!)
{
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, releaseTrackingData, category);
}
}
}

void CreateGlobalConfig(
Version version,
bool isShippedVersion,
string analysisLevelVersionString,
AnalysisMode analysisMode,
ImmutableArray<ReleaseTrackingData> shippedFilesData,
ImmutableArray<ReleaseTrackingData> releaseTrackingData,
string? category)
{
var analysisLevelPropName = "AnalysisLevel";
Expand All @@ -793,7 +821,7 @@ void CreateGlobalConfig(
analysisMode,
category,
allRulesById,
(shippedFilesData, version));
(releaseTrackingData, version, isShippedVersion));
}

static string GetNormalizedVersionStringForEditorconfigFileNameSuffix(Version version)
Expand Down Expand Up @@ -1125,7 +1153,7 @@ private static void CreateGlobalconfig(
AnalysisMode analysisMode,
string? category,
SortedList<string, DiagnosticDescriptor> sortedRulesById,
(ImmutableArray<ReleaseTrackingData> shippedFiles, Version version) shippedReleaseData)
(ImmutableArray<ReleaseTrackingData> releaseTrackingData, Version version, bool isShippedVersion) releaseTrackingDataAndVersion)
{
Debug.Assert(editorconfigFileName.EndsWith(".editorconfig", StringComparison.Ordinal));

Expand All @@ -1135,7 +1163,7 @@ private static void CreateGlobalconfig(
analysisMode,
category,
sortedRulesById,
shippedReleaseData);
releaseTrackingDataAndVersion);
var directory = Directory.CreateDirectory(folder);
var editorconfigFilePath = Path.Combine(directory.FullName, editorconfigFileName.ToLowerInvariant());
File.WriteAllText(editorconfigFilePath, text);
Expand All @@ -1148,7 +1176,7 @@ static string GetGlobalconfigText(
AnalysisMode analysisMode,
string? category,
SortedList<string, DiagnosticDescriptor> sortedRulesById,
(ImmutableArray<ReleaseTrackingData> shippedFiles, Version version)? shippedReleaseData)
(ImmutableArray<ReleaseTrackingData> releaseTrackingData, Version version, bool isShippedVersion)? releaseTrackingDataAndVersion)
{
var result = new StringBuilder();
StartGlobalconfig();
Expand Down Expand Up @@ -1257,14 +1285,16 @@ bool AddRule(DiagnosticDescriptor rule, string? category)
effectiveSeverity = DiagnosticSeverity.Warning;
}

if (shippedReleaseData != null)
if (releaseTrackingDataAndVersion != null)
{
isEnabledByDefault = isEnabledRuleForNonDefaultAnalysisMode;
var maxVersion = shippedReleaseData.Value.version;
var maxVersion = releaseTrackingDataAndVersion.Value.isShippedVersion ?
releaseTrackingDataAndVersion.Value.version :
ReleaseTrackingHelper.UnshippedVersion;
var foundReleaseTrackingEntry = false;
foreach (var shippedFile in shippedReleaseData.Value.shippedFiles)
foreach (var releaseTrackingData in releaseTrackingDataAndVersion.Value.releaseTrackingData)
{
if (shippedFile.TryGetLatestReleaseTrackingLine(rule.Id, maxVersion, out _, out var releaseTrackingLine))
if (releaseTrackingData.TryGetLatestReleaseTrackingLine(rule.Id, maxVersion, out _, out var releaseTrackingLine))
{
foundReleaseTrackingEntry = true;

Expand Down