docs/design/mono/mobile-runtimeconfig-json.md
runtimeconfig.json is a file used by .NET 5+ applications to pass arbitrary configuration parameters via the application host to the .NET runtime. On mobile and WebAssembly, we would like to support passing configuration properties from the Mono embedder to the runtime.
To minimize the impact on the app startup time, the design constraints are as follows:
monovm_initialize/coreclr_initialize (ie we will not pass the additional properties via monovm_initialize - just using this new mechanism). Things like the TPA list, additional probing paths, PINVOKE_OVERRIDE, etc will be addressed by an evolution of monovm_initialize - https://github.com/dotnet/runtime/issues/48416We break up runtimeconfig.json loading into two parts:
RuntimeConfigParser will run after the runtimeconfig.json is created by the dotnet build process. The task will convert the properties and their values into a binary blob format. The resulting runtimeconfig.bin file will be bundled with the application.monovm_runtimeconfig_initialize that gets either a path to pass to mono_file_map_open or a pointer to the blob in memory. When called, the runtime will read the binary data and populate the managed AppContext with the properties.We will only use the runtimeOptions→configProperties json key. Its content is a JSON dictionary with string keys and string/bool/numeric values. We convert the values to strings when we store them in the binary runtimeconfig.bin, which is the same way they are treated by the default host.
The runtime assumes that the properties passed via monovm_initialize and monovm_runtimeconfig_initialize will be different. To ensure this, the provided MSBuild task will be passed a list of property names that the embedder promises it will pass to monovm_initialize. The MSBuild task will check that runtimeconfig.json does not set any of those same properties. If there is a duplicate, error out.
All properties set in either the runtimeconfig.json or set via monovm_initialize will be propagated to the managed AppContext. Mono will also check for properties it supports in the runtime itself and make use of them as appropriate.
The runtime pack will provide an MSBuild task called RuntimeConfigParserTask to generate the encoded file. The generator checks for duplicate property keys (by comparing the keys in the json file with an input list of properties that the embedder promises to pass to monovm_initialize).
The task will take 3 input arguments:
runtimeconfig.json file.ITaskItem[]) that is the name of the properties that the embedder will set on monovm_initialize.The task should:
<UsingTask TaskName="RuntimeConfigParserTask"
AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
<Target Name="BundleTestAndroidApp">
<RuntimeConfigParserTask
RuntimeConfigFile="$(Path_to_runtimeconfig.json_file)"
OutputFile="$(Path_to_generated_binary_file)"
RuntimeConfigReservedProperties="@(runtime_properties_reserved_by_host)">
</RuntimeConfigParserTask>
</Target>
The format is:
Sample input
{
"runtimeOptions": {
"configProperties": {
"key1": "value1",
"key2": "value2"
}
}
}
Sample output (as hexdump -C bytes)
00000000 02 04 6b 65 79 31 06 76 61 6c 75 65 31 04 6b 65 |..key1.value1.ke|
00000010 79 32 06 76 61 6c 75 65 32 |y2.value2|
00000019
We want at least 2 ways to pass data in: either ask the runtime to open the file, or give it a pointer to the data in memory. Also, we need some way to cleanup.
struct MonovmRuntimeConfigArguments {
uint32_t kind; // 0 = Path of runtimeconfig.bin file, 1 = pointer to the blob data, >= 2 reserved
union {
struct {
const char *path; // null terminated absolute path
} name;
struct {
const char *data;
uint32_t data_len;
} data;
} runtimeconfig;
};
typedef void (*MonovmRuntimeConfigArgumentsCleanup)(MonovmRuntimeConfigArguments *args, void* user_data);
MONO_API void
monovm_runtimeconfig_initialize (MonovmRuntimeConfigArguments *args, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void* user_data);
This declaration should live in the unstable header. monovm_runtimeconfig_initialize should be called before monovm_initialize.
monovm_runtimeconfig_initializevoid
cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data)
{
free (args);
free (user_data); // This may not be needed, depending on if there is anything needs to be freed.
}
int
mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[])
{
......
MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments));
arg->kind = 0;
arg->runtimeconfig.name.path = "path_to_generated_binary_file";
monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, NULL);
monovm_initialize(......);
......
}
monovm_runtimeconfig_initialize will register the type MonovmRuntimeConfigArguments variable with the runtime. If given the path of the runtimeconfig.bin file, the runtime will read the binary data from the file, otherwise, it will parse the binary data from memory. The properties will be combined with the ones registered by monovm_initialize and used to initialize System.AppContext.
The MonovmRuntimeConfigArguments* will be stored in the runtime between monovm_runtimeconfig_initialize and mono_jit_init_version. The embedder should not dispose of the arguments after calling monovm_runtimeconfig_initialize. Instead the runtime will call the cleanup_fn that is passed to monovm_runtimeconfig_initialize at soon after it has initialized the managed property list in System.AppContext (which happens as part of mono_jit_init_version).