diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-17 17:37:17 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-17 17:37:17 +0800 |
| commit | df4553d5999016547fa46a22d94593d388952baa (patch) | |
| tree | 42ba04a3a3481d8a1bba8833d0d597115b218707 | |
| parent | 61ea96c953728f034cb766fe44f5419e4f679795 (diff) | |
konbuild: parallel commands, dependency files, remembers its compiler
| -rw-r--r-- | kon/konbuild.h | 248 |
1 files changed, 235 insertions, 13 deletions
diff --git a/kon/konbuild.h b/kon/konbuild.h index d687cbb..270b7cf 100644 --- a/kon/konbuild.h +++ b/kon/konbuild.h @@ -68,7 +68,35 @@ #define KON_TOOLCHAIN_MSVC 0 #endif +/* what built this program is what it builds with, unless CC says otherwise. the compiler tells us what it is, but not what it is called on this machine, so + -DKON_CC='"name"' on the command line that builds it names it by hand, and the program keeps it when it rebuilds itself. gcc and clang (and mingw and llvm-mingw, which + are those two) are gcc and clang, the rest are cc */ +#if defined(KON_CC) +#define KON_CC_DEFAULT KON_CC +#elif defined(__TINYC__) +#define KON_CC_DEFAULT "tcc" +#elif defined(__clang__) +#define KON_CC_DEFAULT "clang" +#elif defined(__GNUC__) +#define KON_CC_DEFAULT "gcc" +#else +#define KON_CC_DEFAULT "cc" +#endif + +#if defined(__TINYC__) +#define KON_CC_FAMILY "tcc" +#elif defined(__clang__) +#define KON_CC_FAMILY "clang" +#elif defined(__GNUC__) +#define KON_CC_FAMILY "gcc" +#elif defined(_MSC_VER) +#define KON_CC_FAMILY "msvc" +#else +#define KON_CC_FAMILY "unknown" +#endif + static const char *kon_cc(void) KON_MAYBE_UNUSED; +static const char *kon_ccVersion(void) KON_MAYBE_UNUSED; static const char *kon_ar(void) KON_MAYBE_UNUSED; typedef struct kon_cmd { @@ -89,6 +117,25 @@ void kon_pushCmd_(KonCmd *c, ...); bool kon_runCmdSync(KonCmd *c); /* the same, with what the command prints appended to a file. NULL is the same as kon_runCmdSync */ bool kon_runCmdLog(KonCmd *c, const char *logPath); + +/* a command running on its own, to wait for later. kon_waitAny waits for whichever of procs ends first, gives its number and in ok whether it worked (-1 when none is running). + kon_cpuCount is how many programs make sense to run at once */ +typedef struct kon_proc { +#ifdef _WIN32 + HANDLE handle; +#else + pid_t pid; +#endif + int active; +} KonProc; + +bool kon_startCmd(KonCmd *c, const char *logPath, KonProc *p); +bool kon_waitProc(KonProc *p); +int kon_waitAny(KonProc *procs, size_t n, bool *ok); +int kon_cpuCount(void); + +/* 0 stops the commands being written to stderr as they run, they are by default */ +extern int kon_verbose; void kon_resetCmd(KonCmd *c); void kon_freeCmd(KonCmd *c); @@ -102,6 +149,9 @@ bool kon_needsRebuild(const char *output, const char **sources, size_t nSources) /* the newest file in a folder (not the folders in it) whose name ends in ext, or -1 */ long long kon_newestIn(const char *dir, const char *ext); +/* a dependency file as -MMD -MF writes it: true when the output is not there, the file is not, or anything it lists is newer than the output or gone */ +bool kon_depsNewer(const char *output, const char *depFile); + bool kon_exists(const char *path); bool kon_isDir(const char *path); bool kon_mkdir(const char *path); @@ -127,7 +177,20 @@ static const char *kon_cc(void) { return "cl"; #else const char *cc = getenv("CC"); - return cc ? cc : "cc"; + return cc && *cc ? cc : KON_CC_DEFAULT; +#endif +} + +/* the version of the compiler this program was built with, as it reports it */ +static const char *kon_ccVersion(void) { +#if defined(__clang__) + return __clang_version__; +#elif defined(__TINYC__) + return "tcc"; +#elif defined(__VERSION__) + return __VERSION__; +#else + return "unknown"; #endif } @@ -438,9 +501,12 @@ void kon_freeTemp(void) { kon_tempCount_ = kon_tempCap_ = 0; } +int kon_verbose = 1; + static void kon_printCmd_(KonCmd *c) { size_t i; + if (!kon_verbose) return; for (i = 0; i < c->count; i++) fprintf(stderr, "%s ", c->items[i]); fprintf(stderr, "\n"); } @@ -493,7 +559,7 @@ static size_t kon_quoteArg_(char *out, const char *arg) { return n; } -bool kon_runCmdLog(KonCmd *c, const char *logPath) { +bool kon_startCmd(KonCmd *c, const char *logPath, KonProc *p) { STARTUPINFOA si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; @@ -501,8 +567,8 @@ bool kon_runCmdLog(KonCmd *c, const char *logPath) { size_t total = 1, at = 0, i; char *line; bool ok; - DWORD code = 1; + p->active = 0; kon_printCmd_(c); if (c->count == 0) return false; @@ -536,28 +602,64 @@ bool kon_runCmdLog(KonCmd *c, const char *logPath) { ok = CreateProcessA(NULL, line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) != 0; free(line); + if (log != INVALID_HANDLE_VALUE) CloseHandle(log); if (!ok) { fprintf(stderr, "konbuild: could not start %s (%lu)\n", c->items[0], (unsigned long)GetLastError()); - if (log != INVALID_HANDLE_VALUE) CloseHandle(log); return false; } - WaitForSingleObject(pi.hProcess, INFINITE); - GetExitCodeProcess(pi.hProcess, &code); - - CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - if (log != INVALID_HANDLE_VALUE) CloseHandle(log); + p->handle = pi.hProcess; + p->active = 1; + return true; +} + +bool kon_waitProc(KonProc *p) { + DWORD code = 1; + if (!p->active) return false; + WaitForSingleObject(p->handle, INFINITE); + GetExitCodeProcess(p->handle, &code); + CloseHandle(p->handle); + p->active = 0; return code == 0; } +int kon_waitAny(KonProc *procs, size_t n, bool *ok) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + size_t map[MAXIMUM_WAIT_OBJECTS], count = 0, i; + DWORD r, code = 1; + + for (i = 0; i < n && count < MAXIMUM_WAIT_OBJECTS; i++) { + if (!procs[i].active) continue; + handles[count] = procs[i].handle; + map[count++] = i; + } + if (count == 0) return -1; + r = WaitForMultipleObjects((DWORD)count, handles, FALSE, INFINITE); + if (r >= WAIT_OBJECT_0 + count) return -1; + i = map[r - WAIT_OBJECT_0]; + GetExitCodeProcess(procs[i].handle, &code); + CloseHandle(procs[i].handle); + procs[i].active = 0; + *ok = code == 0; + return (int)i; +} + +int kon_cpuCount(void) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + return si.dwNumberOfProcessors < 1 ? 1 : (int)si.dwNumberOfProcessors; +} + #else /* posix complian systems */ -bool kon_runCmdLog(KonCmd *c, const char *logPath) { +bool kon_startCmd(KonCmd *c, const char *logPath, KonProc *p) { pid_t pid; - int status = 0, fd = -1; + int fd = -1; + p->active = 0; kon_printCmd_(c); if (c->count == 0) return false; @@ -572,6 +674,7 @@ bool kon_runCmdLog(KonCmd *c, const char *logPath) { } } + fflush(NULL); pid = fork(); if (pid < 0) { fprintf(stderr, "konbuild: fork failed: %s\n", strerror(errno)); @@ -591,21 +694,136 @@ bool kon_runCmdLog(KonCmd *c, const char *logPath) { } if (fd >= 0) close(fd); - while (waitpid(pid, &status, 0) < 0) { + p->pid = pid; + p->active = 1; + return true; +} + +bool kon_waitProc(KonProc *p) { + int status = 0; + + if (!p->active) return false; + while (waitpid(p->pid, &status, 0) < 0) { if (errno == EINTR) continue; fprintf(stderr, "konbuild: waitpid failed: %s\n", strerror(errno)); + p->active = 0; return false; } - + p->active = 0; return WIFEXITED(status) && WEXITSTATUS(status) == 0; } +int kon_waitAny(KonProc *procs, size_t n, bool *ok) { + size_t i, active = 0; + + for (i = 0; i < n; i++) { + if (procs[i].active) active++; + } + if (active == 0) return -1; + for (;;) { + int status = 0; + pid_t pid = waitpid(-1, &status, 0); + + if (pid < 0) { + if (errno == EINTR) continue; + return -1; + } + for (i = 0; i < n; i++) { + if (procs[i].active && procs[i].pid == pid) { + procs[i].active = 0; + *ok = WIFEXITED(status) && WEXITSTATUS(status) == 0; + return (int)i; + } + } + } +} + +int kon_cpuCount(void) { +#ifdef _SC_NPROCESSORS_ONLN + long n = sysconf(_SC_NPROCESSORS_ONLN); + + return n < 1 ? 1 : (int)n; +#else + return 1; +#endif +} + #endif +bool kon_runCmdLog(KonCmd *c, const char *logPath) { + KonProc p; + + if (!kon_startCmd(c, logPath, &p)) return false; + return kon_waitProc(&p); +} + bool kon_runCmdSync(KonCmd *c) { return kon_runCmdLog(c, NULL); } +/*** dependency files ***/ + +/* what -MMD writes: "output: source header header \" and the rest on the lines after, a space in a name has a backslash before it */ +bool kon_depsNewer(const char *output, const char *depFile) { + long long outM = kon_mtime(output); + FILE *f; + char *buf, *tok; + long size; + size_t n, i = 0; + bool newer = false; + + if (outM < 0) return true; + f = fopen(depFile, "rb"); + if (!f) return true; + if (fseek(f, 0, SEEK_END) != 0 || (size = ftell(f)) < 0 || fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return true; + } + n = (size_t)size; + buf = (char *)malloc(n + 1); + tok = (char *)malloc(n + 1); + if (!buf || !tok || fread(buf, 1, n, f) != n) { + fclose(f); + free(buf); + free(tok); + return true; + } + fclose(f); + buf[n] = '\0'; + + while (i < n && !newer) { + size_t len = 0; + + for (;;) { + if (i < n && (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n' || buf[i] == '\r')) i++; + else if (i + 1 < n && buf[i] == '\\' && buf[i + 1] == '\n') i += 2; + else if (i + 2 < n && buf[i] == '\\' && buf[i + 1] == '\r' && buf[i + 2] == '\n') i += 3; + else if (i + 1 == n && buf[i] == '\\') i++; + else break; + } + while (i < n) { + if (buf[i] == '\\' && i + 1 < n && buf[i + 1] == ' ') { + tok[len++] = ' '; + i += 2; + } else if (buf[i] == '\\' && i + 1 < n && (buf[i + 1] == '\n' || buf[i + 1] == '\r')) { + break; + } else if (buf[i] == '\\' && i + 1 == n) { + break; + } else if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n' || buf[i] == '\r') { + break; + } else { + tok[len++] = buf[i++]; + } + } + tok[len] = '\0'; + if (len == 0 || tok[len - 1] == ':') continue; + if (kon_mtime(tok) > outM || kon_mtime(tok) < 0) newer = true; + } + free(buf); + free(tok); + return newer; +} + /*** rebuilding the build program ***/ void kon_rebuildSelf_(const char *src_path, int argc, char **argv) { @@ -655,8 +873,12 @@ void kon_rebuildSelf_(const char *src_path, int argc, char **argv) { kon_pushCmd(&cmd, (char *)kon_cc(), "/nologo", (char *)src_path, feFlag, NULL); } #else +#ifdef KON_CC + kon_pushCmd(&cmd, (char *)kon_cc(), kon_fmt("-DKON_CC=\"%s\"", KON_CC), "-o", (char *)self, (char *)src_path, NULL); +#else kon_pushCmd(&cmd, (char *)kon_cc(), "-o", (char *)self, (char *)src_path, NULL); #endif +#endif if (!kon_runCmdSync(&cmd)) { fprintf(stderr, "konbuild: self-rebuild failed\n"); |
