Skip to content

Commit 84ad1cf

Browse files
Implemented lambda-cc.
lambda-cc is a cc wrapper which can be used by any project to have lambda-pp run on the source files before touching the compiler. lambda-cc will search the environment to find the correct CC to call after preprocessing of the source file has took place. lambda-cc searches the following directories for lambda-pp [./, lambdapp/, /bin, /usr/bin], and the following directories for CC [/bin, /usr/bin]. lambda-cc considers [cc, gcc, clang, pathcc, tcc] as valid CCs and will search for those. The environment variable LAMBDA_PP can also be used to specify lambda-pp's path.
1 parent 636ed01 commit 84ad1cf

File tree

5 files changed

+342
-14
lines changed

5 files changed

+342
-14
lines changed

Makefile

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,44 @@ DATADIR := $(PREFIX)/share
55
MANDIR := $(DATADIR)/man
66

77
CC ?= clang
8-
CFLAGS = -std=c11 -D_XOPEN_SOURCE=700 -Wall -Wextra -pedantic -O2
8+
CFLAGS = -std=c11 -D_BSD_SOURCE -Wall -Wextra -pedantic -O2
99
LDFLAGS =
10-
SOURCES = lambda.c
11-
OBJECTS = lambda.o
12-
LAMBDAPP = lambdapp
10+
PP_SOURCES = lambda-pp.c
11+
PP_OBJECTS = lambda-pp.o
12+
CC_SOURCES = lambda-cc.c
13+
CC_OBJECTS = lambda-cc.o
14+
LAMBDA_PP = lambda-pp
15+
LAMBDA_CC = lambda-cc
1316

14-
all: $(LAMBDAPP)
17+
all: $(LAMBDA_PP) $(LAMBDA_CC)
1518

16-
$(LAMBDAPP): $(OBJECTS)
17-
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
19+
$(LAMBDA_PP): $(PP_OBJECTS)
20+
$(CC) $(PP_OBJECTS) -o $@ $(LDFLAGS)
21+
22+
$(LAMBDA_CC): $(CC_OBJECTS)
23+
$(CC) $(CC_OBJECTS) -o $@ $(LDFLAGS)
1824

1925
.c.o:
2026
$(CC) -c $(CFLAGS) $< -o $@
2127

2228
install:
2329
install -d -m755 $(DESTDIR)$(BINDIR)
24-
install -m755 $(LAMBDAPP) $(DESTDIR)$(BINDIR)/$(LAMBDAPP)
30+
install -m755 $(LAMBDA_PP) $(DESTDIR)$(BINDIR)/$(LAMBDA_PP)
31+
install -m755 $(LAMBDA_CC) $(DESTDIR)$(BINDIR)/$(LAMBDA_CC)
2532
install -d -m755 $(DESTDIR)$(MANDIR)/man1
2633
install -m644 doc/lambdapp.1 $(DESTDIR)$(MANDIR)/man1/
2734

2835
uninstall:
29-
rm -f $(DESTDIR)$(BINDIR)/$(LAMBDAPP)
36+
rm -f $(DESTDIR)$(BINDIR)/$(LAMBDA_PP)
37+
rm -f $(DESTDIR)$(BINDIR)/$(LAMBDA_CC)
3038
rm -f $(DESTDIR)$(MANDIR)/man1/lambdapp.1
3139

32-
check: $(LAMBDAPP)
40+
check: $(LAMBDA_PP)
3341
rm -f tests/test.log
3442
$(MAKE) -C tests
3543

3644
clean:
37-
rm -f $(OBJECTS)
38-
rm -f $(LAMBDAPP)
45+
rm -f $(PP_OBJECTS)
46+
rm -f $(CC_OBJECTS)
47+
rm -f $(LAMBDA_PP)
48+
rm -f $(LAMBDA_CC)

lambda-cc.c

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <stdarg.h>
5+
#include <stdbool.h>
6+
7+
#include <sys/types.h>
8+
#include <dirent.h>
9+
10+
#define PP_ARRAY_COUNT(ARRAY) \
11+
(sizeof((ARRAY))/sizeof(*(ARRAY)))
12+
#define PP_ARRAY_FOR(NAME, ARRAY) \
13+
for (size_t NAME = 0; NAME < PP_ARRAY_COUNT(ARRAY); NAME++)
14+
15+
static void lcc_usage(const char *app) {
16+
fprintf(stderr, "%s usage: [cc options]\n", app);
17+
}
18+
19+
static void lcc_error(const char *message, ...) {
20+
fprintf(stderr, "error: ");
21+
va_list va;
22+
va_start(va, message);
23+
vfprintf(stderr, message, va);
24+
fprintf(stderr, "\n");
25+
va_end(va);
26+
}
27+
28+
typedef struct {
29+
const char *file;
30+
size_t index;
31+
bool cpp;
32+
} lcc_source_t;
33+
34+
typedef struct {
35+
const char *output;
36+
size_t index;
37+
bool aout;
38+
} lcc_output_t;
39+
40+
typedef struct {
41+
char *buffer;
42+
size_t used;
43+
size_t allocated;
44+
} lcc_string_t;
45+
46+
static bool lcc_string_init(lcc_string_t *string) {
47+
if (!(string->buffer = malloc(64)))
48+
return false;
49+
string->buffer[0] = '\0';
50+
string->used = 1; /* always use the null byte */
51+
string->allocated = 64;
52+
return 1;
53+
}
54+
55+
static bool lcc_string_resize(lcc_string_t *string) {
56+
size_t request = string->allocated * 2;
57+
void *attempt = realloc(string->buffer, request);
58+
if (!attempt)
59+
return false;
60+
string->allocated = request;
61+
string->buffer = attempt;
62+
return true;
63+
}
64+
65+
static bool lcc_string_appendf(lcc_string_t *string, const char *message, ...) {
66+
va_list va1, va2;
67+
va_start(va1, message);
68+
va_copy(va2, va1); /* Copy the list */
69+
size_t count = vsnprintf(NULL, 0, message, va1);
70+
while (string->used + count >= string->allocated) {
71+
if (!lcc_string_resize(string))
72+
return false;
73+
}
74+
va_end(va1);
75+
76+
va_start(va2, message);
77+
vsnprintf(string->buffer + string->used - 1, count + 1, message, va2);
78+
va_end(va2);
79+
string->used += count;
80+
return true;
81+
}
82+
83+
static void lcc_string_destroy(lcc_string_t *string) {
84+
free(string->buffer);
85+
}
86+
87+
static const char *lcc_lambdapp_find(void) {
88+
char *search;
89+
if ((search = getenv("LAMBDA_PP")))
90+
return search;
91+
92+
static const char *bins[] = {
93+
".", /* Try reliative to ourselfs as well */
94+
"/bin",
95+
"/usr/bin",
96+
97+
/* When lambdapp is included as a submodule in a project */
98+
"lambdapp"
99+
};
100+
101+
PP_ARRAY_FOR(bin, bins) {
102+
DIR *dir = opendir(bins[bin]);
103+
if (!dir)
104+
continue;
105+
106+
struct dirent *entry;
107+
while ((entry = readdir(dir))) {
108+
if (entry->d_type != DT_REG && entry->d_type != DT_LNK)
109+
continue;
110+
111+
if (!strcmp(entry->d_name, "lambda-pp")) {
112+
closedir(dir);
113+
return bins[bin];
114+
}
115+
}
116+
closedir(dir);
117+
}
118+
return NULL;
119+
}
120+
121+
static const char *lcc_compiler_find(void) {
122+
/* Try enviroment variables first */
123+
char *search;
124+
if ((search = getenv("CC")))
125+
return search;
126+
if ((search = getenv("CXX")))
127+
return search;
128+
129+
/* Start searching toolchain directories */
130+
static const char *bins[] = {
131+
"/bin",
132+
"/usr/bin",
133+
};
134+
135+
static const char *ccs[] = {
136+
"cc", "gcc", "clang", "pathcc", "tcc"
137+
};
138+
139+
PP_ARRAY_FOR(bin, bins) {
140+
DIR *dir = opendir(bins[bin]);
141+
if (!dir)
142+
continue;
143+
144+
PP_ARRAY_FOR(cc, ccs) {
145+
/* Scan the directory for one of the CCs */
146+
struct dirent *entry;
147+
while ((entry = readdir(dir))) {
148+
/* Ignore things which are not files */
149+
if (entry->d_type != DT_REG && entry->d_type != DT_LNK)
150+
continue;
151+
if (!strcmp(entry->d_name, ccs[cc])) {
152+
closedir(dir);
153+
return ccs[cc];
154+
}
155+
}
156+
}
157+
closedir(dir);
158+
}
159+
160+
return NULL;
161+
}
162+
163+
static bool lcc_source_find(int argc, char **argv, lcc_source_t *source) {
164+
static const char *exts[] = {
165+
/* C file extensions */
166+
".c",
167+
168+
/* C++ file extensions */
169+
".cc", ".cx", ".cxx", ".cpp",
170+
};
171+
172+
for (int i = 0; i < argc; i++) {
173+
PP_ARRAY_FOR(ext, exts) {
174+
char *find = strstr(argv[i], exts[ext]);
175+
if (!find)
176+
continue;
177+
178+
/* It could be named stupidly like foo.c.c so we scan the whole filename
179+
* until we reach the end (when strcmp will succeed).
180+
*/
181+
while (find) {
182+
/* Make sure it's the end of the string */
183+
if (!strcmp(find, exts[ext])) {
184+
source->index = i;
185+
source->file = argv[i];
186+
source->cpp = (ext >= 1); /* See table of sources above for when this is valid. */
187+
return true;
188+
}
189+
find = strstr(find + strlen(exts[ext]), exts[ext]);
190+
}
191+
}
192+
}
193+
return false;
194+
}
195+
196+
static bool lcc_output_find(int argc, char **argv, lcc_output_t *output) {
197+
for (int i = 0; i < argc; i++) {
198+
if (strcmp(argv[i], "-o"))
199+
continue;
200+
if (i + 1 >= argc) /* expecting output */
201+
return false;
202+
output->index = i;
203+
output->output = argv[i + 1];
204+
return true;
205+
}
206+
return false;
207+
}
208+
209+
int main(int argc, char **argv) {
210+
argc--;
211+
argv++;
212+
213+
if (!argc) {
214+
lcc_usage(argv[-1]);
215+
return 1;
216+
}
217+
218+
const char *cc = lcc_compiler_find();
219+
if (!cc) {
220+
lcc_error("Couldn't find a compiler");
221+
return 1;
222+
}
223+
224+
const char *lambdapp = lcc_lambdapp_find();
225+
if (!lambdapp) {
226+
lcc_error("Couldn't find lambda-pp");
227+
return 1;
228+
}
229+
230+
/* Get the arguments */
231+
lcc_string_t args_before; /* before -o */
232+
lcc_string_t args_after; /* after -o */
233+
if (!lcc_string_init(&args_before)) {
234+
lcc_error("Out of memory");
235+
return 1;
236+
}
237+
if (!lcc_string_init(&args_after)) {
238+
lcc_error("Out of memory");
239+
lcc_string_destroy(&args_before);
240+
return 1;
241+
}
242+
243+
/* Find the source file */
244+
lcc_source_t source;
245+
if (!lcc_source_find(argc, argv, &source)) {
246+
lcc_error("Couldn't find source file on command line");
247+
goto find_error;
248+
}
249+
250+
/* Find the output file */
251+
lcc_output_t output = { .aout = false };
252+
if (!lcc_output_find(argc, argv, &output)) {
253+
/* not found? default to a.out */
254+
output.output = "a.out";
255+
output.aout = true;
256+
}
257+
258+
/* Stop at the -o */
259+
size_t stop = output.aout ? (size_t)argc : output.index;
260+
for (size_t i = 0; i < stop; i++) {
261+
/* Ignore the source file */
262+
if (i == source.index)
263+
continue;
264+
if (!lcc_string_appendf(&args_before, "%s ", argv[i]))
265+
goto args_oom;
266+
}
267+
/* Trim the trailing whitespace */
268+
if (args_before.used >= 2)
269+
args_before.buffer[args_before.used - 2] = '\0';
270+
271+
/* Handle anythng after the -o */
272+
stop += 2; /* skip -o and <output> */
273+
size_t count = argc;
274+
if (stop != count) {
275+
for (size_t i = stop; i < count; i++) {
276+
if (!lcc_string_appendf(&args_after, "%s ", argv[i]))
277+
goto args_oom;
278+
}
279+
}
280+
/* Trim trailing whitespace */
281+
if (args_after.used >= 2)
282+
args_after.buffer[args_after.used - 2] = '\0';
283+
284+
/* Build the shell call */
285+
lcc_string_t shell;
286+
if (!lcc_string_init(&shell))
287+
goto args_oom;
288+
289+
const char *lang = source.cpp ? "c++" : "c";
290+
if (!lcc_string_appendf(&shell, "%s/lambda-pp %s | %s -x%s %s - -o %s %s",
291+
lambdapp, source.file, cc, lang, args_before.buffer, output.output, args_after.buffer))
292+
goto shell_oom;
293+
294+
int attempt = 0;
295+
#ifndef _NDEBUG
296+
/* Call the shell */
297+
attempt = system(shell.buffer);
298+
#else
299+
printf("%s\n", shell.buffer);
300+
#endif
301+
302+
lcc_string_destroy(&shell);
303+
lcc_string_destroy(&args_before);
304+
lcc_string_destroy(&args_after);
305+
306+
return attempt;
307+
308+
shell_oom:
309+
lcc_string_destroy(&shell);
310+
args_oom:
311+
lcc_error("Out of memory");
312+
find_error:
313+
lcc_string_destroy(&args_before);
314+
lcc_string_destroy(&args_after);
315+
return 1;
316+
}
317+

lambda.c renamed to lambda-pp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ static void parse_error(lambda_source_t *source, const char *message, ...) {
149149
vsnprintf(buffer, sizeof(buffer), message, va);
150150
va_end(va);
151151
fprintf(stderr, "%s:%zu error: %s\n", source->file, source->line, buffer);
152+
fflush(stderr);
152153
}
153154

154155
static bool parse_open(lambda_source_t *source, FILE *handle) {

tests/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CC ?= clang
2-
LAMBDAPP := ../lambdapp
2+
LAMBDAPP := ../lambda-pp
33

44
# Since we create an 'obj/' directory and BSD's make defaults to doing weird
55
# things depending on the stars and weather etc. make sure we don't end up

tests/runtests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/bash
22

33
VERBOSE="${VERBOSE:-0}"
4-
LAMBDAPP="../lambdapp"
4+
LAMBDAPP="../lambda-pp"
55
CC="${CC:-cc}"
66
CFLAGS="${CFLAGS} -std=c11"
77

0 commit comments

Comments
 (0)