普通文本  |  466行  |  12.13 KB

// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

#include "eval.h"

#include <errno.h>
#include <pthread.h>
#include <string.h>

#include "expr.h"
#include "file.h"
#include "file_cache.h"
#include "fileutil.h"
#include "parser.h"
#include "rule.h"
#include "stmt.h"
#include "strutil.h"
#include "symtab.h"
#include "var.h"

Evaluator::Evaluator()
    : last_rule_(NULL),
      current_scope_(NULL),
      avoid_io_(false),
      eval_depth_(0),
      posix_sym_(Intern(".POSIX")),
      is_posix_(false),
      export_error_(false),
      kati_readonly_(Intern(".KATI_READONLY")) {
#if defined(__APPLE__)
  stack_size_ = pthread_get_stacksize_np(pthread_self());
  stack_addr_ = (char*)pthread_get_stackaddr_np(pthread_self()) - stack_size_;
#else
  pthread_attr_t attr;
  CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
  CHECK(pthread_attr_getstack(&attr, &stack_addr_, &stack_size_) == 0);
  CHECK(pthread_attr_destroy(&attr) == 0);
#endif

  lowest_stack_ = (char*)stack_addr_ + stack_size_;
  LOG_STAT("Stack size: %zd bytes", stack_size_);
}

Evaluator::~Evaluator() {
  // delete vars_;
  // for (auto p : rule_vars) {
  //   delete p.second;
  // }
}

Var* Evaluator::EvalRHS(Symbol lhs,
                        Value* rhs_v,
                        StringPiece orig_rhs,
                        AssignOp op,
                        bool is_override) {
  VarOrigin origin =
      ((is_bootstrap_ ? VarOrigin::DEFAULT
                      : is_commandline_ ? VarOrigin::COMMAND_LINE
                                        : is_override ? VarOrigin::OVERRIDE
                                                      : VarOrigin::FILE));

  Var* rhs = NULL;
  Var* prev = NULL;
  bool needs_assign = true;

  switch (op) {
    case AssignOp::COLON_EQ: {
      prev = PeekVarInCurrentScope(lhs);
      SimpleVar* sv = new SimpleVar(origin);
      rhs_v->Eval(this, sv->mutable_value());
      rhs = sv;
      break;
    }
    case AssignOp::EQ:
      prev = PeekVarInCurrentScope(lhs);
      rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      break;
    case AssignOp::PLUS_EQ: {
      prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else if (prev->ReadOnly()) {
        Error(StringPrintf("*** cannot assign to readonly variable: %s",
                           lhs.c_str()));
      } else {
        prev->AppendVar(this, rhs_v);
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
    case AssignOp::QUESTION_EQ: {
      prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else {
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
  }

  if (prev != NULL) {
    prev->Used(this, lhs);
    if (prev->Deprecated()) {
      if (needs_assign) {
        rhs->SetDeprecated(prev->DeprecatedMessage());
      }
    }
  }

  LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
  if (needs_assign) {
    return rhs;
  }
  return NULL;
}

void Evaluator::EvalAssign(const AssignStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;
  Symbol lhs = stmt->GetLhsSymbol(this);
  if (lhs.empty())
    Error("*** empty variable name.");

  if (lhs == kati_readonly_) {
    string rhs;
    stmt->rhs->Eval(this, &rhs);
    for (auto const& name : WordScanner(rhs)) {
      Var* var = Intern(name).GetGlobalVar();
      if (!var->IsDefined()) {
        Error(
            StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
      }
      var->SetReadOnly();
    }
    return;
  }

  Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
                     stmt->directive == AssignDirective::OVERRIDE);
  if (rhs) {
    bool readonly;
    lhs.SetGlobalVar(rhs, stmt->directive == AssignDirective::OVERRIDE,
                     &readonly);
    if (readonly) {
      Error(StringPrintf("*** cannot assign to readonly variable: %s",
                         lhs.c_str()));
    }
  }
}

void Evaluator::EvalRule(const RuleStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;

  const string&& expr = stmt->expr->Eval(this);
  // See semicolon.mk.
  if (expr.find_first_not_of(" \t;") == string::npos) {
    if (stmt->term == ';')
      Error("*** missing rule before commands.");
    return;
  }

  Rule* rule;
  RuleVarAssignment rule_var;
  function<string()> after_term_fn = [this, stmt]() {
    return stmt->after_term ? stmt->after_term->Eval(this) : "";
  };
  ParseRule(loc_, expr, stmt->term, after_term_fn, &rule, &rule_var);

  if (rule) {
    if (stmt->term == ';') {
      rule->cmds.push_back(stmt->after_term);
    }

    for (Symbol o : rule->outputs) {
      if (o == posix_sym_)
        is_posix_ = true;
    }

    LOG("Rule: %s", rule->DebugString().c_str());
    rules_.push_back(rule);
    last_rule_ = rule;
    return;
  }

  Symbol lhs = Intern(rule_var.lhs);
  for (Symbol output : rule_var.outputs) {
    auto p = rule_vars_.emplace(output, nullptr);
    if (p.second) {
      p.first->second = new Vars;
    }

    Value* rhs = stmt->after_term;
    if (!rule_var.rhs.empty()) {
      Value* lit = NewLiteral(rule_var.rhs);
      if (rhs) {
        // TODO: We always insert two whitespaces around the
        // terminator. Preserve whitespaces properly.
        if (stmt->term == ';') {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
        } else {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
        }
      } else {
        rhs = lit;
      }
    }

    current_scope_ = p.first->second;

    if (lhs == kati_readonly_) {
      string rhs_value;
      rhs->Eval(this, &rhs_value);
      for (auto const& name : WordScanner(rhs_value)) {
        Var* var = current_scope_->Lookup(Intern(name));
        if (!var->IsDefined()) {
          Error(StringPrintf("*** unknown variable: %s",
                             name.as_string().c_str()));
        }
        var->SetReadOnly();
      }
      current_scope_ = NULL;
      continue;
    }

    Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
    if (rhs_var) {
      bool readonly;
      current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op), &readonly);
      if (readonly) {
        Error(StringPrintf("*** cannot assign to readonly variable: %s",
                           lhs.c_str()));
      }
    }
    current_scope_ = NULL;
  }
}

void Evaluator::EvalCommand(const CommandStmt* stmt) {
  loc_ = stmt->loc();

  if (!last_rule_) {
    vector<Stmt*> stmts;
    ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
    for (Stmt* a : stmts)
      a->Eval(this);
    return;
  }

  last_rule_->cmds.push_back(stmt->expr);
  if (last_rule_->cmd_lineno == 0)
    last_rule_->cmd_lineno = stmt->loc().lineno;
  LOG("Command: %s", stmt->expr->DebugString().c_str());
}

void Evaluator::EvalIf(const IfStmt* stmt) {
  loc_ = stmt->loc();

  bool is_true;
  switch (stmt->op) {
    case CondOp::IFDEF:
    case CondOp::IFNDEF: {
      string var_name;
      stmt->lhs->Eval(this, &var_name);
      Symbol lhs = Intern(TrimRightSpace(var_name));
      if (lhs.str().find_first_of(" \t") != string::npos)
        Error("*** invalid syntax in conditional.");
      Var* v = LookupVarInCurrentScope(lhs);
      is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF));
      break;
    }
    case CondOp::IFEQ:
    case CondOp::IFNEQ: {
      const string&& lhs = stmt->lhs->Eval(this);
      const string&& rhs = stmt->rhs->Eval(this);
      is_true = ((lhs == rhs) == (stmt->op == CondOp::IFEQ));
      break;
    }
    default:
      CHECK(false);
      abort();
  }

  const vector<Stmt*>* stmts;
  if (is_true) {
    stmts = &stmt->true_stmts;
  } else {
    stmts = &stmt->false_stmts;
  }
  for (Stmt* a : *stmts) {
    LOG("%s", a->DebugString().c_str());
    a->Eval(this);
  }
}

void Evaluator::DoInclude(const string& fname) {
  CheckStack();

  Makefile* mk = MakefileCacheManager::Get()->ReadMakefile(fname);
  if (!mk->Exists()) {
    Error(StringPrintf("%s does not exist", fname.c_str()));
  }

  Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
  var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
  for (Stmt* stmt : mk->stmts()) {
    LOG("%s", stmt->DebugString().c_str());
    stmt->Eval(this);
  }
}

void Evaluator::EvalInclude(const IncludeStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;

  const string&& pats = stmt->expr->Eval(this);
  for (StringPiece pat : WordScanner(pats)) {
    ScopedTerminator st(pat);
    vector<string>* files;
    Glob(pat.data(), &files);

    if (stmt->should_exist) {
      if (files->empty()) {
        // TODO: Kati does not support building a missing include file.
        Error(StringPrintf("%s: %s", pat.data(), strerror(errno)));
      }
    }

    for (const string& fname : *files) {
      if (!stmt->should_exist && g_flags.ignore_optional_include_pattern &&
          Pattern(g_flags.ignore_optional_include_pattern).Match(fname)) {
        continue;
      }
      DoInclude(fname);
    }
  }
}

void Evaluator::EvalExport(const ExportStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;

  const string&& exports = stmt->expr->Eval(this);
  for (StringPiece tok : WordScanner(exports)) {
    size_t equal_index = tok.find('=');
    StringPiece lhs;
    if (equal_index == string::npos) {
      lhs = tok;
    } else if (equal_index == 0 ||
               (equal_index == 1 &&
                (tok[0] == ':' || tok[0] == '?' || tok[0] == '+'))) {
      // Do not export tokens after an assignment.
      break;
    } else {
      StringPiece rhs;
      AssignOp op;
      ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
    }
    Symbol sym = Intern(lhs);
    exports_[sym] = stmt->is_export;

    if (export_message_) {
      const char* prefix = "";
      if (!stmt->is_export) {
        prefix = "un";
      }

      if (export_error_) {
        Error(StringPrintf("*** %s: %sexport is obsolete%s.", sym.c_str(),
                           prefix, export_message_->c_str()));
      } else {
        WARN_LOC(loc(), "%s: %sexport has been deprecated%s.", sym.c_str(),
                 prefix, export_message_->c_str());
      }
    }
  }
}

Var* Evaluator::LookupVarGlobal(Symbol name) {
  Var* v = name.GetGlobalVar();
  if (v->IsDefined())
    return v;
  used_undefined_vars_.insert(name);
  return v;
}

Var* Evaluator::LookupVar(Symbol name) {
  if (current_scope_) {
    Var* v = current_scope_->Lookup(name);
    if (v->IsDefined())
      return v;
  }
  return LookupVarGlobal(name);
}

Var* Evaluator::PeekVar(Symbol name) {
  if (current_scope_) {
    Var* v = current_scope_->Peek(name);
    if (v->IsDefined())
      return v;
  }
  return name.PeekGlobalVar();
}

Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
  if (current_scope_) {
    return current_scope_->Lookup(name);
  }
  return LookupVarGlobal(name);
}

Var* Evaluator::PeekVarInCurrentScope(Symbol name) {
  if (current_scope_) {
    return current_scope_->Peek(name);
  }
  return name.PeekGlobalVar();
}

string Evaluator::EvalVar(Symbol name) {
  return LookupVar(name)->Eval(this);
}

string Evaluator::GetShell() {
  return EvalVar(kShellSym);
}

string Evaluator::GetShellFlag() {
  // TODO: Handle $(.SHELLFLAGS)
  return is_posix_ ? "-ec" : "-c";
}

string Evaluator::GetShellAndFlag() {
  string shell = GetShell();
  shell += ' ';
  shell += GetShellFlag();
  return shell;
}

void Evaluator::Error(const string& msg) {
  ERROR_LOC(loc_, "%s", msg.c_str());
}

void Evaluator::DumpStackStats() const {
  LOG_STAT("Max stack use: %zd bytes at %s:%d",
           ((char*)stack_addr_ - (char*)lowest_stack_) + stack_size_,
           LOCF(lowest_loc_));
}

unordered_set<Symbol> Evaluator::used_undefined_vars_;