Merge pull request #5382 from kumpera/pedump_fix
[mono.git] / mcs / tools / mkbundle / mkbundle.cs
index 2382d37d43d3d04657bb2bcc31759a12bbca39c4..97cf6c6a240c6521ec1a7d23a6a0f5f70365a443 100755 (executable)
@@ -3,6 +3,16 @@
 //
 // Based on the `make-bundle' Perl script written by Paolo Molaro (lupus@debian.org)
 //
+// TODO:
+//   [x] Rename the paths for the zip file that is downloaded
+//   [x] Update documentation with new flag
+//   [x] Load internationalized assemblies
+//   [x] Dependencies - if System.dll -> include Mono.Security.* (not needed, automatic)
+//   [x] --list-targets should download from a different url
+//   [x] --fetch-target should unpack zip file
+//   [x] Update --cross to use not a runtime, but an SDK
+//   [x] Update --local-targets to show the downloaded SDKs
+//
 // Author:
 //   Miguel de Icaza
 //
@@ -23,7 +33,6 @@ using System.Runtime.InteropServices;
 using System.Text;
 using IKVM.Reflection;
 using System.Linq;
-using System.Diagnostics;
 using System.Net;
 using System.Threading.Tasks;
 
@@ -31,6 +40,7 @@ class MakeBundle {
        static string output = "a.out";
        static string object_out = null;
        static List<string> link_paths = new List<string> ();
+       static Dictionary<string,string> libraries = new Dictionary<string,string> ();
        static bool autodeps = false;
        static bool keeptemp = false;
        static bool compile_only = false;
@@ -39,6 +49,7 @@ class MakeBundle {
        static string machine_config_file = null;
        static string config_dir = null;
        static string style = "linux";
+       static bool bundled_header = false;
        static string os_message = "";
        static bool compress;
        static bool nomain;
@@ -46,12 +57,27 @@ class MakeBundle {
        static bool? use_dos2unix = null;
        static bool skip_scan;
        static string ctor_func;
-       static bool quiet;
+       static bool quiet = true;
        static string cross_target = null;
        static string fetch_target = null;
        static bool custom_mode = true;
        static string embedded_options = null;
        static string runtime = null;
+       static string sdk_path = null;
+       static string lib_path = null;
+       static Dictionary<string,string> environment = new Dictionary<string,string>();
+       static string [] i18n = new string [] {
+               "West",
+               ""
+       };
+       static string [] i18n_all = new string [] {
+               "CJK", 
+               "MidEast",
+               "Other",
+               "Rare",
+               "West",
+               ""
+       };
        static string target_server = "https://download.mono-project.com/runtimes/raw/";
        
        static int Main (string [] args)
@@ -61,7 +87,7 @@ class MakeBundle {
                link_paths.Add (".");
 
                DetectOS ();
-               
+
                for (int i = 0; i < top; i++){
                        switch (args [i]){
                        case "--help": case "-h": case "-?":
@@ -72,6 +98,24 @@ class MakeBundle {
                                custom_mode = false;
                                autodeps = true;
                                break;
+
+                       case "-v":
+                               quiet = false;
+                               break;
+                               
+                       case "--i18n":
+                               if (i+1 == top){
+                                       Help ();
+                                       return 1;
+                               }
+                               var iarg = args [++i];
+                               if (iarg == "all")
+                                       i18n = i18n_all;
+                               else if (iarg == "none")
+                                       i18n = new string [0];
+                               else
+                                       i18n = iarg.Split (',');
+                               break;
                                
                        case "--custom":
                                custom_mode = true;
@@ -90,11 +134,38 @@ class MakeBundle {
                                        Help (); 
                                        return 1;
                                }
+                               if (sdk_path != null || runtime != null)
+                                       Error ("You can not specify one of --runtime, --sdk or --cross");
                                custom_mode = false;
                                autodeps = true;
                                cross_target = args [++i];
                                break;
 
+                       case "--library":
+                               if (i+1 == top){
+                                       Help (); 
+                                       return 1;
+                               }
+                               if (custom_mode){
+                                       Console.Error.WriteLine ("--library can only be used with --simple/--runtime/--cross mode");
+                                       Help ();
+                                       return 1;
+                               }
+                               var lspec = args [++i];
+                               var p = lspec.IndexOf (",");
+                               string alias, path;
+                               if (p == -1){
+                                       alias = Path.GetFileName (lspec);
+                                       path = lspec;
+                               } else {
+                                       alias = lspec.Substring (0, p);
+                                       path = lspec.Substring (p+1);
+                               }
+                               if (!File.Exists (path))
+                                       Error ($"The specified library file {path} does not exist");
+                               libraries [alias] = path;
+                               break;
+
                        case "--fetch-target":
                                if (i+1 == top){
                                        Help (); 
@@ -104,10 +175,10 @@ class MakeBundle {
                                break;
 
                        case "--list-targets":
+                               CommandLocalTargets ();
                                var wc = new WebClient ();
-                               var s = wc.DownloadString (new Uri (target_server + "target-list.txt"));
-                               Console.WriteLine ("Cross-compilation targets available:\n" + s);
-                               
+                               var s = wc.DownloadString (new Uri (target_server + "target-sdks.txt"));
+                               Console.WriteLine ("Targets available for download with --fetch-target:\n" + s);
                                return 0;
                                
                        case "--target-server":
@@ -133,11 +204,26 @@ class MakeBundle {
                                }
                                embedded_options = args [++i];
                                break;
+                       case "--sdk":
+                               if (i + 1 == top) {
+                                       Help ();
+                                       return 1;
+                               }
+                               custom_mode = false;
+                               autodeps = true;
+                               sdk_path = args [++i];
+                               if (cross_target != null || runtime != null)
+                                       Error ("You can not specify one of --runtime, --sdk or --cross");
+                               break;
                        case "--runtime":
                                if (i+1 == top){
                                        Help (); 
                                        return 1;
                                }
+                               if (sdk_path != null || cross_target != null)
+                                       Error ("You can only specify one of --runtime, --sdk or --cross");
+                               custom_mode = false;
+                               autodeps = true;
                                runtime = args [++i];
                                break;
                        case "-oo":
@@ -223,7 +309,7 @@ class MakeBundle {
                                case "linux":
                                        break;
                                default:
-                                       Console.Error.WriteLine ("Invalid style '{0}' - only 'windows', 'mac' and 'linux' are supported for --style argument", style);
+                                       Error ("Invalid style '{0}' - only 'windows', 'mac' and 'linux' are supported for --style argument", style);
                                        return 1;
                                }
                                        
@@ -249,77 +335,112 @@ class MakeBundle {
                        case "--quiet":
                                quiet = true;
                                break;
+                       case "-e":
+                       case "--env":
+                               if (i+1 == top) {
+                                       Help ();
+                                       return 1;
+                               }
+                               var env = args [++i];
+                               p = env.IndexOf ('=');
+                               if (p == -1)
+                                       environment.Add (env, "");
+                               else
+                                       environment.Add (env.Substring (0, p), env.Substring (p+1));
+                               break;
+                       case "--bundled-header":
+                               bundled_header = true;
+                               break;
                        default:
                                sources.Add (args [i]);
                                break;
                        }
 
                }
+               // Modern bundling starts here
+               if (!custom_mode){
+                       if (runtime != null){
+                               // Nothing to do here, the user has chosen to manually specify --runtime nad libraries
+                       } else if (sdk_path != null) {
+                               VerifySdk (sdk_path);
+                       } else if (cross_target == "default" || cross_target == null){
+                               sdk_path = Path.GetFullPath (Path.Combine (Process.GetCurrentProcess().MainModule.FileName, "..", ".."));
+                               VerifySdk (sdk_path);
+                       } else {
+                               sdk_path = Path.Combine (targets_dir, cross_target);
+                               Console.WriteLine ("From: " + sdk_path);
+                               VerifySdk (sdk_path);
+                       }
+               }
 
+               if (fetch_target != null){
+                       var directory = Path.Combine (targets_dir, fetch_target);
+                       var zip_download = Path.Combine (directory, "sdk.zip");
+                       Directory.CreateDirectory (directory);
+                       var wc = new WebClient ();
+                       var uri = new Uri ($"{target_server}{fetch_target}");
+                       try {
+                               if (!quiet){
+                                       Console.WriteLine ($"Downloading runtime {uri} to {zip_download}");
+                               }
+                               
+                               wc.DownloadFile (uri, zip_download);
+                               ZipFile.ExtractToDirectory(zip_download, directory);
+                               File.Delete (zip_download);
+                       } catch {
+                               Console.Error.WriteLine ($"Failure to download the specified runtime from {uri}");
+                               File.Delete (zip_download);
+                               return 1;
+                       }
+                       return 0;
+               }
+               
                if (!quiet) {
                        Console.WriteLine (os_message);
                        Console.WriteLine ("Sources: {0} Auto-dependencies: {1}", sources.Count, autodeps);
                }
+
                if (sources.Count == 0 || output == null) {
                        Help ();
                        Environment.Exit (1);
                }
 
                List<string> assemblies = LoadAssemblies (sources);
+               LoadLocalizedAssemblies (assemblies);
                List<string> files = new List<string> ();
                foreach (string file in assemblies)
                        if (!QueueAssembly (files, file))
                                return 1;
-
-               if (fetch_target != null){
-                       var truntime = Path.Combine (targets_dir, fetch_target, "mono");
-                       Directory.CreateDirectory (Path.GetDirectoryName (truntime));
-                       var wc = new WebClient ();
-                       var uri = new Uri ($"{target_server}{fetch_target}");
-                       try {
-                               wc.DownloadFile (uri, truntime);
-                       } catch {
-                               Console.Error.WriteLine ($"Failure to download the specified runtime from {uri}");
-                               File.Delete (truntime);
-                               return 1;
-                       }
-                       return 0;
-               }
-               
                if (custom_mode)
                        GenerateBundles (files);
-               else {
-                       if (cross_target == "default")
-                               runtime = null;
-                       else {
-                               string truntime;
-                               if (runtime != null)
-                                       truntime = runtime;
-                               else {
-                                       if (cross_target == null){
-                                               Console.Error.WriteLine ("you should specify either a --runtime or a --cross compilation target");
-                                               Environment.Exit (1);
-                                       }
-                                       truntime = Path.Combine (targets_dir, cross_target, "mono");
-                               }
-                               if (!File.Exists (truntime)){
-                                       Console.Error.WriteLine ($"The runtime for the {cross_target} does not exist, use --fetch-target {cross_target} to download first");
-                                       return 1;
-                               }
-                       }                               
+               else 
                        GeneratePackage (files);
-               }
-               
+
+               Console.WriteLine ("Generated {0}", output);
+
                return 0;
        }
 
+       static void VerifySdk (string path)
+       {
+               if (!Directory.Exists (path))
+                       Error ($"The specified SDK path does not exist: {path}");
+               runtime = Path.Combine (sdk_path, "bin", "mono");
+               if (!File.Exists (runtime))
+                       Error ($"The SDK location does not contain a {path}/bin/mono runtime");
+               lib_path = Path.Combine (path, "lib", "mono", "4.5");
+               if (!Directory.Exists (lib_path))
+                       Error ($"The SDK location does not contain a {path}/lib/mono/4.5 directory");
+               link_paths.Add (lib_path);
+       }
+
        static string targets_dir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono", "targets");
        
        static void CommandLocalTargets ()
        {
                string [] targets;
 
-               Console.WriteLine ("Available targets:");
+               Console.WriteLine ("Available targets locally:");
                Console.WriteLine ("\tdefault\t- Current System Mono");
                try {
                        targets = Directory.GetDirectories (targets_dir);
@@ -327,7 +448,7 @@ class MakeBundle {
                        return;
                }
                foreach (var target in targets){
-                       var p = Path.Combine (target, "mono");
+                       var p = Path.Combine (target, "bin", "mono");
                        if (File.Exists (p))
                                Console.WriteLine ("\t{0}", Path.GetFileName (target));
                }
@@ -411,8 +532,9 @@ class MakeBundle {
                {
                        using (Stream fileStream = File.OpenRead (fname)){
                                var ret = fileStream.Length;
-                               
-                               Console.WriteLine ("At {0:x} with input {1}", package.Position, fileStream.Length);
+
+                               if (!quiet)
+                                       Console.WriteLine ("At {0:x} with input {1}", package.Position, fileStream.Length);
                                fileStream.CopyTo (package);
                                package.Position = package.Position + (align - (package.Position % align));
 
@@ -436,8 +558,30 @@ class MakeBundle {
                        package.Position = package.Position + (align - (package.Position % align));
                }
 
+               public void AddStringPair (string entry, string key, string value)
+               {
+                       var kbytes = Encoding.UTF8.GetBytes (key);
+                       var vbytes = Encoding.UTF8.GetBytes (value);
+
+                       Console.WriteLine ("ADDING {0} to {1}", key, value);
+                       if (kbytes.Length > 255){
+                               Console.WriteLine ("The key value can not exceed 255 characters: " + key);
+                               Environment.Exit (1);
+                       }
+                               
+                       locations [entry] = Tuple.Create (package.Position, kbytes.Length+vbytes.Length+3);
+                       package.WriteByte ((byte)kbytes.Length);
+                       package.Write (kbytes, 0, kbytes.Length);
+                       package.WriteByte (0);
+                       package.Write (vbytes, 0, vbytes.Length);
+                       package.WriteByte (0);
+                       package.Position = package.Position + (align - (package.Position % align));
+               }
+
                public void Dump ()
                {
+                       if (quiet)
+                               return;
                        foreach (var floc in locations.Keys){
                                Console.WriteLine ($"{floc} at {locations[floc]:x}");
                        }
@@ -476,7 +620,7 @@ class MakeBundle {
                        return true;
                
                if (!File.Exists (file)){
-                       Console.Error.WriteLine ("The file {0} does not exist", file);
+                       Error ("The file {0} does not exist", file);
                        return false;
                }
                maker.Add (code, file);
@@ -489,31 +633,36 @@ class MakeBundle {
                        if (IsUnix)
                                runtime = Process.GetCurrentProcess().MainModule.FileName;
                        else {
-                               Console.Error.WriteLine ("You must specify at least one runtime with --runtime or --cross");
+                               Error ("You must specify at least one runtime with --runtime or --cross");
                                Environment.Exit (1);
                        }
                }
                if (!File.Exists (runtime)){
-                       Console.Error.WriteLine ($"The specified runtime at {runtime} does not exist");
+                       Error ($"The specified runtime at {runtime} does not exist");
                        Environment.Exit (1);
                }
                
                if (ctor_func != null){
-                       Console.Error.WriteLine ("--static-ctor not supported with package bundling, you must use native compilation for this");
+                       Error ("--static-ctor not supported with package bundling, you must use native compilation for this");
                        return false;
                }
                
                var maker = new PackageMaker (output);
+               Console.WriteLine ("Using runtime: " + runtime);
                maker.AddFile (runtime);
                
                foreach (var url in files){
                        string fname = LocateFile (new Uri (url).LocalPath);
-                       string aname = Path.GetFileName (fname);
+                       string aname = MakeBundle.GetAssemblyName (fname);
 
                        maker.Add ("assembly:" + aname, fname);
-                       if (File.Exists (fname + ".config"))
+                       Console.WriteLine ("     Assembly: " + fname);
+                       if (File.Exists (fname + ".config")){
                                maker.Add ("config:" + aname, fname + ".config");
+                               Console.WriteLine ("       Config: " + runtime);
+                       }
                }
+               
                if (!MaybeAddFile (maker, "systemconfig:", config_file) || !MaybeAddFile (maker, "machineconfig:", machine_config_file))
                        return false;
 
@@ -521,6 +670,16 @@ class MakeBundle {
                        maker.Add ("config_dir:", config_dir);
                if (embedded_options != null)
                        maker.AddString ("options:", embedded_options);
+               if (environment.Count > 0){
+                       foreach (var key in environment.Keys)
+                               maker.AddStringPair ("env:" + key, key, environment [key]);
+               }
+               if (libraries.Count > 0){
+                       foreach (var alias_and_path in libraries){
+                               Console.WriteLine ("     Library:  " + alias_and_path.Value);
+                               maker.Add ("library:" + alias_and_path.Key, alias_and_path.Value);
+                       }
+               }
                maker.Dump ();
                maker.Close ();
                return true;
@@ -545,10 +704,10 @@ class MakeBundle {
                        using (StreamWriter tc = new StreamWriter (File.Create (temp_c))) {
                        string prog = null;
 
-#if XAMARIN_ANDROID
-                       tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */");
-                       tc.WriteLine ("\n#ifndef NULL\n#define NULL (void *)0\n#endif");
-                       tc.WriteLine (@"
+                       if (bundled_header) {
+                               tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */");
+                               tc.WriteLine ("\n#ifndef NULL\n#define NULL (void *)0\n#endif");
+                               tc.WriteLine (@"
 typedef struct {
        const char *name;
        const unsigned char *data;
@@ -557,10 +716,10 @@ typedef struct {
 void          mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies);
 void          mono_register_config_for_assembly (const char* assembly_name, const char* config_xml);
 ");
-#else
-                       tc.WriteLine ("#include <mono/metadata/mono-config.h>");
-                       tc.WriteLine ("#include <mono/metadata/assembly.h>\n");
-#endif
+                       } else {
+                               tc.WriteLine ("#include <mono/metadata/mono-config.h>");
+                               tc.WriteLine ("#include <mono/metadata/assembly.h>\n");
+                       }
 
                        if (compress) {
                                tc.WriteLine ("typedef struct _compressed_data {");
@@ -614,7 +773,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                        var symbolEscapeRE = new System.Text.RegularExpressions.Regex ("[^\\w_]");
                        foreach (var url in files) {
                                string fname = LocateFile (new Uri (url).LocalPath);
-                               string aname = Path.GetFileName (fname);
+                               string aname = MakeBundle.GetAssemblyName (fname);
                                string encoded = symbolEscapeRE.Replace (aname, "_");
 
                                if (prog == null)
@@ -667,7 +826,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                try {
                                        conf = File.OpenRead (config_file);
                                } catch {
-                                       Error (String.Format ("Failure to open {0}", config_file));
+                                       Error ("Failure to open {0}", config_file);
                                        return;
                                }
                                if (!quiet)
@@ -686,7 +845,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                try {
                                        conf = File.OpenRead (machine_config_file);
                                } catch {
-                                       Error (String.Format ("Failure to open {0}", machine_config_file));
+                                       Error ("Failure to open {0}", machine_config_file);
                                        return;
                                }
                                if (!quiet)
@@ -773,7 +932,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                string monoPath = GetEnv("MONOPREFIX", @"C:\Program Files (x86)\Mono");
 
                                string[] includes = new string[] {winsdkPath + @"\Include\um", winsdkPath + @"\Include\shared", vsPath + @"\include", monoPath + @"\include\mono-2.0", "." };
-                               string[] libs = new string[] { winsdkPath + @"\Lib\winv6.3\um\x86" , vsPath + @"\lib" };
+                               // string[] libs = new string[] { winsdkPath + @"\Lib\winv6.3\um\x86" , vsPath + @"\lib" };
                                var linkLibraries = new string[] {  "kernel32.lib",
                                                                                                "version.lib",
                                                                                                "Ws2_32.lib",
@@ -894,10 +1053,10 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
        {
                List<string> assemblies = new List<string> ();
                bool error = false;
-               
+
                foreach (string name in sources){
                        try {
-                               Assembly a = LoadAssembly (name);
+                               Assembly a = LoadAssemblyFile (name);
 
                                if (a == null){
                                        error = true;
@@ -916,26 +1075,90 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                        }
                }
 
-               if (error)
+               if (error) {
+                       Error ("Couldn't load one or more of the assemblies.");
                        Environment.Exit (1);
+               }
 
                return assemblies;
        }
+
+       static void LoadLocalizedAssemblies (List<string> assemblies)
+       {
+               var other = i18n.Select (x => "I18N." + x + (x.Length > 0 ? "." : "") + "dll");
+               string error = null;
+
+               foreach (string name in other) {
+                       try {
+                               Assembly a = LoadAssembly (name);
+
+                               if (a == null) {
+                                       error = "Failed to load " + name;
+                                       continue;
+                               }
+
+                               assemblies.Add (a.CodeBase);
+                       } catch (Exception) {
+                               if (skip_scan) {
+                                       if (!quiet)
+                                               Console.WriteLine ("File will not be scanned: {0}", name);
+                                       assemblies.Add (new Uri (new FileInfo (name).FullName).ToString ());
+                               } else {
+                                       throw;
+                               }
+                       }
+               }
+
+               if (error != null) {
+                       Console.Error.WriteLine ("Failure to load i18n assemblies, the following directories were searched for the assemblies:");
+                       foreach (var path in link_paths){
+                               Console.Error.WriteLine ("   Path: " + path);
+                       }
+                       if (custom_mode){
+                               Console.WriteLine ("In Custom mode, you need to provide the directory to lookup assemblies from using -L");
+                       }
+                       
+                       Error ("Couldn't load one or more of the i18n assemblies: " + error);
+                       Environment.Exit (1);
+               }
+       }
+
        
        static readonly Universe universe = new Universe ();
        static readonly Dictionary<string, string> loaded_assemblies = new Dictionary<string, string> ();
-       
+
+       public static string GetAssemblyName (string path)
+       {
+               string resourcePathSeparator = style == "windows" ? "\\\\" : "/";
+               string name = Path.GetFileName (path);
+
+               // A bit of a hack to support satellite assemblies. They all share the same name but
+               // are placed in subdirectories named after the locale they implement. Also, all of
+               // them end in .resources.dll, therefore we can use that to detect the circumstances.
+               if (name.EndsWith (".resources.dll", StringComparison.OrdinalIgnoreCase)) {
+                       string dir = Path.GetDirectoryName (path);
+                       int idx = dir.LastIndexOf (Path.DirectorySeparatorChar);
+                       if (idx >= 0) {
+                               name = dir.Substring (idx + 1) + resourcePathSeparator + name;
+                               Console.WriteLine ($"Storing satellite assembly '{path}' with name '{name}'");
+                       } else if (!quiet)
+                               Console.WriteLine ($"Warning: satellite assembly {path} doesn't have locale path prefix, name conflicts possible");
+               }
+
+               return name;
+       }
+
        static bool QueueAssembly (List<string> files, string codebase)
        {
-               // Console.WriteLine ("CODE BASE IS {0}", codebase);
+               //Console.WriteLine ("CODE BASE IS {0}", codebase);
                if (files.Contains (codebase))
                        return true;
 
                var path = new Uri(codebase).LocalPath;
-               var name = Path.GetFileName (path);
+               var name = GetAssemblyName (path);
                string found;
                if (loaded_assemblies.TryGetValue (name, out found)) {
-                       Error (string.Format ("Duplicate assembly name `{0}'. Both `{1}' and `{2}' use same assembly name.", name, path, found));
+                       Error ("Duplicate assembly name `{0}'. Both `{1}' and `{2}' use same assembly name.", name, path, found);
                        return false;
                }
 
@@ -946,9 +1169,18 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                        return true;
                try {
                        Assembly a = universe.LoadFile (path);
+                       if (a == null) {
+                               Error ("Unable to to load assembly `{0}'", path);
+                               return false;
+                       }
 
                        foreach (AssemblyName an in a.GetReferencedAssemblies ()) {
-                               a = universe.Load (an.FullName);
+                               a = LoadAssembly (an.Name);
+                               if (a == null) {
+                                       Error ("Unable to load assembly `{0}' referenced by `{1}'", an.Name, path);
+                                       return false;
+                               }
+
                                if (!QueueAssembly (files, a.CodeBase))
                                        return false;
                        }
@@ -960,56 +1192,63 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                return true;
        }
 
-       static Assembly LoadAssembly (string assembly)
+       //
+       // Loads an assembly from a specific path
+       //
+       static Assembly LoadAssemblyFile (string assembly)
        {
-               Assembly a;
+               Assembly a = null;
                
                try {
-                       char[] path_chars = { '/', '\\' };
+                       if (!quiet)
+                               Console.WriteLine ("Attempting to load assembly: {0}", assembly);
+                       a = universe.LoadFile (assembly);
+                       if (!quiet)
+                               Console.WriteLine ("Assembly {0} loaded successfully.", assembly);
                        
-                       if (assembly.IndexOfAny (path_chars) != -1) {
-                               a = universe.LoadFile (assembly);
-                       } else {
-                               string ass = assembly;
-                               if (ass.EndsWith (".dll"))
-                                       ass = assembly.Substring (0, assembly.Length - 4);
-                               a = universe.Load (ass);
-                       }
-                       return a;
                } catch (FileNotFoundException){
-                       string total_log = "";
-                       
-                       foreach (string dir in link_paths){
-                               string full_path = Path.Combine (dir, assembly);
-                               if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
-                                       full_path += ".dll";
-                               
-                               try {
-                                       a = universe.LoadFile (full_path);
-                                       return a;
-                               } catch (FileNotFoundException ff) {
-                                       total_log += ff.FusionLog;
-                                       continue;
-                               }
-                       }
-                       Error ("Cannot find assembly `" + assembly + "'" );
-                       if (!quiet)
-                               Console.WriteLine ("Log: \n" + total_log);
+                       Error ($"Cannot find assembly `{assembly}'");
                } catch (IKVM.Reflection.BadImageFormatException f) {
                        if (skip_scan)
                                throw;
-                       Error ("Cannot load assembly (bad file format) " + f.Message);
+                       Error ($"Cannot load assembly (bad file format) " + f.Message);
                } catch (FileLoadException f){
-                       Error ("Cannot load assembly " + f.Message);
+                       Error ($"Cannot load assembly " + f.Message);
                } catch (ArgumentNullException){
-                       Error("Cannot load assembly (null argument)");
+                       Error( $"Cannot load assembly (null argument)");
                }
-               return null;
+               return a;
        }
 
-       static void Error (string msg)
+       //
+       // Loads an assembly from any of the link directories provided
+       //
+       static Assembly LoadAssembly (string assembly)
+       {
+               string total_log = "";
+               foreach (string dir in link_paths){
+                       string full_path = Path.Combine (dir, assembly);
+                       if (!quiet)
+                               Console.WriteLine ("Attempting to load assembly from: " + full_path);
+                       if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
+                               full_path += ".dll";
+                       
+                       try {
+                               var a = universe.LoadFile (full_path);
+                               return a;
+                       } catch (FileNotFoundException ff) {
+                               total_log += ff.FusionLog;
+                               continue;
+                       }
+               }
+               if (!quiet)
+                       Console.WriteLine ("Log: \n" + total_log);
+               return null;
+       }
+       
+       static void Error (string msg, params object [] args)
        {
-               Console.Error.WriteLine ("ERROR: " + msg);
+               Console.Error.WriteLine ("ERROR: {0}", string.Format (msg, args));
                Environment.Exit (1);
        }
 
@@ -1017,37 +1256,45 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
        {
                Console.WriteLine ("Usage is: mkbundle [options] assembly1 [assembly2...]\n\n" +
                                   "Options:\n" +
-                                  "    --config F          Bundle system config file `F'\n" +
-                                  "    --config-dir D      Set MONO_CFG_DIR to `D'\n" +
-                                  "    --deps              Turns on automatic dependency embedding (default on simple)\n" +
-                                  "    -L path             Adds `path' to the search path for assemblies\n" +
-                                  "    --machine-config F  Use the given file as the machine.config for the application.\n" +
-                                  "    -o out              Specifies output filename\n" +
-                                  "    --nodeps            Turns off automatic dependency embedding (default on custom)\n" +
-                                  "    --skip-scan         Skip scanning assemblies that could not be loaded (but still embed them).\n" +
+                                  "    --config F           Bundle system config file `F'\n" +
+                                  "    --config-dir D       Set MONO_CFG_DIR to `D'\n" +
+                                  "    --deps               Turns on automatic dependency embedding (default on simple)\n" +
+                                  "    -L path              Adds `path' to the search path for assemblies\n" +
+                                  "    --machine-config F   Use the given file as the machine.config for the application.\n" +
+                                  "    -o out               Specifies output filename\n" +
+                                  "    --nodeps             Turns off automatic dependency embedding (default on custom)\n" +
+                                  "    --skip-scan          Skip scanning assemblies that could not be loaded (but still embed them).\n" +
+                                  "    --i18n ENCODING      none, all or comma separated list of CJK, MidWest, Other, Rare, West.\n" +
+                                  "    -v                   Verbose output\n" + 
+                                  "    --bundled-header     Do not attempt to include 'mono-config.h'. Define the entry points directly in the generated code\n" +
                                   "\n" + 
                                   "--simple   Simple mode does not require a C toolchain and can cross compile\n" + 
-                                  "    --cross TARGET      Generates a binary for the given TARGET\n"+
-                                  "    --local-targets     Lists locally available targets\n" +
-                                  "    --list-targets      Lists available targets on the remote server\n" +
-                                  "    --options OPTIONS   Embed the specified Mono command line options on target\n" +
-                                  "    --runtime RUNTIME   Manually specifies the Mono runtime to use\n" +
-                                  "    --target-server URL Specified a server to download targets from, default is " + target_server + "\n" +
+                                  "    --cross TARGET       Generates a binary for the given TARGET\n"+
+                                  "    --env KEY=VALUE      Hardcodes an environment variable for the target\n" +
+                                  "    --fetch-target NAME  Downloads the target SDK from the remote server\n" + 
+                                  "    --library [LIB,]PATH Bundles the specified dynamic library to be used at runtime\n" +
+                                  "                         LIB is optional shortname for file located at PATH\n" + 
+                                  "    --list-targets       Lists available targets on the remote server\n" +
+                                  "    --local-targets      Lists locally available targets\n" +
+                                  "    --options OPTIONS    Embed the specified Mono command line options on target\n" +
+                                  "    --runtime RUNTIME    Manually specifies the Mono runtime to use\n" +
+                                  "    --sdk PATH           Use a Mono SDK root location instead of a target\n" + 
+                                  "    --target-server URL  Specified a server to download targets from, default is " + target_server + "\n" +
                                   "\n" +
                                   "--custom   Builds a custom launcher, options for --custom\n" +
-                                  "    -c                  Produce stub only, do not compile\n" +
-                                  "    -oo obj             Specifies output filename for helper object file\n" +
+                                  "    -c                   Produce stub only, do not compile\n" +
+                                  "    -oo obj              Specifies output filename for helper object file\n" +
                                   "    --dos2unix[=true|false]\n" +
-                                  "                        When no value provided, or when `true` specified\n" +
-                                  "                        `dos2unix` will be invoked to convert paths on Windows.\n" +
-                                  "                        When `--dos2unix=false` used, dos2unix is NEVER used.\n" +
-                                  "    --keeptemp          Keeps the temporary files\n" +
-                                  "    --static            Statically link to mono libs\n" +
-                                  "    --nomain            Don't include a main() function, for libraries\n" +
-                                  "    --custom-main C     Link the specified compilation unit (.c or .obj) with entry point/init code\n" +
-                                  "    -z                  Compress the assemblies before embedding.\n" +
-                                  "    --static-ctor ctor  Add a constructor call to the supplied function.\n" +
-                                  "                        You need zlib development headers and libraries.\n");
+                                  "                         When no value provided, or when `true` specified\n" +
+                                  "                         `dos2unix` will be invoked to convert paths on Windows.\n" +
+                                  "                         When `--dos2unix=false` used, dos2unix is NEVER used.\n" +
+                                  "    --keeptemp           Keeps the temporary files\n" +
+                                  "    --static             Statically link to mono libs\n" +
+                                  "    --nomain             Don't include a main() function, for libraries\n" +
+                                  "    --custom-main C      Link the specified compilation unit (.c or .obj) with entry point/init code\n" +
+                                  "    -z                   Compress the assemblies before embedding.\n" +
+                                  "    --static-ctor ctor   Add a constructor call to the supplied function.\n" +
+                                  "                         You need zlib development headers and libraries.\n");
        }
 
        [DllImport ("libc")]
@@ -1162,7 +1409,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                        p.WaitForExit ();
                        int ret = p.ExitCode;
                        if (ret != 0){
-                               Error (String.Format("[Fail] {0}", ret));
+                               Error ("[Fail] {0}", ret);
                        }
                }
        }