diff --git a/.forgejo/workflows/check-gcc.yaml b/.forgejo/workflows/check-gcc.yaml index 38d32ca..8f6f4a2 100644 --- a/.forgejo/workflows/check-gcc.yaml +++ b/.forgejo/workflows/check-gcc.yaml @@ -24,6 +24,7 @@ jobs: uses: actions/checkout@v3 - name: Build Parsee run: | + cc configure.c -o configure && ./configure echo 'CFLAGS=-Werror -Wall -Wextra -pedantic' >> build.conf cat build.conf make && make ayadoc diff --git a/.gitignore b/.gitignore index 6104661..05919ab 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ parsee .* data data/* +Makefile +configure tools/out tools/out/* diff --git a/Makefile b/Makefile deleted file mode 100644 index c1125b3..0000000 --- a/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -# (GNU)Makefile for building Parsee -# ================================ -# TODO: Consider making something akin to a configure script that checks -# for dependencies, or maybe even use *autoconf* (the devil!) - - -# =========================== Parsee Flags ============================= - -include build.conf - -REPOSITORY=$(shell git remote get-url origin) - -# =========================== Compilation Flags ============================ -CYTO_INC ?=/usr/local/include/ # Where Cytoplasm's include path is - # located. -CYTO_LIB ?=/usr/local/lib # Where's Cytoplasm's library is - # located. -PREFIX ?=/usr/local - -AYAS=ayaya -ETC=etc -FCFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" $(CFLAGS) -FLDFLAGS=-L $(CYTO_LIB) -lCytoplasm $(LDFLAGS) -AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" -# ============================ Compilation ================================= -SRC_FILES:=$(shell find $(SOURCE) -name '*.c') $(shell find $(ETC)/media -name '*.png') -OBJ_FILES:=${subst $(ETC)/media/,$(OBJECT)/,${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.png, %.o, $(patsubst %.c, %.o, $(SRC_FILES)))}} - -CPP_FILES:=$(shell find $(INCLUDES) -name '*.h') -AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))} - -all: utils binary - -binary: $(OBJ_FILES) - $(CC) $(FLDFLAGS) $(OBJ_FILES) -o $(BINARY) -tags: $(SRC_FILES) - @ctags --recurse $(SOURCE)/ - -clean: - rm -rf $(OBJECT) $(BINARY) $(AYAS) - -$(OBJECT)/%.o: $(ETC)/media/%.png - @mkdir -p $(shell dirname "$@") - @echo "const char media_$(shell basename $< .png)[] =" > $@.c - @base64 $< | \ - sed -e 's/^\(.*\)$$/ "\1"/' | \ - sed -e '$$ s/^\(.*\)$$/\1;/' >> $@.c - $(CC) -c $(FCFLAGS) $@.c -o $@ -$(OBJECT)/%.o: $(SOURCE)/%.c - @mkdir -p $(shell dirname "$@") - $(CC) -c $(FCFLAGS) $< -o $@ - -utils: - (cd tools && make) - -ayadoc: utils $(AYA_FILES) - -$(AYAS)/%.html: $(INCLUDES)/%.h - @mkdir -p $(shell dirname "$@") - tools/out/aya $(AFLAGS) -i $< -o $@ - - -# Installs everything. -install: binary utils ayadoc install_setup install_parsee install_tools install_aya install_man - @echo Installed $(NAME) to $(PREFIX)! - -install_setup: - install -dm755 "$(PREFIX)/bin" - install -dm755 "$(PREFIX)/share/doc" - install -dm755 "$(PREFIX)/man" - -install_parsee: - install -Dm755 "$(BINARY)" "$(PREFIX)/bin/$(BINARY)" - -TOOLS:=$(shell find 'tools/out' -name '*') -ITOOL:=${subst tools/out/,$(PREFIX)/bin/,$(patsubst tools/out/%, tools/out/$(BINARY)-%, $(TOOLS))} -install_tools: $(ITOOL) -$(PREFIX)/bin/$(BINARY)-%: tools/out/% - install -Dm755 "$<" "$@" - -IHTML:=${subst $(AYAS)/,$(PREFIX)/share/doc/$(BINARY)/,$(AYA_FILES)} -install_aya: $(IHTML) -$(PREFIX)/share/doc/$(BINARY)/%: $(AYAS)/% - install -Dm644 "$<" "$@" - -MPAGE:=$(shell find 'etc/man' -name '*.*') -PPAGE:=${subst etc/man/,$(PREFIX)/man/,$(MPAGE)} -install_man: $(PPAGE) -$(PREFIX)/man/%: etc/man/% - install -Dm644 "$<" "$@" diff --git a/build.c b/build.c deleted file mode 100644 index b80a7d9..0000000 --- a/build.c +++ /dev/null @@ -1,863 +0,0 @@ -/* build.c - Simple, POSIX, non-Cytoplasm utility to build out - * the entirety of Parsee from scratch, without any Makefiles. - * - * The main reason why this tool exists is merely because the - * current Make-based building is not POSIX compliant, and I - * am simply not porting it to be. The Makefile shall stay - * supported however, but if possible, use build.c. - * To run it, just build it with: - * cc build.c -o /tmp/build && /tmp/build - * - * TODO: Parallel jobs, CFLAGS and LDFLAGS. - * Note that this bit is formatted differently. - * -------------------------------- - * LICENSE: CC0 - * Written-By: LDA [lda@freetards.xyz] [@fourier:ari.lt] */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_BUILD_PATH "build.conf" -#define DEFAULT_COMPILER "cc" -#define Compiler(info) (info.basic.cc ? info.basic.cc : DEFAULT_COMPILER) - -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; -} - -typedef struct buildinfo { - struct basic { - char *codename; - char *version; - - char *name; - - char *binary; - - char *src; - char *inc; - char *obj; - - char *cflags, *ldflags; - - char *cc; - } basic; - - char *repo; -} buildinfo_t; -static void -destroy_buildinfo(buildinfo_t *info) -{ - if (!info) - { - return; - } - -#define FreeIfExistent(v) do \ - { \ - if (v) \ - { \ - free(v); \ - v = NULL; \ - } \ - } while (0) - - FreeIfExistent(info->basic.codename); - FreeIfExistent(info->basic.version); - FreeIfExistent(info->basic.ldflags); - FreeIfExistent(info->basic.binary); - FreeIfExistent(info->basic.cflags); - FreeIfExistent(info->basic.name); - FreeIfExistent(info->basic.src); - FreeIfExistent(info->basic.inc); - FreeIfExistent(info->basic.obj); - FreeIfExistent(info->basic.cc); - FreeIfExistent(info->repo); -} - -static char * -cmd_stdout(char *cmd) -{ - FILE *f; - char *line = NULL; - size_t size; - if (!cmd) - { - return NULL; - } - if (!(f = popen(cmd, "r"))) - { - return NULL; - } - - getline(&line, &size, f); - pclose(f); - return line; -} -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 void -mkdir_rec(char *dir) -{ - char tmp[PATH_MAX]; - char *start; - if (!dir || strlen(dir) >= PATH_MAX - 1) - { - return; - } - - memset(tmp, '\0', sizeof(tmp)); - for (start = dir; start && *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'; - - { - memcpy(tmp, dir, start - dir); - tmp[strlen(tmp) - 1] = '\0'; - mkdir(tmp, 0770); - } - } -} -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 time_t -mod_date(char *file) -{ - struct stat s; - if (stat(file, &s)) - { - return (time_t) 0; - } - return s.st_mtime; -} -static bool -build_file(char *cSource, buildinfo_t info, bool isTool) -{ - str_array_t *args, *cflags; - char *oFileName, *objPath, *oFile; - int ret, i = 0; - int srclen; - - char *code, *name, *vers, *repo; - if (!cSource) - { - return false; - } - - if (!isTool) - { - srclen = strncmp(cSource, "src", 3) ? - strlen(info.basic.obj) + 1 : - strlen(info.basic.src) + 1 ; - objPath = string_cat(info.basic.obj, "/"); - oFile = string_rep_ext(cSource + srclen, ".c", ".o"); - oFileName = string_cat(objPath, oFile); - } - else - { - srclen = 6; - objPath = string_dup("tools/out/"); - oFile = string_rep_ext(cSource + srclen, ".c", ""); - oFileName = string_cat(objPath, oFile); - } - mkdir_rec(oFileName); - - if (!isTool && (mod_date(cSource) < mod_date(oFileName))) - { - free(objPath); - free(oFileName); - free(oFile); - return true; - } - - args = str_array_create(); - if (!isTool) - { - printf("\tCC %s...\n", cSource); - } - - str_array_add(args, Compiler(info)); - if (isTool) - { - str_array_add(args, "-lCytoplasm"); - str_array_add(args, "-I."); - } - else - { - str_array_add(args, "-c"); - } - str_array_add(args, cSource); - - cflags = split(info.basic.cflags); - for (i = 0; i < str_array_len(cflags); i++) - { - str_array_add(args, str_array_get(cflags, i)); - } - str_array_free(cflags); - - str_array_add(args, "-o"); - str_array_add(args, oFileName); - - str_array_add(args, "-I"); - str_array_add(args, info.basic.inc); - str_array_add(args, "-I"); - str_array_add(args, info.basic.src); - - { - char *pre = string_cat("\"", info.basic.version); - char *pos = string_cat(pre, "\""); - vers = string_cat("-DVERSION=", pos); - str_array_add(args, vers); - - free(pos); - free(pre); - } - { - char *pre = string_cat("\"", info.basic.name); - char *pos = string_cat(pre, "\""); - name = string_cat("-DNAME=", pos); - str_array_add(args, name); - - free(pos); - free(pre); - } - { - char *pre = string_cat("\"", info.basic.codename); - char *pos = string_cat(pre, "\""); - code = string_cat("-DCODE=", pos); - str_array_add(args, code); - - free(pos); - free(pre); - } - { - char *pre = string_cat("\"", info.repo); - char *pos = string_cat(pre, "\""); - repo = string_cat("-DREPOSITORY=", pos); - str_array_add(args, repo); - - free(pos); - free(pre); - } - - str_array_add(args, NULL); - - ret = exec_code(Compiler(info), args->values); - - str_array_free(args); - free(objPath); - free(oFileName); - free(oFile); - free(vers); - free(name); - free(code); - free(repo); - return ret == EXIT_SUCCESS; -} -static bool -finalise_file(str_array_t *arr, buildinfo_t info) -{ - str_array_t *flags, *ldflags; - size_t i; - bool ret = true; - if (!arr) - { - return false; - } - - flags = str_array_create(); - str_array_add(flags, Compiler(info)); - - ldflags = split(info.basic.cflags); - for (i = 0; i < str_array_len(ldflags); i++) - { - str_array_add(flags, str_array_get(ldflags, i)); - } - str_array_free(ldflags); - - str_array_add(flags, "-lCytoplasm"); - - for (i = 0; i < str_array_len(arr); i++) - { - char *file = str_array_get(arr, i); - if (file) - { - size_t srclen = strncmp(file, "src", 3) ? - strlen(info.basic.obj) + 1 : - strlen(info.basic.src) + 1 ; - char *objPath = string_cat(info.basic.obj, "/"); - char *oFile = string_rep_ext(file + srclen, ".c", ".o"); - char *oFileName = string_cat(objPath, oFile); - - str_array_add(flags, oFileName); - - free(oFileName); - free(oFile); - free(objPath); - } - } - - str_array_add(flags, "-o"); - str_array_add(flags, info.basic.binary); - str_array_add(flags, NULL); - ret = exec_code(Compiler(info), flags->values); - - str_array_free(flags); - 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 (*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; - } - if (!strchr(name, '.') && - strcmp(name, "out") && - strcmp(name, "Makefile")) - { - str_array_t *sub; - char *d1 = string_cat(dir, "/"); - char *d2 = string_cat(d1, name); - size_t i; - - 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; -} - -static char * -process_png(char *png, buildinfo_t info) -{ - size_t i = 0; - char *symbol; - char *cFile, *oFile; - char *pcFile, *poFile; - char *pcFile1, *poFile1; - char *filename, *symbol1; - char *arguments[8] = { 0 }; - if (!png) - { - return NULL; - } - - if (!(filename = strchrl(png, '/'))) - { - return NULL; - } - filename++; - - pcFile1= string_cat(info.basic.obj, "/"); - pcFile = string_cat(pcFile1, filename); - cFile = string_rep_ext(pcFile, ".png", ".c"); - free(pcFile); - free(pcFile1); - - poFile1= string_cat(info.basic.obj, "/"); - poFile = string_cat(poFile1, filename); - oFile = string_rep_ext(poFile, ".png", ".o"); - free(poFile); - free(poFile1); - - symbol1 = string_rep_ext(filename, ".png", ""); - symbol = string_cat("media_", symbol1); - free(symbol1); - - mkdir_rec(oFile); - mkdir_rec(cFile); - - /* Build the image into Base64 */ - arguments[i++] = "tools/out/b64"; - arguments[i++] = png; - arguments[i++] = symbol; - arguments[i++] = cFile; - arguments[i++] = NULL; - - if (exec_code(arguments[0], arguments)) - { - free(symbol); - free(cFile); - free(oFile); - return NULL; - } - - /* Compile it out */ - i = 0; - arguments[i++] = Compiler(info); - arguments[i++] = "-c"; - arguments[i++] = cFile; - arguments[i++] = "-o"; - arguments[i++] = oFile; - arguments[i++] = NULL; - - if (exec_code(arguments[0], arguments)) - { - free(symbol); - free(cFile); - free(oFile); - return NULL; - } - - free(symbol); - free(oFile); - return cFile; -} -/* Builds the entirety of Parsee. */ -static int -main_build(int argc, char *argv[]) -{ - buildinfo_t info = { 0 }; - FILE *buildinfo = NULL; - char *line = NULL; - size_t size, i; - ssize_t nread; - str_array_t *sources, *images; - - /* Step 1: Get all basic information from build.conf */ - if (!(buildinfo = fopen(DEFAULT_BUILD_PATH, "r"))) - { - printf("error: cannot open '%s'\n", DEFAULT_BUILD_PATH); - goto fail; - } - while ((nread = getline(&line, &size, buildinfo)) != -1) - { - char *eq = strchr(line, '='); - char *end = strchr(line, '\n'); - - char *key, *val; - if (!eq) - { - free(line); - printf( - "error: line in '%s' does not contain '='\n", - DEFAULT_BUILD_PATH - ); - goto fail; - } - - /* Set delimiters */ - *eq = '\0'; - if (end) *end = '\0'; - - key = line; - val = eq + 1; - - /* Now, we have KV mappings. */ -#define If(k, v, d) do \ - { \ - if (!strcmp(key, k) && !info.v) \ - { \ - info.v = string_dup(val); \ - printf("%s: %s\n", d, val); \ - } \ - } while (0) - - If("CODE", basic.codename, "Codename"); - If("NAME", basic.name, "Name"); - If("VERSION", basic.version, "Version"); - If("BINARY", basic.binary, "Binary name"); - - If("CFLAGS", basic.cflags, "C compiler arguments"); - If("LDFLAGS", basic.ldflags, "Linker arguments"); - - If("INCLUDES", basic.inc, "Include path"); - If("SOURCE", basic.src, "Source path"); - If("OBJECT", basic.obj, "Object path"); - If("CC", basic.cc, "Compiler"); - } - if (line) - { - free(line); - line = NULL; - } - - fclose(buildinfo); - buildinfo = NULL; - - /* Step 2: Get all information from commands. */ - if (!(info.repo = cmd_stdout("git remote get-url origin"))) - { - printf("error: cannot find origins url\n"); - goto fail; - } - info.repo = trim_nl(info.repo); - - if (argc >= 2 && !strcmp(argv[1], "clean")) - { - char *args[8]; - size_t i; - unlink(info.basic.binary); - - args[i++] = "rm"; - args[i++] = "-r"; - args[i++] = info.basic.obj; - args[i++] = NULL; - exec_code(args[0], args); - goto end; - } - - - /* Step 3: Build all utilities. */ - sources = collect_sources("tools", true, ".c"); - for (i = 0; i < str_array_len(sources); i++) - { - char *file = str_array_get(sources, i); - printf("\tTOOL %s...\n", file); - if (!build_file(file, info, true)) - { - str_array_free(sources); - goto fail; - } - } - str_array_free(sources); - - /* Step 4: Build all media files. */ - sources = collect_sources(info.basic.src, true, ".c"); - images = collect_sources("etc/media", true, ".png"); - for (i = 0; i < str_array_len(images); i++) - { - char *file = str_array_get(images, i); - char *out; - - out = process_png(file, info); - if (!out) - { - str_array_free(images); - str_array_free(sources); - goto fail; - } - printf("\tPNG %s\n", file); - str_array_add(sources, out); - free(out); - } - str_array_free(images); - - /* Step 5: Build all of Parsee itself */ - for (i = 0; i < str_array_len(sources); i++) - { - char *file = str_array_get(sources, i); - if (!build_file(file, info, false)) - { - str_array_free(sources); - goto fail; - } - } - printf("\tLINK\n"); - if (!finalise_file(sources, info)) - { - str_array_free(sources); - goto fail; - } - str_array_free(sources); - /* TODO: Step 6: Build every Ayadoc */ -end: - destroy_buildinfo(&info); - return EXIT_SUCCESS; -fail: - if (buildinfo) - { - fclose(buildinfo); - buildinfo = NULL; - } - destroy_buildinfo(&info); - return EXIT_FAILURE; -} - -int -main(int argc, char *argv[]) -{ - /* TODO: Multiple flags(build/install/ayadoc/...) */ - if ((argc - 1) < 1) - { - /* No arguments, let's just build. */ - return main_build(argc, argv); - } - - if (!strcmp(argv[1], "build") || - !strcmp(argv[1], "clean")) - { - return main_build(argc, argv); - } - - printf("%s: unknown verb: %s\n", argv[0], argv[1]); - return EXIT_FAILURE; -} diff --git a/configure.c b/configure.c new file mode 100644 index 0000000..4d67d07 --- /dev/null +++ b/configure.c @@ -0,0 +1,622 @@ +/* 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 + +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; + if (!cmd) + { + return NULL; + } + if (!(f = popen(cmd, "r"))) + { + return NULL; + } + + getline(&line, &size, f); + pclose(f); + return line; +} +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 (*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; + } + if (!strchr(name, '.') && + strcmp(name, "out") && + strcmp(name, "Makefile")) + { + str_array_t *sub; + char *d1 = string_cat(dir, "/"); + char *d2 = string_cat(d1, name); + size_t i; + + 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 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; + str_array_t *sources, *images, *utils, *aya; + + if (strchr(repo, '\n')) + { + *(strchr(repo, '\n')) = '\0'; + } + + + makefile = fopen("Makefile", "w"); + fprintf(makefile, "# Autogenerated POSIX Makefile\n"); + fprintf(makefile, "# Ideally do not touch.\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) -L $(CYTO_LIB) -lCytoplasm $(LDFLAGS)"); + write_objects(makefile, sources); + write_images(makefile, images); + fprintf(makefile, " -o $(BINARY)\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\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, "\t$(CC) -c -Isrc -Isrc/include -I$(CYTO_INC) "); + fprintf(makefile, "-DVERSION=\"\\\"$(VERDION)\\\"\" "); + fprintf(makefile, "-DNAME=\"\\\"$(NAME)\\\"\" "); + fprintf(makefile, "-DCODE=\"\\\"$(CODE)\\\"\" "); + fprintf(makefile, "-DREPOSITORY=\"\\\"%s\\\"\" ", repo); + fprintf(makefile, "$(CFLAGS) $< -o $@\n"); + } + 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 $< 'media_%s' '%s.c'\n", sym, obj); + fprintf(makefile, "\t$(CC) -c %s.c -o $@\n", 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) $(CFLAGS) -lCytoplasm $< -o $@\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 $< -o $@\n" + ); + } + + 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); +}