diff options
| author | Mel <mel@rnrd.eu> | 2026-01-27 04:15:32 +0100 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-01-27 04:15:32 +0100 |
| commit | 129e5669015a7ffd78d0f665f46cc434bcf126d2 (patch) | |
| tree | 97c203070590cf27bba2209c1998bcc546260753 /boot/build.c | |
| parent | 38bc2d06fff322a99ba6ba46df69bbd2b40c6173 (diff) | |
| download | catskill-129e5669015a7ffd78d0f665f46cc434bcf126d2.tar.zst catskill-129e5669015a7ffd78d0f665f46cc434bcf126d2.zip | |
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/build.c')
| -rw-r--r-- | boot/build.c | 137 |
1 files changed, 126 insertions, 11 deletions
diff --git a/boot/build.c b/boot/build.c index 1a1cb21..0c05372 100644 --- a/boot/build.c +++ b/boot/build.c @@ -99,9 +99,9 @@ write_build_file(struct String source, struct String input_path) bool copy_runtime_library(struct String build_path) { - const ascii core[] = CATSKILL_EMBED("./boot/runtime/core.c"); - const ascii runtime[] = CATSKILL_EMBED("./boot/runtime/runtime.c"); - const ascii stubs[] = CATSKILL_EMBED("./boot/runtime/stubs.c"); + const ascii core[] = CATBOOT_EMBED("./boot/runtime/core.c"); + const ascii runtime[] = CATBOOT_EMBED("./boot/runtime/runtime.c"); + const ascii stubs[] = CATBOOT_EMBED("./boot/runtime/stubs.c"); struct String build_runtime_path = string_append_c_str(build_path, "/runtime/"); if (mkdir(string_c_str(build_runtime_path), 0700) == -1) { @@ -166,17 +166,11 @@ struct Build_Result struct String output_path; }; +#ifdef CATBOOT_BACKEND_CLANG struct Build_Result -compile_using_backend( +compile_using_backend_clang( struct String build_path, struct String source_path, struct String output_path) { - log_debug("current backend: LLVM Clang (CLI)\n"); - - // TODO: right now we always run the compiler from this source directory, - // so we can refer to the runtime libraries by a relative path "./boot/runtime/", - // but in the future we should embed these files into this compiler, and write - // them out into the build directory. - // probably too many flags for our purposes, but we kind of want // this simple bootstrapping output to be the eventual target // of our self-hosted compiler stack, so we avoid anything fancy @@ -246,6 +240,127 @@ compile_using_backend( .output_path = output_path, }; } +#endif + +#ifdef CATBOOT_BACKEND_TCC + +#include <libtcc.h> + +#define BUILD_TCC_MAX_ERROR_LOG_LENGTH 65536 + +static struct String_Buffer tcc_errors; +bool tcc_errors_truncated; + +void +build_tcc_on_error(void* something, const ascii* message) +{ + (void)something; + if (tcc_errors_truncated) return; + + const ascii* log_prefix = ANSI_BOLD " tcc: " ANSI_NO_BOLD; + const ascii* log_suffix = "\n"; + + uint added_length = strlen(message) + strlen(log_prefix) + strlen(log_suffix); + uint free_space = string_buffer_capacity(&tcc_errors) - string_buffer_length(&tcc_errors); + + if (free_space <= added_length) { + tcc_errors_truncated = true; + return; + } + + string_buffer_append_c_str(&tcc_errors, log_prefix); + string_buffer_append_c_str(&tcc_errors, message); + string_buffer_append_c_str(&tcc_errors, log_suffix); +} + +void +build_tcc_reset_errors() +{ + if (tcc_errors.data == nil) + tcc_errors = string_buffer_new(BUILD_TCC_MAX_ERROR_LOG_LENGTH); + string_buffer_clear(&tcc_errors); + tcc_errors_truncated = false; +} + +struct Build_Result +compile_using_backend_tcc( + struct String build_path, struct String source_path, struct String output_path) +{ + TCCState* tcc = tcc_new(); + if (!tcc) { + log_error("failed to initialize backend (TCC) compiler\n"); + goto error; + } + + build_tcc_reset_errors(); + tcc_set_error_func(tcc, nil, build_tcc_on_error); + + // NOTE: this has to be called prior to `tcc_set_output_type`, apparently setting the output + // to EXE pulls in some kind of default runtime which declares the _start & co. symbols before we can! + tcc_set_options(tcc, "-static -nostdlib -nostdinc"); + + // we only want a simple executable output. + // other options can create a dynamic library, or add the output + // to our memory to run directly, like for a just-in-time compiler, pretty neat! + tcc_set_output_type(tcc, TCC_OUTPUT_EXE); + + // add necessary include paths + struct String runtime_path = string_append_c_str(build_path, "/runtime/"); + tcc_add_include_path(tcc, string_c_str(runtime_path)); + + tcc_add_sysinclude_path(tcc, MUSL_DEV "/include"); + + // linking order, must be exact: + // crt1.o -> crti.o -> stubs.c -> source.c -> libc.a -> crtn.o + tcc_add_file(tcc, MUSL_LIB "/lib/crt1.o"); + tcc_add_file(tcc, MUSL_LIB "/lib/crti.o"); + + struct String stubs_path = string_append_c_str(runtime_path, "stubs.c"); + tcc_add_file(tcc, string_c_str(stubs_path)); + + if (tcc_add_file(tcc, string_c_str(source_path)) == -1) { + log_error( + "failed to add transpiled source to backend compiler (TCC), probably malformed?\n"); + goto error; + } + + tcc_add_file(tcc, MUSL_LIB "/lib/libc.a"); + tcc_add_file(tcc, MUSL_LIB "/lib/crtn.o"); + + // now run the compilation, and output! + if (tcc_output_file(tcc, string_c_str(output_path)) == -1) { + log_error("error during backend (TCC) compilation!\n"); + goto error; + } + + tcc_delete(tcc); + return (struct Build_Result){ .success = true }; + +error: + tcc_delete(tcc); + struct String log = string_buffer_to_string(&tcc_errors); + return (struct Build_Result){ + .success = false, + .compiler_exit_code = -1, + .compiler_log = log, + }; +} +#endif + +struct Build_Result +compile_using_backend( + struct String build_path, struct String source_path, struct String output_path) +{ +#if defined(CATBOOT_BACKEND_CLANG) + log_debug("current backend: LLVM Clang (CLI)\n"); + return compile_using_backend_clang(build_path, source_path, output_path); +#elif defined(CATBOOT_BACKEND_TCC) + log_debug("current backend: TCC (libtcc)\n"); + return compile_using_backend_tcc(build_path, source_path, output_path); +#else +#error "no backend defined for compilation" +#endif +} struct Build_Result build_executable(struct String source, struct String output_path) |
