diff options
Diffstat (limited to 'boot/build.c')
| -rw-r--r-- | boot/build.c | 162 |
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; +} |
