From 08b7229355a54a4339931411629bcb9ea48b5926 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Wed, 16 Apr 2025 18:32:30 -0400 Subject: [PATCH] Improve dynamic library behavior --- README.md | 12 +++++++----- cbs.c | 55 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 20ed3ed..e027b35 100644 --- a/README.md +++ b/README.md @@ -86,17 +86,19 @@ The first argument to `load()`[^2] is the type of the target file. The options a 'd' - dynamically linked library ``` -The second argument is the name of the target. There is no assumed typical file extension for the target as executables commonly lack them. It is also common to prepend `lib` to files that are static or dynamic libraries; this is done automatically and in a nature similar to typical file extensions. This allows you to use the same name to refer to a statically linked library in the same way you would when refering to a dynamically linked library in the linker flags. +The second argument is the name of the target. There is no assumed typical file extension for the target as executables commonly lack them. It is also common to prepend `lib` to files that are static or dynamic libraries; this is done automatically and in a nature similar to typical file extensions. The idea is similar to the manner in which you would typically pass system libraries to the linker flag `-l`. -The rest of the arguments are the names of the files you want to link together. The typical file extension for these files is `.o`. Generally the only other files that would use a different file extension would be statically linked libraries (note that dynamically linked libraries aren't really linked in the same way as object files nor statically linked libraries and should thus be passed as linker flags instead). As is the case with compiling, **the arguments passed to `load()` must be terminated with a null pointer**. +The rest of the arguments are the names of the files you want to link together. The typical file extension for these files is `.o`. Generally the only other files that would use a different file extension would be statically linked libraries or dynamically linked libraries (the linker flag `-l` should be used to link system libraries as opposed to project libraries). As is the case with compiling, **the arguments passed to `load()` must be terminated with a null pointer**. In a similar way as compiling, the predefined `lflags` variable can be used to define flags for the linker. -An example of linking `liba.dylib`, `b.o`, and `libc.a` as a statically linked library `libmain.a`. +The `DYEXT` macro has been defined which represents the platform-dependent file extension for dyanmic libraries, `".dylib"` for macOS and `".so"` for anything else. This can be used to allow portability of build files. + +An example of linking `liba.dylib` (or `liba.so`), `b.o`, `libc.a`, and the system math library as a statically linked library `libmain.a`. ```c -lflags = (char *[]){"-la", NULL}; -load('s', "main", "b", "c.a", NULL); +lflags = (char *[]){"-lm", NULL}; +load('s', "main", "a" DYEXT, "b", "c.a", NULL); ``` ### Recursive builds diff --git a/cbs.c b/cbs.c index e392ca6..18a0b39 100644 --- a/cbs.c +++ b/cbs.c @@ -30,39 +30,52 @@ void error(char *fmt, ...) { exit(EXIT_FAILURE); } -void *alloc(size_t s) { +void *allocate(size_t s) { void *r; if (!(r = malloc(s))) error("Memory allocation"); - return r; + return memset(r, 0, s); } char *extend(char *path, char *ext) { - char *bp, *ep, *r, *p; - size_t d, b, l, e; - - if (!path) return NULL; + char *dp, *bp, *e, *r; + size_t d, b, l; + + if (!(dp = path)) return NULL; + + bp = (bp = strrchr(dp, '/')) ? bp + 1 : dp; + d = bp - dp; + b = (e = strrchr(bp, '.')) ? e - bp : (e = ext, strlen(bp)); + if (*ext == '!') e = ++ext; + l = strcmp(e, ".a") == 0 || strcmp(e, DYEXT) == 0 ? 3 : 0; + + if (strcmp(e, DYEXT) == 0) { + path = d ? strndup(dp, d) : strdup("."); + if (!(dp = realpath(path, NULL))) + error("Unable to get absolute path of `%s'", path); + free(path); + dp[(d = strlen(dp))] = '/'; + ++d; + } - bp = (bp = strrchr(path, '/')) ? bp + 1 : path; - d = bp - path; - b = (ep = strrchr(bp, '.')) ? ep - bp : (ep = ext, strlen(bp)); - if (*ext == '!') ep = ext + 1; - l = strcmp(ep, ".a") == 0 || strcmp(ep, DYEXT) == 0 ? 3 : 0; - e = strlen(ep); + r = allocate(d + l + b + strlen(e) + 1); + strncat(r, dp, d); + strncat(r, "lib", l); + strncat(r, bp, b); + strcat(r, e); - p = r = alloc(d + l + b + e + 1); - p = stpncpy(p, path, d); - p = stpncpy(p, "lib", l); - p = stpncpy(p, bp, b); - strcpy(p, ep); + if (dp != path) free(dp); return r; } int modified(char *target, char *dep) { + char *e; struct stat tstat, dstat; + if ((e = strrchr(dep, '.')) && strcmp(e, DYEXT) == 0) return 0; + if (stat(target, &tstat) == -1) { if (errno == ENOENT) return !(errno = 0); error("Unable to stat `%s'", target); @@ -99,15 +112,14 @@ void compile(char *src, ...) { pid_t cpid; if (f = 0, cflags) while (cflags[f]) ++f; - p = args = alloc((5 + f + 1) * sizeof*args); + p = args = allocate((5 + f + 1) * sizeof*args); *p++ = "cc"; - if (cflags) for (f = 0; cflags[f]; *p++ = cflags[f++]); *p++ = "-c"; + if (cflags) for (f = 0; cflags[f]; *p++ = cflags[f++]); *p++ = "-o"; *p++ = obj = extend(src, "!.o"); *p++ = hdr = src = extend(src, ".c"); - *p = NULL; va_start(hdrs, src); do if (modified(obj, hdr = extend(hdr, ".h"))) { @@ -133,7 +145,7 @@ void load(char type, char *target, char *obj, ...) { for (o = 1; va_arg(count, char *); ++o); va_end(count); if (f = 0, lflags) while (lflags[f]) ++f; - args = alloc((3 + o + 1 + f + 1) * sizeof*args); + args = allocate((3 + o + 1 + f + 1) * sizeof*args); fp = (a = (p = args) + 3) + o; switch (type) { @@ -161,7 +173,6 @@ void load(char type, char *target, char *obj, ...) { do *p++ = extend(obj, ".o"); while ((obj = va_arg(objs, char *))); va_end(objs); if (lflags) for (f = 0; lflags[f]; *fp++ = lflags[f++]); - if (fp > p) *fp = NULL; fp = p; p -= o; -- 2.51.0