1#include "ninqu.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7/* ninjas own tokenizer treats '$', ':' and ' ' as special outside
8 * of a rules variables, so any path used in a build statement
9 * (outputs, inputs, deps) needs those three escaped with a leading
10 * '$'. this is a different escaping pass from shell quoting below,
11 * it runs on ninjas file syntax, not on the command it configures */
12static char *
13ninja_path_escape(const char *s)
14{
15 size_t i, j = 0;
16 size_t len = strlen(s);
17 char *out = emalloc(len * 2 + 1);
18
19 for (i = 0; i < len; i++) {
20 if (s[i] == '$' || s[i] == ':' || s[i] == ' ')
21 out[j++] = '$';
22 out[j++] = s[i];
23 }
24 out[j] = '\0';
25 return out;
26}
27
28/* ninqus argv is already fully expanded ($(VAR) substitution
29 * happened at parse time), so the ninja rules command is a plain
30 * literal string, never $in/$out. write one argv word as a single
31 * shell-quoted token: close/escape/reopen for an embedded quote,
32 * and double any '$' so ninjas own preprocessing pass on the
33 * "command" value hands the shell back exactly one '$' */
34static void
35fprint_shell_word_plain(FILE *f, const char *s)
36{
37 fputc('\'', f);
38 for (; *s; s++) {
39 if (*s == '\'')
40 fputs("'\\''", f);
41 else if (*s == '$')
42 fputs("$$", f);
43 else
44 fputc(*s, f);
45 }
46 fputc('\'', f);
47}
48
49/* ninja reads a "command = ..." value to the end of the physical
50 * line: a literal newline byte terminates the statement, and $\n is
51 * a continuation that strips the newline rather than preserving it,
52 * so neither lets a real newline survive inside the value. POSIX
53 * single quotes cannot help either, there is no escape processing
54 * inside them, so a backslash-n written between quotes stays two
55 * literal characters, not a newline, once the shell reads it back.
56 *
57 * the fix is to not carry the newline in quoted text at all: emit
58 * a command substitution that runs the word through `printf '%b'`,
59 * whose %b directive is POSIX-specified to expand backslash escapes
60 * (including \n) in its argument at runtime. the argument to printf
61 * is itself single-quoted using the same rules as the plain case,
62 * plus doubling any backslash so it survives as data rather than
63 * being read as an escape by the quoting layer itself, and the `$(`
64 * that opens the substitution needs its own `$` doubled so ninjas
65 * pass just picks it out unmodified for the shell to see */
66static void
67fprint_shell_word(FILE *f, const char *s)
68{
69 const char *p;
70 int has_nl = 0;
71
72 for (p = s; *p; p++) {
73 if (*p == '\n') {
74 has_nl = 1;
75 break;
76 }
77 }
78
79 if (!has_nl) {
80 fprint_shell_word_plain(f, s);
81 return;
82 }
83
84 /* wrapped in double quotes so the shell treats the substitutions
85 * result as one word instead of splitting it on whitespace, which
86 * is exactly what an unquoted $(...) would do to a multi-line
87 * result */
88 fputs("\"$$(printf '%b' '", f);
89 for (; *s; s++) {
90 if (*s == '\'')
91 fputs("'\\''", f);
92 else if (*s == '$')
93 fputs("$$", f);
94 else if (*s == '\\')
95 fputs("\\\\", f);
96 else if (*s == '\n')
97 fputs("\\n", f);
98 else
99 fputc(*s, f);
100 }
101 fputs("')\"", f);
102}
103
104/* an instance with no (out) has no on-disk path of its own (a
105 * (phony) rule, or a pure grouping rule). ninja still needs a
106 * target name to hang a build statement off, so one is made up
107 * from the rule name and instance index. it is never a real file,
108 * so ninja will always find it missing and re-run it, the same
109 * "always stale" behaviour inst_stale() gives phony instances in
110 * the internal engine. the name is a single flat path component
111 * (no '/'), so ninja never mkdir -p's a directory for it: ninja
112 * only creates the parent directory of a build outputs path, and a
113 * name with no slash has no parent beyond the working directory */
114static const char *
115inst_target(struct Inst *inst, int idx, char *buf, size_t bufsz)
116{
117 if (inst->out[0])
118 return inst->out;
119 snprintf(buf, bufsz, "__ninqu_phony__%s_%d", rules[inst->rule_idx].name, idx);
120 return buf;
121}
122
123/* does this instance look like "cc ... -c ... -o foo.o"? if so its
124 * a real compile step and ninja can track its header dependencies
125 * precisely via a gcc-style depfile instead of only the coarse
126 * order-only edges dep_inst gives every instance. matched by
127 * basename so whatever CC the manifest actually resolved (gcc,
128 * clang, a cross compiler under some path) is what gets the extra
129 * flags, nothing here hardcodes a specific compiler binary; the
130 * check is on -c and a .o output, not on the compiler name meaning
131 * anything beyond "probably understands -MMD -MF" */
132static int
133is_cc_compile(struct Inst *inst)
134{
135 static const char *const compilers[] = {"cc", "gcc", "clang", "g++", "c++", NULL};
136 const char *prog, *slash;
137 size_t len;
138 int k, has_c;
139
140 if (inst->phony || !inst->out[0] || inst->cmd.is_pipe || inst->cmd.argv.n < 2)
141 return 0;
142
143 len = strlen(inst->out);
144 if (len < 2 || strcmp(inst->out + len - 2, ".o") != 0)
145 return 0;
146
147 prog = inst->cmd.argv.v[0];
148 slash = strrchr(prog, '/');
149 if (slash)
150 prog = slash + 1;
151
152 for (k = 0; compilers[k]; k++)
153 if (strcmp(prog, compilers[k]) == 0)
154 break;
155 if (!compilers[k])
156 return 0;
157
158 for (k = 1, has_c = 0; k < inst->cmd.argv.n; k++)
159 if (strcmp(inst->cmd.argv.v[k], "-c") == 0)
160 has_c = 1;
161
162 return has_c;
163}
164
165/* the command a rule runs: an optional workdir cd, the argv or
166 * piped stages shell-quoted word by word, and an optional stdout
167 * redirect into (out). mirrors spawn_inst()/spawn_pipe() exactly,
168 * just written out as text instead of forked and execd. a compile
169 * step (is_cc_compile()) gets "-MMD -MF <out>.d" spliced in right
170 * after the compiler name, paired with the depfile/deps lines
171 * emit_ninja() writes into that instances rule block */
172static void
173emit_command(FILE *f, struct Inst *inst)
174{
175 int is_cc = is_cc_compile(inst);
176 int i;
177
178 if (inst->workdir[0]) {
179 fputs("cd ", f);
180 fprint_shell_word(f, inst->workdir);
181 fputs(" && ", f);
182 }
183
184 if (inst->cmd.is_pipe) {
185 for (i = 0; i < inst->cmd.nstages; i++) {
186 int k;
187 if (i > 0)
188 fputs(" | ", f);
189 for (k = 0; k < inst->cmd.stages[i]->n; k++) {
190 if (k > 0)
191 fputc(' ', f);
192 fprint_shell_word(f, inst->cmd.stages[i]->v[k]);
193 }
194 }
195 } else {
196 for (i = 0; i < inst->cmd.argv.n; i++) {
197 if (i > 0)
198 fputc(' ', f);
199 fprint_shell_word(f, inst->cmd.argv.v[i]);
200 if (i == 0 && is_cc) {
201 fputs(" -MMD -MF ", f);
202 fprint_shell_word(f, inst->out);
203 fputs(".d", f);
204 }
205 }
206 }
207
208 if (inst->redirect) {
209 fputs(" > ", f);
210 fprint_shell_word(f, inst->out);
211 }
212}
213
214/* write every instance currently in insts[] (the same closure the
215 * internal engine would have built, wanted targets plus their
216 * transitive deps) out as a build.ninja. one rule per instance
217 * (ninqu already resolved everything to a literal command line, so
218 * there is no shared rule template to factor out), one build
219 * statement per instance. dep_inst edges become order-only ("||"):
220 * inst_stale() never consults dep_inst for staleness either, only
221 * (in)/(stale_extra), so mirroring that as a plain (not order-only)
222 * prerequisite list is what makes ninjas own mtime check agree
223 * with ninqus.
224 *
225 * ninja only knows build outputs, never ninqus rule/group/meta
226 * names, so `ninja LIBUTIL` (what the Makefile forwards $@ as) has
227 * nothing to resolve against on its own. wanted carries the literal
228 * command-line words ninqu itself was invoked with, one phony alias
229 * per word makes that name buildable too. when more than one target
230 * was requested in the same invocation every alias points at the
231 * whole combined closure rather than just its own slice, since
232 * insts[] no longer remembers which instance came from which wanted
233 * name, thats fine for the Makefiles own use (always one target
234 * per invocation), just slightly conservative for a multi-target
235 * `ninqu -G ninja a b` run */
236void
237emit_ninja(const char *path, struct StrList *wanted)
238{
239 FILE *f = fopen(path, "w");
240 int i, k;
241
242 if (!f)
243 eprintf("emit_ninja: open %s:", path);
244
245 fputs("# autogenerated by `ninqu -G ninja`, do not edit\n", f);
246 fputs("ninja_required_version = 1.3\n\n", f);
247
248 for (i = 0; i < ninsts; i++) {
249 struct Inst *inst = &insts[i];
250 fprintf(f, "rule r%d\n", i);
251 fprintf(
252 f,
253 " description = %s %s\n",
254 rules[inst->rule_idx].name,
255 inst->out[0] ? inst->out : "(phony)"
256 );
257 if (is_cc_compile(inst)) {
258 char *esc = ninja_path_escape(inst->out);
259 fprintf(f, " depfile = %s.d\n", esc);
260 fputs(" deps = gcc\n", f);
261 free(esc);
262 }
263 fputs(" command = ", f);
264 emit_command(f, inst);
265 fputs("\n\n", f);
266 }
267
268 for (i = 0; i < ninsts; i++) {
269 struct Inst *inst = &insts[i];
270 char buf[1024];
271 const char *out = inst_target(inst, i, buf, sizeof buf);
272 char *esc = ninja_path_escape(out);
273
274 fprintf(f, "build %s: r%d", esc, i);
275 free(esc);
276
277 for (k = 0; k < inst->in.n; k++) {
278 esc = ninja_path_escape(inst->in.v[k]);
279 fprintf(f, " %s", esc);
280 free(esc);
281 }
282 for (k = 0; k < inst->stale_extra.n; k++) {
283 esc = ninja_path_escape(inst->stale_extra.v[k]);
284 fprintf(f, " %s", esc);
285 free(esc);
286 }
287
288 if (inst->n_dep > 0) {
289 int printed_bar = 0;
290 for (k = 0; k < inst->n_dep; k++) {
291 struct Inst *dep = &insts[inst->dep_inst[k]];
292 char depbuf[1024];
293 const char *depout = inst_target(dep, inst->dep_inst[k], depbuf, sizeof depbuf);
294 /* a rule ref that is also a real (dep ...) input already
295 * forces both order and staleness, listing it again as
296 * order-only is pure noise */
297 if (sl_has(&inst->in, depout) || sl_has(&inst->stale_extra, depout))
298 continue;
299 if (!printed_bar) {
300 fputs(" ||", f);
301 printed_bar = 1;
302 }
303 esc = ninja_path_escape(depout);
304 fprintf(f, " %s", esc);
305 free(esc);
306 }
307 }
308 fputc('\n', f);
309 }
310
311 fputs("\ndefault", f);
312 for (i = 0; i < ninsts; i++) {
313 struct Inst *inst = &insts[i];
314 char buf[1024];
315 const char *out = inst_target(inst, i, buf, sizeof buf);
316 char *esc = ninja_path_escape(out);
317 fprintf(f, " %s", esc);
318 free(esc);
319 }
320 fputc('\n', f);
321
322 for (i = 0; i < wanted->n; i++) {
323 const char *name = wanted->v[i];
324 int dup = 0;
325
326 for (k = 0; k < ninsts && !dup; k++) {
327 char buf[1024];
328 const char *out = inst_target(&insts[k], k, buf, sizeof buf);
329 if (strcmp(out, name) == 0)
330 dup = 1;
331 }
332 if (dup)
333 continue;
334
335 {
336 char *esc = ninja_path_escape(name);
337 fprintf(f, "build %s: phony", esc);
338 free(esc);
339 }
340 for (k = 0; k < ninsts; k++) {
341 char buf[1024];
342 const char *out = inst_target(&insts[k], k, buf, sizeof buf);
343 char *esc = ninja_path_escape(out);
344 fprintf(f, " %s", esc);
345 free(esc);
346 }
347 fputc('\n', f);
348 }
349
350 if (fshut(f, path))
351 eprintf("emit_ninja: %s:", path);
352 printf(" GEN %s\n", path);
353}