commit 0daea18
shrub
·
2026-09-10 16:50:56 +0000 UTC
parent a27c0ec
fix compdb backend
5 files changed,
+161,
-76
M
TODO
+2,
-1
1@@ -30,4 +30,5 @@ special vars
2
3 other
4 performance, performance, performance. less string ops, better memory model
5- write more tests, observe behaviour other suites don't
6+ write more tests, observe behaviour other suites don't
7+ there are some auto var differences between posix08/24/gnu and we also arent handling $? correctly
+125,
-36
1@@ -50,7 +50,7 @@ static int
2 issource(const char *path)
3 {
4 static const char *const exts[] = {
5- ".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm", 0};
6+ ".c", ".cc", ".C", ".cpp", ".cxx", ".c++", ".m", ".mm", 0};
7 size_t i;
8
9 for (i = 0; exts[i]; i++) {
10@@ -61,61 +61,150 @@ issource(const char *path)
11 }
12
13 static int
14-iscompile(const struct Target *t)
15+argboundary(char c)
16 {
17- const char *src;
18-
19- if (t->recipes.n != 1 || totalprereqs(t) != 1)
20- return 0;
21- src = firstprereq(t);
22- if (!src || !issource(src))
23- return 0;
24- if (strstr(t->recipes.v[0].body, " -c ") || strstr(t->recipes.v[0].body, "\t-c ") ||
25- strstr(t->recipes.v[0].body, " -c\t") || hassuffix(t->recipes.v[0].body, " -c"))
26- return 1;
27- return strstr(t->recipes.v[0].body, "$<") != 0;
28+ return c == 0 || c == ' ' || c == '\t' || c == '\n' || c == ';' || c == '&' || c == '|' ||
29+ c == '(' || c == ')' || c == '\'' || c == '"';
30 }
31
32-int
33-gencompdb(const struct Graph *graph, const char *path)
34+static int
35+hasarg(const char *command, const char *arg)
36 {
37- FILE *fp;
38- size_t i;
39- int first;
40- char cwd[4096];
41+ size_t n;
42+ const char *p;
43
44- fp = fopen(path, "w");
45- if (!fp)
46- return -1;
47- if (!getcwd(cwd, sizeof(cwd))) {
48- fclose(fp);
49- return -1;
50+ n = strlen(arg);
51+ for (p = command; (p = strstr(p, arg)) != 0; p++) {
52+ if ((p == command || argboundary(p[-1])) && argboundary(p[n]))
53+ return 1;
54 }
55+ return 0;
56+}
57+
58+static int
59+findcompile(const struct Target *t, const char **source, const char **command)
60+{
61+ size_t i, j, nowner, nprereqs;
62+
63+ for (i = 0; i < t->recipes.n; i++) {
64+ const char *body, *fallback;
65+
66+ body = t->recipes.v[i].body;
67+ if (!hasarg(body, "-c"))
68+ continue;
69+ fallback = 0;
70+ nowner = t->owner ? strlen(t->owner) : 0;
71+ nprereqs = t->impprereqs.n + t->prereqs.n;
72+ for (j = 0; j < nprereqs; j++) {
73+ const char *p, *local;
74+
75+ p = j < t->impprereqs.n ? t->impprereqs.v[j] :
76+ t->prereqs.v[j - t->impprereqs.n];
77+ if (!issource(p))
78+ continue;
79+ if (!fallback)
80+ fallback = p;
81+ local = nowner && strncmp(p, t->owner, nowner) == 0 && p[nowner] == '/'
82+ ? p + nowner + 1 : p;
83+ if (hasarg(body, local)) {
84+ fallback = p;
85+ break;
86+ }
87+ }
88+ if (fallback) {
89+ *source = fallback;
90+ *command = body;
91+ return 1;
92+ }
93+ }
94+ return 0;
95+}
96+
97+static char *
98+fullpath(const char *cwd, const char *path)
99+{
100+ char *joined, *normalized;
101+
102+ joined = joinpath(cwd, path);
103+ normalized = normpath(joined);
104+ free(joined);
105+ return normalized;
106+}
107+
108+static void
109+emitgraph(FILE *fp, const struct Graph *graph, const char *prefix, const char *cwd, int *first)
110+{
111+ size_t i;
112
113- fprintf(fp, "[\n");
114- first = 1;
115 for (i = 0; i < graph->n; i++) {
116- if (!iscompile(&graph->v[i]))
117+ const struct Target *t;
118+ const char *command, *source;
119+ char *directory, *file, *output;
120+ size_t nowner;
121+
122+ t = &graph->v[i];
123+ if (!targetownedby(t, prefix))
124 continue;
125- if (!first)
126+ if (!findcompile(t, &source, &command))
127+ continue;
128+ directory = fullpath(cwd, t->owner ? t->owner : "");
129+ file = fullpath(cwd, source);
130+ output = fullpath(cwd, t->name);
131+ nowner = t->owner ? strlen(t->owner) : 0;
132+ if (nowner && strncmp(command, "cd ", 3) == 0 &&
133+ strncmp(command + 3, t->owner, nowner) == 0 &&
134+ strncmp(command + 3 + nowner, " && ", 4) == 0)
135+ command += 3 + nowner + 4;
136+ if (!*first)
137 fprintf(fp, ",\n");
138- first = 0;
139+ *first = 0;
140 fprintf(fp, " {\n");
141 fprintf(fp, " \"directory\": \"");
142- emitjson(fp, cwd);
143+ emitjson(fp, directory);
144 fprintf(fp, "\",\n");
145 fprintf(fp, " \"command\": \"");
146- emitjson(fp, graph->v[i].recipes.v[0].body);
147+ emitjson(fp, command);
148 fprintf(fp, "\",\n");
149 fprintf(fp, " \"file\": \"");
150- emitjson(fp, firstprereq(&graph->v[i]));
151+ emitjson(fp, file);
152 fprintf(fp, "\",\n");
153 fprintf(fp, " \"output\": \"");
154- emitjson(fp, graph->v[i].name);
155+ emitjson(fp, output);
156 fprintf(fp, "\"\n");
157 fprintf(fp, " }");
158+ free(directory);
159+ free(file);
160+ free(output);
161+ }
162+ for (i = 0; i < graph->nsubs; i++) {
163+ if (usesub(graph, i))
164+ emitgraph(fp, &graph->subs[i].graph, graph->subs[i].prefix, cwd, first);
165+ }
166+}
167+
168+int
169+gencompdb(const struct Graph *graph, const char *path)
170+{
171+ FILE *fp;
172+ int first, failed;
173+ char *cwd;
174+
175+ fp = fopen(path, "w");
176+ if (!fp)
177+ return -1;
178+ cwd = getcwddup();
179+ if (!cwd) {
180+ fclose(fp);
181+ return -1;
182 }
183+
184+ fprintf(fp, "[\n");
185+ first = 1;
186+ emitgraph(fp, graph, 0, cwd, &first);
187 fprintf(fp, "\n]\n");
188- fclose(fp);
189- return 0;
190+ failed = ferror(fp);
191+ free(cwd);
192+ if (fclose(fp) != 0)
193+ failed = 1;
194+ return failed ? -1 : 0;
195 }
+3,
-39
1@@ -229,42 +229,6 @@ wanteddefault(const struct Graph *graph, const char *prefix)
2 return 0;
3 }
4
5-static int
6-emitgraphsub(const struct Graph *graph, size_t idx)
7-{
8- size_t i;
9- int saw_conflict, saw_wanted;
10-
11- saw_conflict = 0;
12- saw_wanted = 0;
13- for (i = 0; i < graph->nsubs; i++) {
14- if (i == idx)
15- continue;
16- if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) != 0)
17- continue;
18- saw_conflict = 1;
19- if (graph->subs[i].wanted)
20- saw_wanted = 1;
21- }
22- if (!saw_conflict)
23- return 1;
24- if (graph->subs[idx].wanted) {
25- for (i = 0; i < idx; i++) {
26- if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0 &&
27- graph->subs[i].wanted)
28- return 0;
29- }
30- return 1;
31- }
32- if (saw_wanted)
33- return 0;
34- for (i = 0; i < idx; i++) {
35- if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0)
36- return 0;
37- }
38- return 1;
39-}
40-
41 static int
42 genninjafile(const struct Graph *graph, const char *path, const char *prefix, int root)
43 {
44@@ -276,7 +240,7 @@ genninjafile(const struct Graph *graph, const char *path, const char *prefix, in
45 char *escaped;
46
47 for (i = 0; i < graph->nsubs; i++) {
48- if (!emitgraphsub(graph, i))
49+ if (!usesub(graph, i))
50 continue;
51 char *childpath;
52
53@@ -298,12 +262,12 @@ genninjafile(const struct Graph *graph, const char *path, const char *prefix, in
54 rules = 0;
55 nrules = 0;
56 for (i = 0; i < graph->nsubs; i++) {
57- if (!emitgraphsub(graph, i))
58+ if (!usesub(graph, i))
59 continue;
60 fprintf(fp, "subninja %s/build.ninja\n", graph->subs[i].prefix);
61 }
62 for (i = 0; i < graph->nsubs; i++) {
63- if (emitgraphsub(graph, i)) {
64+ if (usesub(graph, i)) {
65 fputc('\n', fp);
66 break;
67 }
+1,
-0
1@@ -57,6 +57,7 @@ char *joinstrs(const struct StrList *list, const char *sep);
2 void addstr(struct StrList *list, const char *s);
3 int hasword(const struct StrList *list, const char *word);
4 int targetownedby(const struct Target *t, const char *owner);
5+int usesub(const struct Graph *graph, size_t idx);
6 const struct Target *defaulttarget(const struct Graph *graph, const char *owner);
7 void addnode(struct NodeList *list, struct Node node);
8 void splitwords(struct StrList *out, const char *s, size_t n);
+30,
-0
1@@ -513,6 +513,36 @@ targetownedby(const struct Target *t, const char *owner)
2 return t->owner && strcmp(t->owner, owner) == 0;
3 }
4
5+int
6+usesub(const struct Graph *graph, size_t idx)
7+{
8+ size_t i;
9+ int conflict, wanted;
10+
11+ conflict = wanted = 0;
12+ for (i = 0; i < graph->nsubs; i++) {
13+ if (i == idx || strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) != 0)
14+ continue;
15+ conflict = 1;
16+ wanted |= graph->subs[i].wanted;
17+ }
18+ if (!conflict)
19+ return 1;
20+ if (graph->subs[idx].wanted) {
21+ for (i = 0; i < idx; i++)
22+ if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0 &&
23+ graph->subs[i].wanted)
24+ return 0;
25+ return 1;
26+ }
27+ if (wanted)
28+ return 0;
29+ for (i = 0; i < idx; i++)
30+ if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0)
31+ return 0;
32+ return 1;
33+}
34+
35 const struct Target *
36 defaulttarget(const struct Graph *graph, const char *owner)
37 {