From bb1eda94c0b3022f4b8c52e7cd1eea9073f607f4 Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Thu, 1 Sep 2016 12:58:48 -0400 Subject: [PATCH] Bundle other options (#3501) * [mkbundle] add support for baking environment variables, make endian-independent * [runtime] Bundle support, implement missing stream processing --- man/mkbundle.1 | 32 +++++++++++++++++++++ mcs/tools/mkbundle/mkbundle.cs | 39 ++++++++++++++++++++++++++ mono/mini/main.c | 51 +++++++++++++++++++++++++++++----- 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/man/mkbundle.1 b/man/mkbundle.1 index 160786d94f6..95861e0aeab 100644 --- a/man/mkbundle.1 +++ b/man/mkbundle.1 @@ -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 diff --git a/mcs/tools/mkbundle/mkbundle.cs b/mcs/tools/mkbundle/mkbundle.cs index b6244881f61..702d74d16a7 100755 --- a/mcs/tools/mkbundle/mkbundle.cs +++ b/mcs/tools/mkbundle/mkbundle.cs @@ -52,6 +52,7 @@ class MakeBundle { static bool custom_mode = true; static string embedded_options = null; static string runtime = null; + static Dictionary environment = new Dictionary(); 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" + diff --git a/mono/mini/main.c b/mono/mini/main.c index 82059df3c3e..8d2f5bd1712 100644 --- a/mono/mini/main.c +++ b/mono/mini/main.c @@ -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); -- 2.25.1