From a2d6fd62f811e400db72818acbc9b2badb1d208f Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Mon, 6 Mar 2023 18:33:21 -0800 Subject: [PATCH] lots of little improvements --- .gitignore | 1 + etc/dist.sh | 11 +++++++ src/build.c | 1 + src/build_syslibs.c | 75 ++++++++++++++++++++++++++++++++++++--------- src/cc.c | 31 ++++++++----------- src/colib.h | 11 +++++++ src/compiler.c | 60 ++++++++++++++++++++++++++++-------- src/compiler.h | 1 + src/err.c | 14 ++++++--- src/fs.c | 62 +++++++++++++++++++++++++++++++++++-- src/llvm/llvm.cc | 10 ------ src/llvm/llvm.h | 3 -- src/main.c | 45 +++++++++++++++++---------- src/path.c | 13 ++++++-- src/path.h | 12 ++++++-- src/sys_homedir.c | 48 +++++++++++++++++++++++++++++ 16 files changed, 311 insertions(+), 87 deletions(-) create mode 100644 src/sys_homedir.c diff --git a/.gitignore b/.gitignore index e8eec057..3c290baf 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ a.out /build /etc/bitset /lib/sysinc +/lib/*.sysroot diff --git a/etc/dist.sh b/etc/dist.sh index 376267c0..8a0279c8 100755 --- a/etc/dist.sh +++ b/etc/dist.sh @@ -31,6 +31,17 @@ for f in lib/*; do _copy $f $DESTDIR/$(basename $f) done +# create symlinks +for name in \ + cc \ + ld.lld \ + ld64.lld \ + wasm-ld \ + lld-link \ +;do + _symlink "$DESTDIR/$name" compis +done + echo "creating $DESTDIR.tar.xz" _create_tar_xz_from_dir $DESTDIR $DESTDIR.tar.xz diff --git a/src/build.c b/src/build.c index 563daeb2..5f8d5846 100644 --- a/src/build.c +++ b/src/build.c @@ -107,6 +107,7 @@ int main_build(int argc, char* argv[]) { if (optind < 0) return 1; + coverbose |= opt_verbose; #if DEBUG // --co-trace turns on all trace flags opt_trace_parse |= opt_trace_all; diff --git a/src/build_syslibs.c b/src/build_syslibs.c index 76fbc589..743deb3c 100644 --- a/src/build_syslibs.c +++ b/src/build_syslibs.c @@ -314,33 +314,78 @@ static err_t build_librt(compiler_t* c) { } +static err_t create_exe_symlinks(compiler_t* c) { + usize dstpathcap = strlen(coexefile) + 16; + char* dstpath = alloca(dstpathcap); + usize dstpathlen = path_dir(dstpath, dstpathcap, coexefile); + const char* coexename = path_base(coexefile); + int r; + + #define CREATE_EXE_SYMLINK(name) { \ + memcpy(&dstpath[dstpathlen], \ + PATH_SEPARATOR_STR name, strlen(PATH_SEPARATOR_STR name) + 1); \ + r = symlink(coexename, dstpath); \ + if (r != 0 && errno != EEXIST) \ + return err_errno(); \ + if (r == 0) dlog("symlink %s -> %s", dstpath, coexename); \ + } + + CREATE_EXE_SYMLINK("cc"); + CREATE_EXE_SYMLINK("ld.lld"); + CREATE_EXE_SYMLINK("ld64.lld"); + CREATE_EXE_SYMLINK("wasm-ld"); + CREATE_EXE_SYMLINK("lld-link"); + + #undef CREATE_EXE_SYMLINK + return 0; +} + + err_t build_syslibs_if_needed(compiler_t* c) { + err_t err = 0; + + if (must_build_libc(c) || must_build_librt(c)) { + lockfile_t lockfile; + char* lockfile_path = path_join_alloca(c->sysroot, "syslibs-build.lock"); + long lockee_pid; + if (( err = lockfile_trylock(&lockfile, lockfile_path, &lockee_pid) )) { + if (err != ErrExists) + return err; + log("waiting for compis (pid %ld) to finish...", lockee_pid); + if (( err = lockfile_lock(&lockfile, lockfile_path) )) + return err; + } + // note: must check again in case another process won and build the libs + if (!err && must_build_libc(c)) err = build_libc(c); + if (!err && must_build_librt(c)) err = build_librt(c); + lockfile_unlock(&lockfile); + } + + if (!err) + err = create_exe_symlinks(c); + + return err; +} + + +/*err_t build_syslibs_if_needed(compiler_t* c) { if (!must_build_libc(c) && !must_build_librt(c)) return 0; - err_t err; + err_t err = 0; lockfile_t lockfile; char* lockfile_path = path_join_alloca(c->sysroot, "syslibs-build.lock"); - long lockee_pid; - err = lockfile_trylock(&lockfile, lockfile_path, &lockee_pid); - if (err) { + if (( err = lockfile_trylock(&lockfile, lockfile_path, &lockee_pid) )) { if (err != ErrExists) return err; log("waiting for compis (pid %ld) to finish...", lockee_pid); - err = lockfile_lock(&lockfile, lockfile_path); - if (err) + if (( err = lockfile_lock(&lockfile, lockfile_path) )) return err; } - // note: must check again in case another process won and build the libs - - if (must_build_libc(c) && (err = build_libc(c))) - goto end; - if (must_build_librt(c) && (err = build_librt(c))) - goto end; - -end: + if (!err && must_build_libc(c)) err = build_libc(c); + if (!err && must_build_librt(c)) err = build_librt(c); lockfile_unlock(&lockfile); return err; -} +}*/ diff --git a/src/cc.c b/src/cc.c index dd4dea3a..90349462 100644 --- a/src/cc.c +++ b/src/cc.c @@ -7,8 +7,6 @@ #include "llvm/llvm.h" #include "clang/Basic/Version.inc" // CLANG_VERSION_STRING -#include // symlink - static void diaghandler(const diag_t* d, void* nullable userdata) { // unused @@ -35,15 +33,22 @@ int cc_main(int user_argc, char* user_argv[]) { const char* arg = user_argv[i]; if (streq(arg, "-nostdlib") || streq(arg, "-nolibc")) { nostdlib = true; - } if (streq(arg, "-nostdinc") || + } else if (streq(arg, "-nostdinc") || streq(arg, "--no-standard-includes") || streq(arg, "-nostdlibinc")) { nostdinc = true; - } if (streq(arg, "-ffreestanding")) { + } else if (streq(arg, "-ffreestanding")) { freestanding = true; - } if (streq(arg, "-c") || streq(arg, "-S") || streq(arg, "-E")) { + } else if (streq(arg, "-c") || streq(arg, "-S") || streq(arg, "-E")) { nolink = true; + } else if (streq(arg, "-v") || streq(arg, "--verbose")) { + c.opt_verbose = true; + coverbose = true; + } else if (streq(arg, "-O0")) { + c.buildmode = BUILDMODE_DEBUG; + } else if (str_startswith(arg, "-O")) { + c.buildmode = BUILDMODE_OPT; } else if (streq(arg, "-target")) { panic("TODO -target"); } else if (str_startswith(arg, "--target=")) { @@ -90,20 +95,8 @@ int cc_main(int user_argc, char* user_argv[]) { // linker flags if (!nolink) { - // create lld symlink - // TODO: move to build_syslibs - const char* ldexefile; - switch ((enum target_sys)target->sys) { - case SYS_linux: ldexefile = "ld.lld"; break; - case SYS_macos: ldexefile = "ld64.lld"; break; - // case SYS_wasm: case SYS_wasi: ldexefile = "wasm-ld"; break; - // case SYS_win32: ldexefile = "lld-link"; break; - case SYS_COUNT: case SYS_none: safefail("bad sys"); break; - } - ldexefile = path_join_alloca(c.sysroot, ldexefile); - symlink(coexefile, ldexefile); - strlist_addf(&args, "-fuse-ld=%s", ldexefile); - + char* bindir = path_dir_alloca(coexefile); + strlist_addf(&args, "-fuse-ld=%s/%s", bindir, c.ldname); if (target->sys == SYS_macos && !nolink) { char macosver[16]; if (streq(target->sysver, "10")) { diff --git a/src/colib.h b/src/colib.h index d0bd81d0..f4d38fce 100644 --- a/src/colib.h +++ b/src/colib.h @@ -635,6 +635,9 @@ enum err_ { ErrNoMem = -12, // cannot allocate memory ErrMFault = -13, // bad memory address ErrOverflow = -14, // value too large + ErrReadOnly = -15, // read-only + ErrIO = -16, // I/O error + ErrNotDir = -17, // not a directory }; EXTERN_C err_t err_errno(); // current errno value @@ -1009,6 +1012,7 @@ u64 microsleep(u64 microseconds); //————————————————————————————————————————————————————————————————————————————————————— // system info u32 sys_ncpu(); // number of available logical CPUs, logs error on failure and returns 1 +const char* sys_homedir(); //————————————————————————————————————————————————————————————————————————————————————— // files @@ -1055,6 +1059,13 @@ extern const char* coexefile; // Can be overridden with env var COROOT extern const char* coroot; +// cocachedir: directory of compis cache (~/.cache/compis) +// Can be overridden with env var COCACHE +extern const char* cocachedir; + +// coverbose: set to true if -v was passed on the command line +extern bool coverbose; + // comaxproc: max thread concurrency (defaults to sys_ncpu, can be set with -j) extern u32 comaxproc; diff --git a/src/compiler.c b/src/compiler.c index 73f3277b..4cfbae4c 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -8,10 +8,13 @@ #include "llvm/llvm.h" #include "clang/Basic/Version.inc" // CLANG_VERSION_STRING +#include +#include +#include #include #include -#include -#include + +#include // XXX static void set_cstr(memalloc_t ma, char*nullable* dst, slice_t src) { @@ -94,6 +97,15 @@ static void configure_target(compiler_t* c) { c->inttype = type_i64; } set_secondary_pointer_types(c); + + // set ldname + switch ((enum target_sys)c->target.sys) { + case SYS_linux: c->ldname = "ld.lld"; break; + case SYS_macos: c->ldname = "ld64.lld"; break; + // case SYS_wasm: case SYS_wasi: c->ldname = "wasm-ld"; break; + // case SYS_win32: c->ldname = "lld-link"; break; + case SYS_COUNT: case SYS_none: safefail("sys"); break; + } } @@ -106,10 +118,38 @@ static const char* buildmode_name(buildmode_t m) { } -static err_t configure_buildroot(compiler_t* c, const char* buildroot) { +static err_t configure_sysroot(compiler_t* c) { + // sysroot = {cocachedir}/{target}[-debug] + + char target[80]; + usize targetlen = target_fmt(&c->target, target, sizeof(target)); + if (c->buildmode == BUILDMODE_DEBUG) { + usize n = strlen("-debug"); + memcpy(&target[targetlen], "-debug", n + 1); + targetlen += n; + } + + err_t err; + char sysroot[PATH_MAX]; + int n = snprintf(sysroot, sizeof(sysroot), + "%s%c%s", cocachedir, PATH_SEPARATOR, target); + safecheck(n > 0 && (usize)n < sizeof(sysroot)); + if (( err = fs_mkdirs(sysroot, 0755) )) + return err; + + if (c->opt_verbose) + log("using sysroot '%s'", sysroot); + + mem_freecstr(c->ma, c->sysroot); + if (( c->sysroot = mem_strdup(c->ma, slice_cstr(sysroot), 0) ) == NULL) + return ErrNoMem; + return 0; +} + + +static err_t configure_builddir(compiler_t* c, const char* buildroot) { // builddir = {buildroot}/{mode}-{target} // pkgbuilddir = {builddir}/{pkgname}.pkg - // sysroot = {builddir}/sysroot if (!( c->buildroot = path_abs(c->ma, buildroot) )) return ErrNoMem; @@ -158,12 +198,6 @@ static err_t configure_buildroot(compiler_t* c, const char* buildroot) { APPEND(suffix); *p = 0; - // sysroot - mem_freecstr(c->ma, c->sysroot); - c->sysroot = path_join_m(c->ma, c->builddir, "sysroot"); - if (!c->sysroot) - return ErrNoMem; - return 0; #undef APPEND @@ -247,9 +281,9 @@ static err_t configure_cflags(compiler_t* c) { err_t compiler_configure(compiler_t* c, const target_t* target, const char* buildroot) { c->target = *target; configure_target(c); - err_t err = configure_buildroot(c, buildroot); - if (!err) - err = configure_cflags(c); + err_t err = configure_builddir(c, buildroot); + if (!err) err = configure_sysroot(c); + if (!err) err = configure_cflags(c); return err; } diff --git a/src/compiler.h b/src/compiler.h index 45350617..5a4a8a54 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -540,6 +540,7 @@ typedef struct compiler { slice_t sflags_common; // flags used for all assembly objects (slice of cflags) slice_t cflags_common; // cflags used for all objects (slice of cflags) slice_t cflags_sysinc; // cflags with -isystemPATH for current target + const char* ldname; // name of linker for target, relative to sysroot // diagnostics diaghandler_t diaghandler; // called when errors are encountered diff --git a/src/err.c b/src/err.c index 8bb26459..10524165 100644 --- a/src/err.c +++ b/src/err.c @@ -20,6 +20,9 @@ const char* err_str(err_t e) { case ErrNoMem: return "cannot allocate memory"; case ErrMFault: return "bad memory address"; case ErrOverflow: return "value too large"; + case ErrReadOnly: return "read-only"; + case ErrIO: return "I/O error"; + case ErrNotDir: return "not a directory"; } return "(unknown error)"; } @@ -28,10 +31,13 @@ const char* err_str(err_t e) { err_t err_errnox(int e) { switch (e) { case 0: return 0; - case EACCES: return ErrAccess; - case EEXIST: return ErrExists; - case ENOENT: return ErrNotFound; - case EBADF: return ErrBadfd; + case EACCES: return ErrAccess; + case EEXIST: return ErrExists; + case ENOENT: return ErrNotFound; + case EBADF: return ErrBadfd; + case EROFS: return ErrReadOnly; + case EIO: return ErrIO; + case ENOTDIR: return ErrNotDir; case ENOTSUP: case ENOSYS: return ErrNotSupported; diff --git a/src/fs.c b/src/fs.c index e268a2f9..c9a4c33b 100644 --- a/src/fs.c +++ b/src/fs.c @@ -67,8 +67,66 @@ err_t writefile(const char* filename, u32 mode, slice_t data) { err_t fs_mkdirs(const char* path, int perms) { - dlog("mkdirs %s", relpath(path)); - return err_errnox(LLVMCreateDirectories(path, strlen(path), perms)); + // copy path into mutable storage + usize len = strlen(path); + if (len == 0) return ErrInvalid; + char* buf = alloca(len + 1); + memcpy(buf, path, len + 1); + + char* s = buf + len; + char* end; + struct stat st; + + // trim away trailing separators, e.g. "/a/b//" => "/a/b" + while (*--s == PATH_SEPARATOR) { + if (s == buf) + return 0; // path is "/" + } + if (s == buf && *buf == '.') + return 0; // path is "." + s[1] = 0; + end = s; + + // stat from leaf to root, e.g "/a/b/c", "/a/b", "/a" + for (;;) { + if (stat(buf, &st) == 0) { + if (!S_ISDIR(st.st_mode)) + return ErrNotDir; + break; + } + if (errno != ENOENT) + goto err; + + // skip past the last component + while (--s > buf) { + if (*s == PATH_SEPARATOR) { + // skip past any trailing separators + while (*--s == PATH_SEPARATOR) {}; + break; + } + } + if (s == buf) + break; + // replace path separator with null terminator + s[1] = 0; + } + + if (s < end && coverbose) + log("creating directory '%s'", relpath(path)); + + // mkdir starting with the first non-existant dir, e.g "/a", "/a/b", "/a/b/c" + while (s < end) { + if (mkdir(buf, perms) < 0 && errno != EEXIST) { + dlog("mkdir %s: %s", buf, err_str(err_errno())); + goto err; + } + while (++s < end && *s) {} + *s = PATH_SEPARATOR; + } + + return 0; +err: + return err_errno(); } diff --git a/src/llvm/llvm.cc b/src/llvm/llvm.cc index cc3853a0..33ffa1c8 100644 --- a/src/llvm/llvm.cc +++ b/src/llvm/llvm.cc @@ -483,13 +483,3 @@ char* LLVMGetMainExecutable(const char* argv0) { void* P = (void*)(intptr_t)LLVMGetMainExecutable; return strdup(sys::fs::getMainExecutable(argv0, P).c_str()); } - - -int LLVMCreateDirectories(const char* path, size_t pathlen, int perms) { - bool IgnoreExisting = true; - sys::fs::perms perms2 = sys::fs::perms(perms); - std::string path2(path, pathlen); - std::error_code err = sys::fs::create_directories(path2, IgnoreExisting, perms2); - return err.value(); -} - diff --git a/src/llvm/llvm.h b/src/llvm/llvm.h index 8f08306c..0f945ee9 100644 --- a/src/llvm/llvm.h +++ b/src/llvm/llvm.h @@ -329,9 +329,6 @@ EXTERN_C err_t llvm_module_emit( EXTERN_C char* LLVMGetMainExecutable(const char* argv0); -// returns errno -EXTERN_C int LLVMCreateDirectories(const char* path, size_t pathlen, int perms); - // DEPRECATED EXTERN_C CoLLVMOS LLVMGetHostOS(); diff --git a/src/main.c b/src/main.c index b9838bf6..5894523f 100644 --- a/src/main.c +++ b/src/main.c @@ -18,8 +18,9 @@ typedef bool(*linkerfn_t)(int argc, char*const* argv, bool can_exit_early); const char* coprogname; const char* coexefile; -static char _coroot[PATH_MAX]; -const char* coroot = _coroot; +const char* coroot; +const char* cocachedir; +bool coverbose = false; u32 comaxproc = 1; extern int main_build(int argc, char*const* argv); // build.c @@ -137,18 +138,6 @@ int main(int argc, char* argv[]) { coprogname = strrchr(argv[0], PATH_SEPARATOR); coprogname = coprogname ? coprogname + 1 : argv[0]; coexefile = safechecknotnull(LLVMGetMainExecutable(argv[0])); - safecheckx(path_dir(_coroot, sizeof(_coroot), coexefile) < sizeof(_coroot)); - relpath_init(); - - // allow overriding coroot with env var - const char* coroot_env = getenv("COROOT"); - if (coroot_env && *coroot_env) - coroot = coroot_env; - - #if DEBUG - if (str_endswith(coroot, "/out/debug")) - coroot = path_join_m(memalloc_ctx(), coroot, "../../lib"); - #endif const char* exe_basename = strrchr(coexefile, PATH_SEPARATOR); exe_basename = exe_basename ? exe_basename + 1 : coexefile; @@ -182,14 +171,36 @@ int main(int argc, char* argv[]) { argv++; } - // initialize global state + const char* envvar; memalloc_t ma = memalloc_ctx(); + + // initialize global state comaxproc = sys_ncpu(); + relpath_init(); tmpbuf_init(ma); sym_init(ma); + + // coroot + if ((envvar = getenv("COROOT")) && *envvar) { + coroot = safechecknotnull(path_abs(ma, envvar)); + } else { + coroot = path_dir_m(ma, coexefile); + #if DEBUG + if (str_endswith(coroot, "/out/debug")) + coroot = path_join_m(memalloc_ctx(), coroot, "../../lib"); + #endif + } + + // cocachedir + if ((envvar = getenv("COCACHE")) && *envvar) { + cocachedir = safechecknotnull(path_abs(ma, envvar)); + } else { + cocachedir = path_join_m(ma, sys_homedir(), ".cache/compis/" CO_VERSION_STR); + } + + // llvm err_t err = llvm_init(); - if (err) - errx(1, "llvm_init: %s", err_str(err)); + if (err) errx(1, "llvm_init: %s", err_str(err)); // primary commands if ISCMD("build") return main_build(argc, argv); diff --git a/src/path.c b/src/path.c index 5ceebdef..d3e8834b 100644 --- a/src/path.c +++ b/src/path.c @@ -76,7 +76,7 @@ usize path_dir(char* buf, usize bufcap, const char* path) { goto singlechar; } } - usize len = MIN(bufcap, i + 1); + usize len = MIN(bufcap - 1, i + 1); if (len > 0) { memcpy(buf, path, len); buf[len] = 0; @@ -89,6 +89,15 @@ usize path_dir(char* buf, usize bufcap, const char* path) { } +char* nullable path_dir_m(memalloc_t ma, const char* path) { + usize cap = path_dirlen(path, strlen(path)) + 1; + char* buf = mem_alloc(ma, cap).p; + if (buf) + path_dir(buf, cap, path); + return buf; +} + + const char* path_base(const char* path) { if (path[0] == 0) return path; @@ -189,7 +198,7 @@ char* nullable path_abs(memalloc_t ma, const char* path) { usize pathlen = strlen(path); mem_t m = mem_alloc(ma, pathlen + 1); if (m.p) - path_cleanx(m.p, m.size, m.p, pathlen); + path_cleanx(m.p, m.size, path, pathlen); return m.p; } else { char cwd[PATH_MAX]; diff --git a/src/path.h b/src/path.h index 25df03d7..f37b06fe 100644 --- a/src/path.h +++ b/src/path.h @@ -26,6 +26,7 @@ usize path_dirlen(const char* path, usize len); // Returns bytes written to buf as if bufcap was infinite. // E.g. "foo/bar/baz.x" => "foo/bar", "/lol" => "/" usize path_dir(char* buf, usize bufcap, const char* path); +char* nullable path_dir_m(memalloc_t ma, const char* path); // path_base returns a pointer to the last path element. E.g. "foo/bar/baz.x" => "baz.x" // If the path is empty, returns "". @@ -70,9 +71,16 @@ void relpath_init(); }) // path_join_alloca allocates space on stack and joins two or more paths together. -// char* path_join_alloca(const char* path1, const char* path2) -// char* path_join_alloca(const char* path1, const char* path2, const char* path3) +// char* path_join_alloca(const char* path1 ...) #define path_join_alloca(args...) __VARG_DISP(_path_join_alloca, args) +#define _path_join_alloca1(p1) ({ \ + const char* p1__ = (p1); usize z1__ = strlen(p1__); \ + char* s__ = safechecknotnull(alloca(z1__ + 1)); \ + memcpy(s__, p1__, z1__); \ + s__[z1__] = 0; \ + path_cleanx(s__, z1__ + 1, s__, z1__); \ + s__; \ +}) #define _path_join_alloca2(p1, p2) ({ \ const char* p1__ = (p1); usize z1__ = strlen(p1__); \ const char* p2__ = (p2); usize z2__ = strlen(p2__); \ diff --git a/src/sys_homedir.c b/src/sys_homedir.c new file mode 100644 index 00000000..080be0dd --- /dev/null +++ b/src/sys_homedir.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "colib.h" +#include "path.h" + +#include +#include +#include +#include + + +const char* sys_homedir() { + static char homedir[PATH_MAX] = {0}; + if (*homedir) + return homedir; + + usize bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == (usize)-1) + bufsize = 16384; + + char* buf = alloca(bufsize); + if (buf) { + struct passwd pwd; + struct passwd* result; + getpwuid_r(getuid(), &pwd, buf, bufsize, &result); + if (result) { + memcpy(homedir, pwd.pw_dir, strlen(pwd.pw_dir) + 1); + return homedir; + } + // note: getpwuid_r returns 0 if the user getuid() was not found (we don't care) + warnx("sys_homedir/getpwuid_r"); + } + + // try HOME in env + const char* home = getenv("HOME"); + if (home) { + memcpy(homedir, home, strlen(home) + 1); + } else { + // last resort + #if defined(WIN32) + memcpy(homedir, "C:\\", strlen("C:\\") + 1); + #else + homedir[0] = PATH_SEPARATOR; + homedir[1] = 0; + #endif + } + + return homedir; +}