blob: 9c400827f5f5a2eb3db56a27d83cf6e9ca82534c [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 Hamajidf1fc8b2015-06-29 15:45:21 +090023#include <memory>
24#include <string>
25#include <unordered_set>
26
27#include "command.h"
28#include "dep.h"
29#include "eval.h"
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +090030#include "flags.h"
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090031#include "log.h"
32#include "string_piece.h"
33#include "stringprintf.h"
34#include "strutil.h"
35#include "var.h"
36
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090037static StringPiece FindCommandLineFlagWithArg(StringPiece cmd,
38 StringPiece name) {
39 size_t index = cmd.find(name);
40 if (index == string::npos)
41 return StringPiece();
42
43 StringPiece val = TrimLeftSpace(cmd.substr(index + name.size()));
44 index = val.find(name);
45 while (index != string::npos) {
46 val = TrimLeftSpace(val.substr(index + name.size()));
47 index = val.find(name);
48 }
49
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090050 index = val.find(' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090051 CHECK(index != string::npos);
52 return val.substr(0, index);
53}
54
Shinichiro Hamaji9facae22015-07-03 17:16:36 +090055static bool StripPrefix(StringPiece p, StringPiece* s) {
56 if (!HasPrefix(*s, p))
57 return false;
58 *s = s->substr(p.size());
59 return true;
60}
61
62static bool IsAndroidCompileCommand(StringPiece cmd) {
63 if (!StripPrefix("prebuilts/", &cmd))
64 return false;
65 if (!StripPrefix("gcc/", &cmd) && !StripPrefix("clang/", &cmd))
66 return false;
67
68 size_t index = cmd.find(' ');
69 if (index == string::npos)
70 return false;
71 StringPiece cc = cmd.substr(0, index);
72 if (!HasSuffix(cc, "gcc") && !HasSuffix(cc, "g++") &&
73 !HasSuffix(cc, "clang") && !HasSuffix(cc, "clang++")) {
74 return false;
75 }
76
77 cmd = cmd.substr(index);
78 return cmd.find(" -c ") != string::npos;
79}
80
81static bool GetDepfileFromCommandImpl(StringPiece cmd, string* out) {
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090082 if (cmd.find(StringPiece(" -MD ")) == string::npos &&
83 cmd.find(StringPiece(" -MMD ")) == string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090084 return false;
85 }
86
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090087 StringPiece mf = FindCommandLineFlagWithArg(cmd, StringPiece(" -MF "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090088 if (!mf.empty()) {
89 mf.AppendToString(out);
90 return true;
91 }
92
Shinichiro Hamaji388e8582015-07-03 16:51:46 +090093 StringPiece o = FindCommandLineFlagWithArg(cmd, StringPiece(" -o "));
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +090094 if (o.empty()) {
95 ERROR("Cannot find the depfile in %s", cmd.as_string().c_str());
96 return false;
97 }
98
99 StripExt(o).AppendToString(out);
100 *out += ".d";
101 return true;
102}
103
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900104bool GetDepfileFromCommand(string* cmd, string* out) {
105 CHECK(!cmd->empty());
106 CHECK((*cmd)[cmd->size()-1] == ' ');
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900107
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900108 if (!GetDepfileFromCommandImpl(*cmd, out))
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900109 return false;
110
111 // A hack for Android - llvm-rs-cc seems not to emit a dep file.
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900112 if (cmd->find("bin/llvm-rs-cc ") != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900113 return false;
114 }
115
116 // TODO: A hack for Makefiles generated by automake.
117
118 // A hack for Android to get .P files instead of .d.
119 string p;
120 StripExt(*out).AppendToString(&p);
121 p += ".P";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900122 if (cmd->find(p) != string::npos) {
123 const string rm_f = "; rm -f " + *out;
124 const size_t found = cmd->find(rm_f);
125 if (found == string::npos) {
126 ERROR("Cannot find removal of .d file: %s", cmd->c_str());
127 }
128 cmd->erase(found, rm_f.size());
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900129 return true;
130 }
131
132 // A hack for Android. For .s files, GCC does not use C
133 // preprocessor, so it ignores -MF flag.
134 string as = "/";
135 StripExt(Basename(*out)).AppendToString(&as);
136 as += ".s";
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900137 if (cmd->find(as) != string::npos) {
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900138 return false;
139 }
140
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900141 *cmd += " && cp ";
142 *cmd += *out;
143 *cmd += ' ';
144 *cmd += *out;
145 *cmd += ".tmp";
146 *out += ".tmp";
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900147 return true;
148}
149
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900150class NinjaGenerator {
151 public:
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900152 NinjaGenerator(const char* ninja_suffix, Evaluator* ev)
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900153 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
154 ev_->set_avoid_io(true);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900155 if (g_goma_dir)
156 gomacc_ = StringPrintf("%s/gomacc ", g_goma_dir);
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900157 if (ninja_suffix) {
158 ninja_suffix_ = ninja_suffix;
159 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900160 }
161
162 ~NinjaGenerator() {
163 ev_->set_avoid_io(false);
164 }
165
166 void Generate(const vector<DepNode*>& nodes) {
167 GenerateShell();
168 GenerateNinja(nodes);
169 }
170
171 private:
172 string GenRuleName() {
173 return StringPrintf("rule%d", rule_id_++);
174 }
175
176 StringPiece TranslateCommand(const char* in) {
177 const size_t orig_size = cmd_buf_.size();
178 bool prev_backslash = false;
179 char quote = 0;
180 bool done = false;
181 for (; *in && !done; in++) {
182 switch (*in) {
183 case '#':
184 if (quote == 0 && !prev_backslash) {
185 done = true;
186 break;
187 }
188
189 case '\'':
190 case '"':
191 case '`':
192 if (quote) {
193 if (quote == *in)
194 quote = 0;
195 } else if (!prev_backslash) {
196 quote = *in;
197 }
Shinichiro Hamajif79ca532015-06-29 17:25:54 +0900198 cmd_buf_ += *in;
Shinichiro Hamaji4212e382015-06-29 17:21:04 +0900199 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900200
201 case '$':
202 cmd_buf_ += "$$";
203 break;
204
205 case '\t':
206 cmd_buf_ += ' ';
207 break;
208
209 case '\n':
210 if (prev_backslash) {
211 cmd_buf_[cmd_buf_.size()-1] = ' ';
212 } else {
213 cmd_buf_ += ' ';
214 }
Shinichiro Hamaji4d151832015-06-29 18:15:46 +0900215 break;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900216
217 case '\\':
218 prev_backslash = !prev_backslash;
219 cmd_buf_ += '\\';
220 break;
221
222 default:
223 cmd_buf_ += *in;
224 prev_backslash = false;
225 }
226 }
227
228 while (true) {
229 char c = cmd_buf_[cmd_buf_.size()-1];
230 if (!isspace(c) && c != ';')
231 break;
232 cmd_buf_.resize(cmd_buf_.size() - 1);
233 }
234
235 return StringPiece(cmd_buf_.data() + orig_size,
236 cmd_buf_.size() - orig_size);
237 }
238
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900239 bool GenShellScript(const vector<Command*>& commands) {
240 bool use_gomacc = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900241 bool should_ignore_error = false;
242 cmd_buf_.clear();
243 for (const Command* c : commands) {
244 if (!cmd_buf_.empty()) {
245 if (should_ignore_error) {
246 cmd_buf_ += " ; ";
247 } else {
248 cmd_buf_ += " && ";
249 }
250 }
251 should_ignore_error = c->ignore_error;
252
253 const char* in = c->cmd->c_str();
254 while (isspace(*in))
255 in++;
256
257 bool needs_subshell = commands.size() > 1;
258 if (*in == '(') {
259 needs_subshell = false;
260 }
261
262 if (needs_subshell)
263 cmd_buf_ += '(';
264
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900265 size_t cmd_start = cmd_buf_.size();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900266 StringPiece translated = TranslateCommand(in);
267 if (translated.empty()) {
268 cmd_buf_ += "true";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900269 } else if (g_goma_dir && IsAndroidCompileCommand(translated)) {
270 cmd_buf_.insert(cmd_start, gomacc_);
271 use_gomacc = true;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900272 }
273
274 if (c == commands.back() && c->ignore_error) {
275 cmd_buf_ += " ; true";
276 }
277
278 if (needs_subshell)
279 cmd_buf_ += ')';
280 }
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900281 return g_goma_dir && !use_gomacc;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900282 }
283
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900284 void EmitDepfile() {
285 cmd_buf_ += ' ';
286 string depfile;
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900287 bool result = GetDepfileFromCommand(&cmd_buf_, &depfile);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900288 cmd_buf_.resize(cmd_buf_.size()-1);
289 if (!result)
290 return;
291 fprintf(fp_, " depfile = %s\n", depfile.c_str());
Shinichiro Hamaji71966412015-07-11 03:12:59 +0900292 fprintf(fp_, " deps = gcc\n");
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900293 }
294
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900295 void EmitNode(DepNode* node) {
296 auto p = done_.insert(node->output);
297 if (!p.second)
298 return;
299
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900300 if (node->cmds.empty() &&
301 node->deps.empty() && node->order_onlys.empty() && !node->is_phony) {
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900302 return;
Shinichiro Hamaji0f39c522015-07-07 13:14:02 +0900303 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900304
305 vector<Command*> commands;
306 ce_.Eval(node, &commands);
307
308 string rule_name = "phony";
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900309 bool use_local_pool = false;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900310 if (!commands.empty()) {
311 rule_name = GenRuleName();
312 fprintf(fp_, "rule %s\n", rule_name.c_str());
313 fprintf(fp_, " description = build $out\n");
314
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900315 use_local_pool |= GenShellScript(commands);
Shinichiro Hamajie9f7e672015-07-03 15:57:45 +0900316 EmitDepfile();
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900317
318 // It seems Linux is OK with ~130kB.
319 // TODO: Find this number automatically.
320 if (cmd_buf_.size() > 100 * 1000) {
321 fprintf(fp_, " rspfile = $out.rsp\n");
322 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
323 fprintf(fp_, " command = sh $out.rsp\n");
324 } else {
325 fprintf(fp_, " command = %s\n", cmd_buf_.c_str());
326 }
327 }
328
329 EmitBuild(node, rule_name);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900330 if (use_local_pool)
331 fprintf(fp_, " pool = local_pool\n");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900332
333 for (DepNode* d : node->deps) {
334 EmitNode(d);
335 }
Shinichiro Hamaji3f2cf1e2015-07-06 18:58:18 +0900336 for (DepNode* d : node->order_onlys) {
337 EmitNode(d);
338 }
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900339 }
340
341 void EmitBuild(DepNode* node, const string& rule_name) {
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900342 fprintf(fp_, "build %s: %s", node->output.c_str(), rule_name.c_str());
343 vector<Symbol> order_onlys;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900344 for (DepNode* d : node->deps) {
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900345 fprintf(fp_, " %s", d->output.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900346 }
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900347 if (!node->order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900348 fprintf(fp_, " ||");
Shinichiro Hamaji183dbb92015-07-06 17:21:39 +0900349 for (DepNode* d : node->order_onlys) {
350 fprintf(fp_, " %s", d->output.c_str());
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900351 }
352 }
353 fprintf(fp_, "\n");
354 }
355
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900356 string GetNinjaFilename() const {
357 return StringPrintf("build%s.ninja", ninja_suffix_.c_str());
358 }
359
360 string GetShellScriptFilename() const {
361 return StringPrintf("ninja%s.sh", ninja_suffix_.c_str());
362 }
363
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900364 void GenerateNinja(const vector<DepNode*>& nodes) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900365 fp_ = fopen(GetNinjaFilename().c_str(), "wb");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900366 if (fp_ == NULL)
367 PERROR("fopen(build.ninja) failed");
368
369 fprintf(fp_, "# Generated by kati\n");
370 fprintf(fp_, "\n");
371
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900372 if (g_goma_dir) {
373 fprintf(fp_, "pool local_pool\n");
Shinichiro Hamaji087cecd2015-07-06 19:51:58 +0900374 fprintf(fp_, " depth = %d\n", g_num_jobs);
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900375 }
376
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900377 for (DepNode* node : nodes) {
378 EmitNode(node);
379 }
380
381 fclose(fp_);
382 }
383
384 void GenerateShell() {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900385 FILE* fp = fopen(GetShellScriptFilename().c_str(), "wb");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900386 if (fp == NULL)
387 PERROR("fopen(ninja.sh) failed");
388
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900389 shared_ptr<string> shell = ev_->EvalVar(kShellSym);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900390 if (shell->empty())
391 shell = make_shared<string>("/bin/sh");
392 fprintf(fp, "#!%s\n", shell->c_str());
393
394 for (const auto& p : ev_->exports()) {
395 if (p.second) {
Shinichiro Hamaji94d6f2a2015-07-05 05:32:25 +0900396 shared_ptr<string> val = ev_->EvalVar(p.first);
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900397 fprintf(fp, "export %s=%s\n", p.first.c_str(), val->c_str());
398 } else {
399 fprintf(fp, "unset %s\n", p.first.c_str());
400 }
401 }
402
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900403 fprintf(fp, "exec ninja -f %s ", GetNinjaFilename().c_str());
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900404 if (g_goma_dir) {
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900405 fprintf(fp, "-j300 ");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900406 }
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900407 fprintf(fp, "\"$@\"\n");
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900408
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900409 if (chmod(GetShellScriptFilename().c_str(), 0755) != 0)
Shinichiro Hamaji54b93ba2015-07-03 20:43:32 +0900410 PERROR("chmod ninja.sh failed");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900411 }
412
413 CommandEvaluator ce_;
414 Evaluator* ev_;
415 FILE* fp_;
Shinichiro Hamajie7992752015-06-29 18:38:35 +0900416 unordered_set<Symbol> done_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900417 int rule_id_;
418 string cmd_buf_;
Shinichiro Hamaji9facae22015-07-03 17:16:36 +0900419 string gomacc_;
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900420 string ninja_suffix_;
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900421};
422
Shinichiro Hamaji8bddb462015-07-06 18:55:47 +0900423void GenerateNinja(const char* ninja_suffix,
424 const vector<DepNode*>& nodes, Evaluator* ev) {
425 NinjaGenerator ng(ninja_suffix, ev);
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900426 ng.Generate(nodes);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900427}