blob: 2fd8046016303581f1e35a0832181f3699d5282b [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 Hamaji5f86e1a2015-06-29 14:25:39 +090020
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +090021#include <memory>
22#include <string>
23#include <unordered_set>
24
25#include "command.h"
26#include "dep.h"
27#include "eval.h"
28#include "log.h"
29#include "string_piece.h"
30#include "stringprintf.h"
31#include "strutil.h"
32#include "var.h"
33
34class NinjaGenerator {
35 public:
36 explicit NinjaGenerator(Evaluator* ev)
37 : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) {
38 ev_->set_avoid_io(true);
39 }
40
41 ~NinjaGenerator() {
42 ev_->set_avoid_io(false);
43 }
44
45 void Generate(const vector<DepNode*>& nodes) {
46 GenerateShell();
47 GenerateNinja(nodes);
48 }
49
50 private:
51 string GenRuleName() {
52 return StringPrintf("rule%d", rule_id_++);
53 }
54
55 StringPiece TranslateCommand(const char* in) {
56 const size_t orig_size = cmd_buf_.size();
57 bool prev_backslash = false;
58 char quote = 0;
59 bool done = false;
60 for (; *in && !done; in++) {
61 switch (*in) {
62 case '#':
63 if (quote == 0 && !prev_backslash) {
64 done = true;
65 break;
66 }
67
68 case '\'':
69 case '"':
70 case '`':
71 if (quote) {
72 if (quote == *in)
73 quote = 0;
74 } else if (!prev_backslash) {
75 quote = *in;
76 }
77
78 case '$':
79 cmd_buf_ += "$$";
80 break;
81
82 case '\t':
83 cmd_buf_ += ' ';
84 break;
85
86 case '\n':
87 if (prev_backslash) {
88 cmd_buf_[cmd_buf_.size()-1] = ' ';
89 } else {
90 cmd_buf_ += ' ';
91 }
92
93 case '\\':
94 prev_backslash = !prev_backslash;
95 cmd_buf_ += '\\';
96 break;
97
98 default:
99 cmd_buf_ += *in;
100 prev_backslash = false;
101 }
102 }
103
104 while (true) {
105 char c = cmd_buf_[cmd_buf_.size()-1];
106 if (!isspace(c) && c != ';')
107 break;
108 cmd_buf_.resize(cmd_buf_.size() - 1);
109 }
110
111 return StringPiece(cmd_buf_.data() + orig_size,
112 cmd_buf_.size() - orig_size);
113 }
114
115 void GenShellScript(const vector<Command*>& commands) {
116 //bool use_gomacc = false;
117 bool should_ignore_error = false;
118 cmd_buf_.clear();
119 for (const Command* c : commands) {
120 if (!cmd_buf_.empty()) {
121 if (should_ignore_error) {
122 cmd_buf_ += " ; ";
123 } else {
124 cmd_buf_ += " && ";
125 }
126 }
127 should_ignore_error = c->ignore_error;
128
129 const char* in = c->cmd->c_str();
130 while (isspace(*in))
131 in++;
132
133 bool needs_subshell = commands.size() > 1;
134 if (*in == '(') {
135 needs_subshell = false;
136 }
137
138 if (needs_subshell)
139 cmd_buf_ += '(';
140
141 StringPiece translated = TranslateCommand(in);
142 if (translated.empty()) {
143 cmd_buf_ += "true";
144 } else {
145 // TODO: flip use_gomacc
146 }
147
148 if (c == commands.back() && c->ignore_error) {
149 cmd_buf_ += " ; true";
150 }
151
152 if (needs_subshell)
153 cmd_buf_ += ')';
154 }
155 }
156
157 void EmitNode(DepNode* node) {
158 auto p = done_.insert(node->output);
159 if (!p.second)
160 return;
161
162 if (node->cmds.empty() && node->deps.empty() && !node->is_phony)
163 return;
164
165 vector<Command*> commands;
166 ce_.Eval(node, &commands);
167
168 string rule_name = "phony";
169 if (!commands.empty()) {
170 rule_name = GenRuleName();
171 fprintf(fp_, "rule %s\n", rule_name.c_str());
172 fprintf(fp_, " description = build $out\n");
173
174 GenShellScript(commands);
175 // TODO: depfile
176
177 // It seems Linux is OK with ~130kB.
178 // TODO: Find this number automatically.
179 if (cmd_buf_.size() > 100 * 1000) {
180 fprintf(fp_, " rspfile = $out.rsp\n");
181 fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str());
182 fprintf(fp_, " command = sh $out.rsp\n");
183 } else {
184 fprintf(fp_, " command = %s\n", cmd_buf_.c_str());
185 }
186 }
187
188 EmitBuild(node, rule_name);
189 // goma
190
191 for (DepNode* d : node->deps) {
192 EmitNode(d);
193 }
194 }
195
196 void EmitBuild(DepNode* node, const string& rule_name) {
197 fprintf(fp_, "build %.*s: %s", SPF(node->output), rule_name.c_str());
198 vector<StringPiece> order_onlys;
199 for (DepNode* d : node->deps) {
200 if (d->is_order_only) {
201 order_onlys.push_back(d->output);
202 } else {
203 fprintf(fp_, " %.*s", SPF(d->output));
204 }
205 }
206 if (!order_onlys.empty()) {
Shinichiro Hamajif4820de2015-06-29 17:03:17 +0900207 fprintf(fp_, " ||");
Shinichiro Hamajidf1fc8b2015-06-29 15:45:21 +0900208 for (StringPiece oo : order_onlys) {
209 fprintf(fp_, " %.*s", SPF(oo));
210 }
211 }
212 fprintf(fp_, "\n");
213 }
214
215 void GenerateNinja(const vector<DepNode*>& nodes) {
216 fp_ = fopen("build.ninja", "wb");
217 if (fp_ == NULL)
218 PERROR("fopen(build.ninja) failed");
219
220 fprintf(fp_, "# Generated by kati\n");
221 fprintf(fp_, "\n");
222
223 for (DepNode* node : nodes) {
224 EmitNode(node);
225 }
226
227 fclose(fp_);
228 }
229
230 void GenerateShell() {
231#if 0
232 Var* v = ev->LookupVar("SHELL");
233 shell_ = v->Eval(ev);
234 if (shell_->empty())
235 shell_ = make_shared<string>("/bin/sh");
236#endif
237 }
238
239 CommandEvaluator ce_;
240 Evaluator* ev_;
241 FILE* fp_;
242 unordered_set<StringPiece> done_;
243 int rule_id_;
244 string cmd_buf_;
245};
246
247void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev) {
248 NinjaGenerator ng(ev);
249 ng.Generate(nodes);
Shinichiro Hamaji5f86e1a2015-06-29 14:25:39 +0900250}