summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-17 17:37:17 +0800
committerKonAki <konaki@anokonaki.org>2026-10-17 17:37:17 +0800
commitdf4553d5999016547fa46a22d94593d388952baa (patch)
tree42ba04a3a3481d8a1bba8833d0d597115b218707
parent61ea96c953728f034cb766fe44f5419e4f679795 (diff)
konbuild: parallel commands, dependency files, remembers its compiler
-rw-r--r--kon/konbuild.h248
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");