Skip to content

Instantly share code, notes, and snippets.

@jeffhandley
Last active April 17, 2021 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffhandley/9600dd0b73e9562b58ed25ad32541a41 to your computer and use it in GitHub Desktop.
Save jeffhandley/9600dd0b73e9562b58ed25ad32541a41 to your computer and use it in GitHub Desktop.
Scan dotnet/runtime artifacts for assembly-level Platform Attributes
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);
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
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