diff options
-rw-r--r-- | eval.cc | 53 | ||||
-rw-r--r-- | eval.h | 2 | ||||
-rw-r--r-- | symtab.cc | 11 | ||||
-rw-r--r-- | symtab.h | 2 | ||||
-rw-r--r-- | testcase/readonly_global.sh | 47 | ||||
-rw-r--r-- | testcase/readonly_global_missing.sh | 32 | ||||
-rw-r--r-- | testcase/readonly_rule.sh | 48 | ||||
-rw-r--r-- | testcase/readonly_rule_missing.sh | 32 | ||||
-rw-r--r-- | var.cc | 9 | ||||
-rw-r--r-- | var.h | 8 |
10 files changed, 234 insertions, 10 deletions
@@ -36,7 +36,8 @@ Evaluator::Evaluator() avoid_io_(false), eval_depth_(0), posix_sym_(Intern(".POSIX")), - is_posix_(false) { + is_posix_(false), + kati_readonly_(Intern(".KATI_READONLY")) { } Evaluator::~Evaluator() { @@ -69,6 +70,8 @@ Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs, Var* 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; @@ -101,11 +104,31 @@ void Evaluator::EvalAssign(const AssignStmt* stmt) { 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) + if (rhs) { + bool readonly; lhs.SetGlobalVar(rhs, - stmt->directive == AssignDirective::OVERRIDE); + stmt->directive == AssignDirective::OVERRIDE, + &readonly); + if (readonly) { + Error(StringPrintf("*** cannot assign to readonly variable: %s", lhs.c_str())); + } + } } void Evaluator::EvalRule(const RuleStmt* stmt) { @@ -167,9 +190,29 @@ void Evaluator::EvalRule(const RuleStmt* stmt) { } 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) - current_scope_->Assign(lhs, new RuleVar(rhs_var, 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; } } @@ -125,6 +125,8 @@ class Evaluator { bool is_posix_; static unordered_set<Symbol> used_undefined_vars_; + + Symbol kati_readonly_; }; #endif // EVAL_H_ @@ -59,11 +59,20 @@ Var* Symbol::GetGlobalVar() const { return v; } -void Symbol::SetGlobalVar(Var* v, bool is_override) const { +void Symbol::SetGlobalVar(Var* v, bool is_override, bool* readonly) const { if (static_cast<size_t>(v_) >= g_symbol_data.size()) { g_symbol_data.resize(v_ + 1); } Var* orig = g_symbol_data[v_].gv; + if (orig->ReadOnly()) { + if (readonly != nullptr) + *readonly = true; + else + ERROR("*** cannot assign to readonly variable: %s", c_str()); + return; + } else if (readonly != nullptr) { + *readonly = false; + } if (!is_override && (orig->Origin() == VarOrigin::OVERRIDE || orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE)) { @@ -56,7 +56,7 @@ class Symbol { bool IsValid() const { return v_ >= 0; } Var* GetGlobalVar() const; - void SetGlobalVar(Var* v, bool is_override = false) const; + void SetGlobalVar(Var* v, bool is_override = false, bool* readonly = nullptr) const; private: explicit Symbol(int v); diff --git a/testcase/readonly_global.sh b/testcase/readonly_global.sh new file mode 100644 index 0000000..33695f2 --- /dev/null +++ b/testcase/readonly_global.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Copyright 2016 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. + +set -u + +mk="$@" + +function build() { + cat <<EOF > Makefile +FOO $1 bar +.KATI_READONLY $2 FOO +FOO $3 baz +all: +EOF + + echo "Testcase: $1 $2 $3" + if echo "${mk}" | grep -q "^make"; then + # Make doesn't support .KATI_READONLY + echo "Makefile:3: *** cannot assign to readonly variable: FOO" + else + ${mk} 2>&1 && echo "Clean exit" + fi +} + +build "=" "=" "=" +build "=" "+=" "=" +build "=" ":=" "=" + +build "=" ":=" ":=" +build "=" ":=" "+=" + +build ":=" ":=" ":=" +build ":=" ":=" "+=" +build ":=" ":=" "=" diff --git a/testcase/readonly_global_missing.sh b/testcase/readonly_global_missing.sh new file mode 100644 index 0000000..cab86f2 --- /dev/null +++ b/testcase/readonly_global_missing.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright 2016 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. + +set -u + +mk="$@" + +cat <<EOF > Makefile +all: FOO = bar +.KATI_READONLY = FOO +all: +EOF + +if echo "${mk}" | grep -q "^make"; then + # Make doesn't support .KATI_READONLY + echo "Makefile:2: *** unknown variable: FOO" +else + ${mk} 2>&1 && echo "Clean exit" +fi diff --git a/testcase/readonly_rule.sh b/testcase/readonly_rule.sh new file mode 100644 index 0000000..676d639 --- /dev/null +++ b/testcase/readonly_rule.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Copyright 2016 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. + +set -u + +mk="$@" + +function build() { + cat <<EOF > Makefile +all: FOO $1 bar +all: .KATI_READONLY $2 FOO +FOO $3 foo +all: FOO $3 baz +all: +EOF + + echo "Testcase: $1 $2 $3" + if echo "${mk}" | grep -q "^make"; then + # Make doesn't support .KATI_READONLY + echo "Makefile:4: *** cannot assign to readonly variable: FOO" + else + ${mk} 2>&1 && echo "Clean exit" + fi +} + +#build "=" "=" "=" +#build "=" "+=" "=" +#build "=" ":=" "=" +# +#build "=" ":=" ":=" +#build "=" ":=" "+=" +# +#build ":=" ":=" ":=" +build ":=" ":=" "+=" +#build ":=" ":=" "=" diff --git a/testcase/readonly_rule_missing.sh b/testcase/readonly_rule_missing.sh new file mode 100644 index 0000000..6940438 --- /dev/null +++ b/testcase/readonly_rule_missing.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright 2016 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. + +set -u + +mk="$@" + +cat <<EOF > Makefile +FOO = bar +all: .KATI_READONLY = FOO +all: +EOF + +if echo "${mk}" | grep -q "^make"; then + # Make doesn't support .KATI_READONLY + echo "Makefile:2: *** unknown variable: FOO" +else + ${mk} 2>&1 && echo "Clean exit" +fi @@ -37,7 +37,7 @@ const char* GetOriginStr(VarOrigin origin) { return "*** broken origin ***"; } -Var::Var() { +Var::Var() : readonly_(false) { } Var::~Var() { @@ -130,10 +130,15 @@ Var* Vars::Lookup(Symbol name) const { return v; } -void Vars::Assign(Symbol name, Var* v) { +void Vars::Assign(Symbol name, Var* v, bool* readonly) { + *readonly = false; auto p = emplace(name, v); if (!p.second) { Var* orig = p.first->second; + if (orig->ReadOnly()) { + *readonly = true; + return; + } if (orig->Origin() == VarOrigin::OVERRIDE || orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE) { return; @@ -56,8 +56,14 @@ class Var : public Evaluable { virtual string DebugString() const = 0; + bool ReadOnly() const { return readonly_; } + void SetReadOnly() { readonly_ = true; } + protected: Var(); + + private: + bool readonly_; }; class SimpleVar : public Var { @@ -177,7 +183,7 @@ class Vars : public unordered_map<Symbol, Var*> { Var* Lookup(Symbol name) const; - void Assign(Symbol name, Var* v); + void Assign(Symbol name, Var* v, bool* readonly); static void add_used_env_vars(Symbol v); |