commit 0daea18

shrub  ·  2026-09-10 16:50:56 +0000 UTC
parent a27c0ec
fix compdb backend
5 files changed,  +161, -76
M TODO
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 {