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