int main(void) {
build("./");
- cflags = NONE;
compile("main");
-
- lflags = NONE;
load('x', "main", LIST("main"));
return EXIT_SUCCESS;
```console
> cc -o build build.c
> build
+cc -c -o build.o build.c
+cc -o build build.o
cc -c -o main.o main.c
cc -o main main.o
> main
```console
> build
-cc -c -o build.o build.c
+cc -c -o build.o build.c
cc -o build build.o
cc -c -o main.o main.c
cc -o main main.o
void compile(char *src);
```
-The `compile()` function is given a single source file to compile and will generate an object file with the same name. In general, file extensions in your build files are optional as they can usually be inferred based on the function being called. This has the added benefit of being able to reuse lists of file names for compiling and linking.
+The `compile()` function is given a single source file to compile and will generate an object file with the same name. In general, file extensions in your build files are optional as they can usually be inferred based on the function being called. This has the added benefit of allowing for the reuse of lists of file names for compiling and linking.
-Before you run `compile()`, the global `cflags` variable has to be initialized with a list of flags to pass to the compiler. If no flags are needed, than the `NONE` macro should be used; otherwise the `LIST()` macro can be used. The value of `cflags` doesn't change between calls to `compile()`, so the same flags can be used for multiple compilations without having to reinitialize.
+The global `cflags` variable can be assigned a list of flags to pass to the compiler using the `LIST()` macro. The value of `cflags` is maintained between calls to `compile()`, so the same flags can automatically be used for multiple translation units. If you want to clear the compiler flags, the `NONE` macro can be used.
```c
cflags = LIST("-Wall", "-O3");
The third argument is a list of object files and libraries that will be linked to create the target file. Here, libraries need to include their file extension so as to disambiguate them from object files. `LIST()` can be used here too since this list must also be NULL-terminated.
-The global `lflags` variable is used to pass flags to the linker, with a behavior identical to `cflags`.
+The global `lflags` variable is used to pass flags to the linker in a way similar to `cflags`.
```c
lflags = LIST("-lm");
void build(char *path);
```
-The `build()` function allows one build executable to run another build executable. The name of the directory that contains the build executable being run is passed to the function by a relative or absolute path. You can think of this function as changing to that directory and running the build executable located therein. `build()` will only recompile the build file it if it can't find the build executable in that directory.
+The `build()` function allows one build executable to run another build executable. The name of the directory that contains the build executable being run is passed to the function by a relative or absolute path. You can think of this function as changing to that directory and running the build executable located therein. `build()` will only recompile the build file if the build executable doesn't exist in that directory.
-If the current directory is passed to `build()`, then it will *recompile its own build file* before rerunning *itself*. Thus, including a statement like `build("./");` at the beginning of your build file means you don't have to manually recompile that build file every time you modify it.
+If the current directory is passed to `build()`, then it will *recompile its own build file* before rerunning *itself*. Thus, including a statement like `build("./");` at the beginning of your build file means you don't have to manually recompile that build file whenever you modify it.
+
+`build()` uses the `cflags` and `lflags` variables whenever it recompiles build files. The value of each variable is maintained between calls to `build()`.
```c
build("./");
build("../../src/");
+
+cflags = LIST("-Iinclude/");
build("/usr/local/project/src/");
```
extern char **environ;
-char **cflags, **lflags;
+char **cflags = NONE, **lflags = NONE;
void *allocate(size_t s) {
void *r;
case 's':
path = "/usr/bin/ar";
*p++ = "ar";
- *p++ = "-r";
+ *p++ = "-rc";
target = extend(target, ".a");
a = p = (fp = p) + f;
break;
}
void build(char *path) {
- char *absolute, *current, **c, **l;
- struct stat exe, src;
- int exists, self, leave;
+ char *absolute, *current;
pid_t cpid;
+ int self, exists, restart;
+ struct stat src, obj, exe;
if (!(absolute = realpath(path, NULL)))
err(EXIT_FAILURE, "Unable to resolve `%s'", path);
if (!(current = getcwd(NULL, 0)))
err(EXIT_FAILURE, "Unable to check current directory");
+ cpid = 0;
- if ((self = strcmp(absolute, current) == 0)) {
- if (stat("build.c", &src) == -1)
- err(EXIT_FAILURE, "Unable to stat `build.c'");
- if ((leave = (exists = stat("build", &exe) == 0) && after(exe, src))
- && utimensat(AT_FDCWD, "build.c", NULL, 0) == -1)
- err(EXIT_FAILURE, "Unable to update `build.c' modification time");
- } else {
+ if (!(self = strcmp(absolute, current) == 0)) {
if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork");
else if (cpid) await(cpid, "run", "build");
printf("cd %s/\n", path);
if (chdir(path) == -1)
err(EXIT_FAILURE, "Unable to change directory to `%s'", path);
-
- exists = stat("build", &exe) == 0;
- leave = cpid;
}
- free(current);
free(absolute);
+ free(current);
- if (leave) return;
- if (self || !exists) {
- c = cflags;
- l = lflags;
+ if (cpid) return;
- cflags = NONE;
- compile("build");
+ if (stat(current = "build.c", &src) == -1)
+ err(EXIT_FAILURE, "Unable to stat `build.c'");
+ if (stat("build.o", &obj) == -1) obj.TIME = (struct timespec){0};
+ if (!(exists = stat("build", &exe) != -1)) exe.TIME = (struct timespec){0};
- lflags = NONE;
+ if ((restart = after(src, obj) || after(exe, obj))
+ && after(src, exe) && (self || !exists)) {
+ compile("build");
load('x', "build", LIST("build"));
-
- cflags = c;
- lflags = l;
+ if (self) current = "build.o";
}
-
- run("!build", LIST("build"), "run", "build");
+ if (utimensat(AT_FDCWD, current, NULL, 0) == -1)
+ err(EXIT_FAILURE, "Unable to update `%s' modification time", current);
+ if (restart) run("!build", LIST("build"), "run", "build");
}