diff options
author | Dan Willemsen <dwillemsen@google.com> | 2017-10-03 14:24:48 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@google.com> | 2017-10-03 17:00:58 -0700 |
commit | 276e96ac9502bc5a399d6cad9e818faac4372ae4 (patch) | |
tree | 56d3e0b5aaf59a06acf34aa8e14ccd98351ec9c4 | |
parent | a71d591065bb4e0b058c7f9efa4e708dbc6c13c9 (diff) | |
download | kati-276e96ac9502bc5a399d6cad9e818faac4372ae4.tar.gz |
Add deprecated / obsolete variable support
By calling the custom KATI_deprecated_var / KATI_obsolete_var functions,
variables may be marked as deprecated or obsolete.
When accessed or assigned, deprecated variables will print a warning.
When accessed or assigned, obsolete variables will print an error and
stop.
Variables do not need to be set before calling the functions, and will
persist the deprecation warning through a reassignment. This way we can
easily mark variables that are sometimes passed via the environment as
deprecated.
Change-Id: Id04c974c446f471a18cc173f817760f4a02b9239
-rw-r--r-- | eval.cc | 11 | ||||
-rw-r--r-- | expr.cc | 9 | ||||
-rw-r--r-- | func.cc | 64 | ||||
-rw-r--r-- | testcase/deprecated_var.mk | 70 | ||||
-rw-r--r-- | testcase/err_deprecated_var_already_deprecated.mk | 4 | ||||
-rw-r--r-- | testcase/err_deprecated_var_already_obsolete.mk | 4 | ||||
-rw-r--r-- | testcase/err_obsolete_var.mk | 6 | ||||
-rw-r--r-- | testcase/err_obsolete_var_already_deprecated.mk | 4 | ||||
-rw-r--r-- | testcase/err_obsolete_var_already_obsolete.mk | 4 | ||||
-rw-r--r-- | testcase/err_obsolete_var_assign.mk | 4 | ||||
-rw-r--r-- | testcase/err_obsolete_var_msg.mk | 4 | ||||
-rw-r--r-- | testcase/err_obsolete_var_varref.mk | 5 | ||||
-rw-r--r-- | testcase/err_obsolete_var_varsubst.mk | 4 | ||||
-rw-r--r-- | var.cc | 3 | ||||
-rw-r--r-- | var.h | 31 |
15 files changed, 222 insertions, 5 deletions
@@ -55,7 +55,9 @@ Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs, is_override ? VarOrigin::OVERRIDE : VarOrigin::FILE)); Var* rhs = NULL; + Var* prev = LookupVarInCurrentScope(lhs); bool needs_assign = true; + switch (op) { case AssignOp::COLON_EQ: { SimpleVar* sv = new SimpleVar(origin); @@ -67,7 +69,6 @@ Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs, rhs = new RecursiveVar(rhs_v, origin, orig_rhs); break; case AssignOp::PLUS_EQ: { - Var* prev = LookupVarInCurrentScope(lhs); if (!prev->IsDefined()) { rhs = new RecursiveVar(rhs_v, origin, orig_rhs); } else if (prev->ReadOnly()) { @@ -80,7 +81,6 @@ Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs, break; } case AssignOp::QUESTION_EQ: { - Var* prev = LookupVarInCurrentScope(lhs); if (!prev->IsDefined()) { rhs = new RecursiveVar(rhs_v, origin, orig_rhs); } else { @@ -91,6 +91,13 @@ Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs, } } + 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; @@ -134,6 +134,7 @@ class SymRef : public Value { virtual void Eval(Evaluator* ev, string* s) const override { Var* v = ev->LookupVar(name_); + v->Used(ev, name_); v->Eval(ev, s); } @@ -158,7 +159,9 @@ class VarRef : public Value { ev->IncrementEvalDepth(); const string&& name = name_->Eval(ev); ev->DecrementEvalDepth(); - Var* v = ev->LookupVar(Intern(name)); + Symbol sym = Intern(name); + Var* v = ev->LookupVar(sym); + v->Used(ev, sym); v->Eval(ev, s); } @@ -184,10 +187,12 @@ class VarSubst : public Value { virtual void Eval(Evaluator* ev, string* s) const override { ev->IncrementEvalDepth(); const string&& name = name_->Eval(ev); - Var* v = ev->LookupVar(Intern(name)); + Symbol sym = Intern(name); + Var* v = ev->LookupVar(sym); const string&& pat_str = pat_->Eval(ev); const string&& subst = subst_->Eval(ev); ev->DecrementEvalDepth(); + v->Used(ev, sym); const string&& value = v->Eval(ev); WordWriter ww(s); Pattern pat(pat_str); @@ -809,6 +809,66 @@ void FileFunc(const vector<Value*>& args, Evaluator* ev, string* s) { } } +void DeprecatedVarFunc(const vector<Value*>& args, Evaluator* ev, string*) { + string vars_str = args[0]->Eval(ev); + string msg; + + if (args.size() == 2) { + msg = ". " + args[1]->Eval(ev); + } + + if (ev->avoid_io()) { + ev->Error("*** $(KATI_deprecated_var ...) is not supported in rules."); + } + + for (StringPiece var : WordScanner(vars_str)) { + Symbol sym = Intern(var); + Var* v = ev->LookupVar(sym); + if (!v->IsDefined()) { + v = new SimpleVar(VarOrigin::FILE); + sym.SetGlobalVar(v, false, nullptr); + } + + if (v->Deprecated()) { + ev->Error(StringPrintf("*** Cannot call KATI_deprecated_var on already deprecated variable: %s.", sym.c_str())); + } else if (v->Obsolete()) { + ev->Error(StringPrintf("*** Cannot call KATI_deprecated_var on already obsolete variable: %s.", sym.c_str())); + } + + v->SetDeprecated(msg); + } +} + +void ObsoleteVarFunc(const vector<Value*>&args, Evaluator* ev, string*) { + string vars_str = args[0]->Eval(ev); + string msg; + + if (args.size() == 2) { + msg = ". " + args[1]->Eval(ev); + } + + if (ev->avoid_io()) { + ev->Error("*** $(KATI_obsolete_var ...) is not supported in rules."); + } + + for (StringPiece var : WordScanner(vars_str)) { + Symbol sym = Intern(var); + Var* v = ev->LookupVar(sym); + if (!v->IsDefined()) { + v = new SimpleVar(VarOrigin::FILE); + sym.SetGlobalVar(v, false, nullptr); + } + + if (v->Deprecated()) { + ev->Error(StringPrintf("*** Cannot call KATI_obsolete_var on already deprecated variable: %s.", sym.c_str())); + } else if (v->Obsolete()) { + ev->Error(StringPrintf("*** Cannot call KATI_obsolete_var on already obsolete variable: %s.", sym.c_str())); + } + + v->SetObsolete(msg); + } +} + FuncInfo g_func_infos[] = { { "patsubst", &PatsubstFunc, 3, 3, false, false }, { "strip", &StripFunc, 1, 1, false, false }, @@ -852,6 +912,10 @@ FuncInfo g_func_infos[] = { { "error", &ErrorFunc, 1, 1, false, false }, { "file", &FileFunc, 2, 1, false, false }, + + /* Kati custom extension functions */ + { "KATI_deprecated_var", &DeprecatedVarFunc, 2, 1, false, false }, + { "KATI_obsolete_var", &ObsoleteVarFunc, 2, 1, false, false }, }; unordered_map<StringPiece, FuncInfo*>* g_func_info_map; diff --git a/testcase/deprecated_var.mk b/testcase/deprecated_var.mk new file mode 100644 index 0000000..2cacbda --- /dev/null +++ b/testcase/deprecated_var.mk @@ -0,0 +1,70 @@ +# TODO(go): not implemented + + +A := test +$(KATI_deprecated_var A B C D) + +# Writing to an undefined deprecated variable +B := test +ifndef KATI +$(info Makefile:8: B has been deprecated.) +endif + +# Reading from deprecated variables (set before/after/never the deprecation func) +# Writing to an undefined deprecated variable +D := $(A)$(B)$(C) +ifndef KATI +$(info Makefile:15: A has been deprecated.) +$(info Makefile:15: B has been deprecated.) +$(info Makefile:15: C has been deprecated.) +$(info Makefile:15: D has been deprecated.) +endif + +# Writing to a reset deprecated variable +D += test +ifndef KATI +$(info Makefile:24: D has been deprecated.) +endif + +# Using a custom message +$(KATI_deprecated_var E,Use X instead) +E = $(C) +ifndef KATI +$(info Makefile:31: E has been deprecated. Use X instead.) +endif + +# Expanding a recursive variable with an embedded deprecated variable +$(E) +ifndef KATI +$(info Makefile:37: E has been deprecated. Use X instead.) +$(info Makefile:37: C has been deprecated.) +endif + +# All of the previous variable references have been basic SymRefs, now check VarRefs +F = E +G := $($(F)) +ifndef KATI +$(info Makefile:45: E has been deprecated. Use X instead.) +$(info Makefile:45: C has been deprecated.) +endif + +# And check VarSubst +G := $(C:%.o=%.c) +ifndef KATI +$(info Makefile:52: C has been deprecated.) +endif + +# Deprecated variable used in a rule-specific variable +test: A := $(E) +ifndef KATI +$(info Makefile:58: E has been deprecated. Use X instead.) +$(info Makefile:58: C has been deprecated.) +# A hides the global A variable, so is not considered deprecated. +endif + +# Deprecated variable used in a rule +test: + echo $(C)Done +ifndef KATI +$(info Makefile:67: C has been deprecated.) +endif diff --git a/testcase/err_deprecated_var_already_deprecated.mk b/testcase/err_deprecated_var_already_deprecated.mk new file mode 100644 index 0000000..3f110cf --- /dev/null +++ b/testcase/err_deprecated_var_already_deprecated.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_deprecated_var A) +$(KATI_deprecated_var A)$(or $(KATI),$(error Cannot call KATI_deprecated_var on already deprecated variable: A)) diff --git a/testcase/err_deprecated_var_already_obsolete.mk b/testcase/err_deprecated_var_already_obsolete.mk new file mode 100644 index 0000000..3f60202 --- /dev/null +++ b/testcase/err_deprecated_var_already_obsolete.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A) +$(KATI_deprecated_var A)$(or $(KATI),$(error Cannot call KATI_deprecated_var on already obsolete variable: A)) diff --git a/testcase/err_obsolete_var.mk b/testcase/err_obsolete_var.mk new file mode 100644 index 0000000..fe032bd --- /dev/null +++ b/testcase/err_obsolete_var.mk @@ -0,0 +1,6 @@ +# TODO(go): not implemented +# +# We go into a lot more cases in deprecated_var.mk, and hope that if deprecated works, obsolete does too. + +$(KATI_obsolete_var A) +$(A) $(or $(KATI),$(error A is obsolete)) diff --git a/testcase/err_obsolete_var_already_deprecated.mk b/testcase/err_obsolete_var_already_deprecated.mk new file mode 100644 index 0000000..42cfbfb --- /dev/null +++ b/testcase/err_obsolete_var_already_deprecated.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_deprecated_var A) +$(KATI_obsolete_var A)$(or $(KATI),$(error Cannot call KATI_obsolete_var on already deprecated variable: A)) diff --git a/testcase/err_obsolete_var_already_obsolete.mk b/testcase/err_obsolete_var_already_obsolete.mk new file mode 100644 index 0000000..e6f9d76 --- /dev/null +++ b/testcase/err_obsolete_var_already_obsolete.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A) +$(KATI_obsolete_var A)$(or $(KATI),$(error Cannot call KATI_obsolete_var on already obsolete variable: A)) diff --git a/testcase/err_obsolete_var_assign.mk b/testcase/err_obsolete_var_assign.mk new file mode 100644 index 0000000..a6b7397 --- /dev/null +++ b/testcase/err_obsolete_var_assign.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A) +A := $(or $(KATI),$(error A is obsolete)) diff --git a/testcase/err_obsolete_var_msg.mk b/testcase/err_obsolete_var_msg.mk new file mode 100644 index 0000000..8162b10 --- /dev/null +++ b/testcase/err_obsolete_var_msg.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A,Use Y instead) +$(A) $(or $(KATI),$(error A is obsolete. Use Y instead)) diff --git a/testcase/err_obsolete_var_varref.mk b/testcase/err_obsolete_var_varref.mk new file mode 100644 index 0000000..a671d56 --- /dev/null +++ b/testcase/err_obsolete_var_varref.mk @@ -0,0 +1,5 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A) +B := A +$($(B)) $(or $(KATI),$(error A is obsolete)) diff --git a/testcase/err_obsolete_var_varsubst.mk b/testcase/err_obsolete_var_varsubst.mk new file mode 100644 index 0000000..33c89b5 --- /dev/null +++ b/testcase/err_obsolete_var_varsubst.mk @@ -0,0 +1,4 @@ +# TODO(go): not implemented + +$(KATI_obsolete_var A) +$(A:%.o=%.c) $(or $(KATI),$(error A is obsolete)) @@ -16,6 +16,7 @@ #include "var.h" +#include "eval.h" #include "expr.h" #include "log.h" @@ -37,7 +38,7 @@ const char* GetOriginStr(VarOrigin origin) { return "*** broken origin ***"; } -Var::Var() : readonly_(false) { +Var::Var() : readonly_(false), message_(), error_(false) { } Var::~Var() { @@ -15,11 +15,14 @@ #ifndef VAR_H_ #define VAR_H_ +#include <memory> #include <string> #include <unordered_map> #include <unordered_set> +#include "eval.h" #include "expr.h" +#include "log.h" #include "stmt.h" #include "string_piece.h" #include "symtab.h" @@ -59,11 +62,39 @@ class Var : public Evaluable { bool ReadOnly() const { return readonly_; } void SetReadOnly() { readonly_ = true; } + bool Deprecated() const { return message_ && !error_; } + void SetDeprecated(StringPiece msg) { + message_.reset(new string(msg.as_string())); + } + + bool Obsolete() const { return error_; } + void SetObsolete(StringPiece msg) { + message_.reset(new string(msg.as_string())); + error_ = true; + } + + const string& DeprecatedMessage() const { return *message_; } + + // This variable was used (either written or read from) + void Used(Evaluator* ev, const Symbol& sym) const { + if (!message_) { + return; + } + + if (error_) { + ev->Error(StringPrintf("*** %s is obsolete%s.", sym.c_str(), message_->c_str())); + } else { + WARN_LOC(ev->loc(), "%s has been deprecated%s.", sym.c_str(), message_->c_str()); + } + } + protected: Var(); private: bool readonly_; + unique_ptr<string> message_; + bool error_; }; class SimpleVar : public Var { |