Parsee/configure.c

727 lines
18 KiB
C

/* 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 <sys/stat.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <libgen.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
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;
}
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));
if (!ret)
{
return NULL;
}
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 char *
strchrn(char *s, char c)
{
s = strchr(s, c);
return s ? s+1 : NULL;
}
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 i;
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");
if (!makefile)
{
fprintf(stderr, "Couldn't create Makefile.\n");
fprintf(stderr, "This isn't good, actually.\n");
return EXIT_FAILURE;
}
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);
}