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 <string.h>
     21 
     22 #include "expr.h"
     23 #include "file.h"
     24 #include "file_cache.h"
     25 #include "fileutil.h"
     26 #include "parser.h"
     27 #include "rule.h"
     28 #include "stmt.h"
     29 #include "strutil.h"
     30 #include "symtab.h"
     31 #include "var.h"
     32 
     33 Evaluator::Evaluator()
     34     : last_rule_(NULL),
     35       current_scope_(NULL),
     36       avoid_io_(false),
     37       eval_depth_(0),
     38       posix_sym_(Intern(".POSIX")),
     39       is_posix_(false),
     40       kati_readonly_(Intern(".KATI_READONLY")) {
     41 }
     42 
     43 Evaluator::~Evaluator() {
     44   // delete vars_;
     45   // for (auto p : rule_vars) {
     46   //   delete p.second;
     47   // }
     48 }
     49 
     50 Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs,
     51                         AssignOp op, bool is_override) {
     52   VarOrigin origin = (
     53       (is_bootstrap_ ? VarOrigin::DEFAULT :
     54        is_commandline_ ? VarOrigin::COMMAND_LINE :
     55        is_override ? VarOrigin::OVERRIDE : VarOrigin::FILE));
     56 
     57   Var* rhs = NULL;
     58   bool needs_assign = true;
     59   switch (op) {
     60     case AssignOp::COLON_EQ: {
     61       SimpleVar* sv = new SimpleVar(origin);
     62       rhs_v->Eval(this, sv->mutable_value());
     63       rhs = sv;
     64       break;
     65     }
     66     case AssignOp::EQ:
     67       rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
     68       break;
     69     case AssignOp::PLUS_EQ: {
     70       Var* prev = LookupVarInCurrentScope(lhs);
     71       if (!prev->IsDefined()) {
     72         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
     73       } else if (prev->ReadOnly()) {
     74         Error(StringPrintf("*** cannot assign to readonly variable: %s", lhs.c_str()));
     75       } else {
     76         prev->AppendVar(this, rhs_v);
     77         rhs = prev;
     78         needs_assign = false;
     79       }
     80       break;
     81     }
     82     case AssignOp::QUESTION_EQ: {
     83       Var* prev = LookupVarInCurrentScope(lhs);
     84       if (!prev->IsDefined()) {
     85         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
     86       } else {
     87         rhs = prev;
     88         needs_assign = false;
     89       }
     90       break;
     91     }
     92   }
     93 
     94   LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
     95   if (needs_assign) {
     96     return rhs;
     97   }
     98   return NULL;
     99 }
    100 
    101 void Evaluator::EvalAssign(const AssignStmt* stmt) {
    102   loc_ = stmt->loc();
    103   last_rule_ = NULL;
    104   Symbol lhs = stmt->GetLhsSymbol(this);
    105   if (lhs.empty())
    106     Error("*** empty variable name.");
    107 
    108   if (lhs == kati_readonly_) {
    109     string rhs;
    110     stmt->rhs->Eval(this, &rhs);
    111     for (auto const& name : WordScanner(rhs)) {
    112       Var* var = Intern(name).GetGlobalVar();
    113       if (!var->IsDefined()) {
    114         Error(StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
    115       }
    116       var->SetReadOnly();
    117     }
    118     return;
    119   }
    120 
    121   Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
    122                      stmt->directive == AssignDirective::OVERRIDE);
    123   if (rhs) {
    124     bool readonly;
    125     lhs.SetGlobalVar(rhs,
    126                      stmt->directive == AssignDirective::OVERRIDE,
    127                      &readonly);
    128     if (readonly) {
    129       Error(StringPrintf("*** cannot assign to readonly variable: %s", lhs.c_str()));
    130     }
    131   }
    132 }
    133 
    134 void Evaluator::EvalRule(const RuleStmt* stmt) {
    135   loc_ = stmt->loc();
    136   last_rule_ = NULL;
    137 
    138   const string&& expr = stmt->expr->Eval(this);
    139   // See semicolon.mk.
    140   if (expr.find_first_not_of(" \t;") == string::npos) {
    141     if (stmt->term == ';')
    142       Error("*** missing rule before commands.");
    143     return;
    144   }
    145 
    146   Rule* rule;
    147   RuleVarAssignment rule_var;
    148   function<string()> after_term_fn = [this, stmt](){
    149     return stmt->after_term ? stmt->after_term->Eval(this) : "";
    150   };
    151   ParseRule(loc_, expr, stmt->term, after_term_fn, &rule, &rule_var);
    152 
    153   if (rule) {
    154     if (stmt->term == ';') {
    155       rule->cmds.push_back(stmt->after_term);
    156     }
    157 
    158     for (Symbol o : rule->outputs) {
    159       if (o == posix_sym_)
    160         is_posix_ = true;
    161     }
    162 
    163     LOG("Rule: %s", rule->DebugString().c_str());
    164     rules_.push_back(rule);
    165     last_rule_ = rule;
    166     return;
    167   }
    168 
    169   Symbol lhs = Intern(rule_var.lhs);
    170   for (Symbol output : rule_var.outputs) {
    171     auto p = rule_vars_.emplace(output, nullptr);
    172     if (p.second) {
    173       p.first->second = new Vars;
    174     }
    175 
    176     Value* rhs = stmt->after_term;
    177     if (!rule_var.rhs.empty()) {
    178       Value* lit = NewLiteral(rule_var.rhs);
    179       if (rhs) {
    180         // TODO: We always insert two whitespaces around the
    181         // terminator. Preserve whitespaces properly.
    182         if (stmt->term == ';') {
    183           rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
    184         } else {
    185           rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
    186         }
    187       } else {
    188         rhs = lit;
    189       }
    190     }
    191 
    192     current_scope_ = p.first->second;
    193 
    194     if (lhs == kati_readonly_) {
    195       string rhs_value;
    196       rhs->Eval(this, &rhs_value);
    197       for (auto const& name : WordScanner(rhs_value)) {
    198         Var* var = current_scope_->Lookup(Intern(name));
    199         if (!var->IsDefined()) {
    200           Error(StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
    201         }
    202         var->SetReadOnly();
    203       }
    204       current_scope_ = NULL;
    205       continue;
    206     }
    207 
    208     Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
    209     if (rhs_var) {
    210       bool readonly;
    211       current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op), &readonly);
    212       if (readonly) {
    213         Error(StringPrintf("*** cannot assign to readonly variable: %s", lhs.c_str()));
    214       }
    215     }
    216     current_scope_ = NULL;
    217   }
    218 }
    219 
    220 void Evaluator::EvalCommand(const CommandStmt* stmt) {
    221   loc_ = stmt->loc();
    222 
    223   if (!last_rule_) {
    224     vector<Stmt*> stmts;
    225     ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
    226     for (Stmt* a : stmts)
    227       a->Eval(this);
    228     return;
    229   }
    230 
    231   last_rule_->cmds.push_back(stmt->expr);
    232   if (last_rule_->cmd_lineno == 0)
    233     last_rule_->cmd_lineno = stmt->loc().lineno;
    234   LOG("Command: %s", stmt->expr->DebugString().c_str());
    235 }
    236 
    237 void Evaluator::EvalIf(const IfStmt* stmt) {
    238   loc_ = stmt->loc();
    239 
    240   bool is_true;
    241   switch (stmt->op) {
    242     case CondOp::IFDEF:
    243     case CondOp::IFNDEF: {
    244       string var_name;
    245       stmt->lhs->Eval(this, &var_name);
    246       Symbol lhs = Intern(TrimRightSpace(var_name));
    247       if (lhs.str().find_first_of(" \t") != string::npos)
    248         Error("*** invalid syntax in conditional.");
    249       Var* v = LookupVarInCurrentScope(lhs);
    250       is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF));
    251       break;
    252     }
    253     case CondOp::IFEQ:
    254     case CondOp::IFNEQ: {
    255       const string&& lhs = stmt->lhs->Eval(this);
    256       const string&& rhs = stmt->rhs->Eval(this);
    257       is_true = ((lhs == rhs) == (stmt->op == CondOp::IFEQ));
    258       break;
    259     }
    260     default:
    261       CHECK(false);
    262       abort();
    263   }
    264 
    265   const vector<Stmt*>* stmts;
    266   if (is_true) {
    267     stmts = &stmt->true_stmts;
    268   } else {
    269     stmts = &stmt->false_stmts;
    270   }
    271   for (Stmt* a : *stmts) {
    272     LOG("%s", a->DebugString().c_str());
    273     a->Eval(this);
    274   }
    275 }
    276 
    277 void Evaluator::DoInclude(const string& fname) {
    278   Makefile* mk = MakefileCacheManager::Get()->ReadMakefile(fname);
    279   if (!mk->Exists()) {
    280     Error(StringPrintf("%s does not exist", fname.c_str()));
    281   }
    282 
    283   Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
    284   var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
    285   for (Stmt* stmt : mk->stmts()) {
    286     LOG("%s", stmt->DebugString().c_str());
    287     stmt->Eval(this);
    288   }
    289 }
    290 
    291 void Evaluator::EvalInclude(const IncludeStmt* stmt) {
    292   loc_ = stmt->loc();
    293   last_rule_ = NULL;
    294 
    295   const string&& pats = stmt->expr->Eval(this);
    296   for (StringPiece pat : WordScanner(pats)) {
    297     ScopedTerminator st(pat);
    298     vector<string>* files;
    299     Glob(pat.data(), &files);
    300 
    301     if (stmt->should_exist) {
    302       if (files->empty()) {
    303         // TODO: Kati does not support building a missing include file.
    304         Error(StringPrintf("%s: %s", pat.data(), strerror(errno)));
    305       }
    306     }
    307 
    308     for (const string& fname : *files) {
    309       if (!stmt->should_exist && g_flags.ignore_optional_include_pattern &&
    310           Pattern(g_flags.ignore_optional_include_pattern).Match(fname)) {
    311         continue;
    312       }
    313       DoInclude(fname);
    314     }
    315   }
    316 }
    317 
    318 void Evaluator::EvalExport(const ExportStmt* stmt) {
    319   loc_ = stmt->loc();
    320   last_rule_ = NULL;
    321 
    322   const string&& exports = stmt->expr->Eval(this);
    323   for (StringPiece tok : WordScanner(exports)) {
    324     size_t equal_index = tok.find('=');
    325     if (equal_index == string::npos) {
    326       exports_[Intern(tok)] = stmt->is_export;
    327     } else if (equal_index == 0 ||
    328                (equal_index == 1 &&
    329                 (tok[0] == ':' || tok[0] == '?' || tok[0] == '+'))) {
    330       // Do not export tokens after an assignment.
    331       break;
    332     } else {
    333       StringPiece lhs, rhs;
    334       AssignOp op;
    335       ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
    336       exports_[Intern(lhs)] = stmt->is_export;
    337     }
    338   }
    339 }
    340 
    341 Var* Evaluator::LookupVarGlobal(Symbol name) {
    342   Var* v = name.GetGlobalVar();
    343   if (v->IsDefined())
    344     return v;
    345   used_undefined_vars_.insert(name);
    346   return v;
    347 }
    348 
    349 Var* Evaluator::LookupVar(Symbol name) {
    350   if (current_scope_) {
    351     Var* v = current_scope_->Lookup(name);
    352     if (v->IsDefined())
    353       return v;
    354   }
    355   return LookupVarGlobal(name);
    356 }
    357 
    358 Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
    359   if (current_scope_) {
    360     return current_scope_->Lookup(name);
    361   }
    362   return LookupVarGlobal(name);
    363 }
    364 
    365 string Evaluator::EvalVar(Symbol name) {
    366   return LookupVar(name)->Eval(this);
    367 }
    368 
    369 string Evaluator::GetShell() {
    370   return EvalVar(kShellSym);
    371 }
    372 
    373 string Evaluator::GetShellFlag() {
    374   // TODO: Handle $(.SHELLFLAGS)
    375   return is_posix_ ? "-ec" : "-c";
    376 }
    377 
    378 string Evaluator::GetShellAndFlag() {
    379   string shell = GetShell();
    380   shell += ' ';
    381   shell += GetShellFlag();
    382   return shell;
    383 }
    384 
    385 void Evaluator::Error(const string& msg) {
    386   ERROR_LOC(loc_, "%s", msg.c_str());
    387 }
    388 
    389 unordered_set<Symbol> Evaluator::used_undefined_vars_;
    390