Skip to content

Instantly share code, notes, and snippets.

@brimonk
Created August 9, 2019 04:59
Show Gist options
  • Save brimonk/f9d07cc0929f725f11af989203584588 to your computer and use it in GitHub Desktop.
Save brimonk/f9d07cc0929f725f11af989203584588 to your computer and use it in GitHub Desktop.
A Trial Run Building Program to Replace Makefiles
/*
* Brian Chrzanowski
* Fri Aug 09, 2019 00:58
*
* Brian's Cool Builder (bcb)
*
* TODO
* 1. Add more wait end conditions, in 'recompile'
* 2. Add the ".d" dependency files
* 3. Add a mechanism to clean up the source tree
* 4. Add mechanisms to have sub-build targets
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define CC "gcc"
#define CARGS "-Wall -g3 -march=native"
#define LARGS "-lm -ldl"
#define TARGET "build"
#define ARRSIZE(x) (sizeof((x))/sizeof((x)[0]))
#define MKOBJNAME(x) (*(strstr((x),".c")+1)='o')
#define BUFSIZE 4096
int recompile_check(char *src, char *obj);
int recompile(char *src, char *obj);
int relink(char **objects, int objlen);
int main(int argc, char **argv)
{
int rc, i, relinkflg;
char objname[BUFSIZE];
char *sources[] = { "build.c" };
char *objects[ARRSIZE(sources)];
relinkflg = rc = 0;
// init objects
for (i = 0; i < ARRSIZE(objects); i++) {
objects[i] = strdup(sources[i]);
MKOBJNAME(objects[i]);
}
// compare the timestamp of current files with what we currently have
// recompile and re-link everything that's different
for (i = 0; i < ARRSIZE(sources); i++) {
memset(objname, 0, sizeof objname);
memcpy(objname, sources[i], strlen(sources[i]));
MKOBJNAME(objname); // get the name of the obj file
switch (recompile_check(sources[i], objname)) {
case -1:
fprintf(stderr, "File %s doesn't exist! Check the sources arr\n", sources[i]);
return -1;
case 0:
continue;
case 1:
relinkflg = 1;
rc = recompile(sources[i], objname);
break;
default: // shouldn't get here!
assert(0);
break;
}
}
if (relinkflg)
relink(objects, ARRSIZE(objects));
// cleanup, cleanup, everybody everywhere...
for (i = 0; i < ARRSIZE(objects); i++)
free(objects[i]);
return rc;
}
/* recompile_check : determine if a file needs to be recompiled */
int recompile_check(char *src, char *obj)
{
struct stat srcstat, objstat;
struct timespec srctime, objtime;
// first, check if both files even exist
if (access(src, F_OK) == -1)
return -2; // source doesn't exist
if (access(obj, F_OK) == -1)
return 1; // recompile
// now figure out their timestamps
stat(src, &srcstat);
stat(obj, &objstat);
// copy the timevalues
srctime = srcstat.st_mtim;
objtime = objstat.st_mtim;
// compare the two, remember, we want to recompile only if src is newer than
// the corresponding object file
if (srctime.tv_sec > objtime.tv_sec)
if (srctime.tv_nsec > objtime.tv_nsec)
return 1;
return 0;
}
/* recompile : recompile src into obj */
int recompile(char *src, char *obj)
{
// WARN (brian) this function isn't robust (obviously)
pid_t imforked;
int len, i, wstatus;
char prog[BUFSIZE];
char args[BUFSIZE];
char *argv[32];
char *s;
// create the path arg for exec
len = snprintf(prog, sizeof prog, "%s", CC);
assert(len != sizeof prog);
// len = snprintf(args, sizeof args, "%s -o %s %s", CARGS, obj, src);
len = snprintf(args, sizeof args, "%s -c %s -o %s %s", CC, CARGS, obj, src);
assert(len != sizeof args);
printf("%s\n", args);
// not robust if arguments have quoted strings
for (i = 0, s = strtok(args, " "); i < ARRSIZE(argv) - 1 && s; i++, s = strtok(NULL, " ")) {
argv[i] = s;
}
argv[i] = NULL;
// now we can fork and exec
imforked = fork();
if (imforked) {
wait(&wstatus);
return WEXITSTATUS(wstatus);
} else {
umask(S_IWGRP | S_IWOTH);
wstatus = execvp(prog, argv);
if (wstatus < 0) {
fprintf(stderr, "We Died Compiling!\n");
exit(-1);
}
}
return -1;
}
/* relink : create our relink command, then execute it */
int relink(char **objects, int objlen)
{
// WARN (brian) this function isn't robust (obviously)
// be careful, the way you link with gcc is pretty freaking jank,
// program, cargs, objects, then linker flags
// ugh
pid_t imforked;
int len, i, wstatus;
char prog[BUFSIZE];
char args[BUFSIZE];
char *argv[32];
char *s;
// create the path arg for exec
len = snprintf(prog, sizeof prog, "%s", CC);
assert(len != sizeof prog);
// len = snprintf(args, sizeof args, "%s -o %s %s", CARGS, obj, src);
len = snprintf(args, sizeof args, "%s %s -o %s", CC, CARGS, TARGET);
assert(len != sizeof args);
for (i = 0; i < objlen; i++) { // print all the objects into the args buf
len += snprintf(args + len, sizeof(args) - len, " %s", objects[i]);
assert(len != sizeof args); // link line too long :)
}
// now we have to print the linker flags at the end of args
len += snprintf(args + len, sizeof(args) - len, " %s", LARGS);
assert(len != sizeof args); // link line too long :)
printf("%s\n", args);
// not robust if arguments have quoted strings
for (i = 0, s = strtok(args, " "); i < ARRSIZE(argv) - 1 && s; i++, s = strtok(NULL, " ")) {
argv[i] = s;
}
argv[i] = NULL;
// now we can fork and exec
imforked = fork();
if (imforked) {
wait(&wstatus);
return WEXITSTATUS(wstatus);
} else {
umask(S_IWGRP | S_IWOTH);
wstatus = execvp(prog, argv);
if (wstatus < 0) {
fprintf(stderr, "We Died Linking!\n");
exit(-1);
}
}
return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment