From 7d703514182273aa467ca8c3b005a0251a4a3588 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Tue, 13 Mar 2018 13:16:59 -0700 Subject: [PATCH] Add linker to wasm build task --- .gitmodules | 3 + Ooui.Wasm.Build.Tasks/BuildDistTask.cs | 127 +++++++++++++++--- .../Ooui.Wasm.Build.Tasks.csproj | 8 ++ Ooui.Wasm.Build.Tasks/linker | 1 + Ooui/PreserveAttribute.cs | 8 ++ Ooui/UI.cs | 3 + 6 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 160000 Ooui.Wasm.Build.Tasks/linker create mode 100644 Ooui/PreserveAttribute.cs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fa91679 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Ooui.Wasm.Build.Tasks/linker"] + path = Ooui.Wasm.Build.Tasks/linker + url = git@github.com:mono/linker.git diff --git a/Ooui.Wasm.Build.Tasks/BuildDistTask.cs b/Ooui.Wasm.Build.Tasks/BuildDistTask.cs index 7b60f30..a8f1e5b 100644 --- a/Ooui.Wasm.Build.Tasks/BuildDistTask.cs +++ b/Ooui.Wasm.Build.Tasks/BuildDistTask.cs @@ -9,6 +9,9 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using System.Text; using Mono.Cecil; +using System.Diagnostics; +using Mono.Linker; +using Mono.Linker.Steps; namespace Ooui.Wasm.Build.Tasks { @@ -82,9 +85,6 @@ namespace Ooui.Wasm.Build.Tasks distPath = Path.Combine (outputPath, "dist"); managedPath = Path.Combine (distPath, "managed"); Directory.CreateDirectory (managedPath); - //foreach (var dll in Directory.GetFiles (managedPath, "*.dll")) { - // File.Delete (dll); - //} } void CopyRuntime () @@ -99,35 +99,124 @@ namespace Ooui.Wasm.Build.Tasks } } - List linkedAsmNames; List linkedAsmPaths; void LinkAssemblies () { var references = ReferencePath.Split (';').Select (x => x.Trim ()).Where (x => x.Length > 0).ToList (); - references.Add (Path.GetFullPath (Assembly)); - - linkedAsmPaths = new List (); + var refpaths = new List (); foreach (var r in references) { var name = Path.GetFileName (r); if (bclAssemblies.ContainsKey (name)) { - linkedAsmPaths.Add (bclAssemblies[name]); + refpaths.Add (bclAssemblies[name]); } else { - linkedAsmPaths.Add (r); + refpaths.Add (r); } } - linkedAsmNames = new List (); - foreach (var l in linkedAsmPaths) { - var name = Path.GetFileName (l); - linkedAsmNames.Add (name); - var dest = Path.Combine (managedPath, name); - if (bclAssemblies.ContainsKey (name) && File.Exists (dest)) - continue; - File.Copy (l, dest, true); - Log.LogMessage ($"Managed {l} -> {dest}"); + var asmPath = Path.GetFullPath (Assembly); + + var pipeline = GetLinkerPipeline (); + using (var context = new LinkContext (pipeline)) { + context.CoreAction = AssemblyAction.CopyUsed; + context.UserAction = AssemblyAction.CopyUsed; + context.OutputDirectory = managedPath; + + pipeline.PrependStep (new ResolveFromAssemblyStep (asmPath, ResolveFromAssemblyStep.RootVisibility.Any)); + + var refdirs = refpaths.Select (x => Path.GetDirectoryName (x)).Distinct ().ToList (); + refdirs.Insert (0, Path.Combine (bclPath, "Facades")); + refdirs.Insert (0, bclPath); + foreach (var d in refdirs.Distinct ()) { + context.Resolver.AddSearchDirectory (d); + } + + pipeline.AddStepAfter (typeof (LoadReferencesStep), new LoadI18nAssemblies (I18nAssemblies.None)); + + foreach (var dll in Directory.GetFiles (managedPath, "*.dll")) { + File.Delete (dll); + } + pipeline.Process (context); } + + linkedAsmPaths = Directory.GetFiles (managedPath, "*.dll").OrderBy (x => Path.GetFileName (x)).ToList (); + } + + class PreserveUsingAttributesStep : ResolveStep + { + readonly HashSet ignoreAsmNames; + + public PreserveUsingAttributesStep (IEnumerable ignoreAsmNames) + { + this.ignoreAsmNames = new HashSet (ignoreAsmNames); + } + + protected override void Process () + { + var asms = Context.GetAssemblies (); + foreach (var a in asms.Where (x => !ignoreAsmNames.Contains (x.Name.Name))) { + foreach (var m in a.Modules) { + foreach (var t in m.Types) { + PreserveTypeIfRequested (t); + } + } + } + } + + void PreserveTypeIfRequested (TypeDefinition type) + { + var typePreserved = IsTypePreserved (type); + if (IsTypePreserved (type)) { + MarkAndPreserveAll (type); + } + else { + foreach (var m in type.Methods.Where (IsMethodPreserved)) { + Annotations.AddPreservedMethod (type, m); + } + foreach (var t in type.NestedTypes) { + PreserveTypeIfRequested (t); + } + } + } + + static bool IsTypePreserved (TypeDefinition m) + { + return m.CustomAttributes.FirstOrDefault (x => x.AttributeType.Name.StartsWith ("Preserve", StringComparison.Ordinal)) != null; + } + + static bool IsMethodPreserved (MethodDefinition m) + { + return m.CustomAttributes.FirstOrDefault (x => x.AttributeType.Name.StartsWith ("Preserve", StringComparison.Ordinal)) != null; + } + + void MarkAndPreserveAll (TypeDefinition type) + { + Annotations.MarkAndPush (type); + Annotations.SetPreserve (type, TypePreserve.All); + if (!type.HasNestedTypes) { + Tracer.Pop (); + return; + } + foreach (TypeDefinition nested in type.NestedTypes) + MarkAndPreserveAll (nested); + Tracer.Pop (); + } + } + + Pipeline GetLinkerPipeline () + { + var p = new Pipeline (); + p.AppendStep (new LoadReferencesStep ()); + p.AppendStep (new PreserveUsingAttributesStep (bclAssemblies.Values.Select (Path.GetFileNameWithoutExtension))); + p.AppendStep (new BlacklistStep ()); + p.AppendStep (new TypeMapStep ()); + p.AppendStep (new MarkStep ()); + p.AppendStep (new SweepStep ()); + p.AppendStep (new CleanStep ()); + p.AppendStep (new RegenerateGuidStep ()); + p.AppendStep (new OutputStep ()); + return p; } void ExtractClientJs () @@ -182,7 +271,7 @@ namespace Ooui.Wasm.Build.Tasks