summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-17 13:47:41 +0800
committerKonAki <konaki@anokonaki.org>2026-10-17 13:47:41 +0800
commitff67f703fbdbb4d0239594213dfba6b6b0dcc702 (patch)
treef289fd296fe130bee20c4b6c9a0411ed2654ebbb
parent6a133b95c137dd1decfea4af9f66ecc7073baeb4 (diff)
build.c: the build script without sh or make
-rw-r--r--.gitignore3
-rw-r--r--build.c736
2 files changed, 739 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 2416a67..99b7055 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
obj/
+build
+build.exe
+build.exe.old
diff --git a/build.c b/build.c
new file mode 100644
index 0000000..18c12d8
--- /dev/null
+++ b/build.c
@@ -0,0 +1,736 @@
+/* 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
+ */
+
+#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", ""}, {"tile", ""}, {"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(&copy, ' ')) != 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] [-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\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(&copy, ' ')) != NULL) push(to, w);
+}
+
+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 ***/
+
+/* 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);
+}
+
+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);
+}
+
+static void run(KonCmd *c)
+{
+ if (!kon_runCmdSync(c)) exit(1);
+ kon_freeCmd(c);
+}
+
+/* returns 1 when it compiled something */
+static int compile(const char *source, const char *object, int ext, const KonCmd *extra)
+{
+ KonCmd c = {0};
+ const char *inputs[1];
+
+ inputs[0] = source;
+ if (!stale(object, inputs, 1, 1)) return 0;
+ kon_mkdirs(objdir);
+ printf("# compile %s\n", object);
+ fflush(stdout);
+ push(&c, compiler);
+ if (ext) extFlags(&c);
+ else ourFlags(&c, 1);
+ if (extra) pushAll(&c, extra);
+ push(&c, "-c");
+ push(&c, source);
+ push(&c, "-o");
+ push(&c, object);
+ run(&c);
+ return 1;
+}
+
+static int linkProgram(const char *output, const char **objects, size_t count, const KonCmd *flags, int withLibs)
+{
+ KonCmd c = {0};
+ size_t i;
+
+ if (!stale(output, objects, count, 0)) return 0;
+ printf("# link %s\n", output);
+ fflush(stdout);
+ push(&c, compiler);
+ push(&c, "-o");
+ push(&c, output);
+ if (flags) pushAll(&c, flags);
+ for (i = 0; i < count; i++) push(&c, objects[i]);
+ if (withLibs) pushAll(&c, &libs);
+ run(&c);
+ return 1;
+}
+
+/*** 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, "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) {
+ if (strcmp(platform, "windows") == 0) compiler = native ? "gcc" : "x86_64-w64-mingw32-gcc";
+ else compiler = "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("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");
+
+ newestHeader = a > b ? a : b;
+ }
+}
+
+/* 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];
+
+ push(&all, compiler);
+ 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) 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 buildObjects(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;
+ compile(units[i].source, obj, units[i].ext, NULL);
+ objects[objectCount++] = obj;
+ }
+}
+
+static void targetAll(void)
+{
+ buildObjects(1);
+ linkProgram(exePath(PROGRAM), objects, (size_t)objectCount, NULL, 1);
+}
+
+static void targetRun(void)
+{
+ KonCmd c = {0};
+
+ targetAll();
+ push(&c, exePath(PROGRAM));
+ if (!kon_runCmdSync(&c)) exit(1);
+ kon_freeCmd(&c);
+}
+
+static const char *examples[] = {"modelview", "sound", "ui", "platform", "dialog", "script", "corridor", "hills", "sectorview"};
+
+static void targetExamples(void)
+{
+ size_t i;
+
+ buildObjects(0);
+ for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) {
+ const char *obj = objPath(examples[i]);
+ const char *src = kon_fmt("examples/%s.c", examples[i]);
+ const char *all[OBJECTS_MAX + 16];
+ int n = 0, k;
+
+ compile(src, obj, 0, NULL);
+ all[n++] = obj;
+ for (k = 0; k < objectCount; k++) all[n++] = objects[k];
+ linkProgram(exePath(examples[i]), all, (size_t)n, NULL, 1);
+ }
+}
+
+/* a program made straight from some files, with the headers they read checked too. the converters do not link the engine */
+static void tool(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) && kon_newestIn("tools", ".h") <= kon_mtime(out)) return;
+ kon_mkdirs(objdir);
+ printf("# link %s\n", out);
+ fflush(stdout);
+ push(&c, compiler);
+ 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");
+ run(&c);
+}
+
+static void targetTools(void)
+{
+ KonCmd extra = {0};
+ const char *cgltfObj = objPath("cgltf");
+ const char *s[8];
+ KonCmd cg = {0};
+
+ /* 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";
+ tool("konpack", s, 2, &extra, 1, 0);
+ kon_freeCmd(&extra);
+
+ {
+ const char *in[1];
+
+ in[0] = "tools/cgltf.c";
+ if (stale(cgltfObj, in, 1, 0)) {
+ printf("# compile %s\n", cgltfObj);
+ fflush(stdout);
+ kon_mkdirs(objdir);
+ push(&cg, compiler);
+ 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);
+ run(&cg);
+ }
+ }
+ 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;
+ tool("konmodel", s, 6, &extra, 0, 1);
+ kon_freeCmd(&extra);
+
+ s[0] = "tools/mzsurvey.c";
+ tool("mzsurvey", s, 1, NULL, 0, 0);
+
+ push(&extra, "-Itools");
+ s[0] = "tools/mzscript.c";
+ s[1] = KONDIR "/konscript.c";
+ tool("mzscript", s, 2, &extra, 0, 0);
+ s[0] = "tools/kons2c.c";
+ tool("kons2c", s, 2, &extra, 0, 0);
+ kon_freeCmd(&extra);
+
+ s[0] = "tools/mzmap.c";
+ tool("mzmap", s, 1, NULL, 0, 0);
+
+ s[0] = "tools/mzdata.c";
+ s[1] = KONDIR "/konbattle.c";
+ tool("mzdata", s, 2, NULL, 0, 1);
+}
+
+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);
+ run(&c);
+}
+
+static void removeFile(const char *path)
+{
+ if (kon_exists(path)) {
+ printf("remove %s\n", path);
+ kon_remove(path);
+ }
+}
+
+static void targetClean(void)
+{
+ static const char *tools[] = {"konpack", "konmodel", "mzsurvey", "mzscript", "kons2c", "mzmap", "mzdata"};
+ KonList list = {0};
+ size_t i;
+
+ 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) kon_remove(list.items[i]);
+ }
+ kon_freeList(&list);
+ }
+ removeFile(exePath(PROGRAM));
+ for (i = 0; i < sizeof(tools) / sizeof(tools[0]); i++) removeFile(exePath(tools[i]));
+ for (i = 0; i < sizeof(examples) / sizeof(examples[0]); i++) removeFile(exePath(examples[i]));
+ removeFile(kon_fmt("%s/data.kpk", objdir));
+ removeFile(kon_fmt("%s/assets.lst", objdir));
+}
+
+/*** main ***/
+
+static void addModuleList(const char *list)
+{
+ char *copy = kon_fmt("%s", list), *w;
+
+ while ((w = word(&copy, ',')) != NULL) addModule(w);
+}
+
+int main(int argc, char **argv)
+{
+ const char *targets[16];
+ int targetCount = 0, i;
+
+ KON_REBUILDSELF(argc, argv);
+ setvbuf(stdout, NULL, _IOLBF, 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 == 'p' || *p == 'm' || *p == 'o') {
+ const char *value = p[1] ? p + 1 : (i + 1 < argc ? argv[++i] : NULL);
+
+ if (!value) usage();
+ if (*p == 'p') platform = value;
+ else if (*p == 'o') objdir = value;
+ else addModuleList(value);
+ break;
+ } else {
+ usage();
+ }
+ }
+ }
+ }
+ if (targetCount == 0) targets[targetCount++] = "all";
+
+ setup();
+ if (hasModule("debug") && !(debug || developer)) printf("note: debug module does nothing without -d or -D\n");
+ printf("===> platform %s, %s build, modules:", platform, debug ? "debug" : "release");
+ 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();
+ else {
+ fprintf(stderr, "build: unknown target '%s'\n", t);
+ usage();
+ }
+ }
+ kon_freeTemp();
+ return 0;
+}