Last active
April 17, 2021 21:35
-
-
Save jeffhandley/9600dd0b73e9562b58ed25ad32541a41 to your computer and use it in GitHub Desktop.
Scan dotnet/runtime artifacts for assembly-level Platform Attributes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
namespace Scanner | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
DirectoryInfo cwd = new(args.Length == 1 ? args[0] : Environment.CurrentDirectory); | |
IEnumerable<AssemblyInfo> assemblyInfos = GetAssemblyInfoFiles(cwd).ToArray(); | |
IEnumerable<LibraryAssemblyInfo> recognizedAssemblies = assemblyInfos.OfType<LibraryAssemblyInfo>(); | |
IEnumerable<AssemblyInfo> unrecognizedAssemblies = assemblyInfos.Except(recognizedAssemblies); | |
Console.WriteLine("# Platform Attributes by AssemblyInfo File"); | |
Console.WriteLine(); | |
if (recognizedAssemblies.Any()) | |
{ | |
Console.WriteLine("## Recognized AssemblyInfo Files"); | |
Console.WriteLine(); | |
Console.WriteLine("| Library | Build | src | ref |"); | |
Console.WriteLine("|---------|-------|-----|-----|"); | |
var libraries = recognizedAssemblies.GroupBy(a => (a.Library, a.Build)).OrderBy(l => l.Key.Library).ThenBy(l => l.Key.Build); | |
foreach (var library in libraries) | |
{ | |
string srcPlatforms = FormatPlatforms(library.SingleOrDefault(l => !l.IsRefAssembly)?.PlatformAttributes); | |
string refPlatforms = FormatPlatforms(library.SingleOrDefault(l => l.IsRefAssembly)?.PlatformAttributes); | |
Console.WriteLine($"| {library.Key.Library} | {library.Key.Build} | {srcPlatforms} | {refPlatforms} |"); | |
} | |
} | |
if (unrecognizedAssemblies.Any(a => a.PlatformAttributes.Any())) | |
{ | |
if (recognizedAssemblies.Any()) | |
{ | |
Console.WriteLine(); | |
} | |
Console.WriteLine("## Unrecognized AssemblyInfo Files"); | |
Console.WriteLine(); | |
Console.WriteLine("| File | Platforms |"); | |
Console.WriteLine("|------|-----------|"); | |
foreach (AssemblyInfo file in unrecognizedAssemblies.Where(a => a.PlatformAttributes.Any()).OrderBy(a => a.FileName)) | |
{ | |
string platforms = FormatPlatforms(file.PlatformAttributes); | |
Console.WriteLine($"| {file.FileName} | {platforms} |"); | |
} | |
} | |
static string FormatPlatforms(IEnumerable<PlatformAttribute> platformAttributes) | |
{ | |
if (platformAttributes is null) | |
{ | |
return "Not Found"; | |
} | |
if (!platformAttributes.Any()) | |
{ | |
return "None"; | |
} | |
var attributes = new List<string>(); | |
var supported = string.Join(',', platformAttributes.Where(a => a.IsSupported).Select(a => a.Platform)); | |
var unsupported = string.Join(',', platformAttributes.Where(a => !a.IsSupported).Select(a => a.Platform)); | |
if (supported.Length > 0) | |
{ | |
attributes.Add($"Supported: {supported}"); | |
} | |
if (unsupported.Length > 0) | |
{ | |
attributes.Add($"Unsupported: {unsupported}"); | |
} | |
return string.Join("; ", attributes); | |
} | |
} | |
static IEnumerable<AssemblyInfo> GetAssemblyInfoFiles(DirectoryInfo directory) | |
{ | |
IEnumerable<FileInfo> assemblyInfoFiles = directory.GetFiles("*AssemblyInfo*.cs", SearchOption.AllDirectories); | |
foreach (var file in assemblyInfoFiles) | |
{ | |
// ...\obj\<Library>\<Build>\<Library>.AssemblyInfo.cs | |
IEnumerable<string> folders = file.DirectoryName.Split('/', '\\'); | |
IEnumerable<string> foldersAfterObj = folders.SkipWhile(f => f != "obj").Skip(1); | |
IEnumerable<PlatformAttribute> platformAttributes = GetOSPlatformAttributes(file.FullName); | |
int folderCount = foldersAfterObj.Count(); | |
if (folderCount == 2 || (folderCount == 3 && foldersAfterObj.Skip(1).First() == "ref")) | |
{ | |
string library = foldersAfterObj.First(); | |
string build = foldersAfterObj.Last(); | |
bool isRefAssembly = folderCount == 3; | |
if (file.Name == $"{library}.AssemblyInfo.cs") | |
{ | |
yield return new LibraryAssemblyInfo(file.FullName, library, build, isRefAssembly, platformAttributes); | |
} | |
else | |
{ | |
yield return new AssemblyInfo(file.FullName, platformAttributes); | |
} | |
} | |
else | |
{ | |
yield return new AssemblyInfo(file.FullName, platformAttributes); | |
} | |
} | |
} | |
static IEnumerable<PlatformAttribute> GetOSPlatformAttributes(string fileName) | |
{ | |
string text = File.ReadAllText(fileName); | |
MatchCollection platformAttributes = Regex.Matches(text, ".*(?<support>Supported|Unsupported)OSPlatform(Attribute)?\\(\"(?<platform>[\\w\\-\\.]+)\"\\).*"); | |
foreach (Match attribute in platformAttributes) | |
{ | |
yield return new(attribute.Groups["support"].Value == "Supported", attribute.Groups["platform"].Value); | |
} | |
} | |
record PlatformAttribute(bool IsSupported, string Platform); | |
record AssemblyInfo(string FileName, IEnumerable<PlatformAttribute> PlatformAttributes); | |
record LibraryAssemblyInfo(string FileName, string Library, string Build, bool IsRefAssembly, IEnumerable<PlatformAttribute> PlatformAttributes) | |
: AssemblyInfo(FileName, PlatformAttributes); | |
record LibraryBuild(string Library, string Build, IEnumerable<PlatformAttribute> SrcPlatformAttributes, IEnumerable<PlatformAttribute> RefPlatformAttributes); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net6.0</TargetFramework> | |
</PropertyGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Microsoft Visual Studio Solution File, Format Version 12.00 | |
# Visual Studio Version 16 | |
VisualStudioVersion = 16.0.31025.218 | |
MinimumVisualStudioVersion = 10.0.40219.1 | |
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scanner", "Scanner.csproj", "{0DF1D90D-B3B9-4842-BA46-17258907F811}" | |
EndProject | |
Global | |
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |
Debug|Any CPU = Debug|Any CPU | |
Release|Any CPU = Release|Any CPU | |
EndGlobalSection | |
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |
{0DF1D90D-B3B9-4842-BA46-17258907F811}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |
{0DF1D90D-B3B9-4842-BA46-17258907F811}.Debug|Any CPU.Build.0 = Debug|Any CPU | |
{0DF1D90D-B3B9-4842-BA46-17258907F811}.Release|Any CPU.ActiveCfg = Release|Any CPU | |
{0DF1D90D-B3B9-4842-BA46-17258907F811}.Release|Any CPU.Build.0 = Release|Any CPU | |
EndGlobalSection | |
GlobalSection(SolutionProperties) = preSolution | |
HideSolutionNode = FALSE | |
EndGlobalSection | |
GlobalSection(ExtensibilityGlobals) = postSolution | |
SolutionGuid = {3193A84A-AA0F-40C2-BB53-F8B6F11F1A83} | |
EndGlobalSection | |
EndGlobal |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment