Make mkbundle use SDKs instead of a single runtime for cross compilation
authorMiguel de Icaza <miguel@gnome.org>
Mon, 24 Oct 2016 16:47:31 +0000 (11:47 -0500)
committerGitHub <noreply@github.com>
Mon, 24 Oct 2016 16:47:31 +0000 (11:47 -0500)
Make mkbundle use SDKs instead of a single runtime for cross compilation

man/mkbundle.1
mcs/tools/mkbundle/Makefile
mcs/tools/mkbundle/mkbundle.cs
mono/metadata/assembly.c
mono/metadata/assembly.h

index 305cfcc7f7224d787f72e728310c6a0e66c49d43..3d067eb27ef9892b7d979a57b40f1d4fa17c4814 100644 (file)
@@ -24,7 +24,8 @@ dependencies referenced, use the "--deps" command line option.
 There are two modes of operation, one uses an existing Mono binary or
 a server-hosted list of binaries and is enabled when you use either
 the 
-.B --cross
+.B --cross,
+.B --sdk
 or the
 .B --runtime
 command line options.   
@@ -119,10 +120,19 @@ are using (1.0 or 2.0)
 When passed, DIR will be set for the MONO_CFG_DIR environment variable
 .TP
 .I "--cross target"
-Creates a bundle for the specified target platform.   The target
-must be a directory in ~/.mono/targets/ that contains a "mono"
-binary.   You can fetch various targets using the --fetch-target
-command line option.
+Use this to request mkbundle generate a cross-compiled binary.  It
+Creates a bundle for the specified target platform.  The target must
+be a directory in ~/.mono/targets/ that contains an SDK installation
+as produced by the mono-package-runtime tool.  You can get a list of
+the precompiled versions of the runtime using --list-targets and you
+can fetch a specific target using the --fetch-target command line
+option.
+.Sp
+This flag is mutually exclusive with 
+.I --sdk
+which is used to specify an absolute path to resolve the Mono runtime
+from and the --runtime option which is used to manually construct the
+cross-platform package.
 .TP
 .I "--deps"
 This option will bundle all of the referenced assemblies for the
@@ -203,7 +213,26 @@ executed.
 Additionally, users of your binary can still configure their own
 options by setting the 
 .I MONO_ENV_OPTIONS
-environment variable.   
+environment variable.
+.TP
+.I "--sdk SDK_PATH"
+Use this flag to specify a path from which mkbundle will resolve the
+Mono SDK from.   The SDK path should be the prefix path that you used
+to configure a Mono installation.   And would typically contain files
+lik
+.I SDK_PATH/bin/mono
+,
+.I SDK_PATH/lib/mono/4.5
+and so on.
+.Sp
+When this flag is specified,
+.I mkbundle
+will resolve the runtime, the framework libraries, unmanaged resources
+and configuration files from the files located in this directory.
+.Sp
+This flag is mutually exlusive with 
+.I --cross
+.
 .TP
 .I "--target-server SERVER"
 By default the mkbundle tool will download from a Mono server the
index 05faf5e8c685e1ec0ce4d295e848619f974d28c8..64a1b9cbf928bc1824705bdebd3d95049321cb23 100644 (file)
@@ -11,10 +11,23 @@ RESOURCE_FILES = $(OTHER_RES)
 LOCAL_MCS_FLAGS= $(OTHER_RES:%=-resource:%)
 
 LOCAL_MCS_FLAGS += -d:STATIC,NO_SYMBOL_WRITER,NO_AUTHENTICODE
-LIB_REFS = System.Xml System System.Core
+LIB_REFS = System.Xml System System.Core System.IO.Compression.FileSystem
 
 EXTRA_DISTFILES = $(RESOURCE_FILES)
 
 include ../../build/executable.make
 
 mkbundle.exe: $(RESOURCE_FILES)
+
+test-simple: simple.exe
+       mono --debug $(the_lib) --simple simple.exe -o foo && ./foo
+       mono --debug $(the_lib) --cross default simple.exe -o foo && ./foo
+       mono --debug $(the_lib) --sdk `dirname \`which mono\``/.. simple.exe -o foo && ./foo
+       -rm DEMO.zip
+       mono-package-runtime `dirname \`which mono\``/.. DEMO
+       mkdir -p ~/.mono/targets/DEMO
+       unzip -d ~/.mono/targets/DEMO DEMO.zip
+       mono --debug $(the_lib) --cross DEMO simple.exe -o foo && ./foo
+
+simple.exe: Makefile
+       echo 'class X { static void Main () { System.Console.WriteLine ("OK");}}' > simple.cs && mcs simple.cs
index 447540860f50024ec14df5685b4cf49d5fc3218d..1ea1d7402455704a22e4b237c25c8fe15f3d3f9b 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
 //
@@ -52,6 +62,8 @@ class MakeBundle {
        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",
@@ -74,7 +86,7 @@ class MakeBundle {
                link_paths.Add (".");
 
                DetectOS ();
-               
+
                for (int i = 0; i < top; i++){
                        switch (args [i]){
                        case "--help": case "-h": case "-?":
@@ -121,6 +133,8 @@ 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];
@@ -146,10 +160,8 @@ class MakeBundle {
                                        alias = lspec.Substring (0, p);
                                        path = lspec.Substring (p+1);
                                }
-                               if (!File.Exists (path)){
-                                       Console.Error.WriteLine ($"The specified library file {path} does not exist");
-                                       return 1;
-                               }
+                               if (!File.Exists (path))
+                                       Error ($"The specified library file {path} does not exist");
                                libraries [alias] = path;
                                break;
 
@@ -162,10 +174,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":
@@ -191,11 +203,24 @@ 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];
@@ -283,7 +308,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;
                                }
                                        
@@ -328,21 +353,39 @@ class MakeBundle {
                        }
 
                }
+               // 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 truntime = Path.Combine (targets_dir, fetch_target, "mono");
-                       Directory.CreateDirectory (Path.GetDirectoryName (truntime));
+                       var directory = Path.Combine (targets_dir, fetch_target);
+                       var zip_download = Path.Combine (directory, "sdk.zip");
+                       Directory.CreateDirectory (Path.GetDirectoryName (directory));
                        var wc = new WebClient ();
                        var uri = new Uri ($"{target_server}{fetch_target}");
                        try {
                                if (!quiet){
-                                       Console.WriteLine ($"Downloading runtime {uri} to {truntime}");
+                                       Console.WriteLine ($"Downloading runtime {uri} to {zip_download}");
                                }
                                
-                               wc.DownloadFile (uri, truntime);
+                               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 (truntime);
+                               File.Delete (zip_download);
                                return 1;
                        }
                        return 0;
@@ -359,48 +402,41 @@ class MakeBundle {
                }
 
                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 (custom_mode)
                        GenerateBundles (files);
-               else {
-                       if (cross_target == "default")
-                               runtime = null;
-                       else {
-                               if (runtime == null){
-                                       if (cross_target == null){
-                                               Console.Error.WriteLine ("you should specify either a --runtime or a --cross compilation target");
-                                               Environment.Exit (1);
-                                       }
-                                       runtime = Path.Combine (targets_dir, cross_target, "mono");
-                                       if (!File.Exists (runtime)){
-                                               Console.Error.WriteLine ($"The runtime for the {cross_target} does not exist, use --fetch-target {cross_target} to download first");
-                                               return 1;
-                                       }
-                               } else {
-                                       if (!File.Exists (runtime)){
-                                               Console.Error.WriteLine ($"The Mono runtime specified with --runtime does not exist");
-                                               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);
@@ -408,7 +444,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));
                }
@@ -580,7 +616,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);
@@ -593,17 +629,17 @@ 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;
                }
                
@@ -786,7 +822,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)
@@ -805,7 +841,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)
@@ -1014,11 +1050,9 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                List<string> assemblies = new List<string> ();
                bool error = false;
 
-               var other = i18n.Select (x=> "I18N." + x + (x.Length > 0 ? "." : "") + "dll");
-               
-               foreach (string name in sources.Concat (other)){
+               foreach (string name in sources){
                        try {
-                               Assembly a = LoadAssembly (name);
+                               Assembly a = LoadAssemblyFile (name);
 
                                if (a == null){
                                        error = true;
@@ -1039,9 +1073,40 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
 
                if (error)
                        Environment.Exit (1);
-
+               
                return assemblies;
        }
+
+       static void LoadLocalizedAssemblies (List<string> assemblies)
+       {
+               var other = i18n.Select (x => "I18N." + x + (x.Length > 0 ? "." : "") + "dll");
+               bool error = false;
+
+               foreach (string name in other) {
+                       try {
+                               Assembly a = LoadAssembly (name);
+
+                               if (a == null) {
+                                       error = true;
+                                       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)
+                       Environment.Exit (1);
+       }
+
        
        static readonly Universe universe = new Universe ();
        static readonly Dictionary<string, string> loaded_assemblies = new Dictionary<string, string> ();
@@ -1076,7 +1141,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                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;
                }
 
@@ -1089,7 +1154,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                        Assembly a = universe.LoadFile (path);
 
                        foreach (AssemblyName an in a.GetReferencedAssemblies ()) {
-                               a = universe.Load (an.FullName);
+                               LoadAssembly (an.FullName);
                                if (!QueueAssembly (files, a.CodeBase))
                                        return false;
                        }
@@ -1101,56 +1166,57 @@ 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 (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;
+                       a = universe.LoadFile (assembly);
                } 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 (!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));
+               throw new Exception ();
                Environment.Exit (1);
        }
 
@@ -1172,12 +1238,14 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                   "--simple   Simple mode does not require a C toolchain and can cross compile\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" +
@@ -1308,7 +1376,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);
                        }
                }
        }
index d4cbfa285b3116b323d886d0b2e97230be488c6c..209e4ab4f988192fe280953fe7d5017c547d56b3 100644 (file)
@@ -570,7 +570,7 @@ mono_assembly_getrootdir (void)
  * Returns: a string with the directory, this string should be freed by
  * the caller.
  */
-G_CONST_RETURN gchar *
+gchar *
 mono_native_getrootdir (void)
 {
        gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL);
index 6126feda0e1c9053ddc792e2a466a1aa60dd2953..0cde65a2b5b1d244f5ae0e5818ee303180332e75 100644 (file)
@@ -37,8 +37,8 @@ MONO_RT_EXTERNAL_ONLY MONO_API MonoImage*    mono_assembly_load_module (MonoAsse
 MONO_API void          mono_assembly_close      (MonoAssembly *assembly);
 MONO_API void          mono_assembly_setrootdir (const char *root_dir);
 MONO_API MONO_CONST_RETURN char *mono_assembly_getrootdir (void);
-MONO_API MONO_CONST_RETURN char *mono_native_getrootdir (void);
-MONO_API void        mono_assembly_foreach    (MonoFunc func, void* user_data);
+MONO_API char         *mono_native_getrootdir (void);
+MONO_API void         mono_assembly_foreach    (MonoFunc func, void* user_data);
 MONO_API void          mono_assembly_set_main   (MonoAssembly *assembly);
 MONO_API MonoAssembly *mono_assembly_get_main   (void);
 MONO_API MonoImage    *mono_assembly_get_image  (MonoAssembly *assembly);