Snap for 4625912 from ec7c40a97a07acadd78d93dd8d9f2c8d361513ef to pi-release

Change-Id: Id765db19513c222ef36fb5b16bd3277612fc53b4
diff --git a/eval.cc b/eval.cc
index cf3f859..fa7bebb 100644
--- a/eval.cc
+++ b/eval.cc
@@ -38,6 +38,7 @@
       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());
@@ -358,18 +359,35 @@
   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) {
-      exports_[Intern(tok)] = stmt->is_export;
+      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 lhs, rhs;
+      StringPiece rhs;
       AssignOp op;
       ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
-      exports_[Intern(lhs)] = stmt->is_export;
+    }
+    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());
+      }
     }
   }
 }
diff --git a/eval.h b/eval.h
index e588f7e..4fb5f05 100644
--- a/eval.h
+++ b/eval.h
@@ -15,6 +15,7 @@
 #ifndef EVAL_H_
 #define EVAL_H_
 
+#include <memory>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -98,6 +99,16 @@
   }
   void DumpStackStats() const;
 
+  bool ExportDeprecated() const { return export_message_ && !export_error_; };
+  bool ExportObsolete() const { return export_error_; };
+  void SetExportDeprecated(StringPiece msg) {
+    export_message_.reset(new string(msg.as_string()));
+  }
+  void SetExportObsolete(StringPiece msg) {
+    export_message_.reset(new string(msg.as_string()));
+    export_error_ = true;
+  }
+
  private:
   Var* EvalRHS(Symbol lhs,
                Value* rhs,
@@ -139,6 +150,9 @@
   void* lowest_stack_;
   Loc lowest_loc_;
 
+  unique_ptr<string> export_message_;
+  bool export_error_;
+
   static unordered_set<Symbol> used_undefined_vars_;
 
   Symbol kati_readonly_;
diff --git a/func.cc b/func.cc
index 1dea7bb..c033d3b 100644
--- a/func.cc
+++ b/func.cc
@@ -892,6 +892,36 @@
   }
 }
 
+void DeprecateExportFunc(const vector<Value*>& args, Evaluator* ev, string*) {
+  string msg = ". " + args[0]->Eval(ev);
+
+  if (ev->avoid_io()) {
+    ev->Error("*** $(KATI_deprecate_export) is not supported in rules.");
+  }
+
+  if (ev->ExportObsolete()) {
+    ev->Error("*** Export is already obsolete.");
+  } else if (ev->ExportDeprecated()) {
+    ev->Error("*** Export is already deprecated.");
+  }
+
+  ev->SetExportDeprecated(msg);
+}
+
+void ObsoleteExportFunc(const vector<Value*>& args, Evaluator* ev, string*) {
+  string msg = ". " + args[0]->Eval(ev);
+
+  if (ev->avoid_io()) {
+    ev->Error("*** $(KATI_obsolete_export) is not supported in rules.");
+  }
+
+  if (ev->ExportObsolete()) {
+    ev->Error("*** Export is already obsolete.");
+  }
+
+  ev->SetExportObsolete(msg);
+}
+
 FuncInfo g_func_infos[] = {
     {"patsubst", &PatsubstFunc, 3, 3, false, false},
     {"strip", &StripFunc, 1, 1, false, false},
@@ -939,6 +969,8 @@
     /* Kati custom extension functions */
     {"KATI_deprecated_var", &DeprecatedVarFunc, 2, 1, false, false},
     {"KATI_obsolete_var", &ObsoleteVarFunc, 2, 1, false, false},
+    {"KATI_deprecate_export", &DeprecateExportFunc, 1, 1, false, false},
+    {"KATI_obsolete_export", &ObsoleteExportFunc, 1, 1, false, false},
 };
 
 unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
diff --git a/testcase/deprecated_export.mk b/testcase/deprecated_export.mk
new file mode 100644
index 0000000..3744f70
--- /dev/null
+++ b/testcase/deprecated_export.mk
@@ -0,0 +1,21 @@
+# TODO(go): not implemented
+
+A := 1
+B := 2
+export A B
+
+$(KATI_deprecate_export Message)
+
+export C := ok
+unexport B
+
+ifndef KATI
+$(info Makefile:9: C: export has been deprecated. Message.)
+$(info Makefile:10: B: unexport has been deprecated. Message.)
+endif
+
+test:
+	echo $$(A)
+	echo $$(B)
+	echo $$(C)
+	echo Done
diff --git a/testcase/err_obsolete_export.mk b/testcase/err_obsolete_export.mk
new file mode 100644
index 0000000..bf78dc3
--- /dev/null
+++ b/testcase/err_obsolete_export.mk
@@ -0,0 +1,7 @@
+# TODO(go): not implemented
+
+export A := ok
+
+$(KATI_obsolete_export Message)
+
+export B := fail $(or $(KATI),$(error B: export is obsolete. Message))