1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# CBS — The C Build System
CBS is a single-header library for writing build scripts in C. The
philosophy behind this project is that the only tool you should ever need
to build your C projects is a C compiler. Not Make, not Cmake, not
Autoconf — just a C compiler.
Using C for your build system also has numerous advantages. C is
portable to almost any platform, C is a turing-complete language that
makes performing very specific build steps easy, and anyone working on
your C project already knows C.
CBS does not aim to be the most powerful and ultimate high-level API. It
simply aims to be a set of potentially useful functions and macros to
make writing a build script in C a bit easier. If there is functionality
you are missing, then add it. You’re a programmer aren’t you?
All functions and macros are documented in cbs.h.
CBS is very much inspired by Tsoding’s ‘Nob’.
## Example
Assuming you have a source file `my-file.c` — you can compile this build
script (called `build.c` for example) with `cc build.c` to bootstrap —
and then run `./a.out` to build the `my-file` binary linked against the
liblux library.
If you make any modifications to the build script *or* to the cbs.h
header, there is no need to manually recompile — the script will rebuild
itself.
```c
#include <stdlib.h>
#include "cbs.h"
#define needs_rebuild(dst, src) (!fexists(dst) || fmdolder(dst, src))
#define CC "cc"
#define CFLAGS "-Wall", "-Wextra", "-Werror", "-O3"
#define TARGET "my-file"
int
main(int argc, char **argv)
{
int ec;
cmd_t cmd = {0};
cbsinit(argc, argv);
rebuild();
if (!needs_rebuild(TARGET, TARGET ".c"))
return EXIT_SUCCESS;
cmdadd(&cmd, CC);
if (!pcquery(&cmd, "liblux", PKGC_LIBS | PKGC_CFLAGS))
cmdadd(&cmd, "-llux");
cmdadd(&cmd, CFLAGS, "-o", TARGET, TARGET ".c");
cmdput(cmd);
if ((ec = cmdexec(cmd)) != EXIT_SUCCESS)
diex("%s failed with exit-code %d", *cmd._argv, ec);
return EXIT_SUCCESS;
}
```
## Example With Threads
This is like the previous example, but you should compile with -lpthread. This
is not the most efficient way to build a multi-file project, but this is simply
for demonstration purposes.
```c
#include <errno.h>
#include <string.h>
#define CBS_PTHREAD
#include "cbs.h"
#define CC "cc"
#define CFLAGS "-Wall", "-Wextra", "-Werror", "-O3"
#define TARGET "my-file"
static const char *sources[] = {"foo.c", "bar.c", "baz.c"};
static const char *objects[] = {"foo.o", "bar.o", "baz.o"};
static void build(void *);
int
main(int argc, char **argv)
{
int ec, cpus;
bool needs_rebuild = false;
cmd_t cmd = {0};
tpool_t tp;
cbsinit(argc, argv);
rebuild();
if (!fexists(TARGET))
needs_rebuild = true;
else {
for (size_t i = 0; i < lengthof(sources); i++) {
if (fmdolder(TARGET, sources[i])) {
needs_rebuild = true;
break;
}
}
}
if (!needs_rebuild)
return EXIT_SUCCESS;
if ((cpus = nproc()) == -1) {
if (errno)
die("nproc");
/* System not properly supported; default to 8 threads */
cpus = 8;
}
tpinit(&tp, cpus);
for (size_t i = 0; i < lengthof(sources); i++)
tpenq(&tp, build, sources[i], NULL);
tpwait(&tp);
tpfree(&tp);
cmdadd(&cmd, CC, "-o", TARGET);
cmdaddv(&cmd, objects, lengthof(objects));
cmdput(cmd);
if ((ec = cmdexec(cmd)) != EXIT_SUCCESS)
diex("%s failed with exit-code %d", *cmd._argv, ec);
return EXIT_SUCCESS;
}
void
build(void *arg)
{
int ec;
char *dst, *src = arg;
cmd_t cmd = {0};
for (size_t i = 0; i < lengthof(sources); i++) {
/* All the sources and objects have 3 letter names + an extension */
if (strncmp(src, objects[i], 3) == 0) {
dst = objects[i];
break;
}
}
cmdadd(&cmd, CC, CFLAGS, "-o", dst, "-c", src);
cmdput(cmd);
if ((ec = cmdexec(cmd)) != EXIT_SUCCESS)
diex("%s failed with exit-code %d", *cmd._argv, ec);
free(cmd._argv);
}
```
|