blob: a562b12f2be8b42556bb07444bb9c24bbb47f0e0 [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 Hamaji54b93ba2015-07-03 20:43:32 +090020#include <sys/stat.h>
21#include <unistd.h>
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +090022
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +090023#include <map>
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090024#include <memory>
25#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 Hamaji087cecd2015-07-06 19:51:58 +090033#include "flags.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090034#include "log.h"
35#include "string_piece.h"
36#include "stringprintf.h"
37#include "strutil.h"
38#include "var.h"
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +090039#include "version.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090040
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090041static size_t FindCommandLineFlag(StringPiece cmd, StringPiece name) {
42 const size_t found = cmd.find(name);
43 if (found == string::npos || found == 0)
44 return string::npos;
45 return found;
46}
47
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090048static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
49 StringPiece name) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090050 size_t index = FindCommandLineFlag(cmd, name);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090051 if (index == string::npos)
52 return StringPiece();
53
54 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
55 index = val.find(name);
56 while (index != string::npos) {
57 val = TrimLeftSpace(val.substr(index + name.size()));
58 index = val.find(name);
59 }
60
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090061 index = val.find_first_of(" \t");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090062 return val.substr(0, index);
63}
64
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090065static bool StripPrefix(StringPiece p, StringPiece* s) {
66 if (!HasPrefix(*s, p))
67 return false;
68 *s = s->substr(p.size());
69 return true;
70}
71
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090072size_t GetGomaccPosForAndroidCompileCommand(StringPiece cmdline) {
73 size_t index = cmdline.find(' ');
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090074 if (index == string::npos)
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090075 return string::npos;
76 StringPiece cmd = cmdline.substr(0, index);
77 if (HasSuffix(cmd, "ccache")) {
78 index++;
79 size_t pos = GetGomaccPosForAndroidCompileCommand(cmdline.substr(index));
80 return pos == string::npos ? string::npos : pos + index;
81 }
82 if (!StripPrefix("prebuilts/", &cmd))
83 return string::npos;
84 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
85 return string::npos;
86 if (!HasSuffix(cmd, "gcc") && !HasSuffix(cmd, "g++") &&
87 !HasSuffix(cmd, "clang") && !HasSuffix(cmd, "clang++")) {
88 return string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090089 }
90
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +090091 StringPiece rest = cmdline.substr(index);
92 return rest.find(" -c ") != string::npos ? 0 : string::npos;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090093}
94
95static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +090096 if ((FindCommandLineFlag(cmd, " -MD") == string::npos &&
97 FindCommandLineFlag(cmd, " -MMD") == string::npos) ||
98 FindCommandLineFlag(cmd, " -c") == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090099 return false;
100 }
101
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900102 StringPiece mf = FindCommandLineFlagWithArg(cmd, " -MF");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900103 if (!mf.empty()) {
104 mf.AppendToString(out);
105 return true;
106 }
107
Shinichiro Hamaji383cfe02015-07-27 16:12:45 +0900108 StringPiece o = FindCommandLineFlagWithArg(cmd, " -o");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900109 if (o.empty()) {
110 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
111 return false;
112 }
113
114 StripExt(o).AppendToString(out);
115 *out += ".d";
116 return true;
117}
118
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900119bool GetDepfileFromCommand(string* cmd, string* out) {
120 CHECK(!cmd->empty());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900121 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900122 return false;
123
124 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900125 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900126 return false;
127 }
128
129 // TODO: A hack for Makefiles generated by automake.
130
131 // A hack for Android to get .P files instead of .d.
132 string p;
133 StripExt(*out).AppendToString(&p);
134 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900135 if (cmd->find(p) != string::npos) {
136 const string rm_f = "; rm -f " + *out;
137 const size_t found = cmd->find(rm_f);
138 if (found == string::npos) {
139 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
140 }
141 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900142 return true;
143 }
144
145 // A hack for Android. For .s files, GCC does not use C
146 // preprocessor, so it ignores -MF flag.
147 string as = "/";
148 StripExt(Basename(*out)).AppendToString(&as);
149 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900150 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900151 return false;
152 }
153
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900154 *cmd += "&& cp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900155 *cmd += *out;
156 *cmd += ' ';
157 *cmd += *out;
Shinichiro Hamajid416e612015-07-18 12:43:34 +0900158 *cmd += ".tmp ";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900159 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900160 return true;
161}
162
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900163class NinjaGenerator {
164 public:
Colin Crossbb2bba12015-07-20 16:31:18 -0700165 NinjaGenerator(const char* ninja_suffix, const char* ninja_dir, Evaluator* ev)
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900166 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
167 ev_->set_avoid_io(true);
Colin Crosse890a912015-07-20 13:28:00 -0700168 shell_ = ev->EvalVar(kShellSym);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900169 if (g_goma_dir)
170 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900171 if (ninja_suffix) {
172 ninja_suffix_ = ninja_suffix;
173 }
Colin Crossbb2bba12015-07-20 16:31:18 -0700174 if (ninja_dir) {
175 ninja_dir_ = ninja_dir;
176 } else {
177 ninja_dir_ = ".";
178 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900179
180 for (Symbol e : Vars::used_env_vars()) {
181 shared_ptr<string> val = ev_->EvalVar(e);
182 used_envs_.emplace(e.str(), *val);
183 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900184 }
185
186 ~NinjaGenerator() {
187 ev_->set_avoid_io(false);
188 }
189
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900190 void Generate(const vector<DepNode*>& nodes,
191 bool build_all_targets,
192 const string& orig_args) {
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900193 GenerateEnvlist();
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900194 GenerateNinja(nodes, build_all_targets, orig_args);
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900195 GenerateShell();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900196 }
197
198 private:
199 string GenRuleName() {
200 return StringPrintf("rule%d", rule_id_++);
201 }
202
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900203 StringPiece TranslateCommand(const char* in, string* cmd_buf) {
204 const size_t orig_size = cmd_buf->size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900205 bool prev_backslash = false;
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900206 // Set space as an initial value so the leading comment will be
207 // stripped out.
208 char prev_char = ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900209 char quote = 0;
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900210 for (; *in; in++) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900211 switch (*in) {
212 case '#':
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900213 if (quote == 0 && isspace(prev_char)) {
Shinichiro Hamajifc14d5f2015-07-28 17:07:57 +0900214 while (in[1] && *in != '\n')
215 in++;
Colin Cross415e4a12015-07-15 18:21:38 -0700216 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900217 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900218 }
Colin Cross415e4a12015-07-15 18:21:38 -0700219 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900220
221 case '\'':
222 case '"':
223 case '`':
224 if (quote) {
225 if (quote == *in)
226 quote = 0;
227 } else if (!prev_backslash) {
228 quote = *in;
229 }
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900230 *cmd_buf += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900231 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900232
233 case '$':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900234 *cmd_buf += "$$";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900235 break;
236
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900237 case '\n':
238 if (prev_backslash) {
Shinichiro Hamajia4dfe752015-07-28 15:54:36 +0900239 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900240 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900241 *cmd_buf += ' ';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900242 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900243 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900244
245 case '\\':
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900246 *cmd_buf += '\\';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900247 break;
248
249 default:
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900250 *cmd_buf += *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900251 }
Colin Crossf6f1cf42015-07-15 15:25:30 -0700252
253 if (*in == '\\') {
254 prev_backslash = !prev_backslash;
255 } else {
256 prev_backslash = false;
257 }
258
Shinichiro Hamaji53eaaf82015-07-15 06:09:43 +0900259 prev_char = *in;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900260 }
261
262 while (true) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900263 char c = (*cmd_buf)[cmd_buf->size()-1];
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900264 if (!isspace(c) && c != ';')
265 break;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900266 cmd_buf->resize(cmd_buf->size() - 1);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900267 }
268
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900269 return StringPiece(cmd_buf->data() + orig_size,
270 cmd_buf->size() - orig_size);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900271 }
272
Colin Cross2e032ba2015-07-15 18:33:37 -0700273 bool GetDescriptionFromCommand(StringPiece cmd, string *out) {
Colin Cross2e032ba2015-07-15 18:33:37 -0700274 if (!HasPrefix(cmd, "echo ")) {
275 return false;
276 }
277 cmd = cmd.substr(5, cmd.size());
278
Colin Cross2e032ba2015-07-15 18:33:37 -0700279 bool prev_backslash = false;
280 char quote = 0;
281 string out_buf;
282
283 // Strip outer quotes, and fail if it is not a single echo command
284 for (StringPiece::iterator in = cmd.begin(); in != cmd.end(); in++) {
285 if (prev_backslash) {
286 prev_backslash = false;
287 out_buf += *in;
288 } else if (*in == '\\') {
289 prev_backslash = true;
290 out_buf += *in;
291 } else if (quote) {
292 if (*in == quote) {
293 quote = 0;
294 } else {
295 out_buf += *in;
296 }
297 } else {
298 switch (*in) {
299 case '\'':
300 case '"':
301 case '`':
302 quote = *in;
303 break;
304
305 case '<':
306 case '>':
307 case '&':
308 case '|':
309 case ';':
310 return false;
311
312 default:
313 out_buf += *in;
314 }
315 }
316 }
317
318 *out = out_buf;
319 return true;
320 }
321
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900322 bool GenShellScript(const vector<Command*>& commands,
323 string* cmd_buf,
324 string* description) {
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900325 bool got_descritpion = false;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900326 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900327 bool should_ignore_error = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900328 for (const Command* c : commands) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900329 if (!cmd_buf->empty()) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900330 if (should_ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900331 *cmd_buf += " ; ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900332 } else {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900333 *cmd_buf += " && ";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900334 }
335 }
336 should_ignore_error = c->ignore_error;
337
338 const char* in = c->cmd->c_str();
339 while (isspace(*in))
340 in++;
341
342 bool needs_subshell = commands.size() > 1;
343 if (*in == '(') {
344 needs_subshell = false;
345 }
346
347 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900348 *cmd_buf += '(';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900349
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900350 size_t cmd_start = cmd_buf->size();
351 StringPiece translated = TranslateCommand(in, cmd_buf);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900352 if (g_detect_android_echo && !got_descritpion && !c->echo &&
353 GetDescriptionFromCommand(translated, description)) {
354 got_descritpion = true;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900355 cmd_buf->resize(cmd_start);
Shinichiro Hamaji2ee6ca12015-07-18 03:37:21 +0900356 translated.clear();
357 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900358 if (translated.empty()) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900359 *cmd_buf += "true";
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900360 } else if (g_goma_dir) {
361 size_t pos = GetGomaccPosForAndroidCompileCommand(translated);
362 if (pos != string::npos) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900363 cmd_buf->insert(cmd_start + pos, gomacc_);
Shinichiro Hamaji2c9fcbe2015-07-14 03:36:34 +0900364 use_gomacc = true;
365 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900366 }
367
368 if (c == commands.back() && c->ignore_error) {
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900369 *cmd_buf += " ; true";
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900370 }
371
372 if (needs_subshell)
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900373 *cmd_buf += ')';
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900374 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900375 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900376 }
377
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900378 void EmitDepfile(string* cmd_buf) {
379 *cmd_buf += ' ';
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900380 string depfile;
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900381 bool result = GetDepfileFromCommand(cmd_buf, &depfile);
382 cmd_buf->resize(cmd_buf->size()-1);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900383 if (!result)
384 return;
385 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900386 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900387 }
388
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900389 void EmitNode(DepNode* node) {
390 auto p = done_.insert(node->output);
391 if (!p.second)
392 return;
393
Shinichiro Hamaji90216592015-07-28 14:36:31 +0900394 // Removing this will fix auto_vars.mk, build_once.mk, and
395 // command_vars.mk. However, this change will make
396 // ninja_normalized_path2.mk fail and cause a lot of warnings for
397 // Android build.
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900398 if (node->cmds.empty() &&
399 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900400 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900401 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900402
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900403 StringPiece base = Basename(node->output.str());
404 if (base != node->output.str()) {
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900405 auto p = short_names_.emplace(Intern(base), node->output);
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900406 if (!p.second) {
407 // We generate shortcuts only for targets whose basename are unique.
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900408 p.first->second = kEmptySym;
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900409 }
410 }
411
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900412 vector<Command*> commands;
413 ce_.Eval(node, &commands);
414
415 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900416 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900417 if (!commands.empty()) {
418 rule_name = GenRuleName();
419 fprintf(fp_, "rule %s\n", rule_name.c_str());
Colin Cross2e032ba2015-07-15 18:33:37 -0700420
421 string description = "build $out";
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900422 string cmd_buf;
423 use_local_pool |= GenShellScript(commands, &cmd_buf, &description);
Colin Cross2e032ba2015-07-15 18:33:37 -0700424 fprintf(fp_, " description = %s\n", description.c_str());
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900425 EmitDepfile(&cmd_buf);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900426
Shinichiro Hamajieb0e5f92015-07-17 03:38:23 +0900427 // It seems Linux is OK with ~130kB and Mac's limit is ~250kB.
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900428 // TODO: Find this number automatically.
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900429 if (cmd_buf.size() > 100 * 1000) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900430 fprintf(fp_, " rspfile = $out.rsp\n");
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900431 fprintf(fp_, " rspfile_content = %s\n", cmd_buf.c_str());
Colin Crosse890a912015-07-20 13:28:00 -0700432 fprintf(fp_, " command = %s $out.rsp\n", shell_->c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900433 } else {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900434 fprintf(fp_, " command = %s -c \"%s\"\n",
Shinichiro Hamajidbefa652015-07-27 14:08:42 +0900435 shell_->c_str(), EscapeShell(cmd_buf).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900436 }
437 }
438
439 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900440 if (use_local_pool)
441 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900442
443 for (DepNode* d : node->deps) {
444 EmitNode(d);
445 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900446 for (DepNode* d : node->order_onlys) {
447 EmitNode(d);
448 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900449 }
450
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900451 string EscapeBuildTarget(Symbol s) {
452 if (s.str().find_first_of("$: ") == string::npos)
453 return s.str();
454 string r;
455 for (char c : s.str()) {
456 switch (c) {
457 case '$':
458 case ':':
459 case ' ':
460 r += '$';
461 // fall through.
462 default:
463 r += c;
464 }
465 }
466 return r;
467 }
468
Colin Crosse890a912015-07-20 13:28:00 -0700469 string EscapeShell(string s) {
470 if (s.find_first_of("$`!\\\"") == string::npos)
471 return s;
472 string r;
473 bool lastDollar = false;
474 for (char c : s) {
475 switch (c) {
476 case '$':
477 if (lastDollar) {
478 r += c;
479 lastDollar = false;
480 } else {
481 r += '\\';
482 r += c;
483 lastDollar = true;
484 }
485 break;
486 case '`':
487 case '"':
488 case '!':
489 case '\\':
490 r += '\\';
491 // fall through.
492 default:
493 r += c;
494 lastDollar = false;
495 }
496 }
497 return r;
498 }
499
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900500 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900501 fprintf(fp_, "build %s: %s",
502 EscapeBuildTarget(node->output).c_str(),
503 rule_name.c_str());
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900504 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900505 for (DepNode* d : node->deps) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900506 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900507 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900508 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900509 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900510 for (DepNode* d : node->order_onlys) {
Shinichiro Hamaji753c8122015-07-18 17:16:24 +0900511 fprintf(fp_, " %s", EscapeBuildTarget(d->output).c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900512 }
513 }
514 fprintf(fp_, "\n");
515 }
516
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900517 void EmitRegenRules(const string& orig_args) {
518 if (!g_gen_regen_rule)
519 return;
520
521 fprintf(fp_, "rule regen_ninja\n");
522 fprintf(fp_, " command = %s\n", orig_args.c_str());
523 fprintf(fp_, " generator = 1\n");
524 fprintf(fp_, " description = Regenerate ninja files due to dependency\n");
525 fprintf(fp_, "build %s: regen_ninja", GetNinjaFilename().c_str());
526 unordered_set<string> makefiles;
527 MakefileCacheManager::Get()->GetAllFilenames(&makefiles);
528 for (const string& makefile : makefiles) {
529 fprintf(fp_, " %.*s", SPF(makefile));
530 }
Shinichiro Hamaji3ed61482015-07-18 14:16:32 +0900531 // TODO: Add dependencies to directories read by $(wildcard)
532 // or $(shell find).
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900533 if (!used_envs_.empty())
534 fprintf(fp_, " %s", GetEnvlistFilename().c_str());
535 fprintf(fp_, "\n\n");
536
537 if (used_envs_.empty())
538 return;
539
540 fprintf(fp_, "build .always_build: phony\n");
541 fprintf(fp_, "rule regen_envlist\n");
542 fprintf(fp_, " command = rm -f $out.tmp");
543 for (const auto& p : used_envs_) {
544 fprintf(fp_, " && echo %s=$$%s >> $out.tmp",
545 p.first.c_str(), p.first.c_str());
546 }
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900547 if (g_error_on_env_change) {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900548 fprintf(fp_,
549 " && (diff $out.tmp $out || "
550 "(echo Environment variable changes are detected && false))\n");
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900551 } else {
552 fprintf(fp_, " && (diff $out.tmp $out || mv $out.tmp $out)\n");
553 }
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900554 fprintf(fp_, " restat = 1\n");
Shinichiro Hamaji8bd580e2015-07-18 14:22:29 +0900555 fprintf(fp_, " generator = 1\n");
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900556 fprintf(fp_, " description = Check $out\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900557 fprintf(fp_, "build %s: regen_envlist .always_build\n\n",
558 GetEnvlistFilename().c_str());
559 }
560
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900561 string GetNinjaFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900562 return StringPrintf("%s/build%s.ninja",
563 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900564 }
565
566 string GetShellScriptFilename() const {
Shinichiro Hamaji2e04d302015-07-24 09:31:18 +0900567 return StringPrintf("%s/ninja%s.sh",
568 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900569 }
570
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900571 string GetEnvlistFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900572 return StringPrintf("%s/.kati_env%s",
573 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900574 }
575
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900576 string GetLunchFilename() const {
Shinichiro Hamaji41957e02015-07-24 10:27:14 +0900577 return StringPrintf("%s/.kati_lunch%s",
578 ninja_dir_.c_str(), ninja_suffix_.c_str());
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900579 }
580
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900581 void GenerateNinja(const vector<DepNode*>& nodes,
582 bool build_all_targets,
583 const string& orig_args) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900584 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900585 if (fp_ == NULL)
586 PERROR("fopen(build.ninja) failed");
587
Shinichiro Hamajid821f6d2015-07-14 04:03:27 +0900588 fprintf(fp_, "# Generated by kati %s\n", kGitVersion);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900589 fprintf(fp_, "\n");
590
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900591 if (!used_envs_.empty()) {
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900592 fprintf(fp_, "# Environment variables used:\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900593 for (const auto& p : used_envs_) {
594 fprintf(fp_, "# %s=%s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamaji5163e042015-07-14 03:51:44 +0900595 }
596 fprintf(fp_, "\n");
597 }
598
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900599 if (g_goma_dir) {
600 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900601 fprintf(fp_, " depth = %d\n\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900602 }
603
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900604 EmitRegenRules(orig_args);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900605
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900606 for (DepNode* node : nodes) {
607 EmitNode(node);
608 }
609
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900610 if (!build_all_targets) {
611 CHECK(!nodes.empty());
Shinichiro Hamaji865597b2015-07-28 16:16:58 +0900612 fprintf(fp_, "\ndefault %s\n",
613 EscapeBuildTarget(nodes.front()->output).c_str());
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900614 }
615
Shinichiro Hamaji15fe11d2015-07-11 06:32:37 +0900616 fprintf(fp_, "\n# shortcuts:\n");
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900617 for (auto p : short_names_) {
Shinichiro Hamaji10bb4ab2015-07-16 06:04:48 +0900618 if (!p.second.empty() && !done_.count(p.first))
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900619 fprintf(fp_, "build %s: phony %s\n", p.first.c_str(), p.second.c_str());
Shinichiro Hamajideb76562015-07-11 05:53:30 +0900620 }
621
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900622 fclose(fp_);
623 }
624
625 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900626 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900627 if (fp == NULL)
628 PERROR("fopen(ninja.sh) failed");
629
Shinichiro Hamaji4dd62de2015-07-28 16:06:52 +0900630 fprintf(fp, "#!/bin/sh\n");
Shinichiro Hamajiaf9887a2015-07-17 03:45:14 +0900631 fprintf(fp, "# Generated by kati %s\n", kGitVersion);
632 fprintf(fp, "\n");
Shinichiro Hamajic7629e02015-07-22 02:41:57 +0900633 if (ninja_dir_ == ".")
634 fprintf(fp, "cd $(dirname \"$0\")\n");
Shinichiro Hamajid556e622015-07-18 15:33:58 +0900635 if (!ninja_suffix_.empty()) {
636 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
637 GetEnvlistFilename().c_str(), GetEnvlistFilename().c_str());
638 fprintf(fp, "if [ -f %s ]; then\n export $(cat %s)\nfi\n",
639 GetLunchFilename().c_str(), GetLunchFilename().c_str());
640 }
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900641
642 for (const auto& p : ev_->exports()) {
643 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900644 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamajif65e5722015-07-28 16:13:18 +0900645 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
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900651 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900652 if (g_goma_dir) {
Shinichiro Hamaji6d7c7b72015-07-18 16:51:39 +0900653 fprintf(fp, "-j500 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900654 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900655 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900656
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900657 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900658 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900659 }
660
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900661 void GenerateEnvlist() {
662 if (used_envs_.empty())
663 return;
664 FILE* fp = fopen(GetEnvlistFilename().c_str(), "wb");
665 for (const auto& p : used_envs_) {
666 fprintf(fp, "%s=%s\n", p.first.c_str(), p.second.c_str());
667 }
668 fclose(fp);
669 }
670
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900671 CommandEvaluator ce_;
672 Evaluator* ev_;
673 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900674 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900675 int rule_id_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900676 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900677 string ninja_suffix_;
Colin Crossbb2bba12015-07-20 16:31:18 -0700678 string ninja_dir_;
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900679 unordered_map<Symbol, Symbol> short_names_;
Colin Crosse890a912015-07-20 13:28:00 -0700680 shared_ptr<string> shell_;
Shinichiro Hamajia6e06f82015-07-18 14:08:49 +0900681 map<string, string> used_envs_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900682};
683
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900684void GenerateNinja(const char* ninja_suffix,
Colin Crossbb2bba12015-07-20 16:31:18 -0700685 const char* ninja_dir,
Shinichiro Hamaji43defe02015-07-11 07:06:43 +0900686 const vector<DepNode*>& nodes,
687 Evaluator* ev,
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900688 bool build_all_targets,
689 const string& orig_args) {
Colin Crossbb2bba12015-07-20 16:31:18 -0700690 NinjaGenerator ng(ninja_suffix, ninja_dir, ev);
Shinichiro Hamaji998ccf72015-07-18 12:37:02 +0900691 ng.Generate(nodes, build_all_targets, orig_args);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900692}