Bundle other options (#3501)
authorMiguel de Icaza <miguel@gnome.org>
Thu, 1 Sep 2016 16:58:48 +0000 (12:58 -0400)
committerGitHub <noreply@github.com>
Thu, 1 Sep 2016 16:58:48 +0000 (12:58 -0400)
* [mkbundle] add support for baking environment variables, make endian-independent

* [runtime] Bundle support, implement missing stream processing

man/mkbundle.1
mcs/tools/mkbundle/mkbundle.cs
mono/mini/main.c

index 160786d94f6a2643fba63837e3f12cdc5826658b..95861e0aeab8e58ebbaaf64c8758ffe813b0f818 100644 (file)
@@ -40,6 +40,16 @@ command:
        $ mkbundle -o hello --simple hello.exe
 
 .fi
+.PP
+You can configure options to be passed to the Mono runtime directly
+into your executable, for this, use the 
+.I --options
+flag.  For example, the following disables inlining, by passing the
+"-O=-inline" command line option to the embedded executable:
+.nf
+
+       $ mkbundle -o hello --options -O=-inline --simple hello.exe
+
 .PP
 The simple version allows for cross-compiling, this requires a Mono
 runtime to be installed in the ~/.mono/targets/TARGET/mono to be
@@ -119,6 +129,12 @@ This option will bundle all of the referenced assemblies for the
 assemblies listed on the command line option.  This is useful to
 distribute a self-contained image.
 .TP
+.I "--env KEY=VALUE"
+Use this to hardcode an environment variable at runtime for KEY to be
+mapped to VALUE.   This is useful in scenarios where you want to
+enable certain Mono runtime configuration options that are controlled
+by environment variables.
+.TP
 .I "--fetch-target target"
 Downloads a precompiled runtime for the specified target from the Mono
 distribution site.
@@ -167,6 +183,22 @@ image created.
 Places the output on `out'.  If the flag -c is specified, this is the
 C host program.  If not, this contains the resulting executable.
 .TP
+.I "--options OPTS"
+Since the resulting executable will be treated as a standalone
+program, you can use this option to pass configuration options to the
+Mono runtime and bake those into the resulting executable.  These
+options are specified as 
+.I OPTS.
+.Sp
+You can use the above to configure options that you would typically
+pass on the command line to Mono, before the main program is
+executed.   
+.Sp
+Additionally, users of your binary can still configure their own
+options by setting the 
+.I MONO_ENV_OPTIONS
+environment variable.   
+.TP
 .I "--target-server SERVER"
 By default the mkbundle tool will download from a Mono server the
 target runtimes, you can specify a different server to provide
index b6244881f61d564bb9115a578ab81bd474486f95..702d74d16a7dd43ecab4b9be6f71f9017fe6f755 100755 (executable)
@@ -52,6 +52,7 @@ class MakeBundle {
        static bool custom_mode = true;
        static string embedded_options = null;
        static string runtime = null;
+       static Dictionary<string,string> environment = new Dictionary<string,string>();
        static string [] i18n = new string [] {
                "West",
                ""
@@ -281,6 +282,19 @@ class MakeBundle {
                        case "--quiet":
                                quiet = true;
                                break;
+                       case "-e":
+                       case "--env":
+                               if (i+1 == top) {
+                                       Help ();
+                                       return 1;
+                               }
+                               var env = args [++i];
+                               var p = env.IndexOf ('=');
+                               if (p == -1)
+                                       environment.Add (env, "");
+                               else
+                                       environment.Add (env.Substring (0, p), env.Substring (p+1));
+                               break;
                        default:
                                sources.Add (args [i]);
                                break;
@@ -477,6 +491,26 @@ 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)
@@ -564,6 +598,10 @@ 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]);
+               }
                maker.Dump ();
                maker.Close ();
                return true;
@@ -1080,6 +1118,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                   "    --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" +
+                                  "    --env KEY=VALUE     Hardcodes an environment variable for the target\n" +
                                   "\n" +
                                   "--custom   Builds a custom launcher, options for --custom\n" +
                                   "    -c                  Produce stub only, do not compile\n" +
index 82059df3c3e79582f23118a4d31d22ce688687af..8d2f5bd17121d78ef98c4fba3c8e1f427d4b86da 100644 (file)
@@ -29,8 +29,40 @@ mono_main_with_options (int argc, char *argv [])
        return mono_main (argc, argv);
 }
 
-#define STREAM_INT(x) (*(uint32_t*)x)
-#define STREAM_LONG(x) (*(uint64_t*)x)
+#define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x))
+#define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x))
+
+/**
+ * Loads a chunk of data from the file pointed to by the
+ * @fd starting at the file offset @offset for @size bytes
+ * and returns an allocated version of that string, or NULL
+ * on error.
+ */
+static char *
+load_from_region (int fd, uint64_t offset, uint64_t size)
+{
+       char *buffer;
+       off_t loc;
+       int status;
+       
+       do {
+               loc = lseek (fd, offset, SEEK_SET);
+       } while (loc == -1 && errno == EINTR);
+       if (loc == -1)
+               return NULL;
+       buffer = g_malloc (size + 1);
+       if (buffer == NULL)
+               return NULL;
+       buffer [size] = 0;
+       do {
+               status = read (fd, buffer, size);
+       } while (status == -1 && errno == EINTR);
+       if (status == -1){
+               g_free (buffer);
+               return NULL;
+       }
+       return buffer;
+}
 
 static gboolean
 probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
@@ -104,15 +136,20 @@ probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
                        char *config = kind + strlen ("config:");
                        char *aname = g_strdup (config);
                        aname [strlen(aname)-strlen(".config")] = 0;
-                       mono_register_config_for_assembly (aname, config);
+                       mono_register_config_for_assembly (aname, load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){
-                       mono_config_parse_memory (kind + strlen ("systemconfig:"));
+                       mono_config_parse_memory (load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "options:", strlen ("options:")) == 0){
-                       mono_parse_options_from (kind + strlen("options:"), ref_argc, ref_argv);
+                       mono_parse_options_from (load_from_region (fd, offset, item_size), ref_argc, ref_argv);
                } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){
-                       mono_set_dirs (getenv ("MONO_PATH"), kind + strlen ("config_dir:"));
+                       mono_set_dirs (getenv ("MONO_PATH"), load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) {
-                       mono_register_machine_config (kind + strlen ("machineconfig:"));
+                       mono_register_machine_config (load_from_region (fd, offset, item_size));
+               } else if (strncmp (kind, "env:", strlen ("env:")) == 0){
+                       char *data = load_from_region (fd, offset, item_size);
+                       uint8_t count = *data++;
+                       char *value = data + count + 1;
+                       g_setenv (data, value, FALSE);
                } else {
                        fprintf (stderr, "Unknown stream on embedded package: %s\n", kind);
                        exit (1);