[C++] Split CommandEvaluator from Executor

This is the first step of --ninja support
diff --git a/Makefile b/Makefile
index 46d7730..bb91bf2 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,7 @@
 GO_SRCS:=$(wildcard *.go)
 CXX_SRCS:= \
 	ast.cc \
+	command.cc \
 	dep.cc \
 	eval.cc \
 	exec.cc \
@@ -24,6 +25,7 @@
 	flags.cc \
 	func.cc \
 	main.cc \
+	ninja.cc \
 	parser.cc \
 	rule.cc \
 	string_piece.cc \
diff --git a/command.cc b/command.cc
new file mode 100644
index 0000000..39aa9fc
--- /dev/null
+++ b/command.cc
@@ -0,0 +1,214 @@
+// 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 "command.h"
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "dep.h"
+#include "eval.h"
+#include "log.h"
+#include "strutil.h"
+#include "var.h"
+
+namespace {
+
+class AutoVar : public Var {
+ public:
+  virtual const char* Flavor() const override {
+    return "undefined";
+  }
+  virtual VarOrigin Origin() const override {
+    return VarOrigin::AUTOMATIC;
+  }
+
+  virtual void AppendVar(Evaluator*, Value*) override { CHECK(false); }
+
+  virtual StringPiece String() const override {
+    ERROR("$(value %s) is not implemented yet", sym_);
+    return "";
+  }
+
+  virtual string DebugString() const override {
+    return string("AutoVar(") + sym_ + ")";
+  }
+
+ protected:
+  AutoVar(CommandEvaluator* ce, const char* sym) : ce_(ce), sym_(sym) {}
+  virtual ~AutoVar() = default;
+
+  CommandEvaluator* ce_;
+  const char* sym_;
+};
+
+#define DECLARE_AUTO_VAR_CLASS(name)                                    \
+  class name : public AutoVar {                                         \
+   public:                                                              \
+   name(CommandEvaluator* ce, const char* sym)                          \
+       : AutoVar(ce, sym) {}                                            \
+   virtual ~name() = default;                                           \
+   virtual void Eval(Evaluator* ev, string* s) const override;          \
+  }
+
+DECLARE_AUTO_VAR_CLASS(AutoAtVar);
+DECLARE_AUTO_VAR_CLASS(AutoLessVar);
+DECLARE_AUTO_VAR_CLASS(AutoHatVar);
+DECLARE_AUTO_VAR_CLASS(AutoPlusVar);
+DECLARE_AUTO_VAR_CLASS(AutoStarVar);
+
+class AutoSuffixDVar : public AutoVar {
+ public:
+  AutoSuffixDVar(CommandEvaluator* ce, const char* sym, Var* wrapped)
+      : AutoVar(ce, sym), wrapped_(wrapped) {
+  }
+  virtual ~AutoSuffixDVar() = default;
+  virtual void Eval(Evaluator* ev, string* s) const override;
+
+ private:
+  Var* wrapped_;
+};
+
+class AutoSuffixFVar : public AutoVar {
+ public:
+  AutoSuffixFVar(CommandEvaluator* ce, const char* sym, Var* wrapped)
+      : AutoVar(ce, sym), wrapped_(wrapped) {}
+  virtual ~AutoSuffixFVar() = default;
+  virtual void Eval(Evaluator* ev, string* s) const override;
+
+ private:
+  Var* wrapped_;
+};
+
+void AutoAtVar::Eval(Evaluator*, string* s) const {
+  AppendString(ce_->current_dep_node()->output, s);
+}
+
+void AutoLessVar::Eval(Evaluator*, string* s) const {
+  auto& ai = ce_->current_dep_node()->actual_inputs;
+  if (!ai.empty())
+    AppendString(ai[0], s);
+}
+
+void AutoHatVar::Eval(Evaluator*, string* s) const {
+  unordered_set<StringPiece> seen;
+  WordWriter ww(s);
+  for (StringPiece ai : ce_->current_dep_node()->actual_inputs) {
+    if (seen.insert(ai).second)
+      ww.Write(ai);
+  }
+}
+
+void AutoPlusVar::Eval(Evaluator*, string* s) const {
+  WordWriter ww(s);
+  for (StringPiece ai : ce_->current_dep_node()->actual_inputs) {
+    ww.Write(ai);
+  }
+}
+
+void AutoStarVar::Eval(Evaluator*, string* s) const {
+  AppendString(StripExt(ce_->current_dep_node()->output), s);
+}
+
+void AutoSuffixDVar::Eval(Evaluator* ev, string* s) const {
+  string buf;
+  wrapped_->Eval(ev, &buf);
+  WordWriter ww(s);
+  for (StringPiece tok : WordScanner(buf)) {
+    ww.Write(Dirname(tok));
+  }
+}
+
+void AutoSuffixFVar::Eval(Evaluator* ev, string* s) const {
+  string buf;
+  wrapped_->Eval(ev, &buf);
+  WordWriter ww(s);
+  for (StringPiece tok : WordScanner(buf)) {
+    ww.Write(Basename(tok));
+  }
+}
+
+void ParseCommandPrefixes(StringPiece* s, bool* echo, bool* ignore_error) {
+  *s = TrimLeftSpace(*s);
+  while (true) {
+    char c = s->get(0);
+    if (c == '@') {
+      *echo = false;
+    } else if (c == '-') {
+      *ignore_error = true;
+    } else {
+      break;
+    }
+    *s = TrimLeftSpace(s->substr(1));
+  }
+}
+
+}  // namespace
+
+CommandEvaluator::CommandEvaluator(Evaluator* ev)
+    : ev_(ev) {
+  Vars* vars = ev_->mutable_vars();
+#define INSERT_AUTO_VAR(name, sym) do {                                 \
+    Var* v = new name(this, sym);                                       \
+    (*vars)[STRING_PIECE(sym)] = v;                                     \
+    (*vars)[STRING_PIECE(sym"D")] = new AutoSuffixDVar(this, sym"D", v); \
+    (*vars)[STRING_PIECE(sym"F")] = new AutoSuffixFVar(this, sym"F", v); \
+  } while (0)
+  INSERT_AUTO_VAR(AutoAtVar, "@");
+  INSERT_AUTO_VAR(AutoLessVar, "<");
+  INSERT_AUTO_VAR(AutoHatVar, "^");
+  INSERT_AUTO_VAR(AutoPlusVar, "+");
+  INSERT_AUTO_VAR(AutoStarVar, "*");
+}
+
+void CommandEvaluator::Eval(DepNode* n, vector<Command*>* commands) {
+  ev_->set_current_scope(n->rule_vars);
+  current_dep_node_ = n;
+  for (Value* v : n->cmds) {
+    shared_ptr<string> cmds_buf = v->Eval(ev_);
+    StringPiece cmds = *cmds_buf;
+    bool global_echo = true;
+    bool global_ignore_error = false;
+    ParseCommandPrefixes(&cmds, &global_echo, &global_ignore_error);
+    if (cmds == "")
+      continue;
+    while (true) {
+      size_t lf_cnt;
+      size_t index = FindEndOfLine(cmds, 0, &lf_cnt);
+      if (index == cmds.size())
+        index = string::npos;
+      StringPiece cmd = TrimLeftSpace(cmds.substr(0, index));
+      cmds = cmds.substr(index + 1);
+
+      bool echo = global_echo;
+      bool ignore_error = global_ignore_error;
+      ParseCommandPrefixes(&cmd, &echo, &ignore_error);
+
+      if (!cmd.empty()) {
+        Command* command = new Command;
+        command->output = n->output;
+        command->cmd = make_shared<string>(cmd.as_string());
+        command->echo = echo;
+        command->ignore_error = ignore_error;
+        commands->push_back(command);
+      }
+      if (index == string::npos)
+        break;
+    }
+    continue;
+  }
+  ev_->set_current_scope(NULL);
+}
diff --git a/command.h b/command.h
new file mode 100644
index 0000000..f06533f
--- /dev/null
+++ b/command.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef COMMAND_H_
+#define COMMAND_H_
+
+#include <memory>
+#include <vector>
+
+#include "string_piece.h"
+
+using namespace std;
+
+struct DepNode;
+class Evaluator;
+
+struct Command {
+  Command()
+      : echo(true), ignore_error(false) {
+  }
+  StringPiece output;
+  shared_ptr<string> cmd;
+  bool echo;
+  bool ignore_error;
+  //StringPiece shell;
+};
+
+class CommandEvaluator {
+ public:
+  explicit CommandEvaluator(Evaluator* ev);
+  void Eval(DepNode* n, vector<Command*>* commands);
+  const DepNode* current_dep_node() const { return current_dep_node_; }
+
+ private:
+  Evaluator* ev_;
+  DepNode* current_dep_node_;
+};
+
+#endif  // COMMAND_H_
diff --git a/exec.cc b/exec.cc
index 9e6b1ab..69cf4ea 100644
--- a/exec.cc
+++ b/exec.cc
@@ -21,10 +21,10 @@
 
 #include <memory>
 #include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include "command.h"
 #include "dep.h"
 #include "eval.h"
 #include "fileutil.h"
@@ -37,101 +37,10 @@
 
 namespace {
 
-class Executor;
-
-class AutoVar : public Var {
- public:
-  virtual const char* Flavor() const override {
-    return "undefined";
-  }
-  virtual VarOrigin Origin() const override {
-    return VarOrigin::AUTOMATIC;
-  }
-
-  virtual void AppendVar(Evaluator*, Value*) override { CHECK(false); }
-
-  virtual StringPiece String() const override {
-    ERROR("$(value %s) is not implemented yet", sym_);
-    return "";
-  }
-
-  virtual string DebugString() const override {
-    return string("AutoVar(") + sym_ + ")";
-  }
-
- protected:
-  AutoVar(Executor* ex, const char* sym) : ex_(ex), sym_(sym) {}
-  virtual ~AutoVar() = default;
-
-  Executor* ex_;
-  const char* sym_;
-};
-
-#define DECLARE_AUTO_VAR_CLASS(name)                            \
-  class name : public AutoVar {                                 \
-   public:                                                      \
-   name(Executor* ex, const char* sym)                          \
-       : AutoVar(ex, sym) {}                                    \
-   virtual ~name() = default;                                   \
-   virtual void Eval(Evaluator* ev, string* s) const override;  \
-  }
-
-DECLARE_AUTO_VAR_CLASS(AutoAtVar);
-DECLARE_AUTO_VAR_CLASS(AutoLessVar);
-DECLARE_AUTO_VAR_CLASS(AutoHatVar);
-DECLARE_AUTO_VAR_CLASS(AutoPlusVar);
-DECLARE_AUTO_VAR_CLASS(AutoStarVar);
-
-class AutoSuffixDVar : public AutoVar {
- public:
-  AutoSuffixDVar(Executor* ex, const char* sym, Var* wrapped)
-      : AutoVar(ex, sym), wrapped_(wrapped) {
-  }
-  virtual ~AutoSuffixDVar() = default;
-  virtual void Eval(Evaluator* ev, string* s) const override;
-
- private:
-  Var* wrapped_;
-};
-
-class AutoSuffixFVar : public AutoVar {
- public:
-  AutoSuffixFVar(Executor* ex, const char* sym, Var* wrapped)
-      : AutoVar(ex, sym), wrapped_(wrapped) {}
-  virtual ~AutoSuffixFVar() = default;
-  virtual void Eval(Evaluator* ev, string* s) const override;
-
- private:
-  Var* wrapped_;
-};
-
-struct Runner {
-  Runner()
-      : echo(true), ignore_error(false) {
-  }
-  StringPiece output;
-  shared_ptr<string> cmd;
-  bool echo;
-  bool ignore_error;
-  //StringPiece shell;
-};
-
 class Executor {
  public:
   explicit Executor(Evaluator* ev)
-      : ev_(ev) {
-    Vars* vars = ev_->mutable_vars();
-#define INSERT_AUTO_VAR(name, sym) do {                                 \
-      Var* v = new name(this, sym);                                     \
-      (*vars)[STRING_PIECE(sym)] = v;                                   \
-      (*vars)[STRING_PIECE(sym"D")] = new AutoSuffixDVar(this, sym"D", v); \
-      (*vars)[STRING_PIECE(sym"F")] = new AutoSuffixFVar(this, sym"F", v); \
-    } while (0)
-    INSERT_AUTO_VAR(AutoAtVar, "@");
-    INSERT_AUTO_VAR(AutoLessVar, "<");
-    INSERT_AUTO_VAR(AutoHatVar, "^");
-    INSERT_AUTO_VAR(AutoPlusVar, "+");
-    INSERT_AUTO_VAR(AutoStarVar, "*");
+      : ce_(ev) {
   }
 
   void ExecNode(DepNode* n, DepNode* needed_by) {
@@ -154,141 +63,35 @@
       ExecNode(d, n);
     }
 
-    vector<Runner*> runners;
-    CreateRunners(n, &runners);
-    for (Runner* runner : runners) {
-      if (runner->echo) {
-        printf("%s\n", runner->cmd->c_str());
+    vector<Command*> commands;
+    ce_.Eval(n, &commands);
+    for (Command* command : commands) {
+      if (command->echo) {
+        printf("%s\n", command->cmd->c_str());
         fflush(stdout);
       }
       if (!g_is_dry_run) {
-        int result = system(runner->cmd->c_str());
+        int result = system(command->cmd->c_str());
         if (result != 0) {
-          if (runner->ignore_error) {
+          if (command->ignore_error) {
             fprintf(stderr, "[%.*s] Error %d (ignored)\n",
-                    SPF(runner->output), WEXITSTATUS(result));
+                    SPF(command->output), WEXITSTATUS(result));
           } else {
             fprintf(stderr, "*** [%.*s] Error %d\n",
-                    SPF(runner->output), WEXITSTATUS(result));
+                    SPF(command->output), WEXITSTATUS(result));
             exit(1);
           }
         }
       }
-      delete runner;
+      delete command;
     }
   }
 
-  void ParseCommandPrefixes(StringPiece* s, bool* echo, bool* ignore_error) {
-    *s = TrimLeftSpace(*s);
-    while (true) {
-      char c = s->get(0);
-      if (c == '@') {
-        *echo = false;
-      } else if (c == '-') {
-        *ignore_error = true;
-      } else {
-        break;
-      }
-      *s = TrimLeftSpace(s->substr(1));
-    }
-  }
-
-  void CreateRunners(DepNode* n, vector<Runner*>* runners) {
-    ev_->set_current_scope(n->rule_vars);
-    current_dep_node_ = n;
-    for (Value* v : n->cmds) {
-      shared_ptr<string> cmds_buf = v->Eval(ev_);
-      StringPiece cmds = *cmds_buf;
-      bool global_echo = true;
-      bool global_ignore_error = false;
-      ParseCommandPrefixes(&cmds, &global_echo, &global_ignore_error);
-      if (cmds == "")
-        continue;
-      while (true) {
-        size_t lf_cnt;
-        size_t index = FindEndOfLine(cmds, 0, &lf_cnt);
-        if (index == cmds.size())
-          index = string::npos;
-        StringPiece cmd = TrimLeftSpace(cmds.substr(0, index));
-        cmds = cmds.substr(index + 1);
-
-        bool echo = global_echo;
-        bool ignore_error = global_ignore_error;
-        ParseCommandPrefixes(&cmd, &echo, &ignore_error);
-
-        if (!cmd.empty()) {
-          Runner* runner = new Runner;
-          runner->output = n->output;
-          runner->cmd = make_shared<string>(cmd.as_string());
-          runner->echo = echo;
-          runner->ignore_error = ignore_error;
-          runners->push_back(runner);
-        }
-        if (index == string::npos)
-          break;
-      }
-      continue;
-    }
-    ev_->set_current_scope(NULL);
-  }
-
-  const DepNode* current_dep_node() const { return current_dep_node_; }
-
  private:
-  Vars* vars_;
-  Evaluator* ev_;
+  CommandEvaluator ce_;
   unordered_map<StringPiece, bool> done_;
-  DepNode* current_dep_node_;
 };
 
-void AutoAtVar::Eval(Evaluator*, string* s) const {
-  AppendString(ex_->current_dep_node()->output, s);
-}
-
-void AutoLessVar::Eval(Evaluator*, string* s) const {
-  auto& ai = ex_->current_dep_node()->actual_inputs;
-  if (!ai.empty())
-    AppendString(ai[0], s);
-}
-
-void AutoHatVar::Eval(Evaluator*, string* s) const {
-  unordered_set<StringPiece> seen;
-  WordWriter ww(s);
-  for (StringPiece ai : ex_->current_dep_node()->actual_inputs) {
-    if (seen.insert(ai).second)
-      ww.Write(ai);
-  }
-}
-
-void AutoPlusVar::Eval(Evaluator*, string* s) const {
-  WordWriter ww(s);
-  for (StringPiece ai : ex_->current_dep_node()->actual_inputs) {
-    ww.Write(ai);
-  }
-}
-
-void AutoStarVar::Eval(Evaluator*, string* s) const {
-  AppendString(StripExt(ex_->current_dep_node()->output), s);
-}
-
-void AutoSuffixDVar::Eval(Evaluator* ev, string* s) const {
-  string buf;
-  wrapped_->Eval(ev, &buf);
-  WordWriter ww(s);
-  for (StringPiece tok : WordScanner(buf)) {
-    ww.Write(Dirname(tok));
-  }
-}
-
-void AutoSuffixFVar::Eval(Evaluator* ev, string* s) const {
-  string buf;
-  wrapped_->Eval(ev, &buf);
-  WordWriter ww(s);
-  for (StringPiece tok : WordScanner(buf)) {
-    ww.Write(Basename(tok));
-  }
-}
-
 }  // namespace
 
 void Exec(const vector<DepNode*>& roots, Evaluator* ev) {
diff --git a/main.cc b/main.cc
index 314df7e..4392f2a 100644
--- a/main.cc
+++ b/main.cc
@@ -29,6 +29,7 @@
 #include "flags.h"
 #include "func.h"
 #include "log.h"
+#include "ninja.h"
 #include "parser.h"
 #include "string_piece.h"
 #include "stringprintf.h"
@@ -38,6 +39,7 @@
 
 static const char* g_makefile;
 static bool g_is_syntax_check_only;
+static bool g_generate_ninja;
 
 static void ParseCommandLine(int argc, char* argv[],
                              vector<StringPiece>* targets,
@@ -52,6 +54,8 @@
       g_is_dry_run = true;
     } else if (!strcmp(arg, "--kati_stats")) {
       g_enable_stat_logs = true;
+    } else if (!strcmp(arg, "--ninja")) {
+      g_generate_ninja = true;
     } else if (arg[0] == '-') {
       ERROR("Unknown flag: %s", arg);
     } else {
@@ -194,6 +198,12 @@
   if (g_is_syntax_check_only)
     return 0;
 
+  if (g_generate_ninja) {
+    ScopedTimeReporter tr("generate ninja time");
+    GenerateNinja(nodes, ev);
+    return 0;
+  }
+
   {
     ScopedTimeReporter tr("exec time");
     Exec(nodes, ev);
diff --git a/ninja.cc b/ninja.cc
new file mode 100644
index 0000000..a14e3f1
--- /dev/null
+++ b/ninja.cc
@@ -0,0 +1,21 @@
+// 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 "ninja.h"
+
+void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev) {
+
+}
diff --git a/ninja.h b/ninja.h
new file mode 100644
index 0000000..74cad0d
--- /dev/null
+++ b/ninja.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef NINJA_H_
+#define NINJA_H_
+
+#include <vector>
+
+using namespace std;
+
+class DepNode;
+class Evaluator;
+
+void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev);
+
+#endif  // NINJA_H_