[runtime] Add new dedup interface to mkbundle
[mono.git] / mcs / tools / mkbundle / mkbundle.cs
1 //
2 // mkbundle: tool to create bundles.
3 //
4 // Based on the `make-bundle' Perl script written by Paolo Molaro (lupus@debian.org)
5 //
6 // TODO:
7 //   [x] Rename the paths for the zip file that is downloaded
8 //   [x] Update documentation with new flag
9 //   [x] Load internationalized assemblies
10 //   [x] Dependencies - if System.dll -> include Mono.Security.* (not needed, automatic)
11 //   [x] --list-targets should download from a different url
12 //   [x] --fetch-target should unpack zip file
13 //   [x] Update --cross to use not a runtime, but an SDK
14 //   [x] Update --local-targets to show the downloaded SDKs
15 //
16 // Author:
17 //   Miguel de Icaza
18 //
19 // (C) Novell, Inc 2004
20 // (C) 2016 Xamarin Inc
21 //
22 // Missing features:
23 // * Add support for packaging native libraries, extracting at runtime and setting the library path.
24 // * Implement --list-targets lists all the available remote targets
25 //
26 using System;
27 using System.Diagnostics;
28 using System.Xml;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.IO.Compression;
32 using System.Runtime.InteropServices;
33 using System.Text;
34 using IKVM.Reflection;
35 using System.Linq;
36 using System.Net;
37 using System.Threading.Tasks;
38
39 class MakeBundle {
40         static string output = "a.out";
41         static string object_out = null;
42         static List<string> link_paths = new List<string> ();
43         static List<string> aot_paths = new List<string> ();
44         static List<string> aot_names = new List<string> ();
45         static Dictionary<string,string> libraries = new Dictionary<string,string> ();
46         static bool autodeps = false;
47         static bool keeptemp = false;
48         static bool compile_only = false;
49         static bool static_link = false;
50         static string config_file = null;
51         static string machine_config_file = null;
52         static string config_dir = null;
53         static string style = "linux";
54         static bool bundled_header = false;
55         static string os_message = "";
56         static bool compress;
57         static bool nomain;
58         static string custom_main = null;
59         static bool? use_dos2unix = null;
60         static bool skip_scan;
61         static string ctor_func;
62         static bool quiet = true;
63         static string cross_target = null;
64         static string fetch_target = null;
65         static bool custom_mode = true;
66         static string embedded_options = null;
67         static string runtime = null;
68         static bool aot_compile = false;
69         static string aot_args = "static";
70         static string aot_mode = "";
71         static string aot_runtime = null;
72         static int? aot_dedup_assembly = null;
73         static string sdk_path = null;
74         static string lib_path = null;
75         static Dictionary<string,string> environment = new Dictionary<string,string>();
76         static string [] i18n = new string [] {
77                 "West",
78                 ""
79         };
80         static string [] i18n_all = new string [] {
81                 "CJK", 
82                 "MidEast",
83                 "Other",
84                 "Rare",
85                 "West",
86                 ""
87         };
88         static string target_server = "https://download.mono-project.com/runtimes/raw/";
89         
90         static int Main (string [] args)
91         {
92                 List<string> sources = new List<string> ();
93                 int top = args.Length;
94                 link_paths.Add (".");
95
96                 DetectOS ();
97
98                 for (int i = 0; i < top; i++){
99                         switch (args [i]){
100                         case "--help": case "-h": case "-?":
101                                 Help ();
102                                 return 1;
103
104                         case "--simple":
105                                 custom_mode = false;
106                                 autodeps = true;
107                                 break;
108
109                         case "-v":
110                                 quiet = false;
111                                 break;
112                                 
113                         case "--i18n":
114                                 if (i+1 == top){
115                                         Help ();
116                                         return 1;
117                                 }
118                                 var iarg = args [++i];
119                                 if (iarg == "all")
120                                         i18n = i18n_all;
121                                 else if (iarg == "none")
122                                         i18n = new string [0];
123                                 else
124                                         i18n = iarg.Split (',');
125                                 break;
126                                 
127                         case "--custom":
128                                 custom_mode = true;
129                                 break;
130                                 
131                         case "-c":
132                                 compile_only = true;
133                                 break;
134
135                         case "--local-targets":
136                                 CommandLocalTargets ();
137                                 return 0;
138
139                         case "--cross":
140                                 if (i+1 == top){
141                                         Help (); 
142                                         return 1;
143                                 }
144                                 if (sdk_path != null || runtime != null)
145                                         Error ("You can only specify one of --runtime, --sdk or --cross");
146                                 custom_mode = false;
147                                 autodeps = true;
148                                 cross_target = args [++i];
149                                 break;
150
151                         case "--library":
152                                 if (i+1 == top){
153                                         Help (); 
154                                         return 1;
155                                 }
156                                 if (custom_mode){
157                                         Console.Error.WriteLine ("--library can only be used with --simple/--runtime/--cross mode");
158                                         Help ();
159                                         return 1;
160                                 }
161                                 var lspec = args [++i];
162                                 var p = lspec.IndexOf (",");
163                                 string alias, path;
164                                 if (p == -1){
165                                         alias = Path.GetFileName (lspec);
166                                         path = lspec;
167                                 } else {
168                                         alias = lspec.Substring (0, p);
169                                         path = lspec.Substring (p+1);
170                                 }
171                                 if (!File.Exists (path))
172                                         Error ($"The specified library file {path} does not exist");
173                                 libraries [alias] = path;
174                                 break;
175
176                         case "--fetch-target":
177                                 if (i+1 == top){
178                                         Help (); 
179                                         return 1;
180                                 }
181                                 fetch_target = args [++i];
182                                 break;
183
184                         case "--list-targets":
185                                 CommandLocalTargets ();
186                                 var wc = new WebClient ();
187                                 var s = wc.DownloadString (new Uri (target_server + "target-sdks.txt"));
188                                 Console.WriteLine ("Targets available for download with --fetch-target:\n" + s);
189                                 return 0;
190                                 
191                         case "--target-server":
192                                 if (i+1 == top){
193                                         Help (); 
194                                         return 1;
195                                 }
196                                 target_server = args [++i];
197                                 break;
198
199                         case "-o": 
200                                 if (i+1 == top){
201                                         Help (); 
202                                         return 1;
203                                 }
204                                 output = args [++i];
205                                 break;
206
207                         case "--options":
208                                 if (i+1 == top){
209                                         Help (); 
210                                         return 1;
211                                 }
212                                 embedded_options = args [++i];
213                                 break;
214                         case "--sdk":
215                                 if (i + 1 == top) {
216                                         Help ();
217                                         return 1;
218                                 }
219                                 custom_mode = false;
220                                 autodeps = true;
221                                 sdk_path = args [++i];
222                                 if (cross_target != null || runtime != null)
223                                         Error ("You can only specify one of --runtime, --sdk or --cross");
224                                 break;
225                         case "--runtime":
226                                 if (i+1 == top){
227                                         Help (); 
228                                         return 1;
229                                 }
230                                 if (sdk_path != null || cross_target != null)
231                                         Error ("You can only specify one of --runtime, --sdk or --cross");
232                                 custom_mode = false;
233                                 autodeps = true;
234                                 runtime = args [++i];
235                                 break;
236                         case "-oo":
237                                 if (i+1 == top){
238                                         Help (); 
239                                         return 1;
240                                 }
241                                 object_out = args [++i];
242                                 break;
243
244                         case "-L":
245                                 if (i+1 == top){
246                                         Help (); 
247                                         return 1;
248                                 }
249                                 link_paths.Add (args [++i]);
250                                 break;
251
252                         case "--nodeps":
253                                 autodeps = false;
254                                 break;
255
256                         case "--deps":
257                                 autodeps = true;
258                                 break;
259
260                         case "--keeptemp":
261                                 keeptemp = true;
262                                 break;
263                                 
264                         case "--static":
265                                 static_link = true;
266                                 break;
267                         case "--config":
268                                 if (i+1 == top) {
269                                         Help ();
270                                         return 1;
271                                 }
272
273                                 config_file = args [++i];
274                                 break;
275                         case "--machine-config":
276                                 if (i+1 == top) {
277                                         Help ();
278                                         return 1;
279                                 }
280
281                                 machine_config_file = args [++i];
282
283                                 if (!quiet)
284                                         Console.WriteLine ("WARNING:\n  Check that the machine.config file you are bundling\n  doesn't contain sensitive information specific to this machine.");
285                                 break;
286                         case "--config-dir":
287                                 if (i+1 == top) {
288                                         Help ();
289                                         return 1;
290                                 }
291
292                                 config_dir = args [++i];
293                                 break;
294                         case "-z":
295                                 compress = true;
296                                 break;
297                         case "--nomain":
298                                 nomain = true;
299                                 break;
300                         case "--custom-main":
301                                 if (i+1 == top) {
302                                         Help ();
303                                         return 1;
304                                 }
305                                 custom_main = args [++i];
306                                 break;
307                         case "--style":
308                                 if (i+1 == top) {
309                                         Help ();
310                                         return 1;
311                                 }
312                                 style = args [++i];
313                                 switch (style) {
314                                 case "windows":
315                                 case "mac":
316                                 case "linux":
317                                         break;
318                                 default:
319                                         Error ("Invalid style '{0}' - only 'windows', 'mac' and 'linux' are supported for --style argument", style);
320                                         return 1;
321                                 }
322                                         
323                                 break;
324                         case "--skip-scan":
325                                 skip_scan = true;
326                                 break;
327                         case "--static-ctor":
328                                 if (i+1 == top) {
329                                         Help ();
330                                         return 1;
331                                 }
332                                 ctor_func = args [++i];
333                                 break;
334                         case "--dos2unix":
335                         case "--dos2unix=true":
336                                 use_dos2unix = true;
337                                 break;
338                         case "--dos2unix=false":
339                                 use_dos2unix = false;
340                                 break;
341                         case "-q":
342                         case "--quiet":
343                                 quiet = true;
344                                 break;
345                         case "-e":
346                         case "--env":
347                                 if (i+1 == top) {
348                                         Help ();
349                                         return 1;
350                                 }
351                                 var env = args [++i];
352                                 p = env.IndexOf ('=');
353                                 if (p == -1)
354                                         environment.Add (env, "");
355                                 else
356                                         environment.Add (env.Substring (0, p), env.Substring (p+1));
357                                 break;
358                         case "--bundled-header":
359                                 bundled_header = true;
360                                 break;
361                         case "--aot-runtime":
362                                 aot_runtime = args [++i];
363                                 break;
364                         case "--aot-dedup":
365                                 if (i+1 == top) {
366                                         Console.WriteLine ("Usage: --aot-dedup <container_dll> ");
367                                         return 1;
368                                 }
369                                 var dedup_file = args [++i];
370                                 sources.Add (dedup_file);
371                                 aot_dedup_assembly = sources.Count () - 1;
372                                 break;
373                         case "--aot-mode":
374                                 if (i+1 == top) {
375                                         Console.WriteLine ("Need string of aot mode (full, llvmonly). Omit for normal AOT.");
376                                         return 1;
377                                 }
378
379                                 aot_mode = args [++i];
380
381                                 if (aot_mode != "full" && aot_mode != "llvmonly") {
382                                         Console.WriteLine ("Need string of aot mode (full, llvmonly). Omit for normal AOT.");
383                                         return 1;
384                                 }
385
386                                 aot_compile = true;
387                                 break;
388                         case "--aot-args":
389                                 if (i+1 == top) {
390                                         Console.WriteLine ("AOT arguments are passed as a comma-delimited list");
391                                         return 1;
392                                 }
393                                 if (args [i + 1].Contains ("outfile")) {
394                                         Console.WriteLine ("Per-aot-output arguments (ex: outfile, llvm-outfile) cannot be given");
395                                         return 1;
396                                 }
397                                 aot_args = String.Format("static,{0}", args [++i]);
398                                 aot_compile = true;
399                                 break;
400                         default:
401                                 sources.Add (args [i]);
402                                 break;
403                         }
404                 }
405                 // Modern bundling starts here
406                 if (!custom_mode){
407                         if (runtime != null){
408                                 // Nothing to do here, the user has chosen to manually specify --runtime nad libraries
409                         } else if (sdk_path != null) {
410                                 VerifySdk (sdk_path);
411                         } else if (cross_target == "default" || cross_target == null){
412                                 sdk_path = Path.GetFullPath (Path.Combine (Process.GetCurrentProcess().MainModule.FileName, "..", ".."));
413                                 VerifySdk (sdk_path);
414                         } else {
415                                 sdk_path = Path.Combine (targets_dir, cross_target);
416                                 Console.WriteLine ("From: " + sdk_path);
417                                 VerifySdk (sdk_path);
418                         }
419                 }
420
421                 if (fetch_target != null){
422                         var directory = Path.Combine (targets_dir, fetch_target);
423                         var zip_download = Path.Combine (directory, "sdk.zip");
424                         Directory.CreateDirectory (directory);
425                         var wc = new WebClient ();
426                         var uri = new Uri ($"{target_server}{fetch_target}");
427                         try {
428                                 if (!quiet){
429                                         Console.WriteLine ($"Downloading runtime {uri} to {zip_download}");
430                                 }
431                                 
432                                 wc.DownloadFile (uri, zip_download);
433                                 ZipFile.ExtractToDirectory(zip_download, directory);
434                                 File.Delete (zip_download);
435                         } catch {
436                                 Console.Error.WriteLine ($"Failure to download the specified runtime from {uri}");
437                                 File.Delete (zip_download);
438                                 return 1;
439                         }
440                         return 0;
441                 }
442                 
443                 if (!quiet) {
444                         Console.WriteLine (os_message);
445                         Console.WriteLine ("Sources: {0} Auto-dependencies: {1}", sources.Count, autodeps);
446                 }
447
448                 if (sources.Count == 0 || output == null) {
449                         Help ();
450                         Environment.Exit (1);
451                 }
452
453                 List<string> assemblies = LoadAssemblies (sources);
454                 LoadLocalizedAssemblies (assemblies);
455                 List<string> files = new List<string> ();
456                 foreach (string file in assemblies)
457                         if (!QueueAssembly (files, file))
458                                 return 1;
459
460                 if (aot_compile)
461                         AotCompile (files);
462
463                 if (custom_mode)
464                         GenerateBundles (files);
465                 else 
466                         GeneratePackage (files);
467
468                 Console.WriteLine ("Generated {0}", output);
469
470                 return 0;
471         }
472
473         static void VerifySdk (string path)
474         {
475                 if (!Directory.Exists (path))
476                         Error ($"The specified SDK path does not exist: {path}");
477                 runtime = Path.Combine (sdk_path, "bin", "mono");
478                 if (!File.Exists (runtime))
479                         Error ($"The SDK location does not contain a {path}/bin/mono runtime");
480                 lib_path = Path.Combine (path, "lib", "mono", "4.5");
481                 if (!Directory.Exists (lib_path))
482                         Error ($"The SDK location does not contain a {path}/lib/mono/4.5 directory");
483                 link_paths.Add (lib_path);
484         }
485
486         static string targets_dir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono", "targets");
487         
488         static void CommandLocalTargets ()
489         {
490                 string [] targets;
491
492                 Console.WriteLine ("Available targets locally:");
493                 Console.WriteLine ("\tdefault\t- Current System Mono");
494                 try {
495                         targets = Directory.GetDirectories (targets_dir);
496                 } catch {
497                         return;
498                 }
499                 foreach (var target in targets){
500                         var p = Path.Combine (target, "bin", "mono");
501                         if (File.Exists (p))
502                                 Console.WriteLine ("\t{0}", Path.GetFileName (target));
503                 }
504         }
505
506         static void WriteSymbol (StreamWriter sw, string name, long size)
507         {
508                 switch (style){
509                 case "linux":
510                         sw.WriteLine (
511                                 ".globl {0}\n" +
512                                 "\t.section .rodata\n" +
513                                 "\t.p2align 5\n" +
514                                 "\t.type {0}, \"object\"\n" +
515                                 "\t.size {0}, {1}\n" +
516                                 "{0}:\n",
517                                 name, size);
518                         break;
519                 case "osx":
520                         sw.WriteLine (
521                                 "\t.section __TEXT,__text,regular,pure_instructions\n" + 
522                                 "\t.globl _{0}\n" +
523                                 "\t.data\n" +
524                                 "\t.align 4\n" +
525                                 "_{0}:\n",
526                                 name, size);
527                         break;
528                 case "windows":
529                         sw.WriteLine (
530                                 ".globl _{0}\n" +
531                                 "\t.section .rdata,\"dr\"\n" +
532                                 "\t.align 32\n" +
533                                 "_{0}:\n",
534                                 name, size);
535                         break;
536                 }
537         }
538         
539         static string [] chars = new string [256];
540         
541         static void WriteBuffer (StreamWriter ts, Stream stream, byte[] buffer)
542         {
543                 int n;
544                 
545                 // Preallocate the strings we need.
546                 if (chars [0] == null) {
547                         for (int i = 0; i < chars.Length; i++)
548                                 chars [i] = string.Format ("{0}", i.ToString ());
549                 }
550
551                 while ((n = stream.Read (buffer, 0, buffer.Length)) != 0) {
552                         int count = 0;
553                         for (int i = 0; i < n; i++) {
554                                 if (count % 32 == 0) {
555                                         ts.Write ("\n\t.byte ");
556                                 } else {
557                                         ts.Write (",");
558                                 }
559                                 ts.Write (chars [buffer [i]]);
560                                 count ++;
561                         }
562                 }
563
564                 ts.WriteLine ();
565         }
566
567         class PackageMaker {
568                 Dictionary<string, Tuple<long,int>> locations = new Dictionary<string, Tuple<long,int>> ();
569                 const int align = 4096;
570                 Stream package;
571                 
572                 public PackageMaker (string output)
573                 {
574                         package = File.Create (output, 128*1024);
575                         if (IsUnix){
576                                 File.SetAttributes (output, unchecked ((FileAttributes) 0x80000000));
577                         }
578                 }
579
580                 public int AddFile (string fname)
581                 {
582                         using (Stream fileStream = File.OpenRead (fname)){
583                                 var ret = fileStream.Length;
584
585                                 if (!quiet)
586                                         Console.WriteLine ("At {0:x} with input {1}", package.Position, fileStream.Length);
587                                 fileStream.CopyTo (package);
588                                 package.Position = package.Position + (align - (package.Position % align));
589                                 return (int) ret;
590                         }
591                 }
592                 
593                 public void Add (string entry, string fname)
594                 {
595                         var p = package.Position;
596                         var size = AddFile (fname);
597                         locations [entry] = Tuple.Create(p, size);
598                 }
599
600                 public void AddString (string entry, string text)
601                 {
602                         var bytes = Encoding.UTF8.GetBytes (text);
603                         locations [entry] = Tuple.Create (package.Position, bytes.Length);
604                         package.Write (bytes, 0, bytes.Length);
605                         package.Position = package.Position + (align - (package.Position % align));
606                 }
607
608                 public void AddStringPair (string entry, string key, string value)
609                 {
610                         var kbytes = Encoding.UTF8.GetBytes (key);
611                         var vbytes = Encoding.UTF8.GetBytes (value);
612
613                         Console.WriteLine ("ADDING {0} to {1}", key, value);
614                         if (kbytes.Length > 255){
615                                 Console.WriteLine ("The key value can not exceed 255 characters: " + key);
616                                 Environment.Exit (1);
617                         }
618                                 
619                         locations [entry] = Tuple.Create (package.Position, kbytes.Length+vbytes.Length+3);
620                         package.WriteByte ((byte)kbytes.Length);
621                         package.Write (kbytes, 0, kbytes.Length);
622                         package.WriteByte (0);
623                         package.Write (vbytes, 0, vbytes.Length);
624                         package.WriteByte (0);
625                         package.Position = package.Position + (align - (package.Position % align));
626                 }
627
628                 public void Dump ()
629                 {
630                         if (quiet)
631                                 return;
632                         foreach (var floc in locations.Keys){
633                                 Console.WriteLine ($"{floc} at {locations[floc]:x}");
634                         }
635                 }
636
637                 public void WriteIndex ()
638                 {
639                         var indexStart = package.Position;
640                         var binary = new BinaryWriter (package);
641
642                         binary.Write (locations.Count);
643                         foreach (var entry in from entry in locations orderby entry.Value.Item1 ascending select entry){
644                                 var bytes = Encoding.UTF8.GetBytes (entry.Key);
645                                 binary.Write (bytes.Length+1);
646                                 binary.Write (bytes);
647                                 binary.Write ((byte) 0);
648                                 binary.Write (entry.Value.Item1);
649                                 binary.Write (entry.Value.Item2);
650                         }
651                         binary.Write (indexStart);
652                         binary.Write (Encoding.UTF8.GetBytes ("xmonkeysloveplay"));
653                         binary.Flush ();
654                 }
655                 
656                 public void Close ()
657                 {
658                         WriteIndex ();
659                         package.Close ();
660                         package = null;
661                 }
662         }
663
664         static bool MaybeAddFile (PackageMaker maker, string code, string file)
665         {
666                 if (file == null)
667                         return true;
668                 
669                 if (!File.Exists (file)){
670                         Error ("The file {0} does not exist", file);
671                         return false;
672                 }
673                 maker.Add (code, file);
674                 return true;
675         }
676         
677         static bool GeneratePackage (List<string> files)
678         {
679                 if (runtime == null){
680                         if (IsUnix)
681                                 runtime = Process.GetCurrentProcess().MainModule.FileName;
682                         else {
683                                 Error ("You must specify at least one runtime with --runtime or --cross");
684                                 Environment.Exit (1);
685                         }
686                 }
687                 if (!File.Exists (runtime)){
688                         Error ($"The specified runtime at {runtime} does not exist");
689                         Environment.Exit (1);
690                 }
691                 
692                 if (ctor_func != null){
693                         Error ("--static-ctor not supported with package bundling, you must use native compilation for this");
694                         return false;
695                 }
696                 
697                 var maker = new PackageMaker (output);
698                 Console.WriteLine ("Using runtime: " + runtime);
699                 maker.AddFile (runtime);
700                 
701                 foreach (var url in files){
702                         string fname = LocateFile (new Uri (url).LocalPath);
703                         string aname = MakeBundle.GetAssemblyName (fname);
704
705                         maker.Add ("assembly:" + aname, fname);
706                         Console.WriteLine ("     Assembly: " + fname);
707                         if (File.Exists (fname + ".config")){
708                                 maker.Add ("config:" + aname, fname + ".config");
709                                 Console.WriteLine ("       Config: " + runtime);
710                         }
711                 }
712                 
713                 if (!MaybeAddFile (maker, "systemconfig:", config_file) || !MaybeAddFile (maker, "machineconfig:", machine_config_file))
714                         return false;
715
716                 if (config_dir != null)
717                         maker.Add ("config_dir:", config_dir);
718                 if (embedded_options != null)
719                         maker.AddString ("options:", embedded_options);
720                 if (environment.Count > 0){
721                         foreach (var key in environment.Keys)
722                                 maker.AddStringPair ("env:" + key, key, environment [key]);
723                 }
724                 if (libraries.Count > 0){
725                         foreach (var alias_and_path in libraries){
726                                 Console.WriteLine ("     Library:  " + alias_and_path.Value);
727                                 maker.Add ("library:" + alias_and_path.Key, alias_and_path.Value);
728                         }
729                 }
730                 maker.Dump ();
731                 maker.Close ();
732                 return true;
733         }
734         
735         static void GenerateBundles (List<string> files)
736         {
737                 string temp_s = "temp.s"; // Path.GetTempFileName ();
738                 string temp_c = "temp.c";
739                 string temp_o = "temp.o";
740
741                 if (compile_only)
742                         temp_c = output;
743                 if (object_out != null)
744                         temp_o = object_out;
745                 
746                 try {
747                         List<string> c_bundle_names = new List<string> ();
748                         List<string[]> config_names = new List<string[]> ();
749
750                         using (StreamWriter ts = new StreamWriter (File.Create (temp_s))) {
751                         using (StreamWriter tc = new StreamWriter (File.Create (temp_c))) {
752                         string prog = null;
753
754                         if (bundled_header) {
755                                 tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */");
756                                 tc.WriteLine ("\n#ifndef NULL\n#define NULL (void *)0\n#endif");
757                                 tc.WriteLine (@"
758 typedef struct {
759         const char *name;
760         const unsigned char *data;
761         const unsigned int size;
762 } MonoBundledAssembly;
763 void          mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies);
764 void          mono_register_config_for_assembly (const char* assembly_name, const char* config_xml);
765 ");
766                         } else {
767                                 tc.WriteLine ("#include <mono/metadata/mono-config.h>");
768                                 tc.WriteLine ("#include <mono/metadata/assembly.h>\n");
769                                 tc.WriteLine ("#include <mono/jit/jit.h>\n");
770                         }
771
772                         if (compress) {
773                                 tc.WriteLine ("typedef struct _compressed_data {");
774                                 tc.WriteLine ("\tMonoBundledAssembly assembly;");
775                                 tc.WriteLine ("\tint compressed_size;");
776                                 tc.WriteLine ("} CompressedAssembly;\n");
777                         }
778
779                         object monitor = new object ();
780
781                         var streams = new Dictionary<string, Stream> ();
782                         var sizes = new Dictionary<string, long> ();
783
784                         // Do the file reading and compression in parallel
785                         Action<string> body = delegate (string url) {
786                                 string fname = LocateFile (new Uri (url).LocalPath);
787                                 Stream stream = File.OpenRead (fname);
788
789                                 long real_size = stream.Length;
790                                 int n;
791                                 if (compress) {
792                                         byte[] cbuffer = new byte [8192];
793                                         MemoryStream ms = new MemoryStream ();
794                                         GZipStream deflate = new GZipStream (ms, CompressionMode.Compress, leaveOpen:true);
795                                         while ((n = stream.Read (cbuffer, 0, cbuffer.Length)) != 0){
796                                                 deflate.Write (cbuffer, 0, n);
797                                         }
798                                         stream.Close ();
799                                         deflate.Close ();
800                                         byte [] bytes = ms.GetBuffer ();
801                                         stream = new MemoryStream (bytes, 0, (int) ms.Length, false, false);
802                                 }
803
804                                 lock (monitor) {
805                                         streams [url] = stream;
806                                         sizes [url] = real_size;
807                                 }
808                         };
809
810                         //#if NET_4_5
811 #if FALSE
812                         Parallel.ForEach (files, body);
813 #else
814                         foreach (var url in files)
815                                 body (url);
816 #endif
817
818                         // The non-parallel part
819                         byte [] buffer = new byte [8192];
820                         // everything other than a-zA-Z0-9_ needs to be escaped in asm symbols.
821                         var symbolEscapeRE = new System.Text.RegularExpressions.Regex ("[^\\w_]");
822                         foreach (var url in files) {
823                                 string fname = LocateFile (new Uri (url).LocalPath);
824                                 string aname = MakeBundle.GetAssemblyName (fname);
825                                 string encoded = symbolEscapeRE.Replace (aname, "_");
826
827                                 if (prog == null)
828                                         prog = aname;
829
830                                 var stream = streams [url];
831                                 var real_size = sizes [url];
832
833                                 if (!quiet)
834                                         Console.WriteLine ("   embedding: " + fname);
835
836                                 WriteSymbol (ts, "assembly_data_" + encoded, stream.Length);
837                         
838                                 WriteBuffer (ts, stream, buffer);
839
840                                 if (compress) {
841                                         tc.WriteLine ("extern const unsigned char assembly_data_{0} [];", encoded);
842                                         tc.WriteLine ("static CompressedAssembly assembly_bundle_{0} = {{{{\"{1}\"," +
843                                                                   " assembly_data_{0}, {2}}}, {3}}};",
844                                                                   encoded, aname, real_size, stream.Length);
845                                         if (!quiet) {
846                                                 double ratio = ((double) stream.Length * 100) / real_size;
847                                                 Console.WriteLine ("   compression ratio: {0:.00}%", ratio);
848                                         }
849                                 } else {
850                                         tc.WriteLine ("extern const unsigned char assembly_data_{0} [];", encoded);
851                                         tc.WriteLine ("static const MonoBundledAssembly assembly_bundle_{0} = {{\"{1}\", assembly_data_{0}, {2}}};",
852                                                                   encoded, aname, real_size);
853                                 }
854                                 stream.Close ();
855
856                                 c_bundle_names.Add ("assembly_bundle_" + encoded);
857
858                                 try {
859                                         FileStream cf = File.OpenRead (fname + ".config");
860                                         if (!quiet)
861                                                 Console.WriteLine (" config from: " + fname + ".config");
862                                         tc.WriteLine ("extern const unsigned char assembly_config_{0} [];", encoded);
863                                         WriteSymbol (ts, "assembly_config_" + encoded, cf.Length);
864                                         WriteBuffer (ts, cf, buffer);
865                                         ts.WriteLine ();
866                                         config_names.Add (new string[] {aname, encoded});
867                                 } catch (FileNotFoundException) {
868                                         /* we ignore if the config file doesn't exist */
869                                 }
870                         }
871
872                         if (config_file != null){
873                                 FileStream conf;
874                                 try {
875                                         conf = File.OpenRead (config_file);
876                                 } catch {
877                                         Error ("Failure to open {0}", config_file);
878                                         return;
879                                 }
880                                 if (!quiet)
881                                         Console.WriteLine ("System config from: " + config_file);
882                                 tc.WriteLine ("extern const char system_config;");
883                                 WriteSymbol (ts, "system_config", config_file.Length);
884
885                                 WriteBuffer (ts, conf, buffer);
886                                 // null terminator
887                                 ts.Write ("\t.byte 0\n");
888                                 ts.WriteLine ();
889                         }
890
891                         if (machine_config_file != null){
892                                 FileStream conf;
893                                 try {
894                                         conf = File.OpenRead (machine_config_file);
895                                 } catch {
896                                         Error ("Failure to open {0}", machine_config_file);
897                                         return;
898                                 }
899                                 if (!quiet)
900                                         Console.WriteLine ("Machine config from: " + machine_config_file);
901                                 tc.WriteLine ("extern const char machine_config;");
902                                 WriteSymbol (ts, "machine_config", machine_config_file.Length);
903
904                                 WriteBuffer (ts, conf, buffer);
905                                 ts.Write ("\t.byte 0\n");
906                                 ts.WriteLine ();
907                         }
908                         ts.Close ();
909
910                         // Managed assemblies baked in
911                         if (compress)
912                                 tc.WriteLine ("\nstatic const CompressedAssembly *compressed [] = {");
913                         else
914                                 tc.WriteLine ("\nstatic const MonoBundledAssembly *bundled [] = {");
915
916                         foreach (string c in c_bundle_names){
917                                 tc.WriteLine ("\t&{0},", c);
918                         }
919                         tc.WriteLine ("\tNULL\n};\n");
920
921
922                         // AOT baked in plus loader
923                         foreach (string asm in aot_names){
924                                 tc.WriteLine ("\textern const void *mono_aot_module_{0}_info;", asm);
925                         }
926
927                         tc.WriteLine ("\nstatic void install_aot_modules (void) {\n");
928                         foreach (string asm in aot_names){
929                                 tc.WriteLine ("\tmono_aot_register_module (mono_aot_module_{0}_info);\n", asm);
930                         }
931
932                         string enum_aot_mode;
933                         switch (aot_mode) {
934                         case "full": 
935                                 enum_aot_mode = "MONO_AOT_MODE_FULL";
936                                 break;
937                         case "llvmonly": 
938                                 enum_aot_mode = "MONO_AOT_MODE_LLVMONLY";
939                                 break;
940                         case "": 
941                                 enum_aot_mode = "MONO_AOT_MODE_NORMAL";
942                                 break;
943                         default:
944                                 throw new Exception ("Unsupported AOT mode");
945                         }
946                         tc.WriteLine ("\tmono_jit_set_aot_mode ({0});", enum_aot_mode);
947
948                         tc.WriteLine ("\n}\n");
949
950
951                         tc.WriteLine ("static char *image_name = \"{0}\";", prog);
952
953                         if (ctor_func != null) {
954                                 tc.WriteLine ("\nextern void {0} (void);", ctor_func);
955                                 tc.WriteLine ("\n__attribute__ ((constructor)) static void mono_mkbundle_ctor (void)");
956                                 tc.WriteLine ("{{\n\t{0} ();\n}}", ctor_func);
957                         }
958
959                         tc.WriteLine ("\nstatic void install_dll_config_files (void) {\n");
960                         foreach (string[] ass in config_names){
961                                 tc.WriteLine ("\tmono_register_config_for_assembly (\"{0}\", assembly_config_{1});\n", ass [0], ass [1]);
962                         }
963                         if (config_file != null)
964                                 tc.WriteLine ("\tmono_config_parse_memory (&system_config);\n");
965                         if (machine_config_file != null)
966                                 tc.WriteLine ("\tmono_register_machine_config (&machine_config);\n");
967                         tc.WriteLine ("}\n");
968
969                         if (config_dir != null)
970                                 tc.WriteLine ("static const char *config_dir = \"{0}\";", config_dir);
971                         else
972                                 tc.WriteLine ("static const char *config_dir = NULL;");
973
974                         Stream template_stream;
975                         if (compress) {
976                                 template_stream = System.Reflection.Assembly.GetAssembly (typeof(MakeBundle)).GetManifestResourceStream ("template_z.c");
977                         } else {
978                                 template_stream = System.Reflection.Assembly.GetAssembly (typeof(MakeBundle)).GetManifestResourceStream ("template.c");
979                         }
980
981                         StreamReader s = new StreamReader (template_stream);
982                         string template = s.ReadToEnd ();
983                         tc.Write (template);
984
985                         if (!nomain && custom_main == null) {
986                                 Stream template_main_stream = System.Reflection.Assembly.GetAssembly (typeof(MakeBundle)).GetManifestResourceStream ("template_main.c");
987                                 StreamReader st = new StreamReader (template_main_stream);
988                                 string maintemplate = st.ReadToEnd ();
989                                 tc.Write (maintemplate);
990                         }
991
992                         tc.Close ();
993
994                         string assembler = GetEnv("AS", "as");
995                         string as_cmd = String.Format("{0} -o {1} {2} ", assembler, temp_o, temp_s);
996                         Execute(as_cmd);
997
998                         if (compile_only)
999                                 return;
1000
1001                         if (!quiet)
1002                                 Console.WriteLine("Compiling:");
1003
1004                         if (style == "windows")
1005                         {
1006
1007                                 Func<string, string> quote = (pp) => { return "\"" + pp + "\""; };
1008
1009                                 string compiler = GetEnv("CC", "cl.exe");
1010                                 string winsdkPath = GetEnv("WINSDK", @"C:\Program Files (x86)\Windows Kits\8.1");
1011                                 string vsPath = GetEnv("VSINCLUDE", @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC");
1012                                 string monoPath = GetEnv("MONOPREFIX", @"C:\Program Files (x86)\Mono");
1013
1014                                 string[] includes = new string[] {winsdkPath + @"\Include\um", winsdkPath + @"\Include\shared", vsPath + @"\include", monoPath + @"\include\mono-2.0", "." };
1015                                 // string[] libs = new string[] { winsdkPath + @"\Lib\winv6.3\um\x86" , vsPath + @"\lib" };
1016                                 var linkLibraries = new string[] {  "kernel32.lib",
1017                                                                                                 "version.lib",
1018                                                                                                 "Ws2_32.lib",
1019                                                                                                 "Mswsock.lib",
1020                                                                                                 "Psapi.lib",
1021                                                                                                 "shell32.lib",
1022                                                                                                 "OleAut32.lib",
1023                                                                                                 "ole32.lib",
1024                                                                                                 "winmm.lib",
1025                                                                                                 "user32.lib",
1026                                                                                                 "libvcruntime.lib",
1027                                                                                                 "advapi32.lib",
1028                                                                                                 "OLDNAMES.lib",
1029                                                                                                 "libucrt.lib" };
1030
1031                                 string glue_obj = "mkbundle_glue.obj";
1032                                 string monoLib;
1033
1034                                 if (static_link)
1035                                         monoLib = LocateFile (monoPath + @"\lib\monosgen-2.0-static.lib");
1036
1037                                 else {
1038                                         Console.WriteLine ("WARNING: Dynamically linking the Mono runtime on Windows is not a tested option.");
1039                                         monoLib = LocateFile (monoPath + @"\lib\monosgen-2.0.lib");
1040                                         LocateFile (monoPath + @"\lib\monosgen-2.0.dll"); // in this case, the .lib is just the import library, and the .dll is also needed
1041                                 }
1042
1043                                 var compilerArgs = new List<string>();
1044                                 compilerArgs.Add("/MT");
1045
1046                                 foreach (string include in includes)
1047                                         compilerArgs.Add(String.Format ("/I {0}", quote (include)));
1048
1049                                 if (!nomain || custom_main != null) {
1050                                         compilerArgs.Add(quote(temp_c));
1051                                         compilerArgs.Add(quote(temp_o));
1052                                         if (custom_main != null)
1053                                                 compilerArgs.Add(quote(custom_main));
1054                                         compilerArgs.Add(quote(monoLib));
1055                                         compilerArgs.Add("/link");
1056                                         compilerArgs.Add("/NODEFAULTLIB");
1057                                         compilerArgs.Add("/SUBSYSTEM:windows");
1058                                         compilerArgs.Add("/ENTRY:mainCRTStartup");
1059                                         compilerArgs.AddRange(linkLibraries);
1060                                         compilerArgs.Add("/out:"+ output);
1061
1062                                         string cl_cmd = String.Format("{0} {1}", compiler, String.Join(" ", compilerArgs.ToArray()));
1063                                         Execute (cl_cmd);
1064                                 }
1065                                 else
1066                                 {
1067                                         // we are just creating a .lib
1068                                         compilerArgs.Add("/c"); // compile only
1069                                         compilerArgs.Add(temp_c);
1070                                         compilerArgs.Add(String.Format("/Fo" + glue_obj)); // .obj output name
1071
1072                                         string cl_cmd = String.Format("{0} {1}", compiler, String.Join(" ", compilerArgs.ToArray()));
1073                                         Execute (cl_cmd);
1074
1075                                         string librarian = GetEnv ("LIB", "lib.exe");
1076                                         var librarianArgs = new List<string> ();
1077                                         librarianArgs.Add (String.Format ("/out:{0}.lib" + output));
1078                                         librarianArgs.Add (temp_o);
1079                                         librarianArgs.Add (glue_obj);
1080                                         librarianArgs.Add (monoLib);
1081                                         string lib_cmd = String.Format("{0} {1}", librarian, String.Join(" ", librarianArgs.ToArray()));
1082                                         Execute (lib_cmd);
1083                                 }
1084                         }
1085                         else
1086                         {
1087                                 string zlib = (compress ? "-lz" : "");
1088                                 string debugging = "-g";
1089                                 string cc = GetEnv("CC", "cc");
1090                                 string cmd = null;
1091
1092                                 if (style == "linux")
1093                                         debugging = "-ggdb";
1094                                 if (static_link)
1095                                 {
1096                                         string platform_libs;
1097                                         string smonolib;
1098                                         if (style == "osx") {
1099                                                 smonolib = "`pkg-config --variable=libdir mono-2`/libmono-2.0.a ";
1100                                                 platform_libs = "-liconv -framework Foundation ";
1101                                         } else {
1102                                                 smonolib = "-Wl,-Bstatic -lmono-2.0 -Wl,-Bdynamic ";
1103                                                 platform_libs = "";
1104                                         }
1105
1106                                         cmd = String.Format("{4} -o '{2}' -Wall `pkg-config --cflags mono-2` {0} {3} " +
1107                                                 "`pkg-config --libs-only-L mono-2` {5} {6} " + platform_libs +
1108                                                 "`pkg-config --libs-only-l mono-2 | sed -e \"s/\\-lmono-2.0 //\"` {1} -g ",
1109                                                 temp_c, temp_o, output, zlib, cc, smonolib, String.Join (" ", aot_paths));
1110                                 }
1111                                 else
1112                                 {
1113
1114                                         cmd = String.Format("{4} " + debugging + " -o '{2}' -Wall {0} `pkg-config --cflags --libs mono-2` {3} {1}",
1115                                                 temp_c, temp_o, output, zlib, cc);
1116                                 }
1117                                 Execute (cmd);
1118                         }
1119
1120                         if (!quiet)
1121                                 Console.WriteLine ("Done");
1122                 }
1123         }
1124                 } finally {
1125                         if (!keeptemp){
1126                                 if (object_out == null){
1127                                         File.Delete (temp_o);
1128                                 }
1129                                 if (!compile_only){
1130                                         File.Delete (temp_c);
1131                                 }
1132                                 File.Delete (temp_s);
1133                         }
1134                 }
1135         }
1136         
1137         static List<string> LoadAssemblies (List<string> sources)
1138         {
1139                 List<string> assemblies = new List<string> ();
1140                 bool error = false;
1141
1142                 foreach (string name in sources){
1143                         try {
1144                                 Assembly a = LoadAssemblyFile (name);
1145
1146                                 if (a == null){
1147                                         error = true;
1148                                         continue;
1149                                 }
1150                         
1151                                 assemblies.Add (a.CodeBase);
1152                         } catch (Exception) {
1153                                 if (skip_scan) {
1154                                         if (!quiet)
1155                                                 Console.WriteLine ("File will not be scanned: {0}", name);
1156                                         assemblies.Add (new Uri (new FileInfo (name).FullName).ToString ());
1157                                 } else {
1158                                         throw;
1159                                 }
1160                         }
1161                 }
1162
1163                 if (error) {
1164                         Error ("Couldn't load one or more of the assemblies.");
1165                         Environment.Exit (1);
1166                 }
1167
1168                 return assemblies;
1169         }
1170
1171         static void LoadLocalizedAssemblies (List<string> assemblies)
1172         {
1173                 var other = i18n.Select (x => "I18N." + x + (x.Length > 0 ? "." : "") + "dll");
1174                 string error = null;
1175
1176                 foreach (string name in other) {
1177                         try {
1178                                 Assembly a = LoadAssembly (name);
1179
1180                                 if (a == null) {
1181                                         error = "Failed to load " + name;
1182                                         continue;
1183                                 }
1184
1185                                 assemblies.Add (a.CodeBase);
1186                         } catch (Exception) {
1187                                 if (skip_scan) {
1188                                         if (!quiet)
1189                                                 Console.WriteLine ("File will not be scanned: {0}", name);
1190                                         assemblies.Add (new Uri (new FileInfo (name).FullName).ToString ());
1191                                 } else {
1192                                         throw;
1193                                 }
1194                         }
1195                 }
1196
1197                 if (error != null) {
1198                         Console.Error.WriteLine ("Failure to load i18n assemblies, the following directories were searched for the assemblies:");
1199                         foreach (var path in link_paths){
1200                                 Console.Error.WriteLine ("   Path: " + path);
1201                         }
1202                         if (custom_mode){
1203                                 Console.WriteLine ("In Custom mode, you need to provide the directory to lookup assemblies from using -L");
1204                         }
1205                         
1206                         Error ("Couldn't load one or more of the i18n assemblies: " + error);
1207                         Environment.Exit (1);
1208                 }
1209         }
1210
1211         
1212         static readonly Universe universe = new Universe ();
1213         static readonly Dictionary<string, string> loaded_assemblies = new Dictionary<string, string> ();
1214
1215         public static string GetAssemblyName (string path)
1216         {
1217                 string resourcePathSeparator = style == "windows" ? "\\\\" : "/";
1218                 string name = Path.GetFileName (path);
1219
1220                 // A bit of a hack to support satellite assemblies. They all share the same name but
1221                 // are placed in subdirectories named after the locale they implement. Also, all of
1222                 // them end in .resources.dll, therefore we can use that to detect the circumstances.
1223                 if (name.EndsWith (".resources.dll", StringComparison.OrdinalIgnoreCase)) {
1224                         string dir = Path.GetDirectoryName (path);
1225                         int idx = dir.LastIndexOf (Path.DirectorySeparatorChar);
1226                         if (idx >= 0) {
1227                                 name = dir.Substring (idx + 1) + resourcePathSeparator + name;
1228                                 Console.WriteLine ($"Storing satellite assembly '{path}' with name '{name}'");
1229                         } else if (!quiet)
1230                                 Console.WriteLine ($"Warning: satellite assembly {path} doesn't have locale path prefix, name conflicts possible");
1231                 }
1232
1233                 return name;
1234         }
1235
1236         static bool QueueAssembly (List<string> files, string codebase)
1237         {
1238                 //Console.WriteLine ("CODE BASE IS {0}", codebase);
1239                 if (files.Contains (codebase))
1240                         return true;
1241
1242                 var path = new Uri(codebase).LocalPath;
1243                 var name = GetAssemblyName (path);
1244                 string found;
1245                 if (loaded_assemblies.TryGetValue (name, out found)) {
1246                         Error ("Duplicate assembly name `{0}'. Both `{1}' and `{2}' use same assembly name.", name, path, found);
1247                         return false;
1248                 }
1249
1250                 loaded_assemblies.Add (name, path);
1251
1252                 files.Add (codebase);
1253                 if (!autodeps)
1254                         return true;
1255                 try {
1256                         Assembly a = universe.LoadFile (path);
1257                         if (a == null) {
1258                                 Error ("Unable to to load assembly `{0}'", path);
1259                                 return false;
1260                         }
1261
1262                         foreach (AssemblyName an in a.GetReferencedAssemblies ()) {
1263                                 a = LoadAssembly (an.Name);
1264                                 if (a == null) {
1265                                         Error ("Unable to load assembly `{0}' referenced by `{1}'", an.Name, path);
1266                                         return false;
1267                                 }
1268
1269                                 if (!QueueAssembly (files, a.CodeBase))
1270                                         return false;
1271                         }
1272                 } catch (Exception) {
1273                         if (!skip_scan)
1274                                 throw;
1275                 }
1276
1277                 return true;
1278         }
1279
1280         //
1281         // Loads an assembly from a specific path
1282         //
1283         static Assembly LoadAssemblyFile (string assembly)
1284         {
1285                 Assembly a = null;
1286                 
1287                 try {
1288                         if (!quiet)
1289                                 Console.WriteLine ("Attempting to load assembly: {0}", assembly);
1290                         a = universe.LoadFile (assembly);
1291                         if (!quiet)
1292                                 Console.WriteLine ("Assembly {0} loaded successfully.", assembly);
1293                         
1294                 } catch (FileNotFoundException){
1295                         Error ($"Cannot find assembly `{assembly}'");
1296                 } catch (IKVM.Reflection.BadImageFormatException f) {
1297                         if (skip_scan)
1298                                 throw;
1299                         Error ($"Cannot load assembly (bad file format) " + f.Message);
1300                 } catch (FileLoadException f){
1301                         Error ($"Cannot load assembly " + f.Message);
1302                 } catch (ArgumentNullException){
1303                         Error( $"Cannot load assembly (null argument)");
1304                 }
1305                 return a;
1306         }
1307
1308         //
1309         // Loads an assembly from any of the link directories provided
1310         //
1311         static Assembly LoadAssembly (string assembly)
1312         {
1313                 string total_log = "";
1314                 foreach (string dir in link_paths){
1315                         string full_path = Path.Combine (dir, assembly);
1316                         if (!quiet)
1317                                 Console.WriteLine ("Attempting to load assembly from: " + full_path);
1318                         if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
1319                                 full_path += ".dll";
1320                         
1321                         try {
1322                                 var a = universe.LoadFile (full_path);
1323                                 return a;
1324                         } catch (FileNotFoundException ff) {
1325                                 total_log += ff.FusionLog;
1326                                 continue;
1327                         }
1328                 }
1329                 if (!quiet)
1330                         Console.WriteLine ("Log: \n" + total_log);
1331                 return null;
1332         }
1333         
1334         static void Error (string msg, params object [] args)
1335         {
1336                 Console.Error.WriteLine ("ERROR: {0}", string.Format (msg, args));
1337                 Environment.Exit (1);
1338         }
1339
1340         static void Help ()
1341         {
1342                 Console.WriteLine ("Usage is: mkbundle [options] assembly1 [assembly2...]\n\n" +
1343                                    "Options:\n" +
1344                                    "    --config F           Bundle system config file `F'\n" +
1345                                    "    --config-dir D       Set MONO_CFG_DIR to `D'\n" +
1346                                    "    --deps               Turns on automatic dependency embedding (default on simple)\n" +
1347                                    "    -L path              Adds `path' to the search path for assemblies\n" +
1348                                    "    --machine-config F   Use the given file as the machine.config for the application.\n" +
1349                                    "    -o out               Specifies output filename\n" +
1350                                    "    --nodeps             Turns off automatic dependency embedding (default on custom)\n" +
1351                                    "    --skip-scan          Skip scanning assemblies that could not be loaded (but still embed them).\n" +
1352                                    "    --i18n ENCODING      none, all or comma separated list of CJK, MidWest, Other, Rare, West.\n" +
1353                                    "    -v                   Verbose output\n" + 
1354                                    "    --bundled-header     Do not attempt to include 'mono-config.h'. Define the entry points directly in the generated code\n" +
1355                                    "\n" + 
1356                                    "--simple   Simple mode does not require a C toolchain and can cross compile\n" + 
1357                                    "    --cross TARGET       Generates a binary for the given TARGET\n"+
1358                                    "    --env KEY=VALUE      Hardcodes an environment variable for the target\n" +
1359                                    "    --fetch-target NAME  Downloads the target SDK from the remote server\n" + 
1360                                    "    --library [LIB,]PATH Bundles the specified dynamic library to be used at runtime\n" +
1361                                    "                         LIB is optional shortname for file located at PATH\n" + 
1362                                    "    --list-targets       Lists available targets on the remote server\n" +
1363                                    "    --local-targets      Lists locally available targets\n" +
1364                                    "    --options OPTIONS    Embed the specified Mono command line options on target\n" +
1365                                    "    --runtime RUNTIME    Manually specifies the Mono runtime to use\n" +
1366                                    "    --sdk PATH           Use a Mono SDK root location instead of a target\n" + 
1367                                    "    --target-server URL  Specified a server to download targets from, default is " + target_server + "\n" +
1368                                    "\n" +
1369                                    "--custom   Builds a custom launcher, options for --custom\n" +
1370                                    "    -c                   Produce stub only, do not compile\n" +
1371                                    "    -oo obj              Specifies output filename for helper object file\n" +
1372                                    "    --dos2unix[=true|false]\n" +
1373                                    "                         When no value provided, or when `true` specified\n" +
1374                                    "                         `dos2unix` will be invoked to convert paths on Windows.\n" +
1375                                    "                         When `--dos2unix=false` used, dos2unix is NEVER used.\n" +
1376                                    "    --keeptemp           Keeps the temporary files\n" +
1377                                    "    --static             Statically link to mono libs\n" +
1378                                    "    --nomain             Don't include a main() function, for libraries\n" +
1379                                    "    --custom-main C      Link the specified compilation unit (.c or .obj) with entry point/init code\n" +
1380                                    "    -z                   Compress the assemblies before embedding.\n" +
1381                                    "    --static-ctor ctor   Add a constructor call to the supplied function.\n" +
1382                                    "                         You need zlib development headers and libraries.\n");
1383         }
1384
1385         [DllImport ("libc")]
1386         static extern int system (string s);
1387         [DllImport ("libc")]
1388         static extern int uname (IntPtr buf);
1389                 
1390         static void DetectOS ()
1391         {
1392                 if (!IsUnix) {
1393                         os_message = "OS is: Windows";
1394                         style = "windows";
1395                         return;
1396                 }
1397
1398                 IntPtr buf = Marshal.AllocHGlobal (8192);
1399                 if (uname (buf) != 0){
1400                         os_message = "Warning: Unable to detect OS";
1401                         Marshal.FreeHGlobal (buf);
1402                         return;
1403                 }
1404                 string os = Marshal.PtrToStringAnsi (buf);
1405                 os_message = "OS is: " + os;
1406                 if (os == "Darwin")
1407                         style = "osx";
1408                 
1409                 Marshal.FreeHGlobal (buf);
1410         }
1411
1412         static bool IsUnix {
1413                 get {
1414                         int p = (int) Environment.OSVersion.Platform;
1415                         return ((p == 4) || (p == 128) || (p == 6));
1416                 }
1417         }
1418
1419
1420         static string EncodeAotSymbol (string symbol)
1421         {
1422                 var sb = new StringBuilder ();
1423                 /* This mimics what the aot-compiler does */
1424                 foreach (var b in System.Text.Encoding.UTF8.GetBytes (symbol)) {
1425                         char c = (char) b;
1426                         if ((c >= '0' && c <= '9') ||
1427                                 (c >= 'a' && c <= 'z') ||
1428                                 (c >= 'A' && c <= 'Z')) {
1429                                 sb.Append (c);
1430                                 continue;
1431                         }
1432                         sb.Append ('_');
1433                 }
1434                 return sb.ToString ();
1435         }
1436
1437         static void AotCompile (List<string> files)
1438         {
1439                 if (aot_runtime == null)
1440                         aot_runtime = runtime;
1441
1442                 var aot_mode_string = "";
1443                 if (aot_mode != null)
1444                         aot_mode_string = "," + aot_mode;
1445
1446                 var dedup_mode_string = "";
1447                 StringBuilder all_assemblies = null;
1448                 if (aot_dedup_assembly != null) {
1449                         dedup_mode_string = ",dedup-skip";
1450                         all_assemblies = new StringBuilder("");
1451                 }
1452
1453                 for (int i=0; i < files.Count; i++) {
1454                         var fileName = files [i];
1455                         string path = LocateFile (new Uri (fileName).LocalPath);
1456                         string outPath = String.Format ("{0}.aot_out", path);
1457                         aot_paths.Add (outPath);
1458                         var name = System.Reflection.Assembly.LoadFrom(path).GetName().Name;
1459                         aot_names.Add (EncodeAotSymbol (name));
1460
1461                         if (aot_dedup_assembly != null && i != (int) aot_dedup_assembly) {
1462                                 all_assemblies.Append (path);
1463                                 all_assemblies.Append (" ");
1464                                 Execute (String.Format ("{0} --aot={1},outfile={2}{3}{4} {5}",
1465                                         aot_runtime, aot_args, outPath, aot_mode_string, dedup_mode_string, path));
1466                         }
1467                 }
1468                 if (aot_dedup_assembly != null) {
1469                         string fileName = files [(int) aot_dedup_assembly];
1470                         string path = LocateFile (new Uri (fileName).LocalPath);
1471                         dedup_mode_string = String.Format (",dedup-include={0}", path);
1472                         string outPath = String.Format ("{0}.aot_out", path);
1473                         Execute (String.Format ("{0} --aot={1},outfile={2}{3}{4} {5} {6}",
1474                                 aot_runtime, aot_args, outPath, aot_mode_string, dedup_mode_string, path, all_assemblies.ToString ()));
1475                 }
1476         }
1477
1478
1479         static void Execute (string cmdLine)
1480         {
1481                 if (IsUnix) {
1482                         if (!quiet)
1483                                 Console.WriteLine ("[execute cmd]: " + cmdLine);
1484                         int ret = system (cmdLine);
1485                         if (ret != 0)
1486                         {
1487                                 Error(String.Format("[Fail] {0}", ret));
1488                         }
1489                         return;
1490                 }
1491
1492                 // on Windows, we have to pipe the output of a
1493                 // `cmd` interpolation to dos2unix, because the shell does not
1494                 // strip the CRLFs generated by the native pkg-config distributed
1495                 // with Mono.
1496                 //
1497                 // But if it's *not* on cygwin, just skip it.
1498
1499                 // check if dos2unix is applicable.
1500                 if (use_dos2unix == true)
1501                         try {
1502                         var info = new ProcessStartInfo ("dos2unix");
1503                         info.CreateNoWindow = true;
1504                         info.RedirectStandardInput = true;
1505                         info.UseShellExecute = false;
1506                         var dos2unix = Process.Start (info);
1507                         dos2unix.StandardInput.WriteLine ("aaa");
1508                         dos2unix.StandardInput.WriteLine ("\u0004");
1509                         dos2unix.StandardInput.Close ();
1510                         dos2unix.WaitForExit ();
1511                         if (dos2unix.ExitCode == 0)
1512                                 use_dos2unix = true;
1513                 } catch {
1514                         Console.WriteLine("Warning: dos2unix not found");
1515                         use_dos2unix = false;
1516                 }
1517
1518                 if (use_dos2unix == null)
1519                         use_dos2unix = false;
1520
1521                 ProcessStartInfo psi = new ProcessStartInfo();
1522                 psi.UseShellExecute = false;
1523
1524                 // if there is no dos2unix, just run cmd /c.
1525                 if (use_dos2unix == false)
1526                 {
1527                         psi.FileName = "cmd";
1528                         psi.Arguments = String.Format("/c \"{0}\"", cmdLine);
1529                 }
1530                 else
1531                 {
1532                         psi.FileName = "sh";
1533                         StringBuilder b = new StringBuilder();
1534                         int count = 0;
1535                         for (int i = 0; i < cmdLine.Length; i++)
1536                         {
1537                                 if (cmdLine[i] == '`')
1538                                 {
1539                                         if (count % 2 != 0)
1540                                         {
1541                                                 b.Append("|dos2unix");
1542                                         }
1543                                         count++;
1544                                 }
1545                                 b.Append(cmdLine[i]);
1546                         }
1547                         cmdLine = b.ToString();
1548                         psi.Arguments = String.Format("-c \"{0}\"", cmdLine);
1549                 }
1550
1551                 if (!quiet)
1552                         Console.WriteLine(cmdLine);
1553                 using (Process p = Process.Start (psi)) {
1554                         p.WaitForExit ();
1555                         int ret = p.ExitCode;
1556                         if (ret != 0){
1557                                 Error ("[Fail] {0}", ret);
1558                         }
1559                 }
1560         }
1561
1562         static string GetEnv(string name, string defaultValue)
1563         {
1564                 string val = Environment.GetEnvironmentVariable(name);
1565                 if (val != null)
1566                 {
1567                         if (!quiet)
1568                                 Console.WriteLine("{0} = {1}", name, val);
1569                 }
1570                 else
1571                 {
1572                         val = defaultValue;
1573                         if (!quiet)
1574                                 Console.WriteLine("{0} = {1} (default)", name, val);
1575                 }
1576                 return val;
1577         }
1578
1579         static string LocateFile(string default_path)
1580         {
1581                 var override_path = Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(default_path));
1582                 if (File.Exists(override_path))
1583                         return override_path;
1584                 else if (File.Exists(default_path))
1585                         return default_path;
1586                 else
1587                         throw new FileNotFoundException(default_path);
1588         }
1589 }