func.cc revision e41c7556c22bda359c2b97cd98d59082110add95
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 43namespace { 44 45// TODO: This code is very similar to 46// NinjaGenerator::TranslateCommand. Factor them out. 47void 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 99void 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 111void 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 119void 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 140void 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 147void 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 165void 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 186void 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 205static 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 215void 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 237void 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 270void 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 281void 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 289void 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 298void 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 318void 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 334void 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 343void 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 355void 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 365void 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 373void 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 383void 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 393void 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 415void 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 425void 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 435void 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 447void 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 457void 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 463void 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. 488static 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 496static void ShellFuncImpl(const string& shell, const string& shellflag, 497 const string& cmd, string* s, FindCommand** fc) { 498 LOG("ShellFunc: %s", cmd.c_str()); 499 500#ifdef TEST_FIND_EMULATOR 501 bool need_check = false; 502 string out2; 503#endif 504 if (FindEmulator::Get()) { 505 *fc = new FindCommand(); 506 if ((*fc)->Parse(cmd)) { 507#ifdef TEST_FIND_EMULATOR 508 if (FindEmulator::Get()->HandleFind(cmd, **fc, &out2)) { 509 need_check = true; 510 } 511#else 512 if (FindEmulator::Get()->HandleFind(cmd, **fc, s)) { 513 return; 514 } 515#endif 516 } 517 delete *fc; 518 *fc = NULL; 519 } 520 521 COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str()); 522 RunCommand(shell, shellflag, cmd, RedirectStderr::NONE, s); 523 FormatForCommandSubstitution(s); 524 525#ifdef TEST_FIND_EMULATOR 526 if (need_check) { 527 if (*s != out2) { 528 ERROR("FindEmulator is broken: %s\n%s\nvs\n%s", 529 cmd.c_str(), s->c_str(), out2.c_str()); 530 } 531 } 532#endif 533} 534 535static vector<CommandResult*> g_command_results; 536 537bool ShouldStoreCommandResult(StringPiece cmd) { 538 // We really just want to ignore this one, or remove BUILD_DATETIME from 539 // Android completely 540 if (cmd == "date +%s") 541 return false; 542 543 Pattern pat(g_flags.ignore_dirty_pattern); 544 Pattern nopat(g_flags.no_ignore_dirty_pattern); 545 for (StringPiece tok : WordScanner(cmd)) { 546 if (pat.Match(tok) && !nopat.Match(tok)) { 547 return false; 548 } 549 } 550 551 return true; 552} 553 554void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 555 string cmd = args[0]->Eval(ev); 556 if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) { 557 if (ev->eval_depth() > 1) { 558 ERROR_LOC(ev->loc(), "kati doesn't support passing results of $(shell) " 559 "to other make constructs: %s", 560 cmd.c_str()); 561 } 562 StripShellComment(&cmd); 563 *s += "$("; 564 *s += cmd; 565 *s += ")"; 566 return; 567 } 568 569 const string&& shell = ev->GetShell(); 570 const string&& shellflag = ev->GetShellFlag(); 571 572 string out; 573 FindCommand* fc = NULL; 574 ShellFuncImpl(shell, shellflag, cmd, &out, &fc); 575 if (ShouldStoreCommandResult(cmd)) { 576 CommandResult* cr = new CommandResult(); 577 cr->op = (fc == NULL) ? CommandOp::SHELL : CommandOp::FIND, 578 cr->shell = shell; 579 cr->shellflag = shellflag; 580 cr->cmd = cmd; 581 cr->find.reset(fc); 582 cr->result = out; 583 g_command_results.push_back(cr); 584 } 585 *s += out; 586} 587 588void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 589 static const Symbol tmpvar_names[] = { 590 Intern("0"), Intern("1"), Intern("2"), Intern("3"), Intern("4"), 591 Intern("5"), Intern("6"), Intern("7"), Intern("8"), Intern("9") 592 }; 593 594 const string&& func_name_buf = args[0]->Eval(ev); 595 const StringPiece func_name = TrimSpace(func_name_buf); 596 Var* func = ev->LookupVar(Intern(func_name)); 597 if (!func->IsDefined()) { 598 KATI_WARN_LOC(ev->loc(), "*warning*: undefined user function: %s", 599 func_name.as_string().c_str()); 600 } 601 vector<unique_ptr<SimpleVar>> av; 602 for (size_t i = 1; i < args.size(); i++) { 603 unique_ptr<SimpleVar> s( 604 new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC)); 605 av.push_back(move(s)); 606 } 607 vector<unique_ptr<ScopedGlobalVar>> sv; 608 for (size_t i = 1; ; i++) { 609 string s; 610 Symbol tmpvar_name_sym(Symbol::IsUninitialized{}); 611 if (i < sizeof(tmpvar_names)/sizeof(tmpvar_names[0])) { 612 tmpvar_name_sym = tmpvar_names[i]; 613 } else { 614 s = StringPrintf("%d", i); 615 tmpvar_name_sym = Intern(s); 616 } 617 if (i < args.size()) { 618 sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get())); 619 } else { 620 // We need to blank further automatic vars 621 Var *v = ev->LookupVar(tmpvar_name_sym); 622 if (!v->IsDefined()) break; 623 if (v->Origin() != VarOrigin::AUTOMATIC) break; 624 625 av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC)); 626 sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get())); 627 } 628 } 629 630 ev->DecrementEvalDepth(); 631 func->Eval(ev, s); 632 ev->IncrementEvalDepth(); 633} 634 635void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 636 const string&& varname = args[0]->Eval(ev); 637 const string&& list = args[1]->Eval(ev); 638 ev->DecrementEvalDepth(); 639 WordWriter ww(s); 640 for (StringPiece tok : WordScanner(list)) { 641 unique_ptr<SimpleVar> v(new SimpleVar( 642 tok.as_string(), VarOrigin::AUTOMATIC)); 643 ScopedGlobalVar sv(Intern(varname), v.get()); 644 ww.MaybeAddWhitespace(); 645 args[2]->Eval(ev, s); 646 } 647 ev->IncrementEvalDepth(); 648} 649 650void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 651 const string&& var_name = args[0]->Eval(ev); 652 Var* var = ev->LookupVar(Intern(var_name)); 653 *s += GetOriginStr(var->Origin()); 654} 655 656void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 657 const string&& var_name = args[0]->Eval(ev); 658 Var* var = ev->LookupVar(Intern(var_name)); 659 *s += var->Flavor(); 660} 661 662void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) { 663 const string&& a = args[0]->Eval(ev); 664 if (ev->avoid_io()) { 665 ev->add_delayed_output_command(StringPrintf("echo -e \"%s\"", EchoEscape(a).c_str())); 666 return; 667 } 668 printf("%s\n", a.c_str()); 669 fflush(stdout); 670} 671 672void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) { 673 const string&& a = args[0]->Eval(ev); 674 if (ev->avoid_io()) { 675 ev->add_delayed_output_command( 676 StringPrintf("echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str())); 677 return; 678 } 679 WARN_LOC(ev->loc(), "%s", a.c_str()); 680} 681 682void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) { 683 const string&& a = args[0]->Eval(ev); 684 if (ev->avoid_io()) { 685 ev->add_delayed_output_command( 686 StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false", 687 LOCF(ev->loc()), EchoEscape(a).c_str())); 688 return; 689 } 690 ev->Error(StringPrintf("*** %s.", a.c_str())); 691} 692 693static void FileReadFunc(Evaluator* ev, const string& filename, string* s) { 694 int fd = open(filename.c_str(), O_RDONLY); 695 if (fd < 0) { 696 if (errno == ENOENT) { 697 if (ShouldStoreCommandResult(filename)) { 698 CommandResult* cr = new CommandResult(); 699 cr->op = CommandOp::READ_MISSING; 700 cr->cmd = filename; 701 g_command_results.push_back(cr); 702 } 703 return; 704 } else { 705 ev->Error("*** open failed."); 706 } 707 } 708 709 struct stat st; 710 if (fstat(fd, &st) < 0) { 711 ev->Error("*** fstat failed."); 712 } 713 714 size_t len = st.st_size; 715 string out; 716 out.resize(len); 717 ssize_t r = HANDLE_EINTR(read(fd, &out[0], len)); 718 if (r != static_cast<ssize_t>(len)) { 719 ev->Error("*** read failed."); 720 } 721 722 if (close(fd) < 0) { 723 ev->Error("*** close failed."); 724 } 725 726 if (out.back() == '\n') { 727 out.pop_back(); 728 } 729 730 if (ShouldStoreCommandResult(filename)) { 731 CommandResult* cr = new CommandResult(); 732 cr->op = CommandOp::READ; 733 cr->cmd = filename; 734 g_command_results.push_back(cr); 735 } 736 *s += out; 737} 738 739static void FileWriteFunc(Evaluator* ev, const string& filename, bool append, string text) { 740 FILE* f = fopen(filename.c_str(), append ? "ab" : "wb"); 741 if (f == NULL) { 742 ev->Error("*** fopen failed."); 743 } 744 745 if (fwrite(&text[0], text.size(), 1, f) != 1) { 746 ev->Error("*** fwrite failed."); 747 } 748 749 if (fclose(f) != 0) { 750 ev->Error("*** fclose failed."); 751 } 752 753 if (ShouldStoreCommandResult(filename)) { 754 CommandResult* cr = new CommandResult(); 755 cr->op = CommandOp::WRITE; 756 cr->cmd = filename; 757 cr->result = text; 758 g_command_results.push_back(cr); 759 } 760} 761 762void FileFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 763 if (ev->avoid_io()) { 764 ev->Error("*** $(file ...) is not supported in rules."); 765 } 766 767 string arg = args[0]->Eval(ev); 768 StringPiece filename = TrimSpace(arg); 769 770 if (filename.size() <= 1) { 771 ev->Error("*** Missing filename"); 772 } 773 774 if (filename[0] == '<') { 775 filename = TrimLeftSpace(filename.substr(1)); 776 if (!filename.size()) { 777 ev->Error("*** Missing filename"); 778 } 779 if (args.size() > 1) { 780 ev->Error("*** invalid argument"); 781 } 782 783 FileReadFunc(ev, filename.as_string(), s); 784 } else if (filename[0] == '>') { 785 bool append = false; 786 if (filename[1] == '>') { 787 append = true; 788 filename = filename.substr(2); 789 } else { 790 filename = filename.substr(1); 791 } 792 filename = TrimLeftSpace(filename); 793 if (!filename.size()) { 794 ev->Error("*** Missing filename"); 795 } 796 797 string text; 798 if (args.size() > 1) { 799 text = args[1]->Eval(ev); 800 if (text.size() == 0 || text.back() != '\n') { 801 text.push_back('\n'); 802 } 803 } 804 805 FileWriteFunc(ev, filename.as_string(), append, text); 806 } else { 807 ev->Error(StringPrintf("*** Invalid file operation: %s. Stop.", filename.as_string().c_str())); 808 } 809} 810 811FuncInfo g_func_infos[] = { 812 { "patsubst", &PatsubstFunc, 3, 3, false, false }, 813 { "strip", &StripFunc, 1, 1, false, false }, 814 { "subst", &SubstFunc, 3, 3, false, false }, 815 { "findstring", &FindstringFunc, 2, 2, false, false }, 816 { "filter", &FilterFunc, 2, 2, false, false }, 817 { "filter-out", &FilterOutFunc, 2, 2, false, false }, 818 { "sort", &SortFunc, 1, 1, false, false }, 819 { "word", &WordFunc, 2, 2, false, false }, 820 { "wordlist", &WordlistFunc, 3, 3, false, false }, 821 { "words", &WordsFunc, 1, 1, false, false }, 822 { "firstword", &FirstwordFunc, 1, 1, false, false }, 823 { "lastword", &LastwordFunc, 1, 1, false, false }, 824 825 { "join", &JoinFunc, 2, 2, false, false }, 826 { "wildcard", &WildcardFunc, 1, 1, false, false }, 827 { "dir", &DirFunc, 1, 1, false, false }, 828 { "notdir", &NotdirFunc, 1, 1, false, false }, 829 { "suffix", &SuffixFunc, 1, 1, false, false }, 830 { "basename", &BasenameFunc, 1, 1, false, false }, 831 { "addsuffix", &AddsuffixFunc, 2, 2, false, false }, 832 { "addprefix", &AddprefixFunc, 2, 2, false, false }, 833 { "realpath", &RealpathFunc, 1, 1, false, false }, 834 { "abspath", &AbspathFunc, 1, 1, false, false }, 835 836 { "if", &IfFunc, 3, 2, false, true }, 837 { "and", &AndFunc, 0, 0, true, false }, 838 { "or", &OrFunc, 0, 0, true, false }, 839 840 { "value", &ValueFunc, 1, 1, false, false }, 841 { "eval", &EvalFunc, 1, 1, false, false }, 842 { "shell", &ShellFunc, 1, 1, false, false }, 843 { "call", &CallFunc, 0, 0, false, false }, 844 { "foreach", &ForeachFunc, 3, 3, false, false }, 845 846 { "origin", &OriginFunc, 1, 1, false, false }, 847 { "flavor", &FlavorFunc, 1, 1, false, false }, 848 849 { "info", &InfoFunc, 1, 1, false, false }, 850 { "warning", &WarningFunc, 1, 1, false, false }, 851 { "error", &ErrorFunc, 1, 1, false, false }, 852 853 { "file", &FileFunc, 2, 1, false, false }, 854}; 855 856unordered_map<StringPiece, FuncInfo*>* g_func_info_map; 857 858} // namespace 859 860void InitFuncTable() { 861 g_func_info_map = new unordered_map<StringPiece, FuncInfo*>; 862 for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) { 863 FuncInfo* fi = &g_func_infos[i]; 864 bool ok = g_func_info_map->emplace(fi->name, fi).second; 865 CHECK(ok); 866 } 867} 868 869void QuitFuncTable() { 870 delete g_func_info_map; 871} 872 873FuncInfo* GetFuncInfo(StringPiece name) { 874 auto found = g_func_info_map->find(name); 875 if (found == g_func_info_map->end()) 876 return NULL; 877 return found->second; 878} 879 880const vector<CommandResult*>& GetShellCommandResults() { 881 return g_command_results; 882} 883