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}