main 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] != '.')
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] != '.')
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)
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					goto next_target;
285				}
286			}
287			rules->singlesuf = xrealloc(rules->singlesuf,
288			                            (rules->nsinglesuf + 1) * sizeof(rules->singlesuf[0]));
289			sr = &rules->singlesuf[rules->nsinglesuf++];
290			memset(sr, 0, sizeof(*sr));
291			sr->from = from;
292			addrecipes(&sr->recipes, &rule->recipes);
293			continue;
294		}
295		issuf(rule->targets.v[i], &from, &to);
296
297		for (k = 0; k < rules->nsufs; k++) {
298			if (strcmp(rules->sufs[k].from, from) == 0 &&
299			    strcmp(rules->sufs[k].to, to) == 0) {
300				free(from);
301				free(to);
302				freerecipes(&rules->sufs[k].recipes);
303				memset(&rules->sufs[k].recipes, 0, sizeof(rules->sufs[k].recipes));
304				addrecipes(&rules->sufs[k].recipes, &rule->recipes);
305				goto next_target;
306			}
307		}
308		rules->sufs = xrealloc(rules->sufs, (rules->nsufs + 1) * sizeof(rules->sufs[0]));
309		memset(&rules->sufs[rules->nsufs], 0, sizeof(rules->sufs[rules->nsufs]));
310		rules->sufs[rules->nsufs].from = from;
311		rules->sufs[rules->nsufs].to = to;
312		addrecipes(&rules->sufs[rules->nsufs].recipes, &rule->recipes);
313		rules->nsufs++;
314	next_target:
315		;
316	}
317	return 1;
318}
319
320int
321instsufrule(const struct SufRules *rules,
322            const struct Graph *graph,
323            struct Target *t,
324            struct EvalCtx *ctx)
325{
326	size_t i, k;
327
328	for (i = 0; i < rules->active.n; i++) {
329		size_t j, r;
330
331		for (j = 0; j < rules->active.n; j++) {
332			for (r = 0; r < rules->nsufs; r++) {
333				char *stem, *src;
334				int exists;
335
336				if (!sufeq(rules->sufs[r].to, rules->active.v[i]))
337					continue;
338				if (!sufeq(rules->sufs[r].from, rules->active.v[j]))
339					continue;
340				if (!matchsuf(rules->sufs[r].to, t->name, &stem))
341					continue;
342				src = cat3(stem, "", rules->sufs[r].from);
343				exists = pathorargetexists(graph, src);
344				if (!exists && !sufbuildable(rules, graph, src, 0)) {
345					free(stem);
346					free(src);
347					continue;
348				}
349				if (!hasword(&t->impprereqs, src)) {
350					t->impprereqs.v = xrealloc(t->impprereqs.v,
351					                            (t->impprereqs.n + 1) * sizeof(t->impprereqs.v[0]));
352					memmove(t->impprereqs.v + 1, t->impprereqs.v,
353					        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
354					t->impprereqs.v[0] = src;
355					t->impprereqs.n++;
356				} else {
357					free(src);
358				}
359				(void)exists;
360				for (k = 0; k < rules->sufs[r].recipes.n; k++) {
361					char *exp;
362
363					exp = expandauto(rules->sufs[r].recipes.v[k].body, t, stem);
364					if (!exp[0]) {
365						free(exp);
366						continue;
367					}
368					t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
369					t->recipes.v[t->recipes.n].body = exp;
370					t->recipes.v[t->recipes.n].silent = rules->sufs[r].recipes.v[k].silent;
371					t->recipes.v[t->recipes.n].ignore = rules->sufs[r].recipes.v[k].ignore;
372					t->recipes.v[t->recipes.n].recursive = rules->sufs[r].recipes.v[k].recursive;
373					t->recipes.v[t->recipes.n].submake = rules->sufs[r].recipes.v[k].submake;
374					copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->sufs[r].recipes.v[k].sm);
375					t->recipes.n++;
376				}
377				freeenv(&t->env);
378				copyenv(&t->env, ctx->env);
379				free(stem);
380				return 1;
381			}
382		}
383	}
384
385	for (i = 0; i < rules->active.n; i++) {
386		size_t j;
387
388		for (j = 0; j < rules->nsinglesuf; j++) {
389			char *src;
390			int exists;
391
392			if (!sufeq(rules->singlesuf[j].from, rules->active.v[i]))
393				continue;
394			src = cat3(t->name, "", rules->singlesuf[j].from);
395			exists = pathorargetexists(graph, src);
396			if (!exists && !sufbuildable(rules, graph, src, 0)) {
397				free(src);
398				continue;
399			}
400			if (!hasword(&t->impprereqs, src)) {
401				t->impprereqs.v = xrealloc(t->impprereqs.v,
402				                            (t->impprereqs.n + 1) * sizeof(t->impprereqs.v[0]));
403				memmove(t->impprereqs.v + 1, t->impprereqs.v,
404				        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
405				t->impprereqs.v[0] = src;
406				t->impprereqs.n++;
407			} else {
408				free(src);
409			}
410			(void)exists;
411			for (k = 0; k < rules->singlesuf[j].recipes.n; k++) {
412				char *exp;
413
414				exp = expandauto(rules->singlesuf[j].recipes.v[k].body, t, t->name);
415				if (!exp[0]) {
416					free(exp);
417					continue;
418				}
419				t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
420				t->recipes.v[t->recipes.n].body = exp;
421				t->recipes.v[t->recipes.n].silent = rules->singlesuf[j].recipes.v[k].silent;
422				t->recipes.v[t->recipes.n].ignore = rules->singlesuf[j].recipes.v[k].ignore;
423				t->recipes.v[t->recipes.n].recursive = rules->singlesuf[j].recipes.v[k].recursive;
424				t->recipes.v[t->recipes.n].submake = rules->singlesuf[j].recipes.v[k].submake;
425				copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->singlesuf[j].recipes.v[k].sm);
426				t->recipes.n++;
427			}
428			freeenv(&t->env);
429			copyenv(&t->env, ctx->env);
430			return 1;
431		}
432	}
433	return 0;
434}
435
436void
437freesufrules(struct SufRules *rules)
438{
439	size_t i;
440
441	for (i = 0; i < rules->nsinglesuf; i++) {
442		free(rules->singlesuf[i].from);
443		freerecipes(&rules->singlesuf[i].recipes);
444	}
445	for (i = 0; i < rules->nsufs; i++) {
446		free(rules->sufs[i].from);
447		free(rules->sufs[i].to);
448		freerecipes(&rules->sufs[i].recipes);
449	}
450	clearsufs(&rules->active);
451	free(rules->singlesuf);
452	free(rules->sufs);
453	rules->singlesuf = 0;
454	rules->nsinglesuf = 0;
455	rules->sufs = 0;
456	rules->nsufs = 0;
457}