diff options
Diffstat (limited to 'src/tomo.c')
| -rw-r--r-- | src/tomo.c | 511 |
1 files changed, 216 insertions, 295 deletions
@@ -13,9 +13,9 @@ #endif #include "ast.h" -#include "config.h" #include "changes.md.h" #include "compile.h" +#include "config.h" #include "modules.h" #include "naming.h" #include "parse.h" @@ -33,8 +33,18 @@ #include "stdlib/text.h" #include "types.h" -#define run_cmd(...) ({ const char *_cmd = String(__VA_ARGS__); if (verbose) print("\033[34;1m", _cmd, "\033[m"); popen(_cmd, "w"); }) -#define xsystem(...) ({ int _status = system(String(__VA_ARGS__)); if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) errx(1, "Failed to run command: %s", String(__VA_ARGS__)); }) +#define run_cmd(...) \ + ({ \ + const char *_cmd = String(__VA_ARGS__); \ + if (verbose) print("\033[34;1m", _cmd, "\033[m"); \ + popen(_cmd, "w"); \ + }) +#define xsystem(...) \ + ({ \ + int _status = system(String(__VA_ARGS__)); \ + if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) \ + errx(1, "Failed to run command: %s", String(__VA_ARGS__)); \ + }) #define list_text(list) Text$join(Text(" "), list) #define whisper(...) print("\033[2m", __VA_ARGS__, "\033[m") @@ -48,7 +58,7 @@ static const char *paths_str(List_t paths) { Text_t result = EMPTY_TEXT; for (int64_t i = 0; i < paths.length; i++) { if (i > 0) result = Texts(result, Text(" ")); - result = Texts(result, Path$as_text((Path_t*)(paths.data + i*paths.stride), false, &Path$info)); + result = Texts(result, Path$as_text((Path_t *)(paths.data + i * paths.stride), false, &Path$info)); } return Text$as_c_string(result); } @@ -59,50 +69,39 @@ static const char *paths_str(List_t paths) { #define SHARED_SUFFIX ".so" #endif -static OptionalList_t files = NONE_LIST, - args = NONE_LIST, - uninstall = NONE_LIST, - libraries = NONE_LIST; -static OptionalBool_t verbose = false, - quiet = false, - show_version = false, - show_parse_tree = false, - show_prefix = false, - stop_at_transpile = false, - stop_at_obj_compilation = false, - compile_exe = false, - should_install = false, - clean_build = false, - source_mapping = true, +static OptionalList_t files = NONE_LIST, args = NONE_LIST, uninstall = NONE_LIST, libraries = NONE_LIST; +static OptionalBool_t verbose = false, quiet = false, show_version = false, show_parse_tree = false, + show_prefix = false, stop_at_transpile = false, stop_at_obj_compilation = false, + compile_exe = false, should_install = false, clean_build = false, source_mapping = true, show_changelog = false; -static OptionalText_t - show_codegen = NONE_TEXT, - cflags = Text("-Werror -fdollars-in-identifiers -std=c2x -Wno-trigraphs " - " -ffunction-sections -fdata-sections" - " -fno-signed-zeros -fno-finite-math-only " - " -D_XOPEN_SOURCE -D_DEFAULT_SOURCE -fPIC -ggdb" +static OptionalText_t show_codegen = NONE_TEXT, + cflags = Text("-Werror -fdollars-in-identifiers -std=c2x -Wno-trigraphs " + " -ffunction-sections -fdata-sections" + " -fno-signed-zeros -fno-finite-math-only " + " -D_XOPEN_SOURCE -D_DEFAULT_SOURCE -fPIC -ggdb" #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) - " -D_BSD_SOURCE" + " -D_BSD_SOURCE" #endif - " -DGC_THREADS" - " -I'" TOMO_PREFIX "/include' -I'" TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed' -I/usr/local/include"), - ldlibs = Text("-lgc -lm -lgmp -lunistring -ltomo_"TOMO_VERSION), - ldflags = Text("-Wl,-rpath,'"TOMO_PREFIX"/lib',-rpath,/usr/local/lib" - " -L/usr/local/lib"), - optimization = Text("2"), - cc = Text(DEFAULT_C_COMPILER); + " -DGC_THREADS" + " -I'" TOMO_PREFIX "/include' -I'" TOMO_PREFIX "/share/tomo_" TOMO_VERSION + "/installed' -I/usr/local/include"), + ldlibs = Text("-lgc -lm -lgmp -lunistring -ltomo_" TOMO_VERSION), + ldflags = Text("-Wl,-rpath,'" TOMO_PREFIX "/lib',-rpath,/usr/local/lib" + " -L/usr/local/lib"), + optimization = Text("2"), cc = Text(DEFAULT_C_COMPILER); static Text_t config_summary, - // This will be either "" or "sudo -u <user>" or "doas -u <user>" - // to allow a command to put stuff into TOMO_PREFIX as the owner - // of that directory. - as_owner = Text(""); + // This will be either "" or "sudo -u <user>" or "doas -u <user>" + // to allow a command to put stuff into TOMO_PREFIX as the owner + // of that directory. + as_owner = Text(""); static void transpile_header(env_t *base_env, Path_t path); static void transpile_code(env_t *base_env, Path_t path); static void compile_object_file(Path_t path); -static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs); +static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, + List_t extra_ldlibs); static void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link); static void build_library(Path_t lib_dir); static void install_library(Path_t lib_dir); @@ -114,25 +113,22 @@ static void wait_for_child_success(pid_t child); static bool is_config_outdated(Path_t path); typedef struct { - bool h:1, c:1, o:1; + bool h : 1, c : 1, o : 1; } staleness_t; #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { #ifdef __linux__ // Get the file modification time of the compiler, so we // can recompile files after changing the compiler: char compiler_path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", compiler_path, PATH_MAX); - if (count == -1) - err(1, "Could not find age of compiler"); + if (count == -1) err(1, "Could not find age of compiler"); compiler_path[count] = '\0'; - if (stat(compiler_path, &compiler_stat) != 0) - err(1, "Could not find age of compiler"); + if (stat(compiler_path, &compiler_stat) != 0) err(1, "Could not find age of compiler"); #endif #ifdef __OpenBSD__ @@ -140,31 +136,32 @@ int main(int argc, char *argv[]) #endif USE_COLOR = getenv("COLOR") ? strcmp(getenv("COLOR"), "1") == 0 : isatty(STDOUT_FILENO); - if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') - USE_COLOR = false; + if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') USE_COLOR = false; #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) arc4random_buf(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY)); #elif defined(__linux__) assert(getrandom(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY), 0) == sizeof(TOMO_HASH_KEY)); #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif // Set up environment variables: const char *PATH = getenv("PATH"); - setenv("PATH", PATH ? String(TOMO_PREFIX"/bin:", PATH) : TOMO_PREFIX"/bin", 1); + setenv("PATH", PATH ? String(TOMO_PREFIX "/bin:", PATH) : TOMO_PREFIX "/bin", 1); const char *LD_LIBRARY_PATH = getenv("LD_LIBRARY_PATH"); - setenv("LD_LIBRARY_PATH", LD_LIBRARY_PATH ? String(TOMO_PREFIX"/lib:", LD_LIBRARY_PATH) : TOMO_PREFIX"/lib", 1); + setenv("LD_LIBRARY_PATH", LD_LIBRARY_PATH ? String(TOMO_PREFIX "/lib:", LD_LIBRARY_PATH) : TOMO_PREFIX "/lib", 1); const char *LIBRARY_PATH = getenv("LIBRARY_PATH"); - setenv("LIBRARY_PATH", LIBRARY_PATH ? String(TOMO_PREFIX"/lib:", LIBRARY_PATH) : TOMO_PREFIX"/lib", 1); + setenv("LIBRARY_PATH", LIBRARY_PATH ? String(TOMO_PREFIX "/lib:", LIBRARY_PATH) : TOMO_PREFIX "/lib", 1); const char *C_INCLUDE_PATH = getenv("C_INCLUDE_PATH"); - setenv("C_INCLUDE_PATH", C_INCLUDE_PATH ? String(TOMO_PREFIX"/include:", C_INCLUDE_PATH) : TOMO_PREFIX"/include", 1); + setenv("C_INCLUDE_PATH", C_INCLUDE_PATH ? String(TOMO_PREFIX "/include:", C_INCLUDE_PATH) : TOMO_PREFIX "/include", + 1); // Run a tool: if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) { if (strcspn(argv[2], "/;$") == strlen(argv[2])) { - const char *program = String("'"TOMO_PREFIX"'/share/tomo_"TOMO_VERSION"/installed/", argv[2], "/", argv[2]); + const char *program = + String("'" TOMO_PREFIX "'/share/tomo_" TOMO_VERSION "/installed/", argv[2], "/", argv[2]); execv(program, &argv[2]); } print_err("This is not an installed tomo program: ", argv[2]); @@ -183,43 +180,25 @@ int main(int argc, char *argv[]) " --parse|-p: show parse tree\n" " --install|-I: install the executable or library\n" " --optimization|-O <level>: set optimization level\n" - " --run|-r: run a program from " TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed\n" - ); + " --run|-r: run a program from " TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed\n"); Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage); tomo_parse_args( - argc, argv, usage, help, TOMO_VERSION, - {"files", true, List$info(&Path$info), &files}, - {"args", true, List$info(&Text$info), &args}, - {"verbose", false, &Bool$info, &verbose}, - {"v", false, &Bool$info, &verbose}, - {"version", false, &Bool$info, &show_version}, - {"parse", false, &Bool$info, &show_parse_tree}, - {"p", false, &Bool$info, &show_parse_tree}, - {"prefix", false, &Bool$info, &show_prefix}, - {"quiet", false, &Bool$info, &quiet}, - {"q", false, &Bool$info, &quiet}, - {"transpile", false, &Bool$info, &stop_at_transpile}, - {"t", false, &Bool$info, &stop_at_transpile}, - {"compile-obj", false, &Bool$info, &stop_at_obj_compilation}, - {"c", false, &Bool$info, &stop_at_obj_compilation}, - {"compile-exe", false, &Bool$info, &compile_exe}, - {"e", false, &Bool$info, &compile_exe}, - {"uninstall", false, List$info(&Text$info), &uninstall}, - {"u", false, List$info(&Text$info), &uninstall}, - {"library", false, List$info(&Path$info), &libraries}, - {"L", false, List$info(&Path$info), &libraries}, - {"show-codegen", false, &Text$info, &show_codegen}, - {"C", false, &Text$info, &show_codegen}, - {"install", false, &Bool$info, &should_install}, - {"I", false, &Bool$info, &should_install}, - {"optimization", false, &Text$info, &optimization}, - {"O", false, &Text$info, &optimization}, - {"force-rebuild", false, &Bool$info, &clean_build}, - {"f", false, &Bool$info, &clean_build}, - {"source-mapping", false, &Bool$info, &source_mapping}, - {"m", false, &Bool$info, &source_mapping}, - {"changelog", false, &Bool$info, &show_changelog}, - ); + argc, argv, usage, help, TOMO_VERSION, {"files", true, List$info(&Path$info), &files}, + {"args", true, List$info(&Text$info), &args}, {"verbose", false, &Bool$info, &verbose}, + {"v", false, &Bool$info, &verbose}, {"version", false, &Bool$info, &show_version}, + {"parse", false, &Bool$info, &show_parse_tree}, {"p", false, &Bool$info, &show_parse_tree}, + {"prefix", false, &Bool$info, &show_prefix}, {"quiet", false, &Bool$info, &quiet}, + {"q", false, &Bool$info, &quiet}, {"transpile", false, &Bool$info, &stop_at_transpile}, + {"t", false, &Bool$info, &stop_at_transpile}, {"compile-obj", false, &Bool$info, &stop_at_obj_compilation}, + {"c", false, &Bool$info, &stop_at_obj_compilation}, {"compile-exe", false, &Bool$info, &compile_exe}, + {"e", false, &Bool$info, &compile_exe}, {"uninstall", false, List$info(&Text$info), &uninstall}, + {"u", false, List$info(&Text$info), &uninstall}, {"library", false, List$info(&Path$info), &libraries}, + {"L", false, List$info(&Path$info), &libraries}, {"show-codegen", false, &Text$info, &show_codegen}, + {"C", false, &Text$info, &show_codegen}, {"install", false, &Bool$info, &should_install}, + {"I", false, &Bool$info, &should_install}, {"optimization", false, &Text$info, &optimization}, + {"O", false, &Text$info, &optimization}, {"force-rebuild", false, &Bool$info, &clean_build}, + {"f", false, &Bool$info, &clean_build}, {"source-mapping", false, &Bool$info, &source_mapping}, + {"m", false, &Bool$info, &source_mapping}, {"changelog", false, &Bool$info, &show_changelog}, ); if (show_prefix) { print(TOMO_PREFIX); @@ -227,15 +206,13 @@ int main(int argc, char *argv[]) } if (show_changelog) { - print_inline(string_slice((const char*)CHANGES_md, CHANGES_md_len)); + print_inline(string_slice((const char *)CHANGES_md, CHANGES_md_len)); return 0; } if (show_version) { - if (verbose) - print(TOMO_VERSION, " ", GIT_VERSION); - else - print(TOMO_VERSION); + if (verbose) print(TOMO_VERSION, " ", GIT_VERSION); + else print(TOMO_VERSION); return 0; } @@ -256,25 +233,25 @@ int main(int argc, char *argv[]) #endif if (show_codegen.length > 0 && Text$equal_values(show_codegen, Text("pretty"))) - show_codegen = Text("{ sed '/^#line/d;/^$/d' | indent -o /dev/stdout | bat -l c -P; }"); + show_codegen = Text("{ sed '/^#line/d;/^$/d' | clang-format | bat -l c -P; }"); config_summary = Text$from_str(String(cc, " ", cflags, " -O", optimization)); Text_t owner = Path$owner(Path$from_str(TOMO_PREFIX), true); Text_t user = Text$from_str(getenv("USER")); if (!Text$equal_values(user, owner)) { - as_owner = Texts(Text(SUDO" -u "), owner, Text(" ")); + as_owner = Texts(Text(SUDO " -u "), owner, Text(" ")); } for (int64_t i = 0; i < uninstall.length; i++) { - Text_t *u = (Text_t*)(uninstall.data + i*uninstall.stride); - xsystem(as_owner, "rm -rvf '"TOMO_PREFIX"'/share/tomo_"TOMO_VERSION"/installed/", *u); + Text_t *u = (Text_t *)(uninstall.data + i * uninstall.stride); + xsystem(as_owner, "rm -rvf '" TOMO_PREFIX "'/share/tomo_" TOMO_VERSION "/installed/", *u); print("Uninstalled ", *u); } Path_t cwd = Path$current_dir(); for (int64_t i = 0; i < libraries.length; i++) { - Path_t *lib = (Path_t*)(libraries.data + i*libraries.stride); + Path_t *lib = (Path_t *)(libraries.data + i * libraries.stride); *lib = Path$resolved(*lib, cwd); // Fork a child process to build the library to prevent cross-contamination // of side effects when building one library from affecting another library. @@ -282,8 +259,7 @@ int main(int argc, char *argv[]) pid_t child = fork(); if (child == 0) { build_library(*lib); - if (should_install) - install_library(*lib); + if (should_install) install_library(*lib); _exit(0); } wait_for_child_success(child); @@ -301,22 +277,19 @@ int main(int argc, char *argv[]) // Convert `foo` to `foo/foo.tm` and resolve all paths to absolute paths: Path_t cur_dir = Path$current_dir(); for (int64_t i = 0; i < files.length; i++) { - Path_t *path = (Path_t*)(files.data + i*files.stride); - if (Path$is_directory(*path, true)) - *path = Path$child(*path, Texts(Path$base_name(*path), Text(".tm"))); + Path_t *path = (Path_t *)(files.data + i * files.stride); + if (Path$is_directory(*path, true)) *path = Path$child(*path, Texts(Path$base_name(*path), Text(".tm"))); *path = Path$resolved(*path, cur_dir); - if (!Path$exists(*path)) - fail("File not found: ", *path); + if (!Path$exists(*path)) fail("File not found: ", *path); } - if (files.length < 1) - print_err("No file specified!"); + if (files.length < 1) print_err("No file specified!"); quiet = !verbose; for (int64_t i = 0; i < files.length; i++) { - Path_t path = *(Path_t*)(files.data + i*files.stride); + Path_t path = *(Path_t *)(files.data + i * files.stride); if (show_parse_tree) { ast_t *ast = parse_file(Path$as_c_string(path), NULL); print(ast_to_sexp_str(ast)); @@ -324,23 +297,21 @@ int main(int argc, char *argv[]) } Path_t exe_path = compile_exe ? Path$with_extension(path, Text(""), true) - : build_file(Path$with_extension(path, Text(""), true), ""); + : build_file(Path$with_extension(path, Text(""), true), ""); pid_t child = fork(); if (child == 0) { env_t *env = global_env(source_mapping); - List_t object_files = {}, - extra_ldlibs = {}; + List_t object_files = {}, extra_ldlibs = {}; compile_files(env, files, &object_files, &extra_ldlibs); compile_executable(env, path, exe_path, object_files, extra_ldlibs); - if (compile_exe) - _exit(0); + if (compile_exe) _exit(0); char *prog_args[1 + args.length + 1]; - prog_args[0] = (char*)Path$as_c_string(exe_path); + prog_args[0] = (char *)Path$as_c_string(exe_path); for (int64_t j = 0; j < args.length; j++) - prog_args[j + 1] = Text$as_c_string(*(Text_t*)(args.data + j*args.stride)); + prog_args[j + 1] = Text$as_c_string(*(Text_t *)(args.data + j * args.stride)); prog_args[1 + args.length] = NULL; execv(prog_args[0], prog_args); print_err("Could not execute program: ", prog_args[0]); @@ -351,9 +322,9 @@ int main(int argc, char *argv[]) if (compile_exe && should_install) { for (int64_t i = 0; i < files.length; i++) { - Path_t path = *(Path_t*)(files.data + i*files.stride); + Path_t path = *(Path_t *)(files.data + i * files.stride); Path_t exe = Path$with_extension(path, Text(""), true); - xsystem(as_owner, "cp -v '", exe, "' '"TOMO_PREFIX"'/bin/"); + xsystem(as_owner, "cp -v '", exe, "' '" TOMO_PREFIX "'/bin/"); } } return 0; @@ -362,14 +333,11 @@ int main(int argc, char *argv[]) #pragma GCC diagnostic pop #endif -void wait_for_child_success(pid_t child) -{ +void wait_for_child_success(pid_t child) { int status; while (waitpid(child, &status, 0) < 0 && errno == EINTR) { - if (WIFEXITED(status) || WIFSIGNALED(status)) - break; - else if (WIFSTOPPED(status)) - kill(child, SIGCONT); + if (WIFEXITED(status) || WIFSIGNALED(status)) break; + else if (WIFSTOPPED(status)) kill(child, SIGCONT); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { @@ -377,18 +345,15 @@ void wait_for_child_success(pid_t child) } } -Path_t build_file(Path_t path, const char *extension) -{ +Path_t build_file(Path_t path, const char *extension) { Path_t build_dir = Path$sibling(path, Text(".build")); if (mkdir(Path$as_c_string(build_dir), 0755) != 0) { - if (!Path$is_directory(build_dir, true)) - err(1, "Could not make .build directory"); + if (!Path$is_directory(build_dir, true)) err(1, "Could not make .build directory"); } return Path$child(build_dir, Texts(Path$base_name(path), Text$from_str(extension))); } -static const char *get_version(Path_t lib_dir) -{ +static const char *get_version(Path_t lib_dir) { Path_t changes_file = Path$child(lib_dir, Text("CHANGES.md")); OptionalText_t changes = Path$read(changes_file); if (changes.length <= 0) { @@ -401,22 +366,16 @@ static const char *get_version(Path_t lib_dir) return String(string_slice(version_line + 4, strcspn(version_line + 4, "\r\n"))); } -static Text_t get_version_suffix(Path_t lib_dir) -{ - return Texts(Text("_"), Text$from_str(get_version(lib_dir))); -} +static Text_t get_version_suffix(Path_t lib_dir) { return Texts(Text("_"), Text$from_str(get_version(lib_dir))); } -void build_library(Path_t lib_dir) -{ +void build_library(Path_t lib_dir) { lib_dir = Path$resolved(lib_dir, Path$current_dir()); - if (!Path$is_directory(lib_dir, true)) - print_err("Not a valid directory: ", lib_dir); + if (!Path$is_directory(lib_dir, true)) print_err("Not a valid directory: ", lib_dir); Text_t lib_dir_name = Path$base_name(lib_dir); List_t tm_files = Path$glob(Path$child(lib_dir, Text("[!._0-9]*.tm"))); env_t *env = fresh_scope(global_env(source_mapping)); - List_t object_files = {}, - extra_ldlibs = {}; + List_t object_files = {}, extra_ldlibs = {}; compile_files(env, tm_files, &object_files, &extra_ldlibs); @@ -429,27 +388,27 @@ void build_library(Path_t lib_dir) FILE *prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), #ifdef __APPLE__ - " -Wl,-install_name,@rpath/'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "'" + " -Wl,-install_name,@rpath/'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "'" #else - " -Wl,-soname,'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "'" + " -Wl,-soname,'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "'" #endif - " -shared ", paths_str(object_files), " -o '", shared_lib, "'"); + " -shared ", + paths_str(object_files), " -o '", shared_lib, "'"); - if (!prog) - print_err("Failed to run C compiler: ", cc); + if (!prog) print_err("Failed to run C compiler: ", cc); int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - if (!quiet) - print("Compiled library:\t", shared_lib); + if (!quiet) print("Compiled library:\t", shared_lib); } -void install_library(Path_t lib_dir) -{ +void install_library(Path_t lib_dir) { Text_t lib_dir_name = Path$base_name(lib_dir); Text_t version_suffix = get_version_suffix(lib_dir); - Path_t dest = Path$child(Path$from_str(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed"), Texts(lib_dir_name, version_suffix)); + Path_t dest = Path$child(Path$from_str(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed"), + Texts(lib_dir_name, version_suffix)); if (!Path$equal_values(lib_dir, dest)) { if (verbose) whisper("Clearing out any pre-existing version of ", lib_dir_name); xsystem(as_owner, "rm -rf '", dest, "'"); @@ -461,25 +420,26 @@ void install_library(Path_t lib_dir) // If we have `debugedit` on this system, use it to remap the debugging source information // to point to the installed version of the source file. Otherwise, fail silently. if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_dir_name, SHARED_SUFFIX); - int result = system(String(as_owner, "debugedit -b ", lib_dir, - " -d '", dest, "'" - " '", dest, "/lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "' " + int result = system(String(as_owner, "debugedit -b ", lib_dir, " -d '", dest, + "'" + " '", + dest, "/lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "' " ">/dev/null 2>/dev/null")); (void)result; - print("Installed \033[1m", lib_dir_name, "\033[m to "TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", lib_dir_name, version_suffix); + print("Installed \033[1m", lib_dir_name, "\033[m to " TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", + lib_dir_name, version_suffix); } -void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs) -{ +void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs) { Table_t to_link = {}; Table_t dependency_files = {}; for (int64_t i = 0; i < to_compile.length; i++) { - Path_t filename = *(Path_t*)(to_compile.data + i*to_compile.stride); + Path_t filename = *(Path_t *)(to_compile.data + i * to_compile.stride); Text_t extension = Path$extension(filename, true); if (!Text$equal_values(extension, Text("tm"))) print_err("Not a valid .tm file: \x1b[31;1m", filename, "\x1b[m"); - if (!Path$is_file(filename, true)) - print_err("Couldn't find file: ", filename); + if (!Path$is_file(filename, true)) print_err("Couldn't find file: ", filename); build_file_dependency_graph(filename, &dependency_files, &to_link); } @@ -488,14 +448,14 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); Path_t id_file = build_file(entry->filename, ".id"); if (!Path$exists(id_file)) { static const char id_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - char id_str[8]; + char id_str[8]; for (int j = 0; j < (int)sizeof(id_str); j++) { - id_str[j] = id_chars[random_range(0, sizeof(id_chars)-1)]; + id_str[j] = id_chars[random_range(0, sizeof(id_chars) - 1)]; } Text_t filename_id = Text(""); Text_t base = Path$base_name(entry->filename); @@ -516,19 +476,18 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); if (entry->staleness.h || clean_build) { transpile_header(env, entry->filename); entry->staleness.o = true; } else { if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".h")); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", build_file(entry->filename, ".h")); + if (show_codegen.length > 0) xsystem(show_codegen, " <", build_file(entry->filename, ".h")); } } - env->imports = new(Table_t); + env->imports = new (Table_t); struct child_s { struct child_s *next; @@ -541,26 +500,23 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); if (!clean_build && !entry->staleness.c && !entry->staleness.h && !entry->staleness.o && !is_config_outdated(entry->filename)) { if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".c")); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", build_file(entry->filename, ".c")); + if (show_codegen.length > 0) xsystem(show_codegen, " <", build_file(entry->filename, ".c")); if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".o")); continue; } pid_t pid = fork(); if (pid == 0) { - if (clean_build || entry->staleness.c) - transpile_code(env, entry->filename); + if (clean_build || entry->staleness.c) transpile_code(env, entry->filename); else if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".c")); - if (!stop_at_transpile) - compile_object_file(entry->filename); + if (!stop_at_transpile) compile_object_file(entry->filename); _exit(EXIT_SUCCESS); } - child_processes = new(struct child_s, .next=child_processes, .pid=pid); + child_processes = new (struct child_s, .next = child_processes, .pid = pid); } for (; child_processes; child_processes = child_processes->next) @@ -571,7 +527,7 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); Path_t path = entry->filename; path = build_file(path, ".o"); List$insert(object_files, &path, I(0), sizeof(Path_t)); @@ -579,44 +535,39 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * } if (extra_ldlibs) { for (int64_t i = 0; i < to_link.entries.length; i++) { - Text_t lib = *(Text_t*)(to_link.entries.data + i*to_link.entries.stride); + Text_t lib = *(Text_t *)(to_link.entries.data + i * to_link.entries.stride); List$insert(extra_ldlibs, &lib, I(0), sizeof(Text_t)); } } } -bool is_config_outdated(Path_t path) -{ +bool is_config_outdated(Path_t path) { OptionalText_t config = Path$read(build_file(path, ".config")); if (config.length < 0) return true; return !Text$equal_values(config, config_summary); } -void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link) -{ - if (Table$has_value(*to_compile, path, Table$info(&Path$info, &Byte$info))) - return; +void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link) { + if (Table$has_value(*to_compile, path, Table$info(&Path$info, &Byte$info))) return; staleness_t staleness = { - .h=is_stale(build_file(path, ".h"), Path$sibling(path, Text("modules.ini")), true) - || is_stale(build_file(path, ".h"), build_file(path, ":modules.ini"), true) - || is_stale(build_file(path, ".h"), path, false) - || is_stale(build_file(path, ".h"), build_file(path, ".id"), false), - .c=is_stale(build_file(path, ".c"), Path$sibling(path, Text("modules.ini")), true) - || is_stale(build_file(path, ".c"), build_file(path, ":modules.ini"), true) - || is_stale(build_file(path, ".c"), path, false) - || is_stale(build_file(path, ".c"), build_file(path, ".id"), false), + .h = is_stale(build_file(path, ".h"), Path$sibling(path, Text("modules.ini")), true) + || is_stale(build_file(path, ".h"), build_file(path, ":modules.ini"), true) + || is_stale(build_file(path, ".h"), path, false) + || is_stale(build_file(path, ".h"), build_file(path, ".id"), false), + .c = is_stale(build_file(path, ".c"), Path$sibling(path, Text("modules.ini")), true) + || is_stale(build_file(path, ".c"), build_file(path, ":modules.ini"), true) + || is_stale(build_file(path, ".c"), path, false) + || is_stale(build_file(path, ".c"), build_file(path, ".id"), false), }; - staleness.o = staleness.c || staleness.h - || is_stale(build_file(path, ".o"), build_file(path, ".c"), false) - || is_stale(build_file(path, ".o"), build_file(path, ".h"), false); + staleness.o = staleness.c || staleness.h || is_stale(build_file(path, ".o"), build_file(path, ".c"), false) + || is_stale(build_file(path, ".o"), build_file(path, ".h"), false); Table$set(to_compile, &path, &staleness, Table$info(&Path$info, &Byte$info)); assert(Text$equal_values(Path$extension(path, true), Text("tm"))); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { ast_t *stmt_ast = stmt->ast; @@ -626,14 +577,10 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l switch (use->what) { case USE_LOCAL: { Path_t dep_tm = Path$resolved(Path$from_str(use->path), Path$parent(path)); - if (!Path$is_file(dep_tm, true)) - code_err(stmt_ast, "Not a valid file: ", dep_tm); - if (is_stale(build_file(path, ".h"), dep_tm, false)) - staleness.h = true; - if (is_stale(build_file(path, ".c"), dep_tm, false)) - staleness.c = true; - if (staleness.c || staleness.h) - staleness.o = true; + if (!Path$is_file(dep_tm, true)) code_err(stmt_ast, "Not a valid file: ", dep_tm); + if (is_stale(build_file(path, ".h"), dep_tm, false)) staleness.h = true; + if (is_stale(build_file(path, ".c"), dep_tm, false)) staleness.c = true; + if (staleness.c || staleness.h) staleness.o = true; Table$set(to_compile, &path, &staleness, Table$info(&Path$info, &Byte$info)); build_file_dependency_graph(dep_tm, to_compile, to_link); break; @@ -641,16 +588,17 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l case USE_MODULE: { module_info_t mod = get_module_info(stmt_ast); const char *full_name = mod.version ? String(mod.name, "_", mod.version) : mod.name; - Text_t lib = Texts(Text("-Wl,-rpath,'"), - Text(TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), Text$from_str(full_name), - Text("' '" TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), - Text$from_str(full_name), Text("/lib"), Text$from_str(full_name), Text(SHARED_SUFFIX "'")); + Text_t lib = + Texts(Text("-Wl,-rpath,'"), Text(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/"), + Text$from_str(full_name), Text("' '" TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/"), + Text$from_str(full_name), Text("/lib"), Text$from_str(full_name), Text(SHARED_SUFFIX "'")); Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info)); - List_t children = Path$glob(Path$from_str(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", full_name, "/[!._0-9]*.tm"))); + List_t children = Path$glob(Path$from_str( + String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", full_name, "/[!._0-9]*.tm"))); for (int64_t i = 0; i < children.length; i++) { - Path_t *child = (Path_t*)(children.data + i*children.stride); - Table_t discarded = {.fallback=to_compile}; + Path_t *child = (Path_t *)(children.data + i * children.stride); + Table_t discarded = {.fallback = to_compile}; build_file_dependency_graph(*child, &discarded, to_link); } break; @@ -671,9 +619,9 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l } break; } - case USE_HEADER: case USE_C_CODE: { - if (use->path[0] == '<') - break; + case USE_HEADER: + case USE_C_CODE: { + if (use->path[0] == '<') break; Path_t dep_path = Path$resolved(Path$from_str(use->path), Path$parent(path)); if (is_stale(build_file(path, ".o"), dep_path, false)) { @@ -687,38 +635,36 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l } } -time_t latest_included_modification_time(Path_t path) -{ +time_t latest_included_modification_time(Path_t path) { static Table_t c_modification_times = {}; - const TypeInfo_t time_info = {.size=sizeof(time_t), .align=__alignof__(time_t), .tag=OpaqueInfo}; + const TypeInfo_t time_info = {.size = sizeof(time_t), .align = __alignof__(time_t), .tag = OpaqueInfo}; time_t *cached_latest = Table$get(c_modification_times, &path, Table$info(&Path$info, &time_info)); if (cached_latest) return *cached_latest; struct stat s; time_t latest = 0; - if (stat(Path$as_c_string(path), &s) == 0) - latest = s.st_mtime; + if (stat(Path$as_c_string(path), &s) == 0) latest = s.st_mtime; Table$set(&c_modification_times, &path, &latest, Table$info(&Path$info, &time_info)); OptionalClosure_t by_line = Path$by_line(path); if (by_line.fn == NULL) return 0; - OptionalText_t (*next_line)(void*) = by_line.fn; + OptionalText_t (*next_line)(void *) = by_line.fn; Path_t parent = Path$parent(path); bool allow_dot_include = Path$has_extension(path, Text("s")) || Path$has_extension(path, Text("S")); - for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) { + for (Text_t line; (line = next_line(by_line.userdata)).length >= 0;) { line = Text$trim(line, Text(" \t"), true, false); - if (!Text$starts_with(line, Text("#include"), NULL) && !(allow_dot_include && Text$starts_with(line, Text(".include"), NULL))) + if (!Text$starts_with(line, Text("#include"), NULL) + && !(allow_dot_include && Text$starts_with(line, Text(".include"), NULL))) continue; // Check for `"` after `#include` or `.include` and some spaces: - if (!Text$starts_with(Text$trim(Text$from(line, I(9)), Text(" \t"), true, false), Text("\""), NULL)) - continue; + if (!Text$starts_with(Text$trim(Text$from(line, I(9)), Text(" \t"), true, false), Text("\""), NULL)) continue; List_t chunks = Text$split(line, Text("\"")); if (chunks.length < 3) // Should be `#include "foo" ...` -> ["#include ", "foo", "..."] continue; - Text_t included = *(Text_t*)(chunks.data + 1*chunks.stride); + Text_t included = *(Text_t *)(chunks.data + 1 * chunks.stride); Path_t included_path = Path$resolved(Path$from_text(included), parent); time_t included_time = latest_included_modification_time(included_path); if (included_time > latest) { @@ -729,8 +675,7 @@ time_t latest_included_modification_time(Path_t path) return latest; } -bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) -{ +bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) { struct stat target_stat; if (stat(Path$as_c_string(path), &target_stat) != 0) { if (ignore_missing) return false; @@ -739,8 +684,7 @@ bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) #ifdef __linux__ // Any file older than the compiler is stale: - if (target_stat.st_mtime < compiler_stat.st_mtime) - return true; + if (target_stat.st_mtime < compiler_stat.st_mtime) return true; #endif if (Path$has_extension(relative_to, Text("c")) || Path$has_extension(relative_to, Text("h")) @@ -757,55 +701,44 @@ bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) return target_stat.st_mtime < relative_to_stat.st_mtime; } -bool is_stale_for_any(Path_t path, List_t relative_to, bool ignore_missing) -{ +bool is_stale_for_any(Path_t path, List_t relative_to, bool ignore_missing) { for (int64_t i = 0; i < relative_to.length; i++) { - Path_t r = *(Path_t*)(relative_to.data + i*relative_to.stride); - if (is_stale(path, r, ignore_missing)) - return true; + Path_t r = *(Path_t *)(relative_to.data + i * relative_to.stride); + if (is_stale(path, r, ignore_missing)) return true; } return false; } -void transpile_header(env_t *base_env, Path_t path) -{ +void transpile_header(env_t *base_env, Path_t path) { Path_t h_filename = build_file(path, ".h"); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); env_t *module_env = load_module_env(base_env, ast); Text_t h_code = compile_file_header(module_env, Path$resolved(h_filename, Path$from_str(".")), ast); FILE *header = fopen(Path$as_c_string(h_filename), "w"); - if (!header) - print_err("Failed to open header file: ", h_filename); + if (!header) print_err("Failed to open header file: ", h_filename); Text$print(header, h_code); - if (fclose(header) == -1) - print_err("Failed to write header file: ", h_filename); + if (fclose(header) == -1) print_err("Failed to write header file: ", h_filename); - if (!quiet) - print("Transpiled header:\t", h_filename); + if (!quiet) print("Transpiled header:\t", h_filename); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", h_filename); + if (show_codegen.length > 0) xsystem(show_codegen, " <", h_filename); } -void transpile_code(env_t *base_env, Path_t path) -{ +void transpile_code(env_t *base_env, Path_t path) { Path_t c_filename = build_file(path, ".c"); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); env_t *module_env = load_module_env(base_env, ast); Text_t c_code = compile_file(module_env, ast); FILE *c_file = fopen(Path$as_c_string(c_filename), "w"); - if (!c_file) - print_err("Failed to write C file: ", c_filename); + if (!c_file) print_err("Failed to write C file: ", c_filename); Text$print(c_file, c_code); @@ -814,54 +747,43 @@ void transpile_code(env_t *base_env, Path_t path) if (main_binding && main_binding->type->tag == FunctionType) { type_t *ret = Match(main_binding->type, FunctionType)->ret; if (ret->tag != VoidType && ret->tag != AbortType) - compiler_err(ast->file, ast->start, ast->end, - "The main() function in this file has a return type of ", type_to_str(ret), - ", but it should not have any return value!"); + compiler_err(ast->file, ast->start, ast->end, "The main() function in this file has a return type of ", + type_to_str(ret), ", but it should not have any return value!"); - Text$print(c_file, Texts( - "int parse_and_run$$", main_binding->code, "(int argc, char *argv[]) {\n", - module_env->do_source_mapping ? Text("#line 1\n") : EMPTY_TEXT, - "tomo_init();\n", - namespace_name(module_env, module_env->namespace, Text("$initialize")), "();\n" - "\n", - compile_cli_arg_call(module_env, main_binding->code, main_binding->type, version), - "return 0;\n" - "}\n")); + Text$print(c_file, Texts("int parse_and_run$$", main_binding->code, "(int argc, char *argv[]) {\n", + module_env->do_source_mapping ? Text("#line 1\n") : EMPTY_TEXT, "tomo_init();\n", + namespace_name(module_env, module_env->namespace, Text("$initialize")), + "();\n" + "\n", + compile_cli_arg_call(module_env, main_binding->code, main_binding->type, version), + "return 0;\n" + "}\n")); } - if (fclose(c_file) == -1) - print_err("Failed to output C code to ", c_filename); + if (fclose(c_file) == -1) print_err("Failed to output C code to ", c_filename); - if (!quiet) - print("Transpiled code:\t", c_filename); + if (!quiet) print("Transpiled code:\t", c_filename); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", c_filename); + if (show_codegen.length > 0) xsystem(show_codegen, " <", c_filename); } -void compile_object_file(Path_t path) -{ +void compile_object_file(Path_t path) { Path_t obj_file = build_file(path, ".o"); Path_t c_file = build_file(path, ".c"); FILE *prog = run_cmd(cc, " ", cflags, " -O", optimization, " -c ", c_file, " -o ", obj_file); - if (!prog) - print_err("Failed to run C compiler: ", cc); + if (!prog) print_err("Failed to run C compiler: ", cc); int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); Path$write(build_file(path, ".config"), config_summary, 0644); - if (!quiet) - print("Compiled object:\t", obj_file); + if (!quiet) print("Compiled object:\t", obj_file); } -Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) -{ +Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) { ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file ", path); + if (!ast) print_err("Could not parse file ", path); env_t *env = load_module_env(base_env, ast); binding_t *main_binding = get_binding(env, "main"); if (!main_binding || main_binding->type->tag != FunctionType) @@ -875,15 +797,16 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t return exe_path; } - FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", - paths_str(object_files), " -x c - -o ", exe_path); - Text_t program = Texts( - "extern int parse_and_run$$", main_binding->code, "(int argc, char *argv[]);\n" - "__attribute__ ((noinline))\n" - "int main(int argc, char *argv[]) {\n" - "\treturn parse_and_run$$", main_binding->code, "(argc, argv);\n" - "}\n" - ); + FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", + list_text(extra_ldlibs), " ", paths_str(object_files), " -x c - -o ", exe_path); + Text_t program = Texts("extern int parse_and_run$$", main_binding->code, + "(int argc, char *argv[]);\n" + "__attribute__ ((noinline))\n" + "int main(int argc, char *argv[]) {\n" + "\treturn parse_and_run$$", + main_binding->code, + "(argc, argv);\n" + "}\n"); if (show_codegen.length > 0) { FILE *out = run_cmd(show_codegen); @@ -893,11 +816,9 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t Text$print(runner, program); int status = pclose(runner); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - if (!quiet) - print("Compiled executable:\t", exe_path); + if (!quiet) print("Compiled executable:\t", exe_path); return exe_path; } |
