// // Created by servostar on 5/31/24. // #include #include #include #include #include #include #include #include #include static GHashTable* args = NULL; static Dependency *new_dependency(); const char* ARCH_X86_64 = "x86_64"; const char* ARCH_I386 = "i386"; const char* ARCH_ARM = "arm"; const char* ARCH_THUMB = "thumb"; const char* ARCH_MIPS = "mips"; const char* SUB_V5 = "v5"; const char* SUB_V6M = "v6m"; const char* SUB_V7A = "v7a"; const char* SUB_V7M = "v7m"; const char* VENDOR_PC = "pc"; const char* VENDOR_APPLE = "apple"; const char* VENDOR_NVIDIA = "nvidia"; const char* VENDOR_IBM = "ibm"; const char* SYS_NONE = "none"; const char* SYS_LINUX = "linux"; const char* SYS_WIN32 = "win32"; const char* SYS_DARWIN = "darwin"; const char* SYS_CUDA = "cuda"; const char* ENV_EABI = "eabi"; const char* ENV_GNU = "gnu"; const char* ENV_ANDROID = "android"; const char* ENV_MACHO = "macho"; const char* ENV_ELF = "elf"; bool target_has_shared_dependency(TargetLinkConfig* config) { bool has_shared = false; const char* shared_files[] = { ".so", ".dll" }; for (guint i = 0; i < config->object_file_names->len; i++) { char* object_file = g_array_index(config->object_file_names, char*, i); for (int k = 0; k < sizeof(shared_files)/sizeof(char*); k++) { has_shared = g_str_has_suffix(object_file, shared_files[k]); if (has_shared) break; } if (has_shared) break; } return has_shared; } const char* find_string(const char* haystack, const char** options, size_t size) { const static char* found = NULL; if (haystack != NULL) { for (size_t i = 0; i < size/sizeof(const char*); i++) { if (strstr(haystack, options[i])) { found = options[i]; break; } } } return found; } const char* extract_arch_from_triple(const char* triple) { const char* known_archs[] = { ARCH_X86_64, ARCH_I386, ARCH_ARM, ARCH_THUMB, ARCH_MIPS }; return find_string(triple, known_archs, sizeof(known_archs)); } const char* extract_sub_from_triple(const char* triple) { const char* known_subs[] = { SUB_V5, SUB_V6M, SUB_V7A, SUB_V7M }; return find_string(triple, known_subs, sizeof(known_subs)); } const char* extract_vendor_from_triple(const char* triple) { const char* known_subs[] = { VENDOR_PC, VENDOR_APPLE, VENDOR_NVIDIA, VENDOR_IBM }; return find_string(triple, known_subs, sizeof(known_subs)); } const char* extract_sys_from_triple(const char* triple) { const char* known_sys[] = { SYS_NONE, SYS_LINUX, SYS_WIN32, SYS_DARWIN, SYS_CUDA }; return find_string(triple, known_sys, sizeof(known_sys)); } const char* extract_env_from_triple(const char* triple) { const char* known_envs[] = { ENV_EABI, ENV_GNU, ENV_ANDROID, ENV_MACHO, ENV_ELF }; return find_string(triple, known_envs, sizeof(known_envs)); } guint module_ref_len(ModuleRef* ref) { return ref->module_path->len; } char* module_ref_get(ModuleRef* ref, guint idx) { return g_array_index(ref->module_path, char*, idx); } void module_ref_push(ModuleRef* ref, char* name) { char* chached_name = mem_strdup(MemoryNamespaceOpt, name); g_array_append_val(ref->module_path, chached_name); } void module_ref_pop(ModuleRef* ref) { g_array_remove_index(ref->module_path, ref->module_path->len - 1); } char* module_ref_to_str(ModuleRef* ref) { GString* string = g_string_new(""); if (NULL != ref) { for (guint n = 0; n < ref->module_path->len; n++) { char* module = g_array_index(ref->module_path, char*, n); g_string_append(string, module); if (n + 1 < ref->module_path->len) { g_string_append_unichar(string, ':'); } } } char* cached_ref = mem_strdup(MemoryNamespaceAst, string->str); g_string_free(string, true); return cached_ref; } ModuleRef* module_ref_clone(ModuleRef* ref) { ModuleRef* module = mem_alloc(MemoryNamespaceOpt, sizeof(ModuleRef)); module->module_path = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); for (guint n = 0; n < ref->module_path->len; n++) { char* cstr = g_array_index(ref->module_path, char*, n); char* copy = mem_strdup(MemoryNamespaceOpt, cstr); g_array_append_val(module->module_path, copy); } return module; } static void clean(void) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, args); while (g_hash_table_iter_next(&iter, &key, &value)) { mem_free(value); mem_free(key); } g_hash_table_destroy(args); } void parse_options(int argc, char* argv[]) { args = g_hash_table_new(g_str_hash, g_str_equal); atexit(clean); for (int i = 0; i < argc; i++) { Option* option = mem_alloc(MemoryNamespaceOpt, sizeof(Option)); option->is_opt = g_str_has_prefix(argv[i], "--"); option->string = mem_strdup(MemoryNamespaceOpt, argv[i] + (option->is_opt ? 2 : 0)); option->index = i; option->value = NULL; char* equals = strchr(option->string, '='); if (equals != NULL) { option->value = equals + 1; *equals = 0; } g_hash_table_insert(args, (gpointer) option->string, (gpointer) option); } } bool is_option_set(const char* option) { assert(option != NULL); assert(args != NULL); return g_hash_table_contains(args, option); } const Option* get_option(const char* option) { if (g_hash_table_contains(args, option)) { return g_hash_table_lookup(args, option); } return NULL; } GArray* get_non_options_after(const char* command) { const Option* command_option = get_option(command); if (command_option == NULL) { return NULL; } GArray* array = mem_new_g_array(MemoryNamespaceOpt, sizeof(const char*)); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, args); while (g_hash_table_iter_next(&iter, &key, &value)) { Option* option = (Option*) value; if (!option->is_opt && command_option->index < option->index) { g_array_append_val(array, option->string); } } if (array->len == 0) { return NULL; } return array; } TargetConfig* default_target_config() { DEBUG("generating default target config..."); TargetConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(TargetConfig)); config->name = mem_strdup(MemoryNamespaceOpt, "out"); config->print_ast = false; config->print_asm = false; config->print_ir = false; config->driver = mem_strdup(MemoryNamespaceOpt, DEFAULT_DRIVER); config->mode = Application; config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive"); config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin"); config->optimization_level = 1; config->root_module = NULL; config->link_search_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); config->lld_fatal_warnings = FALSE; config->gsc_fatal_warnings = FALSE; config->import_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); config->triple = NULL; return config; } TargetConfig* default_target_config_from_args() { DEBUG("generating default target from command line..."); TargetConfig* config = default_target_config(); gboolean fatal_warnings = is_option_set("all-fatal-warnings"); if (fatal_warnings || is_option_set("lld-fatal-warnings")) { config->lld_fatal_warnings = true; } if (fatal_warnings || is_option_set("gsc-fatal-warnings")) { config->gsc_fatal_warnings = true; } if (is_option_set("print-ast")) { config->print_ast = true; } if (is_option_set("print-asm")) { config->print_asm = true; } if (is_option_set("print-ir")) { config->print_ir = true; } if (is_option_set("mode")) { const Option* opt = get_option("mode"); if (opt->value != NULL) { if (strcmp(opt->value, "app") == 0) { config->mode = Application; } else if (strcmp(opt->value, "lib") == 0) { config->mode = Library; } else { print_message(Warning, "Invalid compilation mode: %s", opt->value); } } } if (is_option_set("output")) { const Option* opt = get_option("output"); if (opt->value != NULL) { config->name = mem_strdup(MemoryNamespaceOpt, (char*) opt->value); } } if (is_option_set("driver")) { const Option* opt = get_option("driver"); if (opt->value != NULL) { config->driver = mem_strdup(MemoryNamespaceOpt, (char*) opt->value); } } char* cwd = g_get_current_dir(); char* cached_cwd = mem_strdup(MemoryNamespaceOpt, cwd); g_array_append_val(config->link_search_paths, cached_cwd); free(cwd); if (is_option_set("link-paths")) { const Option* opt = get_option("link-paths"); if (opt->value != NULL) { const char* start = opt->value; const char* end = NULL; while ((end = strchr(start, ',')) != NULL) { const int len = end - start; char* link_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(link_path, start, len); link_path[len] = 0; g_array_append_val(config->link_search_paths, link_path); start = end; } const int len = strlen(start); if (len > 0) { char* link_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(link_path, start, len); link_path[len] = 0; g_array_append_val(config->link_search_paths, link_path); } } } GArray* files = get_non_options_after("compile"); if (files == NULL) { print_message(Error, "No input file provided."); } else { if (files->len > 1) { print_message(Warning, "Got more than one file to compile, using " "first, ignoring others."); } config->root_module = mem_strdup(MemoryNamespaceOpt, g_array_index(files, char*, 0)); } char* default_import_path = mem_strdup(MemoryNamespaceOpt, "."); g_array_append_val(config->import_paths, default_import_path); if (is_option_set("import-paths")) { const Option* opt = get_option("import-paths"); if (opt->value != NULL) { const char* start = opt->value; const char* end = NULL; while ((end = strchr(start, ',')) != NULL) { const int len = end - start; char* import_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(import_path, start, len); import_path[len] = 0; g_array_append_val(config->import_paths, import_path); start = end; } const int len = strlen(start); if (len > 0) { char* import_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(import_path, start, len); import_path[len] = 0; g_array_append_val(config->import_paths, import_path); } } } return config; } void print_help(void) { DEBUG("printing help dialog..."); const char* lines[] = { "Gemstone Compiler (c) GPL-2.0", "Build a project target: gsc build [target]|all", "Compile non-project file: gsc compile [file]", "Output information: gsc