/* configure.c - Simple, POSIX, non-Cytoplasm utility to build out * a Parsee Makefile. * To run it, just build it with: * cc configure.c -o configure && configure * -------------------------------- * LICENSE: CC0 * Written-By: LDA [lda@freetards.xyz] [@fourier:ari.lt] */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char * string_rep_ext(char *in, char *ext1, char *ext2) { char *end; size_t inLen; if (!in || !ext1 || !ext2) { return NULL; } inLen = strlen(in); end = inLen + in; while (end >= in) { if (!strcmp(end, ext1)) { size_t cpyLen = end - in; size_t extLen = strlen(ext2); char *ret = malloc(cpyLen + extLen + 1); memcpy(ret, in, cpyLen); memcpy(ret + cpyLen, ext2, extLen); ret[cpyLen + extLen] = '\0'; return ret; } end--; } return NULL; } static char * string_dup(char *in) { char *out; size_t len; if (!in) { return NULL; } len = strlen(in); out = malloc(len + 1); memcpy(out, in, len); out[len] = '\0'; return out; } static char * string_cat(char *in1, char *in2) { char *out; size_t len1, len2; if (!in1) { return string_dup(in2); } if (!in2) { return string_dup(in1); } len1 = strlen(in1); len2 = strlen(in2); out = malloc(len1 + len2 + 1); memcpy(out, in1, len1); memcpy(out + len1, in2, len2); out[len1 + len2] = '\0'; return out; } static char * trim_nl(char *in) { char *tc; if (!in) { return NULL; } while ((tc = strrchr(in, '\n'))) { *tc = '\0'; } return in; } typedef struct str_array { char **values; size_t quantity; } str_array_t; static str_array_t * str_array_create(void) { str_array_t *ret = malloc(sizeof(*ret)); ret->values = NULL; ret->quantity = 0; return ret; } static void str_array_free(str_array_t *arr) { size_t i; if (!arr) { return; } for (i = 0; i < arr->quantity; i++) { if (arr->values[i]) free(arr->values[i]); } if (arr->values) free(arr->values); free(arr); } static void str_array_set(str_array_t *arr, size_t i, char *str) { if (!arr) { return; } if (i >= arr->quantity) { size_t size = (i+1) * sizeof(*arr->values); size_t j; arr->values = realloc(arr->values, size); for (j = arr->quantity; j <= i; j++) { arr->values[j] = NULL; } arr->quantity = i + 1 ; } if (arr->values[i]) free(arr->values[i]); arr->values[i] = string_dup(str); } static void str_array_add(str_array_t *arr, char *str) { if (!arr) { return; } str_array_set(arr, arr->quantity, str); } static char * str_array_get(str_array_t *arr, size_t i) { if (!arr) { return NULL; } return i < arr->quantity ? arr->values[i] : NULL; } static size_t str_array_len(str_array_t *arr) { return arr ? arr->quantity : 0; } static char * cmd_stdout(char *cmd) { FILE *f; char *line = NULL; size_t size; int result; if (!cmd) { goto failure; } if (!(f = popen(cmd, "r"))) { goto failure; } getline(&line, &size, f); result = pclose(f); if (result < 0) { perror("pclose(3)"); goto failure; } else if (result) { fprintf(stderr, "command exited with status %d: %s\n", result, cmd); goto failure; } return line; failure: free(line); return NULL; } static int exec_code(char *program, char *argv[]) { pid_t forkRet; if (!program || !argv) { return -1; } forkRet = fork(); if (forkRet == 0) { /* Child */ execvp(program, argv); exit(0); } else { /* Parent */ int status; if (waitpid(forkRet, &status, 0) == -1) { return -1; } return status; } /* We're not meant to ever be there, but TCC is stupid. */ return -1; } static char * strchrn(char *s, char c) { s = strchr(s, c); return s ? s+1 : NULL; } static char * strchrl(char *s, char c) { char *s1 = NULL; while ((s = strchr(s, c))) { s1 = s; s++; } return s1; } static str_array_t * split(char *dir) { str_array_t *ret; char *start; if (!dir || strlen(dir) >= PATH_MAX - 1) { return NULL; } ret = str_array_create(); for (start = dir; start; start = strchrn(start, '/')) { char subtmp[PATH_MAX]; char *next = strchr(start, '/'); if (!next) { next = start + strlen(start); } memcpy(subtmp, start, next - start); subtmp[next - start] = '\0'; str_array_add(ret, subtmp); } return ret; } static str_array_t * collect_sources(char *dir, bool head, char *ext) { DIR *handle; str_array_t *ret; struct dirent *ent; if (!dir) { return NULL; } ret = str_array_create(); handle = opendir(dir); if (!handle) { printf("error: cannot open directory '%s'\n", dir); return ret; } while ((ent = readdir(handle))) { char *name = ent->d_name; if (!strcmp(name, ".") || !strcmp(name, "..")) continue; if (strlen(name) > strlen(ext) && !strcmp(name + strlen(name) - strlen(ext), ext)) { char *d1 = string_cat(dir, "/"); char *na = string_cat(d1, name); str_array_add(ret, na); free(d1); free(na); continue; } { char *d1 = string_cat(dir, "/"); char *d2 = string_cat(d1, name); size_t i; struct stat sb; if (stat(d2, &sb)) { fprintf(stderr, "stat(2) %s: %s\n", d2, strerror(errno)); free(d2); free(d1); } else if (S_ISDIR(sb.st_mode)) { str_array_t *sub = collect_sources(d2, false, ext); for (i = 0; i < str_array_len(sub); i++) { char *file = str_array_get(sub, i); str_array_add(ret, file); } str_array_free(sub); } free(d2); free(d1); } } closedir(handle); return ret; } /* Builds the entirety of Parsee. */ static void write_objects(FILE *makefile, str_array_t *sources) { size_t i; if (!makefile || !sources) { return; } for (i = 0; i < str_array_len(sources); i++) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".c", ".o"); char *obj = string_cat("build", ofl + 3); fprintf(makefile, " %s", obj); free(ofl); free(obj); } } static void write_images(FILE *makefile, str_array_t *sources) { size_t i; if (!makefile || !sources) { return; } for (i = 0; i < str_array_len(sources); i++) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".png", ".o"); char *obj = string_cat("build", ofl + 3 + 1 + 5); fprintf(makefile, " %s", obj); free(ofl); free(obj); } } static void write_executable(FILE *makefile, str_array_t *sources) { size_t i; if (!makefile || !sources) { return; } for (i = 0; i < str_array_len(sources); i++) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".c", ""); char *obj = string_cat("tools/out", ofl + 5); fprintf(makefile, " %s", obj); free(ofl); free(obj); } } static void write_ayas(FILE *makefile, str_array_t *sources) { size_t i; if (!makefile || !sources) { return; } for (i = 0; i < str_array_len(sources); i++) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".h", ".html"); char *obj = string_cat("$(AYAYAS)", ofl + 3 + 1 + 7); fprintf(makefile, " %s", obj); free(ofl); free(obj); } } static void analyse_dependencies(FILE *makefile, char *src) { FILE *source = fopen(src, "r"); char *lineptr = NULL; char *dn = strdup(src), *dirn; size_t len = 0; dirn = dirname(dn); while (getline(&lineptr, &len, source) != -1) { char *corrptr = lineptr, *nl, *chevron, *quote, *cutoff; char *basedir, *srcdir, *incdir, *tmp; struct stat statinfo; /* try to parse the line */ if ((nl = strchr(corrptr, '\n'))) { *nl = '\0'; } while (*corrptr && isblank(*corrptr)) { corrptr++; } if (strncmp(corrptr, "#include \"", 10) && strncmp(corrptr, "#include <", 10)) { continue; } corrptr += 10; chevron = strchr(corrptr, '>'); chevron = chevron ? chevron : corrptr + strlen(corrptr); quote = strchr(corrptr, '"'); quote = quote ? quote : corrptr + strlen(corrptr); cutoff = chevron < quote ? chevron : quote; *cutoff = '\0'; /* if we found something, try to resolve it */ tmp = string_cat(dirn, "/"); basedir = string_cat(tmp, corrptr); free(tmp); if (!stat(basedir, &statinfo)) { fprintf(makefile, " %s", basedir); free(basedir); continue; } free(basedir); srcdir = string_cat("src/", corrptr); if (!stat(srcdir, &statinfo)) { fprintf(makefile, " %s", srcdir); free(srcdir); continue; } free(srcdir); incdir = string_cat("src/include/", corrptr); if (!stat(incdir, &statinfo)) { fprintf(makefile, " %s", incdir); free(incdir); continue; } free(incdir); } free(lineptr); free(dn); fclose(source); } static int main_build(int argc, char *argv[]) { FILE *makefile; char *repo = cmd_stdout("git remote get-url origin"); size_t size, i; ssize_t nread; bool with_static = false, with_lmdb = false; int opt; str_array_t *sources, *images, *utils, *aya; if (repo) { char *lf = strchr(repo, '\n'); if (lf) { *lf = '\0'; } } else { repo = strdup("N/A"); } while ((opt = getopt(argc, argv, "sl")) != -1) { switch (opt) { case 's': with_static = true; break; case 'l': with_lmdb = with_static; with_static = true; break; } } makefile = fopen("Makefile", "w"); fprintf(makefile, "# Autogenerated POSIX Makefile from Parsee\n"); fprintf(makefile, "# Ideally do not touch, unless you have a very "); fprintf(makefile, "good reason to do it. \n\n"); fprintf(makefile, ".POSIX: \n"); fprintf(makefile, "include build.conf\n\n"); fprintf(makefile, "AYAYAS=ayaya\n"); fprintf(makefile, "CYTO_LIB=/usr/local/lib\n"); fprintf(makefile, "CYTO_INC=/usr/local/include\n"); fprintf(makefile, "ETC=etc\n\n"); fprintf(makefile, "all: utils binary ayadoc\n\n"); /* Create all objects */ sources = collect_sources("src", true, ".c"); images = collect_sources("etc/media", true, ".png"); fprintf(makefile, "binary:"); write_objects(makefile, sources); write_images(makefile, images); fprintf(makefile, "\n\t"); { fprintf(makefile, "$(CC) -o $(BINARY)"); if (with_static) { fprintf(makefile, " -static"); } fprintf(makefile, " -L $(CYTO_LIB)"); write_objects(makefile, sources); write_images(makefile, images); if (with_static) { fprintf(makefile, " -lm -lpthread -lmbedtls -lmbedx509 -lmbedcrypto -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto"); if (with_lmdb) fprintf(makefile, " -llmdb"); } fprintf(makefile, " -lCytoplasm $(LDFLAGS)\n"); } /* Write rules for every source */ for (i = 0; i < str_array_len(sources); i++) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".c", ".o"); char *obj = string_cat("build", ofl + 3); fprintf(makefile, "%s: %s", obj, src); analyse_dependencies(makefile, src); fprintf(makefile, "\n"); { str_array_t *s = split(obj); ssize_t j; fprintf(makefile, "\t@mkdir -p "); for (j = 0; j < str_array_len(s) - 1; j++) { fprintf(makefile, "%s/", str_array_get(s, j)); } fprintf(makefile, "\n"); str_array_free(s); fprintf(makefile, "\t$(CC) -c -Isrc -Isrc/include -I$(CYTO_INC) "); fprintf(makefile, "-DVERSION=\"\\\"$(VERSION)\\\"\" "); fprintf(makefile, "-DNAME=\"\\\"$(NAME)\\\"\" "); fprintf(makefile, "-DCODE=\"\\\"$(CODE)\\\"\" "); fprintf(makefile, "-DREPOSITORY=\"\\\"%s\\\"\" ", repo); fprintf(makefile, "$(CFLAGS) %s -o %s\n", src, obj); } free(ofl); free(obj); } for (i = 0; i < str_array_len(images); i++) { char *src = str_array_get(images, i); char *ofl = string_rep_ext(src, ".png", ".o"); char *obj = string_cat("build", ofl + 3 + 1 + 5); char *cfl = string_cat("build", src + 3 + 1 + 5); fprintf(makefile, "%s: %s\n", obj, src); { str_array_t *s = split(obj); char *sym; ssize_t j; fprintf(makefile, "\t@mkdir -p "); for (j = 0; j < str_array_len(s) - 1; j++) { fprintf(makefile, "%s/", str_array_get(s, j)); } fprintf(makefile, "\n"); sym = j != -1 ? str_array_get(s, j): NULL; sym = string_rep_ext(sym, ".o", ""); str_array_free(s); fprintf(makefile, "\ttools/out/b64 %s 'media_%s' '%s.c'\n", src, sym, obj); fprintf(makefile, "\t$(CC) -c %s.c -o %s\n", obj, obj); free(sym); } free(obj); free(ofl); free(cfl); } /* Build utilities */ utils = collect_sources("tools", true, ".c"); fprintf(makefile, "utils:"); write_executable(makefile, utils); fprintf(makefile, "\n"); for (i = 0; i < str_array_len(utils); i++) { char *src = str_array_get(utils, i); char *ofl = string_rep_ext(src, ".c", ""); char *obj = string_cat("tools/out", ofl + 5); fprintf(makefile, "%s: %s\n", obj, src); { fprintf(makefile, "\t@mkdir -p tools/out\n"); fprintf(makefile, "\t$(CC) -o %s", obj); if (with_static) { fprintf(makefile, " -static"); } fprintf(makefile, " %s", src); fprintf(makefile, " -I $(CYTO_INC)"); fprintf(makefile, " -L $(CYTO_LIB)"); if (with_static) { fprintf(makefile, " -lm -lpthread -lmbedtls -lmbedx509 -lmbedcrypto"); fprintf(makefile, " -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto"); if (with_lmdb) fprintf(makefile, " -llmdb"); } fprintf(makefile, " -lCytoplasm $(CFLAGS)\n"); } free(ofl); free(obj); } /* Build Aya */ aya = collect_sources("src/include", true, ".h"); fprintf(makefile, "ayadoc:"); write_ayas(makefile, aya); fprintf(makefile, "\n"); for (i = 0; i < str_array_len(aya); i++) { char *src = str_array_get(aya, i); char *ofl = string_rep_ext(src, ".h", ".html"); char *obj = string_cat("$(AYAYAS)", ofl + 3 + 1 + 7); fprintf(makefile, "%s: %s\n", obj, src); { str_array_t *s = split(obj); ssize_t j; fprintf(makefile, "\t@mkdir -p "); for (j = 0; j < str_array_len(s) - 1; j++) { fprintf(makefile, "%s/", str_array_get(s, j)); } fprintf(makefile, "\n"); str_array_free(s); fprintf(makefile, "\ttools/out/aya " "-C etc/ayadoc/style.css " "-p $(NAME) " "-i %s -o %s\n", src, obj ); } free(ofl); free(obj); } fprintf(makefile, "install-parsee: binary\n"); { fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/bin\n"); fprintf(makefile, "\tcp $(BINARY) $(PREFIX)/bin\n"); fprintf(makefile, "\tchmod 755 $(PREFIX)/bin/$(BINARY)\n"); } fprintf(makefile, "install-tools: utils\n"); fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/bin\n"); for (i = 0; i < str_array_len(utils); i++) { char *tool = str_array_get(utils, i); char *ofl = string_rep_ext(tool, ".c", ""); char *obj = string_cat("tools/out", ofl + 5); char *bna = basename(obj); fprintf(makefile, "\tcp %s $(PREFIX)/bin/parsee-%s\n", obj, bna); fprintf(makefile, "\tchmod 755 $(PREFIX)/bin/parsee-%s\n", bna); free(ofl); free(obj); } fprintf(makefile, "install-aya: ayadoc\n"); fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/aya/$(BINARY)\n"); fprintf(makefile, "\tcp -R $(AYAYAS)/* $(PREFIX)/aya/$(BINARY)\n"); fprintf(makefile, "install-man:\n"); fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/share/man\n"); fprintf(makefile, "\tcp -R etc/man/* $(PREFIX)/share/man\n"); fprintf(makefile, "install: " "install-parsee " "install-tools " "install-aya " "install-man\n" ); fprintf(makefile, "clean:\n\trm -rf ayaya build $(BINARY) tools/out\n"); str_array_free(sources); str_array_free(images); str_array_free(utils); str_array_free(aya); fflush(makefile); fclose(makefile); free(repo); return EXIT_SUCCESS; } int main(int argc, char *argv[]) { return main_build(argc, argv); }