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 "$@"