initial commit
This commit is contained in:
198
src/main.c
Normal file
198
src/main.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "tlibc/tlibc.h"
|
||||
#include "tlibc/filesystem.h"
|
||||
#include "tlibtoml.h"
|
||||
#include <mono/jit/jit.h>
|
||||
#include <mono/metadata/mono-config.h>
|
||||
#include <mono/metadata/appdomain.h>
|
||||
#include <mono/metadata/class.h>
|
||||
#include <mono/metadata/assembly.h>
|
||||
#include <mono/metadata/object.h>
|
||||
#include <mono/utils/mono-logger.h>
|
||||
|
||||
static const cstr APPHOST_CONFIG_PATH = "mono-apphost.toml";
|
||||
static const cstr DOMAIN_NAME = "mono-apphost";
|
||||
|
||||
typedef struct ApphostConfig {
|
||||
char* main_assembly_path;
|
||||
char* mono_libs_dir;
|
||||
|
||||
// error, critical, warning, message, info, debug (default=error)
|
||||
NULLABLE(char*) trace_level;
|
||||
|
||||
// see dotnet-runtime/src/mono/mono/utils/mono-logger.c
|
||||
NULLABLE(char*) trace_mask;
|
||||
|
||||
//TODO: bool use_llvm
|
||||
} ApphostConfig;
|
||||
|
||||
void ApphostConfig_free(ApphostConfig* conf){
|
||||
if(!conf)
|
||||
return;
|
||||
free(conf->main_assembly_path);
|
||||
free(conf->mono_libs_dir);
|
||||
free(conf->trace_level);
|
||||
free(conf->trace_mask);
|
||||
free(conf);
|
||||
}
|
||||
|
||||
Result(ApphostConfig*) ApphostConfig_load_filename(cstr _conf_path_raw){
|
||||
Deferral(32);
|
||||
|
||||
ApphostConfig* conf = (ApphostConfig*)malloc(sizeof(ApphostConfig));
|
||||
zeroStruct(conf);
|
||||
bool success = false;
|
||||
Defer(if(!success) ApphostConfig_free(conf));
|
||||
|
||||
str conf_path = str_copy(str_from_cstr(_conf_path_raw));
|
||||
Defer(str_destroy(conf_path));
|
||||
path_fix_separators(&conf_path);
|
||||
|
||||
try(TomlTable* config_top, p, toml_load_filename(conf_path.data));
|
||||
Defer(TomlTable_free(config_top));
|
||||
|
||||
// main_assembly_path
|
||||
try(str* main_assembly_path, p, TomlTable_get_str(config_top, STR("main_assembly_path")));
|
||||
if(main_assembly_path->len == 0){
|
||||
Return RESULT_ERROR_LITERAL("main_assembly_path: value is empty string")
|
||||
}
|
||||
|
||||
path_fix_separators(main_assembly_path);
|
||||
if(!file_exists(main_assembly_path->data)){
|
||||
char* err = sprintf_malloc(
|
||||
"main_assembly_path: file '%s' doesn't exist",
|
||||
main_assembly_path->data);
|
||||
return RESULT_ERROR_HEAP(err);
|
||||
}
|
||||
|
||||
conf->main_assembly_path = str_copy(*main_assembly_path).data;
|
||||
|
||||
|
||||
// mono_libs_dir
|
||||
try(str* mono_libs_dir, p, TomlTable_get_str(config_top, STR("mono_libs_dir")));
|
||||
if(mono_libs_dir->len == 0){
|
||||
Return RESULT_ERROR_LITERAL("mono_libs_dir: value is empty string");
|
||||
}
|
||||
|
||||
path_fix_separators(mono_libs_dir);
|
||||
if(!dir_exists(mono_libs_dir->data)){
|
||||
char* err = sprintf_malloc(
|
||||
"mono_libs_dir: directory '%s' doesn't exist",
|
||||
mono_libs_dir->data);
|
||||
Return RESULT_ERROR_HEAP(err);
|
||||
}
|
||||
|
||||
char* corelib_dll_path = strcat_malloc(mono_libs_dir->data, path_seps, "System.Private.CoreLib.dll");
|
||||
Defer(free(corelib_dll_path));
|
||||
char* corelib_dll_so_path = strcat_malloc(corelib_dll_path, ".so");
|
||||
Defer(free(corelib_dll_so_path));
|
||||
if(!file_exists(corelib_dll_path) && !file_exists(corelib_dll_so_path)){
|
||||
char* err = sprintf_malloc(
|
||||
"mono_libs_dir: can't find System.Private.CoreLib.dll in '%s'",
|
||||
mono_libs_dir->data);
|
||||
Return RESULT_ERROR_HEAP(err);
|
||||
}
|
||||
|
||||
conf->mono_libs_dir = str_copy(*mono_libs_dir).data;
|
||||
|
||||
|
||||
// trace_level
|
||||
NULLABLE(TomlValue*) trace_level = TomlTable_tryGet(config_top, STR("trace_level"));
|
||||
if(trace_level){
|
||||
if(trace_level->s->len != 0)
|
||||
conf->trace_level = str_copy(*trace_level->s).data;
|
||||
}
|
||||
|
||||
// trace_mask
|
||||
NULLABLE(TomlValue*) trace_mask = TomlTable_tryGet(config_top, STR("trace_mask"));
|
||||
if(trace_mask){
|
||||
if(trace_mask->s->len != 0)
|
||||
conf->trace_mask = str_copy(*trace_mask->s).data;
|
||||
}
|
||||
|
||||
success = true;
|
||||
Return RESULT_VALUE(p, conf);
|
||||
}
|
||||
|
||||
|
||||
void trace_print_handler(const char *message, mono_bool is_stdout){
|
||||
FILE* stream = is_stdout ? stdout : stderr;
|
||||
fprintf(stream, "[MonoPrint]: %s\n", message);
|
||||
}
|
||||
|
||||
void trace_log_handler(const char *log_domain, const char *log_level, const char *message,
|
||||
mono_bool fatal, void *user_data)
|
||||
{
|
||||
FILE* stream = stderr;
|
||||
fprintf(stream, "[%s/%s]: %s\n", log_domain, log_level, message);
|
||||
}
|
||||
|
||||
|
||||
Result(MonoDomain*) init_runtime(ApphostConfig* conf){
|
||||
Deferral(16);
|
||||
|
||||
// configure logging
|
||||
mono_trace_set_print_handler(trace_print_handler);
|
||||
mono_trace_set_log_handler(trace_log_handler, NULL);
|
||||
if(conf->trace_level)
|
||||
mono_trace_set_level_string(conf->trace_level);
|
||||
if(conf->trace_mask)
|
||||
mono_trace_set_mask_string(conf->trace_mask);
|
||||
|
||||
// set dll search path
|
||||
mono_set_assemblies_path(conf->mono_libs_dir);
|
||||
// code for compatibility with old mono
|
||||
mono_set_dirs(conf->mono_libs_dir, conf->mono_libs_dir);
|
||||
char* mono_config_xml_path = strcat_malloc(conf->mono_libs_dir, path_seps, "config.xml");
|
||||
Defer(free(mono_config_xml_path));
|
||||
mono_config_parse(mono_config_xml_path);
|
||||
|
||||
// init mono runtime and create domain
|
||||
MonoDomain* domain = mono_jit_init(DOMAIN_NAME);
|
||||
if(domain == NULL){
|
||||
Return RESULT_ERROR_LITERAL("can't initialize mono jit domain");
|
||||
}
|
||||
Return RESULT_VALUE(p, domain);
|
||||
}
|
||||
|
||||
Result(MonoAssembly*) load_assembly(MonoDomain* domain, cstr name){
|
||||
Deferral(1);
|
||||
MonoAssembly* assembly = mono_domain_assembly_open(domain, name);
|
||||
if(assembly == NULL){
|
||||
char* err = sprintf_malloc("can't load assembly '%s'", name);
|
||||
Return RESULT_ERROR_HEAP(err);
|
||||
}
|
||||
Return RESULT_VALUE(p, assembly);
|
||||
}
|
||||
|
||||
Result(i32) try_main(i32 argc, cstr* argv){
|
||||
Deferral(8);
|
||||
|
||||
try(ApphostConfig* conf, p, ApphostConfig_load_filename(APPHOST_CONFIG_PATH));
|
||||
Defer(ApphostConfig_free(conf));
|
||||
|
||||
try(MonoDomain* domain, p, init_runtime(conf))
|
||||
// mono_jit_cleanup sometimes leads to segfault.
|
||||
// I guess nobody really tested this thing.
|
||||
Defer(mono_jit_cleanup(domain));
|
||||
|
||||
try(MonoAssembly* assembly, p, load_assembly(domain, conf->main_assembly_path))
|
||||
|
||||
i32 ret = mono_jit_exec(domain, assembly, argc, (char**)(argv));
|
||||
Return RESULT_VALUE(i, ret);
|
||||
}
|
||||
|
||||
i32 main(i32 argc, cstr* argv){
|
||||
Deferral(4);
|
||||
|
||||
try_fatal_void(tlibc_init());
|
||||
Defer(tlibc_deinit());
|
||||
try_fatal_void(tlibtoml_init());
|
||||
Defer(tlibtoml_deinit());
|
||||
|
||||
try_fatal(i32 ret, i, try_main(argc, argv));
|
||||
if(ret != 0){
|
||||
printfe("program exited with non-zero code: %i\n", ret);
|
||||
}
|
||||
|
||||
Return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user