about summary refs log tree commit diff
path: root/boot/build.c
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-01-26 03:15:18 +0100
committerMel <mel@rnrd.eu>2026-01-26 03:15:18 +0100
commit38bc2d06fff322a99ba6ba46df69bbd2b40c6173 (patch)
tree0485c271240bdde9d5cded82623b76e734fe9f70 /boot/build.c
parent4dc6b49db40eaebf700d5c26c93c5f33b3db60ac (diff)
downloadcatskill-38bc2d06fff322a99ba6ba46df69bbd2b40c6173.tar.zst
catskill-38bc2d06fff322a99ba6ba46df69bbd2b40c6173.zip
Streamline build and temporary build directory, copy runtime
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/build.c')
-rw-r--r--boot/build.c162
1 files changed, 150 insertions, 12 deletions
diff --git a/boot/build.c b/boot/build.c
index 1c95414..1a1cb21 100644
--- a/boot/build.c
+++ b/boot/build.c
@@ -28,6 +28,30 @@
 #error "MUSL_DEV not defined"
 #endif
 
+bool
+create_build_directory(struct String* build_path)
+{
+    // we can't directly call mkdtemp to create nested directories,
+    // so we create the parent /tmp/catskill/ first.
+    ascii build_dir_template[] = "/tmp/catskill/build_XXXXXX";
+
+    struct stat st;
+    if (stat("/tmp/catskill", &st) == -1) {
+        if (mkdir("/tmp/catskill", 0700) == -1) {
+            log_error("failed to create /tmp/catskill/ directory\n");
+            return false;
+        }
+    }
+
+    ascii* build_dir_path = mkdtemp(build_dir_template);
+    if (!build_dir_path) { return false; }
+
+    *build_path = string_from_c_string(build_dir_path);
+    return true;
+}
+
+// TODO: clean up build directory after!
+
 struct Compiler_Command_Result
 {
     integer exit_code;
@@ -47,7 +71,9 @@ run_compiler_command(const ascii* command)
 
     while ((bytes_read = fread(log_buffer, 1, ARRAY_SIZE(log_buffer), command_pipe)) > 0) {
         if (bytes_read >= ARRAY_SIZE(log_buffer)) {
-            log_error("compiler output surpassed maximum output length... truncating to %lu bytes\n", ARRAY_SIZE(log_buffer) - 1);
+            log_error(
+                "compiler output surpassed maximum output length... truncating to %lu bytes\n",
+                ARRAY_SIZE(log_buffer) - 1);
             break;
         }
     }
@@ -58,6 +84,79 @@ run_compiler_command(const ascii* command)
     return (struct Compiler_Command_Result){ .exit_code = exit_code, .log = log };
 }
 
+// TODO: move this over to common library
+bool
+write_build_file(struct String source, struct String input_path)
+{
+    FILE* input_file = fopen(string_c_str(input_path), "w");
+    if (!input_file) { return false; }
+
+    fwrite(source.data, 1, source.length, input_file);
+    fclose(input_file);
+    return true;
+}
+
+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");
+
+    struct String build_runtime_path = string_append_c_str(build_path, "/runtime/");
+    if (mkdir(string_c_str(build_runtime_path), 0700) == -1) {
+        log_error("failed to create build runtime directory\n");
+        return false;
+    }
+
+    struct String
+        core_path = string_append_c_str(build_runtime_path, "core.c"),
+        runtime_path = string_append_c_str(build_runtime_path, "runtime.c"),
+        stubs_path = string_append_c_str(build_runtime_path, "stubs.c");
+
+    struct String
+        core_source = string_from_c_string(core),
+        runtime_source = string_from_c_string(runtime), stubs_source = string_from_c_string(stubs);
+
+    if (!write_build_file(core_source, core_path)) return false;
+    if (!write_build_file(runtime_source, runtime_path)) return false;
+    if (!write_build_file(stubs_source, stubs_path)) return false;
+
+    return true;
+}
+
+bool
+copy_file(struct String source_path, struct String dest_path)
+{
+    FILE* source_file = fopen(string_c_str(source_path), "rb");
+    if (!source_file) {
+        log_error("failed to open source file for copying: %s\n", source_path);
+        return false;
+    }
+
+    // create destination file with the right permissions first
+    if (!creat(string_c_str(dest_path), 0700)) {
+        log_error("failed to create destination file for copying: %s\n", dest_path);
+        fclose(source_file);
+        return false;
+    }
+    FILE* dest_file = fopen(string_c_str(dest_path), "wb");
+    if (!dest_file) {
+        log_error("failed to open destination file for copying: %s\n", dest_path);
+        fclose(source_file);
+        return false;
+    }
+
+    ascii buffer[4096];
+    size_t bytes;
+    while ((bytes = fread(buffer, 1, sizeof(buffer), source_file)) > 0) {
+        fwrite(buffer, 1, bytes, dest_file);
+    }
+    fclose(source_file);
+    fclose(dest_file);
+    return true;
+}
+
 struct Build_Result
 {
     bool success;
@@ -68,15 +167,10 @@ struct Build_Result
 };
 
 struct Build_Result
-build_executable(struct String build_path)
+compile_using_backend(
+    struct String build_path, struct String source_path, struct String output_path)
 {
-
-    struct String source_path = string_append_c_str(build_path, "/input.c");
-    struct String output_path = string_append_c_str(build_path, "/output");
-
-    ascii* compiler = "clang";
-
-    log_debug("current backend: %s\n", compiler);
+    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/",
@@ -87,8 +181,10 @@ build_executable(struct String build_path)
     // this simple bootstrapping output to be the eventual target
     // of our self-hosted compiler stack, so we avoid anything fancy
     // for an easier target to hit.
+    struct String runtime_path = string_append_c_str(build_path, "/runtime/");
+    struct String stubs_path = string_append_c_str(runtime_path, "stubs.c");
     const ascii* arguments[] = {
-        compiler,
+        "clang",
 
         // high-level compiler behavior
         "-O0",
@@ -116,19 +212,22 @@ build_executable(struct String build_path)
         // linker and header options, static compilation w/ musl
         "-static",
         "-nostdlib",
-        "-I./boot/runtime/",
+        "-I",
+        string_c_str(runtime_path),
         "-isystem",
         MUSL_DEV "/include",
         // linking must follow this exact order
         MUSL_LIB "/lib/crt1.o",
         MUSL_LIB "/lib/crti.o",
-        "./boot/runtime/stubs.c", // stub out some software float implementations
+        string_c_str(stubs_path), // stub out some software float implementations
         string_c_str(source_path),
         MUSL_LIB "/lib/libc.a",
         MUSL_LIB "/lib/crtn.o",
 
         "-o",
         string_c_str(output_path),
+
+        "2>&1", // errors are output to stderr, we want to capture them
     };
 
     // append all flags to single command
@@ -147,3 +246,42 @@ build_executable(struct String build_path)
         .output_path = output_path,
     };
 }
+
+struct Build_Result
+build_executable(struct String source, struct String output_path)
+{
+    struct String build_path;
+    if (!create_build_directory(&build_path)) {
+        log_error("failed to create build directory\n");
+        return (struct Build_Result){};
+    }
+    log_debug("temporary build directory for compilation: %s\n", build_path);
+
+    // temporary input and output paths
+    struct String build_input = string_append_c_str(build_path, "/input.c");
+    struct String build_output = string_append_c_str(build_path, "/output");
+
+    if (!write_build_file(source, build_input)) {
+        log_error("failed to create input source file\n");
+        return (struct Build_Result){};
+    }
+
+    // the runtime library files need to be present in the build directory
+    // for inclusion within the generated c source.
+    if (!copy_runtime_library(build_path)) {
+        log_error("failed to copy runtime library files\n");
+        return (struct Build_Result){};
+    }
+
+    struct Build_Result result = compile_using_backend(build_path, build_input, build_output);
+    if (!result.success) { return result; }
+
+    // copy the output executable to the desired location
+    if (!copy_file(build_output, output_path)) {
+        log_error("failed to copy final executable to %s\n", output_path);
+        return (struct Build_Result){};
+    }
+
+    result.output_path = output_path;
+    return result;
+}