]>
git.saurik.com Git - apple/xnu.git/blob - SETUP/json_compilation_db/json_compilation_db.c
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * json_compilation_db is a helper tool that takes a compiler invocation, and
26 * appends it in JSON format to the specified database.
39 #include <sys/fcntl.h>
40 #include <sys/param.h>
43 char *escape_string(const char *);
46 * We support appending to two databases.
59 int main(int argc
, char * argv
[])
65 const char *json_output
= NULL
;
66 const char *cwd
= NULL
;
67 const char *input_file
= NULL
;
71 size_t input_file_len
;
77 json_output
= argv
[1];
84 input_file_len
= strlen(input_file
);
85 if (!(input_file_len
> 2 && 0 == strcmp(".c", input_file
+ input_file_len
- 2)) &&
86 !(input_file_len
> 3 && 0 == strcmp(".cp", input_file
+ input_file_len
- 3)) &&
87 !(input_file_len
> 4 && 0 == strcmp(".cpp", input_file
+ input_file_len
- 4))) {
88 /* Not a C/C++ file, just skip it */
92 dstfd
= open(json_output
, O_RDWR
| O_CREAT
| O_EXLOCK
, DEFFILEMODE
);
94 err(EX_NOINPUT
, "open(%s)", json_output
);
96 ret
= fstat(dstfd
, &sb
);
98 err(EX_NOINPUT
, "fstat(%s)", json_output
);
100 if (!S_ISREG(sb
.st_mode
))
101 err(EX_USAGE
, "%s is not a regular file", json_output
);
103 dst
= fdopen(dstfd
, "w+");
105 err(EX_UNAVAILABLE
, "fdopen");
107 read_bytes
= fread(start
, sizeof(start
[0]), sizeof(start
)/sizeof(start
[0]), dst
);
108 if ((read_bytes
!= sizeof(start
)) || (0 != memcmp(start
, "[\n", sizeof(start
)/sizeof(start
[0])))) {
109 /* no JSON start, we don't really care why */
110 ret
= fseeko(dst
, 0, SEEK_SET
);
112 err(EX_UNAVAILABLE
, "fseeko");
114 ret
= fputs("[", dst
);
116 err(EX_UNAVAILABLE
, "fputs");
118 /* has at least two bytes at the start. Seek to 3 bytes before the end */
119 ret
= fseeko(dst
, -3, SEEK_END
);
121 err(EX_UNAVAILABLE
, "fseeko");
123 ret
= fputs(",", dst
);
125 err(EX_UNAVAILABLE
, "fputs");
130 fprintf(dst
, " \"directory\": \"%s\",\n", cwd
);
131 fprintf(dst
, " \"file\": \"%s\",\n", input_file
);
132 fprintf(dst
, " \"command\": \"");
133 for (i
=0; i
< argc
; i
++) {
134 bool needs_escape
= strchr(argv
[i
], '\\') || strchr(argv
[i
], '"') || strchr(argv
[i
], ' ');
137 char *escaped_string
= escape_string(argv
[i
]);
138 fprintf(dst
, "%s\\\"%s\\\"", i
== 0 ? "" : " ", escaped_string
);
139 free(escaped_string
);
141 fprintf(dst
, "%s%s", i
== 0 ? "" : " ", argv
[i
]);
144 fprintf(dst
, "\"\n");
150 err(EX_UNAVAILABLE
, "fclose");
157 fprintf(stderr
, "Usage: %s <json_output> <cwd> <input_file> <compiler> [<invocation> ...]\n", getprogname());
162 * A valid JSON string can't contain \ or ", so we look for these in our argv[] array (which
163 * our parent shell would have done shell metacharacter evaluation on, and escape just these.
164 * The entire string is put in \" escaped quotes to handle spaces that are valid JSON
165 * but should be used for grouping when running the compiler for real.
168 escape_string(const char *input
)
170 size_t len
= strlen(input
);
172 char *output
= malloc(len
* 4 + 1);
174 for (i
=0, j
=0; i
< len
; i
++) {
177 if (ch
== '\\' || ch
== '"') {
179 output
[j
++] = '\\'; /* output \\ in JSON, which the final shell will see as \ */
180 output
[j
++] = '\\'; /* escape \ or ", which the final shell will see and pass to the compiler */