diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/build.c | 162 | ||||
| -rw-r--r-- | boot/catboot.c | 89 | ||||
| -rw-r--r-- | boot/common.c | 8 | ||||
| -rwxr-xr-x | boot/scripts/embed.sh | 2 |
4 files changed, 198 insertions, 63 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; +} diff --git a/boot/catboot.c b/boot/catboot.c index 40756ac..db100dd 100644 --- a/boot/catboot.c +++ b/boot/catboot.c @@ -20,12 +20,6 @@ #define _POSIX_C_SOURCE 200809L -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/stat.h> - #include "catboot.h" #define VERSION "0.0.0" @@ -206,11 +200,33 @@ test_transpile_command(struct Command_Arguments* arguments) return COMMAND_OK; } +struct String +output_name_from_input_name(struct String input) +{ + uint end = string_length(input); + for (uint start = string_length(input) - 1; start > 0; --start) { + switch (string_at(input, start)) { + // we captured the file extension, we need to keep going until we find a slash, + // but we move the end index to the current index first to exclude the extension. + case '.': + end = start; + break; + // we found a slash, so we captured the first part of the filename, + // we can stop here. + case '/': + return string_slice(input, start + 1, end); // exclude the slash itself + // otherwise, keep going. + default: + break; + } + } + + return string_slice(input, 0, end); +} + enum Command_Result default_command(struct Command_Arguments* arguments) { - enum Command_Result result = COMMAND_OK; - struct String source = read_file(arguments->input); struct Source_File source_file = { .source = source, @@ -226,61 +242,38 @@ default_command(struct Command_Arguments* arguments) struct Tree tree; if (parser_do_your_thing(&parser, &tree) != 0) { log_error("parser finished with errors\n"); - - result = COMMAND_FAIL; - goto end; + return COMMAND_FAIL; } - ascii build_directory_template[] = "/tmp/catboot-build-XXXXXX"; // TODO: /tmp/catboot/build-XXXXXX - - ascii* build_directory_path = mkdtemp(build_directory_template); - struct String build_path = string_from_c_string(build_directory_path); - struct String source_path = string_append_c_str(build_path, "/input.c"); - - FILE* output_file = fopen(string_c_str(source_path), "w"); - if (!output_file) { - log_error("could not open temporary output file: %s\n", string_c_str(source_path)); - - result = COMMAND_FAIL; - goto end; - } - - struct Transpile_Output output = transpile_output_from_file(output_file); - struct Transpiler transpiler; - transpiler_new(&transpiler, output); + transpiler_new(&transpiler, transpile_output_from_string()); if (transpiler_catskill_to_c(&transpiler, &tree) != 0) { log_error("transpiler finished with errors\n"); - fclose(output_file); + return COMMAND_FAIL; + } + struct String transpiled_source = transpile_output_string(&transpiler.output); - result = COMMAND_FAIL; - goto end; + struct String output_file; + if (arguments->output) { + output_file = string_from_c_string(arguments->output); + } else { + output_file = output_name_from_input_name(source_file.path); } - fclose(output_file); - struct Build_Result build_result = build_executable(build_path); + struct Build_Result build_result = build_executable(transpiled_source, output_file); if (!build_result.success) { log_error("backend failure with exit code %ld\n", build_result.compiler_exit_code); - log_error("compiler output:\n%.*s\n", (int)build_result.compiler_log.length, build_result.compiler_log.data); + log_error( + "compiler output:\n%.*s\n", (int)build_result.compiler_log.length, + build_result.compiler_log.data); - result = COMMAND_FAIL; - goto end; + return COMMAND_FAIL; } - // copy the output executable to the desired location - const ascii* output_filename = arguments->output ? arguments->output : "a.out"; + log_debug("success! executable written to '%s'.\n", string_c_str(build_result.output_path)); - // simple copy command for now - ascii copy_command[2048]; - snprintf(copy_command, sizeof(copy_command), "cp %s %s", string_c_str(build_result.output_path), output_filename); - if (system(copy_command) != 0) { - log_error("failed to copy executable to %s\n", output_filename); - result = COMMAND_FAIL; - } - -end: - return result; + return COMMAND_OK; } typedef enum Command_Result (*Command_Function)(struct Command_Arguments*); diff --git a/boot/common.c b/boot/common.c index 1c65deb..125af86 100644 --- a/boot/common.c +++ b/boot/common.c @@ -10,6 +10,7 @@ #pragma once +#include <fcntl.h> #include <math.h> #include <stdarg.h> #include <stddef.h> @@ -17,6 +18,9 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #define uint8 uint8_t @@ -53,7 +57,7 @@ // the array will be filled with hexadecimal values of the file contents, // with a nil byte appended at the end. // the size of the array can be retrieved through `strlen(array)`, or `ARRAY_SIZE(array)-1`. -#define CATSKILL_EMBED(...) {0} +#define CATSKILL_EMBED(...) { 0 } // ansi escape codes for terminal color and style #define ANSI(code) "\33[" code "m" @@ -188,7 +192,7 @@ log_error(const ascii* message, ...) #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) // the common size of region memory blocks. -#define REGION_SIZE 65536 +#define REGION_SIZE 1048576 // statically allocates a region of memory of a given size // for a single type. diff --git a/boot/scripts/embed.sh b/boot/scripts/embed.sh index f27c2a6..baf2e38 100755 --- a/boot/scripts/embed.sh +++ b/boot/scripts/embed.sh @@ -23,7 +23,7 @@ process_file() { cp "$source_file" "$output_file" - local embed_regex='CATSKILL_EMBED\(([^)]+)\)' # const byte data[] = CATSKILL_EMBED(./file); + local embed_regex='CATSKILL_EMBED\("([^)]+)"\)' # const byte data[] = CATSKILL_EMBED("./file"); local comment_regex='^\s*//' # // the macro CATSKILL_EMBED does... local define_regex='^\s*#define' # #define CATSKILL_EMBED {0} |
