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