4 using System
.Collections
.Generic
;
10 // Google V8 style options:
11 // - bool: --foo/--no-foo
18 // 'Option' is already used by Mono.Options
20 public Flag (string name
, string desc
, FlagType type
) {
30 public FlagType FlagType
{
34 public string Description
{
39 class BoolFlag
: Flag
{
40 public BoolFlag (string name
, string description
, bool def_value
, Action
<bool> action
) : base (name
, description
, FlagType
.BoolFlag
) {
42 DefaultValue
= def_value
;
45 public Action
<bool> Setter
{
49 public bool DefaultValue
{
55 static bool enable_debug
, enable_linker
;
56 static string app_prefix
, framework_prefix
, bcl_prefix
, bcl_tools_prefix
, bcl_facades_prefix
, out_prefix
;
57 static HashSet
<string> asm_map
= new HashSet
<string> ();
58 static List
<string> file_list
= new List
<string> ();
59 static HashSet
<string> assemblies_with_dbg_info
= new HashSet
<string> ();
60 static List
<string> root_search_paths
= new List
<string>();
62 const string BINDINGS_ASM_NAME
= "WebAssembly.Bindings";
63 const string BINDINGS_RUNTIME_CLASS_NAME
= "WebAssembly.Runtime";
64 const string HTTP_ASM_NAME
= "WebAssembly.Net.Http";
65 const string WEBSOCKETS_ASM_NAME
= "WebAssembly.Net.WebSockets";
66 const string BINDINGS_MODULE
= "corebindings.o";
67 const string BINDINGS_MODULE_SUPPORT
= "$tool_prefix/binding_support.js";
73 public string filename
;
74 // Path outside build tree
75 public string src_path
;
77 public string bc_path
;
79 public string app_path
;
81 public string linkin_path
;
83 public string linkout_path
;
84 // Finaly output path after IL strip
85 public string final_path
;
86 // Whenever to AOT this assembly
90 static List
<AssemblyData
> assemblies
= new List
<AssemblyData
> ();
99 void AddFlag (OptionSet options
, Flag flag
) {
100 if (flag
is BoolFlag
) {
101 options
.Add (flag
.Name
, s
=> (flag
as BoolFlag
).Setter (true));
102 options
.Add ("no-" + flag
.Name
, s
=> (flag
as BoolFlag
).Setter (false));
104 option_list
.Add (flag
);
107 static List
<Flag
> option_list
= new List
<Flag
> ();
109 static void Usage () {
110 Console
.WriteLine ("Usage: packager.exe <options> <assemblies>");
111 Console
.WriteLine ("Valid options:");
112 Console
.WriteLine ("\t--help Show this help message");
113 Console
.WriteLine ("\t--debugrt Use the debug runtime (default release) - this has nothing to do with C# debugging");
114 Console
.WriteLine ("\t--aot Enable AOT mode");
115 Console
.WriteLine ("\t--aot-interp Enable AOT+INTERP mode");
116 Console
.WriteLine ("\t--prefix=x Set the input assembly prefix to 'x' (default to the current directory)");
117 Console
.WriteLine ("\t--out=x Set the output directory to 'x' (default to the current directory)");
118 Console
.WriteLine ("\t--mono-sdkdir=x Set the mono sdk directory to 'x'");
119 Console
.WriteLine ("\t--deploy=x Set the deploy prefix to 'x' (default to 'managed')");
120 Console
.WriteLine ("\t--vfs=x Set the VFS prefix to 'x' (default to 'managed')");
121 Console
.WriteLine ("\t--template=x Set the template name to 'x' (default to 'runtime.js')");
122 Console
.WriteLine ("\t--asset=x Add specified asset 'x' to list of assets to be copied");
123 Console
.WriteLine ("\t--search-path=x Add specified path 'x' to list of paths used to resolve assemblies");
124 Console
.WriteLine ("\t--copy=always|ifnewer Set the type of copy to perform.");
125 Console
.WriteLine ("\t\t 'always' overwrites the file if it exists.");
126 Console
.WriteLine ("\t\t 'ifnewer' copies or overwrites the file if modified or size is different.");
127 Console
.WriteLine ("\t--profile=x Enable the 'x' mono profiler.");
128 Console
.WriteLine ("\t--aot-assemblies=x List of assemblies to AOT in AOT+INTERP mode.");
129 Console
.WriteLine ("\t--link-mode=sdkonly|all Set the link type used for AOT. (EXPERIMENTAL)");
130 Console
.WriteLine ("\t--pinvoke-libs=x DllImport libraries used.");
131 Console
.WriteLine ("\t\t 'sdkonly' only link the Core libraries.");
132 Console
.WriteLine ("\t\t 'all' link Core and User assemblies. (default)");
134 Console
.WriteLine ("foo.dll Include foo.dll as one of the root assemblies");
135 Console
.WriteLine ();
137 Console
.WriteLine ("Additional options (--option/--no-option):");
138 foreach (var flag
in option_list
) {
139 if (flag
is BoolFlag
) {
140 Console
.WriteLine (" --" + flag
.Name
+ " (" + flag
.Description
+ ")");
141 Console
.WriteLine (" type: bool default: " + ((flag
as BoolFlag
).DefaultValue
? "true" : "false"));
146 static void Debug (string s
) {
147 Console
.WriteLine (s
);
150 static string FindFrameworkAssembly (string asm
) {
154 static bool Try (string prefix
, string name
, out string out_res
) {
157 string res
= (Path
.Combine (prefix
, name
));
158 if (File
.Exists (res
)) {
159 out_res
= Path
.GetFullPath (res
);
165 static string ResolveWithExtension (string prefix
, string name
) {
168 if (Try (prefix
, name
, out res
))
170 if (Try (prefix
, name
+ ".dll", out res
))
172 if (Try (prefix
, name
+ ".exe", out res
))
177 static string ResolveUser (string asm_name
) {
178 return ResolveWithExtension (app_prefix
, asm_name
);
181 static string ResolveFramework (string asm_name
) {
182 return ResolveWithExtension (framework_prefix
, asm_name
);
185 static string ResolveBcl (string asm_name
) {
186 return ResolveWithExtension (bcl_prefix
, asm_name
);
189 static string ResolveBclFacade (string asm_name
) {
190 return ResolveWithExtension (bcl_facades_prefix
, asm_name
);
193 static string Resolve (string asm_name
, out AssemblyKind kind
) {
194 kind
= AssemblyKind
.User
;
195 var asm
= ResolveUser (asm_name
);
199 kind
= AssemblyKind
.Framework
;
200 asm
= ResolveFramework (asm_name
);
204 kind
= AssemblyKind
.Bcl
;
205 asm
= ResolveBcl (asm_name
);
207 asm
= ResolveBclFacade (asm_name
);
211 kind
= AssemblyKind
.None
;
212 throw new Exception ($"Could not resolve {asm_name}");
215 static void Import (string ra
, AssemblyKind kind
) {
216 if (!asm_map
.Add (ra
))
218 ReaderParameters rp
= new ReaderParameters();
219 bool add_pdb
= enable_debug
&& File
.Exists (Path
.ChangeExtension (ra
, "pdb"));
221 rp
.ReadSymbols
= true;
222 // Facades do not have symbols
223 rp
.ThrowIfSymbolsAreNotMatching
= false;
224 rp
.SymbolReaderProvider
= new DefaultSymbolReaderProvider(false);
227 var resolver
= new DefaultAssemblyResolver();
228 root_search_paths
.ForEach(resolver
.AddSearchDirectory
);
229 resolver
.AddSearchDirectory(bcl_facades_prefix
);
230 resolver
.AddSearchDirectory(bcl_prefix
);
231 resolver
.AddSearchDirectory(framework_prefix
);
232 rp
.AssemblyResolver
= resolver
;
235 var image
= ModuleDefinition
.ReadModule (ra
, rp
);
237 //Debug ($"Processing {ra} debug {add_pdb}");
239 var data
= new AssemblyData () { name = image.Assembly.Name.Name, src_path = ra }
;
240 assemblies
.Add (data
);
242 if (add_pdb
&& (kind
== AssemblyKind
.User
|| kind
== AssemblyKind
.Framework
)) {
243 file_list
.Add (Path
.ChangeExtension (ra
, "pdb"));
244 assemblies_with_dbg_info
.Add (Path
.ChangeExtension (ra
, "pdb"));
247 foreach (var ar
in image
.AssemblyReferences
) {
248 // Resolve using root search paths first
249 var resolved
= image
.AssemblyResolver
.Resolve(ar
, rp
);
251 var searchName
= resolved
?.MainModule
.FileName
?? ar
.Name
;
253 var resolve
= Resolve(searchName
, out kind
);
254 Import(resolve
, kind
);
258 void GenDriver (string builddir
, List
<string> profilers
, ExecMode ee_mode
, bool link_icalls
) {
259 var symbols
= new List
<string> ();
260 foreach (var adata
in assemblies
) {
262 symbols
.Add (String
.Format ("mono_aot_module_{0}_info", adata
.name
.Replace ('.', '_').Replace ('-', '_')));
265 var w
= File
.CreateText (Path
.Combine (builddir
, "driver-gen.c.in"));
267 foreach (var symbol
in symbols
) {
268 w
.WriteLine ($"extern void *{symbol};");
271 w
.WriteLine ("static void register_aot_modules ()");
273 foreach (var symbol
in symbols
)
274 w
.WriteLine ($"\tmono_aot_register_module ({symbol});");
277 foreach (var profiler
in profilers
) {
278 w
.WriteLine ($"void mono_profiler_init_{profiler} (const char *desc);");
279 w
.WriteLine ("EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_" + profiler
+ " (const char *desc) { mono_profiler_init_" + profiler + " (desc); }");
283 case ExecMode
.AotInterp
:
284 w
.WriteLine ("#define EE_MODE_LLVMONLY_INTERP 1");
287 w
.WriteLine ("#define EE_MODE_LLVMONLY 1");
294 w
.WriteLine ("#define LINK_ICALLS 1");
299 public static int Main (string[] args
) {
300 return new Driver ().Run (args
);
324 public bool DebugRuntime
;
325 public bool AddBinding
;
327 public bool LinkIcalls
;
329 public bool LinkerVerbose
;
332 int Run (string[] args
) {
333 var add_binding
= true;
334 var root_assemblies
= new List
<string> ();
335 enable_debug
= false;
336 string builddir
= null;
337 string sdkdir
= null;
338 string emscripten_sdkdir
= null;
339 var aot_assemblies
= "";
340 out_prefix
= Environment
.CurrentDirectory
;
341 app_prefix
= Environment
.CurrentDirectory
;
342 var deploy_prefix
= "managed";
343 var vfs_prefix
= "managed";
344 var use_release_runtime
= true;
345 var enable_aot
= false;
346 var enable_dedup
= true;
347 var print_usage
= false;
348 var emit_ninja
= false;
349 bool build_wasm
= false;
350 bool enable_lto
= false;
351 bool link_icalls
= false;
352 bool gen_pinvoke
= false;
353 var il_strip
= false;
354 var runtimeTemplate
= "runtime.js";
355 var assets
= new List
<string> ();
356 var profilers
= new List
<string> ();
357 var pinvoke_libs
= "";
358 var copyTypeParm
= "default";
359 var copyType
= CopyType
.Default
;
360 var ee_mode
= ExecMode
.Interp
;
361 var linkModeParm
= "all";
362 var linkMode
= LinkMode
.All
;
363 string coremode
, usermode
;
364 var linker_verbose
= false;
366 var opts
= new WasmOptions () {
369 DebugRuntime
= false,
372 LinkerVerbose
= false
375 var p
= new OptionSet () {
376 { "nobinding", s => opts.AddBinding = false }
,
377 { "out=", s => out_prefix = s }
,
378 { "appdir=", s => out_prefix = s }
,
379 { "builddir=", s => builddir = s }
,
380 { "mono-sdkdir=", s => sdkdir = s }
,
381 { "emscripten-sdkdir=", s => emscripten_sdkdir = s }
,
382 { "prefix=", s => app_prefix = s }
,
383 { "deploy=", s => deploy_prefix = s }
,
384 { "vfs=", s => vfs_prefix = s }
,
385 { "aot", s => ee_mode = ExecMode.Aot }
,
386 { "aot-interp", s => ee_mode = ExecMode.AotInterp }
,
387 { "template=", s => runtimeTemplate = s }
,
388 { "asset=", s => assets.Add(s) }
,
389 { "search-path=", s => root_search_paths.Add(s) }
,
390 { "profile=", s => profilers.Add (s) }
,
391 { "copy=", s => copyTypeParm = s }
,
392 { "aot-assemblies=", s => aot_assemblies = s }
,
393 { "link-mode=", s => linkModeParm = s }
,
394 { "pinvoke-libs=", s => pinvoke_libs = s }
,
395 { "help", s => print_usage = true }
,
398 AddFlag (p
, new BoolFlag ("debug", "enable c# debugging", opts
.Debug
, b
=> opts
.Debug
= b
));
399 AddFlag (p
, new BoolFlag ("debugrt", "enable debug runtime", opts
.DebugRuntime
, b
=> opts
.DebugRuntime
= b
));
400 AddFlag (p
, new BoolFlag ("linker", "enable the linker", opts
.Linker
, b
=> opts
.Linker
= b
));
401 AddFlag (p
, new BoolFlag ("binding", "enable the binding engine", opts
.AddBinding
, b
=> opts
.AddBinding
= b
));
402 AddFlag (p
, new BoolFlag ("link-icalls", "link away unused icalls", opts
.LinkIcalls
, b
=> opts
.LinkIcalls
= b
));
403 AddFlag (p
, new BoolFlag ("il-strip", "strip IL code from assemblies in AOT mode", opts
.ILStrip
, b
=> opts
.ILStrip
= b
));
404 AddFlag (p
, new BoolFlag ("linker-verbose", "set verbose option on linker", opts
.LinkerVerbose
, b
=> opts
.LinkerVerbose
= b
));
406 var new_args
= p
.Parse (args
).ToArray ();
407 foreach (var a
in new_args
) {
408 root_assemblies
.Add (a
);
416 if (!Enum
.TryParse(copyTypeParm
, true, out copyType
)) {
417 Console
.WriteLine("Invalid copy value");
422 if (!Enum
.TryParse(linkModeParm
, true, out linkMode
)) {
423 Console
.WriteLine("Invalid link-mode value");
428 enable_debug
= opts
.Debug
;
429 enable_linker
= opts
.Linker
;
430 add_binding
= opts
.AddBinding
;
431 use_release_runtime
= !opts
.DebugRuntime
;
432 il_strip
= opts
.ILStrip
;
433 linker_verbose
= opts
.LinkerVerbose
;
434 gen_pinvoke
= pinvoke_libs
!= "";
436 if (ee_mode
== ExecMode
.Aot
|| ee_mode
== ExecMode
.AotInterp
)
439 if (enable_aot
|| opts
.Linker
)
440 enable_linker
= true;
443 if (!enable_linker
|| !enable_aot
)
444 enable_dedup
= false;
445 if (enable_aot
|| link_icalls
|| gen_pinvoke
)
447 if (!enable_aot
&& link_icalls
)
449 if (ee_mode
!= ExecMode
.Aot
)
450 // Can't strip out IL code in mixed mode, since the interpreter might execute some methods even if they have AOTed code available
453 if (aot_assemblies
!= "") {
454 if (ee_mode
!= ExecMode
.AotInterp
) {
455 Console
.Error
.WriteLine ("The --aot-assemblies= argument requires --aot-interp.");
459 if (link_icalls
&& !enable_linker
) {
460 Console
.Error
.WriteLine ("The --link-icalls option requires the --linker option.");
464 var tool_prefix
= Path
.GetDirectoryName (typeof (Driver
).Assembly
.Location
);
466 //are we working from the tree?
467 if (sdkdir
!= null) {
468 framework_prefix
= Path
.Combine (tool_prefix
, "framework"); //all framework assemblies are currently side built to packager.exe
469 bcl_prefix
= Path
.Combine (sdkdir
, "wasm-bcl/wasm");
470 bcl_tools_prefix
= Path
.Combine (sdkdir
, "wasm-bcl/wasm_tools");
471 } else if (Directory
.Exists (Path
.Combine (tool_prefix
, "../out/wasm-bcl/wasm"))) {
472 framework_prefix
= Path
.Combine (tool_prefix
, "framework"); //all framework assemblies are currently side built to packager.exe
473 bcl_prefix
= Path
.Combine (tool_prefix
, "../out/wasm-bcl/wasm");
474 bcl_tools_prefix
= Path
.Combine (tool_prefix
, "../out/wasm-bcl/wasm_tools");
475 sdkdir
= Path
.Combine (tool_prefix
, "../out");
477 framework_prefix
= Path
.Combine (tool_prefix
, "framework");
478 bcl_prefix
= Path
.Combine (tool_prefix
, "wasm-bcl/wasm");
479 bcl_tools_prefix
= Path
.Combine (tool_prefix
, "wasm-bcl/wasm_tools");
480 sdkdir
= tool_prefix
;
482 bcl_facades_prefix
= Path
.Combine (bcl_prefix
, "Facades");
484 foreach (var ra
in root_assemblies
) {
486 var resolved
= Resolve (ra
, out kind
);
487 Import (resolved
, kind
);
490 var bindings
= ResolveFramework (BINDINGS_ASM_NAME
+ ".dll");
491 Import (bindings
, AssemblyKind
.Framework
);
492 var http
= ResolveFramework (HTTP_ASM_NAME
+ ".dll");
493 Import (http
, AssemblyKind
.Framework
);
494 var websockets
= ResolveFramework (WEBSOCKETS_ASM_NAME
+ ".dll");
495 Import (websockets
, AssemblyKind
.Framework
);
499 var to_aot
= new Dictionary
<string, bool> ();
500 to_aot
["mscorlib"] = true;
501 if (aot_assemblies
!= "") {
502 foreach (var s
in aot_assemblies
.Split (','))
505 foreach (var ass
in assemblies
) {
506 if (aot_assemblies
== "" || to_aot
.ContainsKey (ass
.name
)) {
508 to_aot
.Remove (ass
.name
);
511 if (to_aot
.Count
> 0) {
512 Console
.Error
.WriteLine ("Unknown assembly name '" + to_aot
.Keys
.ToArray ()[0] + "' in --aot-assemblies option.");
517 if (builddir
!= null) {
519 if (!Directory
.Exists (builddir
))
520 Directory
.CreateDirectory (builddir
);
524 if (!Directory
.Exists (out_prefix
))
525 Directory
.CreateDirectory (out_prefix
);
526 var bcl_dir
= Path
.Combine (out_prefix
, deploy_prefix
);
527 if (Directory
.Exists (bcl_dir
))
528 Directory
.Delete (bcl_dir
, true);
529 Directory
.CreateDirectory (bcl_dir
);
530 foreach (var f
in file_list
) {
531 CopyFile(f
, Path
.Combine (bcl_dir
, Path
.GetFileName (f
)), copyType
);
535 if (deploy_prefix
.EndsWith ("/"))
536 deploy_prefix
= deploy_prefix
.Substring (0, deploy_prefix
.Length
- 1);
537 if (vfs_prefix
.EndsWith ("/"))
538 vfs_prefix
= vfs_prefix
.Substring (0, vfs_prefix
.Length
- 1);
540 // the linker does not consider these core by default
541 var wasm_core_assemblies
= new Dictionary
<string, bool> ();
543 wasm_core_assemblies
[BINDINGS_ASM_NAME
] = true;
544 wasm_core_assemblies
[HTTP_ASM_NAME
] = true;
545 wasm_core_assemblies
[WEBSOCKETS_ASM_NAME
] = true;
547 // wasm core bindings module
548 var wasm_core_bindings
= string.Empty
;
550 wasm_core_bindings
= BINDINGS_MODULE
;
552 // wasm core bindings support file
553 var wasm_core_support
= string.Empty
;
554 var wasm_core_support_library
= string.Empty
;
556 wasm_core_support
= BINDINGS_MODULE_SUPPORT
;
557 wasm_core_support_library
= $"--js-library {BINDINGS_MODULE_SUPPORT}";
559 var runtime_js
= Path
.Combine (emit_ninja
? builddir
: out_prefix
, "runtime.js");
561 File
.Delete (runtime_js
);
562 File
.Copy (runtimeTemplate
, runtime_js
);
564 if (File
.Exists(runtime_js
) && (File
.Exists(runtimeTemplate
))) {
565 CopyFile (runtimeTemplate
, runtime_js
, CopyType
.IfNewer
, $"runtime template <{runtimeTemplate}> ");
567 if (File
.Exists(runtimeTemplate
))
568 CopyFile (runtimeTemplate
, runtime_js
, CopyType
.IfNewer
, $"runtime template <{runtimeTemplate}> ");
570 var runtime_gen
= "\nvar Module = {\n\tonRuntimeInitialized: function () {\n\t\tMONO.mono_load_runtime_and_bcl (\n\t\tconfig.vfs_prefix,\n\t\tconfig.deploy_prefix,\n\t\tconfig.enable_debugging,\n\t\tconfig.file_list,\n\t\tfunction () {\n\t\t\tApp.init ();\n\t\t}\n\t)\n\t},\n};";
571 File
.Delete (runtime_js
);
572 File
.WriteAllText (runtime_js
, runtime_gen
);
577 AssemblyData dedup_asm
= null;
580 dedup_asm
= new AssemblyData () { name
= "aot-dummy",
581 filename
= "aot-dummy.dll",
582 bc_path
= "$builddir/aot-dummy.dll.bc",
583 app_path
= "$appdir/$deploy_prefix/aot-dummy.dll",
584 linkout_path
= "$builddir/linker-out/aot-dummy.dll",
587 assemblies
.Add (dedup_asm
);
588 file_list
.Add ("aot-dummy.dll");
591 var file_list_str
= string.Join (",", file_list
.Select (f
=> $"\"{Path.GetFileName (f)}\"").Distinct());
592 var config
= String
.Format ("config = {{\n \tvfs_prefix: \"{0}\",\n \tdeploy_prefix: \"{1}\",\n \tenable_debugging: {2},\n \tfile_list: [ {3} ],\n", vfs_prefix
, deploy_prefix
, enable_debug
? "1" : "0", file_list_str
);
594 var config_js
= Path
.Combine (emit_ninja
? builddir
: out_prefix
, "mono-config.js");
595 File
.Delete (config_js
);
596 File
.WriteAllText (config_js
, config
);
598 string runtime_dir
= Path
.Combine (tool_prefix
, use_release_runtime
? "release" : "debug");
600 File
.Delete (Path
.Combine (out_prefix
, "mono.js"));
601 File
.Delete (Path
.Combine (out_prefix
, "mono.wasm"));
604 Path
.Combine (runtime_dir
, "mono.js"),
605 Path
.Combine (out_prefix
, "mono.js"));
607 Path
.Combine (runtime_dir
, "mono.wasm"),
608 Path
.Combine (out_prefix
, "mono.wasm"));
610 foreach(var asset
in assets
) {
612 Path
.Combine (out_prefix
, Path
.GetFileName (asset
)), copyType
, "Asset: ");
620 if (sdkdir
== null) {
621 Console
.WriteLine ("The --mono-sdkdir argument is required.");
624 if (emscripten_sdkdir
== null) {
625 Console
.WriteLine ("The --emscripten-sdkdir argument is required.");
628 GenDriver (builddir
, profilers
, ee_mode
, link_icalls
);
631 string runtime_libs
= "";
632 if (ee_mode
== ExecMode
.AotInterp
|| link_icalls
) {
633 runtime_libs
+= "$mono_sdkdir/wasm-runtime-release/lib/libmono-ee-interp.a $mono_sdkdir/wasm-runtime-release/lib/libmono-ilgen.a ";
634 // We need to link the icall table because the interpreter uses it to lookup icalls even if the aot-ed icall wrappers are available
636 runtime_libs
+= "$mono_sdkdir/wasm-runtime-release/lib/libmono-icall-table.a ";
638 runtime_libs
+= "$mono_sdkdir/wasm-runtime-release/lib/libmonosgen-2.0.a";
640 string aot_args
= "";
641 string profiler_libs
= "";
642 string profiler_aot_args
= "";
643 foreach (var profiler
in profilers
) {
644 profiler_libs
+= $"$mono_sdkdir/wasm-runtime-release/lib/libmono-profiler-{profiler}-static.a ";
645 if (profiler_aot_args
!= "")
646 profiler_aot_args
+= " ";
647 profiler_aot_args
+= $"--profile={profiler}";
649 if (ee_mode
== ExecMode
.AotInterp
)
650 aot_args
= "interp,";
652 runtime_dir
= Path
.GetFullPath (runtime_dir
);
653 sdkdir
= Path
.GetFullPath (sdkdir
);
654 out_prefix
= Path
.GetFullPath (out_prefix
);
656 string driver_deps
= "";
658 driver_deps
+= "$builddir/icall-table.h";
660 driver_deps
+= "$builddir/pinvoke-table.h";
661 string emcc_flags
= "";
663 emcc_flags
+= "--llvm-lto 1 ";
665 var ninja
= File
.CreateText (Path
.Combine (builddir
, "build.ninja"));
668 ninja
.WriteLine ($"mono_sdkdir = {sdkdir}");
669 ninja
.WriteLine ($"emscripten_sdkdir = {emscripten_sdkdir}");
670 ninja
.WriteLine ($"tool_prefix = {tool_prefix}");
671 ninja
.WriteLine ($"appdir = {out_prefix}");
672 ninja
.WriteLine ($"builddir = .");
673 ninja
.WriteLine ($"wasm_runtime_dir = {runtime_dir}");
674 ninja
.WriteLine ($"deploy_prefix = {deploy_prefix}");
675 ninja
.WriteLine ($"bcl_dir = {bcl_prefix}");
676 ninja
.WriteLine ($"bcl_facades_dir = {bcl_facades_prefix}");
677 ninja
.WriteLine ($"tools_dir = {bcl_tools_prefix}");
679 ninja
.WriteLine ($"wasm_core_bindings = $builddir/{BINDINGS_MODULE}");
680 ninja
.WriteLine ($"wasm_core_support = {wasm_core_support}");
681 ninja
.WriteLine ($"wasm_core_support_library = {wasm_core_support_library}");
683 ninja
.WriteLine ("wasm_core_bindings =");
684 ninja
.WriteLine ("wasm_core_support =");
685 ninja
.WriteLine ("wasm_core_support_library =");
687 ninja
.WriteLine ("cross = $mono_sdkdir/wasm-cross-release/bin/wasm32-unknown-none-mono-sgen");
688 ninja
.WriteLine ("emcc = source $emscripten_sdkdir/emsdk_env.sh && emcc");
689 // -s ASSERTIONS=2 is very slow
690 ninja
.WriteLine ($"emcc_flags = -Oz -g {emcc_flags}-s EMULATED_FUNCTION_POINTERS=1 -s DISABLE_EXCEPTION_CATCHING=0 -s ASSERTIONS=1 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s BINARYEN=1 -s \"BINARYEN_TRAP_MODE=\'clamp\'\" -s TOTAL_MEMORY=134217728 -s ALIASING_FUNCTION_POINTERS=0 -s NO_EXIT_RUNTIME=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s \"EXTRA_EXPORTED_RUNTIME_METHODS=[\'ccall\', \'cwrap\', \'setValue\', \'getValue\', \'UTF8ToString\']\" -s \"EXPORTED_FUNCTIONS=[\'___cxa_is_pointer_type\', \'___cxa_can_catch\']\" -s \"DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[\'setThrew\']\"");
693 ninja
.WriteLine ("rule aot");
694 ninja
.WriteLine ($" command = MONO_PATH=$mono_path $cross --debug {profiler_aot_args} --aot=$aot_args,{aot_args}llvmonly,asmonly,no-opt,static,direct-icalls,llvm-outfile=$outfile $src_file");
695 ninja
.WriteLine (" description = [AOT] $src_file -> $outfile");
696 ninja
.WriteLine ("rule aot-instances");
697 ninja
.WriteLine ($" command = MONO_PATH=$mono_path $cross --debug {profiler_aot_args} --aot={aot_args}llvmonly,asmonly,no-opt,static,direct-icalls,llvm-outfile=$outfile,dedup-include=$dedup_image $src_files");
698 ninja
.WriteLine (" description = [AOT-INSTANCES] $outfile");
699 ninja
.WriteLine ("rule mkdir");
700 ninja
.WriteLine (" command = mkdir -p $out");
701 // Copy $in to $out only if it changed
702 ninja
.WriteLine ("rule cpifdiff");
703 ninja
.WriteLine (" command = if cmp -s $in $out ; then : ; else cp $in $out ; fi");
704 ninja
.WriteLine (" restat = true");
705 ninja
.WriteLine (" description = [CPIFDIFF] $in -> $out");
706 ninja
.WriteLine ("rule emcc");
707 ninja
.WriteLine (" command = bash -c '$emcc $emcc_flags $flags -c -o $out $in'");
708 ninja
.WriteLine (" description = [EMCC] $in -> $out");
709 ninja
.WriteLine ("rule emcc-link");
710 ninja
.WriteLine ($" command = bash -c '$emcc $emcc_flags -o $out --js-library $tool_prefix/library_mono.js --js-library $tool_prefix/dotnet_support.js {wasm_core_support_library} $in'");
711 ninja
.WriteLine (" description = [EMCC-LINK] $in -> $out");
712 ninja
.WriteLine ("rule linker");
713 ninja
.WriteLine (" command = mono $tools_dir/monolinker.exe -out $builddir/linker-out -l none --explicit-reflection --disable-opt unreachablebodies --exclude-feature com --exclude-feature remoting --exclude-feature etw $linker_args || exit 1; for f in $out; do if test ! -f $$f; then echo > empty.cs; csc /nologo /out:$$f /target:library empty.cs; fi; done");
714 ninja
.WriteLine (" description = [IL-LINK]");
715 ninja
.WriteLine ("rule aot-dummy");
716 ninja
.WriteLine (" command = echo > aot-dummy.cs; csc /out:$out /target:library aot-dummy.cs");
717 ninja
.WriteLine ("rule gen-runtime-icall-table");
718 ninja
.WriteLine (" command = $cross --print-icall-table > $out");
719 ninja
.WriteLine ("rule gen-icall-table");
720 ninja
.WriteLine (" command = mono $tools_dir/wasm-tuner.exe --gen-icall-table $runtime_table $in > $out");
721 ninja
.WriteLine ("rule gen-pinvoke-table");
722 ninja
.WriteLine (" command = mono $tools_dir/wasm-tuner.exe --gen-pinvoke-table $pinvoke_libs $in > $out");
723 ninja
.WriteLine ("rule ilstrip");
724 ninja
.WriteLine (" command = cp $in $out; mono $tools_dir/mono-cil-strip.exe $out");
725 ninja
.WriteLine (" description = [IL-STRIP]");
728 ninja
.WriteLine ("build $appdir: mkdir");
729 ninja
.WriteLine ("build $appdir/$deploy_prefix: mkdir");
730 ninja
.WriteLine ("build $appdir/runtime.js: cpifdiff $builddir/runtime.js");
731 ninja
.WriteLine ("build $appdir/mono-config.js: cpifdiff $builddir/mono-config.js");
733 var source_file
= Path
.GetFullPath (Path
.Combine (tool_prefix
, "driver.c"));
734 ninja
.WriteLine ($"build $builddir/driver.c: cpifdiff {source_file}");
735 ninja
.WriteLine ($"build $builddir/driver-gen.c: cpifdiff $builddir/driver-gen.c.in");
737 var pinvoke_file
= Path
.GetFullPath (Path
.Combine (tool_prefix
, "pinvoke-tables-default.h"));
738 ninja
.WriteLine ($"build $builddir/pinvoke-tables-default.h: cpifdiff {pinvoke_file}");
739 driver_deps
+= $" $builddir/pinvoke-tables-default.h";
741 var driver_cflags
= enable_aot
? "-DENABLE_AOT=1" : "";
744 var bindings_source_file
= Path
.GetFullPath (Path
.Combine (tool_prefix
, "corebindings.c"));
745 ninja
.WriteLine ($"build $builddir/corebindings.c: cpifdiff {bindings_source_file}");
747 ninja
.WriteLine ($"build $builddir/corebindings.o: emcc $builddir/corebindings.c");
748 ninja
.WriteLine ($" flags = -I$mono_sdkdir/wasm-runtime-release/include/mono-2.0");
749 driver_cflags
+= " -DCORE_BINDINGS ";
752 driver_cflags
+= " -DGEN_PINVOKE ";
754 ninja
.WriteLine ($"build $builddir/driver.o: emcc $builddir/driver.c | $builddir/driver-gen.c {driver_deps}");
755 ninja
.WriteLine ($" flags = {driver_cflags} -DDRIVER_GEN=1 -I$mono_sdkdir/wasm-runtime-release/include/mono-2.0");
757 ninja
.WriteLine ("build $appdir/mono.js: cpifdiff $wasm_runtime_dir/mono.js");
758 ninja
.WriteLine ("build $appdir/mono.wasm: cpifdiff $wasm_runtime_dir/mono.wasm");
762 string linker_infiles
= "";
763 string linker_ofiles
= "";
764 string dedup_infiles
= "";
766 string path
= Path
.Combine (builddir
, "linker-in");
767 if (!Directory
.Exists (path
))
768 Directory
.CreateDirectory (path
);
770 string aot_in_path
= enable_linker
? "$builddir/linker-out" : "$builddir";
771 foreach (var a
in assemblies
) {
772 var assembly
= a
.src_path
;
773 if (assembly
== null)
775 string filename
= Path
.GetFileName (assembly
);
776 var filename_noext
= Path
.GetFileNameWithoutExtension (filename
);
777 string filename_pdb
= Path
.ChangeExtension (filename
, "pdb");
778 var source_file_path
= Path
.GetFullPath (assembly
);
779 var source_file_path_pdb
= Path
.ChangeExtension (source_file_path
, "pdb");
781 string infile_pdb
= "";
782 bool emit_pdb
= assemblies_with_dbg_info
.Contains (source_file_path_pdb
);
784 a
.linkin_path
= $"$builddir/linker-in/{filename}";
785 a
.linkout_path
= $"$builddir/linker-out/{filename}";
786 linker_infiles
+= $"{a.linkin_path} ";
787 linker_ofiles
+= $" {a.linkout_path}";
788 infile
= $"{a.linkout_path}";
789 ninja
.WriteLine ($"build {a.linkin_path}: cpifdiff {source_file_path}");
791 infile
= $"$builddir/{filename}";
792 ninja
.WriteLine ($"build $builddir/{filename}: cpifdiff {source_file_path}");
794 ninja
.WriteLine ($"build $builddir/{filename_pdb}: cpifdiff {source_file_path_pdb}");
795 infile_pdb
= $"$builddir/{filename_pdb}";
799 a
.final_path
= infile
;
801 ninja
.WriteLine ($"build $builddir/ilstrip-out/{filename} : ilstrip {infile}");
802 a
.final_path
= $"$builddir/ilstrip-out/{filename}";
805 ninja
.WriteLine ($"build $appdir/$deploy_prefix/{filename}: cpifdiff {a.final_path}");
807 ninja
.WriteLine ($"build $appdir/$deploy_prefix/{filename_pdb}: cpifdiff {infile_pdb}");
809 a
.bc_path
= $"$builddir/{filename}.bc";
811 ninja
.WriteLine ($"build {a.bc_path}: aot {infile}");
812 ninja
.WriteLine ($" src_file={infile}");
813 ninja
.WriteLine ($" outfile={a.bc_path}");
814 ninja
.WriteLine ($" mono_path={aot_in_path}");
816 ninja
.WriteLine ($" aot_args=dedup-skip");
818 ofiles
+= " " + ($"{a.bc_path}");
819 dedup_infiles
+= $" {a.linkout_path}";
824 * Run the aot compiler in dedup mode:
825 * mono --aot=<args>,dedup-include=aot-dummy.dll <assemblies> aot-dummy.dll
826 * This will process all assemblies and emit all instances into the aot image of aot-dummy.dll
830 * The dedup process will read in the .dedup files created when running with dedup-skip, so add all the
831 * .bc files as dependencies.
833 ninja
.WriteLine ($"build {a.bc_path}: aot-instances | {ofiles} {a.linkout_path}");
834 ninja
.WriteLine ($" dedup_image={a.filename}");
835 ninja
.WriteLine ($" src_files={dedup_infiles} {a.linkout_path}");
836 ninja
.WriteLine ($" outfile={a.bc_path}");
837 ninja
.WriteLine ($" mono_path={aot_in_path}");
838 ninja
.WriteLine ($"build {a.app_path}: cpifdiff {a.linkout_path}");
839 ninja
.WriteLine ($"build {a.linkout_path}: aot-dummy");
840 ofiles
+= $" {a.bc_path}";
843 string icall_assemblies
= "";
844 foreach (var a
in assemblies
) {
845 if (a
.name
== "mscorlib" || a
.name
== "System")
846 icall_assemblies
+= $"{a.linkout_path} ";
848 ninja
.WriteLine ("build $builddir/icall-table.json: gen-runtime-icall-table");
849 ninja
.WriteLine ($"build $builddir/icall-table.h: gen-icall-table {icall_assemblies}");
850 ninja
.WriteLine ($" runtime_table=$builddir/icall-table.json");
853 string pinvoke_assemblies
= "";
854 foreach (var a
in assemblies
)
855 pinvoke_assemblies
+= $"{a.linkout_path} ";
856 ninja
.WriteLine ($"build $builddir/pinvoke-table.h: gen-pinvoke-table {pinvoke_assemblies}");
857 ninja
.WriteLine ($" pinvoke_libs=System.Native,{pinvoke_libs}");
860 ninja
.WriteLine ($"build $appdir/mono.js: emcc-link $builddir/driver.o {wasm_core_bindings} {ofiles} {profiler_libs} {runtime_libs} $mono_sdkdir/wasm-runtime-release/lib/libmono-native.a | $tool_prefix/library_mono.js $tool_prefix/dotnet_support.js {wasm_core_support}");
864 case LinkMode
.SdkOnly
:
878 string linker_args
= "";
879 foreach (var assembly
in root_assemblies
) {
880 string filename
= Path
.GetFileName (assembly
);
881 linker_args
+= $"-a linker-in/{filename} ";
884 // the linker does not consider these core by default
885 foreach (var assembly
in wasm_core_assemblies
.Keys
) {
886 linker_args
+= $"-p {coremode} {assembly} ";
888 if (linker_verbose
) {
889 linker_args
+= "--verbose ";
891 linker_args
+= $"-d linker-in -d $bcl_dir -d $bcl_facades_dir -c {coremode} -u {usermode} ";
892 foreach (var assembly
in wasm_core_assemblies
.Keys
) {
893 linker_args
+= $"-r {assembly} ";
896 ninja
.WriteLine ("build $builddir/linker-out: mkdir");
897 ninja
.WriteLine ($"build {linker_ofiles}: linker {linker_infiles}");
898 ninja
.WriteLine ($" linker_args={linker_args}");
901 ninja
.WriteLine ("build $builddir/ilstrip-out: mkdir");
903 foreach(var asset
in assets
) {
904 var filename
= Path
.GetFileName (asset
);
905 var abs_path
= Path
.GetFullPath (asset
);
906 ninja
.WriteLine ($"build $appdir/{filename}: cpifdiff {abs_path}");
914 static void CopyFile(string sourceFileName
, string destFileName
, CopyType copyType
, string typeFile
= "")
916 Console
.WriteLine($"{typeFile}cp: {copyType} - {sourceFileName} -> {destFileName}");
919 case CopyType
.Always
:
920 File
.Copy(sourceFileName
, destFileName
, true);
922 case CopyType
.IfNewer
:
923 if (!File
.Exists(destFileName
))
925 File
.Copy(sourceFileName
, destFileName
);
929 var srcInfo
= new FileInfo (sourceFileName
);
930 var dstInfo
= new FileInfo (destFileName
);
932 if (srcInfo
.LastWriteTime
.Ticks
> dstInfo
.LastWriteTime
.Ticks
|| srcInfo
.Length
> dstInfo
.Length
)
933 File
.Copy(sourceFileName
, destFileName
, true);
935 Console
.WriteLine($" skipping: {sourceFileName}");
939 File
.Copy(sourceFileName
, destFileName
);