nuget-hash-extractor: Add support for handling nuget with multiple
[mono-project.git] / tools / nuget-hash-extractor / nuget-hash-extractor.cs
blobf43d5d7cc65b82088ea044d23c7a7c858dfa40a5
1 using System;
2 using System.Collections.Generic;
3 using System.Collections.Specialized;
4 using System.Xml.Linq;
5 using System.Linq;
6 using System.IO;
7 using System.IO.Compression;
8 using System.Reflection;
10 class Driver {
12 internal static Dictionary<string, string> BadAssembliesToEnumTable = new Dictionary<string, string> {
13 {"System.Runtime.InteropServices.RuntimeInformation.dll", "SYS_RT_INTEROP_RUNTIME_INFO"},
14 {"System.Globalization.Extensions.dll", "SYS_GLOBALIZATION_EXT"},
15 {"System.IO.Compression.dll", "SYS_IO_COMPRESSION"},
16 {"System.Net.Http.dll", "SYS_NET_HTTP"},
17 {"System.Text.Encoding.CodePages.dll", "SYS_TEXT_ENC_CODEPAGES"},
18 {"System.Reflection.DispatchProxy.dll", "SYS_REF_DISP_PROXY"},
19 {"System.Threading.Overlapped.dll", "SYS_THREADING_OVERLAPPED"}
22 static ZipArchiveEntry FindSpecFile (ZipArchive zip) {
23 foreach (var entry in zip.Entries) {
24 if (entry.Name.EndsWith (".nuspec"))
25 return entry;
27 throw new Exception ("Could not find nuspec file");
30 static void DumpNuget (string nupkg) {
31 var zip = new ZipArchive(new FileStream (nupkg, FileMode.Open));
33 var nuspec = FindSpecFile (zip);
34 var l = XElement.Load (new StreamReader (nuspec.Open ()));
35 var version = (from el in l.Descendants() where el.Name.LocalName == "version" select el.Value).FirstOrDefault ();
37 var prefixesToCheckInOrder = new string[] {
38 "lib/net4",
39 "lib/netstandard1",
40 "msbuildExtensions/Microsoft/Microsoft.NET.Build.Extensions/net4"
44 // Logic copied from https://github.com/NuGet/NuGet.Client/blob/4cccb13833ad29d6a0bcff055460d964f1b49cfe/src/NuGet.Core/NuGet.Frameworks/DefaultFrameworkMappings.cs#L385
46 IEnumerable<ZipArchiveEntry> entries = null;
47 foreach (var prefix in prefixesToCheckInOrder) {
48 entries = zip.Entries.Where (e => e.FullName.StartsWith (prefix) && e.Name.EndsWith (".dll") && BadAssembliesToEnumTable.ContainsKey (e.Name));
49 if (entries.Any ())
50 break;
53 if (!entries.Any ()) {
54 Console.Error.WriteLine ($"** Warning: No relevant assemblies found for nukpkg: {nupkg}");
55 return;
58 // Only take assemblies from first prefix
59 foreach (var et in entries) {
60 LoadAndDump (et, version);
64 static bool dump_asm, dump_ver, dump_guids_for_msbuild;
65 static void Main (string[] args) {
67 if (args.Length > 1) {
68 dump_asm = args [1].Equals ("asm");
69 dump_ver = args [1].Equals ("ver");
70 dump_guids_for_msbuild = args [1].Equals ("guids_for_msbuild");
71 } else {
72 dump_asm = true;
74 foreach (var f in Directory.GetFiles (args [0], "*.nupkg")) {
75 DumpNuget (f);
79 static byte[] StreamToArray (Stream s) {
80 using(var ms = new MemoryStream ()) {
81 s.CopyTo (ms);
82 return ms.ToArray ();
86 static int domain_id = 1;
87 static void LoadAndDump (ZipArchiveEntry entry, string version) {
88 var data = StreamToArray (entry.Open ());
89 AppDomain ad = AppDomain.CreateDomain ("parse_" + ++domain_id);
90 DoParse p = (DoParse)ad.CreateInstanceAndUnwrap (typeof (DoParse).Assembly.FullName, typeof (DoParse).FullName);
91 p.ParseAssembly (data, version, entry.Name, entry.FullName, dump_asm, dump_ver, dump_guids_for_msbuild);
92 AppDomain.Unload (ad);
96 class DoParse : MarshalByRefObject {
98 static int Hash (string str) {
99 int h = 5381;
100 for (int i = 0; i < str.Length; ++i)
101 h = ((h << 5) + h) ^ str[i];
102 return h;
105 static string FileToMoniker (string path) =>
106 path.Split (Path.DirectorySeparatorChar)
107 .Where (p => p.StartsWith ("net"))
108 .FirstOrDefault ()
109 ?? "unknown";
111 static string FileToEnum (string name) =>
112 Driver.BadAssembliesToEnumTable.ContainsKey (name)
113 ? Driver.BadAssembliesToEnumTable [name]
114 : throw new Exception ($"No idea what to do with {name}");
116 public void ParseAssembly (byte[] data, string version, string name, string fullname, bool dump_asm, bool dump_ver, bool dump_guids_for_msbuild) {
117 var a = Assembly.ReflectionOnlyLoad (data);
118 var m = a.GetModules ()[0];
119 var id = m.ModuleVersionId.ToString ().ToUpper ();
120 var hash_code = Hash (id).ToString ("X");
121 var str = FileToEnum (name);
123 string ver_str = version + " " + FileToMoniker (fullname);
125 if (dump_asm)
126 Console.WriteLine ($"IGNORED_ASSEMBLY (0x{hash_code}, {str}, \"{id}\", \"{ver_str}\"),");
128 //IGNORED_ASM_VER (SYS_IO_COMPRESSION, 4, 1, 2, 0),
129 var ver = a.GetName ().Version;
130 if (dump_ver) {
131 Console.WriteLine ($"IGNORED_ASM_VER ({str}, {ver.Major}, {ver.Minor}, {ver.Build}, {ver.Revision}),");
132 } else if (dump_guids_for_msbuild) {
133 // This needs to be kept in sync with FilterDeniedAssemblies msbuild task in msbuild
134 Console.WriteLine ($"{name},{id},{ver.Major},{ver.Minor},{ver.Build},{ver.Revision}");