1 // 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 "func.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include <algorithm>
28 #include <iterator>
29 #include <memory>
30 #include <unordered_map>
31
32 #include "eval.h"
33 #include "fileutil.h"
34 #include "find.h"
35 #include "log.h"
36 #include "parser.h"
37 #include "stats.h"
38 #include "stmt.h"
39 #include "strutil.h"
40 #include "symtab.h"
41 #include "var.h"
42
43 namespace {
44
45 // TODO: This code is very similar to
46 // NinjaGenerator::TranslateCommand. Factor them out.
StripShellComment(string * cmd)47 void StripShellComment(string* cmd) {
48 if (cmd->find('#') == string::npos)
49 return;
50
51 string res;
52 bool prev_backslash = false;
53 // Set space as an initial value so the leading comment will be
54 // stripped out.
55 char prev_char = ' ';
56 char quote = 0;
57 bool done = false;
58 const char* in = cmd->c_str();
59 for (; *in && !done; in++) {
60 switch (*in) {
61 case '#':
62 if (quote == 0 && isspace(prev_char)) {
63 while (in[1] && *in != '\n')
64 in++;
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 res += *in;
78 break;
79
80 case '\\':
81 res += '\\';
82 break;
83
84 default:
85 res += *in;
86 }
87
88 if (*in == '\\') {
89 prev_backslash = !prev_backslash;
90 } else {
91 prev_backslash = false;
92 }
93
94 prev_char = *in;
95 }
96 cmd->swap(res);
97 }
98
PatsubstFunc(const vector<Value * > & args,Evaluator * ev,string * s)99 void PatsubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
100 const string&& pat_str = args[0]->Eval(ev);
101 const string&& repl = args[1]->Eval(ev);
102 const string&& str = args[2]->Eval(ev);
103 WordWriter ww(s);
104 Pattern pat(pat_str);
105 for (StringPiece tok : WordScanner(str)) {
106 ww.MaybeAddWhitespace();
107 pat.AppendSubst(tok, repl, s);
108 }
109 }
110
StripFunc(const vector<Value * > & args,Evaluator * ev,string * s)111 void StripFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
112 const string&& str = args[0]->Eval(ev);
113 WordWriter ww(s);
114 for (StringPiece tok : WordScanner(str)) {
115 ww.Write(tok);
116 }
117 }
118
SubstFunc(const vector<Value * > & args,Evaluator * ev,string * s)119 void SubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
120 const string&& pat = args[0]->Eval(ev);
121 const string&& repl = args[1]->Eval(ev);
122 const string&& str = args[2]->Eval(ev);
123 if (pat.empty()) {
124 *s += str;
125 *s += repl;
126 return;
127 }
128 size_t index = 0;
129 while (index < str.size()) {
130 size_t found = str.find(pat, index);
131 if (found == string::npos)
132 break;
133 AppendString(StringPiece(str).substr(index, found - index), s);
134 AppendString(repl, s);
135 index = found + pat.size();
136 }
137 AppendString(StringPiece(str).substr(index), s);
138 }
139
FindstringFunc(const vector<Value * > & args,Evaluator * ev,string * s)140 void FindstringFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
141 const string&& find = args[0]->Eval(ev);
142 const string&& in = args[1]->Eval(ev);
143 if (in.find(find) != string::npos)
144 AppendString(find, s);
145 }
146
FilterFunc(const vector<Value * > & args,Evaluator * ev,string * s)147 void FilterFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
148 const string&& pat_buf = args[0]->Eval(ev);
149 const string&& text = args[1]->Eval(ev);
150 vector<Pattern> pats;
151 for (StringPiece pat : WordScanner(pat_buf)) {
152 pats.push_back(Pattern(pat));
153 }
154 WordWriter ww(s);
155 for (StringPiece tok : WordScanner(text)) {
156 for (const Pattern& pat : pats) {
157 if (pat.Match(tok)) {
158 ww.Write(tok);
159 break;
160 }
161 }
162 }
163 }
164
FilterOutFunc(const vector<Value * > & args,Evaluator * ev,string * s)165 void FilterOutFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
166 const string&& pat_buf = args[0]->Eval(ev);
167 const string&& text = args[1]->Eval(ev);
168 vector<Pattern> pats;
169 for (StringPiece pat : WordScanner(pat_buf)) {
170 pats.push_back(Pattern(pat));
171 }
172 WordWriter ww(s);
173 for (StringPiece tok : WordScanner(text)) {
174 bool matched = false;
175 for (const Pattern& pat : pats) {
176 if (pat.Match(tok)) {
177 matched = true;
178 break;
179 }
180 }
181 if (!matched)
182 ww.Write(tok);
183 }
184 }
185
SortFunc(const vector<Value * > & args,Evaluator * ev,string * s)186 void SortFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
187 string list;
188 args[0]->Eval(ev, &list);
189 COLLECT_STATS("func sort time");
190 // TODO(hamaji): Probably we could use a faster string-specific sort
191 // algorithm.
192 vector<StringPiece> toks;
193 WordScanner(list).Split(&toks);
194 stable_sort(toks.begin(), toks.end());
195 WordWriter ww(s);
196 StringPiece prev;
197 for (StringPiece tok : toks) {
198 if (prev != tok) {
199 ww.Write(tok);
200 prev = tok;
201 }
202 }
203 }
204
GetNumericValueForFunc(const string & buf)205 static int GetNumericValueForFunc(const string& buf) {
206 StringPiece s = TrimLeftSpace(buf);
207 char* end;
208 long n = strtol(s.data(), &end, 10);
209 if (n < 0 || n == LONG_MAX || s.data() + s.size() != end) {
210 return -1;
211 }
212 return n;
213 }
214
WordFunc(const vector<Value * > & args,Evaluator * ev,string * s)215 void WordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
216 const string&& n_str = args[0]->Eval(ev);
217 int n = GetNumericValueForFunc(n_str);
218 if (n < 0) {
219 ev->Error(StringPrintf(
220 "*** non-numeric first argument to `word' function: '%s'.",
221 n_str.c_str()));
222 }
223 if (n == 0) {
224 ev->Error("*** first argument to `word' function must be greater than 0.");
225 }
226
227 const string&& text = args[1]->Eval(ev);
228 for (StringPiece tok : WordScanner(text)) {
229 n--;
230 if (n == 0) {
231 AppendString(tok, s);
232 break;
233 }
234 }
235 }
236
WordlistFunc(const vector<Value * > & args,Evaluator * ev,string * s)237 void WordlistFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
238 const string&& s_str = args[0]->Eval(ev);
239 int si = GetNumericValueForFunc(s_str);
240 if (si < 0) {
241 ev->Error(StringPrintf(
242 "*** non-numeric first argument to `wordlist' function: '%s'.",
243 s_str.c_str()));
244 }
245 if (si == 0) {
246 ev->Error(StringPrintf(
247 "*** invalid first argument to `wordlist' function: %s`",
248 s_str.c_str()));
249 }
250
251 const string&& e_str = args[1]->Eval(ev);
252 int ei = GetNumericValueForFunc(e_str);
253 if (ei < 0) {
254 ev->Error(StringPrintf(
255 "*** non-numeric second argument to `wordlist' function: '%s'.",
256 e_str.c_str()));
257 }
258
259 const string&& text = args[2]->Eval(ev);
260 int i = 0;
261 WordWriter ww(s);
262 for (StringPiece tok : WordScanner(text)) {
263 i++;
264 if (si <= i && i <= ei) {
265 ww.Write(tok);
266 }
267 }
268 }
269
WordsFunc(const vector<Value * > & args,Evaluator * ev,string * s)270 void WordsFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
271 const string&& text = args[0]->Eval(ev);
272 WordScanner ws(text);
273 int n = 0;
274 for (auto iter = ws.begin(); iter != ws.end(); ++iter)
275 n++;
276 char buf[32];
277 sprintf(buf, "%d", n);
278 *s += buf;
279 }
280
FirstwordFunc(const vector<Value * > & args,Evaluator * ev,string * s)281 void FirstwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
282 const string&& text = args[0]->Eval(ev);
283 for (StringPiece tok : WordScanner(text)) {
284 AppendString(tok, s);
285 return;
286 }
287 }
288
LastwordFunc(const vector<Value * > & args,Evaluator * ev,string * s)289 void LastwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
290 const string&& text = args[0]->Eval(ev);
291 StringPiece last;
292 for (StringPiece tok : WordScanner(text)) {
293 last = tok;
294 }
295 AppendString(last, s);
296 }
297
JoinFunc(const vector<Value * > & args,Evaluator * ev,string * s)298 void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
299 const string&& list1 = args[0]->Eval(ev);
300 const string&& list2 = args[1]->Eval(ev);
301 WordScanner ws1(list1);
302 WordScanner ws2(list2);
303 WordWriter ww(s);
304 WordScanner::Iterator iter1, iter2;
305 for (iter1 = ws1.begin(), iter2 = ws2.begin();
306 iter1 != ws1.end() && iter2 != ws2.end();
307 ++iter1, ++iter2) {
308 ww.Write(*iter1);
309 // Use |AppendString| not to append extra ' '.
310 AppendString(*iter2, s);
311 }
312 for (; iter1 != ws1.end(); ++iter1)
313 ww.Write(*iter1);
314 for (; iter2 != ws2.end(); ++iter2)
315 ww.Write(*iter2);
316 }
317
WildcardFunc(const vector<Value * > & args,Evaluator * ev,string * s)318 void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
319 const string&& pat = args[0]->Eval(ev);
320 COLLECT_STATS("func wildcard time");
321 // Note GNU make does not delay the execution of $(wildcard) so we
322 // do not need to check avoid_io here.
323 WordWriter ww(s);
324 vector<string>* files;
325 for (StringPiece tok : WordScanner(pat)) {
326 ScopedTerminator st(tok);
327 Glob(tok.data(), &files);
328 for (const string& file : *files) {
329 ww.Write(file);
330 }
331 }
332 }
333
DirFunc(const vector<Value * > & args,Evaluator * ev,string * s)334 void DirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
335 const string&& text = args[0]->Eval(ev);
336 WordWriter ww(s);
337 for (StringPiece tok : WordScanner(text)) {
338 ww.Write(Dirname(tok));
339 s->push_back('/');
340 }
341 }
342
NotdirFunc(const vector<Value * > & args,Evaluator * ev,string * s)343 void NotdirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
344 const string&& text = args[0]->Eval(ev);
345 WordWriter ww(s);
346 for (StringPiece tok : WordScanner(text)) {
347 if (tok == "/") {
348 ww.Write(StringPiece(""));
349 } else {
350 ww.Write(Basename(tok));
351 }
352 }
353 }
354
SuffixFunc(const vector<Value * > & args,Evaluator * ev,string * s)355 void SuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
356 const string&& text = args[0]->Eval(ev);
357 WordWriter ww(s);
358 for (StringPiece tok : WordScanner(text)) {
359 StringPiece suf = GetExt(tok);
360 if (!suf.empty())
361 ww.Write(suf);
362 }
363 }
364
BasenameFunc(const vector<Value * > & args,Evaluator * ev,string * s)365 void BasenameFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
366 const string&& text = args[0]->Eval(ev);
367 WordWriter ww(s);
368 for (StringPiece tok : WordScanner(text)) {
369 ww.Write(StripExt(tok));
370 }
371 }
372
AddsuffixFunc(const vector<Value * > & args,Evaluator * ev,string * s)373 void AddsuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
374 const string&& suf = args[0]->Eval(ev);
375 const string&& text = args[1]->Eval(ev);
376 WordWriter ww(s);
377 for (StringPiece tok : WordScanner(text)) {
378 ww.Write(tok);
379 *s += suf;
380 }
381 }
382
AddprefixFunc(const vector<Value * > & args,Evaluator * ev,string * s)383 void AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
384 const string&& pre = args[0]->Eval(ev);
385 const string&& text = args[1]->Eval(ev);
386 WordWriter ww(s);
387 for (StringPiece tok : WordScanner(text)) {
388 ww.Write(pre);
389 AppendString(tok, s);
390 }
391 }
392
RealpathFunc(const vector<Value * > & args,Evaluator * ev,string * s)393 void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
394 const string&& text = args[0]->Eval(ev);
395 if (ev->avoid_io()) {
396 *s += "$(";
397 string kati_binary;
398 GetExecutablePath(&kati_binary);
399 *s += kati_binary;
400 *s += " --realpath ";
401 *s += text;
402 *s += " 2> /dev/null)";
403 return;
404 }
405
406 WordWriter ww(s);
407 for (StringPiece tok : WordScanner(text)) {
408 ScopedTerminator st(tok);
409 char buf[PATH_MAX];
410 if (realpath(tok.data(), buf))
411 ww.Write(buf);
412 }
413 }
414
AbspathFunc(const vector<Value * > & args,Evaluator * ev,string * s)415 void AbspathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
416 const string&& text = args[0]->Eval(ev);
417 WordWriter ww(s);
418 string buf;
419 for (StringPiece tok : WordScanner(text)) {
420 AbsPath(tok, &buf);
421 ww.Write(buf);
422 }
423 }
424
IfFunc(const vector<Value * > & args,Evaluator * ev,string * s)425 void IfFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
426 const string&& cond = args[0]->Eval(ev);
427 if (cond.empty()) {
428 if (args.size() > 2)
429 args[2]->Eval(ev, s);
430 } else {
431 args[1]->Eval(ev, s);
432 }
433 }
434
AndFunc(const vector<Value * > & args,Evaluator * ev,string * s)435 void AndFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
436 string cond;
437 for (Value* a : args) {
438 cond = a->Eval(ev);
439 if (cond.empty())
440 return;
441 }
442 if (!cond.empty()) {
443 *s += cond;
444 }
445 }
446
OrFunc(const vector<Value * > & args,Evaluator * ev,string * s)447 void OrFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
448 for (Value* a : args) {
449 const string&& cond = a->Eval(ev);
450 if (!cond.empty()) {
451 *s += cond;
452 return;
453 }
454 }
455 }
456
ValueFunc(const vector<Value * > & args,Evaluator * ev,string * s)457 void ValueFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
458 const string&& var_name = args[0]->Eval(ev);
459 Var* var = ev->LookupVar(Intern(var_name));
460 AppendString(var->String().as_string(), s);
461 }
462
EvalFunc(const vector<Value * > & args,Evaluator * ev,string *)463 void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) {
464 // TODO: eval leaks everything... for now.
465 //const string text = args[0]->Eval(ev);
466 string* text = new string;
467 args[0]->Eval(ev, text);
468 if (ev->avoid_io()) {
469 KATI_WARN_LOC(ev->loc(), "*warning*: $(eval) in a recipe is not recommended: %s",
470 text->c_str());
471 }
472 vector<Stmt*> stmts;
473 Parse(*text, ev->loc(), &stmts);
474 for (Stmt* stmt : stmts) {
475 LOG("%s", stmt->DebugString().c_str());
476 stmt->Eval(ev);
477 //delete stmt;
478 }
479 }
480
481 //#define TEST_FIND_EMULATOR
482
483 // A hack for Android build. We need to evaluate things like $((3+4))
484 // when we emit ninja file, because the result of such expressions
485 // will be passed to other make functions.
486 // TODO: Maybe we should introduce a helper binary which evaluate
487 // make expressions at ninja-time.
HasNoIoInShellScript(const string & cmd)488 static bool HasNoIoInShellScript(const string& cmd) {
489 if (cmd.empty())
490 return true;
491 if (HasPrefix(cmd, "echo $((") && cmd[cmd.size()-1] == ')')
492 return true;
493 return false;
494 }
495
ShellFuncImpl(const string & shell,const string & shellflag,const string & cmd,const Loc & loc,string * s,FindCommand ** fc)496 static void ShellFuncImpl(const string& shell, const string& shellflag,
497 const string& cmd, const Loc& loc, string* s,
498 FindCommand** fc) {
499 LOG("ShellFunc: %s", cmd.c_str());
500
501 #ifdef TEST_FIND_EMULATOR
502 bool need_check = false;
503 string out2;
504 #endif
505 if (FindEmulator::Get()) {
506 *fc = new FindCommand();
507 if ((*fc)->Parse(cmd)) {
508 #ifdef TEST_FIND_EMULATOR
509 if (FindEmulator::Get()->HandleFind(cmd, **fc, loc, &out2)) {
510 need_check = true;
511 }
512 #else
513 if (FindEmulator::Get()->HandleFind(cmd, **fc, loc, s)) {
514 return;
515 }
516 #endif
517 }
518 delete *fc;
519 *fc = NULL;
520 }
521
522 COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
523 RunCommand(shell, shellflag, cmd, RedirectStderr::NONE, s);
524 FormatForCommandSubstitution(s);
525
526 #ifdef TEST_FIND_EMULATOR
527 if (need_check) {
528 if (*s != out2) {
529 ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
530 cmd.c_str(), s->c_str(), out2.c_str());
531 }
532 }
533 #endif
534 }
535
536 static vector<CommandResult*> g_command_results;
537
ShouldStoreCommandResult(StringPiece cmd)538 bool ShouldStoreCommandResult(StringPiece cmd) {
539 // We really just want to ignore this one, or remove BUILD_DATETIME from
540 // Android completely
541 if (cmd == "date +%s")
542 return false;
543
544 Pattern pat(g_flags.ignore_dirty_pattern);
545 Pattern nopat(g_flags.no_ignore_dirty_pattern);
546 for (StringPiece tok : WordScanner(cmd)) {
547 if (pat.Match(tok) && !nopat.Match(tok)) {
548 return false;
549 }
550 }
551
552 return true;
553 }
554
ShellFunc(const vector<Value * > & args,Evaluator * ev,string * s)555 void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
556 string cmd = args[0]->Eval(ev);
557 if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
558 if (ev->eval_depth() > 1) {
559 ERROR_LOC(ev->loc(), "kati doesn't support passing results of $(shell) "
560 "to other make constructs: %s",
561 cmd.c_str());
562 }
563 StripShellComment(&cmd);
564 *s += "$(";
565 *s += cmd;
566 *s += ")";
567 return;
568 }
569
570 const string&& shell = ev->GetShell();
571 const string&& shellflag = ev->GetShellFlag();
572
573 string out;
574 FindCommand* fc = NULL;
575 ShellFuncImpl(shell, shellflag, cmd, ev->loc(), &out, &fc);
576 if (ShouldStoreCommandResult(cmd)) {
577 CommandResult* cr = new CommandResult();
578 cr->op = (fc == NULL) ? CommandOp::SHELL : CommandOp::FIND,
579 cr->shell = shell;
580 cr->shellflag = shellflag;
581 cr->cmd = cmd;
582 cr->find.reset(fc);
583 cr->result = out;
584 g_command_results.push_back(cr);
585 }
586 *s += out;
587 }
588
CallFunc(const vector<Value * > & args,Evaluator * ev,string * s)589 void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
590 static const Symbol tmpvar_names[] = {
591 Intern("0"), Intern("1"), Intern("2"), Intern("3"), Intern("4"),
592 Intern("5"), Intern("6"), Intern("7"), Intern("8"), Intern("9")
593 };
594
595 const string&& func_name_buf = args[0]->Eval(ev);
596 const StringPiece func_name = TrimSpace(func_name_buf);
597 Var* func = ev->LookupVar(Intern(func_name));
598 if (!func->IsDefined()) {
599 KATI_WARN_LOC(ev->loc(), "*warning*: undefined user function: %s",
600 func_name.as_string().c_str());
601 }
602 vector<unique_ptr<SimpleVar>> av;
603 for (size_t i = 1; i < args.size(); i++) {
604 unique_ptr<SimpleVar> s(
605 new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC));
606 av.push_back(move(s));
607 }
608 vector<unique_ptr<ScopedGlobalVar>> sv;
609 for (size_t i = 1; ; i++) {
610 string s;
611 Symbol tmpvar_name_sym(Symbol::IsUninitialized{});
612 if (i < sizeof(tmpvar_names)/sizeof(tmpvar_names[0])) {
613 tmpvar_name_sym = tmpvar_names[i];
614 } else {
615 s = StringPrintf("%d", i);
616 tmpvar_name_sym = Intern(s);
617 }
618 if (i < args.size()) {
619 sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get()));
620 } else {
621 // We need to blank further automatic vars
622 Var *v = ev->LookupVar(tmpvar_name_sym);
623 if (!v->IsDefined()) break;
624 if (v->Origin() != VarOrigin::AUTOMATIC) break;
625
626 av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC));
627 sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get()));
628 }
629 }
630
631 ev->DecrementEvalDepth();
632 func->Eval(ev, s);
633 ev->IncrementEvalDepth();
634 }
635
ForeachFunc(const vector<Value * > & args,Evaluator * ev,string * s)636 void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
637 const string&& varname = args[0]->Eval(ev);
638 const string&& list = args[1]->Eval(ev);
639 ev->DecrementEvalDepth();
640 WordWriter ww(s);
641 for (StringPiece tok : WordScanner(list)) {
642 unique_ptr<SimpleVar> v(new SimpleVar(
643 tok.as_string(), VarOrigin::AUTOMATIC));
644 ScopedGlobalVar sv(Intern(varname), v.get());
645 ww.MaybeAddWhitespace();
646 args[2]->Eval(ev, s);
647 }
648 ev->IncrementEvalDepth();
649 }
650
OriginFunc(const vector<Value * > & args,Evaluator * ev,string * s)651 void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
652 const string&& var_name = args[0]->Eval(ev);
653 Var* var = ev->LookupVar(Intern(var_name));
654 *s += GetOriginStr(var->Origin());
655 }
656
FlavorFunc(const vector<Value * > & args,Evaluator * ev,string * s)657 void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
658 const string&& var_name = args[0]->Eval(ev);
659 Var* var = ev->LookupVar(Intern(var_name));
660 *s += var->Flavor();
661 }
662
InfoFunc(const vector<Value * > & args,Evaluator * ev,string *)663 void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
664 const string&& a = args[0]->Eval(ev);
665 if (ev->avoid_io()) {
666 ev->add_delayed_output_command(StringPrintf("echo -e \"%s\"", EchoEscape(a).c_str()));
667 return;
668 }
669 printf("%s\n", a.c_str());
670 fflush(stdout);
671 }
672
WarningFunc(const vector<Value * > & args,Evaluator * ev,string *)673 void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) {
674 const string&& a = args[0]->Eval(ev);
675 if (ev->avoid_io()) {
676 ev->add_delayed_output_command(
677 StringPrintf("echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str()));
678 return;
679 }
680 WARN_LOC(ev->loc(), "%s", a.c_str());
681 }
682
ErrorFunc(const vector<Value * > & args,Evaluator * ev,string *)683 void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
684 const string&& a = args[0]->Eval(ev);
685 if (ev->avoid_io()) {
686 ev->add_delayed_output_command(
687 StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false",
688 LOCF(ev->loc()), EchoEscape(a).c_str()));
689 return;
690 }
691 ev->Error(StringPrintf("*** %s.", a.c_str()));
692 }
693
FileReadFunc(Evaluator * ev,const string & filename,string * s)694 static void FileReadFunc(Evaluator* ev, const string& filename, string* s) {
695 int fd = open(filename.c_str(), O_RDONLY);
696 if (fd < 0) {
697 if (errno == ENOENT) {
698 if (ShouldStoreCommandResult(filename)) {
699 CommandResult* cr = new CommandResult();
700 cr->op = CommandOp::READ_MISSING;
701 cr->cmd = filename;
702 g_command_results.push_back(cr);
703 }
704 return;
705 } else {
706 ev->Error("*** open failed.");
707 }
708 }
709
710 struct stat st;
711 if (fstat(fd, &st) < 0) {
712 ev->Error("*** fstat failed.");
713 }
714
715 size_t len = st.st_size;
716 string out;
717 out.resize(len);
718 ssize_t r = HANDLE_EINTR(read(fd, &out[0], len));
719 if (r != static_cast<ssize_t>(len)) {
720 ev->Error("*** read failed.");
721 }
722
723 if (close(fd) < 0) {
724 ev->Error("*** close failed.");
725 }
726
727 if (out.back() == '\n') {
728 out.pop_back();
729 }
730
731 if (ShouldStoreCommandResult(filename)) {
732 CommandResult* cr = new CommandResult();
733 cr->op = CommandOp::READ;
734 cr->cmd = filename;
735 g_command_results.push_back(cr);
736 }
737 *s += out;
738 }
739
FileWriteFunc(Evaluator * ev,const string & filename,bool append,string text)740 static void FileWriteFunc(Evaluator* ev, const string& filename, bool append, string text) {
741 FILE* f = fopen(filename.c_str(), append ? "ab" : "wb");
742 if (f == NULL) {
743 ev->Error("*** fopen failed.");
744 }
745
746 if (fwrite(&text[0], text.size(), 1, f) != 1) {
747 ev->Error("*** fwrite failed.");
748 }
749
750 if (fclose(f) != 0) {
751 ev->Error("*** fclose failed.");
752 }
753
754 if (ShouldStoreCommandResult(filename)) {
755 CommandResult* cr = new CommandResult();
756 cr->op = CommandOp::WRITE;
757 cr->cmd = filename;
758 cr->result = text;
759 g_command_results.push_back(cr);
760 }
761 }
762
FileFunc(const vector<Value * > & args,Evaluator * ev,string * s)763 void FileFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
764 if (ev->avoid_io()) {
765 ev->Error("*** $(file ...) is not supported in rules.");
766 }
767
768 string arg = args[0]->Eval(ev);
769 StringPiece filename = TrimSpace(arg);
770
771 if (filename.size() <= 1) {
772 ev->Error("*** Missing filename");
773 }
774
775 if (filename[0] == '<') {
776 filename = TrimLeftSpace(filename.substr(1));
777 if (!filename.size()) {
778 ev->Error("*** Missing filename");
779 }
780 if (args.size() > 1) {
781 ev->Error("*** invalid argument");
782 }
783
784 FileReadFunc(ev, filename.as_string(), s);
785 } else if (filename[0] == '>') {
786 bool append = false;
787 if (filename[1] == '>') {
788 append = true;
789 filename = filename.substr(2);
790 } else {
791 filename = filename.substr(1);
792 }
793 filename = TrimLeftSpace(filename);
794 if (!filename.size()) {
795 ev->Error("*** Missing filename");
796 }
797
798 string text;
799 if (args.size() > 1) {
800 text = args[1]->Eval(ev);
801 if (text.size() == 0 || text.back() != '\n') {
802 text.push_back('\n');
803 }
804 }
805
806 FileWriteFunc(ev, filename.as_string(), append, text);
807 } else {
808 ev->Error(StringPrintf("*** Invalid file operation: %s. Stop.", filename.as_string().c_str()));
809 }
810 }
811
812 FuncInfo g_func_infos[] = {
813 { "patsubst", &PatsubstFunc, 3, 3, false, false },
814 { "strip", &StripFunc, 1, 1, false, false },
815 { "subst", &SubstFunc, 3, 3, false, false },
816 { "findstring", &FindstringFunc, 2, 2, false, false },
817 { "filter", &FilterFunc, 2, 2, false, false },
818 { "filter-out", &FilterOutFunc, 2, 2, false, false },
819 { "sort", &SortFunc, 1, 1, false, false },
820 { "word", &WordFunc, 2, 2, false, false },
821 { "wordlist", &WordlistFunc, 3, 3, false, false },
822 { "words", &WordsFunc, 1, 1, false, false },
823 { "firstword", &FirstwordFunc, 1, 1, false, false },
824 { "lastword", &LastwordFunc, 1, 1, false, false },
825
826 { "join", &JoinFunc, 2, 2, false, false },
827 { "wildcard", &WildcardFunc, 1, 1, false, false },
828 { "dir", &DirFunc, 1, 1, false, false },
829 { "notdir", &NotdirFunc, 1, 1, false, false },
830 { "suffix", &SuffixFunc, 1, 1, false, false },
831 { "basename", &BasenameFunc, 1, 1, false, false },
832 { "addsuffix", &AddsuffixFunc, 2, 2, false, false },
833 { "addprefix", &AddprefixFunc, 2, 2, false, false },
834 { "realpath", &RealpathFunc, 1, 1, false, false },
835 { "abspath", &AbspathFunc, 1, 1, false, false },
836
837 { "if", &IfFunc, 3, 2, false, true },
838 { "and", &AndFunc, 0, 0, true, false },
839 { "or", &OrFunc, 0, 0, true, false },
840
841 { "value", &ValueFunc, 1, 1, false, false },
842 { "eval", &EvalFunc, 1, 1, false, false },
843 { "shell", &ShellFunc, 1, 1, false, false },
844 { "call", &CallFunc, 0, 0, false, false },
845 { "foreach", &ForeachFunc, 3, 3, false, false },
846
847 { "origin", &OriginFunc, 1, 1, false, false },
848 { "flavor", &FlavorFunc, 1, 1, false, false },
849
850 { "info", &InfoFunc, 1, 1, false, false },
851 { "warning", &WarningFunc, 1, 1, false, false },
852 { "error", &ErrorFunc, 1, 1, false, false },
853
854 { "file", &FileFunc, 2, 1, false, false },
855 };
856
857 unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
858
859 } // namespace
860
InitFuncTable()861 void InitFuncTable() {
862 g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
863 for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
864 FuncInfo* fi = &g_func_infos[i];
865 bool ok = g_func_info_map->emplace(fi->name, fi).second;
866 CHECK(ok);
867 }
868 }
869
QuitFuncTable()870 void QuitFuncTable() {
871 delete g_func_info_map;
872 }
873
GetFuncInfo(StringPiece name)874 FuncInfo* GetFuncInfo(StringPiece name) {
875 auto found = g_func_info_map->find(name);
876 if (found == g_func_info_map->end())
877 return NULL;
878 return found->second;
879 }
880
GetShellCommandResults()881 const vector<CommandResult*>& GetShellCommandResults() {
882 return g_command_results;
883 }
884