commit 30eeafb
shrub
·
2026-04-15 14:42:20 +0000 UTC
parent b28bd5e
handle implicit suffix rules and .SUFFIXES and key=val assigns on cli
7 files changed,
+420,
-52
M
Makefile
+0,
-3
1@@ -17,9 +17,6 @@ all: $(BIN)
2 $(BIN): $(OBJS)
3 $(CC) $(LDFLAGS) -o $(BIN) $(OBJS) $(LDLIBS)
4
5-.c.o:
6- $(CC) $(CFLAGS) -c -o $@ $<
7-
8 fmt:
9 clang-format -i $(FMTSRCS)
10
M
TODO
+3,
-0
1@@ -0,0 +1,3 @@
2+implement .POSIX
3+handle recursive ${MAKE} as subninja
4+
+62,
-0
1@@ -48,6 +48,51 @@ readfile(const char *path)
2 return buf;
3 }
4
5+static int
6+isvarassign(const char *s)
7+{
8+ const char *eq;
9+
10+ eq = strchr(s, '=');
11+ if (!eq || eq == s)
12+ return 0;
13+ return 1;
14+}
15+
16+static char *
17+appendassigns(char *src, char **assigns, size_t nassigns)
18+{
19+ size_t i, len, extra;
20+ char *out;
21+
22+ if (nassigns == 0)
23+ return src;
24+ len = strlen(src);
25+ extra = len > 0 && src[len - 1] != '\n' ? 1 : 0;
26+ for (i = 0; i < nassigns; i++)
27+ extra += strlen(assigns[i]) + 1;
28+ out = malloc(len + extra + 1);
29+ if (!out) {
30+ free(src);
31+ return 0;
32+ }
33+ memcpy(out, src, len);
34+ extra = len;
35+ if (extra > 0 && out[extra - 1] != '\n')
36+ out[extra++] = '\n';
37+ for (i = 0; i < nassigns; i++) {
38+ size_t n;
39+
40+ n = strlen(assigns[i]);
41+ memcpy(out + extra, assigns[i], n);
42+ extra += n;
43+ out[extra++] = '\n';
44+ }
45+ out[extra] = 0;
46+ free(src);
47+ return out;
48+}
49+
50 static const char *
51 assignopname(enum AssignOp op)
52 {
53@@ -218,6 +263,8 @@ main(int argc, char **argv)
54 int dump_dot;
55 int dump_graph;
56 int i;
57+ char **assigns;
58+ size_t nassigns;
59 struct Ast ast;
60 struct Ast out;
61 struct Graph graph;
62@@ -226,6 +273,8 @@ main(int argc, char **argv)
63 dump_ast = 0;
64 dump_dot = 0;
65 dump_graph = 0;
66+ assigns = 0;
67+ nassigns = 0;
68 for (i = 1; i < argc; i++) {
69 if (strcmp(argv[i], "-a") == 0) {
70 dump_ast = 1;
71@@ -256,6 +305,13 @@ main(int argc, char **argv)
72 }
73 ++i;
74 path = argv[i];
75+ } else if (isvarassign(argv[i])) {
76+ assigns = realloc(assigns, (nassigns + 1) * sizeof(assigns[0]));
77+ if (!assigns) {
78+ fprintf(stderr, "out of memory\n");
79+ return 1;
80+ }
81+ assigns[nassigns++] = argv[i];
82 } else if (argv[i][0] == '-') {
83 usage(stderr, argv[0]);
84 return 1;
85@@ -288,6 +344,12 @@ main(int argc, char **argv)
86 }
87 }
88 }
89+ src = appendassigns(src, assigns, nassigns);
90+ free(assigns);
91+ if (!src) {
92+ fprintf(stderr, "out of memory\n");
93+ return 1;
94+ }
95 if (parse(path, src, &ast) < 0) {
96 fprintf(stderr, "parse error in %s\n", path);
97 free(src);
+75,
-0
1@@ -24,8 +24,69 @@ struct GraphState {
2 struct SufRules sufs;
3 struct TAssign *tas;
4 size_t ntas;
5+ int saw_suffixes;
6 };
7
8+static int
9+isonesuffix(const char *s, char **from)
10+{
11+ if (!s || s[0] != '.')
12+ return 0;
13+ if (!s[1] || strchr(s + 1, '.'))
14+ return 0;
15+ *from = xstrdup(s);
16+ return 1;
17+}
18+
19+static int
20+istwosuffix(const char *s, char **from, char **to)
21+{
22+ const char *mid;
23+
24+ if (!s || s[0] != '.')
25+ return 0;
26+ mid = strchr(s + 1, '.');
27+ if (!mid || mid == s + 1 || !mid[1] || strchr(mid + 1, '.'))
28+ return 0;
29+ *from = xstrndup(s, (size_t)(mid - s));
30+ *to = xstrdup(mid);
31+ return 1;
32+}
33+
34+static void
35+seedsufs(struct SufRules *rules, const struct RuleNode *rule)
36+{
37+ char *from, *to;
38+
39+ if (rule->targets.n != 1 || rule->prereqs.n != 0 || rule->order_only.n != 0)
40+ return;
41+ if (isonesuffix(rule->targets.v[0], &from)) {
42+ struct StrList list;
43+
44+ memset(&list, 0, sizeof(list));
45+ list.v = &from;
46+ list.n = 1;
47+ addsufs(&rules->active, &list);
48+ free(from);
49+ return;
50+ }
51+ if (!istwosuffix(rule->targets.v[0], &from, &to))
52+ return;
53+ {
54+ char *tmp[2];
55+ struct StrList list;
56+
57+ tmp[0] = from;
58+ tmp[1] = to;
59+ memset(&list, 0, sizeof(list));
60+ list.v = tmp;
61+ list.n = 2;
62+ addsufs(&rules->active, &list);
63+ }
64+ free(from);
65+ free(to);
66+}
67+
68 static void
69 collectrule(struct GraphState *gs, const struct RuleNode *rule)
70 {
71@@ -153,6 +214,7 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
72 memset(&gs, 0, sizeof(gs));
73 gs.graph = graph;
74 seedenv(&gs.env);
75+ imprules(&gs.sufs);
76
77 /* get the state, env vars in gs.env, target-specific var in gs.tas,
78 * and rules in gs.rules */
79@@ -163,10 +225,23 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
80 else
81 evalassign(&gs.env, &ast->v[i].data.assign);
82 } else if (ast->v[i].kind == NODE_RULE) {
83+ if (issufrule(&ast->v[i].data.rule)) {
84+ gs.saw_suffixes = 1;
85+ if (ast->v[i].data.rule.prereqs.n == 0)
86+ clearsufs(&gs.sufs.active);
87+ else
88+ addsufs(&gs.sufs.active, &ast->v[i].data.rule.prereqs);
89+ continue;
90+ }
91 collectrule(&gs, &ast->v[i].data.rule);
92 }
93 }
94
95+ if (!gs.saw_suffixes) {
96+ for (i = 0; i < gs.nrules; i++)
97+ seedsufs(&gs.sufs, gs.rules[i]);
98+ }
99+
100 for (i = 0; i < gs.nrules; i++) {
101 size_t k;
102
+245,
-44
1@@ -4,6 +4,90 @@
2 #include <stdlib.h>
3 #include <string.h>
4
5+static int
6+sufeq(const char *a, const char *b)
7+{
8+ return strcmp(a, b) == 0;
9+}
10+
11+static int
12+sufidx(const struct SuffixList *list, const char *suf)
13+{
14+ size_t i;
15+
16+ for (i = 0; i < list->n; i++) {
17+ if (sufeq(list->v[i], suf))
18+ return (int)i;
19+ }
20+ return -1;
21+}
22+
23+static int
24+sufactive(const struct SuffixList *list, const char *suf)
25+{
26+ return sufidx(list, suf) >= 0;
27+}
28+
29+static void
30+addimprecipe(struct RecipeList *recipes, const char *line)
31+{
32+ recipes->v = xrealloc(recipes->v, (recipes->n + 1) * sizeof(recipes->v[0]));
33+ recipes->v[recipes->n++] = xstrdup(line);
34+}
35+
36+static void
37+addimpsingle(struct SufRules *rules, const char *from, const char *const *recipev, size_t nrecipe)
38+{
39+ size_t i, k;
40+ struct SingleSufRule *sr;
41+
42+ for (i = 0; i < rules->nsinglesuf; i++) {
43+ if (strcmp(rules->singlesuf[i].from, from) == 0) {
44+ freerecipes(&rules->singlesuf[i].recipes);
45+ memset(&rules->singlesuf[i].recipes, 0, sizeof(rules->singlesuf[i].recipes));
46+ for (k = 0; k < nrecipe; k++)
47+ addimprecipe(&rules->singlesuf[i].recipes, recipev[k]);
48+ return;
49+ }
50+ }
51+ rules->singlesuf = xrealloc(rules->singlesuf,
52+ (rules->nsinglesuf + 1) * sizeof(rules->singlesuf[0]));
53+ sr = &rules->singlesuf[rules->nsinglesuf++];
54+ memset(sr, 0, sizeof(*sr));
55+ sr->from = xstrdup(from);
56+ for (k = 0; k < nrecipe; k++)
57+ addimprecipe(&sr->recipes, recipev[k]);
58+}
59+
60+static void
61+addimpdouble(struct SufRules *rules,
62+ const char *from,
63+ const char *to,
64+ const char *const *recipev,
65+ size_t nrecipe)
66+{
67+ size_t i, k;
68+ struct SufRule *sr;
69+
70+ for (i = 0; i < rules->nsufs; i++) {
71+ if (strcmp(rules->sufs[i].from, from) == 0 &&
72+ strcmp(rules->sufs[i].to, to) == 0) {
73+ freerecipes(&rules->sufs[i].recipes);
74+ memset(&rules->sufs[i].recipes, 0, sizeof(rules->sufs[i].recipes));
75+ for (k = 0; k < nrecipe; k++)
76+ addimprecipe(&rules->sufs[i].recipes, recipev[k]);
77+ return;
78+ }
79+ }
80+ rules->sufs = xrealloc(rules->sufs, (rules->nsufs + 1) * sizeof(rules->sufs[0]));
81+ sr = &rules->sufs[rules->nsufs++];
82+ memset(sr, 0, sizeof(*sr));
83+ sr->from = xstrdup(from);
84+ sr->to = xstrdup(to);
85+ for (k = 0; k < nrecipe; k++)
86+ addimprecipe(&sr->recipes, recipev[k]);
87+}
88+
89 static int
90 pathorargetexists(const struct Graph *graph, const char *name)
91 {
92@@ -153,6 +237,8 @@ seedenv(struct Env *env)
93 envsetvar(env, "CFLAGS", xstrdup(""), 1);
94 envsetvar(env, "CXX", xstrdup("c++"), 1);
95 envsetvar(env, "CPP", xstrdup("$(CC) -E"), 0);
96+ envsetvar(env, "FC", xstrdup("fort77"), 1);
97+ envsetvar(env, "FFLAGS", xstrdup(""), 1);
98 envsetvar(env, "AR", xstrdup("ar"), 1);
99 envsetvar(env, "ARFLAGS", xstrdup("-rv"), 1);
100 envsetvar(env, "AS", xstrdup("as"), 1);
101@@ -169,6 +255,99 @@ seedenv(struct Env *env)
102 envsetvar(env, "SHELL", xstrdup("/bin/sh"), 1);
103 }
104
105+void
106+imprules(struct SufRules *rules)
107+{
108+ static const char *const c_bin[] = {
109+ "$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<",
110+ };
111+ static const char *const f_bin[] = {
112+ "$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<",
113+ };
114+ static const char *const sh_bin[] = {
115+ "cp $< $@",
116+ "chmod a+x $@",
117+ };
118+ /* TODO -o here is NOT POSIX. im keeping it here for now for gnu compatibility,
119+ * because it's conveinient. when .POSIX is passed, we should drop the -o. */
120+ static const char *const c_o[] = {
121+ "$(CC) $(CFLAGS) -c -o $@ $<",
122+ };
123+ static const char *const f_o[] = {
124+ "$(FC) $(FFLAGS) -c -o $@ $<",
125+ };
126+ static const char *const y_o[] = {
127+ "$(YACC) $(YFLAGS) $<",
128+ "$(CC) $(CFLAGS) -c y.tab.c",
129+ "rm -f y.tab.c",
130+ "mv y.tab.o $@",
131+ };
132+ static const char *const l_o[] = {
133+ "$(LEX) $(LFLAGS) $<",
134+ "$(CC) $(CFLAGS) -c lex.yy.c",
135+ "rm -f lex.yy.c",
136+ "mv lex.yy.o $@",
137+ };
138+ static const char *const y_c[] = {
139+ "$(YACC) $(YFLAGS) $<",
140+ "mv y.tab.c $@",
141+ };
142+ static const char *const l_c[] = {
143+ "$(LEX) $(LFLAGS) $<",
144+ "mv lex.yy.c $@",
145+ };
146+ static const char *const suffixes[] = {
147+ ".o", ".c", ".f", ".y", ".l", ".sh",
148+ };
149+ struct StrList list;
150+
151+ memset(&list, 0, sizeof(list));
152+ list.v = (char **)suffixes;
153+ list.n = sizeof(suffixes) / sizeof(suffixes[0]);
154+ addsufs(&rules->active, &list);
155+
156+ addimpsingle(rules, ".c", c_bin, sizeof(c_bin) / sizeof(c_bin[0]));
157+ addimpsingle(rules, ".f", f_bin, sizeof(f_bin) / sizeof(f_bin[0]));
158+ addimpsingle(rules, ".sh", sh_bin, sizeof(sh_bin) / sizeof(sh_bin[0]));
159+ addimpdouble(rules, ".c", ".o", c_o, sizeof(c_o) / sizeof(c_o[0]));
160+ addimpdouble(rules, ".f", ".o", f_o, sizeof(f_o) / sizeof(f_o[0]));
161+ addimpdouble(rules, ".y", ".o", y_o, sizeof(y_o) / sizeof(y_o[0]));
162+ addimpdouble(rules, ".l", ".o", l_o, sizeof(l_o) / sizeof(l_o[0]));
163+ addimpdouble(rules, ".y", ".c", y_c, sizeof(y_c) / sizeof(y_c[0]));
164+ addimpdouble(rules, ".l", ".c", l_c, sizeof(l_c) / sizeof(l_c[0]));
165+}
166+
167+int
168+issufrule(const struct RuleNode *rule)
169+{
170+ return rule->targets.n == 1 && strcmp(rule->targets.v[0], ".SUFFIXES") == 0;
171+}
172+
173+void
174+addsufs(struct SuffixList *list, const struct StrList *sufs)
175+{
176+ size_t i;
177+
178+ for (i = 0; i < sufs->n; i++) {
179+ if (sufactive(list, sufs->v[i]))
180+ continue;
181+ list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
182+ list->v[list->n++] = xstrdup(sufs->v[i]);
183+ }
184+}
185+
186+void
187+clearsufs(struct SuffixList *list)
188+{
189+ size_t i;
190+
191+ for (i = 0; i < list->n; i++)
192+ free(list->v[i]);
193+ free(list->v);
194+ list->v = 0;
195+ list->n = 0;
196+}
197+
198 int
199 collectsufrule(struct SufRules *rules, const struct RuleNode *rule)
200 {
201@@ -231,61 +410,82 @@ instsufrule(const struct SufRules *rules,
202 {
203 size_t i, k;
204
205- for (i = 0; i < rules->nsufs; i++) {
206- char *stem, *src;
207+ for (i = 0; i < rules->active.n; i++) {
208+ size_t j, r;
209
210- if (!matchsuf(rules->sufs[i].to, t->name, &stem))
211- continue;
212- src = cat3(stem, "", rules->sufs[i].from);
213- t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
214- memmove(t->prereqs.v + 1, t->prereqs.v, t->prereqs.n * sizeof(t->prereqs.v[0]));
215- t->prereqs.v[0] = src;
216- t->prereqs.n++;
217- for (k = 0; k < rules->sufs[i].recipes.n; k++) {
218- char *vars, *exp;
219-
220- vars = expandstr(env, rules->sufs[i].recipes.v[k]);
221- exp = expandauto(vars, t, stem);
222- free(vars);
223- if (!exp[0]) {
224- free(exp);
225- continue;
226+ for (j = 0; j < rules->active.n; j++) {
227+ for (r = 0; r < rules->nsufs; r++) {
228+ char *stem, *src;
229+
230+ if (!sufeq(rules->sufs[r].to, rules->active.v[i]))
231+ continue;
232+ if (!sufeq(rules->sufs[r].from, rules->active.v[j]))
233+ continue;
234+ if (!matchsuf(rules->sufs[r].to, t->name, &stem))
235+ continue;
236+ src = cat3(stem, "", rules->sufs[r].from);
237+ if (!pathorargetexists(graph, src)) {
238+ free(stem);
239+ free(src);
240+ continue;
241+ }
242+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
243+ memmove(t->prereqs.v + 1, t->prereqs.v, t->prereqs.n * sizeof(t->prereqs.v[0]));
244+ t->prereqs.v[0] = src;
245+ t->prereqs.n++;
246+ for (k = 0; k < rules->sufs[r].recipes.n; k++) {
247+ char *vars, *exp;
248+
249+ vars = expandstr(env, rules->sufs[r].recipes.v[k]);
250+ exp = expandauto(vars, t, stem);
251+ free(vars);
252+ if (!exp[0]) {
253+ free(exp);
254+ continue;
255+ }
256+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
257+ t->recipes.v[t->recipes.n++] = exp;
258+ }
259+ free(stem);
260+ return 1;
261 }
262- t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
263- t->recipes.v[t->recipes.n++] = exp;
264 }
265- free(stem);
266- return 1;
267 }
268
269 if (strchr(t->name, '.'))
270 return 0;
271- for (i = 0; i < rules->nsinglesuf; i++) {
272- char *src;
273+ for (i = 0; i < rules->active.n; i++) {
274+ size_t j;
275
276- src = cat3(t->name, "", rules->singlesuf[i].from);
277- if (!pathorargetexists(graph, src)) {
278- free(src);
279- continue;
280- }
281- t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
282- memmove(t->prereqs.v + 1, t->prereqs.v, t->prereqs.n * sizeof(t->prereqs.v[0]));
283- t->prereqs.v[0] = src;
284- t->prereqs.n++;
285- for (k = 0; k < rules->singlesuf[i].recipes.n; k++) {
286- char *vars, *exp;
287-
288- vars = expandstr(env, rules->singlesuf[i].recipes.v[k]);
289- exp = expandauto(vars, t, t->name);
290- free(vars);
291- if (!exp[0]) {
292- free(exp);
293+ for (j = 0; j < rules->nsinglesuf; j++) {
294+ char *src;
295+
296+ if (!sufeq(rules->singlesuf[j].from, rules->active.v[i]))
297+ continue;
298+ src = cat3(t->name, "", rules->singlesuf[j].from);
299+ if (!pathorargetexists(graph, src)) {
300+ free(src);
301 continue;
302 }
303- t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
304- t->recipes.v[t->recipes.n++] = exp;
305+ t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
306+ memmove(t->prereqs.v + 1, t->prereqs.v, t->prereqs.n * sizeof(t->prereqs.v[0]));
307+ t->prereqs.v[0] = src;
308+ t->prereqs.n++;
309+ for (k = 0; k < rules->singlesuf[j].recipes.n; k++) {
310+ char *vars, *exp;
311+
312+ vars = expandstr(env, rules->singlesuf[j].recipes.v[k]);
313+ exp = expandauto(vars, t, t->name);
314+ free(vars);
315+ if (!exp[0]) {
316+ free(exp);
317+ continue;
318+ }
319+ t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
320+ t->recipes.v[t->recipes.n++] = exp;
321+ }
322+ return 1;
323 }
324- return 1;
325 }
326 return 0;
327 }
328@@ -304,6 +504,7 @@ freesufrules(struct SufRules *rules)
329 free(rules->sufs[i].to);
330 freerecipes(&rules->sufs[i].recipes);
331 }
332+ clearsufs(&rules->active);
333 free(rules->singlesuf);
334 free(rules->sufs);
335 rules->singlesuf = 0;
+13,
-3
1@@ -12,6 +12,11 @@ struct SingleSufRule {
2 struct RecipeList recipes;
3 };
4
5+struct SuffixList {
6+ char **v;
7+ size_t n;
8+};
9+
10 /* normal suffix like .c.o */
11 struct SufRule {
12 char *from;
13@@ -25,14 +30,19 @@ struct SufRules {
14 size_t nsinglesuf;
15 struct SufRule *sufs;
16 size_t nsufs;
17+ struct SuffixList active;
18 };
19
20 void seedenv(struct Env *env);
21+void imprules(struct SufRules *rules);
22 int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
23 int instsufrule(const struct SufRules *rules,
24- const struct Graph *graph,
25- struct Target *t,
26- struct Env *env);
27+ const struct Graph *graph,
28+ struct Target *t,
29+ struct Env *env);
30+int issufrule(const struct RuleNode *rule);
31+void addsufs(struct SuffixList *list, const struct StrList *sufs);
32+void clearsufs(struct SuffixList *list);
33 void freesufrules(struct SufRules *rules);
34
35 #endif
+22,
-2
1@@ -12,6 +12,7 @@ workdir=
2 dryrun=0
3 jobs=
4 targets=
5+vars=
6
7 lie() {
8 printf '%s\n' 'GNU Make 4.4'
9@@ -110,8 +111,16 @@ while [ "$#" -gt 0 ]; do
10 shift
11 ;;
12 *)
13- targets=${targets}${targets:+'
14+ case $1 in
15+ *=*)
16+ vars=${vars}${vars:+'
17 '}$1
18+ ;;
19+ *)
20+ targets=${targets}${targets:+'
21+'}$1
22+ ;;
23+ esac
24 shift
25 ;;
26 esac
27@@ -125,7 +134,18 @@ if probemk; then
28 exit 0
29 fi
30
31-if ! "$shin_bin" "$makefile"; then
32+set -- "$shin_bin" -f "$makefile"
33+if [ -n "$vars" ]; then
34+ oldifs=$IFS
35+ IFS='
36+'
37+ for var in $vars; do
38+ set -- "$@" "$var"
39+ done
40+ IFS=$oldifs
41+fi
42+
43+if ! "$@"; then
44 exit $?
45 fi
46