/* SPDX-License-Identifier: MIT */ /* * build.c, the engine's build program. it does what build.sh and the Makefile do without needing sh or make, only a C compiler: * * cc -o build build.c * ./build [-d] [-D] [-p platform] [-m module,...] [-o objdir] [target ...] * * it rebuilds itself when this file or kon/konbuild.h changes */ #ifdef _MSC_VER #error "build.c uses gcc and clang flags, on Windows build it with mingw or llvm-mingw" #endif #define _POSIX_C_SOURCE 200809L #define KONBUILD_IMPLEMENTATION #include "kon/konbuild.h" #define PROGRAM "konengine" #define SRCDIR "src" #define KONDIR "kon" #define EXTDIR "ext" /*** modules ***/ typedef struct module { const char *name; const char *needs; /* the modules it cannot do without, separated by spaces */ } module_t; static const module_t known[] = { {"font", ""}, {"toml", ""}, {"fs", ""}, {"tile", ""}, {"pack", ""}, {"packw", "pack"}, {"ui", "font"}, {"debug", "font assets"}, {"assets", "pack"}, {"image", ""}, {"audio", ""}, {"mic", ""}, {"sound", "audio"}, {"model", "assets"}, {"2d", ""}, {"fx", "2d"}, {"particles", ""}, {"picture", "2d"}, {"rm", ""}, {"rmmap", ""}, {"rmtile", ""}, {"rmdraw", "rmmap rmtile"}, {"rmfield", "rmmap"}, {"rmplugin", ""}, {"rmdata", "battle party save"}, {"script", ""}, {"battle", ""}, {"party", "battle save"}, {"save", ""}, {"stage", "2d"}, {"textbox", "2d font"}, {"platformer", "tile"}, {"rpg", "tile"}, {"3d", "tile"}, {"sector", ""}, {"world", "3d sector tile"} }; #define KNOWN (sizeof(known) / sizeof(known[0])) #define MODULES_MAX 64 static const char *chosen[MODULES_MAX]; static int chosenCount; /* the next word of a string cut at sep, writing a 0 over the separator. strtok keeps its place in a hidden variable, and these are nested */ static char *word(char **at, char sep) { char *start = *at, *end; while (start && *start == sep) start++; if (!start || !*start) { *at = NULL; return NULL; } end = strchr(start, sep); if (end) { *end = 0; *at = end + 1; } else { *at = NULL; } return start; } static const module_t *findModule(const char *name) { size_t i; for (i = 0; i < KNOWN; i++) { if (strcmp(known[i].name, name) == 0) return &known[i]; } return NULL; } static int hasModule(const char *name) { int i; for (i = 0; i < chosenCount; i++) { if (strcmp(chosen[i], name) == 0) return 1; } return 0; } static void addModule(const char *name); static void addNeeds(const char *needs) { char *copy = kon_fmt("%s", needs), *w; while ((w = word(©, ' ')) != NULL) addModule(w); } static void addModule(const char *name) { const module_t *m = findModule(name); if (!m) { fprintf(stderr, "build: unknown module '%s'\n", name); exit(1); } if (hasModule(m->name)) return; if (chosenCount >= MODULES_MAX) { fprintf(stderr, "build: too many modules\n"); exit(1); } chosen[chosenCount++] = m->name; addNeeds(m->needs); } static void usage(void) { size_t i; fprintf(stderr, "usage: build [-d] [-D] [-v] [-j jobs] [-p platform] [-m module,...] [-o objdir] [target ...]\n"); fprintf(stderr, "modules: "); for (i = 0; i < KNOWN; i++) fprintf(stderr, " %s", known[i].name); fprintf(stderr, "\nplatforms: native linux bsd windows\n"); fprintf(stderr, "targets: all run clean tools pack examples konedit\n"); exit(1); } /*** settings ***/ static const char *platform = "native"; static const char *objdir = "obj"; static int debug, developer; static const char *exe = ""; static const char *compiler; static KonCmd modeFlags, defs, libs; static long long newestHeader; static const char *flagsFile; static void push(KonCmd *c, const char *s) { kon_pushCmd(c, (char *)s, NULL); } static void pushAll(KonCmd *to, const KonCmd *from) { size_t i; for (i = 0; i < from->count; i++) push(to, from->items[i]); } static void pushWords(KonCmd *to, const char *words) { char *copy = kon_fmt("%s", words), *w; while ((w = word(©, ' ')) != NULL) push(to, w); } /* CC can be a program with arguments, like "ccache cc". one that is a file on its own is taken whole, a path on Windows has spaces in it */ static void pushCompiler(KonCmd *to) { if (kon_exists(compiler)) push(to, compiler); else pushWords(to, compiler); } static char *upper(const char *s) { char *u = kon_fmt("%s", s), *p; for (p = u; *p; p++) { if (*p >= 'a' && *p <= 'z') *p = (char)(*p - 'a' + 'A'); } return u; } static char *objPath(const char *name) { return kon_fmt("%s/%s.o", objdir, name); } static char *exePath(const char *name) { return kon_fmt("%s/%s%s", objdir, name, exe); } /*** compiling ***/ /* gcc and clang write a file of the headers a source read when asked to, the others have none of it and rebuild when any header changes */ #if defined(__TINYC__) #define HAVE_DEPS 0 #else #define HAVE_DEPS 1 #endif /* the warnings the engine's own code is held to. the stb files and miniaudio are not ours, they get -w */ static void ourFlags(KonCmd *c, int withDefs) { push(c, "-std=c99"); push(c, "-Wall"); push(c, "-Wextra"); push(c, "-Wpedantic"); pushAll(c, &modeFlags); if (withDefs) pushAll(c, &defs); push(c, "-I" KONDIR); push(c, "-I" EXTDIR); } static void extFlags(KonCmd *c) { push(c, "-std=gnu99"); push(c, "-w"); pushAll(c, &modeFlags); pushAll(c, &defs); push(c, "-I" EXTDIR); } /* for what has no dependency file: older than a file it is made from, or than any header of the engine */ static int stale(const char *output, const char **inputs, size_t count, int headers) { long long m = kon_mtime(output); if (m < 0) return 1; if (headers && newestHeader > m) return 1; return kon_needsRebuild(output, inputs, count); } /* obj/name.o has its dependencies in obj/name.d */ static char *depPath(const char *object) { char *d = kon_fmt("%s", object); size_t n = strlen(d); if (n > 2 && d[n - 2] == '.') d[n - 1] = 'd'; return d; } /*** jobs ***/ #define JOBS_MAX 256 #define JOBS_LIMIT 32 typedef struct job { KonCmd cmd; const char *label; } job_t; static job_t jobs[JOBS_MAX]; static int jobCount; static int maxJobs = 1; static void queue(const char *label, KonCmd *c) { if (jobCount >= JOBS_MAX) { fprintf(stderr, "build: too many jobs\n"); exit(1); } jobs[jobCount].cmd = *c; jobs[jobCount].label = label; jobCount++; *c = (KonCmd){0}; } static void showLog(const char *path) { FILE *f = fopen(path, "rb"); char buf[4096]; size_t n; if (!f) return; while ((n = fread(buf, 1, sizeof(buf), f)) > 0) fwrite(buf, 1, n, stderr); fclose(f); } /* runs what is queued, maxJobs at a time. what each one prints is kept in a file and shown when it is done, so the messages of two compilers do not run into each other. after a failure nothing new starts, the ones running finish, and the build stops */ static void runJobs(void) { KonProc procs[JOBS_LIMIT]; int running = 0, failed = 0, next = 0, i; int limit = maxJobs < 1 ? 1 : (maxJobs > JOBS_LIMIT ? JOBS_LIMIT : maxJobs); for (i = 0; i < JOBS_LIMIT; i++) procs[i].active = 0; if (jobCount > 0) kon_mkdirs(objdir); while (next < jobCount || running > 0) { while (!failed && next < jobCount && running < limit) { const char *log; int s = 0; while (procs[s].active) s++; log = kon_fmt("%s/.job%d.log", objdir, s); kon_remove(log); printf("%s\n", jobs[next].label); if (!kon_startCmd(&jobs[next].cmd, log, &procs[s])) { failed = 1; break; } next++; running++; } if (running == 0) break; { bool ok = false; int s = kon_waitAny(procs, (size_t)limit, &ok); const char *log; if (s < 0) { failed = 1; break; } running--; log = kon_fmt("%s/.job%d.log", objdir, s); showLog(log); kon_remove(log); if (!ok) failed = 1; } } for (i = 0; i < jobCount; i++) kon_freeCmd(&jobs[i].cmd); jobCount = 0; if (failed) exit(1); } static void queueCompile(const char *source, const char *object, int ext, const KonCmd *extra) { KonCmd c = {0}; const char *dep = depPath(object); #if HAVE_DEPS if (!kon_depsNewer(object, dep)) return; #else { const char *in[1]; in[0] = source; if (!stale(object, in, 1, 1)) return; } #endif pushCompiler(&c); if (ext) extFlags(&c); else ourFlags(&c, 1); if (extra) pushAll(&c, extra); #if HAVE_DEPS push(&c, "-MMD"); push(&c, "-MF"); push(&c, dep); #endif push(&c, "-c"); push(&c, source); push(&c, "-o"); push(&c, object); queue(kon_fmt("# compile %s", object), &c); } static void queueLink(const char *output, const char **objects, size_t count, int withLibs) { KonCmd c = {0}; size_t i; if (!stale(output, objects, count, 0)) return; pushCompiler(&c); push(&c, "-o"); push(&c, output); for (i = 0; i < count; i++) push(&c, objects[i]); if (withLibs) pushAll(&c, &libs); queue(kon_fmt("# link %s", output), &c); } /*** what goes into the program ***/ #define OBJECTS_MAX 128 typedef struct unit { const char *name; /* the object, without the folder and the .o */ const char *source; int ext; } unit_t; static unit_t units[OBJECTS_MAX]; static int unitCount; static void addUnit(const char *name, const char *source, int ext) { int i; for (i = 0; i < unitCount; i++) { if (strcmp(units[i].name, name) == 0) return; } if (unitCount >= OBJECTS_MAX) { fprintf(stderr, "build: too many files\n"); exit(1); } units[unitCount].name = name; units[unitCount].source = source; units[unitCount].ext = ext; unitCount++; } /* a library that is a single header has its implementation in src/impl, a module has its .c next to its header */ static void addKon(const char *name) { const char *impl = kon_fmt("%s/impl/%s.c", SRCDIR, name); addUnit(name, kon_exists(impl) ? impl : kon_fmt("%s/%s.c", KONDIR, name), 0); } static void addModuleUnits(const char *m) { if (strcmp(m, "assets") == 0) { addKon("konassets"); } else if (strcmp(m, "model") == 0) { addKon("konmodel"); addKon("konassets"); } else if (strcmp(m, "packw") == 0) { /* the pack unit is there with pack, this only turns the writer on in it */ } else if (strcmp(m, "image") == 0) { addUnit("stb_image", SRCDIR "/impl/stb_image.c", 1); } else if (strcmp(m, "audio") == 0 || strcmp(m, "mic") == 0) { addUnit("miniaudio", SRCDIR "/impl/miniaudio.c", 1); addKon(kon_fmt("kon%s", m)); if (strcmp(m, "audio") == 0 && kon_exists(EXTDIR "/stb_vorbis.c")) addUnit("stb_vorbis", EXTDIR "/stb_vorbis.c", 1); } else { addKon(kon_fmt("kon%s", m)); } } static void setup(void) { int i, native = 0; const char *x11 = ""; KonCmd platformDefs = {0}; #if defined(_WIN32) const char *here = "windows"; #elif defined(__linux__) const char *here = "linux"; #else const char *here = "bsd"; #endif if (strcmp(platform, "native") == 0) platform = here; native = strcmp(platform, here) == 0; if (strcmp(platform, "linux") == 0 || strcmp(platform, "bsd") == 0) { push(&libs, "-lX11"); push(&libs, "-lXext"); push(&libs, "-lXrandr"); push(&libs, "-lm"); push(&platformDefs, "-D_POSIX_C_SOURCE=200809L"); #if defined(__NetBSD__) x11 = "/usr/X11R7"; #elif defined(__OpenBSD__) x11 = "/usr/X11R6"; #elif defined(__FreeBSD__) || defined(__DragonFly__) x11 = "/usr/local"; #endif if (*x11 && native) { push(&platformDefs, kon_fmt("-I%s/include", x11)); libs.count = 0; push(&libs, kon_fmt("-L%s/lib", x11)); push(&libs, kon_fmt("-Wl,-R%s/lib", x11)); push(&libs, "-lX11"); push(&libs, "-lXext"); push(&libs, "-lXrandr"); push(&libs, "-lm"); } } else if (strcmp(platform, "windows") == 0) { push(&libs, "-lgdi32"); push(&libs, "-lm"); exe = ".exe"; } else { fprintf(stderr, "build: unknown platform '%s'\n", platform); usage(); } compiler = getenv("CC"); if (!compiler || !*compiler) { /* the one that built this program, see konbuild.h. a Windows build from another system has to be a cross compiler */ if (strcmp(platform, "windows") == 0 && !native) compiler = "x86_64-w64-mingw32-gcc"; else compiler = kon_cc(); } if (debug) { push(&modeFlags, "-g"); push(&modeFlags, "-O1"); push(&modeFlags, "-DKON_DEBUG"); developer = 1; } else { push(&modeFlags, "-O2"); push(&modeFlags, "-DNDEBUG"); } if (developer) push(&modeFlags, "-DKON_DEVELOPER"); /* the modules are all in, with what they need */ pushAll(&defs, &platformDefs); kon_freeCmd(&platformDefs); for (i = 0; i < chosenCount; i++) push(&defs, kon_fmt("-DKON_MODULE_%s", upper(chosen[i]))); if (hasModule("3d") || hasModule("model") || hasModule("sector")) push(&defs, "-DKONSOFREN_3D"); if (hasModule("packw")) push(&defs, "-DKONPACK_WRITER"); if (hasModule("audio") && kon_exists(EXTDIR "/stb_vorbis.c")) push(&defs, "-DKON_VORBIS"); if (hasModule("audio") || hasModule("mic")) { if (strcmp(platform, "linux") == 0) { push(&libs, "-lpthread"); push(&libs, "-ldl"); } else if (strcmp(platform, "bsd") == 0) { push(&libs, "-lpthread"); } } addUnit("engine", SRCDIR "/engine.c", 0); addUnit("konwinlib", SRCDIR "/impl/konwinlib.c", 0); addUnit("konsofren", SRCDIR "/impl/konsofren.c", 0); addUnit("kontime", SRCDIR "/impl/kontime.c", 0); addUnit("koninput", SRCDIR "/impl/koninput.c", 0); addUnit("konprogram", KONDIR "/konprogram.c", 0); for (i = 0; i < chosenCount; i++) addModuleUnits(chosen[i]); { long long a = kon_newestIn(KONDIR, ".h"), b = kon_newestIn(EXTDIR, ".h"), t = kon_newestIn("tools", ".h"); newestHeader = a > b ? a : b; if (t > newestHeader) newestHeader = t; } } /* what was built with, so a changed flag or module list starts the objects again */ static void checkFlags(void) { KonCmd all = {0}; char *signature; size_t i, size = 1; FILE *f; char old[8192]; pushCompiler(&all); pushAll(&all, &modeFlags); pushAll(&all, &defs); pushAll(&all, &libs); for (i = 0; i < (size_t)unitCount; i++) push(&all, units[i].name); for (i = 0; i < all.count; i++) size += strlen(all.items[i]) + 1; signature = (char *)malloc(size); if (!signature) exit(1); signature[0] = '\0'; for (i = 0; i < all.count; i++) { strcat(signature, all.items[i]); strcat(signature, " "); } kon_freeCmd(&all); kon_mkdirs(objdir); flagsFile = kon_fmt("%s/.flags", objdir); old[0] = '\0'; f = fopen(flagsFile, "r"); if (f) { size_t n = fread(old, 1, sizeof(old) - 1, f); old[n] = '\0'; fclose(f); } if (strcmp(old, signature) != 0) { KonList list = {0}; if (kon_listFiles(objdir, &list)) { for (i = 0; i < list.count; i++) { size_t n = strlen(list.items[i]); if (n > 2 && (strcmp(list.items[i] + n - 2, ".o") == 0 || strcmp(list.items[i] + n - 2, ".d") == 0)) kon_remove(list.items[i]); } kon_freeList(&list); } f = fopen(flagsFile, "w"); if (f) { fputs(signature, f); fclose(f); } } free(signature); } /*** targets ***/ static const char *objects[OBJECTS_MAX + 16]; static int objectCount; static void queueObjects(int withEngine) { int i; objectCount = 0; for (i = 0; i < unitCount; i++) { const char *obj = objPath(units[i].name); if (!withEngine && strcmp(units[i].name, "engine") == 0) continue; queueCompile(units[i].source, obj, units[i].ext, NULL); objects[objectCount++] = obj; } } static void targetAll(void) { queueObjects(1); runJobs(); queueLink(exePath(PROGRAM), objects, (size_t)objectCount, 1); runJobs(); } static void targetRun(void) { KonCmd c = {0}; targetAll(); push(&c, exePath(PROGRAM)); if (!kon_runCmdSync(&c)) exit(1); kon_freeCmd(&c); } static const char *toolNames[] = {"konpack", "konmodel", "mzsurvey", "mzscript", "kons2c", "mzmap", "mzdata"}; static const char *editorName = "konedit"; static const char *examples[] = {"modelview", "sound", "ui", "platform", "dialog", "script", "corridor", "hills", "sectorview"}; /* only is the name of one example, or NULL for all of them */ static void targetExamples(const char *only) { size_t i; queueObjects(0); for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) { if (only && strcmp(only, examples[i]) != 0) continue; queueCompile(kon_fmt("examples/%s.c", examples[i]), objPath(examples[i]), 0, NULL); } runJobs(); for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) { const char *all[OBJECTS_MAX + 16]; int n = 0, k; if (only && strcmp(only, examples[i]) != 0) continue; all[n++] = objPath(examples[i]); for (k = 0; k < objectCount; k++) all[n++] = objects[k]; queueLink(exePath(examples[i]), all, (size_t)n, 1); } runJobs(); } /* the editor is a program of its own with the modules it needs, so asking for it adds them */ static void targetEditor(void) { const char *all[OBJECTS_MAX + 16]; int n = 0, k; queueObjects(0); queueCompile("editor/konedit.c", objPath(editorName), 0, NULL); runJobs(); all[n++] = objPath(editorName); for (k = 0; k < objectCount; k++) all[n++] = objects[k]; queueLink(exePath(editorName), all, (size_t)n, 1); runJobs(); } static int isEditorTarget(const char *t) { size_t d = strlen(objdir); if (strcmp(t, editorName) == 0) return 1; if (strncmp(t, objdir, d) != 0 || t[d] != 0x2f) return 0; return strcmp(t + d + 1, editorName) == 0 || strcmp(t + d + 1, "konedit.exe") == 0; } /* a program made straight from some files, with the headers they read checked too. the converters do not link the engine */ static void queueTool(const char *name, const char **sources, size_t count, const KonCmd *flags, int withEngineFlags, int withMath) { KonCmd c = {0}; const char *out = exePath(name); size_t i; if (!stale(out, sources, count, 1)) return; pushCompiler(&c); ourFlags(&c, withEngineFlags); if (flags) pushAll(&c, flags); push(&c, "-o"); push(&c, out); for (i = 0; i < count; i++) push(&c, sources[i]); if (withMath) push(&c, "-lm"); queue(kon_fmt("# link %s", out), &c); } static void targetTools(void) { KonCmd extra = {0}; const char *cgltfObj = objPath("cgltf"); const char *s[8]; /* the packer uses the module flags, the others are built without them */ push(&extra, "-DKONPACK_WRITER"); s[0] = "tools/konpack.c"; s[1] = KONDIR "/konpack.c"; queueTool("konpack", s, 2, &extra, 1, 0); kon_freeCmd(&extra); { KonCmd cg = {0}; const char *in[1]; in[0] = "tools/cgltf.c"; if (stale(cgltfObj, in, 1, 0)) { pushCompiler(&cg); push(&cg, "-std=gnu99"); push(&cg, "-w"); pushAll(&cg, &modeFlags); push(&cg, "-I" EXTDIR); push(&cg, "-c"); push(&cg, "tools/cgltf.c"); push(&cg, "-o"); push(&cg, cgltfObj); queue(kon_fmt("# compile %s", cgltfObj), &cg); } } s[0] = "tools/mzsurvey.c"; queueTool("mzsurvey", s, 1, NULL, 0, 0); push(&extra, "-Itools"); s[0] = "tools/mzscript.c"; s[1] = KONDIR "/konscript.c"; queueTool("mzscript", s, 2, &extra, 0, 0); s[0] = "tools/kons2c.c"; queueTool("kons2c", s, 2, &extra, 0, 0); kon_freeCmd(&extra); s[0] = "tools/mzmap.c"; queueTool("mzmap", s, 1, NULL, 0, 0); s[0] = "tools/mzdata.c"; s[1] = KONDIR "/konbattle.c"; queueTool("mzdata", s, 2, NULL, 0, 1); /* the model converter links the object the first batch makes */ runJobs(); push(&extra, "-DKONSOFREN_3D"); push(&extra, "-DKONMODEL_WRITER"); s[0] = "tools/konmodel.c"; s[1] = KONDIR "/konmodel.c"; s[2] = KONDIR "/konassets.c"; s[3] = KONDIR "/konpack.c"; s[4] = SRCDIR "/impl/konsofren.c"; s[5] = cgltfObj; queueTool("konmodel", s, 6, &extra, 0, 1); kon_freeCmd(&extra); runJobs(); } static void targetPack(void) { KonList list = {0}; KonCmd c = {0}; const char *assets = getenv("ASSETDIR") ? getenv("ASSETDIR") : "assets"; const char *listFile = kon_fmt("%s/assets.lst", objdir); const char *packFile = kon_fmt("%s/data.kpk", objdir); const char *extra = getenv("PACKFLAGS"); FILE *f; size_t i; targetTools(); if (!kon_listFiles(assets, &list)) { fprintf(stderr, "build: cannot read %s\n", assets); exit(1); } f = fopen(listFile, "w"); if (!f) { fprintf(stderr, "build: cannot write %s\n", listFile); exit(1); } for (i = 0; i < list.count; i++) fprintf(f, "%s\n", list.items[i]); fclose(f); kon_freeList(&list); push(&c, exePath("konpack")); push(&c, "-c"); push(&c, packFile); if (extra && *extra) pushWords(&c, extra); push(&c, "-b"); push(&c, assets); push(&c, "-l"); push(&c, listFile); if (!kon_runCmdSync(&c)) exit(1); kon_freeCmd(&c); } static void removeFile(const char *path) { if (kon_exists(path)) { printf("remove %s\n", path); kon_remove(path); } } static int endsWith(const char *s, const char *tail) { size_t n = strlen(s), t = strlen(tail); return n >= t && strcmp(s + n - t, tail) == 0; } static void targetClean(void) { KonList list = {0}; size_t i; if (kon_listFiles(objdir, &list)) { for (i = 0; i < list.count; i++) { if (endsWith(list.items[i], ".o") || endsWith(list.items[i], ".d") || endsWith(list.items[i], ".log")) kon_remove(list.items[i]); } kon_freeList(&list); } removeFile(exePath(PROGRAM)); for (i = 0; i < sizeof(toolNames) / sizeof(toolNames[0]); i++) removeFile(exePath(toolNames[i])); for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) removeFile(exePath(examples[i])); removeFile(exePath(editorName)); removeFile(kon_fmt("%s/data.kpk", objdir)); removeFile(kon_fmt("%s/assets.lst", objdir)); } /*** main ***/ /* a target that is a program in the object folder, like obj/sound, as make takes them. returns 0 when it is none */ static int targetFile(const char *t) { size_t d = strlen(objdir), n, e = strlen(exe), i; char *name; if (strncmp(t, objdir, d) != 0 || t[d] != '/') return 0; name = kon_fmt("%s", t + d + 1); n = strlen(name); if (e > 0 && n > e && strcmp(name + n - e, exe) == 0) name[n - e] = 0; for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) { if (strcmp(name, examples[i]) == 0) { targetExamples(name); return 1; } } for (i = 0; i < sizeof(toolNames) / sizeof(toolNames[0]); i++) { if (strcmp(name, toolNames[i]) == 0) { targetTools(); return 1; } } if (strcmp(name, editorName) == 0) { targetEditor(); return 1; } if (strcmp(name, PROGRAM) == 0) { targetAll(); return 1; } return 0; } static void addModuleList(const char *list) { char *copy = kon_fmt("%s", list), *w; while ((w = word(©, ',')) != NULL) addModule(w); } int main(int argc, char **argv) { const char *targets[16]; int targetCount = 0, i, jobsAsked = 0; KON_REBUILDSELF(argc, argv); setvbuf(stdout, NULL, _IOLBF, 0); kon_verbose = 0; for (i = 1; i < argc; i++) { const char *a = argv[i]; if (a[0] != '-' || a[1] == '\0') { if (targetCount >= 16) usage(); targets[targetCount++] = a; continue; } { const char *p; for (p = a + 1; *p; p++) { if (*p == 'd') { debug = 1; } else if (*p == 'D') { developer = 1; } else if (*p == 'v') { kon_verbose = 1; } else if (*p == 'p' || *p == 'm' || *p == 'o' || *p == 'j') { const char *value = p[1] ? p + 1 : (i + 1 < argc ? argv[++i] : NULL); if (!value) usage(); if (*p == 'p') platform = value; else if (*p == 'j') jobsAsked = atoi(value); else if (*p == 'o') objdir = value; else addModuleList(value); break; } else { usage(); } } } } if (targetCount == 0) targets[targetCount++] = "all"; for (i = 0; i < targetCount; i++) { if (isEditorTarget(targets[i])) addModuleList("ui,font,toml,fs,packw,image"); } maxJobs = jobsAsked > 0 ? jobsAsked : kon_cpuCount(); setup(); if (hasModule("debug") && !(debug || developer)) printf("note: debug module does nothing without -d or -D\n"); printf("===> platform %s, %s build, compiler %s, modules:", platform, debug ? "debug" : "release", compiler); if (chosenCount == 0) printf(" none"); for (i = 0; i < chosenCount; i++) printf(" %s", chosen[i]); printf("\n"); checkFlags(); for (i = 0; i < targetCount; i++) { const char *t = targets[i]; if (strcmp(t, "all") == 0) targetAll(); else if (strcmp(t, "run") == 0) targetRun(); else if (strcmp(t, "clean") == 0) targetClean(); else if (strcmp(t, "tools") == 0) targetTools(); else if (strcmp(t, "pack") == 0) targetPack(); else if (strcmp(t, "examples") == 0) targetExamples(NULL); else if (strcmp(t, editorName) == 0) targetEditor(); else if (!targetFile(t)) { fprintf(stderr, "build: unknown target '%s'\n", t); usage(); } } kon_freeTemp(); return 0; }