Overhaul of how libraries are installed
This commit is contained in:
parent
3c19f6387f
commit
6b5a9d65e3
39
compile.c
39
compile.c
@ -1,5 +1,6 @@
|
||||
// Compilation logic
|
||||
#include <ctype.h>
|
||||
#include <glob.h>
|
||||
#include <gc.h>
|
||||
#include <gc/cord.h>
|
||||
#include <gmp.h>
|
||||
@ -7,15 +8,16 @@
|
||||
#include <uninorm.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "stdlib/integers.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "compile.h"
|
||||
#include "cordhelpers.h"
|
||||
#include "enums.h"
|
||||
#include "structs.h"
|
||||
#include "environment.h"
|
||||
#include "typecheck.h"
|
||||
#include "stdlib/integers.h"
|
||||
#include "stdlib/patterns.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "stdlib/util.h"
|
||||
#include "structs.h"
|
||||
#include "typecheck.h"
|
||||
|
||||
typedef ast_t* (*comprehension_body_t)(ast_t*, ast_t*);
|
||||
|
||||
@ -1367,19 +1369,19 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
CORD name = file_base_name(Match(ast, Use)->path);
|
||||
env->code->variable_initializers = CORD_all(env->code->variable_initializers, name, "$$initialize();\n");
|
||||
} else if (use->what == USE_MODULE) {
|
||||
const char *libname = file_base_name(use->path);
|
||||
const char *files_filename = heap_strf("%s/lib%s.files", libname, libname);
|
||||
const char *resolved_path = resolve_path(files_filename, ast->file->filename, getenv("TOMO_IMPORT_PATH"));
|
||||
if (!resolved_path)
|
||||
code_err(ast, "No such library exists: \"lib%s.files\"", libname);
|
||||
file_t *files_f = load_file(resolved_path);
|
||||
if (!files_f) errx(1, "Couldn't open file: %s", resolved_path);
|
||||
for (int64_t i = 1; i <= files_f->num_lines; i++) {
|
||||
const char *line = get_line(files_f, i);
|
||||
line = GC_strndup(line, strcspn(line, "\r\n"));
|
||||
const char *libname = Text$as_c_string(
|
||||
Text$replace(Text$from_str(use->path), Pattern("{1+ !alphanumeric}"), Text("_"), Pattern(""), false));
|
||||
|
||||
glob_t tm_files;
|
||||
if (glob(heap_strf("~/.local/share/tomo/installed/%s/[!._0-9]*.tm", libname), GLOB_TILDE, NULL, &tm_files) != 0)
|
||||
code_err(ast, "Could not find library");
|
||||
|
||||
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
|
||||
const char *filename = tm_files.gl_pathv[i];
|
||||
env->code->variable_initializers = CORD_all(
|
||||
env->code->variable_initializers, use->path, "$", file_base_name(line), "$$initialize();\n");
|
||||
env->code->variable_initializers, use->path, "$", file_base_name(filename), "$$initialize();\n");
|
||||
}
|
||||
globfree(&tm_files);
|
||||
}
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
@ -3778,8 +3780,11 @@ CORD compile_statement_imports(env_t *env, ast_t *ast)
|
||||
case Use: {
|
||||
auto use = Match(ast, Use);
|
||||
switch (use->what) {
|
||||
case USE_MODULE:
|
||||
return CORD_all("#include <tomo/lib", use->path, ".h>\n");
|
||||
case USE_MODULE: {
|
||||
const char *libname = Text$as_c_string(
|
||||
Text$replace(Text$from_str(use->path), Pattern("{1+ !alphanumeric}"), Text("_"), Pattern(""), false));
|
||||
return CORD_all("#include <", libname, "/", libname, ".h>\n");
|
||||
}
|
||||
case USE_LOCAL:
|
||||
return CORD_all("#include \"", use->path, ".h\"\n");
|
||||
case USE_HEADER:
|
||||
|
@ -116,15 +116,11 @@ Finally, the resulting binary can be executed to actually run the program!
|
||||
|
||||
## Shared Library Imports
|
||||
|
||||
Now, what's the story with shared library imports? The equivalent process for C
|
||||
is to create a `.so` or `.dll` file. In order to build a shared library, you
|
||||
run the command `tomo -s=qux.1.2.3 file1.tm file2.tm...`. Each specified file
|
||||
will have its `.o` static object file compiled, along with its dependencies,
|
||||
and all of the resulting `.o` files will be linked together by `tomo` with a
|
||||
command like `cc <flags...> -Wl,-soname=libqux.1.2.3.so -shared file1.tm.o
|
||||
file2.tm.o dep1.tm.o ... -o libqux.1.2.3.so`. The specified files must not
|
||||
define the same public symbols as each other, since `foo` will now be treated
|
||||
as a single namespace that holds all the symbols from each of the given files.
|
||||
In Tomo, a shared library is built out of a *directory* that contains multiple
|
||||
`.tm` files. Each `.tm` file in the directory (excluding those that start with
|
||||
an underscore) will be compiled and linked together to produce a single
|
||||
`libwhatever.so` file and `whatever.h` file that can be used by other Tomo
|
||||
projects.
|
||||
|
||||
### Symbol Uniqueness
|
||||
|
||||
@ -144,16 +140,10 @@ extern void qux$1$2$3$baz$say_stuff();
|
||||
|
||||
### Installing
|
||||
|
||||
Now, the components necessary to install this shared library on your computer
|
||||
are these:
|
||||
|
||||
- The `.so` file, installed in a standard location. In our case, we will default
|
||||
to `~/.local/share/tomo/lib/libqux.so`
|
||||
- The standalone `.h` file, installed in a standard location. We default to
|
||||
`~/.local/share/tomo/include/qux.h`
|
||||
- All of the source `.tm` files (which store type information necessary for tomo
|
||||
to understand what's being imported. These will be installed to
|
||||
`~/.local/share/tomo/src/qux/`
|
||||
Once the `libwhatever.so` and `whatever.h` files have been built, Tomo will ask
|
||||
if you want to install this library. If you choose to install it, Tomo will
|
||||
copy the entire directory (excluding files and directories that begin with `.`
|
||||
such as `.git`) into `~/.local/share/tomo/installed/`.
|
||||
|
||||
### Using Shared Libraries
|
||||
|
||||
@ -162,22 +152,3 @@ name (i.e. not an absolute or relative path like `/qux` or `./qux`). When a
|
||||
program uses a shared library, that shared library gets dynamically linked to
|
||||
the executable when compiling, and all of the necessary symbol information is
|
||||
read from the source files during compilation.
|
||||
|
||||
### Library Versioning
|
||||
|
||||
In order to accommodate multiple versions of the same shared libraries on a
|
||||
system, users may specify a library version when compiling, for example: `tomo
|
||||
-s qux.1.2.3 foo.tm baz.tm` During installation, symlinks are created to map
|
||||
less specific version numbers to more specific version numbers. For example,
|
||||
when installing `qux.1.2.3`, links are created:
|
||||
|
||||
- `~/.local/share/lib/tomo/libqux.1.2.so` -> `~/.local/share/lib/tomo/libqux.1.2.3.so`
|
||||
- `~/.local/share/lib/tomo/libqux.1.so` -> `~/.local/share/lib/tomo/libqux.1.2.3.so`
|
||||
- `~/.local/share/lib/tomo/libqux.so` -> `~/.local/share/lib/tomo/libqux.1.2.3.so`
|
||||
- And so on for `include/tomo/libqux.1.2.3.h` and `src/tomo/qux.1.2.3/`
|
||||
|
||||
If there are multiple versions (e.g. `1.2.3` and `1.3.0`), then links point at
|
||||
the highest-numbered version with the necessary prefix. In this case, `qux ->
|
||||
qux.1.3.0`, `qux.1 -> qux.1.3.0`, `qux.1.3 -> qux.1.3.0`, and `qux.1.2 ->
|
||||
qux.1.2.3`.
|
||||
|
||||
|
4
parse.c
4
parse.c
@ -2368,6 +2368,10 @@ PARSER(parse_linker) {
|
||||
}
|
||||
|
||||
ast_t *parse_file(const char *path, jmp_buf *on_err) {
|
||||
const char *resolved = resolve_path(path, ".", ".");
|
||||
if (!resolved)
|
||||
errx(1, "Could not resolve path: %s", path);
|
||||
path = resolved;
|
||||
// NOTE: this cache leaks a bounded amount of memory. The cache will never
|
||||
// hold more than PARSE_CACHE_SIZE entries (see below), but each entry's
|
||||
// AST holds onto a reference to the file it came from, so they could
|
||||
|
@ -1,4 +1,4 @@
|
||||
vectors := use ../examples/vectors.tm
|
||||
vectors := use ../examples/vectors/vectors.tm
|
||||
use ./use_import.tm
|
||||
|
||||
func returns_vec()->vectors.Vec2:
|
||||
|
331
tomo.c
331
tomo.c
@ -2,6 +2,7 @@
|
||||
#include <ctype.h>
|
||||
#include <gc.h>
|
||||
#include <gc/cord.h>
|
||||
#include <glob.h>
|
||||
#include <libgen.h>
|
||||
#include <printf.h>
|
||||
#include <stdio.h>
|
||||
@ -13,6 +14,7 @@
|
||||
#include "stdlib/arrays.h"
|
||||
#include "stdlib/datatypes.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "stdlib/patterns.h"
|
||||
#include "compile.h"
|
||||
#include "cordhelpers.h"
|
||||
#include "parse.h"
|
||||
@ -32,39 +34,36 @@ static CORD autofmt, cconfig, cflags, ldlibs, ldflags, cc;
|
||||
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, CORD object_files);
|
||||
static int compile_executable(env_t *base_env, const char *filename, CORD object_files, CORD extra_ldlibs);
|
||||
static void build_file_dependency_graph(const char *filename, Table_t *to_compile, Table_t *to_link);
|
||||
static const char *escape_lib_name(const char *lib_name);
|
||||
static int build_library(const char *lib_base_name);
|
||||
static int compile_files(env_t *env, int filec, const char **filev, bool only_compile_arguments, CORD *object_files, CORD *ldlibs);
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstack-protector"
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mode_e mode = MODE_RUN;
|
||||
int after_flags = 1;
|
||||
const char *libname = NULL;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (streq(argv[i], "-t")) {
|
||||
mode = MODE_TRANSPILE;
|
||||
} else if (streq(argv[i], "-c")) {
|
||||
mode = MODE_COMPILE_OBJ;
|
||||
} else if (strncmp(argv[i], "-s=", 3) == 0) {
|
||||
} else if (streq(argv[i], "-L")) {
|
||||
mode = MODE_COMPILE_SHARED_OBJ;
|
||||
libname = argv[i] + strlen("-s=");
|
||||
} else if (streq(argv[i], "-s")) {
|
||||
mode = MODE_COMPILE_SHARED_OBJ;
|
||||
if (i+1 >= argc)
|
||||
errx(1, "You must provide at least one file to build a shared library");
|
||||
libname = file_base_name(argv[i+1]);
|
||||
} else if (streq(argv[i], "-r")) {
|
||||
mode = MODE_RUN;
|
||||
} else if (streq(argv[i], "-e")) {
|
||||
mode = MODE_COMPILE_EXE;
|
||||
} else if (streq(argv[i], "-h") || streq(argv[i], "--help")) {
|
||||
printf("Usage: %s | %s [-r] file.tm args... | %s (-t|-c|-s) file1.tm file2.tm...\n", argv[0], argv[0], argv[0]);
|
||||
printf("Usage: %s | %s [-r] file.tm args... | %s (-t|-c) file1.tm file2.tm... | %s -L [dir]\n",
|
||||
argv[0], argv[0], argv[0], argv[0]);
|
||||
return 0;
|
||||
} else if (streq(argv[i], "-u")) {
|
||||
// Uninstall libraries:
|
||||
for (int j = i + 1; j < argc; j++) {
|
||||
system(heap_strf("rm -rvf ~/.local/src/tomo/%s ~/.local/include/tomo/lib%s.h ~/.local/lib/tomo/lib%s.so",
|
||||
system(heap_strf("rm -rvf ~/.local/share/tomo/installed/%s ~/.local/share/tomo/lib/lib%s.so",
|
||||
argv[j], argv[j], argv[j]));
|
||||
}
|
||||
return 0;
|
||||
@ -80,6 +79,7 @@ int main(int argc, char *argv[])
|
||||
after_flags = i;
|
||||
break;
|
||||
}
|
||||
after_flags = i + 1;
|
||||
}
|
||||
|
||||
if (register_printf_specifier('T', printf_type, printf_pointer_size))
|
||||
@ -89,9 +89,6 @@ int main(int argc, char *argv[])
|
||||
if (register_printf_specifier('k', printf_text, printf_text_size))
|
||||
errx(1, "Couldn't set printf specifier");
|
||||
|
||||
setenv("TOMO_IMPORT_PATH", "~/.local/src/tomo:.", 0);
|
||||
setenv("TOMO_LIB_PATH", "~/.local/lib/tomo:.", 0);
|
||||
|
||||
CORD home = ENV_CORD("HOME");
|
||||
autofmt = ENV_CORD("AUTOFMT");
|
||||
if (!autofmt) autofmt = "sed '/^\\s*$/d' | indent -kr -l100 -nbbo -nut -sob";
|
||||
@ -100,7 +97,7 @@ int main(int argc, char *argv[])
|
||||
verbose = (getenv("VERBOSE") && (streq(getenv("VERBOSE"), "1") || streq(getenv("VERBOSE"), "2")));
|
||||
show_codegen = (getenv("VERBOSE") && streq(getenv("VERBOSE"), "2"));
|
||||
|
||||
if (after_flags >= argc) {
|
||||
if (after_flags >= argc && mode != MODE_COMPILE_SHARED_OBJ) {
|
||||
repl();
|
||||
return 0;
|
||||
}
|
||||
@ -116,29 +113,161 @@ int main(int argc, char *argv[])
|
||||
|
||||
cflags = ENV_CORD("CFLAGS");
|
||||
if (!cflags)
|
||||
cflags = CORD_all(cconfig, " ", optimization, " -fPIC -ggdb -I./include -I'", home, "/.local/include' -D_DEFAULT_SOURCE");
|
||||
cflags = CORD_all(cconfig, " ", optimization, " -fPIC -ggdb -D_DEFAULT_SOURCE");
|
||||
cflags = CORD_all(cflags, " -I'", home, "/.local/share/tomo/installed'");
|
||||
|
||||
ldflags = CORD_all("-Wl,-rpath='$ORIGIN',-rpath='", home, "/.local/lib/tomo' -L. -L'", home, "/.local/lib/tomo'");
|
||||
ldflags = CORD_all("-Wl,-rpath='$ORIGIN',-rpath='", home, "/.local/share/tomo/lib' -L. -L'", home, "/.local/share/tomo/lib'");
|
||||
|
||||
ldlibs = "-lgc -lgmp -lm -ltomo";
|
||||
|
||||
cc = ENV_CORD("CC");
|
||||
if (!cc) cc = "cc";
|
||||
|
||||
CORD compilation_library_name = CORD_EMPTY;
|
||||
env_t *env = new_compilation_unit(&compilation_library_name);
|
||||
Table_t dependency_files = {};
|
||||
if (mode == MODE_COMPILE_EXE || mode == MODE_RUN) {
|
||||
const char *filename = argv[after_flags];
|
||||
|
||||
env_t *env = new_compilation_unit(NULL);
|
||||
CORD object_files, extra_ldlibs;
|
||||
compile_files(env, 1, &filename, false, &object_files, &extra_ldlibs);
|
||||
int status = compile_executable(env, filename, object_files, extra_ldlibs);
|
||||
if (status != 0 || mode == MODE_COMPILE_EXE)
|
||||
return status;
|
||||
|
||||
char *exe_name = GC_strndup(filename, strlen(filename) - strlen(".tm"));
|
||||
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[after_flags + i];
|
||||
prog_args[num_args+1] = NULL;
|
||||
execv(exe_name, prog_args);
|
||||
errx(1, "Failed to run compiled program");
|
||||
} else if (mode == MODE_COMPILE_OBJ) {
|
||||
env_t *env = new_compilation_unit(NULL);
|
||||
return compile_files(env, argc - after_flags, (const char**)&argv[after_flags], true, NULL, NULL);
|
||||
} else if (mode == MODE_COMPILE_SHARED_OBJ) {
|
||||
char *cwd = get_current_dir_name();
|
||||
for (int i = after_flags; i < argc; i++) {
|
||||
if (chdir(argv[i]) != 0)
|
||||
errx(1, "Could not enter directory: %s", argv[i]);
|
||||
char *libdir = get_current_dir_name();
|
||||
char *libdirname = basename(libdir);
|
||||
int status = build_library(libdirname);
|
||||
if (status != 0)
|
||||
return status;
|
||||
free(libdir);
|
||||
chdir(cwd);
|
||||
}
|
||||
free(cwd);
|
||||
}
|
||||
}
|
||||
|
||||
const char *escape_lib_name(const char *lib_name)
|
||||
{
|
||||
return Text$as_c_string(
|
||||
Text$replace(Text$from_str(lib_name), Pattern("{1+ !alphanumeric}"), Text("_"), Pattern(""), false));
|
||||
}
|
||||
|
||||
int build_library(const char *lib_base_name)
|
||||
{
|
||||
glob_t tm_files;
|
||||
if (glob("[!._0-9]*.tm", 0, NULL, &tm_files) != 0)
|
||||
errx(1, "Couldn't get .tm files in directory: %s", get_current_dir_name());
|
||||
env_t *env = new_compilation_unit(NULL);
|
||||
CORD object_files, extra_ldlibs;
|
||||
compile_files(env, (int)tm_files.gl_pathc, (const char**)tm_files.gl_pathv, false, &object_files, &extra_ldlibs);
|
||||
|
||||
// Library name replaces all stretchs of non-alphanumeric chars with an underscore
|
||||
// So e.g. https://github.com/foo/baz --> https_github_com_foo_baz
|
||||
const char *libname = escape_lib_name(lib_base_name);
|
||||
env->libname = &libname;
|
||||
|
||||
// Build a "whatever.h" header that loads all the headers:
|
||||
FILE *header_prog = CORD_RUN(autofmt ? autofmt : "cat", " 2>/dev/null >", libname, ".h");
|
||||
fputs("#pragma once\n", header_prog);
|
||||
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
|
||||
const char *filename = tm_files.gl_pathv[i];
|
||||
ast_t *ast = parse_file(filename, NULL);
|
||||
if (!ast) errx(1, "Could not parse file %s", filename);
|
||||
env_t *module_env = load_module_env(env, ast);
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
CORD h = compile_statement_typedefs(module_env, stmt->ast);
|
||||
if (h) CORD_put(h, header_prog);
|
||||
}
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
CORD h = compile_statement_definitions(module_env, stmt->ast);
|
||||
if (h) CORD_put(h, header_prog);
|
||||
}
|
||||
fprintf(header_prog, "void %s$%s$$initialize(void);\n", libname, file_base_name(filename));
|
||||
}
|
||||
if (pclose(header_prog) == -1)
|
||||
errx(1, "Failed to run autoformat program on header file: %s", autofmt);
|
||||
|
||||
// Build up a list of symbol renamings:
|
||||
unlink("symbol_renames.txt");
|
||||
FILE *prog;
|
||||
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
|
||||
const char *filename = tm_files.gl_pathv[i];
|
||||
prog = CORD_RUN("nm -U -fjust-symbols ", filename, ".o | sed 's/.*/\\0 ", libname, "$\\0/' >>symbol_renames.txt");
|
||||
int status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to create symbol rename table with `nm` and `sed`");
|
||||
}
|
||||
|
||||
globfree(&tm_files);
|
||||
|
||||
prog = CORD_RUN(cc, " ", cflags, " ", ldflags, " ", ldlibs, " ", extra_ldlibs, " -Wl,-soname=lib", libname, ".so -shared ", object_files, " -o lib", libname, ".so");
|
||||
int status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to compile shared library file");
|
||||
if (verbose)
|
||||
CORD_printf("Compiled to lib%s.so\n", libname);
|
||||
|
||||
prog = CORD_RUN("objcopy --redefine-syms=symbol_renames.txt lib", libname, ".so");
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to run `objcopy` to add library prefix to symbols");
|
||||
|
||||
prog = CORD_RUN("patchelf --rename-dynamic-symbols symbol_renames.txt lib", libname, ".so");
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to run `patchelf` to rename dynamic symbols with library prefix");
|
||||
|
||||
if (verbose)
|
||||
CORD_printf("Successfully renamed symbols with library prefix!\n");
|
||||
|
||||
unlink("symbol_renames.txt");
|
||||
|
||||
printf("Do you want to install %s? [Y/n] ", libname);
|
||||
fflush(stdout);
|
||||
switch (getchar()) {
|
||||
case 'y': case 'Y':
|
||||
getchar();
|
||||
// Fall through
|
||||
case '\n': {
|
||||
system(heap_strf("rm -rvf ~/.local/share/tomo/installed/'%s'", lib_base_name));
|
||||
system(heap_strf("mkdir -p ~/.local/share/tomo/installed/'%s'", lib_base_name));
|
||||
system(heap_strf("cp -rv * ~/.local/share/tomo/installed/'%s'", lib_base_name));
|
||||
system("mkdir -p ~/.local/share/tomo/lib/");
|
||||
system(heap_strf("ln -fv -s ../installed/'%s'/lib%s.so ~/.local/share/tomo/lib/lib%s.so", lib_base_name, libname, libname));
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compile_files(env_t *env, int filec, const char **filev, bool only_compile_arguments, CORD *object_files, CORD *extra_ldlibs)
|
||||
{
|
||||
Table_t to_link = {};
|
||||
Table_t argument_files = {};
|
||||
|
||||
for (int i = after_flags; i < argc; i++) {
|
||||
if (strlen(argv[i]) < 4 || strncmp(argv[i] + strlen(argv[i]) - 3, ".tm", 3) != 0)
|
||||
errx(1, "Not a valid .tm file: \x1b[31;1m%s\x1b[m", argv[i]);
|
||||
const char *resolved = resolve_path(argv[i], ".", ".");
|
||||
if (!resolved) errx(1, "Couldn't resolve path: %s", argv[i]);
|
||||
Table$str_set(&argument_files, resolved, argv[i]);
|
||||
Table_t dependency_files = {};
|
||||
for (int i = 0; i < filec; i++) {
|
||||
if (strlen(filev[i]) < 4 || strncmp(filev[i] + strlen(filev[i]) - 3, ".tm", 3) != 0)
|
||||
errx(1, "Not a valid .tm file: \x1b[31;1m%s\x1b[m", filev[i]);
|
||||
const char *resolved = resolve_path(filev[i], ".", ".");
|
||||
if (!resolved) errx(1, "Couldn't resolve path: %s", filev[i]);
|
||||
Table$str_set(&argument_files, resolved, filev[i]);
|
||||
build_file_dependency_graph(resolved, &dependency_files, &to_link);
|
||||
if (mode == MODE_RUN) break;
|
||||
}
|
||||
|
||||
int status;
|
||||
@ -163,7 +292,7 @@ int main(int argc, char *argv[])
|
||||
for (int64_t i = 0; i < dependency_files.entries.length; i++) {
|
||||
const char *filename = *(char**)(dependency_files.entries.data + i*dependency_files.entries.stride);
|
||||
bool is_argument_file = (Table$str_get(argument_files, filename) != NULL);
|
||||
if (mode == MODE_COMPILE_OBJ && !is_argument_file)
|
||||
if (!is_argument_file && only_compile_arguments)
|
||||
continue;
|
||||
|
||||
pid_t pid = fork();
|
||||
@ -183,123 +312,21 @@ int main(int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (mode == MODE_COMPILE_OBJ)
|
||||
return 0;
|
||||
|
||||
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 ");
|
||||
if (object_files) {
|
||||
*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) {
|
||||
char *libname_id = GC_strdup(libname);
|
||||
for (char *p = libname_id; *p; p++) {
|
||||
if (!isalnum(*p) && *p != '_' && *p != '$')
|
||||
*p = '_';
|
||||
}
|
||||
compilation_library_name = libname_id;
|
||||
|
||||
// Build a "libwhatever.h" header that loads all the headers:
|
||||
const char *h_filename = heap_strf("lib%s.h", libname);
|
||||
FILE *header_prog = CORD_RUN(autofmt ? autofmt : "cat", " 2>/dev/null >", h_filename);
|
||||
fputs("#pragma once\n", header_prog);
|
||||
for (int i = after_flags; i < argc; i++) {
|
||||
const char *filename = argv[i];
|
||||
ast_t *ast = parse_file(filename, NULL);
|
||||
if (!ast) errx(1, "Could not parse file %s", filename);
|
||||
env_t *module_env = load_module_env(env, ast);
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
CORD h = compile_statement_typedefs(module_env, stmt->ast);
|
||||
if (h) CORD_put(h, header_prog);
|
||||
}
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
CORD h = compile_statement_definitions(module_env, stmt->ast);
|
||||
if (h) CORD_put(h, header_prog);
|
||||
}
|
||||
fprintf(header_prog, "void %s$%s$$initialize(void);\n", libname, file_base_name(filename));
|
||||
}
|
||||
if (pclose(header_prog) == -1)
|
||||
errx(1, "Failed to run autoformat program on header file: %s", autofmt);
|
||||
|
||||
// Also output a "libwhatever.files" file that lists the .tm files it used:
|
||||
const char *files_filename = heap_strf("lib%s.files", libname);
|
||||
FILE *files_file = fopen(files_filename, "w");
|
||||
if (!files_file)
|
||||
errx(1, "Couldn't open file: %s", files_filename);
|
||||
for (int i = after_flags; i < argc; i++) {
|
||||
fprintf(files_file, "%s\n", argv[i]);
|
||||
}
|
||||
if (fclose(files_file))
|
||||
errx(1, "Failed to close file: %s", files_filename);
|
||||
|
||||
// Build up a list of symbol renamings:
|
||||
unlink("symbol_renames.txt");
|
||||
FILE *prog;
|
||||
for (int i = after_flags; i < argc; i++) {
|
||||
prog = CORD_RUN("nm -U -fjust-symbols ", argv[i], ".o | sed 's/.*/\\0 ", libname_id, "$\\0/' >>symbol_renames.txt");
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to create symbol rename table with `nm` and `sed`");
|
||||
}
|
||||
|
||||
CORD outfile = CORD_all("lib", libname, ".so");
|
||||
prog = CORD_RUN(cc, " ", cflags, " ", ldflags, " ", ldlibs, " -Wl,-soname=", outfile, " -shared ", object_files, " -o ", outfile);
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to compile shared library file");
|
||||
if (verbose)
|
||||
CORD_printf("Compiled to %r\n", outfile);
|
||||
|
||||
prog = CORD_RUN("objcopy --redefine-syms=symbol_renames.txt lib", libname, ".so");
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to run `objcopy` to add library prefix to symbols");
|
||||
|
||||
prog = CORD_RUN("patchelf --rename-dynamic-symbols symbol_renames.txt lib", libname, ".so");
|
||||
status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
errx(WEXITSTATUS(status), "Failed to run `patchelf` to rename dynamic symbols with library prefix");
|
||||
|
||||
if (verbose)
|
||||
CORD_printf("Successfully renamed symbols with library prefix!\n");
|
||||
|
||||
unlink("symbol_renames.txt");
|
||||
|
||||
printf("Do you want to install your library? [Y/n] ");
|
||||
fflush(stdout);
|
||||
switch (getchar()) {
|
||||
case 'y': case 'Y': case '\n': {
|
||||
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;
|
||||
} else {
|
||||
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;
|
||||
|
||||
char *exe_name = GC_strndup(filename, strlen(filename) - strlen(".tm"));
|
||||
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[after_flags+1+i];
|
||||
prog_args[num_args+1] = NULL;
|
||||
execv(exe_name, prog_args);
|
||||
|
||||
errx(1, "Failed to run compiled program");
|
||||
if (extra_ldlibs) {
|
||||
*extra_ldlibs = CORD_EMPTY;
|
||||
for (int64_t i = 0; i < to_link.entries.length; i++) {
|
||||
const char *lib = *(char**)(to_link.entries.data + i*to_link.entries.stride);
|
||||
*extra_ldlibs = CORD_all(*extra_ldlibs, " ", lib);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void build_file_dependency_graph(const char *filename, Table_t *to_compile, Table_t *to_link)
|
||||
@ -338,11 +365,9 @@ void build_file_dependency_graph(const char *filename, Table_t *to_compile, Tabl
|
||||
break;
|
||||
}
|
||||
case USE_MODULE: {
|
||||
const char *base_name = file_base_name(use->path);
|
||||
const char *lib_path = heap_strf("%s/lib%s.so", base_name, base_name);
|
||||
const char *libfile = resolve_path(lib_path, filename, getenv("TOMO_IMPORT_PATH"));
|
||||
if (!libfile) errx(1, "Couldn't resolve path: %s", lib_path);
|
||||
const char *lib = heap_strf("-l%s", use->path);
|
||||
const char *lib_name = escape_lib_name(use->path);
|
||||
// const char *lib = heap_strf("-l:'lib%s.so'", lib_name);
|
||||
const char *lib = heap_strf("'%s/.local/share/tomo/installed/%s/lib%s.so'", getenv("HOME"), lib_name, lib_name);
|
||||
Table$str_set(to_link, lib, lib);
|
||||
break;
|
||||
}
|
||||
@ -360,10 +385,10 @@ void build_file_dependency_graph(const char *filename, Table_t *to_compile, Tabl
|
||||
static bool is_stale(const char *filename, const char *relative_to)
|
||||
{
|
||||
struct stat target_stat;
|
||||
if (stat(filename, &target_stat))
|
||||
if (stat(filename, &target_stat) != 0)
|
||||
return true;
|
||||
struct stat relative_to_stat;
|
||||
if (stat(relative_to, &relative_to_stat))
|
||||
if (stat(relative_to, &relative_to_stat) != 0)
|
||||
errx(1, "File doesn't exist: %s", relative_to);
|
||||
return target_stat.st_mtime < relative_to_stat.st_mtime;
|
||||
}
|
||||
@ -488,7 +513,7 @@ int compile_object_file(const char *filename, bool force_recompile)
|
||||
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int compile_executable(env_t *base_env, const char *filename, CORD object_files)
|
||||
int compile_executable(env_t *base_env, const char *filename, CORD object_files, CORD extra_ldlibs)
|
||||
{
|
||||
ast_t *ast = parse_file(filename, NULL);
|
||||
if (!ast)
|
||||
@ -499,7 +524,7 @@ int compile_executable(env_t *base_env, const char *filename, CORD object_files)
|
||||
errx(1, "No main() function has been defined for %s, so it can't be run!", filename);
|
||||
|
||||
const char *bin_name = GC_strndup(filename, strlen(filename) - strlen(".tm"));
|
||||
FILE *runner = CORD_RUN(cc, " ", cflags, " ", ldflags, " ", ldlibs, " ", object_files, " -x c - -o ", bin_name);
|
||||
FILE *runner = CORD_RUN(cc, " ", cflags, " ", ldflags, " ", ldlibs, " ", extra_ldlibs, " ", object_files, " -x c - -o ", bin_name);
|
||||
|
||||
CORD program = CORD_all(
|
||||
"extern int ", main_binding->code, "$parse_and_run(int argc, char *argv[]);\n"
|
||||
|
35
typecheck.c
35
typecheck.c
@ -1,6 +1,7 @@
|
||||
// Logic for getting a type from an AST node
|
||||
#include <ctype.h>
|
||||
#include <gc.h>
|
||||
#include <glob.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
@ -8,11 +9,12 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "stdlib/util.h"
|
||||
#include "cordhelpers.h"
|
||||
#include "environment.h"
|
||||
#include "parse.h"
|
||||
#include "stdlib/patterns.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "stdlib/util.h"
|
||||
#include "typecheck.h"
|
||||
#include "types.h"
|
||||
|
||||
@ -151,13 +153,12 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
|
||||
return load_module_env(env, ast);
|
||||
}
|
||||
case USE_MODULE: {
|
||||
const char *libname = file_base_name(use->path);
|
||||
const char *files_filename = heap_strf("%s/lib%s.files", libname, libname);
|
||||
const char *resolved_path = resolve_path(files_filename, module_ast->file->filename, getenv("TOMO_IMPORT_PATH"));
|
||||
if (!resolved_path)
|
||||
code_err(module_ast, "No such library exists: \"lib%s.files\"", libname);
|
||||
file_t *files_f = load_file(resolved_path);
|
||||
if (!files_f) errx(1, "Couldn't open file: %s", resolved_path);
|
||||
const char *libname = Text$as_c_string(
|
||||
Text$replace(Text$from_str(use->path), Pattern("{1+ !alphanumeric}"), Text("_"), Pattern(""), false));
|
||||
|
||||
glob_t tm_files;
|
||||
if (glob(heap_strf("~/.local/share/tomo/installed/%s/[!._0-9]*.tm", libname), GLOB_TILDE, NULL, &tm_files) != 0)
|
||||
code_err(module_ast, "Could not find library");
|
||||
|
||||
env_t *module_env = fresh_scope(env);
|
||||
Table$str_set(env->imports, use->path, module_env);
|
||||
@ -168,17 +169,12 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
|
||||
}
|
||||
module_env->libname = new(CORD);
|
||||
*module_env->libname = (CORD)libname_id;
|
||||
for (int64_t i = 1; i <= files_f->num_lines; i++) {
|
||||
const char *line = get_line(files_f, i);
|
||||
line = GC_strndup(line, strcspn(line, "\r\n"));
|
||||
if (!line || line[0] == '\0') continue;
|
||||
const char *tm_path = resolve_path(line, resolved_path, ".");
|
||||
if (!tm_path) errx(1, "Couldn't find library %s dependency: %s", libname, line);
|
||||
|
||||
ast_t *ast = parse_file(tm_path, NULL);
|
||||
if (!ast) errx(1, "Could not compile file %s", tm_path);
|
||||
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
|
||||
const char *filename = tm_files.gl_pathv[i];
|
||||
ast_t *ast = parse_file(filename, NULL);
|
||||
if (!ast) errx(1, "Could not compile file %s", filename);
|
||||
env_t *module_file_env = fresh_scope(module_env);
|
||||
char *file_prefix = GC_strdup(file_base_name(line));
|
||||
char *file_prefix = GC_strdup(file_base_name(filename));
|
||||
for (char *p = file_prefix; *p; p++) {
|
||||
if (!isalnum(*p) && *p != '_' && *p != '$')
|
||||
*p = '_';
|
||||
@ -192,6 +188,7 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
|
||||
set_binding(module_env, entry->name, entry->binding);
|
||||
}
|
||||
}
|
||||
globfree(&tm_files);
|
||||
return module_env;
|
||||
}
|
||||
case USE_SHARED_OBJECT: return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user