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