/* * carmel-build * Copyright (c) 2024 Jeremy Baxter. */ #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include "util.h" typedef struct { char *name; char *description; char *version; char *builder; } manifest; static const char *buildBase = "/tmp/carmel-"; static const char *defaultOutputFile = "auto"; static const char *defaultPackageName = "void"; static const char *defaultPackageVersion = "0"; static const char *defaultPackageBuilder = "builder.sh"; static bool oflag, vflag; #define WARNV(...) if (vflag) warnx(__VA_ARGS__) void builder(const char *script) { FILE *proc; ssize_t ret; int fd; char ch[1]; proc = popen("sh", "w"); fd = open(script, O_RDONLY); if (!proc) err(1, "cannot execute shell"); if (fd == -1) err(1, "cannot open %s", script); while ((ret = read(fd, &ch, 1)) > 0) { fwrite(ch, sizeof(char), 1, proc); } if (ret == -1) err(1, "cannot read from %s", script); pclose(proc); } #define KEY(name, def) strdup(iniparser_getstring(ini, (":" name), (def))) manifest makeManifest(const char *path) { dictionary *ini; manifest m; ini = iniparser_load(path); m.name = KEY("name", defaultPackageName); m.description = KEY("description", ""); m.version = KEY("version", defaultPackageVersion); m.builder = KEY("builder", defaultPackageBuilder); iniparser_freedict(ini); return m; } #undef KEY void freeManifest(manifest *m) { free(m->name); free(m->description); free(m->version); free(m->builder); } void makeDirectory(const char *path) { if (mkdir(path, 0710) == -1) err(1, "cannot create directory '%s'", path); } void sh(const char *command) { int ret; ret = system(command); if (ret == -1) err(1, "system"); if (WEXITSTATUS(ret) != 0) errx(1, "command failed with exit code %d: %s", ret, command); } char * workingDirectory(void) { char *buffer; size_t len; len = 64; do { buffer = malloc(len * sizeof(char)); len *= 2; } while (getcwd(buffer, len) == NULL); return buffer; } void writeTo(const char *path, const char *contents) { int fd, i; fd = open(path, O_WRONLY | O_CREAT, 0644); for (i = 0; contents[i] != 0; i++) write(fd, (char[]){contents[i]}, 1); } int main(int argc, char **argv) { manifest m; char *buildDirectory; char *outputFile; char *startDirectory; size_t buildDirectoryLen, outputFileLen; char ch; outputFileLen = strlen(defaultOutputFile) + 1; outputFile = malloc(outputFileLen * sizeof(char)); strbcpy(outputFile, defaultOutputFile, outputFileLen); while ((ch = getopt(argc, argv, "C:o:v")) != -1) { switch (ch) { case 'C': changeDirectory(optarg); break; case 'o': oflag = 1; outputFileLen = strlen(optarg) + 1; outputFile = malloc(outputFileLen * sizeof(char)); strbcpy(outputFile, optarg, outputFileLen); break; case 'v': vflag = 1; break; } } if (argc - optind != 1) { fputs("usage: carmel-build [-v] [-C directory] [-o output] manifest\n", stderr); return 1; } startDirectory = workingDirectory(); if (!exists(argv[optind])) errx(1, "cannot open %s", argv[optind]); m = makeManifest(argv[optind]); if (strcmp(m.name, defaultPackageName) == 0) warnx("manifest name not provided; using '%s'", m.name); if (strcmp(m.version, defaultPackageVersion) == 0) warnx("manifest version not provided; using '%s'", m.version); if (strlen(m.builder) == 0) errx(1, "manifest builder not provided; using '%s'", m.builder); if (strcmp(outputFile, defaultOutputFile) == 0) { char *extension = ".tar.gz"; outputFileLen = strlen(m.name) + 1 + strlen(m.version) + strlen(extension) + 1; outputFile = malloc(outputFileLen * sizeof(char)); snprintf(outputFile, outputFileLen, "%s-%s%s", m.name, m.version, extension); } WARNV("building %s", outputFile); /* PIDs up to 7 digits */ buildDirectoryLen = strlen(buildBase) + 8; buildDirectory = malloc(buildDirectoryLen * sizeof(char)); snprintf(buildDirectory, buildDirectoryLen, "%s%d", buildBase, getpid()); WARNV("entering build origin"); makeDirectory(buildDirectory); changeDirectory(buildDirectory); makeDirectory("origin"); changeDirectory("origin"); { char *buffer; size_t len; len = strlen(startDirectory) + 1 + strlen(m.builder) + 1; buffer = malloc(len * sizeof(char)); snprintf(buffer, len, "%s/%s", startDirectory, m.builder); copyFile(buffer, "builder.sh"); } WARNV("executing builder"); setenv("out", buildDirectory, 1); builder("builder.sh"); changeDirectory(".."); sh("rm -fr origin/"); WARNV("writing to carmel-meta"); makeDirectory("carmel-meta"); changeDirectory("carmel-meta"); writeTo("name", m.name); writeTo("description", m.description); writeTo("version", m.version); WARNV("making tarball"); setenv("out", outputFile, 1); setenv("start", startDirectory, 1); setenv("build", buildDirectory, 1); changeDirectory(buildDirectory); sh("tar -czf \"$out\" *"); sh("mv \"$out\" \"$start\"/"); changeDirectory(startDirectory); sh("rm -fr \"$build\""); freeManifest(&m); free(outputFile); free(startDirectory); return 0; }