main shrub/shinobi / src / sufrule.c
  1#include "posix.h"
  2
  3#include <unistd.h>
  4#include <stdio.h>
  5#include <stdlib.h>
  6#include <string.h>
  7
  8extern char **environ;
  9
 10static int matchsuf(const char *suf, const char *name, char **stem);
 11
 12static int
 13sufeq(const char *a, const char *b)
 14{
 15	return strcmp(a, b) == 0;
 16}
 17
 18static int
 19sufidx(const struct SuffixList *list, const char *suf)
 20{
 21	size_t i;
 22
 23	for (i = 0; i < list->n; i++) {
 24		if (sufeq(list->v[i], suf))
 25			return (int)i;
 26	}
 27	return -1;
 28}
 29
 30static int
 31sufactive(const struct SuffixList *list, const char *suf)
 32{
 33	return sufidx(list, suf) >= 0;
 34}
 35
 36static int
 37pathorargetexists(const struct Graph *graph, const char *name)
 38{
 39	const struct Target *t;
 40
 41	t = findctarget(graph, name);
 42	if (t && (t->defined || t->recipes.n > 0 || totalprereqs(t) > 0))
 43		return 1;
 44	return access(name, F_OK) == 0;
 45}
 46
 47/* do multi layer suf rule inference: check whether a missing suffix rule
 48 * source can be created by another suffix rule or suffix rule chain, so
 49 * we can infer things like .ssa -> .s -> .o without inventing fkae prereqs 
 50 * such as all.scd. (examples from hare compiler makefile which makes 
 51 * quite heavy use of this) */
 52static int
 53sufbuildable(const struct SufRules *rules, const struct Graph *graph, const char *name, size_t depth)
 54{
 55	size_t i;
 56
 57	if (pathorargetexists(graph, name))
 58		return 1;
 59	if (depth > rules->active.n + rules->nsufs + rules->nsinglesuf)
 60		return 0;
 61
 62	for (i = 0; i < rules->nsufs; i++) {
 63		size_t j;
 64		char *stem;
 65
 66		if (sufidx(&rules->active, rules->sufs[i].to) < 0 ||
 67		    sufidx(&rules->active, rules->sufs[i].from) < 0)
 68			continue;
 69		if (!matchsuf(rules->sufs[i].to, name, &stem))
 70			continue;
 71		j = strlen(stem);
 72		free(stem);
 73		{
 74			char *src;
 75			int ok;
 76
 77			src = xmalloc(j + strlen(rules->sufs[i].from) + 1);
 78			memcpy(src, name, j);
 79			strcpy(src + j, rules->sufs[i].from);
 80			ok = sufbuildable(rules, graph, src, depth + 1);
 81			free(src);
 82			if (ok)
 83				return 1;
 84		}
 85	}
 86
 87	if (strchr(name, '.'))
 88		return 0;
 89	for (i = 0; i < rules->nsinglesuf; i++) {
 90		char *src;
 91		int ok;
 92
 93		if (sufidx(&rules->active, rules->singlesuf[i].from) < 0)
 94			continue;
 95		src = cat3(name, "", rules->singlesuf[i].from);
 96		ok = sufbuildable(rules, graph, src, depth + 1);
 97		free(src);
 98		if (ok)
 99			return 1;
100	}
101	return 0;
102}
103
104int
105issuf(const char *s, char **from, char **to)
106{
107	const char *mid;
108
109	if (!s || s[0] != '.' || strchr(s, '/'))
110		return 0;
111	mid = strchr(s + 1, '.');
112	if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
113		return 0;
114	*from = xstrndup(s, (size_t)(mid - s));
115	*to = xstrdup(mid);
116	return 1;
117}
118
119int
120issinglesuf(const char *s, char **from)
121{
122	if (!s || s[0] != '.' || strchr(s, '/'))
123		return 0;
124	if (!s[1] || strchr(s + 1, '.'))
125		return 0;
126	*from = xstrdup(s);
127	return 1;
128}
129
130static int
131matchsuf(const char *suf, const char *name, char **stem)
132{
133	size_t nsuf, nname;
134
135	nsuf = strlen(suf);
136	nname = strlen(name);
137	if (nname < nsuf || strcmp(name + nname - nsuf, suf) != 0)
138		return 0;
139	*stem = xstrndup(name, nname - nsuf);
140	return 1;
141}
142
143/*
144 * expand automatic variables like so:
145 *   $@  target
146 *   $<  first prereq
147 *   $^  all prereqs
148 *   $+  all prereqs
149 *   $?  all prereqs, let ninja decide
150 *   $*  stem
151 *   $$  literal $
152 *
153 *  this turns them into concrete strings.
154 */
155static char *
156expandauto(const char *s, const struct Target *t, const char *stem)
157{
158	size_t i, n, cap, len;
159	char *out;
160
161	n = strlen(s);
162	cap = n + 1;
163	len = 0;
164	out = xmalloc(cap);
165	for (i = 0; i < n; i++) {
166		if (s[i] == '$' && i + 1 < n) {
167			const char *val;
168			int mustfree;
169
170			val = 0;
171			mustfree = 0;
172			if (s[i + 1] == '$') {
173				if (len + 3 > cap) {
174					cap = len + 3 + (n - i);
175					out = xrealloc(out, cap);
176				}
177				out[len++] = '$';
178				out[len++] = '$';
179				i++;
180				continue;
181			}
182			if (s[i + 1] == '@') {
183				val = t->name;
184			} else if (s[i + 1] == '<') {
185				val = firstprereq(t);
186				if (!val)
187					val = "";
188			} else if (s[i + 1] == '^' || s[i + 1] == '+' || s[i + 1] == '?') {
189				val = joinallprereqs(t, " ");
190				mustfree = 1;
191			} else if (s[i + 1] == '*') {
192				val = stem ? stem : "";
193			}
194			if (val) {
195				size_t vlen = strlen(val);
196				if (len + vlen + 1 > cap) {
197					cap = len + vlen + (n - i) + 1;
198					out = xrealloc(out, cap);
199				}
200				memcpy(out + len, val, vlen);
201				len += vlen;
202				if (mustfree)
203					free((char *)val);
204				i++;
205				continue;
206			}
207		}
208		if (len + 2 > cap) {
209			cap *= 2;
210			out = xrealloc(out, cap);
211		}
212		out[len++] = s[i];
213	}
214	out[len] = 0;
215	return out;
216}
217
218
219int
220issufrule(const struct RuleNode *rule)
221{
222	return rule->targets.n == 1 && strcmp(rule->targets.v[0], ".SUFFIXES") == 0;
223}
224
225void
226addsufs(struct SuffixList *list, const struct StrList *sufs)
227{
228	size_t i;
229
230	for (i = 0; i < sufs->n; i++) {
231		if (sufactive(list, sufs->v[i]))
232			continue;
233		list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
234		list->v[list->n++] = xstrdup(sufs->v[i]);
235	}
236}
237
238void
239clearsufs(struct SuffixList *list)
240{
241	size_t i;
242
243	for (i = 0; i < list->n; i++)
244		free(list->v[i]);
245	free(list->v);
246	list->v = 0;
247	list->n = 0;
248}
249
250int
251collectsufrule(struct SufRules *rules, const struct RuleNode *rule, int builtin)
252{
253	size_t i;
254
255	if (rule->targets.n == 0 || rule->prereqs.n != 0 || rule->order_only.n != 0)
256		return 0;
257	for (i = 0; i < rule->targets.n; i++) {
258		char *from, *to;
259
260		if (issinglesuf(rule->targets.v[i], &from)) {
261			free(from);
262			continue;
263		}
264		if (issuf(rule->targets.v[i], &from, &to)) {
265			free(from);
266			free(to);
267			continue;
268		}
269		return 0;
270	}
271	for (i = 0; i < rule->targets.n; i++) {
272		char *from, *to;
273		size_t k;
274
275		if (issinglesuf(rule->targets.v[i], &from)) {
276			struct SingleSufRule *sr;
277
278			for (k = 0; k < rules->nsinglesuf; k++) {
279				if (strcmp(rules->singlesuf[k].from, from) == 0) {
280					free(from);
281					freerecipes(&rules->singlesuf[k].recipes);
282					memset(&rules->singlesuf[k].recipes, 0, sizeof(rules->singlesuf[k].recipes));
283					addrecipes(&rules->singlesuf[k].recipes, &rule->recipes);
284					rules->singlesuf[k].builtin = builtin;
285					goto next_target;
286				}
287			}
288			rules->singlesuf = xrealloc(rules->singlesuf,
289			                            (rules->nsinglesuf + 1) * sizeof(rules->singlesuf[0]));
290			sr = &rules->singlesuf[rules->nsinglesuf++];
291			memset(sr, 0, sizeof(*sr));
292			sr->from = from;
293			addrecipes(&sr->recipes, &rule->recipes);
294			sr->builtin = builtin;
295			continue;
296		}
297		issuf(rule->targets.v[i], &from, &to);
298
299		for (k = 0; k < rules->nsufs; k++) {
300			if (strcmp(rules->sufs[k].from, from) == 0 &&
301			    strcmp(rules->sufs[k].to, to) == 0) {
302				free(from);
303				free(to);
304				freerecipes(&rules->sufs[k].recipes);
305				memset(&rules->sufs[k].recipes, 0, sizeof(rules->sufs[k].recipes));
306				addrecipes(&rules->sufs[k].recipes, &rule->recipes);
307				rules->sufs[k].builtin = builtin;
308				goto next_target;
309			}
310		}
311		rules->sufs = xrealloc(rules->sufs, (rules->nsufs + 1) * sizeof(rules->sufs[0]));
312		memset(&rules->sufs[rules->nsufs], 0, sizeof(rules->sufs[rules->nsufs]));
313		rules->sufs[rules->nsufs].from = from;
314		rules->sufs[rules->nsufs].to = to;
315		addrecipes(&rules->sufs[rules->nsufs].recipes, &rule->recipes);
316		rules->sufs[rules->nsufs].builtin = builtin;
317		rules->nsufs++;
318	next_target:
319		;
320	}
321	return 1;
322}
323
324int
325instsufrule(const struct SufRules *rules,
326            const struct Graph *graph,
327            struct Target *t,
328            struct EvalCtx *ctx,
329            int allow_builtin)
330{
331	size_t i, k;
332
333	for (i = 0; i < rules->active.n; i++) {
334		size_t j, r;
335
336		for (j = 0; j < rules->active.n; j++) {
337			for (r = 0; r < rules->nsufs; r++) {
338				char *stem, *src;
339				int exists;
340
341				if (rules->sufs[r].builtin && !allow_builtin)
342					continue;
343				if (!sufeq(rules->sufs[r].to, rules->active.v[i]))
344					continue;
345				if (!sufeq(rules->sufs[r].from, rules->active.v[j]))
346					continue;
347				if (!matchsuf(rules->sufs[r].to, t->name, &stem))
348					continue;
349				src = cat3(stem, "", rules->sufs[r].from);
350				exists = pathorargetexists(graph, src);
351				if (!exists && !sufbuildable(rules, graph, src, 0)) {
352					free(stem);
353					free(src);
354					continue;
355				}
356				if (!hasword(&t->impprereqs, src)) {
357					t->impprereqs.v = xrealloc(t->impprereqs.v,
358					                            (t->impprereqs.n + 1) * sizeof(t->impprereqs.v[0]));
359					memmove(t->impprereqs.v + 1, t->impprereqs.v,
360					        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
361					t->impprereqs.v[0] = src;
362					t->impprereqs.n++;
363				} else {
364					free(src);
365				}
366				(void)exists;
367				for (k = 0; k < rules->sufs[r].recipes.n; k++) {
368					char *exp;
369
370					exp = expandauto(rules->sufs[r].recipes.v[k].body, t, stem);
371					if (!exp[0]) {
372						free(exp);
373						continue;
374					}
375					t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
376					t->recipes.v[t->recipes.n].body = exp;
377					t->recipes.v[t->recipes.n].silent = rules->sufs[r].recipes.v[k].silent;
378					t->recipes.v[t->recipes.n].ignore = rules->sufs[r].recipes.v[k].ignore;
379					t->recipes.v[t->recipes.n].recursive = rules->sufs[r].recipes.v[k].recursive;
380					t->recipes.v[t->recipes.n].submake = rules->sufs[r].recipes.v[k].submake;
381					copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->sufs[r].recipes.v[k].sm);
382					t->recipes.n++;
383				}
384				freeenv(&t->env);
385				copyenv(&t->env, ctx->env);
386				free(stem);
387				return 1;
388			}
389		}
390	}
391
392	for (i = 0; i < rules->active.n; i++) {
393		size_t j;
394
395		for (j = 0; j < rules->nsinglesuf; j++) {
396			char *src;
397			int exists;
398
399			if (rules->singlesuf[j].builtin && !allow_builtin)
400				continue;
401			if (!sufeq(rules->singlesuf[j].from, rules->active.v[i]))
402				continue;
403			src = cat3(t->name, "", rules->singlesuf[j].from);
404			exists = pathorargetexists(graph, src);
405			if (!exists && !sufbuildable(rules, graph, src, 0)) {
406				free(src);
407				continue;
408			}
409			if (!hasword(&t->impprereqs, src)) {
410				t->impprereqs.v = xrealloc(t->impprereqs.v,
411				                            (t->impprereqs.n + 1) * sizeof(t->impprereqs.v[0]));
412				memmove(t->impprereqs.v + 1, t->impprereqs.v,
413				        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
414				t->impprereqs.v[0] = src;
415				t->impprereqs.n++;
416			} else {
417				free(src);
418			}
419			(void)exists;
420			for (k = 0; k < rules->singlesuf[j].recipes.n; k++) {
421				char *exp;
422
423				exp = expandauto(rules->singlesuf[j].recipes.v[k].body, t, t->name);
424				if (!exp[0]) {
425					free(exp);
426					continue;
427				}
428				t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
429				t->recipes.v[t->recipes.n].body = exp;
430				t->recipes.v[t->recipes.n].silent = rules->singlesuf[j].recipes.v[k].silent;
431				t->recipes.v[t->recipes.n].ignore = rules->singlesuf[j].recipes.v[k].ignore;
432				t->recipes.v[t->recipes.n].recursive = rules->singlesuf[j].recipes.v[k].recursive;
433				t->recipes.v[t->recipes.n].submake = rules->singlesuf[j].recipes.v[k].submake;
434				copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->singlesuf[j].recipes.v[k].sm);
435				t->recipes.n++;
436			}
437			freeenv(&t->env);
438			copyenv(&t->env, ctx->env);
439			return 1;
440		}
441	}
442	return 0;
443}
444
445void
446freesufrules(struct SufRules *rules)
447{
448	size_t i;
449
450	for (i = 0; i < rules->nsinglesuf; i++) {
451		free(rules->singlesuf[i].from);
452		freerecipes(&rules->singlesuf[i].recipes);
453	}
454	for (i = 0; i < rules->nsufs; i++) {
455		free(rules->sufs[i].from);
456		free(rules->sufs[i].to);
457		freerecipes(&rules->sufs[i].recipes);
458	}
459	clearsufs(&rules->active);
460	free(rules->singlesuf);
461	free(rules->sufs);
462	rules->singlesuf = 0;
463	rules->nsinglesuf = 0;
464	rules->sufs = 0;
465	rules->nsufs = 0;
466}