public static int Main(string[] args) { const int expectedArguments = 9; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Expected {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } var outputDir = args[0]; var packageName = args[1]; string targetsFileDir = args[2]; string targetsFileName = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var binDirectory = args[5]; var configuration = args[6]; var tfm = args[7]; var releaseTrackingOptOutString = args[8]; if (!bool.TryParse(releaseTrackingOptOutString, out bool releaseTrackingOptOut)) { releaseTrackingOptOut = false; } using var shippedFilesDataBuilder = ArrayBuilder <ReleaseTrackingData> .GetInstance(); using var versionsBuilder = PooledHashSet <Version> .GetInstance(); // Validate all assemblies exist on disk and can be loaded. foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); if (!File.Exists(assemblyPath)) { Console.Error.WriteLine($"'{assemblyPath}' does not exist"); return(2); } try { _ = Assembly.LoadFrom(assemblyPath); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(3); } } // Compute descriptors by rule ID and shipped analyzer release versions and shipped data. var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var sawShippedFile = false; foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); var analyzerFileReference = new AnalyzerFileReference(assemblyPath, AnalyzerAssemblyLoader.Instance); analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); foreach (var analyzer in analyzers) { foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; } } var assemblyDir = Path.GetDirectoryName(assemblyPath); if (assemblyDir is null) { continue; } var assemblyName = Path.GetFileNameWithoutExtension(assembly); var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName); if (File.Exists(shippedFile)) { sawShippedFile = true; if (releaseTrackingOptOut) { Console.Error.WriteLine($"'{shippedFile}' exists but was not expected"); return(4); } try { using var fileStream = File.OpenRead(shippedFile); var sourceText = SourceText.From(fileStream); var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText, onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new Exception($"Duplicate entry in {shippedFile} at {line.LineNumber}: '{line}'"), onInvalidEntry: (line, _2, _3, _4) => throw new Exception($"Invalid entry in {shippedFile} at {line.LineNumber}: '{line}'"), isShippedFile: true); shippedFilesDataBuilder.Add(releaseTrackingData); versionsBuilder.AddRange(releaseTrackingData.Versions); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(5); } } } if (!releaseTrackingOptOut && !sawShippedFile) { Console.Error.WriteLine($"Could not find any 'AnalyzerReleases.Shipped.md' file"); return(6); } if (versionsBuilder.Count > 0) { var shippedFilesData = shippedFilesDataBuilder.ToImmutable(); // Generate global analyzer config files for each shipped version, if required. foreach (var version in versionsBuilder) { var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix(version); foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode))) { CreateEditorconfig( outputDir, $"AnalysisLevel_{analysisLevelVersionString}_{analysisMode}.editorconfig", $"Rules from '{version}' release with '{analysisMode}' analysis mode", $"Rules with enabled-by-default state from '{version}' release with '{analysisMode}' analysis mode. Rules that are first released in a version later than '{version}' are disabled.", (AnalysisMode)analysisMode !, allRulesById, (shippedFilesData, version)); } } } CreateTargetsFile(targetsFileDir, targetsFileName, packageName); return(0);
public static int Main(string[] args) { const int expectedArguments = 9; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Expected {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } var outputDir = args[0]; var packageName = args[1]; string targetsFileDir = args[2]; string targetsFileName = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var binDirectory = args[5]; var configuration = args[6]; var tfm = args[7]; var releaseTrackingOptOutString = args[8]; if (!bool.TryParse(releaseTrackingOptOutString, out bool releaseTrackingOptOut)) { releaseTrackingOptOut = false; } using var shippedFilesDataBuilder = ArrayBuilder <ReleaseTrackingData> .GetInstance(); using var versionsBuilder = PooledHashSet <Version> .GetInstance(); // Validate all assemblies exist on disk and can be loaded. foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); if (!File.Exists(assemblyPath)) { Console.Error.WriteLine($"'{assemblyPath}' does not exist"); return(2); } try { _ = Assembly.LoadFrom(assemblyPath); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(3); } } // Compute descriptors by rule ID and shipped analyzer release versions and shipped data. var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var hasInfoOrHiddenDiagnostic = false; var sawShippedFile = false; foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); var analyzerFileReference = new AnalyzerFileReference(assemblyPath, AnalyzerAssemblyLoader.Instance); analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); foreach (var analyzer in analyzers) { foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; hasInfoOrHiddenDiagnostic = hasInfoOrHiddenDiagnostic || rule.IsEnabledByDefault && (rule.DefaultSeverity == DiagnosticSeverity.Info || rule.DefaultSeverity == DiagnosticSeverity.Hidden); } } var assemblyDir = Path.GetDirectoryName(assemblyPath); var assemblyName = Path.GetFileNameWithoutExtension(assembly); var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName); if (File.Exists(shippedFile)) { sawShippedFile = true; if (releaseTrackingOptOut) { Console.Error.WriteLine($"'{shippedFile}' exists but was not expected"); return(4); } try { using var fileStream = File.OpenRead(shippedFile); var sourceText = SourceText.From(fileStream); var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText, onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new Exception($"Duplicate entry in {shippedFile} at {line.LineNumber}: '{line}'"), onInvalidEntry: (line, _2, _3, _4) => throw new Exception($"Invalid entry in {shippedFile} at {line.LineNumber}: '{line}'"), isShippedFile: true); shippedFilesDataBuilder.Add(releaseTrackingData); versionsBuilder.AddRange(releaseTrackingData.Versions); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(5); } } } if (!releaseTrackingOptOut && !sawShippedFile) { Console.Error.WriteLine($"Could not find any 'AnalyzerReleases.Shipped.md' file"); return(6); } // Bail out if following conditions hold for the analyzer package: // 1. No Info/Hidden diagnostic in the package: No need for have different global analyzer config for build and live analysis. // 2. No shipped releases: User cannot choose a version specific global analyzer config. if (!hasInfoOrHiddenDiagnostic && versionsBuilder.Count == 0) { return(0); } var shippedFilesData = shippedFilesDataBuilder.ToImmutable(); // Generate build and live analysis global analyzer config files for latest/unshipped version. CreateGlobalAnalyzerConfig( "BuildRules", "All build rules with default severity", "All build rules (warnings/errors) with default severity. Rules with IsEnabledByDefault = false or default severity Suggestion/Hidden are disabled.", EditorConfigKind.CommandLine); CreateGlobalAnalyzerConfig( "LiveAnalysisRules", "All rules with default severity", "All rules are enabled with default severity. Rules with IsEnabledByDefault = false are disabled.", EditorConfigKind.LiveAnalysis); // Generate build and live analysis global analyzer config files for each shipped version. foreach (var version in versionsBuilder) { var versionString = version.ToString().Replace(".", "_", StringComparison.Ordinal); CreateGlobalAnalyzerConfig( $"BuildRulesVersion{versionString}", $"All '{version}' build rules with default severity", $"All '{version}' build rules (warnings/errors) with default severity. Rules with IsEnabledByDefault = false or first released in a version later then {version} or default severity Suggestion/Hidden are disabled.", EditorConfigKind.CommandLine, (shippedFilesData, version)); CreateGlobalAnalyzerConfig( $"LiveAnalysisRules{versionString}", $"All '{version}' rules with default severity", $"All '{version}' rules are enabled with default severity. Rules with IsEnabledByDefault = false or first released in a version later then {version} are disabled.", EditorConfigKind.LiveAnalysis, (shippedFilesData, version)); } CreateTargetsFile(targetsFileDir, targetsFileName, packageName); return(0);