Mono(mkbundle)

πŸ“¦
version 6.14.1 Β· 2 versions
, in alpine 3.24, epel 10, fedora 44, nixos 26.05, nixpkgs unstable, ubuntu resolute, ubuntu stonking

okay so you found the manual for Mono(mkbundle) bestie πŸ’– β€” this is the whole scoop on mkbundle and its lil sibling mkbundle2, the tools that smoosh your assemblies into one cute standalone executable. let's get into it 🫢


✨ NAME ✨

mkbundle, mkbundle2 β€” creates a bundled executable. that's the whole job, no cap πŸ‘‘


πŸŽ€ SYNOPSIS πŸŽ€

mkbundle [options] assembly1 [assembly2 ...]

πŸ’… DESCRIPTION πŸ’…

okay so here's the tea 🍡: mkbundle generates an executable program that will contain static copies of the assemblies you list on the command line. by default, only the assemblies you actually name on the command line get included in the bundle β€” that's it, that's the list.

but if you want it to automatically include all of the dependencies referenced, you just hit it with the --deps command line option. easy. ✨

there are two modes of operation, and this is important so listen up 🎧:

  • one mode uses an existing Mono binary, or a server-hosted list of binaries. it's enabled when you use either the --cross, --sdk or the --runtime command line options.
  • an older mechanism creates a small C stub that links against the libmono library to produce a self-contained executable, and it requires a C compiler. that one is described in the "OLD EMBEDDING" section below πŸ‘‡

🌸 a lil hello world moment 🌸

for example, to create a bundle for hello world, use the following command:

$ mkbundle -o hello --simple hello.exe

πŸ¦‹ passing options to the runtime πŸ¦‹

you can configure options to be passed to the Mono runtime directly into your executable β€” for this, use the --options flag. like, for example, the following disables inlining, by passing the "-O=-inline" command line option to the embedded executable:

$ mkbundle -o hello --options -O=-inline --simple hello.exe

πŸ’€ cross-compiling era πŸ’€

the simple version allows for cross-compiling. this requires a Mono runtime to be installed in the ~/.mono/targets/TARGET/mono to be available. you can use the --local-targets to list all available targets, and the --cross argument to specify the target, like this:

$ mkbundle --local-targets
Available targets:
     default   - Current System Mono
     4.4.0-macosx-x86
     4.4.0-debian-8-arm64
$ mkbundle --cross 4.4.0-debian-8-powerpc hello.exe -o hello-debian

the above will bundle your native library into hello-debian for a Debian 8 system running on a PowerPC machine. iconic tbh πŸ‘‘


⭐ pre-packaged binaries, we love to see it ⭐

we provide pre-packages binaries for Mono for various architectures, which allow you to cross compile. use the --list-targets to get a list of all targets supported, and use the --fetch-target flag to retrieve a target that you do not have installed, like this:

$ mkbundle --list-targets
Cross-compilation targets available:
4.4.0-linux-libc2.13-amd64
4.4.0-linux-libc2.13-armel
4.4.0-linux-libc2.13-armhf
4.4.0-linux-libc2.13-i386
4.4.0-macos-10.7-amd64
4.4.0-macos-10.7-i386
4.4.2-linux-libc2.13-amd64
4.4.2-linux-libc2.13-armel
4.4.2-linux-libc2.13-armhf
4.4.2-linux-libc2.13-i386
4.4.2-macos-10.7-amd64
4.4.2-macos-10.7-i386

$ mkbundle --fetch-target 4.4.2-macos-10.7-i386

and then you can produce a binary that will run on 32-bit Mono on MacOS:

$ mkbundle --cross 4.4.2-macos-10.7-i386 hello.exe -o hello-macos
πŸ’‘ downloaded targets are stored ~/.mono/targets directory.

and that's part 1 bestie, the plot thickens from here πŸ‘€

✨ OPTIONS ✨

okay bestie, buckle up, this is the flag buffet and it is long. every single one is here, no skips, no shortcuts, periodt. πŸ’…

  • --config FILE β€” says "yes, bundle a DLLMAP Mono config file too." In simple and cross compiler modes, if you don't hand over a config file, it just grabs the one for the current target (the system one in simple mode, or the one that came from the cross compilation target in cross compiling mode). easy peasy. πŸŽ€
  • --config-dir DIR β€” pass this and DIR gets set for the MONO_CFG_DIR environment variable. that's it, that's the whole flag.
  • --cross target β€” use this when you want mkbundle to generate a cross-compiled binary, no cap. It creates a bundle for the specified target platform. the target has to 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. πŸ¦‹
heads up bestie: this flag is mutually exclusive with --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). pick a lane. πŸ’€
  • --deps β€” this one bundles all of the referenced assemblies for the assemblies listed on the command line option. lowkey iconic when you want to distribute a self-contained image. πŸ“¦
  • --env KEY=VALUE β€” hardcodes an environment variable at runtime, so KEY maps to VALUE. super useful when you want to enable certain Mono runtime configuration options that are controlled by environment variables. baked in, forever. πŸ“
  • --fetch-target target β€” downloads a precompiled runtime for the specified target from the Mono distribution site. go get it. 🌈
  • --i18n encoding β€” specifies which encoding tables ship with the executable. by default, Mono ships the supporting I18N.dll assembly and the I18N.West.dll assembly. if your app is gonna use System.Text.Encoding.GetEncoding with encodings other than the West encodings, you should specify them right here. ✨
    • you can use the none parameter to request that no implicit encodings get bundled, including the supporting I18N.dll β€” use this option if you have ran a linker on your own.
    • you can use the all flag to bundle all available encodings.
    • or you can use a comma delimited list of the words CJK, MidWest, Other, Rare and West to specify which encoding assemblies to distribute.
  • -L path β€” adds path to the search list for assemblies. the rules are the same as for the compiler -lib: or -L flags. πŸ”
  • --library [LIB,]PATH β€” embeds the dynamic library file pointed to by PATH and optionally gives it the name LIB into the bundled executable. this is how you ship native library dependencies that are unpacked at startup and loaded from the runtime. specify multiple libraries in dependency order, where later ones on the command line depend on earlier ones. order matters, don't play with it. 🫢
  • --lists-targets β€” lists all of the available local cross compilation targets available as precompiled binaries on the Mono distribution server. window shopping, basically. πŸ›οΈ
  • --local-targets β€” lists all of the available local cross compilation targets. the ones already in your house. 🏠
  • --cil-strip PATH β€” provides a CIL stripper that mkbundle will use if able to. the intended use is to help reduce file size on AOT. skinnier binaries, we love to see it. 🧚
  • --in-tree path/to/mono/source/root β€” provides mkbundle with a mono source repository from which to pull the necessary headers for compilation. this lets mkbundle run out of the project's source tree, which is useful for working with multiple runtimes and for testing without installing. 🌸
  • --managed-linker PATH β€” provides mkbundle access to a managed linker to preprocess the assemblies. pre-processing era. πŸ’œ
  • --machine-config FILE β€” uses the given FILE as the machine.config file for the generated application. the machine config contains an XML file that is used by System.Configuration APIs to configure the .NET stack. typically this is $prefix/etc/mono/4.5/machine.config. πŸ“œ

if you want to disable this automatic bundling, you can use the --no-machine-config flag. in the simple and cross compiler modes, if no machine.config file is specified, the one for the current target is picked (either the system one in the case of the simple mode, or the one that came from the cross compilation target for the cross compiling mode).

  • --no-config β€” in simple or cross compiling mode, this prevents mkbundle from automatically bundling a config file. hands off, thank you. πŸ™…
  • --nodeps β€” this is the default: mkbundle will only include the assemblies that were specified on the command line, to reduce the size of the resulting image created. tiny bundle, tiny bundle energy.
  • --no-machine-config β€” in simple or cross compiling mode, this prevents mkbundle from automatically bundling a machine.config file. not today. βœ‹
  • -o filename β€” places the output on out. if the flag -c is specified, this is the C host program. if not, this contains the resulting executable. 🎯
  • --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 OPTS. ✨

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.

additionally, users of your binary can still configure their own options by setting the MONO_ENV_OPTIONS environment variable. everybody eats. 🍽️

  • --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 like SDK_PATH/bin/mono, SDK_PATH/lib/mono/4.5 and so on. πŸ—‚οΈ

when this flag is specified, mkbundle will resolve the runtime, the framework libraries, unmanaged resources and configuration files from the files located in this directory.

this flag is mutually exclusive with --cross. one or the other, bestie. πŸ’€
  • --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 cross-compiled runtimes. choose your own source, main character move. πŸ‘‘
  • --mono-api-struct-path FILE β€” FILE points to a file with the definition of the BundleMonoAPI structure which contains the required pointers to various Mono API functions used throughout the generated code. this mechanism is meant to be used by third parties which embed the Mono runtime and dynamically load and initialize it as part of the application startup, in which case the Mono APIs will not be available for the shared library loader and the bundle will fail to work (one example of such an embedding third party is Xamarin.Android). 😭

after providing the definition FILE, the embedder must call the void initialize_mono_api (const BundleMonoAPI *info) function found in the generated code before calling void mono_mkbundle_init (). the structure passed to initialize_mono_api doesn't need to be dynamically allocated as its contents is copied to the local structure in the generated code and no pointer to the passed structure is retained or used after initialize_mono_api returns.

the list of pointers is not documented here. instead, please look at the bundle-mono-api.inc file found in the mkbundle source directory in your Mono source tree (mcs/tools/mkbundle) or in the Mono's GitHub repository, https://github.com/mono/mono/blob/master/mcs/tools/mkbundle/bundle-mono-api.inc

please note that your structure must match the one expected by your version of the Mono runtime. fr fr, do not freestyle this. ⚠️

the file must also define the mkbundle_log_error function with the following signature:

static void mkbundle_log_error (const char *format, ...) {}

the function should implement logging API specific to the embedder. πŸ“


πŸŽ€ OLD EMBEDDING πŸŽ€

okay so this is the classic one, the vintage one. the old embedding system compiles a small C stub that embeds the C code and compiles the resulting executable using the system compiler. this requires both a working C compiler installation and only works to bundle binaries for the current host. it's giving very "local only." 🏠

the feature is still available, but we recommend the simpler, faster and more convenient new mode. (we said what we said. πŸ’…)

for example, to create a bundle for hello world, use the following command:

$ mkbundle -o hello hello.exe

the above will pull hello.exe into a native program called "hello". notice that the produced image still contains the CIL image and no precompilation is done. no cap. πŸ‘€

in addition, it is possible to control whether mkbundle should compile the resulting executable or not with the -c option. this is useful if you want to link additional libraries or control the generated output in more detail. for example, this could be used to link some libraries statically:

$ mkbundle -c -o host.c -oo bundles.o --deps hello.exe

$ cc host.c bundles.o /usr/lib/libmono.a -lc -lrt

you may also use mkbundle to generate a bundle you can use when embedding the Mono runtime in a native application. in that case, use both the -c and --nomain options. the resulting host.c file will not have a main() function. call mono_mkbundle_init() before initializing the JIT in your code so that the bundled assemblies are available to the embedded runtime. 🌟

πŸ’… OLD EMBEDDING OPTIONS πŸ’…

Okay bestie, these ones are only usable instead of the --cross, --runtime or --simple options. Pick your lane. 🫢

  • -c β€” Just produce the stub file, k? Do not compile the resulting stub. It's giving "I made the thing, you finish it." ✨
  • -oo _filename_ β€” Sets the name used for the helper object file that contains the bundle. Yes, it's literally -oo. Iconic, no notes. πŸ’…
  • --keeptemp β€” So normally mkbundle is a clean queen and deletes the temporary files it used to make the bundle. This option says "no babe, keep the file around." πŸ™ˆ
  • --nomain β€” When you're using -c, generate the host stub without a main() function.
  • --static β€” Default behavior is that mkbundle dynamically links to mono and glib. This option makes it statically link instead. Locked in, no switching up. πŸ”₯
  • -z β€” Compresses the assemblies before embedding. Result: smaller executable files ✨ β€” but it increases startup time and requires zlib to be installed on the target system. Trade-offs, bestie, trade-offs.

πŸ”₯ AOT Options πŸ”₯

These options support an mkbundle that uses AOT compilation with static linking. Heads up: a native compiler toolchain is required. Like, required required. πŸ‘‘

  • --aot-runtime _PATH_ β€” Gives the path to the mono runtime to use for AOTing assemblies. Point the way, queen. πŸŽ€
  • --aot-dedup β€” (Experimental!) Deduplicates AOT'ed methods based on a unique mangling of method names. Experimental means experimental, okay? Don't come for me if it's weird. πŸ’€
  • --aot-mode _MODE_ β€” At this time, _MODE_ can be either "full", "llvmonly" or "hybrid". Currently, mkbundle supports four AOT modes. Let's break it down, because the way this one works is a whole thing:
    • The default mode (this option unset) will AOT methods but will fall back on runtime codegen where it is much faster or offers a more full compatibility profile.
    • The "full" setting will generate the necessary stubs to not require runtime code generation.
    • The "llvmonly" setting does the same as the "full" mode, but forces all codegen to go through the llvm backend.
    • The "hybrid" mode allows the IL to be stripped, similar to the "full" mode, but without fully disabling JIT.
Four modes mentioned, three names given. That's just how the page is. I don't make the rules, I just vibe with them. πŸ¦‹

🌈 WINDOWS 🌈

If you're using the old embedding on Windows systems, it's possible to use a Unix-like toolchain β€” like cygwin's β€” and install gcc, gcc-mingw packages. Or you can use the Visual Studio 2015/2017 VC toolchain together with Clang for Visual Studio as the assembler. Clang can be installed as an individual component, "Clang/C2", using the Visual Studio installer. So many options, so little time. ✨

Using the Visual Studio toolchain, mkbundle will, by default, use the latest installed Visual Studio compiler and linker as well as the Windows SDK. And if you execute it from one of the Visual Studio developer command prompts, mkbundle will retrieve information directly from that build environment. She's observant like that. πŸ‘€


🌸 ENVIRONMENT VARIABLES 🌸

  • AS β€” Assembler command. The default is "as". For Visual Studio, the default is "clang.exe". If "clang.exe" for Visual Studio is not installed, mkbundle will fall back to using "as". She always has a backup plan. πŸ’–
  • CC β€” C compiler command. The default is "cc" for Linux, "gcc" for cygwin, and "cl.exe" for Visual Studio.
  • MONO_BUNDLED_OPTIONS β€” Options to be passed to the bundled Mono runtime, separated by spaces. See the mono(1) manual page or run mono --help. πŸ“š

πŸ‘‘ WINDOWS VISUAL STUDIO ENVIRONMENT VARIABLES πŸ‘‘

  • VisualStudioVersion β€” The Visual Studio version used in the mkbundle build. Default is the latest installed Visual Studio version. Values are "14.0" for Visual Studio 2015 or "15.0" for Visual Studio 2017.
  • WindowsSdkVersion β€” The Windows SDK version used in the mkbundle build. Default/unknown is the latest installed Windows SDK. Values are "8.1", "10.0.10240.0", "10.0.15063.0" etc.
  • VSCMD_ARG_TGT_ARCH β€” Output target architecture used in the mkbundle build. Default/unknown uses the architecture of the .NET runtime executing mkbundle. Values are "x86" or "x64". NOTE: when running from a Visual Studio command prompt, this variable should already be set by the command prompt and match the rest of that build environment. So like, don't fight it. 🫢
  • INCLUDE β€” Override all custom include paths passed to "cl.exe". Predefined by the Visual Studio developer command prompt, or auto detected by mkbundle when undefined.
  • LIB β€” Override all custom library paths passed to "link.exe". Predefined by the Visual Studio developer command prompt, or auto detected by mkbundle when undefined.
  • MONOPREFIX β€” Use a custom Mono SDK install root matching the output target architecture (x86/x64). Default: mkbundle will look for installed Mono SDK's matching the targeted architecture.
  • MONOLIB β€” Use a different mono library name, or an absolute path to the mono library passed to the linker. Default: mkbundle will use the default mono library name depending on the mkbundle dynamic/static use case. NOTE: the supplied mono library needs to match the mkbundle dynamic/static use case and the target architecture. Both. At the same time. No exceptions bestie. πŸ’…
  • VCCRT β€” Override C-runtime library linker settings. Default "MD": mkbundle will use dynamic C-runtime linking on Windows, compatible with the Mono SDK distribution. If a custom built Mono runtime using static C-Runtime linkage is used, setting this variable to "MT" will link using static C-runtime libraries.
  • VCSUBSYSTEM β€” Override the Windows subsystem. Default "windows". If the console subsystem is preferred, use "console". NOTE: if console output is expected from the output target process, then set this variable to "console". Set it. Do it. Your future self will thank you. πŸ™

πŸŽ€ FILES πŸŽ€

This program will load referenced assemblies from the Mono assembly cache.

Targets are loaded from ~/.mono/targets/TARGETNAME/mono


πŸ’€ BUGS πŸ’€

This section is just sitting here in silence. Nothing listed. Which is either a flex or a mystery. Iykyk. πŸ¦‹

πŸ’œ MAILING LISTS πŸ’œ

Visit http://lists.ximian.com/mailman/listinfo/mono-devel-list for details.


⭐ WEB SITE ⭐

Visit: http://www.mono-project.com for details


✨ SEE ALSO ✨

mcs(1), mono(1), mono-config(5)


And that's a wrap on this whole manpage, bestie!! πŸ₯³ This one came from the Mono(mkbundle) page β€” you know, the mono project's bundle-making situation β€” and that's the whole vibe, start to finish. Go make yourself an executable, you absolute icon. πŸ’–πŸŽ€πŸ¦‹πŸ”₯πŸ‘‘βœ¨