• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "eval.h"
18 
19 #include <errno.h>
20 #include <pthread.h>
21 #include <string.h>
22 
23 #include "expr.h"
24 #include "file.h"
25 #include "file_cache.h"
26 #include "fileutil.h"
27 #include "parser.h"
28 #include "rule.h"
29 #include "stmt.h"
30 #include "strutil.h"
31 #include "symtab.h"
32 #include "var.h"
33 
Evaluator()34 Evaluator::Evaluator()
35     : last_rule_(NULL),
36       current_scope_(NULL),
37       avoid_io_(false),
38       eval_depth_(0),
39       posix_sym_(Intern(".POSIX")),
40       is_posix_(false),
41       export_error_(false) {
42 #if defined(__APPLE__)
43   stack_size_ = pthread_get_stacksize_np(pthread_self());
44   stack_addr_ = (char*)pthread_get_stackaddr_np(pthread_self()) - stack_size_;
45 #else
46   pthread_attr_t attr;
47   CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
48   CHECK(pthread_attr_getstack(&attr, &stack_addr_, &stack_size_) == 0);
49   CHECK(pthread_attr_destroy(&attr) == 0);
50 #endif
51 
52   lowest_stack_ = (char*)stack_addr_ + stack_size_;
53   LOG_STAT("Stack size: %zd bytes", stack_size_);
54 }
55 
~Evaluator()56 Evaluator::~Evaluator() {
57   // delete vars_;
58   // for (auto p : rule_vars) {
59   //   delete p.second;
60   // }
61 }
62 
EvalRHS(Symbol lhs,Value * rhs_v,StringPiece orig_rhs,AssignOp op,bool is_override,bool * needs_assign)63 Var* Evaluator::EvalRHS(Symbol lhs,
64                         Value* rhs_v,
65                         StringPiece orig_rhs,
66                         AssignOp op,
67                         bool is_override,
68                         bool* needs_assign) {
69   VarOrigin origin =
70       ((is_bootstrap_ ? VarOrigin::DEFAULT
71                       : is_commandline_ ? VarOrigin::COMMAND_LINE
72                                         : is_override ? VarOrigin::OVERRIDE
73                                                       : VarOrigin::FILE));
74 
75   Var* result = NULL;
76   Var* prev = NULL;
77   *needs_assign = true;
78 
79   switch (op) {
80     case AssignOp::COLON_EQ: {
81       prev = PeekVarInCurrentScope(lhs);
82       result = new SimpleVar(origin, this, rhs_v);
83       break;
84     }
85     case AssignOp::EQ:
86       prev = PeekVarInCurrentScope(lhs);
87       result = new RecursiveVar(rhs_v, origin, orig_rhs);
88       break;
89     case AssignOp::PLUS_EQ: {
90       prev = LookupVarInCurrentScope(lhs);
91       if (!prev->IsDefined()) {
92         result = new RecursiveVar(rhs_v, origin, orig_rhs);
93       } else if (prev->ReadOnly()) {
94         Error(StringPrintf("*** cannot assign to readonly variable: %s",
95                            lhs.c_str()));
96       } else {
97         result = prev;
98         result->AppendVar(this, rhs_v);
99         *needs_assign = false;
100       }
101       break;
102     }
103     case AssignOp::QUESTION_EQ: {
104       prev = LookupVarInCurrentScope(lhs);
105       if (!prev->IsDefined()) {
106         result = new RecursiveVar(rhs_v, origin, orig_rhs);
107       } else {
108         result = prev;
109         *needs_assign = false;
110       }
111       break;
112     }
113   }
114 
115   if (prev != NULL) {
116     prev->Used(this, lhs);
117     if (prev->Deprecated() && *needs_assign) {
118       result->SetDeprecated(prev->DeprecatedMessage());
119     }
120   }
121 
122   LOG("Assign: %s=%s", lhs.c_str(), result->DebugString().c_str());
123   return result;
124 }
125 
EvalAssign(const AssignStmt * stmt)126 void Evaluator::EvalAssign(const AssignStmt* stmt) {
127   loc_ = stmt->loc();
128   last_rule_ = NULL;
129   Symbol lhs = stmt->GetLhsSymbol(this);
130   if (lhs.empty())
131     Error("*** empty variable name.");
132 
133   if (lhs == kKatiReadonlySym) {
134     string rhs;
135     stmt->rhs->Eval(this, &rhs);
136     for (auto const& name : WordScanner(rhs)) {
137       Var* var = Intern(name).GetGlobalVar();
138       if (!var->IsDefined()) {
139         Error(
140             StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
141       }
142       var->SetReadOnly();
143     }
144     return;
145   }
146 
147   bool needs_assign;
148   Var* var =
149       EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
150               stmt->directive == AssignDirective::OVERRIDE, &needs_assign);
151   if (needs_assign) {
152     bool readonly;
153     lhs.SetGlobalVar(var, stmt->directive == AssignDirective::OVERRIDE,
154                      &readonly);
155     if (readonly) {
156       Error(StringPrintf("*** cannot assign to readonly variable: %s",
157                          lhs.c_str()));
158     }
159   }
160 
161   if (stmt->is_final) {
162     var->SetReadOnly();
163   }
164 }
165 
166 // With rule broken into
167 //   <before_term> <term> <after_term>
168 // parses <before_term> into Symbol instances until encountering ':'
169 // Returns the remainder of <before_term>.
ParseRuleTargets(const Loc & loc,const StringPiece & before_term,vector<Symbol> * targets,bool * is_pattern_rule)170 static StringPiece ParseRuleTargets(const Loc& loc,
171                                     const StringPiece& before_term,
172                                     vector<Symbol>* targets,
173                                     bool* is_pattern_rule) {
174   size_t pos = before_term.find(':');
175   if (pos == string::npos) {
176     ERROR_LOC(loc, "*** missing separator.");
177   }
178   StringPiece targets_string = before_term.substr(0, pos);
179   size_t pattern_rule_count = 0;
180   for (auto const& word : WordScanner(targets_string)) {
181     StringPiece target = TrimLeadingCurdir(word);
182     targets->push_back(Intern(target));
183     if (Rule::IsPatternRule(target)) {
184       ++pattern_rule_count;
185     }
186   }
187   // Check consistency: either all outputs are patterns or none.
188   if (pattern_rule_count && (pattern_rule_count != targets->size())) {
189     ERROR_LOC(loc, "*** mixed implicit and normal rules: deprecated syntax");
190   }
191   *is_pattern_rule = pattern_rule_count;
192   return before_term.substr(pos + 1);
193 }
194 
MarkVarsReadonly(Value * vars_list)195 void Evaluator::MarkVarsReadonly(Value* vars_list) {
196   string vars_list_string;
197   vars_list->Eval(this, &vars_list_string);
198   for (auto const& name : WordScanner(vars_list_string)) {
199     Var* var = current_scope_->Lookup(Intern(name));
200     if (!var->IsDefined()) {
201       Error(StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
202     }
203     var->SetReadOnly();
204   }
205 }
206 
EvalRuleSpecificAssign(const vector<Symbol> & targets,const RuleStmt * stmt,const StringPiece & after_targets,size_t separator_pos)207 void Evaluator::EvalRuleSpecificAssign(const vector<Symbol>& targets,
208                                        const RuleStmt* stmt,
209                                        const StringPiece& after_targets,
210                                        size_t separator_pos) {
211   StringPiece var_name;
212   StringPiece rhs_string;
213   AssignOp assign_op;
214   ParseAssignStatement(after_targets, separator_pos, &var_name, &rhs_string,
215                        &assign_op);
216   Symbol var_sym = Intern(var_name);
217   bool is_final = (stmt->sep == RuleStmt::SEP_FINALEQ);
218   for (Symbol target : targets) {
219     auto p = rule_vars_.emplace(target, nullptr);
220     if (p.second) {
221       p.first->second = new Vars;
222     }
223 
224     Value* rhs;
225     if (rhs_string.empty()) {
226       rhs = stmt->rhs;
227     } else if (stmt->rhs) {
228       StringPiece sep(stmt->sep == RuleStmt::SEP_SEMICOLON ? " ; " : " = ");
229       rhs = Value::NewExpr(Value::NewLiteral(rhs_string),
230                            Value::NewLiteral(sep), stmt->rhs);
231     } else {
232       rhs = Value::NewLiteral(rhs_string);
233     }
234 
235     current_scope_ = p.first->second;
236     if (var_sym == kKatiReadonlySym) {
237       MarkVarsReadonly(rhs);
238     } else {
239       bool needs_assign;
240       Var* rhs_var = EvalRHS(var_sym, rhs, StringPiece("*TODO*"), assign_op,
241                              false, &needs_assign);
242       if (needs_assign) {
243         bool readonly;
244         rhs_var->SetAssignOp(assign_op);
245         current_scope_->Assign(var_sym, rhs_var, &readonly);
246         if (readonly) {
247           Error(StringPrintf("*** cannot assign to readonly variable: %s",
248                              var_name));
249         }
250       }
251       if (is_final) {
252         rhs_var->SetReadOnly();
253       }
254     }
255     current_scope_ = NULL;
256   }
257 }
258 
EvalRule(const RuleStmt * stmt)259 void Evaluator::EvalRule(const RuleStmt* stmt) {
260   loc_ = stmt->loc();
261   last_rule_ = NULL;
262 
263   const string&& before_term = stmt->lhs->Eval(this);
264   // See semicolon.mk.
265   if (before_term.find_first_not_of(" \t;") == string::npos) {
266     if (stmt->sep == RuleStmt::SEP_SEMICOLON)
267       Error("*** missing rule before commands.");
268     return;
269   }
270 
271   vector<Symbol> targets;
272   bool is_pattern_rule;
273   StringPiece after_targets =
274       ParseRuleTargets(loc_, before_term, &targets, &is_pattern_rule);
275   bool is_double_colon = (after_targets[0] == ':');
276   if (is_double_colon) {
277     after_targets = after_targets.substr(1);
278   }
279 
280   // Figure out if this is a rule-specific variable assignment.
281   // It is an assignment when either after_targets contains an assignment token
282   // or separator is an assignment token, but only if there is no ';' before the
283   // first assignment token.
284   size_t separator_pos = after_targets.find_first_of("=;");
285   char separator = '\0';
286   if (separator_pos != string::npos) {
287     separator = after_targets[separator_pos];
288   } else if (separator_pos == string::npos &&
289              (stmt->sep == RuleStmt::SEP_EQ ||
290               stmt->sep == RuleStmt::SEP_FINALEQ)) {
291     separator_pos = after_targets.size();
292     separator = '=';
293   }
294 
295   // If variable name is not empty, we have rule- or target-specific
296   // variable assignment.
297   if (separator == '=' && separator_pos) {
298     EvalRuleSpecificAssign(targets, stmt, after_targets, separator_pos);
299     return;
300   }
301 
302   // "test: =foo" is questionable but a valid rule definition (not a
303   // target specific variable).
304   // See https://github.com/google/kati/issues/83
305   string buf;
306   if (!separator_pos) {
307     KATI_WARN_LOC(loc_,
308                   "defining a target which starts with `=', "
309                   "which is not probably what you meant");
310     buf = after_targets.as_string();
311     if (stmt->sep == RuleStmt::SEP_SEMICOLON) {
312       buf += ';';
313     } else if (stmt->sep == RuleStmt::SEP_EQ ||
314                stmt->sep == RuleStmt::SEP_FINALEQ) {
315       buf += '=';
316     }
317     if (stmt->rhs) {
318       buf += stmt->rhs->Eval(this);
319     }
320     after_targets = buf;
321     separator_pos = string::npos;
322   }
323 
324   Rule* rule = new Rule();
325   rule->loc = loc_;
326   rule->is_double_colon = is_double_colon;
327   if (is_pattern_rule) {
328     rule->output_patterns.swap(targets);
329   } else {
330     rule->outputs.swap(targets);
331   }
332   rule->ParsePrerequisites(after_targets, separator_pos, stmt);
333 
334   if (stmt->sep == RuleStmt::SEP_SEMICOLON) {
335     rule->cmds.push_back(stmt->rhs);
336   }
337 
338   for (Symbol o : rule->outputs) {
339     if (o == posix_sym_)
340       is_posix_ = true;
341   }
342 
343   LOG("Rule: %s", rule->DebugString().c_str());
344   rules_.push_back(rule);
345   last_rule_ = rule;
346 }
347 
EvalCommand(const CommandStmt * stmt)348 void Evaluator::EvalCommand(const CommandStmt* stmt) {
349   loc_ = stmt->loc();
350 
351   if (!last_rule_) {
352     vector<Stmt*> stmts;
353     ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
354     for (Stmt* a : stmts)
355       a->Eval(this);
356     return;
357   }
358 
359   last_rule_->cmds.push_back(stmt->expr);
360   if (last_rule_->cmd_lineno == 0)
361     last_rule_->cmd_lineno = stmt->loc().lineno;
362   LOG("Command: %s", Value::DebugString(stmt->expr).c_str());
363 }
364 
EvalIf(const IfStmt * stmt)365 void Evaluator::EvalIf(const IfStmt* stmt) {
366   loc_ = stmt->loc();
367 
368   bool is_true;
369   switch (stmt->op) {
370     case CondOp::IFDEF:
371     case CondOp::IFNDEF: {
372       string var_name;
373       stmt->lhs->Eval(this, &var_name);
374       Symbol lhs = Intern(TrimRightSpace(var_name));
375       if (lhs.str().find_first_of(" \t") != string::npos)
376         Error("*** invalid syntax in conditional.");
377       Var* v = LookupVarInCurrentScope(lhs);
378       v->Used(this, lhs);
379       is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF));
380       break;
381     }
382     case CondOp::IFEQ:
383     case CondOp::IFNEQ: {
384       const string&& lhs = stmt->lhs->Eval(this);
385       const string&& rhs = stmt->rhs->Eval(this);
386       is_true = ((lhs == rhs) == (stmt->op == CondOp::IFEQ));
387       break;
388     }
389     default:
390       CHECK(false);
391       abort();
392   }
393 
394   const vector<Stmt*>* stmts;
395   if (is_true) {
396     stmts = &stmt->true_stmts;
397   } else {
398     stmts = &stmt->false_stmts;
399   }
400   for (Stmt* a : *stmts) {
401     LOG("%s", a->DebugString().c_str());
402     a->Eval(this);
403   }
404 }
405 
DoInclude(const string & fname)406 void Evaluator::DoInclude(const string& fname) {
407   CheckStack();
408 
409   Makefile* mk = MakefileCacheManager::Get()->ReadMakefile(fname);
410   if (!mk->Exists()) {
411     Error(StringPrintf("%s does not exist", fname.c_str()));
412   }
413 
414   Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
415   var_list->AppendVar(
416       this, Value::NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
417   for (Stmt* stmt : mk->stmts()) {
418     LOG("%s", stmt->DebugString().c_str());
419     stmt->Eval(this);
420   }
421 }
422 
EvalInclude(const IncludeStmt * stmt)423 void Evaluator::EvalInclude(const IncludeStmt* stmt) {
424   loc_ = stmt->loc();
425   last_rule_ = NULL;
426 
427   const string&& pats = stmt->expr->Eval(this);
428   for (StringPiece pat : WordScanner(pats)) {
429     ScopedTerminator st(pat);
430     vector<string>* files;
431     Glob(pat.data(), &files);
432 
433     if (stmt->should_exist) {
434       if (files->empty()) {
435         // TODO: Kati does not support building a missing include file.
436         Error(StringPrintf("%s: %s", pat.data(), strerror(errno)));
437       }
438     }
439 
440     for (const string& fname : *files) {
441       if (!stmt->should_exist && g_flags.ignore_optional_include_pattern &&
442           Pattern(g_flags.ignore_optional_include_pattern).Match(fname)) {
443         continue;
444       }
445       DoInclude(fname);
446     }
447   }
448 }
449 
EvalExport(const ExportStmt * stmt)450 void Evaluator::EvalExport(const ExportStmt* stmt) {
451   loc_ = stmt->loc();
452   last_rule_ = NULL;
453 
454   const string&& exports = stmt->expr->Eval(this);
455   for (StringPiece tok : WordScanner(exports)) {
456     size_t equal_index = tok.find('=');
457     StringPiece lhs;
458     if (equal_index == string::npos) {
459       lhs = tok;
460     } else if (equal_index == 0 ||
461                (equal_index == 1 &&
462                 (tok[0] == ':' || tok[0] == '?' || tok[0] == '+'))) {
463       // Do not export tokens after an assignment.
464       break;
465     } else {
466       StringPiece rhs;
467       AssignOp op;
468       ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
469     }
470     Symbol sym = Intern(lhs);
471     exports_[sym] = stmt->is_export;
472 
473     if (export_message_) {
474       const char* prefix = "";
475       if (!stmt->is_export) {
476         prefix = "un";
477       }
478 
479       if (export_error_) {
480         Error(StringPrintf("*** %s: %sexport is obsolete%s.", sym.c_str(),
481                            prefix, export_message_->c_str()));
482       } else {
483         WARN_LOC(loc(), "%s: %sexport has been deprecated%s.", sym.c_str(),
484                  prefix, export_message_->c_str());
485       }
486     }
487   }
488 }
489 
LookupVarGlobal(Symbol name)490 Var* Evaluator::LookupVarGlobal(Symbol name) {
491   Var* v = name.GetGlobalVar();
492   if (v->IsDefined())
493     return v;
494   used_undefined_vars_.insert(name);
495   return v;
496 }
497 
LookupVar(Symbol name)498 Var* Evaluator::LookupVar(Symbol name) {
499   if (current_scope_) {
500     Var* v = current_scope_->Lookup(name);
501     if (v->IsDefined())
502       return v;
503   }
504   return LookupVarGlobal(name);
505 }
506 
PeekVar(Symbol name)507 Var* Evaluator::PeekVar(Symbol name) {
508   if (current_scope_) {
509     Var* v = current_scope_->Peek(name);
510     if (v->IsDefined())
511       return v;
512   }
513   return name.PeekGlobalVar();
514 }
515 
LookupVarInCurrentScope(Symbol name)516 Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
517   if (current_scope_) {
518     return current_scope_->Lookup(name);
519   }
520   return LookupVarGlobal(name);
521 }
522 
PeekVarInCurrentScope(Symbol name)523 Var* Evaluator::PeekVarInCurrentScope(Symbol name) {
524   if (current_scope_) {
525     return current_scope_->Peek(name);
526   }
527   return name.PeekGlobalVar();
528 }
529 
EvalVar(Symbol name)530 string Evaluator::EvalVar(Symbol name) {
531   return LookupVar(name)->Eval(this);
532 }
533 
GetShell()534 string Evaluator::GetShell() {
535   return EvalVar(kShellSym);
536 }
537 
GetShellFlag()538 string Evaluator::GetShellFlag() {
539   // TODO: Handle $(.SHELLFLAGS)
540   return is_posix_ ? "-ec" : "-c";
541 }
542 
GetShellAndFlag()543 string Evaluator::GetShellAndFlag() {
544   string shell = GetShell();
545   shell += ' ';
546   shell += GetShellFlag();
547   return shell;
548 }
549 
Error(const string & msg)550 void Evaluator::Error(const string& msg) {
551   ERROR_LOC(loc_, "%s", msg.c_str());
552 }
553 
DumpStackStats() const554 void Evaluator::DumpStackStats() const {
555   LOG_STAT("Max stack use: %zd bytes at %s:%d",
556            ((char*)stack_addr_ - (char*)lowest_stack_) + stack_size_,
557            LOCF(lowest_loc_));
558 }
559 
560 SymbolSet Evaluator::used_undefined_vars_;
561