rework of module import

This commit is contained in:
Sven Vogel 2024-09-01 16:55:53 +02:00
parent cf59e9640d
commit 76f5a0fd7f
15 changed files with 211 additions and 69 deletions

View File

@ -1,13 +0,0 @@
[project]
name = "gscstd"
version = "0.1.0"
description = "glibc build of the gemstone standard library"
license = "GPL-2.0"
authors = [ "Sven Vogel <sven.vogel123@web.de>" ]
[target.gscstd]
import-paths = [ "glibc" ]
root = "glibc/std.gsc"
mode = "library"
output = "bin"
archive = "archive"

22
lib/src/build.toml Normal file
View File

@ -0,0 +1,22 @@
[project]
name = "gscstd"
version = "0.1.0"
description = "Test applications for the GSC standard library."
license = "GPL-2.0"
authors = [ "Sven Vogel <sven.vogel123@web.de>" ]
[[targets]]
name = "gsc-libc"
import-paths = [ "libc" ]
root = "libc/std.gsc"
mode = "library"
output = "bin"
archive = "archive"
driver = "ld.lld"
print_asm = true
print_ir = true
print_ast = true
[[targets.dependencies]]
# link against system default libc shared library
libc = { shared = true }

View File

@ -2,13 +2,13 @@
# Bootstrap module for libgscstd-glibc
# used on GNU/Linux operating systems
import "os"
include "os"
# main function is to be implemented by the application source
fun main()
# entrypoint function
fun start() {
fun _start() {
main()
exit(0 as i32)
}

View File

@ -336,6 +336,29 @@ void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) {
AST_delete_node(src);
}
void AST_import_module(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) {
assert(dst != NULL);
assert(src != NULL);
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);
for (int u = 0; u < node->children->len - 1; u++) {
AST_push_node(decl, AST_get_node(node, u));
}
node = decl;
}
AST_insert_node(dst, k + i, node);
}
AST_delete_node(src);
}
void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child) {
assert(owner != NULL);
assert(child != NULL);

View File

@ -230,6 +230,9 @@ AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner,
[[gnu::nonnull(1), gnu::nonnull(3)]]
void AST_merge_modules(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src);
[[gnu::nonnull(1), gnu::nonnull(3)]]
void AST_import_module(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src);
[[gnu::nonnull(1), gnu::nonnull(3)]]
void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child);

View File

@ -401,13 +401,24 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) {
return PROJECT_SEMANTIC_ERR;
}
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");
dependency->libraries = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
return PROJECT_OK;
}
static int parse_target(const ProjectConfig* config,
const toml_table_t* target_table, const char* name) {
const toml_table_t* target_table) {
DEBUG("parsing target table...");
TargetConfig* target_config = default_target_config();
target_config->name = (char*) name;
get_str(&target_config->name, target_table, "name");
get_bool(&target_config->print_ast, target_table, "print_ast");
get_bool(&target_config->print_asm, target_table, "print_asm");
@ -442,13 +453,33 @@ static int parse_target(const ProjectConfig* config,
g_hash_table_insert(config->targets, target_config->name, target_config);
toml_table_t* dependencies = toml_table_in(target_table, "dependencies");
if (dependencies) {
target_config->dependencies = mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal);
for (int i = 0; i < toml_table_ntab(dependencies); i++) {
char* key = (char*) toml_key_in(dependencies, i);
if (key == NULL) {
break;
}
toml_table_t* dependency_table = toml_table_in(dependencies, key);
Dependency* dependency = mem_alloc(MemoryNamespaceOpt, sizeof(Dependency));
if (parse_dependency(dependency, dependency_table, key) == PROJECT_SEMANTIC_ERR) {
return PROJECT_SEMANTIC_ERR;
}
g_hash_table_insert(target_config->dependencies, mem_strdup(MemoryNamespaceOpt, key), dependency);
}
}
return PROJECT_OK;
}
static int parse_targets(ProjectConfig* config, const toml_table_t* root) {
DEBUG("parsing targets of project \"%s\"", config->name);
toml_table_t* targets = toml_table_in(root, "target");
toml_array_t* targets = toml_array_in(root, "targets");
if (targets == NULL) {
print_message(Warning, "Project has no targets");
return PROJECT_SEMANTIC_ERR;
@ -457,16 +488,9 @@ static int parse_targets(ProjectConfig* config, const toml_table_t* root) {
config->targets =
mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal);
for (int i = 0; i < toml_table_ntab(targets); i++) {
const char* key = toml_key_in(targets, i);
if (key == NULL) {
break;
}
toml_table_t* target = toml_table_in(targets, key);
parse_target(config, target,
mem_strdup(MemoryNamespaceOpt, (char*) key));
for (int i = 0; i < toml_array_nelem(targets); i++) {
toml_table_t* target = toml_table_at(targets, i);
parse_target(config, target);
}
return PROJECT_OK;

View File

@ -34,6 +34,13 @@ typedef enum TargetCompilationMode_t {
Library
} TargetCompilationMode;
typedef struct Dependency_t {
char* name;
char* path;
char* target;
GArray* libraries;
} Dependency;
/**
* @brief A target defines a source file which is to be compiled into a specific
* format. Additionally properties such as output folders can be set.
@ -66,6 +73,7 @@ typedef struct TargetConfig_t {
// treat parser warnings as errors
bool gsc_fatal_warnings;
GArray* import_paths;
GHashTable* dependencies;
} TargetConfig;
/**

View File

@ -7,6 +7,7 @@
#include <cfg/opt.h>
#include <codegen/backend.h>
#include <compiler.h>
#include <glib.h>
#include <io/files.h>
#include <lex/util.h>
#include <llvm/backend.h>
@ -14,6 +15,7 @@
#include <set/set.h>
#include <stdlib.h>
#include <sys/log.h>
#include <unistd.h>
#include <yacc/parser.tab.h>
#define GRAPHVIZ_FILE_EXTENSION "gv"
@ -21,11 +23,18 @@
extern void yyrestart(FILE*);
// Module AST node used by the parser for AST construction.
[[maybe_unused]] AST_NODE_PTR root;
[[maybe_unused]]
AST_NODE_PTR root;
// Current file which gets compiled the parser.
// NOTE: due to global state no concurrent compilation is possible
// on parser level.
[[maybe_unused]] ModuleFile* current_file;
[[maybe_unused]]
ModuleFile* current_file;
static int build_project_targets(ModuleFileStack* unit,
const ProjectConfig* config);
static int build_target(ModuleFileStack* unit, const TargetConfig* target);
/**
* @brief Compile the specified file into AST
@ -230,12 +239,84 @@ static int compile_module_with_dependencies(ModuleFileStack* unit,
for (size_t i = 0; i < AST_get_child_count(root_module); i++) {
AST_NODE_PTR child = AST_get_node(root_module, i);
if (child->kind == AST_Import || child->kind == AST_Include) {
if (child->kind == AST_Import) {
if (g_hash_table_contains(target->dependencies, child->value)) {
Dependency* dependency =
g_hash_table_lookup(target->dependencies, child->value);
gchar* cwd = g_get_current_dir();
chdir(dependency->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;
}
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`",
child->value);
return EXIT_FAILURE;
}
} else if (child->kind == AST_Include) {
const char* path =
get_absolute_import_path(target, child->value);
if (path == NULL) {
print_message(Error, "Cannot resolve path for import: `%s`",
print_message(Error,
"Cannot resolve path for include: `%s`",
child->value);
return EXIT_FAILURE;
}
@ -305,10 +386,6 @@ static int build_target(ModuleFileStack* unit, const TargetConfig* target) {
}
}
mem_purge_namespace(MemoryNamespaceLex);
mem_purge_namespace(MemoryNamespaceAst);
mem_purge_namespace(MemoryNamespaceSet);
print_file_statistics(file);
return err;

View File

@ -10,6 +10,10 @@
extern int lld_main(int Argc, const char **Argv, const char **outstr);
const char* FLAGS[] = {
""
};
bool lldc_link(TargetLinkConfig* config) {
GArray* arguments = mem_new_g_array(MemoryNamespaceLld, sizeof(char*));
@ -21,6 +25,13 @@ bool lldc_link(TargetLinkConfig* config) {
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);
}
char* output_flag = "-o";
g_array_append_val(arguments, output_flag);
g_array_append_val(arguments, config->output_file);
@ -33,10 +44,9 @@ bool lldc_link(TargetLinkConfig* config) {
const char* message = NULL;
const bool code = lld_main(arguments->len, (const char**) arguments->data, &message);
free((void*) message);
if (!code) {
print_message(Error, message);
free((void*) message);
}
return code;

View File

@ -89,26 +89,18 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused))
// resolve absolute paths to dependent library object files
DEBUG("resolving target dependencies...");
for (guint i = 0; i < module->imports->len; i++) {
const char* dependency = g_array_index(module->imports, const char*, i);
GHashTableIter iter;
const char* library = g_strjoin("", "libgsc", dependency, ".o", NULL);
g_hash_table_iter_init(&iter, target_config->dependencies);
char* key;
Dependency* dep;
while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &dep)) {
const char* dependency_object =
get_absolute_link_path(target_config, library);
if (dependency_object == NULL) {
ERROR("failed to resolve path to dependency object: %s", library);
print_message(Warning,
"failed to resolve path to dependency object: %s",
dependency);
lld_delete_link_config(config);
lld_delete_link_config(config);
g_free((void*) library);
return NULL;
for (guint k = 0; k < dep->libraries->len; k++) {
char* lib = g_array_index(dep->libraries, char*, k);
g_array_append_val(config->object_file_names, lib);
}
g_free((void*) library);
g_array_append_val(config->object_file_names, dependency_object);
INFO("resolved path of target object: %s", dependency_object);
}
INFO("resolved %d dependencies", config->object_file_names->len);

View File

@ -2442,7 +2442,7 @@ int addFunction(const char* name, Function* function) {
function->name);
return SEMANTIC_ERROR;
}
g_hash_table_insert(declaredFunctions, (gpointer) name, function);
g_hash_table_insert(definedFunctions, (gpointer) name, function);
} else if (function->kind == FunctionDeclarationKind) {
if (g_hash_table_contains(declaredFunctions, name)) {
Function* declaredFunction =
@ -2893,16 +2893,7 @@ Module* create_set(AST_NODE_PTR currentNode) {
return NULL;
}
if (g_hash_table_contains(functions, function->name)) {
print_diagnostic(
&function->impl.definition.nodePtr->location, Error,
"Multiple definition of function: `%s`",
function->name);
return NULL;
}
g_hash_table_insert(functions, (gpointer) function->name,
function);
g_hash_table_insert(rootModule->functions, function->name, function);
DEBUG("created function successfully");
break;

View File

@ -5,12 +5,17 @@ description = "Test applications for the GSC standard library."
license = "GPL-2.0"
authors = [ "Sven Vogel <sven.vogel123@web.de>" ]
[target.prime]
[[targets]]
name = "prime"
import-paths = [ "." ]
root = "main.gsc"
mode = "application"
output = "bin"
archive = "archive"
driver = "ld.lld"
print_asm = true
print_ir = true
print_ast = true
[dependencies]
gscstd = { path = "../../lib" }
[targets.dependencies]
std = { path = "../../lib/src", target = "gsc-libc" }