main shrub/shinobi / src / submake.c
  1#include "internal.h"
  2
  3#include <ctype.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7/* recursive make handling: sort of done naively, doesent always work.
  8 *
  9 * TODO:
 10 * idea for a better implementation: create a new binary. shin replaces $(MAKE) with the 
 11 * name of this new binary that we create. this binary is a program that simply expands the 
 12 * graph of the submake and returns some sort of graph IR. shin pre-emptively runs all submake 
 13 * recipes with the new helper binary sibstituted in as $(MAKE), then it reads the output 
 14 * for each one and parses the graph IR into subgraphs. because we just shell out pre-emnptively,
 15 * we don't have to implement a small shell parser (like the one down below) and it will work
 16 * in 99% if not 100% of cases. */
 17
 18static void
 19tokview(const char *s, const char **out, size_t *n)
 20{
 21	const char *p;
 22	size_t len;
 23
 24	p = s;
 25	while (*p == '(')
 26		p++;
 27	len = strlen(p);
 28	while (len > 0 && (p[len - 1] == ')' || p[len - 1] == ';'))
 29		len--;
 30	*out = p;
 31	*n = len;
 32}
 33
 34static int
 35tokeq(const char *s, const char *lit)
 36{
 37	const char *p;
 38	size_t n, ln;
 39
 40	tokview(s, &p, &n);
 41	ln = strlen(lit);
 42	return n == ln && strncmp(p, lit, n) == 0;
 43}
 44
 45static char *
 46tokdup(const char *s)
 47{
 48	const char *p;
 49	size_t n;
 50
 51	tokview(s, &p, &n);
 52	return xstrndup(p, n);
 53}
 54
 55static int
 56ismakevarref(const char *s)
 57{
 58	const char *p;
 59	char close;
 60	size_t n;
 61
 62	if (!s)
 63		return 0;
 64	tokview(s, &p, &n);
 65	if (n == 0 || p[0] != '$')
 66		return 0;
 67	if (n != 7)
 68		return 0;
 69	if (p[1] != '(' && p[1] != '{')
 70		return 0;
 71	close = p[1] == '(' ? ')' : '}';
 72	return strncmp(p + 2, "MAKE", 4) == 0 && p[6] == close;
 73}
 74
 75static int
 76ismaketoken(const char *s)
 77{
 78	const char *p;
 79	const char *base;
 80	size_t n;
 81	int ok;
 82	char *tmp;
 83
 84	if (ismakevarref(s))
 85		return 1;
 86	tokview(s, &p, &n);
 87	tmp = xstrndup(p, n);
 88	base = strrchr(tmp, '/');
 89	base = base ? base + 1 : tmp;
 90	ok = strcmp(base, "make") == 0 || strcmp(base, "gmake") == 0;
 91	free(tmp);
 92	return ok;
 93}
 94
 95static int
 96isassignment(const char *s)
 97{
 98	const char *eq;
 99	size_t i;
100
101	eq = strchr(s, '=');
102	if (!eq || eq == s)
103		return 0;
104	for (i = 0; s + i < eq; i++) {
105		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
106			return 0;
107	}
108	return 1;
109}
110
111static int
112flagsarg(const char *s)
113{
114	static const char *const flags[] = {
115	    "-C", "--directory", "-f", "--file", "-I", "-o", "-W", "-j", "-l", "--eval", 0};
116	size_t i;
117
118	for (i = 0; flags[i]; i++) {
119		if (strcmp(s, flags[i]) == 0)
120			return 1;
121	}
122	return 0;
123}
124
125static void
126addtok(struct StrList *out, const char *s, size_t n)
127{
128	out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
129	out->v[out->n++] = xstrndup(s, n);
130}
131
132static void
133freetoks(struct StrList *toks)
134{
135	freestrs(toks);
136}
137
138static int
139tokenizecmd(struct StrList *out, const char *s)
140{
141	size_t i;
142
143	memset(out, 0, sizeof(*out));
144	for (i = 0; s[i];) {
145		size_t start;
146		char quote;
147
148		while (isspace((unsigned char)s[i]))
149			i++;
150		if (!s[i])
151			break;
152		/* comment after recope shouldnt get parsed as a target*/
153		if (s[i] == '#')
154			break;
155		if (s[i] == '&' && s[i + 1] == '&') {
156			addtok(out, "&&", 2);
157			i += 2;
158			continue;
159		}
160		start = i;
161		quote = 0;
162		while (s[i]) {
163			if (!quote && isspace((unsigned char)s[i]))
164				break;
165			if (!quote && s[i] == '&' && s[i + 1] == '&')
166				break;
167			if (!quote && s[i] == '#')
168				break;
169			if (s[i] == '\\' && s[i + 1]) {
170				i += 2;
171				continue;
172			}
173			if (!quote && (s[i] == '\'' || s[i] == '"')) {
174				quote = s[i++];
175				continue;
176			}
177			if (quote && s[i] == quote) {
178				quote = 0;
179				i++;
180				continue;
181			}
182			i++;
183		}
184		addtok(out, s + start, i - start);
185	}
186	return 0;
187}
188
189void
190freesubmake(struct SubMake *sm)
191{
192	if (!sm)
193		return;
194	free(sm->makeprog);
195	sm->makeprog = 0;
196	free(sm->dir);
197	sm->dir = 0;
198	free(sm->makefile);
199	sm->makefile = 0;
200	freestrs(&sm->assigns);
201	freestrs(&sm->flags);
202	freestrs(&sm->goals);
203}
204
205void
206copysubmake(struct SubMake *dst, const struct SubMake *src)
207{
208	memset(dst, 0, sizeof(*dst));
209	if (!src)
210		return;
211	if (src->makeprog)
212		dst->makeprog = xstrdup(src->makeprog);
213	if (src->dir)
214		dst->dir = xstrdup(src->dir);
215	if (src->makefile)
216		dst->makefile = xstrdup(src->makefile);
217	addwords(&dst->assigns, &src->assigns);
218	addwords(&dst->flags, &src->flags);
219	addwords(&dst->goals, &src->goals);
220}
221
222int
223parsesubmake(struct SubMake *dst, const char *cmd)
224{
225	struct StrList toks;
226	size_t i, start;
227	const char *cddir;
228	const char *makeprog;
229
230	memset(dst, 0, sizeof(*dst));
231	cddir = 0;
232	makeprog = 0;
233	tokenizecmd(&toks, cmd);
234	start = 0;
235	if (toks.n >= 3 && tokeq(toks.v[0], "cd") && tokeq(toks.v[2], "&&")) {
236		cddir = toks.v[1];
237		start = 3;
238	}
239	if (start >= toks.n || !ismaketoken(toks.v[start])) {
240		freetoks(&toks);
241		return 0;
242	}
243	makeprog = toks.v[start];
244	dst->makeprog = tokdup(makeprog);
245	if (cddir)
246		dst->dir = tokdup(cddir);
247	for (i = start + 1; i < toks.n; i++) {
248		const char *tok;
249		char *ntok;
250
251		tok = toks.v[i];
252		ntok = tokdup(tok);
253		if (strcmp(ntok, "&&") == 0) {
254			free(ntok);
255			break;
256		}
257		if ((strcmp(ntok, "-C") == 0 || strcmp(ntok, "--directory") == 0) && i + 1 < toks.n) {
258			addstr(&dst->flags, ntok);
259			free(dst->dir);
260			dst->dir = tokdup(toks.v[++i]);
261			free(ntok);
262			continue;
263		}
264		if (strncmp(ntok, "-C", 2) == 0 && ntok[2]) {
265			addstr(&dst->flags, "-C");
266			free(dst->dir);
267			dst->dir = xstrdup(ntok + 2);
268			free(ntok);
269			continue;
270		}
271		if (strncmp(ntok, "--directory=", 12) == 0) {
272			addstr(&dst->flags, "--directory");
273			free(dst->dir);
274			dst->dir = xstrdup(ntok + 12);
275			free(ntok);
276			continue;
277		}
278		if ((strcmp(ntok, "-f") == 0 || strcmp(ntok, "--file") == 0) && i + 1 < toks.n) {
279			addstr(&dst->flags, ntok);
280			free(dst->makefile);
281			dst->makefile = tokdup(toks.v[++i]);
282			free(ntok);
283			continue;
284		}
285		if (strncmp(ntok, "-f", 2) == 0 && ntok[2]) {
286			addstr(&dst->flags, "-f");
287			free(dst->makefile);
288			dst->makefile = xstrdup(ntok + 2);
289			free(ntok);
290			continue;
291		}
292		if (strncmp(ntok, "--file=", 7) == 0) {
293			addstr(&dst->flags, "--file");
294			free(dst->makefile);
295			dst->makefile = xstrdup(ntok + 7);
296			free(ntok);
297			continue;
298		}
299		if (flagsarg(ntok) && i + 1 < toks.n) {
300			addstr(&dst->flags, ntok);
301			i++;
302			free(ntok);
303			continue;
304		}
305		if (ntok[0] == '-') {
306			addstr(&dst->flags, ntok);
307			free(ntok);
308			continue;
309		}
310		if (isassignment(ntok)) {
311			addstr(&dst->assigns, ntok);
312			free(ntok);
313			continue;
314		}
315		addstr(&dst->goals, ntok);
316		free(ntok);
317	}
318	freetoks(&toks);
319	return 1;
320}