blob: f14610fc8fe56dbc9c0ab03aa5f554c249b9e1ef [file] [log] [blame]
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build ignore
16
17#include "ninja.h"
18
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090019#include <stdio.h>
Shinichiro Hamajied883ef2015-07-31 13:15:04 +090020#include <stdlib.h>
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +090021#include <sys/stat.h>
22#include <unistd.h>
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +090023
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090024#include <map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090025#include <string>
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090026#include <unordered_map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090027#include <unordered_set>
28
29#include "command.h"
30#include "dep.h"
31#include "eval.h"
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +090032#include "file_cache.h"
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +090033#include "fileutil.h"
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +090034#include "find.h"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090035#include "flags.h"
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +090036#include "func.h"
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +090037#include "io.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090038#include "log.h"
Shinichiro Hamajia8e903f2015-08-06 19:41:01 +090039#include "stats.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090040#include "string_piece.h"
41#include "stringprintf.h"
42#include "strutil.h"
43#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090044#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090045
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090046static size_t FindCommandLineFlag(StringPiece cmd, StringPiece name) {
47 const size_t found = cmd.find(name);
48 if (found == string::npos || found == 0)
49 return string::npos;
50 return found;
51}
52
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090053static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
54 StringPiece name) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090055 size_t index = FindCommandLineFlag(cmd, name);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090056 if (index == string::npos)
57 return StringPiece();
58
59 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
60 index = val.find(name);
61 while (index != string::npos) {
62 val = TrimLeftSpace(val.substr(index + name.size()));
63 index = val.find(name);
64 }
65
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090066 index = val.find_first_of(" \t");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090067 return val.substr(0, index);
68}
69
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090070static bool StripPrefix(StringPiece p, StringPiece* s) {
71 if (!HasPrefix(*s, p))
72 return false;
73 *s = s->substr(p.size());
74 return true;
75}
76
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090077size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
78 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090079 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090080 return string::npos;
81 StringPiece cmd = cmdline.substr(0, index);
82 if (HasSuffix(cmd, "ccache")) {
83 index++;
84 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
85 return pos == string::npos ? string::npos : pos + index;
86 }
87 if (!StripPrefix("prebuilts/", &cmd))
88 return string::npos;
89 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
90 return string::npos;
91 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
92 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
93 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090094 }
95
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090096 StringPiece rest = cmdline.substr(index);
97 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090098}
99
100static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900101 if ((FindCommandLineFlag(cmd, " -MD") == string::npos &&
102 FindCommandLineFlag(cmd, " -MMD") == string::npos) ||
103 FindCommandLineFlag(cmd, " -c") == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900104 return false;
105 }
106
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900107 StringPiece mf = FindCommandLineFlagWithArg(cmd, " -MF");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900108 if (!mf.empty()) {
109 mf.AppendToString(out);
110 return true;
111 }
112
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900113 StringPiece o = FindCommandLineFlagWithArg(cmd, " -o");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900114 if (o.empty()) {
115 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
116 return false;
117 }
118
119 StripExt(o).AppendToString(out);
120 *out += ".d";
121 return true;
122}
123
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900124bool GetDepfileFromCommand(string* cmd, string* out) {
125 CHECK(!cmd->empty());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900126 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900127 return false;
128
129 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900130 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900131 return false;
132 }
133
134 // TODO: A hack for Makefiles generated by automake.
135
136 // A hack for Android to get .P files instead of .d.
137 string p;
138 StripExt(*out).AppendToString(&p);
139 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900140 if (cmd->find(p) != string::npos) {
141 const string rm_f = "; rm -f " + *out;
142 const size_t found = cmd->find(rm_f);
143 if (found == string::npos) {
144 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
145 }
146 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900147 return true;
148 }
149
150 // A hack for Android. For .s files, GCC does not use C
151 // preprocessor, so it ignores -MF flag.
152 string as = "/";
153 StripExt(Basename(*out)).AppendToString(&as);
154 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900155 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900156 return false;
157 }
158
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900159 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900160 *cmd += *out;
161 *cmd += ' ';
162 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900163 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900164 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900165 return true;
166}
167
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900168class NinjaGenerator {
169 public:
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900170 NinjaGenerator(Evaluator* ev, double start_time)
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900171 : ce_(ev),
172 ev_(ev),
173 fp_(NULL),
174 rule_id_(0),
175 start_time_(start_time),
Shinichiro Hamaji11347162016-01-20 16:23:22 +0900176 default_target_(NULL) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900177 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700178 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900179 if (g_flags.goma_dir)
180 gomacc_ = StringPrintf("%s/gomacc ", g_flags.goma_dir);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900181
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900182 GetExecutablePath(&kati_binary_);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900183 }
184
185 ~NinjaGenerator() {
186 ev_->set_avoid_io(false);
187 }
188
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900189 void Generate(const vector<DepNode*>& nodes,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900190 const string& orig_args) {
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900191 unlink(GetNinjaStampFilename().c_str());
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900192 GenerateNinja(nodes, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900193 GenerateShell();
Dan Willemsen87417412015-08-24 17:57:12 -0700194 GenerateStamp(orig_args);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900195 }
196
Shinichiro Hamajied8db982016-01-27 06:12:33 +0000197 static string GetStampTempFilename() {
198 return GetFilename(".kati_stamp%s.tmp");
Shinichiro Hamaji8ef0ce52016-01-19 18:07:43 +0900199 }
200
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900201 static string GetFilename(const char* fmt) {
202 string r = g_flags.ninja_dir ? g_flags.ninja_dir : ".";
203 r += '/';
204 r += StringPrintf(fmt, g_flags.ninja_suffix ? g_flags.ninja_suffix : "");
205 return r;
206 }
207
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900208 private:
209 string GenRuleName() {
210 return StringPrintf("rule%d", rule_id_++);
211 }
212
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900213 StringPiece TranslateCommand(const char* in, string* cmd_buf) {
214 const size_t orig_size = cmd_buf->size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900215 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900216 // Set space as an initial value so the leading comment will be
217 // stripped out.
218 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900219 char quote = 0;
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900220 for (; *in; in++) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900221 switch (*in) {
222 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900223 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900224 while (in[1] && *in != '\n')
225 in++;
Colin Cross415e4a12015-07-15 18:21:38 -0700226 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900227 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900228 }
Colin Cross415e4a12015-07-15 18:21:38 -0700229 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900230
231 case '\'':
232 case '"':
233 case '`':
234 if (quote) {
235 if (quote == *in)
236 quote = 0;
237 } else if (!prev_backslash) {
238 quote = *in;
239 }
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900240 *cmd_buf += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900241 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900242
243 case '$':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900244 *cmd_buf += "$$";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900245 break;
246
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900247 case '\n':
248 if (prev_backslash) {
Shinichiro Hamajia4dfe752015-07-28 15:54:36 +0900249 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900250 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900251 *cmd_buf += ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900252 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900253 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900254
255 case '\\':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900256 *cmd_buf += '\\';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900257 break;
258
259 default:
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900260 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900261 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700262
263 if (*in == '\\') {
264 prev_backslash = !prev_backslash;
265 } else {
266 prev_backslash = false;
267 }
268
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900269 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900270 }
271
Dan Willemsen45c49cc2015-08-21 12:39:34 -0700272 if (prev_backslash) {
273 cmd_buf->resize(cmd_buf->size()-1);
274 }
275
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900276 while (true) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900277 char c = (*cmd_buf)[cmd_buf->size()-1];
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900278 if (!isspace(c) && c != ';')
279 break;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900280 cmd_buf->resize(cmd_buf->size() - 1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900281 }
282
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900283 return StringPiece(cmd_buf->data() + orig_size,
284 cmd_buf->size() - orig_size);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900285 }
286
Colin Cross2e032ba2015-07-15 18:33:37 -0700287 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700288 if (!HasPrefix(cmd, "echo ")) {
289 return false;
290 }
291 cmd = cmd.substr(5, cmd.size());
292
Colin Cross2e032ba2015-07-15 18:33:37 -0700293 bool prev_backslash = false;
294 char quote = 0;
295 string out_buf;
296
297 // Strip outer quotes, and fail if it is not a single echo command
298 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
299 if (prev_backslash) {
300 prev_backslash = false;
301 out_buf += *in;
302 } else if (*in == '\\') {
303 prev_backslash = true;
304 out_buf += *in;
305 } else if (quote) {
306 if (*in == quote) {
307 quote = 0;
308 } else {
309 out_buf += *in;
310 }
311 } else {
312 switch (*in) {
313 case '\'':
314 case '"':
315 case '`':
316 quote = *in;
317 break;
318
319 case '<':
320 case '>':
321 case '&':
322 case '|':
323 case ';':
324 return false;
325
326 default:
327 out_buf += *in;
328 }
329 }
330 }
331
332 *out = out_buf;
333 return true;
334 }
335
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900336 bool GenShellScript(const vector<Command*>& commands,
337 string* cmd_buf,
338 string* description) {
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900339 // TODO: This is a dirty hack to set local_pool even without
340 // --goma_dir or --remote_num_jobs which are not used in AOSP
341 // anymore. This won't set local_pool for targets which appear
342 // before the first command which uses gomacc. Fortunately, such
343 // command appears soon so almost all build targets have
344 // local_pool appropriately, but it's definitely better to come up
345 // with a more reliable solution.
346 static bool was_gomacc_found = false;
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900347 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900348 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900349 bool should_ignore_error = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900350 for (const Command* c : commands) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900351 if (!cmd_buf->empty()) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900352 if (should_ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900353 *cmd_buf += " ; ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900354 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900355 *cmd_buf += " && ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900356 }
357 }
358 should_ignore_error = c->ignore_error;
359
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900360 const char* in = c->cmd.c_str();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900361 while (isspace(*in))
362 in++;
363
364 bool needs_subshell = commands.size() > 1;
365 if (*in == '(') {
366 needs_subshell = false;
367 }
368
369 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900370 *cmd_buf += '(';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900371
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900372 size_t cmd_start = cmd_buf->size();
373 StringPiece translated = TranslateCommand(in, cmd_buf);
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900374 if (g_flags.detect_android_echo && !got_descritpion && !c->echo &&
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900375 GetDescriptionFromCommand(translated, description)) {
376 got_descritpion = true;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900377 cmd_buf->resize(cmd_start);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900378 translated.clear();
379 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900380 if (translated.empty()) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900381 *cmd_buf += "true";
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900382 } else if (g_flags.goma_dir) {
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900383 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
384 if (pos != string::npos) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900385 cmd_buf->insert(cmd_start + pos, gomacc_);
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900386 use_gomacc = true;
387 }
Shinichiro Hamaji4cb8a6f2015-08-17 16:30:49 +0900388 } else if (translated.find("/gomacc") != string::npos) {
389 use_gomacc = true;
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900390 was_gomacc_found = true;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900391 }
392
393 if (c == commands.back() && c->ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900394 *cmd_buf += " ; true";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900395 }
396
397 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900398 *cmd_buf += ')';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900399 }
Shinichiro Hamaji1ce0d142015-11-26 15:21:22 +0900400 return (was_gomacc_found || g_flags.remote_num_jobs ||
401 g_flags.goma_dir) && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900402 }
403
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900404 bool GetDepfile(DepNode* node, string* cmd_buf, string* depfile) {
405 if (node->depfile_var) {
406 node->depfile_var->Eval(ev_, depfile);
407 return true;
408 }
409
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900410 *cmd_buf += ' ';
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900411 bool result = GetDepfileFromCommand(cmd_buf, depfile);
Shinichiro Hamaji11347162016-01-20 16:23:22 +0900412 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900413 return result;
414 }
415
416 void EmitDepfile(DepNode* node, string* cmd_buf) {
417 string depfile;
418 if (!GetDepfile(node, cmd_buf, &depfile))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900419 return;
420 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900421 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900422 }
423
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900424 void EmitNode(DepNode* node) {
425 auto p = done_.insert(node->output);
426 if (!p.second)
427 return;
428
Shinichiro Hamaji346b20a2015-08-17 14:23:50 +0900429 // A hack to exclude out phony target in Android. If this exists,
430 // "ninja -t clean" tries to remove this directory and fails.
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900431 if (g_flags.detect_android_echo && node->output.str() == "out")
Shinichiro Hamaji346b20a2015-08-17 14:23:50 +0900432 return;
433
Dan Willemsenf3a4ced2015-08-25 14:22:35 -0700434 // This node is a leaf node
435 if (!node->has_rule && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900436 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900437 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900438
439 vector<Command*> commands;
440 ce_.Eval(node, &commands);
441
442 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900443 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900444 if (!commands.empty()) {
445 rule_name = GenRuleName();
446 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700447
448 string description = "build $out";
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900449 string cmd_buf;
450 use_local_pool |= GenShellScript(commands, &cmd_buf, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700451 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamaji85e5ed02016-01-20 16:25:32 +0900452 EmitDepfile(node, &cmd_buf);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900453
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900454 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900455 // TODO: Find this number automatically.
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900456 if (cmd_buf.size() > 100 * 1000) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900457 fprintf(fp_, " rspfile = $out.rsp\n");
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900458 fprintf(fp_, " rspfile_content = %s\n", cmd_buf.c_str());
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900459 fprintf(fp_, " command = %s $out.rsp\n", shell_.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900460 } else {
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900461 EscapeShell(&cmd_buf);
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900462 fprintf(fp_, " command = %s -c \"%s\"\n",
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900463 shell_.c_str(), cmd_buf.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900464 }
Shinichiro Hamaji3ac2a092015-10-01 18:38:02 +0900465 if (node->is_restat) {
466 fprintf(fp_, " restat = 1\n");
467 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900468 }
469
Colin Cross5b26db32015-09-29 16:51:02 -0700470 EmitBuild(node, rule_name, use_local_pool);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900471
472 for (DepNode* d : node->deps) {
473 EmitNode(d);
474 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900475 for (DepNode* d : node->order_onlys) {
476 EmitNode(d);
477 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900478 }
479
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900480 string EscapeBuildTarget(Symbol s) const {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900481 if (s.str().find_first_of("$: ") == string::npos)
482 return s.str();
483 string r;
484 for (char c : s.str()) {
485 switch (c) {
486 case '$':
487 case ':':
488 case ' ':
489 r += '$';
490 // fall through.
491 default:
492 r += c;
493 }
494 }
495 return r;
496 }
497
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900498 void EscapeShell(string* s) const {
Shinichiro Hamajic58db992015-10-09 16:12:32 +0900499 if (s->find_first_of("$`\\\"") == string::npos)
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900500 return;
Colin Crosse890a912015-07-20 13:28:00 -0700501 string r;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900502 bool last_dollar = false;
503 for (char c : *s) {
Colin Crosse890a912015-07-20 13:28:00 -0700504 switch (c) {
505 case '$':
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900506 if (last_dollar) {
Colin Crosse890a912015-07-20 13:28:00 -0700507 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900508 last_dollar = false;
Colin Crosse890a912015-07-20 13:28:00 -0700509 } else {
510 r += '\\';
511 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900512 last_dollar = true;
Colin Crosse890a912015-07-20 13:28:00 -0700513 }
514 break;
515 case '`':
516 case '"':
Colin Crosse890a912015-07-20 13:28:00 -0700517 case '\\':
518 r += '\\';
519 // fall through.
520 default:
521 r += c;
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900522 last_dollar = false;
Colin Crosse890a912015-07-20 13:28:00 -0700523 }
524 }
Shinichiro Hamajidb559f52015-08-14 17:24:21 +0900525 s->swap(r);
Colin Crosse890a912015-07-20 13:28:00 -0700526 }
527
Colin Cross5b26db32015-09-29 16:51:02 -0700528 void EmitBuild(DepNode* node, const string& rule_name, bool use_local_pool) {
529 string target = EscapeBuildTarget(node->output);
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900530 fprintf(fp_, "build %s: %s",
Colin Cross5b26db32015-09-29 16:51:02 -0700531 target.c_str(),
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900532 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900533 vector<Symbol> order_onlys;
Dan Willemsen3faa60f2015-08-21 13:24:39 -0700534 if (node->is_phony) {
535 fprintf(fp_, " _kati_always_build_");
536 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900537 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900538 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900539 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900540 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900541 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900542 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900543 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900544 }
545 }
546 fprintf(fp_, "\n");
Colin Cross5b26db32015-09-29 16:51:02 -0700547 if (use_local_pool)
548 fprintf(fp_, " pool = local_pool\n");
549 if (node->is_default_target) {
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900550 default_target_ = node;
Colin Cross5b26db32015-09-29 16:51:02 -0700551 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900552 }
553
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900554 void EmitRegenRules(const string& orig_args) {
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900555 if (!g_flags.gen_regen_rule)
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900556 return;
557
558 fprintf(fp_, "rule regen_ninja\n");
559 fprintf(fp_, " command = %s\n", orig_args.c_str());
560 fprintf(fp_, " generator = 1\n");
561 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
562 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
563 unordered_set<string> makefiles;
564 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
565 for (const string& makefile : makefiles) {
566 fprintf(fp_, " %.*s", SPF(makefile));
567 }
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900568 fprintf(fp_, " %s", kati_binary_.c_str());
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900569 fprintf(fp_, "\n\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900570 }
571
Shinichiro Hamaji5a5a7f62015-12-12 12:44:00 +0900572 static string GetEnvScriptFilename() {
Colin Cross27df5312015-11-09 13:39:14 -0800573 return GetFilename("env%s.sh");
574 }
575
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900576 void GenerateNinja(const vector<DepNode*>& nodes,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900577 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900578 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900579 if (fp_ == NULL)
580 PERROR("fopen(build.ninja) failed");
581
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900582 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900583 fprintf(fp_, "\n");
584
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900585 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900586 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900587 for (const auto& p : used_envs_) {
588 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900589 }
590 fprintf(fp_, "\n");
591 }
592
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900593 if (g_flags.ninja_dir) {
594 fprintf(fp_, "builddir = %s\n\n", g_flags.ninja_dir);
Dan Willemsen2e762292015-08-19 20:39:41 -0700595 }
596
Shinichiro Hamaji4cb8a6f2015-08-17 16:30:49 +0900597 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900598 fprintf(fp_, " depth = %d\n\n", g_flags.num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900599
Dan Willemsen3faa60f2015-08-21 13:24:39 -0700600 fprintf(fp_, "build _kati_always_build_: phony\n\n");
601
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900602 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900603
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900604 for (DepNode* node : nodes) {
605 EmitNode(node);
606 }
607
Shinichiro Hamaji29e45702015-12-09 15:44:51 +0900608 unordered_set<Symbol> used_env_vars(Vars::used_env_vars());
609 // PATH changes $(shell).
610 used_env_vars.insert(Intern("PATH"));
611 for (Symbol e : used_env_vars) {
Shinichiro Hamajied883ef2015-07-31 13:15:04 +0900612 StringPiece val(getenv(e.c_str()));
613 used_envs_.emplace(e.str(), val.as_string());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900614 }
615
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900616 string default_targets;
Shinichiro Hamaji4e3b4212015-11-09 17:08:32 +0900617 if (g_flags.targets.empty() || g_flags.gen_all_targets) {
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900618 CHECK(default_target_);
619 default_targets = EscapeBuildTarget(default_target_->output);
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900620 } else {
621 for (Symbol s : g_flags.targets) {
622 if (!default_targets.empty())
623 default_targets += ' ';
624 default_targets += EscapeBuildTarget(s);
625 }
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900626 }
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900627 fprintf(fp_, "\n");
628 fprintf(fp_, "default %s\n", default_targets.c_str());
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900629
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900630 fclose(fp_);
631 }
632
633 void GenerateShell() {
Colin Cross27df5312015-11-09 13:39:14 -0800634 FILE* fp = fopen(GetEnvScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900635 if (fp == NULL)
Colin Cross27df5312015-11-09 13:39:14 -0800636 PERROR("fopen(env.sh) failed");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900637
Shinichiro Hamaji4dd62de2015-07-28 16:06:52 +0900638 fprintf(fp, "#!/bin/sh\n");
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900639 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
640 fprintf(fp, "\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900641
642 for (const auto& p : ev_->exports()) {
643 if (p.second) {
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900644 const string val = ev_->EvalVar(p.first);
645 fprintf(fp, "export '%s'='%s'\n", p.first.c_str(), val.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900646 } else {
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900647 fprintf(fp, "unset '%s'\n", p.first.c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900648 }
649 }
650
Colin Cross27df5312015-11-09 13:39:14 -0800651 fclose(fp);
652
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900653 fp = fopen(GetNinjaShellScriptFilename().c_str(), "wb");
Colin Cross27df5312015-11-09 13:39:14 -0800654 if (fp == NULL)
655 PERROR("fopen(ninja.sh) failed");
656
657 fprintf(fp, "#!/bin/sh\n");
658 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
659 fprintf(fp, "\n");
660
661 fprintf(fp, ". %s\n", GetEnvScriptFilename().c_str());
662
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900663 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji003d06e2015-09-09 18:22:04 +0900664 if (g_flags.remote_num_jobs > 0) {
665 fprintf(fp, "-j%d ", g_flags.remote_num_jobs);
666 } else if (g_flags.goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900667 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900668 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900669 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900670
Colin Cross27df5312015-11-09 13:39:14 -0800671 fclose(fp);
672
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900673 if (chmod(GetNinjaShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900674 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900675 }
676
Dan Willemsen87417412015-08-24 17:57:12 -0700677 void GenerateStamp(const string& orig_args) {
Colin Cross0850f7d2015-09-14 15:11:45 -0700678 FILE* fp = fopen(GetStampTempFilename().c_str(), "wb");
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900679 CHECK(fp);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900680
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900681 size_t r = fwrite(&start_time_, sizeof(start_time_), 1, fp);
682 CHECK(r == 1);
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900683
684 unordered_set<string> makefiles;
685 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
686 DumpInt(fp, makefiles.size() + 1);
687 DumpString(fp, kati_binary_);
688 for (const string& makefile : makefiles) {
689 DumpString(fp, makefile);
690 }
691
Shinichiro Hamaji7e708012015-07-31 13:07:34 +0900692 DumpInt(fp, Evaluator::used_undefined_vars().size());
693 for (Symbol v : Evaluator::used_undefined_vars()) {
694 DumpString(fp, v.str());
695 }
696
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900697 DumpInt(fp, used_envs_.size());
698 for (const auto& p : used_envs_) {
699 DumpString(fp, p.first);
700 DumpString(fp, p.second);
701 }
702
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900703 const unordered_map<string, vector<string>*>& globs = GetAllGlobCache();
704 DumpInt(fp, globs.size());
705 for (const auto& p : globs) {
706 DumpString(fp, p.first);
707 const vector<string>& files = *p.second;
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900708#if 0
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900709 unordered_set<string> dirs;
710 GetReadDirs(p.first, files, &dirs);
711 DumpInt(fp, dirs.size());
712 for (const string& dir : dirs) {
713 DumpString(fp, dir);
714 }
Shinichiro Hamajie3c43602015-08-13 13:47:27 +0900715#endif
Shinichiro Hamajia09ed282015-07-31 12:21:54 +0900716 DumpInt(fp, files.size());
717 for (const string& file : files) {
718 DumpString(fp, file);
719 }
720 }
721
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900722 const vector<CommandResult*>& crs = GetShellCommandResults();
723 DumpInt(fp, crs.size());
724 for (CommandResult* cr : crs) {
725 DumpString(fp, cr->cmd);
726 DumpString(fp, cr->result);
727 if (!cr->find.get()) {
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900728 // Always re-run this command.
729 DumpInt(fp, 0);
730 continue;
731 }
732
733 DumpInt(fp, 1);
734
735 vector<string> missing_dirs;
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900736 for (StringPiece fd : cr->find->finddirs) {
737 const string& d = ConcatDir(cr->find->chdir, fd);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900738 if (!Exists(d))
739 missing_dirs.push_back(d);
740 }
741 DumpInt(fp, missing_dirs.size());
742 for (const string& d : missing_dirs) {
743 DumpString(fp, d);
744 }
745
Shinichiro Hamaji4db9edc2015-08-13 16:43:02 +0900746 DumpInt(fp, cr->find->read_dirs->size());
747 for (StringPiece s : *cr->find->read_dirs) {
748 DumpString(fp, ConcatDir(cr->find->chdir, s));
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900749 }
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900750 }
751
Dan Willemsen87417412015-08-24 17:57:12 -0700752 DumpString(fp, orig_args);
753
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900754 fclose(fp);
Colin Cross0850f7d2015-09-14 15:11:45 -0700755
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900756 rename(GetStampTempFilename().c_str(), GetNinjaStampFilename().c_str());
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900757 }
758
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900759 CommandEvaluator ce_;
760 Evaluator* ev_;
761 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900762 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900763 int rule_id_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900764 string gomacc_;
Shinichiro Hamajifb415ad2015-08-14 17:19:34 +0900765 string shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900766 map<string, string> used_envs_;
Shinichiro Hamajib58bb4b2015-07-30 18:02:51 +0900767 string kati_binary_;
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900768 double start_time_;
Shinichiro Hamajia62b02a2015-10-01 14:21:40 +0900769 DepNode* default_target_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900770};
771
Shinichiro Hamajifcc7c3a2016-01-27 17:46:49 +0900772string GetNinjaFilename() {
773 return NinjaGenerator::GetFilename("build%s.ninja");
774}
775
776string GetNinjaShellScriptFilename() {
777 return NinjaGenerator::GetFilename("ninja%s.sh");
778}
779
780string GetNinjaStampFilename() {
781 return NinjaGenerator::GetFilename(".kati_stamp%s");
782}
783
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900784void GenerateNinja(const vector<DepNode*>& nodes,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900785 Evaluator* ev,
Shinichiro Hamajia4bb7742015-07-31 11:27:30 +0900786 const string& orig_args,
Shinichiro Hamaji36eeb752015-07-31 15:12:04 +0900787 double start_time) {
Shinichiro Hamajif9869fc2015-09-28 17:18:59 +0900788 NinjaGenerator ng(ev, start_time);
Shinichiro Hamaji9c5e60c2015-09-29 13:26:45 +0900789 ng.Generate(nodes, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900790}