Home | History | Annotate | Download | only in kati
      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 
     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       kati_readonly_(Intern(".KATI_READONLY")) {
     43 #if defined(__APPLE__)
     44   stack_size_ = pthread_get_stacksize_np(pthread_self());
     45   stack_addr_ = (char*)pthread_get_stackaddr_np(pthread_self()) - stack_size_;
     46 #else
     47   pthread_attr_t attr;
     48   CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
     49   CHECK(pthread_attr_getstack(&attr, &stack_addr_, &stack_size_) == 0);
     50   CHECK(pthread_attr_destroy(&attr) == 0);
     51 #endif
     52 
     53   lowest_stack_ = (char*)stack_addr_ + stack_size_;
     54   LOG_STAT("Stack size: %zd bytes", stack_size_);
     55 }
     56 
     57 Evaluator::~Evaluator() {
     58   // delete vars_;
     59   // for (auto p : rule_vars) {
     60   //   delete p.second;
     61   // }
     62 }
     63 
     64 Var* Evaluator::EvalRHS(Symbol lhs,
     65                         Value* rhs_v,
     66                         StringPiece orig_rhs,
     67                         AssignOp op,
     68                         bool is_override) {
     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* rhs = NULL;
     76   Var* prev = NULL;
     77   bool needs_assign = true;
     78 
     79   switch (op) {
     80     case AssignOp::COLON_EQ: {
     81       prev = PeekVarInCurrentScope(lhs);
     82       SimpleVar* sv = new SimpleVar(origin);
     83       rhs_v->Eval(this, sv->mutable_value());
     84       rhs = sv;
     85       break;
     86     }
     87     case AssignOp::EQ:
     88       prev = PeekVarInCurrentScope(lhs);
     89       rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
     90       break;
     91     case AssignOp::PLUS_EQ: {
     92       prev = LookupVarInCurrentScope(lhs);
     93       if (!prev->IsDefined()) {
     94         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
     95       } else if (prev->ReadOnly()) {
     96         Error(StringPrintf("*** cannot assign to readonly variable: %s",
     97                            lhs.c_str()));
     98       } else {
     99         prev->AppendVar(this, rhs_v);
    100         rhs = prev;
    101         needs_assign = false;
    102       }
    103       break;
    104     }
    105     case AssignOp::QUESTION_EQ: {
    106       prev = LookupVarInCurrentScope(lhs);
    107       if (!prev->IsDefined()) {
    108         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
    109       } else {
    110         rhs = prev;
    111         needs_assign = false;
    112       }
    113       break;
    114     }
    115   }
    116 
    117   if (prev != NULL) {
    118     prev->Used(this, lhs);
    119     if (prev->Deprecated()) {
    120       if (needs_assign) {
    121         rhs->SetDeprecated(prev->DeprecatedMessage());
    122       }
    123     }
    124   }
    125 
    126   LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
    127   if (needs_assign) {
    128     return rhs;
    129   }
    130   return NULL;
    131 }
    132 
    133 void Evaluator::EvalAssign(const AssignStmt* stmt) {
    134   loc_ = stmt->loc();
    135   last_rule_ = NULL;
    136   Symbol lhs = stmt->GetLhsSymbol(this);
    137   if (lhs.empty())
    138     Error("*** empty variable name.");
    139 
    140   if (lhs == kati_readonly_) {
    141     string rhs;
    142     stmt->rhs->Eval(this, &rhs);
    143     for (auto const& name : WordScanner(rhs)) {
    144       Var* var = Intern(name).GetGlobalVar();
    145       if (!var->IsDefined()) {
    146         Error(
    147             StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
    148       }
    149       var->SetReadOnly();
    150     }
    151     return;
    152   }
    153 
    154   Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
    155                      stmt->directive == AssignDirective::OVERRIDE);
    156   if (rhs) {
    157     bool readonly;
    158     lhs.SetGlobalVar(rhs, stmt->directive == AssignDirective::OVERRIDE,
    159                      &readonly);
    160     if (readonly) {
    161       Error(StringPrintf("*** cannot assign to readonly variable: %s",
    162                          lhs.c_str()));
    163     }
    164   }
    165 }
    166 
    167 void Evaluator::EvalRule(const RuleStmt* stmt) {
    168   loc_ = stmt->loc();
    169   last_rule_ = NULL;
    170 
    171   const string&& expr = stmt->expr->Eval(this);
    172   // See semicolon.mk.
    173   if (expr.find_first_not_of(" \t;") == string::npos) {
    174     if (stmt->term == ';')
    175       Error("*** missing rule before commands.");
    176     return;
    177   }
    178 
    179   Rule* rule;
    180   RuleVarAssignment rule_var;
    181   function<string()> after_term_fn = [this, stmt]() {
    182     return stmt->after_term ? stmt->after_term->Eval(this) : "";
    183   };
    184   ParseRule(loc_, expr, stmt->term, after_term_fn, &rule, &rule_var);
    185 
    186   if (rule) {
    187     if (stmt->term == ';') {
    188       rule->cmds.push_back(stmt->after_term);
    189     }
    190 
    191     for (Symbol o : rule->outputs) {
    192       if (o == posix_sym_)
    193         is_posix_ = true;
    194     }
    195 
    196     LOG("Rule: %s", rule->DebugString().c_str());
    197     rules_.push_back(rule);
    198     last_rule_ = rule;
    199     return;
    200   }
    201 
    202   Symbol lhs = Intern(rule_var.lhs);
    203   for (Symbol output : rule_var.outputs) {
    204     auto p = rule_vars_.emplace(output, nullptr);
    205     if (p.second) {
    206       p.first->second = new Vars;
    207     }
    208 
    209     Value* rhs = stmt->after_term;
    210     if (!rule_var.rhs.empty()) {
    211       Value* lit = NewLiteral(rule_var.rhs);
    212       if (rhs) {
    213         // TODO: We always insert two whitespaces around the
    214         // terminator. Preserve whitespaces properly.
    215         if (stmt->term == ';') {
    216           rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
    217         } else {
    218           rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
    219         }
    220       } else {
    221         rhs = lit;
    222       }
    223     }
    224 
    225     current_scope_ = p.first->second;
    226 
    227     if (lhs == kati_readonly_) {
    228       string rhs_value;
    229       rhs->Eval(this, &rhs_value);
    230       for (auto const& name : WordScanner(rhs_value)) {
    231         Var* var = current_scope_->Lookup(Intern(name));
    232         if (!var->IsDefined()) {
    233           Error(StringPrintf("*** unknown variable: %s",
    234                              name.as_string().c_str()));
    235         }
    236         var->SetReadOnly();
    237       }
    238       current_scope_ = NULL;
    239       continue;
    240     }
    241 
    242     Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
    243     if (rhs_var) {
    244       bool readonly;
    245       current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op), &readonly);
    246       if (readonly) {
    247         Error(StringPrintf("*** cannot assign to readonly variable: %s",
    248                            lhs.c_str()));
    249       }
    250     }
    251     current_scope_ = NULL;
    252   }
    253 }
    254 
    255 void Evaluator::EvalCommand(const CommandStmt* stmt) {
    256   loc_ = stmt->loc();
    257 
    258   if (!last_rule_) {
    259     vector<Stmt*> stmts;
    260     ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
    261     for (Stmt* a : stmts)
    262       a->Eval(this);
    263     return;
    264   }
    265 
    266   last_rule_->cmds.push_back(stmt->expr);
    267   if (last_rule_->cmd_lineno == 0)
    268     last_rule_->cmd_lineno = stmt->loc().lineno;
    269   LOG("Command: %s", stmt->expr->DebugString().c_str());
    270 }
    271 
    272 void Evaluator::EvalIf(const IfStmt* stmt) {
    273   loc_ = stmt->loc();
    274 
    275   bool is_true;
    276   switch (stmt->op) {
    277     case CondOp::IFDEF:
    278     case CondOp::IFNDEF: {
    279       string var_name;
    280       stmt->lhs->Eval(this, &var_name);
    281       Symbol lhs = Intern(TrimRightSpace(var_name));
    282       if (lhs.str().find_first_of(" \t") != string::npos)
    283         Error("*** invalid syntax in conditional.");
    284       Var* v = LookupVarInCurrentScope(lhs);
    285       is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF));
    286       break;
    287     }
    288     case CondOp::IFEQ:
    289     case CondOp::IFNEQ: {
    290       const string&& lhs = stmt->lhs->Eval(this);
    291       const string&& rhs = stmt->rhs->Eval(this);
    292       is_true = ((lhs == rhs) == (stmt->op == CondOp::IFEQ));
    293       break;
    294     }
    295     default:
    296       CHECK(false);
    297       abort();
    298   }
    299 
    300   const vector<Stmt*>* stmts;
    301   if (is_true) {
    302     stmts = &stmt->true_stmts;
    303   } else {
    304     stmts = &stmt->false_stmts;
    305   }
    306   for (Stmt* a : *stmts) {
    307     LOG("%s", a->DebugString().c_str());
    308     a->Eval(this);
    309   }
    310 }
    311 
    312 void Evaluator::DoInclude(const string& fname) {
    313   CheckStack();
    314 
    315   Makefile* mk = MakefileCacheManager::Get()->ReadMakefile(fname);
    316   if (!mk->Exists()) {
    317     Error(StringPrintf("%s does not exist", fname.c_str()));
    318   }
    319 
    320   Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
    321   var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
    322   for (Stmt* stmt : mk->stmts()) {
    323     LOG("%s", stmt->DebugString().c_str());
    324     stmt->Eval(this);
    325   }
    326 }
    327 
    328 void Evaluator::EvalInclude(const IncludeStmt* stmt) {
    329   loc_ = stmt->loc();
    330   last_rule_ = NULL;
    331 
    332   const string&& pats = stmt->expr->Eval(this);
    333   for (StringPiece pat : WordScanner(pats)) {
    334     ScopedTerminator st(pat);
    335     vector<string>* files;
    336     Glob(pat.data(), &files);
    337 
    338     if (stmt->should_exist) {
    339       if (files->empty()) {
    340         // TODO: Kati does not support building a missing include file.
    341         Error(StringPrintf("%s: %s", pat.data(), strerror(errno)));
    342       }
    343     }
    344 
    345     for (const string& fname : *files) {
    346       if (!stmt->should_exist && g_flags.ignore_optional_include_pattern &&
    347           Pattern(g_flags.ignore_optional_include_pattern).Match(fname)) {
    348         continue;
    349       }
    350       DoInclude(fname);
    351     }
    352   }
    353 }
    354 
    355 void Evaluator::EvalExport(const ExportStmt* stmt) {
    356   loc_ = stmt->loc();
    357   last_rule_ = NULL;
    358 
    359   const string&& exports = stmt->expr->Eval(this);
    360   for (StringPiece tok : WordScanner(exports)) {
    361     size_t equal_index = tok.find('=');
    362     StringPiece lhs;
    363     if (equal_index == string::npos) {
    364       lhs = tok;
    365     } else if (equal_index == 0 ||
    366                (equal_index == 1 &&
    367                 (tok[0] == ':' || tok[0] == '?' || tok[0] == '+'))) {
    368       // Do not export tokens after an assignment.
    369       break;
    370     } else {
    371       StringPiece rhs;
    372       AssignOp op;
    373       ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
    374     }
    375     Symbol sym = Intern(lhs);
    376     exports_[sym] = stmt->is_export;
    377 
    378     if (export_message_) {
    379       const char* prefix = "";
    380       if (!stmt->is_export) {
    381         prefix = "un";
    382       }
    383 
    384       if (export_error_) {
    385         Error(StringPrintf("*** %s: %sexport is obsolete%s.", sym.c_str(),
    386                            prefix, export_message_->c_str()));
    387       } else {
    388         WARN_LOC(loc(), "%s: %sexport has been deprecated%s.", sym.c_str(),
    389                  prefix, export_message_->c_str());
    390       }
    391     }
    392   }
    393 }
    394 
    395 Var* Evaluator::LookupVarGlobal(Symbol name) {
    396   Var* v = name.GetGlobalVar();
    397   if (v->IsDefined())
    398     return v;
    399   used_undefined_vars_.insert(name);
    400   return v;
    401 }
    402 
    403 Var* Evaluator::LookupVar(Symbol name) {
    404   if (current_scope_) {
    405     Var* v = current_scope_->Lookup(name);
    406     if (v->IsDefined())
    407       return v;
    408   }
    409   return LookupVarGlobal(name);
    410 }
    411 
    412 Var* Evaluator::PeekVar(Symbol name) {
    413   if (current_scope_) {
    414     Var* v = current_scope_->Peek(name);
    415     if (v->IsDefined())
    416       return v;
    417   }
    418   return name.PeekGlobalVar();
    419 }
    420 
    421 Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
    422   if (current_scope_) {
    423     return current_scope_->Lookup(name);
    424   }
    425   return LookupVarGlobal(name);
    426 }
    427 
    428 Var* Evaluator::PeekVarInCurrentScope(Symbol name) {
    429   if (current_scope_) {
    430     return current_scope_->Peek(name);
    431   }
    432   return name.PeekGlobalVar();
    433 }
    434 
    435 string Evaluator::EvalVar(Symbol name) {
    436   return LookupVar(name)->Eval(this);
    437 }
    438 
    439 string Evaluator::GetShell() {
    440   return EvalVar(kShellSym);
    441 }
    442 
    443 string Evaluator::GetShellFlag() {
    444   // TODO: Handle $(.SHELLFLAGS)
    445   return is_posix_ ? "-ec" : "-c";
    446 }
    447 
    448 string Evaluator::GetShellAndFlag() {
    449   string shell = GetShell();
    450   shell += ' ';
    451   shell += GetShellFlag();
    452   return shell;
    453 }
    454 
    455 void Evaluator::Error(const string& msg) {
    456   ERROR_LOC(loc_, "%s", msg.c_str());
    457 }
    458 
    459 void Evaluator::DumpStackStats() const {
    460   LOG_STAT("Max stack use: %zd bytes at %s:%d",
    461            ((char*)stack_addr_ - (char*)lowest_stack_) + stack_size_,
    462            LOCF(lowest_loc_));
    463 }
    464 
    465 unordered_set<Symbol> Evaluator::used_undefined_vars_;
    466