commit 91483b0
shrub
·
2026-04-09 12:09:15 +0000 UTC
parent 110cfec
handle info, filter-out, addprefix, @
7 files changed,
+303,
-40
+16,
-1
1@@ -19,6 +19,18 @@ emitrule(FILE *fp, const char *cmd, int id)
2 fprintf(fp, " command = %s\n\n", cmd);
3 }
4
5+static char *
6+cleanrecipe(const char *s)
7+{
8+ while (*s == ' ' || *s == '\t')
9+ s++;
10+ while (*s == '@' || *s == '+' || *s == '-')
11+ s++;
12+ while (*s == ' ' || *s == '\t')
13+ s++;
14+ return xstrdup(s);
15+}
16+
17 static char *
18 joinrecipes(const struct Target *t)
19 {
20@@ -27,9 +39,12 @@ joinrecipes(const struct Target *t)
21
22 s = xstrdup("");
23 for (i = 0; i < t->recipes.n; i++) {
24+ char *clean;
25 char *next;
26
27- next = s[0] ? cat3(s, " && ", t->recipes.v[i]) : xstrdup(t->recipes.v[i]);
28+ clean = cleanrecipe(t->recipes.v[i]);
29+ next = s[0] ? cat3(s, " && ", clean) : xstrdup(clean);
30+ free(clean);
31 free(s);
32 s = next;
33 }
+106,
-5
1@@ -320,15 +320,16 @@ expandstr(struct Env *env, const char *s)
2 memcpy(out + len, val, k);
3 len += k;
4 free(val);
5- } else if (j - i - 3 >= 8 &&
6- memcmp(s + i + 2, "filter", 6) == 0 &&
7- isspace((unsigned char)s[i + 8])) {
8+ } else if (j - i - 3 >= 6 &&
9+ memcmp(s + i + 2, "filter", 6) == 0) {
10 char *args_raw, *pat_raw, *text_raw;
11 char *pat_exp, *text_exp, *val;
12 size_t astart;
13 ptrdiff_t comma;
14
15- astart = i + 9;
16+ astart = i + 8;
17+ while (astart < j - 1 && isspace((unsigned char)s[astart]))
18+ astart++;
19 args_raw = xstrndup(s + astart, j - 1 - astart);
20 comma = findargcomma(args_raw, strlen(args_raw));
21 if (comma < 0) {
22@@ -355,6 +356,99 @@ expandstr(struct Env *env, const char *s)
23 memcpy(out + len, val, k);
24 len += k;
25 free(val);
26+ } else if (j - i - 3 >= 10 &&
27+ memcmp(s + i + 2, "filter-out", 10) == 0) {
28+ char *args_raw, *pat_raw, *text_raw;
29+ char *pat_exp, *text_exp, *val;
30+ size_t astart;
31+ ptrdiff_t comma;
32+
33+ astart = i + 12;
34+ while (astart < j - 1 && isspace((unsigned char)s[astart]))
35+ astart++;
36+ args_raw = xstrndup(s + astart, j - 1 - astart);
37+ comma = findargcomma(args_raw, strlen(args_raw));
38+ if (comma < 0) {
39+ evalerr("malformed function arguments", "$(filter-out)");
40+ free(args_raw);
41+ val = xstrdup("");
42+ } else {
43+ pat_raw = xstrndup(args_raw, (size_t)comma);
44+ text_raw = xstrdup(args_raw + comma + 1);
45+ free(args_raw);
46+ pat_exp = expandstr(env, pat_raw);
47+ text_exp = expandstr(env, text_raw);
48+ free(pat_raw);
49+ free(text_raw);
50+ val = fnfilterout(pat_exp, text_exp);
51+ free(pat_exp);
52+ free(text_exp);
53+ }
54+ k = strlen(val);
55+ if (len + k + 1 > cap) {
56+ cap = len + k + 1;
57+ out = xrealloc(out, cap);
58+ }
59+ memcpy(out + len, val, k);
60+ len += k;
61+ free(val);
62+ } else if (j - i - 3 >= 9 &&
63+ memcmp(s + i + 2, "addprefix", 9) == 0) {
64+ char *args_raw, *pre_raw, *names_raw;
65+ char *pre_exp, *names_exp, *val;
66+ size_t astart;
67+ ptrdiff_t comma;
68+
69+ astart = i + 11;
70+ while (astart < j - 1 && isspace((unsigned char)s[astart]))
71+ astart++;
72+ args_raw = xstrndup(s + astart, j - 1 - astart);
73+ comma = findargcomma(args_raw, strlen(args_raw));
74+ if (comma < 0) {
75+ evalerr("malformed function arguments", "$(addprefix)");
76+ free(args_raw);
77+ val = xstrdup("");
78+ } else {
79+ pre_raw = xstrndup(args_raw, (size_t)comma);
80+ names_raw = xstrdup(args_raw + comma + 1);
81+ free(args_raw);
82+ pre_exp = expandstr(env, pre_raw);
83+ names_exp = expandstr(env, names_raw);
84+ free(pre_raw);
85+ free(names_raw);
86+ val = fnaddprefix(pre_exp, names_exp);
87+ free(pre_exp);
88+ free(names_exp);
89+ }
90+ k = strlen(val);
91+ if (len + k + 1 > cap) {
92+ cap = len + k + 1;
93+ out = xrealloc(out, cap);
94+ }
95+ memcpy(out + len, val, k);
96+ len += k;
97+ free(val);
98+ } else if (j - i - 3 >= 4 &&
99+ memcmp(s + i + 2, "info", 4) == 0) {
100+ char *text_raw, *text_exp, *val;
101+ size_t tstart;
102+
103+ tstart = i + 6;
104+ while (tstart < j - 1 && isspace((unsigned char)s[tstart]))
105+ tstart++;
106+ text_raw = xstrndup(s + tstart, j - 1 - tstart);
107+ text_exp = expandstr(env, text_raw);
108+ free(text_raw);
109+ val = fninfo(text_exp);
110+ free(text_exp);
111+ k = strlen(val);
112+ if (len + k + 1 > cap) {
113+ cap = len + k + 1;
114+ out = xrealloc(out, cap);
115+ }
116+ memcpy(out + len, val, k);
117+ len += k;
118+ free(val);
119 } else {
120 char *unsup;
121
122@@ -544,9 +638,16 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
123 case NODE_BLANK:
124 break;
125 case NODE_COMMENT:
126- case NODE_RAW:
127 node.data.raw.text = xstrdup(src->data.raw.text);
128 break;
129+ case NODE_RAW: {
130+ char *exp;
131+
132+ exp = expandstr(env, src->data.raw.text);
133+ free(exp);
134+ node.kind = NODE_BLANK;
135+ break;
136+ }
137 case NODE_INCLUDE:
138 node.data.include.optional = src->data.include.optional;
139 node.data.include.path = expandstr(env, src->data.include.path);
+131,
-30
1@@ -10,23 +10,39 @@
2 /* implementations of gnu make builtin functions*/
3
4 static int
5-matchword(const char *pat, size_t npat, const char *word, size_t nword)
6+matchword(const char *patterns, const char *word, size_t nword)
7 {
8- const char *pct;
9- size_t pre, suf;
10-
11- pct = memchr(pat, '%', npat);
12- if (!pct)
13- return npat == nword && memcmp(pat, word, npat) == 0;
14- pre = (size_t)(pct - pat);
15- suf = npat - pre - 1;
16- if (nword < pre + suf)
17- return 0;
18- if (memcmp(pat, word, pre) != 0)
19- return 0;
20- if (memcmp(pct + 1, word + nword - suf, suf) != 0)
21- return 0;
22- return 1;
23+ size_t p0, p1;
24+
25+ for (p0 = 0; patterns[p0];) {
26+ const char *pat, *pct;
27+ size_t npat, pre, suf;
28+
29+ while (patterns[p0] && isspace((unsigned char)patterns[p0]))
30+ p0++;
31+ if (!patterns[p0])
32+ break;
33+ p1 = p0;
34+ while (patterns[p1] && !isspace((unsigned char)patterns[p1]))
35+ p1++;
36+ pat = patterns + p0;
37+ npat = p1 - p0;
38+ pct = memchr(pat, '%', npat);
39+ if (!pct) {
40+ if (npat == nword && memcmp(pat, word, npat) == 0)
41+ return 1;
42+ p0 = p1;
43+ continue;
44+ }
45+ pre = (size_t)(pct - pat);
46+ suf = npat - pre - 1;
47+ if (nword >= pre + suf &&
48+ memcmp(pat, word, pre) == 0 &&
49+ memcmp(pct + 1, word + nword - suf, suf) == 0)
50+ return 1;
51+ p0 = p1;
52+ }
53+ return 0;
54 }
55
56 char *
57@@ -133,7 +149,7 @@ char *
58 fnfilter(const char *patterns, const char *text)
59 {
60 char *out;
61- size_t cap, len, i, j, p0, p1;
62+ size_t cap, len, i, j;
63
64 cap = strlen(text) + 1;
65 if (cap < 64)
66@@ -153,19 +169,7 @@ fnfilter(const char *patterns, const char *text)
67 while (text[j] && !isspace((unsigned char)text[j]))
68 j++;
69
70- keep = 0;
71- for (p0 = 0; patterns[p0] && !keep;) {
72- while (patterns[p0] && isspace((unsigned char)patterns[p0]))
73- p0++;
74- if (!patterns[p0])
75- break;
76- p1 = p0;
77- while (patterns[p1] && !isspace((unsigned char)patterns[p1]))
78- p1++;
79- if (matchword(patterns + p0, p1 - p0, text + i, j - i))
80- keep = 1;
81- p0 = p1;
82- }
83+ keep = matchword(patterns, text + i, j - i);
84
85 if (keep) {
86 size_t wn, need;
87@@ -188,3 +192,100 @@ fnfilter(const char *patterns, const char *text)
88
89 return out;
90 }
91+
92+char *
93+fnfilterout(const char *patterns, const char *text)
94+{
95+ char *out;
96+ size_t cap, len, i, j;
97+
98+ cap = strlen(text) + 1;
99+ if (cap < 64)
100+ cap = 64;
101+ len = 0;
102+ out = xmalloc(cap);
103+ out[0] = 0;
104+
105+ for (i = 0; text[i];) {
106+ size_t wn, need;
107+
108+ while (text[i] && isspace((unsigned char)text[i]))
109+ i++;
110+ if (!text[i])
111+ break;
112+ j = i;
113+ while (text[j] && !isspace((unsigned char)text[j]))
114+ j++;
115+ if (matchword(patterns, text + i, j - i)) {
116+ i = j;
117+ continue;
118+ }
119+ wn = j - i;
120+ need = len + wn + 2;
121+ if (need > cap) {
122+ while (cap < need)
123+ cap *= 2;
124+ out = xrealloc(out, cap);
125+ }
126+ if (len)
127+ out[len++] = ' ';
128+ memcpy(out + len, text + i, wn);
129+ len += wn;
130+ out[len] = 0;
131+ i = j;
132+ }
133+
134+ return out;
135+}
136+
137+char *
138+fnaddprefix(const char *prefix, const char *names)
139+{
140+ char *out;
141+ size_t cap, len, i, j, nprefix;
142+
143+ nprefix = strlen(prefix);
144+ cap = strlen(names) + nprefix + 1;
145+ if (cap < 64)
146+ cap = 64;
147+ len = 0;
148+ out = xmalloc(cap);
149+ out[0] = 0;
150+
151+ for (i = 0; names[i];) {
152+ size_t wn, need;
153+
154+ while (names[i] && isspace((unsigned char)names[i]))
155+ i++;
156+ if (!names[i])
157+ break;
158+ j = i;
159+ while (names[j] && !isspace((unsigned char)names[j]))
160+ j++;
161+ wn = j - i;
162+ need = len + nprefix + wn + 2;
163+ if (need > cap) {
164+ while (cap < need)
165+ cap *= 2;
166+ out = xrealloc(out, cap);
167+ }
168+ if (len)
169+ out[len++] = ' ';
170+ memcpy(out + len, prefix, nprefix);
171+ len += nprefix;
172+ memcpy(out + len, names + i, wn);
173+ len += wn;
174+ out[len] = 0;
175+ i = j;
176+ }
177+
178+ return out;
179+}
180+
181+char *
182+fninfo(const char *text)
183+{
184+ fputs(text, stdout);
185+ fputc('\n', stdout);
186+ return xstrdup("");
187+}
+13,
-2
1@@ -227,6 +227,10 @@ addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Env
2 char *exp;
3
4 exp = expandstr(env, src->v[i]);
5+ if (!exp[0]) {
6+ free(exp);
7+ continue;
8+ }
9 dest->v = xrealloc(dest->v, (dest->n + 1) * sizeof(dest->v[0]));
10 dest->v[dest->n++] = exp;
11 }
12@@ -400,8 +404,7 @@ addsuffix(struct GraphState *gs, const struct RuleNode *rule)
13 char *from, *to;
14 struct Suffix *sr;
15
16- if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0)
17- {
18+ if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0) {
19 addpattern(gs, rule);
20 return;
21 }
22@@ -456,6 +459,10 @@ instantiate(struct GraphState *gs, struct Target *t, struct Pattern *p, const ch
23 exp = expandstem(vars, stem);
24 free(vars);
25 free(s);
26+ if (!exp[0]) {
27+ free(exp);
28+ continue;
29+ }
30 t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
31 t->recipes.v[t->recipes.n++] = exp;
32 }
33@@ -483,6 +490,10 @@ instantiatesuffix(struct GraphState *gs, struct Target *t)
34 vars = expandstr(&env, gs->sufs[i].recipes.v[k]);
35 exp = expandauto(vars, t, stem);
36 free(vars);
37+ if (!exp[0]) {
38+ free(exp);
39+ continue;
40+ }
41 t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
42 t->recipes.v[t->recipes.n++] = exp;
43 }
+3,
-0
1@@ -34,5 +34,8 @@ void evalassign(struct Env *env, const struct AssignNode *in);
2 char *fnwildcard(const char *patterns);
3 char *fnshell(const char *cmd);
4 char *fnfilter(const char *patterns, const char *text);
5+char *fnfilterout(const char *patterns, const char *text);
6+char *fnaddprefix(const char *prefix, const char *names);
7+char *fninfo(const char *text);
8
9 #endif
+33,
-1
1@@ -601,7 +601,8 @@ parserule(const struct PreLine *line, const char *s, size_t n, size_t colon)
2 {
3 struct Node state;
4 const char *rhs;
5- size_t rhsn, split;
6+ size_t rhsn, split, semi;
7+ char *recipe;
8
9 memset(&state, 0, sizeof(state));
10 state.kind = NODE_RULE;
11@@ -612,6 +613,19 @@ parserule(const struct PreLine *line, const char *s, size_t n, size_t colon)
12
13 rhs = s + colon + 1;
14 rhsn = n - colon - 1;
15+ semi = (size_t)findtop(rhs, rhsn, ';');
16+ if (semi != (size_t)-1) {
17+ recipe = trimdup(rhs + semi + 1, rhsn - semi - 1);
18+ if (recipe[0]) {
19+ state.data.rule.recipes.v =
20+ xrealloc(state.data.rule.recipes.v,
21+ (state.data.rule.recipes.n + 1) * sizeof(state.data.rule.recipes.v[0]));
22+ state.data.rule.recipes.v[state.data.rule.recipes.n++] = recipe;
23+ } else {
24+ free(recipe);
25+ }
26+ rhsn = semi;
27+ }
28 split = (size_t)findtop(rhs, rhsn, '|');
29 if (split != (size_t)-1) {
30 splitwords(&state.data.rule.prereqs, rhs, split);
31@@ -622,6 +636,19 @@ parserule(const struct PreLine *line, const char *s, size_t n, size_t colon)
32 return state;
33 }
34
35+static struct Node
36+parseexpr(const struct PreLine *line, const char *s)
37+{
38+ struct Node state;
39+
40+ memset(&state, 0, sizeof(state));
41+ state.kind = NODE_RAW;
42+ state.loc.line0 = line->line0;
43+ state.loc.line1 = line->line1;
44+ state.data.raw.text = xstrndup(s, strlen(s));
45+ return state;
46+}
47+
48 /* unclassified line, treat as unsupported syntax */
49 static struct Node
50 parseraw(const struct PreLine *line, const char *s)
51@@ -724,6 +751,11 @@ parseline(const struct PreLine *line)
52 free(trim);
53 return state;
54 }
55+ if (trim[0] == '$' && (trim[1] == '(' || trim[1] == '{')) {
56+ state = parseexpr(line, trim);
57+ free(trim);
58+ return state;
59+ }
60 state = parseraw(line, trim);
61 free(trim);
62 return state;
+1,
-1
1@@ -146,4 +146,4 @@ if [ -n "$targets" ]; then
2 IFS=$oldifs
3 fi
4
5-exec "$@"
6+NINJA_STATUS= exec "$@"