Merge remote-tracking branch 'aosp/upstream'

Mainly for 9dce444 also add a few warnings for unsupported
features.

42ce87c [C++] Do not fail by $(shell) in functions for now
9dce444 [C++] Never specify non-positive -j value
b8517f7 Add implicit_pattern_rule_chain2.mk
28da237 [C++] Explicitly disallow $(shell) in other make constructs
77be80d [C++] Warn about unsupported builtin targets
cbe9f49 [C++] Add a minimal support for .SUFFIXES
3de9ae0 Add delete_on_error.mk

Change-Id: Ia19d8d5f8e5bfb22627aaaa7ed7c461f47b34733
diff --git a/dep.cc b/dep.cc
index 56c45ce..657c97f 100644
--- a/dep.cc
+++ b/dep.cc
@@ -139,6 +139,38 @@
         restat_.insert(input);
       }
     }
+    found = rules_.find(Intern(".SUFFIXES"));
+    if (found != rules_.end()) {
+      if (found->second->inputs.empty()) {
+        suffix_rules_.clear();
+      } else {
+        WARN("%s:%d: kati doesn't support .SUFFIXES with prerequisites",
+             LOCF(found->second->loc));
+      }
+    }
+
+    // Note we can safely ignore .DELETE_ON_ERROR for --ninja mode.
+    static const char* kUnsupportedBuiltinTargets[] = {
+      ".DEFAULT",
+      ".PRECIOUS",
+      ".INTERMEDIATE",
+      ".SECONDARY",
+      ".SECONDEXPANSION",
+      ".IGNORE",
+      ".LOW_RESOLUTION_TIME",
+      ".SILENT",
+      ".EXPORT_ALL_VARIABLES",
+      ".NOTPARALLEL",
+      ".ONESHELL",
+      ".POSIX",
+      NULL
+    };
+    for (const char** p = kUnsupportedBuiltinTargets; *p; p++) {
+      auto found = rules_.find(Intern(*p));
+      if (found != rules_.end()) {
+        WARN("%s:%d: kati doesn't support %s", LOCF(found->second->loc), *p);
+      }
+    }
   }
 
   ~DepBuilder() {
diff --git a/eval.cc b/eval.cc
index 2ba4a56..3c0eae2 100644
--- a/eval.cc
+++ b/eval.cc
@@ -41,7 +41,8 @@
       vars_(new Vars()),
       last_rule_(NULL),
       current_scope_(NULL),
-      avoid_io_(false) {
+      avoid_io_(false),
+      eval_depth_(0) {
 }
 
 Evaluator::~Evaluator() {
diff --git a/eval.h b/eval.h
index 3ebb21b..6bc21b9 100644
--- a/eval.h
+++ b/eval.h
@@ -92,6 +92,14 @@
     return used_undefined_vars_;
   }
 
+  int eval_depth() const { return eval_depth_; }
+  void IncrementEvalDepth() {
+    eval_depth_++;
+  }
+  void DecrementEvalDepth() {
+    eval_depth_--;
+  }
+
  private:
   Var* EvalRHS(Symbol lhs, Value* rhs, StringPiece orig_rhs, AssignOp op,
                bool is_override = false);
@@ -112,6 +120,10 @@
   bool is_bootstrap_;
 
   bool avoid_io_;
+  // This value tracks the nest level of make expressions. For
+  // example, $(YYY) in $(XXX $(YYY)) is evaluated with depth==2.
+  // This will be used to disallow $(shell) in other make constructs.
+  int eval_depth_;
   // Commands which should run at ninja-time (i.e., info, warning, and
   // error).
   vector<string> delayed_output_commands_;
diff --git a/expr.cc b/expr.cc
index e43d189..d0d3e38 100644
--- a/expr.cc
+++ b/expr.cc
@@ -152,7 +152,9 @@
   }
 
   virtual void Eval(Evaluator* ev, string* s) const override {
+    ev->IncrementEvalDepth();
     const string&& name = name_->Eval(ev);
+    ev->DecrementEvalDepth();
     Var* v = ev->LookupVar(Intern(name));
     v->Eval(ev, s);
   }
@@ -177,11 +179,13 @@
   }
 
   virtual void Eval(Evaluator* ev, string* s) const override {
+    ev->IncrementEvalDepth();
     const string&& name = name_->Eval(ev);
     Var* v = ev->LookupVar(Intern(name));
-    const string&& value = v->Eval(ev);
     const string&& pat_str = pat_->Eval(ev);
     const string&& subst = subst_->Eval(ev);
+    ev->DecrementEvalDepth();
+    const string&& value = v->Eval(ev);
     WordWriter ww(s);
     Pattern pat(pat_str);
     for (StringPiece tok : WordScanner(value)) {
@@ -216,7 +220,9 @@
 
   virtual void Eval(Evaluator* ev, string* s) const override {
     LOG("Invoke func %s(%s)", name(), JoinValues(args_, ",").c_str());
+    ev->IncrementEvalDepth();
     fi_->func(args_, ev, s);
+    ev->DecrementEvalDepth();
   }
 
   virtual string DebugString_() const override {
diff --git a/func.cc b/func.cc
index 33c5013..2e6eb99 100644
--- a/func.cc
+++ b/func.cc
@@ -546,6 +546,12 @@
 void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   string cmd = args[0]->Eval(ev);
   if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
+    if (ev->eval_depth() > 1) {
+      // TODO: Make this an error.
+      WARN("%s:%d: kati doesn't support passing results of $(shell) "
+           "to other make constructs: %s",
+           LOCF(ev->loc()), cmd.c_str());
+    }
     StripShellComment(&cmd);
     *s += "$(";
     *s += cmd;
@@ -609,12 +615,16 @@
                                     Intern(tmpvar_name), av[i-1].get()));
     }
   }
+
+  ev->DecrementEvalDepth();
   func->Eval(ev, s);
+  ev->IncrementEvalDepth();
 }
 
 void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   const string&& varname = args[0]->Eval(ev);
   const string&& list = args[1]->Eval(ev);
+  ev->DecrementEvalDepth();
   WordWriter ww(s);
   for (StringPiece tok : WordScanner(list)) {
     unique_ptr<SimpleVar> v(new SimpleVar(
@@ -623,6 +633,7 @@
     ww.MaybeAddWhitespace();
     args[2]->Eval(ev, s);
   }
+  ev->IncrementEvalDepth();
 }
 
 void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
diff --git a/main.cc b/main.cc
index 930348b..ff2b751 100644
--- a/main.cc
+++ b/main.cc
@@ -86,7 +86,7 @@
                       );
   if (g_flags.generate_ninja) {
     bootstrap += StringPrintf("MAKE?=make -j%d\n",
-                              g_flags.num_jobs < 1 ? 1 : g_flags.num_jobs / 2);
+                              g_flags.num_jobs <= 1 ? 1 : g_flags.num_jobs / 2);
   } else {
     bootstrap += StringPrintf("MAKE?=%s\n",
                               JoinStrings(g_flags.subkati_args, " ").c_str());
diff --git a/testcase/delete_on_error.mk b/testcase/delete_on_error.mk
new file mode 100644
index 0000000..f194759
--- /dev/null
+++ b/testcase/delete_on_error.mk
@@ -0,0 +1,9 @@
+# TODO: Fix for non-ninja mode.
+
+.DELETE_ON_ERROR:
+
+test: file
+
+file:
+	touch $@
+	false
diff --git a/testcase/err_suffixes.mk b/testcase/err_suffixes.mk
new file mode 100644
index 0000000..2ac6d09
--- /dev/null
+++ b/testcase/err_suffixes.mk
@@ -0,0 +1,10 @@
+# TODO: Fix
+
+test1:
+	touch a.src
+
+test2: a.out
+
+# This isn't in .SUFFIXES.
+.src.out:
+	echo $< > $@
diff --git a/testcase/err_suffixes2.mk b/testcase/err_suffixes2.mk
new file mode 100644
index 0000000..8f9bacb
--- /dev/null
+++ b/testcase/err_suffixes2.mk
@@ -0,0 +1,8 @@
+# TODO(go): Fix
+
+test1:
+	touch a.c
+
+test2: a.o
+
+.SUFFIXES:
diff --git a/testcase/implicit_pattern_rule_chain2.mk b/testcase/implicit_pattern_rule_chain2.mk
new file mode 100644
index 0000000..65e914b
--- /dev/null
+++ b/testcase/implicit_pattern_rule_chain2.mk
@@ -0,0 +1,13 @@
+# TODO: Fix. We probably need to assume foo.y exists as there's a rule
+# to generate it.
+
+test1:
+	touch foo.x
+
+test2: foo.z
+
+%.z: %.y
+	cp $< $@
+
+%.y: %.x
+	cp $< $@
diff --git a/testcase/strip_and_shell.mk b/testcase/strip_and_shell.mk
index db1bbf2..cb550f3 100644
--- a/testcase/strip_and_shell.mk
+++ b/testcase/strip_and_shell.mk
@@ -1,3 +1,4 @@
+# TODO(c-ninja): $(shell) in another make expression is not supported.
 
 test:
 	echo $(strip $(shell pwd))