From 8ce69d082941be3b9be76c6615482ff50255dd75 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 2 Sep 2024 21:42:40 +0200 Subject: [PATCH] [ADD/WIP] build.c script, raise to 0.1 --- Makefile | 4 - build.c | 785 ++++++++++++++++++++++++++++++++++++++++++++++++++++ build.conf | 6 +- tools/b64.c | 54 ++++ 4 files changed, 844 insertions(+), 5 deletions(-) create mode 100644 build.c create mode 100644 tools/b64.c diff --git a/Makefile b/Makefile index cfcc15f..347e44c 100644 --- a/Makefile +++ b/Makefile @@ -20,12 +20,8 @@ CYTO_LIB ?=/usr/local/lib # Where's Cytoplasm's library is # located. PREFIX ?=/usr/local -SOURCE=src -OBJECT=build AYAS=ayaya ETC=etc -INCLUDES=src/include -CC ?=cc CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -O3 -g -Wall -Werror LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -g AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" diff --git a/build.c b/build.c new file mode 100644 index 0000000..27f29af --- /dev/null +++ b/build.c @@ -0,0 +1,785 @@ +/* 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 + +#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 *cc; + } basic; + + char *repo; +} buildinfo_t; +static void +destroy_buildinfo(buildinfo_t *info) +{ + if (!info) + { + return; + } + + if (info->basic.codename) + { + free(info->basic.codename); + info->basic.codename = NULL; + } + if (info->basic.version) + { + free(info->basic.version); + info->basic.version = NULL; + } + if (info->basic.name) + { + free(info->basic.name); + info->basic.name = NULL; + } + if (info->basic.binary) + { + free(info->basic.binary); + info->basic.binary = NULL; + } + if (info->basic.src) + { + free(info->basic.src); + info->basic.src = NULL; + } + if (info->basic.inc) + { + free(info->basic.inc); + info->basic.inc = NULL; + } + if (info->basic.obj) + { + free(info->basic.obj); + info->basic.obj = NULL; + } + + if (info->repo) + { + free(info->repo); + info->repo = NULL; + } +} + +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; + } +} + + +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 bool +build_file(char *cSource, buildinfo_t info, bool isTool) +{ + char *args[16] = { 0 }; + 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); + + args[i++] = Compiler(info); + if (isTool) + { + args[i++] = "-lCytoplasm"; + args[i++] = "-I."; + } + else + { + args[i++] = "-c"; + } + args[i++] = cSource; + args[i++] = "-o"; + args[i++] = oFileName; + + args[i++] = "-I"; + args[i++] = info.basic.inc; + args[i++] = "-I"; + args[i++] = info.basic.src; + + { + char *pre = string_cat("\"", info.basic.version); + char *pos = string_cat(pre, "\""); + vers = string_cat("-DVERSION=", pos); + args[i++] = vers; + + free(pos); + free(pre); + } + { + char *pre = string_cat("\"", info.basic.name); + char *pos = string_cat(pre, "\""); + name = string_cat("-DNAME=", pos); + args[i++] = name; + + free(pos); + free(pre); + } + { + char *pre = string_cat("\"", info.basic.codename); + char *pos = string_cat(pre, "\""); + code = string_cat("-DCODE=", pos); + args[i++] = code; + + free(pos); + free(pre); + } + { + char *pre = string_cat("\"", info.repo); + char *pos = string_cat(pre, "\""); + repo = string_cat("-DREPOSITORY=", pos); + args[i++] = repo; + + free(pos); + free(pre); + } + + args[i++] = NULL; + + ret = exec_code(Compiler(info), 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; + size_t i; + bool ret = true; + if (!arr) + { + return false; + } + + flags = str_array_create(); + str_array_add(flags, Compiler(info)); + + 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("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); + + + /* 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); + printf("\tCC %s...\n", file); + 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); + /* Step 6: Build every Ayadoc */ + + /* Step 7: Good! */ + + 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/...) */ + return main_build(argc, argv); +} diff --git a/build.conf b/build.conf index 3e8cbd7..b64d366 100644 --- a/build.conf +++ b/build.conf @@ -1,4 +1,8 @@ CODE=tomboyish-bridges-adventure NAME=Parsee -VERSION=0.0.1 +VERSION=0.1.0 BINARY=parsee +SOURCE=src +INCLUDES=src/include +OBJECT=build +CC=tcc diff --git a/tools/b64.c b/tools/b64.c new file mode 100644 index 0000000..53c08e8 --- /dev/null +++ b/tools/b64.c @@ -0,0 +1,54 @@ +/* b64.c - Generates a C string symbol stored as Base64 + * ============================================================ + * Under CC0, as its a rather useful example of a KappaChat tool. + * See LICENSE for more information about Parsee's licensing. */ + +#include +#include +#include +#include +#include + +#include + +int +Main(Array *args, HashMap *env) +{ + char *file = ArrayGet(args, 1); + char *symbol = ArrayGet(args, 2); + char *out = ArrayGet(args, 3); + + Stream *inStream; + uint8_t *bytes; + size_t len; + int o; + + char *b64; + Stream *outStream; + + if (!file || !symbol || !out) + { + return EXIT_FAILURE; + } + + inStream = StreamOpen(file, "rb"); + bytes = NULL; + len = 0; + while ((o = StreamGetc(inStream)) != EOF) + { + bytes = Realloc(bytes, len + 1); + bytes[len++] = o; + } + StreamClose(inStream); + + b64 = Base64Encode((const char *) bytes, len); + Free(bytes); + outStream = StreamOpen(out, "w"); + StreamPrintf(outStream, "const char %s[] = ", symbol); + StreamPrintf(outStream, "\"%s\";\n", b64); + Free(b64); + StreamFlush(outStream); + StreamClose(outStream); + + return EXIT_SUCCESS; +}