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}