1#include "compdb.h"
2#include "internal.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8
9/* compile_commands.json backend */
10
11static void
12emitjson(FILE *fp, const char *s)
13{
14 size_t i;
15
16 for (i = 0; s[i]; i++) {
17 switch (s[i]) {
18 case '"':
19 case '\\':
20 fputc('\\', fp);
21 fputc(s[i], fp);
22 break;
23 case '\n':
24 fputs("\\n", fp);
25 break;
26 case '\r':
27 fputs("\\r", fp);
28 break;
29 case '\t':
30 fputs("\\t", fp);
31 break;
32 default:
33 fputc(s[i], fp);
34 break;
35 }
36 }
37}
38
39static int
40hassuffix(const char *s, const char *suffix)
41{
42 size_t ns, nfx;
43
44 ns = strlen(s);
45 nfx = strlen(suffix);
46 return ns >= nfx && strcmp(s + ns - nfx, suffix) == 0;
47}
48
49static int
50issource(const char *path)
51{
52 static const char *const exts[] = {
53 ".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm", 0};
54 size_t i;
55
56 for (i = 0; exts[i]; i++) {
57 if (hassuffix(path, exts[i]))
58 return 1;
59 }
60 return 0;
61}
62
63static int
64iscompile(const struct Target *t)
65{
66 const char *src;
67
68 if (t->recipes.n != 1 || totalprereqs(t) != 1)
69 return 0;
70 src = firstprereq(t);
71 if (!src || !issource(src))
72 return 0;
73 if (strstr(t->recipes.v[0].body, " -c ") || strstr(t->recipes.v[0].body, "\t-c ") ||
74 strstr(t->recipes.v[0].body, " -c\t") || hassuffix(t->recipes.v[0].body, " -c"))
75 return 1;
76 return strstr(t->recipes.v[0].body, "$<") != 0;
77}
78
79int
80gencompdb(const struct Graph *graph, const char *path)
81{
82 FILE *fp;
83 size_t i;
84 int first;
85 char cwd[4096];
86
87 fp = fopen(path, "w");
88 if (!fp)
89 return -1;
90 if (!getcwd(cwd, sizeof(cwd))) {
91 fclose(fp);
92 return -1;
93 }
94
95 fprintf(fp, "[\n");
96 first = 1;
97 for (i = 0; i < graph->n; i++) {
98 if (!iscompile(&graph->v[i]))
99 continue;
100 if (!first)
101 fprintf(fp, ",\n");
102 first = 0;
103 fprintf(fp, " {\n");
104 fprintf(fp, " \"directory\": \"");
105 emitjson(fp, cwd);
106 fprintf(fp, "\",\n");
107 fprintf(fp, " \"command\": \"");
108 emitjson(fp, graph->v[i].recipes.v[0].body);
109 fprintf(fp, "\",\n");
110 fprintf(fp, " \"file\": \"");
111 emitjson(fp, firstprereq(&graph->v[i]));
112 fprintf(fp, "\",\n");
113 fprintf(fp, " \"output\": \"");
114 emitjson(fp, graph->v[i].name);
115 fprintf(fp, "\"\n");
116 fprintf(fp, " }");
117 }
118 fprintf(fp, "\n]\n");
119 fclose(fp);
120 return 0;
121}