aboutsummaryrefslogtreecommitdiff
path: root/tomo.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-06-09 16:16:22 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-06-09 16:16:22 -0400
commit77aa022df0dc8c73f8936f5c920941e8a817b1e7 (patch)
treeb23cdc5984ee1d15083bb82872ba6bf505011882 /tomo.c
parent7c255c517e9531e2b9499ab08205dd13c8798b92 (diff)
Change how tomo command line behavior works and how shared libraries are
built/installed.
Diffstat (limited to 'tomo.c')
-rw-r--r--tomo.c254
1 files changed, 118 insertions, 136 deletions
diff --git a/tomo.c b/tomo.c
index a14ae918..63d1249d 100644
--- a/tomo.c
+++ b/tomo.c
@@ -18,24 +18,25 @@
#include "typecheck.h"
#include "types.h"
+#define ENV_CORD(name) CORD_from_char_star(getenv(name) ? getenv(name) : "")
+#define CORD_RUN(...) ({ CORD _cmd = CORD_all(__VA_ARGS__); if (verbose) CORD_printf("\x1b[33m%r\x1b[m\n", _cmd); popen(CORD_to_const_char_star(_cmd), "w"); })
+
typedef enum { MODE_TRANSPILE = 0, MODE_COMPILE_OBJ = 1, MODE_COMPILE_SHARED_OBJ = 2, MODE_COMPILE_EXE = 3, MODE_RUN = 4 } mode_e;
static bool verbose = false;
static bool show_codegen = false;
-static bool cleanup_files = false;
-static const char *autofmt, *cconfig, *cflags, *objfiles, *ldlibs, *ldflags, *cc;
+static CORD autofmt, cconfig, cflags, ldlibs, ldflags, cc;
-static array_t get_file_dependencies(const char *filename, array_t *object_files);
static int transpile_header(env_t *base_env, const char *filename, bool force_retranspile);
static int transpile_code(env_t *base_env, const char *filename, bool force_retranspile);
static int compile_object_file(const char *filename, bool force_recompile);
-static int compile_executable(env_t *base_env, const char *filename, array_t object_files);
+static int compile_executable(env_t *base_env, const char *filename, CORD object_files);
+static void build_file_dependency_graph(const char *filename, table_t *to_compile, table_t *to_link);
int main(int argc, char *argv[])
{
mode_e mode = MODE_RUN;
- const char *filename = NULL;
- int program_arg_index = argc + 1;
+ int after_flags = 1;
for (int i = 1; i < argc; i++) {
if (streq(argv[i], "-t")) {
mode = MODE_TRANSPILE;
@@ -47,10 +48,8 @@ int main(int argc, char *argv[])
mode = MODE_RUN;
} else if (streq(argv[i], "-e")) {
mode = MODE_COMPILE_EXE;
- } else if (streq(argv[i], "-C")) {
- cleanup_files = true;
} else if (streq(argv[i], "-h") || streq(argv[i], "--help")) {
- printf("Usage: %s [[-t|-c|-e|-r] [-C] [option=value]* file.tm [args...]]\n", argv[0]);
+ printf("Usage: %s | %s [-r] file.tm args... | %s (-t|-c|-s) file1.tm file2.tm...\n", argv[0], argv[0], argv[0]);
return 0;
} else if (strchr(argv[i], '=')) {
while (argv[i][0] == '-')
@@ -61,165 +60,176 @@ int main(int argc, char *argv[])
*p = toupper(*p);
setenv(argv[i], eq + 1, 1);
} else {
- filename = argv[i];
- program_arg_index = i + 1;
+ after_flags = i;
break;
}
}
- // register_printf_modifier(L"p");
if (register_printf_specifier('T', printf_type, printf_pointer_size))
errx(1, "Couldn't set printf specifier");
if (register_printf_specifier('W', printf_ast, printf_pointer_size))
errx(1, "Couldn't set printf specifier");
- setenv("TOMO_IMPORT_PATH", "~/.local/tomo/src:.", 0);
+ setenv("TOMO_IMPORT_PATH", "~/.local/src/tomo:.", 0);
- autofmt = getenv("AUTOFMT");
+ CORD home = ENV_CORD("HOME");
+ autofmt = ENV_CORD("AUTOFMT");
if (!autofmt) autofmt = "indent -kr -l100 -nbbo -nut -sob";
if (!autofmt[0]) autofmt = "cat";
verbose = (getenv("VERBOSE") && (streq(getenv("VERBOSE"), "1") || streq(getenv("VERBOSE"), "2")));
show_codegen = (getenv("VERBOSE") && streq(getenv("VERBOSE"), "2"));
- if (filename == NULL) {
+ if (after_flags >= argc) {
repl();
return 0;
}
- if (strlen(filename) < strlen(".tm") + 1 || strncmp(filename + strlen(filename) - strlen(".tm"), ".tm", strlen(".tm")) != 0)
- errx(1, "Not a valid .tm file: %s", filename);
-
- cconfig = getenv("CCONFIG");
+ cconfig = ENV_CORD("CCONFIG");
if (!cconfig)
cconfig = "-std=c11 -fdollars-in-identifiers -fsanitize=signed-integer-overflow -fno-sanitize-recover"
" -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE";
- const char *optimization = getenv("O");
- if (!optimization || !optimization[0]) optimization = "-O1";
- else optimization = heap_strf("-O%s", optimization);
-
- objfiles = getenv("OBJFILES");
- if (!objfiles)
- objfiles = "";
+ CORD optimization = ENV_CORD("O");
+ if (!optimization) optimization = "-O1";
+ else optimization = CORD_all("-O", optimization);
- cflags = getenv("CFLAGS");
+ cflags = ENV_CORD("CFLAGS");
if (!cflags)
- cflags = heap_strf("%s %s -fPIC -ggdb -I./include -I%s/.local/tomo/include -D_DEFAULT_SOURCE", cconfig, optimization, getenv("HOME"));
+ cflags = CORD_all(cconfig, " ", optimization, " -fPIC -ggdb -I./include -I'", home, "/.local/include/tomo' -D_DEFAULT_SOURCE");
- ldflags = heap_strf("-Wl,-rpath,'$ORIGIN',-rpath,'%s/.local/tomo/lib' -L. -L%s/.local/tomo/lib", getenv("HOME"), getenv("HOME"));
+ ldflags = CORD_all("-Wl,-rpath='$ORIGIN',-rpath='", home, "/.local/lib/tomo' -L. -L'", home, "/.local/lib/tomo'");
ldlibs = "-lgc -lcord -lm -ltomo";
- if (getenv("LDLIBS"))
- ldlibs = heap_strf("%s %s", ldlibs, getenv("LDLIBS"));
- cc = getenv("CC");
+ cc = ENV_CORD("CC");
if (!cc) cc = "cc";
- array_t object_files = {};
- const char *my_obj = heap_strf("%s.o", resolve_path(filename, ".", "."));
- Array$insert(&object_files, &my_obj, 0, $ArrayInfo(&$Text));
- array_t file_deps = get_file_dependencies(filename, &object_files);
-
env_t *env = new_compilation_unit();
- int status = transpile_header(env, filename, true);
- if (status != 0) return status;
+ table_t dependency_files = {};
+ table_t to_link = {};
+
+ for (int i = after_flags; i < argc; i++) {
+ const char *resolved = resolve_path(argv[i], ".", ".");
+ if (!resolved) errx(1, "Couldn't resolve path: %s", argv[i]);
+ build_file_dependency_graph(resolved, &dependency_files, &to_link);
+ if (mode == MODE_RUN) break;
+ }
- for (int64_t i = 0; i < file_deps.length; i++) {
- const char *dep = *(char**)(file_deps.data + i*file_deps.stride);
- status = transpile_header(env, dep, false);
+ int status;
+ // Non-lazily (re)compile header files for each source file passed to the compiler:
+ for (int i = after_flags; i < argc; i++) {
+ const char *filename = argv[i];
+ status = transpile_header(env, filename, true);
if (status != 0) return status;
}
- env->imports = new(table_t);
+ // Lazily (re)compile all the header files:
+ for (int64_t i = 0; i < dependency_files.entries.length; i++) {
+ const char *filename = *(char**)(dependency_files.entries.data + i*dependency_files.entries.stride);
+ status = transpile_header(env, filename, false);
+ if (status != 0) return status;
+ }
- status = transpile_code(env, filename, true);
- if (status != 0) return status;
+ // env->imports = new(table_t);
- status = compile_object_file(filename, true);
- if (status != 0) return status;
+ // Non-lazily (re)compile object files for each source file passed to the compiler:
+ for (int i = after_flags; i < argc; i++) {
+ const char *filename = argv[i];
+ status = transpile_code(env, filename, true);
+ if (status != 0) return status;
+ status = compile_object_file(filename, true);
+ if (status != 0) return status;
+ if (mode == MODE_RUN) break;
+ }
if (mode == MODE_COMPILE_OBJ)
return 0;
- for (int64_t i = 0; i < file_deps.length; i++) {
- const char *dep = *(char**)(file_deps.data + i*file_deps.stride);
-
- status = transpile_code(env, dep, false);
+ // Lazily (re)compile object files for each dependency:
+ for (int64_t i = 0; i < dependency_files.entries.length; i++) {
+ const char *filename = *(char**)(dependency_files.entries.data + i*dependency_files.entries.stride);
+ status = transpile_code(env, filename, false);
if (status != 0) return status;
-
- status = compile_object_file(dep, false);
+ status = compile_object_file(filename, false);
if (status != 0) return status;
}
+ CORD object_files = CORD_EMPTY;
+ for (int64_t i = 0; i < dependency_files.entries.length; i++) {
+ const char *filename = *(char**)(dependency_files.entries.data + i*dependency_files.entries.stride);
+ object_files = CORD_all(object_files, filename, ".o ");
+ }
+ for (int64_t i = 0; i < to_link.entries.length; i++) {
+ const char *lib = *(char**)(to_link.entries.data + i*to_link.entries.stride);
+ ldlibs = CORD_all(ldlibs, " ", lib);
+ }
+
+ // For shared objects, link up all the object files into one .so file:
if (mode == MODE_COMPILE_SHARED_OBJ) {
- const char *base = file_base_name(filename);
- const char *outfile = heap_strf("lib%s.so", base);
- const char *cmd = heap_strf("%s %s %s %s %s -Wl,-soname,lib%s.so -shared %s.o -o %s", cc, cflags, ldflags, ldlibs, objfiles, base, filename, outfile);
- if (verbose)
- printf("Running: %s\n", cmd);
- FILE *prog = popen(cmd, "w");
+ const char *first_file = argv[after_flags];
+ const char *libname = file_base_name(first_file);
+
+ const char *h_filename = heap_strf("lib%s.h", libname);
+ FILE *h_file = fopen(h_filename, "w");
+ if (!h_file)
+ errx(1, "Couldn't open file: %s", h_filename);
+ fputs("#pragma once\n", h_file);
+ for (int i = after_flags; i < argc; i++) {
+ const char *filename = argv[i];
+ fprintf(h_file, "#include <%s/%s.h>\n", libname, filename);
+ }
+ if (fclose(h_file))
+ errx(1, "Failed to close file: %s", h_filename);
+
+ CORD outfile = CORD_all("lib", libname, ".so");
+ FILE *prog = CORD_RUN(cc, " ", cflags, " ", ldflags, " ", ldlibs, " -Wl,-soname=", outfile, " -shared ", object_files, " -o ", outfile);
status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return WEXITSTATUS(status);
if (verbose)
- printf("Compiled to %s\n", outfile);
+ CORD_printf("Compiled to %r\n", outfile);
printf("Do you want to install your library? [Y/n] ");
fflush(stdout);
switch (getchar()) {
case 'y': case 'Y': case '\n': {
- const char *name = file_base_name(filename);
- system(heap_strf("mkdir -p ~/.local/tomo/lib ~/.local/tomo/'%s'", name));
- system(heap_strf("cp -v 'lib%s.so' ~/.local/tomo/'%s'/", name, name));
- system(heap_strf("ln -sv '../%s/lib%s.so' ~/.local/tomo/lib/'lib%s.so'", name, name, name));
- for (int64_t i = 0; i < file_deps.length; i++) {
- const char *dep = *(char**)(file_deps.data + i*file_deps.stride);
- system(heap_strf("cp -v %s %s.c %s.h %s.o ~/.local/tomo/%s/", dep, dep, dep, dep, name));
- }
+ system(heap_strf("mkdir -p ~/.local/lib/tomo ~/.local/include/tomo ~/.local/src/tomo/'%s'", libname));
+ system(heap_strf("cp -rv . ~/.local/src/tomo/'%s'", libname));
+ system(heap_strf("ln -sfv '../../src/tomo/%s/lib%s.so' ~/.local/lib/tomo/'lib%s.so'", libname, libname, libname));
+ system(heap_strf("ln -sfv '../../src/tomo/%s/lib%s.h' ~/.local/include/tomo/'lib%s.h'", libname, libname, libname));
}
default: break;
}
return 0;
}
+ const char *filename = argv[after_flags];
int executable_status = compile_executable(env, filename, object_files);
if (mode == MODE_COMPILE_EXE || executable_status != 0)
return executable_status;
- if (cleanup_files) {
- for (int64_t i = 0; i < file_deps.length; i++) {
- const char *dep = *(char**)(file_deps.data + i*file_deps.stride);
- if (verbose)
- printf("Cleaning up %s files...\n", dep);
-
- system(heap_strf("rm -f %s.c %s.h %s.o", dep, dep, dep));
- }
- }
-
char *exe_name = heap_strn(filename, strlen(filename) - strlen(".tm"));
- int num_args = argc - program_arg_index;
+ int num_args = argc - after_flags - 1;
char *prog_args[num_args + 2];
prog_args[0] = exe_name;
for (int i = 0; i < num_args; i++)
- prog_args[i+1] = argv[program_arg_index+i];
+ prog_args[i+1] = argv[after_flags+1+i];
prog_args[num_args+1] = NULL;
execv(exe_name, prog_args);
errx(1, "Failed to run compiled program");
}
-static void build_file_dependency_graph(const char *filename, table_t *dependencies, array_t *object_files)
+void build_file_dependency_graph(const char *filename, table_t *to_compile, table_t *to_link)
{
- size_t len = strlen(filename);
- assert(strncmp(filename + len - 3, ".tm", 3) == 0);
-
- if (Table$str_get(*dependencies, filename))
+ if (Table$str_get(*to_compile, filename))
return;
- array_t *deps = new(array_t);
- Array$insert(deps, &filename, 0, $ArrayInfo(&$Text));
- Table$str_set(dependencies, filename, deps);
+ Table$str_set(to_compile, filename, filename);
+
+ size_t len = strlen(filename);
+ assert(strncmp(filename + len - 3, ".tm", 3) == 0);
file_t *f = load_file(filename);
if (!f)
@@ -241,45 +251,24 @@ static void build_file_dependency_graph(const char *filename, table_t *dependenc
use_path = Match(decl->value, Use)->raw_path;
}
if (!use_path) continue;
- const char *import, *obj_file;
+ const char *import;
if (use_path[0] == '/' || strncmp(use_path, "~/", 2) == 0 || strncmp(use_path, "./", 2) == 0 || strncmp(use_path, "../", 3) == 0) {
import = resolve_path(use_path, filename, "");
if (!import) errx(1, "Couldn't resolve path: %s", use_path);
- obj_file = heap_strf("%s.o", resolve_path(use_path, filename, ""));
+ const char *path = resolve_path(use_path, filename, "");
+ if (Table$str_get(*to_compile, path))
+ continue;
+ Table$str_set(to_compile, path, path);
} else {
import = resolve_path(use_path, filename, getenv("TOMO_IMPORT_PATH"));
- obj_file = heap_strf("-l%.*s", strlen(use_path)-3, use_path);
+ const char *lib = heap_strf("-l%.*s", strlen(use_path)-strlen(".tm"), use_path);
+ Table$str_set(to_link, lib, lib);
}
- Array$insert(deps, &import, 0, $ArrayInfo(&$Text));
- Array$insert(object_files, &obj_file, 0, $ArrayInfo(&$Text));
- build_file_dependency_graph(import, dependencies, object_files);
+ build_file_dependency_graph(import, to_compile, to_link);
}
free(file_dir);
}
-array_t get_file_dependencies(const char *filename, array_t *object_files)
-{
- const char *resolved = resolve_path(filename, ".", ".");
- if (!resolved) errx(1, "Couldn't resolve path: %s", filename);
-
- table_t file_dependencies = {};
- build_file_dependency_graph(resolved, &file_dependencies, object_files);
- table_t dependency_set = {};
-
- const TypeInfo unit = {.size=0, .align=0, .tag=CustomInfo};
- const TypeInfo info = {.size=sizeof(table_t), .align=__alignof__(table_t),
- .tag=TableInfo, .TableInfo.key=&$Text, .TableInfo.value=&unit};
-
- for (int64_t i = 1; i <= Table$length(file_dependencies); i++) {
- struct { const char *name; array_t *deps; } *entry = Table$entry(file_dependencies, i);
- for (int64_t j = 0; j < entry->deps->length; j++) {
- const char *dep = *(char**)(entry->deps->data + j*entry->deps->stride);
- Table$set(&dependency_set, &dep, &dep, &info);
- }
- }
- return dependency_set.entries;
-}
-
static bool is_stale(const char *filename, const char *relative_to)
{
struct stat target_stat;
@@ -310,8 +299,8 @@ int transpile_header(env_t *base_env, const char *filename, bool force_retranspi
CORD h_code = compile_header(module_env, ast);
- if (autofmt && autofmt[0]) {
- FILE *prog = popen(heap_strf("%s 2>/dev/null >%s", autofmt, h_filename), "w");
+ if (autofmt) {
+ FILE *prog = CORD_RUN(autofmt, " 2>/dev/null >", h_filename);
CORD_put(h_code, prog);
if (pclose(prog) == -1)
errx(1, "Failed to run autoformat program on header file: %s", autofmt);
@@ -328,7 +317,7 @@ int transpile_header(env_t *base_env, const char *filename, bool force_retranspi
printf("Transpiled to %s\n", h_filename);
if (show_codegen) {
- FILE *out = popen(heap_strf("bat -P %s", h_filename), "w");
+ FILE *out = CORD_RUN("bat -P ", h_filename);
pclose(out);
}
@@ -354,8 +343,8 @@ int transpile_code(env_t *base_env, const char *filename, bool force_retranspile
CORD c_code = compile_file(module_env, ast);
- if (autofmt && autofmt[0]) {
- FILE *prog = popen(heap_strf("%s 2>/dev/null >%s", autofmt, c_filename), "w");
+ if (autofmt) {
+ FILE *prog = CORD_RUN(autofmt, " 2>/dev/null >", c_filename);
CORD_put(c_code, prog);
if (pclose(prog) == -1)
errx(1, "Failed to output autoformatted C code to %s: %s", c_filename, autofmt);
@@ -372,7 +361,7 @@ int transpile_code(env_t *base_env, const char *filename, bool force_retranspile
printf("Transpiled to %s\n", c_filename);
if (show_codegen) {
- FILE *out = popen(heap_strf("bat -P %s", c_filename), "w");
+ FILE *out = CORD_RUN("bat -P ", c_filename);
pclose(out);
}
@@ -387,20 +376,17 @@ int compile_object_file(const char *filename, bool force_recompile)
&& !is_stale(obj_file, heap_strf("%s.h", filename))) {
return 0;
}
- const char *outfile = heap_strf("%s.o", filename);
- const char *cmd = heap_strf("%s %s -c %s.c -o %s", cc, cflags, filename, outfile);
- if (verbose)
- printf("Running: %s\n", cmd);
- FILE *prog = popen(cmd, "w");
+ CORD outfile = CORD_all(filename, ".o");
+ FILE *prog = CORD_RUN(cc, " ", cflags, " -c ", filename, ".c -o ", outfile);
int status = pclose(prog);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
if (verbose)
- printf("Compiled to %s\n", outfile);
+ CORD_printf("Compiled to %r\n", outfile);
}
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
}
-int compile_executable(env_t *base_env, const char *filename, array_t object_files)
+int compile_executable(env_t *base_env, const char *filename, CORD object_files)
{
const char *name = file_base_name(filename);
env_t *env = Table$str_get(*base_env->imports, name);
@@ -411,11 +397,7 @@ int compile_executable(env_t *base_env, const char *filename, array_t object_fil
}
const char *bin_name = heap_strn(filename, strlen(filename) - strlen(".tm"));
- const char *run = heap_strf("%s | %s %s %s %s %s %s -x c - -o %s",
- autofmt, cc, cflags, ldflags, ldlibs, objfiles, CORD_to_const_char_star(Text$join(" ", object_files)), bin_name);
- if (verbose)
- printf("%s\n", run);
- FILE *runner = popen(run, "w");
+ FILE *runner = CORD_RUN(autofmt, " | ", cc, " ", cflags, " ", ldflags, " ", ldlibs, " ", object_files, " -x c - -o ", bin_name);
CORD program = CORD_all(
"#include <tomo/tomo.h>\n"
@@ -429,7 +411,7 @@ int compile_executable(env_t *base_env, const char *filename, array_t object_fil
);
if (show_codegen) {
- FILE *out = popen(heap_strf("%s | bat -P --file-name=run.c", autofmt), "w");
+ FILE *out = CORD_RUN(autofmt, " | bat -P --file-name=run.c");
CORD_put(program, out);
pclose(out);
}