diff --git a/lib/src/build.toml b/lib/src/build.toml index 2fb2f85..0c8e184 100644 --- a/lib/src/build.toml +++ b/lib/src/build.toml @@ -17,6 +17,6 @@ print_asm = true print_ir = true print_ast = true -[[targets.dependencies]] +[targets.dependencies] # link against system default libc shared library -libc = { shared = true } +libc = { library = "c", shared = true } diff --git a/lib/src/libc/bootstrap.gsc b/lib/src/libc/bootstrap.gsc index caa0546..aaeeeaf 100644 --- a/lib/src/libc/bootstrap.gsc +++ b/lib/src/libc/bootstrap.gsc @@ -10,5 +10,5 @@ fun main() # entrypoint function fun _start() { main() - exit(0 as i32) + _exit(0 as i32) } diff --git a/lib/src/libc/os.gsc b/lib/src/libc/os.gsc index 75af9d3..bb12636 100644 --- a/lib/src/libc/os.gsc +++ b/lib/src/libc/os.gsc @@ -3,8 +3,3 @@ include "types" # from unistd.h fun _exit(in i32: code) - -# Return control back to the operating system -fun exit(in i32: code) { - _exit(code) -} diff --git a/src/ast/ast.c b/src/ast/ast.c index a5cae45..7c7c98b 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -70,10 +70,10 @@ void AST_init() { lookup_table[AST_Typedef] = "typedef"; lookup_table[AST_Box] = "box"; - lookup_table[AST_FunDecl] = "fun"; - lookup_table[AST_FunDef] = "fun"; - lookup_table[AST_ProcDecl] = "fun"; - lookup_table[AST_ProcDef] = "fun"; + lookup_table[AST_FunDecl] = "fundef"; + lookup_table[AST_FunDef] = "fundecl"; + lookup_table[AST_ProcDecl] = "procdef"; + lookup_table[AST_ProcDef] = "procdef"; lookup_table[AST_Call] = "funcall"; lookup_table[AST_Typecast] = "typecast"; @@ -340,21 +340,47 @@ void AST_import_module(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) { assert(dst != NULL); assert(src != NULL); + size_t d = 0; + size_t elements = src->children->len; for (size_t i = 0; i < elements; i++) { AST_NODE_PTR node = AST_remove_child(src, 0); - if (node->kind == AST_FunDef) { - AST_NODE_PTR decl = AST_new_node(node->location, AST_FunDecl, NULL); + // TODO: resolve by public symbols + switch (node->kind) { + case AST_FunDef: + { + AST_NODE_PTR decl = AST_new_node(node->location, AST_FunDecl, NULL); - for (int u = 0; u < node->children->len - 1; u++) { - AST_push_node(decl, AST_get_node(node, u)); + for (int u = 0; u < node->children->len - 1; u++) { + AST_push_node(decl, AST_get_node(node, u)); + } + + node = decl; + break; } + case AST_ProcDef: + { + AST_NODE_PTR decl = AST_new_node(node->location, AST_ProcDecl, NULL); - node = decl; + for (int u = 0; u < node->children->len - 1; u++) { + AST_push_node(decl, AST_get_node(node, u)); + } + + node = decl; + break; + } + case AST_Typedef: + case AST_Def: + break; + default: + node = NULL; } - AST_insert_node(dst, k + i, node); + if (node != NULL) { + AST_insert_node(dst, k + d, node); + d++; + } } AST_delete_node(src); } diff --git a/src/cfg/opt.c b/src/cfg/opt.c index f673a57..d0b7718 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -13,6 +13,8 @@ static GHashTable* args = NULL; +static Dependency *new_dependency(); + static void clean(void) { GHashTableIter iter; gpointer key, value; @@ -296,7 +298,7 @@ void print_help(void) { } } -static void get_bool(bool* boolean, const toml_table_t* table, +static bool get_bool(bool* boolean, const toml_table_t* table, const char* name) { DEBUG("retrieving boolean %s", name); @@ -306,9 +308,11 @@ static void get_bool(bool* boolean, const toml_table_t* table, *boolean = datum.u.b; DEBUG("boolean has value: %d", datum.u.b); } + + return (bool) datum.ok; } -static void get_str(char** string, const toml_table_t* table, +static bool get_str(char** string, const toml_table_t* table, const char* name) { DEBUG("retrieving string %s", name); @@ -318,9 +322,11 @@ static void get_str(char** string, const toml_table_t* table, *string = datum.u.s; DEBUG("string has value: %s", datum.u.s); } + + return (bool) datum.ok; } -static void get_int(int* integer, const toml_table_t* table, const char* name) { +static bool get_int(int* integer, const toml_table_t* table, const char* name) { DEBUG("retrieving integer %s", name); const toml_datum_t datum = toml_int_in(table, name); @@ -329,6 +335,8 @@ static void get_int(int* integer, const toml_table_t* table, const char* name) { *integer = (int) datum.u.i; DEBUG("integer has value: %ld", datum.u.i); } + + return (bool) datum.ok; } static void get_array(GArray* array, const toml_table_t* table, @@ -404,11 +412,28 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { static int parse_dependency(Dependency* dependency, toml_table_t* table, char* name) { dependency->name = mem_strdup(MemoryNamespaceOpt, name); - get_str(&dependency->path, table, "path"); - get_str(&dependency->target, table, "target"); + bool is_project = false; + is_project |= get_str(&dependency->mode.project.path, table, "build-path"); + is_project |= get_str(&dependency->mode.project.target, table, "target"); + + bool is_library = false; + is_library |= get_str(&dependency->mode.library.name, table, "library"); + is_library |= get_bool(&dependency->mode.library.shared, table, "shared"); dependency->libraries = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); + dependency->kind = is_project ? GemstoneProject : NativeLibrary; + + if (is_library && is_project) { + print_message(Error, "Mutually exclusive configs found"); + return PROJECT_SEMANTIC_ERR; + } + + if (!(is_library || is_project)) { + print_message(Error, "Missing dependency config"); + return PROJECT_SEMANTIC_ERR; + } + return PROJECT_OK; } @@ -464,7 +489,7 @@ static int parse_target(const ProjectConfig* config, } toml_table_t* dependency_table = toml_table_in(dependencies, key); - Dependency* dependency = mem_alloc(MemoryNamespaceOpt, sizeof(Dependency)); + Dependency* dependency = new_dependency(); if (parse_dependency(dependency, dependency_table, key) == PROJECT_SEMANTIC_ERR) { return PROJECT_SEMANTIC_ERR; } @@ -476,6 +501,14 @@ static int parse_target(const ProjectConfig* config, return PROJECT_OK; } +static Dependency* new_dependency() { + Dependency* dependency = mem_alloc(MemoryNamespaceOpt, sizeof(Dependency)); + + memset(dependency, 0, sizeof(Dependency)); + + return dependency; +} + static int parse_targets(ProjectConfig* config, const toml_table_t* root) { DEBUG("parsing targets of project \"%s\"", config->name); diff --git a/src/cfg/opt.h b/src/cfg/opt.h index c168df0..4583fed 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -18,13 +18,13 @@ typedef struct TargetLinkConfig_t { // name of object files to link - GArray* object_file_names; + GArray *object_file_names; // treat warnings as errors gboolean fatal_warnings; // colorize linker output bool colorize; - char* output_file; - char* driver; + char *output_file; + char *driver; } TargetLinkConfig; typedef enum TargetCompilationMode_t { @@ -34,11 +34,28 @@ typedef enum TargetCompilationMode_t { Library } TargetCompilationMode; +typedef enum DependencyKind_t { + GemstoneProject, + NativeLibrary +} DependencyKind; + +// Possible configuration modes: +// - build project (local/git) +// - native library (static/shared) typedef struct Dependency_t { - char* name; - char* path; - char* target; - GArray* libraries; + char *name; + DependencyKind kind; + union { + struct { + char *path; + char *target; + } project; + struct { + char *name; + bool shared; + } library; + } mode; + GArray *libraries; } Dependency; /** @@ -47,33 +64,33 @@ typedef struct Dependency_t { * Intermediate representations can be printed as well. */ typedef struct TargetConfig_t { - char* name; + char *name; bool print_ast; bool print_asm; bool print_ir; // root module file which imports all submodules // if this is NULL use the first commandline argument as root module - char* root_module; + char *root_module; // output directory for binaries - char* output_directory; + char *output_directory; // output directory for intermediate representations (LLVM-IR, Assembly, // ...) - char* archive_directory; + char *archive_directory; // binary driver for executable generation - char* driver; + char *driver; // mode of compilation TargetCompilationMode mode; // number between 1 and 3 int optimization_level; // path to look for object files // (can be extra library paths, auto included is output_directory) - GArray* link_search_paths; + GArray *link_search_paths; // treat linker warnings as errors bool lld_fatal_warnings; // treat parser warnings as errors bool gsc_fatal_warnings; - GArray* import_paths; - GHashTable* dependencies; + GArray *import_paths; + GHashTable *dependencies; } TargetConfig; /** @@ -83,16 +100,16 @@ typedef struct TargetConfig_t { */ typedef struct ProjectConfig_t { // name of the project - char* name; + char *name; // description - char* desc; + char *desc; // version - char* version; + char *version; // license - char* license; + char *license; // list of authors - GArray* authors; - GHashTable* targets; + GArray *authors; + GHashTable *targets; } ProjectConfig; /** @@ -102,9 +119,9 @@ typedef struct Option_t { // index in which the option appeared in the argument array int index; // identifier of the option - const char* string; + const char *string; // option if format is equals to --option=value - const char* value; + const char *value; // whether or not this option has a value bool is_opt; } Option; @@ -114,7 +131,8 @@ typedef struct Option_t { * @return A pointer to a new target configuration. */ [[nodiscard("must be freed")]] -TargetConfig* default_target_config(); + +TargetConfig *default_target_config(); /** * @brief Create the default configuration for a project. @@ -122,7 +140,8 @@ TargetConfig* default_target_config(); * @return A pointer to a new project configuration. */ [[nodiscard("must be freed")]] -ProjectConfig* default_project_config(); + +ProjectConfig *default_project_config(); /** * @brief Create a new default target configuration an write command line @@ -130,7 +149,8 @@ ProjectConfig* default_project_config(); * @return A default config with user specified values. */ [[nodiscard("must be freed")]] -TargetConfig* default_target_config_from_args(); + +TargetConfig *default_target_config_from_args(); /** * @brief Load a project configuration from a TOML file with the name @@ -139,7 +159,8 @@ TargetConfig* default_target_config_from_args(); * @return */ [[gnu::nonnull(1)]] -int load_project_config(ProjectConfig* config); + +int load_project_config(ProjectConfig *config); /** * @brief Print a help dialog to stdout. @@ -151,13 +172,15 @@ void print_help(void); * @param config The config to free. */ [[gnu::nonnull(1)]] -void delete_project_config(ProjectConfig* config); + +void delete_project_config(ProjectConfig *config); /** * @brief Delete a target configuration by deallocation. */ [[gnu::nonnull(1)]] -void delete_target_config(TargetConfig*); + +void delete_target_config(TargetConfig *); /** * @brief Parse the given command line arguments so that calls to @@ -165,7 +188,7 @@ void delete_target_config(TargetConfig*); * @param argc Number of arguments * @param argv Array of arguments */ -void parse_options(int argc, char* argv[]); +void parse_options(int argc, char *argv[]); /** * @brief Tests whether an option was set as argument. @@ -174,7 +197,8 @@ void parse_options(int argc, char* argv[]); * @return 1 if the options was set, 0 otherwise */ [[gnu::nonnull(1)]] -bool is_option_set(const char* option); + +bool is_option_set(const char *option); /** * @brief Returns the options information if present @@ -183,7 +207,8 @@ bool is_option_set(const char* option); * @return A valid option struct or NULL if not found. */ [[gnu::nonnull(1)]] -const Option* get_option(const char* option); + +const Option *get_option(const char *option); /** * @brief Put a copy of all options whos index is greather than the index @@ -192,7 +217,8 @@ const Option* get_option(const char* option); * @return an array of options that followed command. */ [[gnu::nonnull(1)]] [[nodiscard("must be freed")]] -GArray* get_non_options_after(const char* command); + +GArray *get_non_options_after(const char *command); void init_toml(); diff --git a/src/compiler.c b/src/compiler.c index 3d12803..fac0dba 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -17,6 +17,7 @@ #include #include #include +#include #define GRAPHVIZ_FILE_EXTENSION "gv" @@ -244,65 +245,97 @@ static int compile_module_with_dependencies(ModuleFileStack* unit, Dependency* dependency = g_hash_table_lookup(target->dependencies, child->value); - gchar* cwd = g_get_current_dir(); - chdir(dependency->path); + switch (dependency->kind) { + case GemstoneProject: + gchar* cwd = g_get_current_dir(); + chdir(dependency->mode.project.path); - ProjectConfig* new_config = default_project_config(); - if (load_project_config(new_config)) { - print_message(Error, "Failed to load project config: `%s`", - child->value); - return EXIT_FAILURE; + ProjectConfig* new_config = default_project_config(); + if (load_project_config(new_config)) { + print_message(Error, "Failed to load project config: `%s`", + child->value); + return EXIT_FAILURE; + } + + TargetConfig* dep_target = g_hash_table_lookup(new_config->targets, dependency->mode.project.target); + if (build_target(unit, dep_target)) { + print_message(Error, "Failed to build project config: `%s`", + child->value); + return EXIT_FAILURE; + } + + GPathBuf* buf = g_path_buf_new_from_path(dependency->mode.project.path); + TargetConfig* dep_conf = g_hash_table_lookup(new_config->targets, dependency->mode.project.target); + char* root_mod = dep_conf->root_module; + g_path_buf_push(buf, root_mod); + char* rel_path = g_path_buf_to_path(buf); + + GPathBuf* dep_bin = g_path_buf_new(); + g_path_buf_push(dep_bin, dependency->mode.project.path); + g_path_buf_push(dep_bin, dep_conf->archive_directory); + g_path_buf_push(dep_bin, g_strjoin(".", dep_conf->name, "o", NULL)); + char* dep_bin_path = g_path_buf_to_path(dep_bin); + + g_array_append_val(dependency->libraries, dep_bin_path); + + const char* path = + get_absolute_import_path(target, rel_path); + + if (g_hash_table_contains(imports, path)) { + continue; + } + + ModuleFile* imported_file = push_file(unit, path); + AST_NODE_PTR imported_module = + AST_new_node(empty_location(imported_file), AST_Module, NULL); + + if (compile_module_with_dependencies(unit, imported_file, dep_conf, imported_module) + == EXIT_SUCCESS) { + AST_import_module(root_module, i + 1, imported_module); + } else { + return EXIT_FAILURE; + } + + g_hash_table_insert(imports, (gpointer) path, NULL); + + g_path_buf_pop(buf); + gchar* directory = g_path_buf_to_path(buf); + gchar* cached_directory = + mem_strdup(MemoryNamespaceLld, directory); + g_free(directory); + g_array_append_val(target->import_paths, cached_directory); + + chdir(cwd); + + GHashTableIter iter; + + g_hash_table_iter_init(&iter, dep_target->dependencies); + char* key; + Dependency* dep; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &dep)) { + if (dep->kind == GemstoneProject) { + for (guint i = 0; i < dep->libraries->len; i++) { + char* dep_lib = g_array_index(dep->libraries, char*, i); + g_array_append_val(dependency->libraries, dep_lib); + } + } else if (dep->kind == NativeLibrary) { + char* library_name = build_platform_library_name(dep->mode.library.name, dep->mode.library.shared); + g_array_append_val(dependency->libraries, library_name); + } + } + + break; + case NativeLibrary: + + char* library_name = build_platform_library_name(dependency->mode.library.name, dependency->mode.library.shared); + g_array_append_val(dependency->libraries, library_name); + + break; + default: + break; } - TargetConfig* dep_target = g_hash_table_lookup(new_config->targets, dependency->target); - if (build_target(unit, dep_target)) { - print_message(Error, "Failed to build project config: `%s`", - child->value); - return EXIT_FAILURE; - } - GPathBuf* buf = g_path_buf_new_from_path(dependency->path); - TargetConfig* dep_conf = g_hash_table_lookup(new_config->targets, dependency->target); - char* root_mod = dep_conf->root_module; - g_path_buf_push(buf, root_mod); - char* rel_path = g_path_buf_to_path(buf); - - GPathBuf* dep_bin = g_path_buf_new(); - g_path_buf_push(dep_bin, dependency->path); - g_path_buf_push(dep_bin, dep_conf->archive_directory); - g_path_buf_push(dep_bin, g_strjoin(".", dep_conf->name, "o", NULL)); - char* dep_bin_path = g_path_buf_to_path(dep_bin); - - g_array_append_val(dependency->libraries, dep_bin_path); - - const char* path = - get_absolute_import_path(target, rel_path); - - if (g_hash_table_contains(imports, path)) { - continue; - } - - ModuleFile* imported_file = push_file(unit, path); - AST_NODE_PTR imported_module = - AST_new_node(empty_location(imported_file), AST_Module, NULL); - - if (compile_file_to_ast(imported_module, imported_file) - == EXIT_SUCCESS) { - AST_import_module(root_module, i + 1, imported_module); - } else { - return EXIT_FAILURE; - } - - g_hash_table_insert(imports, (gpointer) path, NULL); - - g_path_buf_pop(buf); - gchar* directory = g_path_buf_to_path(buf); - gchar* cached_directory = - mem_strdup(MemoryNamespaceLld, directory); - g_free(directory); - g_array_append_val(target->import_paths, cached_directory); - - chdir(cwd); } else { print_message(Error, "Cannot resolve path for import: `%s`", diff --git a/src/link/lib.c b/src/link/lib.c index 8f733d3..25d288f 100644 --- a/src/link/lib.c +++ b/src/link/lib.c @@ -72,3 +72,18 @@ void link_print_available_driver() { printf(" - %s\n", (char*) key); } } + +static const char* get_library_file_extension(bool shared) { + if (shared) { + return "so"; + } else { + return "o"; + } +} + +char* build_platform_library_name(char* basename, bool shared) { + char* library_name = g_strjoin("", "lib", basename, ".", get_library_file_extension(shared), NULL); + char* cached_library_name = mem_strdup(MemoryNamespaceLld, library_name); + g_free(library_name); + return cached_library_name; +} diff --git a/src/link/lib.h b/src/link/lib.h index 3e83dcb..71c6054 100644 --- a/src/link/lib.h +++ b/src/link/lib.h @@ -15,4 +15,6 @@ bool link_run(TargetLinkConfig*); void link_print_available_driver(); +char* build_platform_library_name(char* basename, bool shared); + #endif // GEMSTONE_LIB_H diff --git a/src/link/lldc/lldc.c b/src/link/lldc/lldc.c index 4005603..44fce15 100644 --- a/src/link/lldc/lldc.c +++ b/src/link/lldc/lldc.c @@ -11,7 +11,9 @@ extern int lld_main(int Argc, const char **Argv, const char **outstr); const char* FLAGS[] = { - "" + "--fatal-warnings", + "--Bdynamic", + "--dynamic-linker=/usr/bin/ld.so" }; bool lldc_link(TargetLinkConfig* config) { @@ -21,17 +23,17 @@ bool lldc_link(TargetLinkConfig* config) { char* linker = "ld.lld"; g_array_append_val(arguments, linker); - for (guint i = 0; i < config->object_file_names->len; i++) { - char* obj = g_array_index(config->object_file_names, char*, i); - g_array_append_val(arguments, obj); - } - for (int i = 0; i < sizeof(FLAGS)/sizeof(char*); i++) { char* flag = (char*) FLAGS[i]; g_array_append_val(arguments, flag); } + for (guint i = 0; i < config->object_file_names->len; i++) { + char* obj = g_array_index(config->object_file_names, char*, i); + g_array_append_val(arguments, obj); + } + char* output_flag = "-o"; g_array_append_val(arguments, output_flag); g_array_append_val(arguments, config->output_file); diff --git a/src/llvm/link/lld.c b/src/llvm/link/lld.c index 83a676d..2ec24d1 100644 --- a/src/llvm/link/lld.c +++ b/src/llvm/link/lld.c @@ -98,8 +98,21 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) for (guint k = 0; k < dep->libraries->len; k++) { char* lib = g_array_index(dep->libraries, char*, k); + if (lib == NULL) + continue; - g_array_append_val(config->object_file_names, lib); + // resolve path to object file + if (g_file_test(lib, G_FILE_TEST_EXISTS)) { + g_array_append_val(config->object_file_names, lib); + continue; + } + + char* path = get_absolute_link_path(target_config, lib); + if (path == NULL) { + print_message(Error, "Unable to resolve dependency: %s", lib); + } else { + g_array_append_val(config->object_file_names, path); + } } } diff --git a/tests/box/build.toml b/tests/box/build.toml index 6b20110..102bb25 100644 --- a/tests/box/build.toml +++ b/tests/box/build.toml @@ -8,6 +8,7 @@ authors = [ "Sven Vogel " ] [[targets]] name = "prime" import-paths = [ "." ] +link-paths = [ "/usr/lib" ] root = "main.gsc" mode = "application" output = "bin" @@ -18,4 +19,4 @@ print_ir = true print_ast = true [targets.dependencies] -std = { path = "../../lib/src", target = "gsc-libc" } +std = { build-path = "../../lib/src", target = "gsc-libc" }