remove command keyword, convert to variable binding
authorEvan Martin <martine@danga.com>
Sun, 24 Oct 2010 18:51:20 +0000 (11:51 -0700)
committerEvan Martin <martine@danga.com>
Sun, 24 Oct 2010 18:51:20 +0000 (11:51 -0700)
README.markdown
manifest_parser.h
ninja_test.cc

index da79400..5a83e79 100644 (file)
@@ -43,10 +43,13 @@ project, while `rule` statements describe how to generate the files
 within a given edge of the graph.
 
 ### Rules
-Here's an example of a simple rule for compiling C code.
+
+Rules begin with the `rule` keyword and a name for the rule, followed
+by an indented set of `key = val` lines.  Here's an example of a
+simple rule for compiling C code.
 
     rule cc
-    command gcc -c $in -o $out
+      command = gcc -c $in -o $out
 
 This declares a new rule named `cc`, along with the command to run.
 `Variables` begin with a `$` and are described more fully later, but
@@ -75,7 +78,7 @@ build files readable (debuggable), Ninja supports declaring bindings
 Can be used in a rule like this:
 
     rule cc
-    command gcc $cflags -c $in -o $out
+      command = gcc $cflags -c $in -o $out
 
 Variables might better be called "bindings", in that a given variable
 cannot be changed, only shadowed.  Within a larger Ninja project,
index f0ebd5c..a46f16c 100644 (file)
@@ -7,7 +7,6 @@ struct Token {
     NONE,
     IDENT,
     RULE,
-    COMMAND,
     BUILD,
     NEWLINE,
     EQUALS,
@@ -23,7 +22,6 @@ struct Token {
     switch (type_) {
       case IDENT:   return "'" + extra_ + "'";
       case RULE:    return "'rule'";
-      case COMMAND: return "'command'";
       case BUILD:   return "'build'";
       case NEWLINE: return "newline";
       case EQUALS:  return "'='";
@@ -190,8 +188,6 @@ Token::Type Parser::PeekToken() {
     }
     if (token_.extra_ == "rule")
       token_.type_ = Token::RULE;
-    else if (token_.extra_ == "command")
-      token_.type_ = Token::COMMAND;
     else if (token_.extra_ == "build")
       token_.type_ = Token::BUILD;
     else
@@ -228,7 +224,7 @@ struct ManifestParser {
   bool Parse(const string& input, string* err);
 
   bool ParseRule(string* err);
-  bool ParseLet(string* err);
+  bool ParseLet(string* key, string* val, string* err);
   bool ParseEdge(string* err);
 
   string ExpandFile(const string& file);
@@ -276,10 +272,19 @@ bool ManifestParser::Parse(const string& input, string* err) {
         if (!ParseEdge(err))
           return false;
         break;
-      case Token::IDENT:
-        if (!ParseLet(err))
+      case Token::IDENT: {
+        string name, value;
+        if (!ParseLet(&name, &value, err))
           return false;
+
+        state_->AddBinding(name, value);
+        if (name == "builddir") {
+          builddir_ = value;
+          if (!builddir_.empty() && builddir_[builddir_.size() - 1] != '/')
+            builddir_.push_back('/');
+        }
         break;
+      }
       case Token::TEOF:
         continue;
       default:
@@ -304,39 +309,33 @@ bool ManifestParser::ParseRule(string* err) {
   if (parser_.PeekToken() == Token::INDENT) {
     parser_.ConsumeToken();
 
-    if (!parser_.ExpectToken(Token::COMMAND, err))
-      return false;
-    if (!parser_.ReadToNewline(&command, err))
-      return false;
+    while (parser_.PeekToken() != Token::OUTDENT) {
+      string key, val;
+      if (!ParseLet(&key, &val, err))
+        return false;
 
-    if (!parser_.ExpectToken(Token::OUTDENT, err))
-      return false;
+      if (key != "command") {
+        *err = "expected 'command'";
+        return false;
+      }
+      command = val;
+    }
+    parser_.ConsumeToken();
   }
 
-  state_->AddRule(name, command);
+  if (!command.empty())
+    state_->AddRule(name, command);
 
   return true;
 }
 
-bool ManifestParser::ParseLet(string* err) {
-  string name;
-  if (!parser_.ReadIdent(&name))
+bool ManifestParser::ParseLet(string* name, string* value, string* err) {
+  if (!parser_.ReadIdent(name))
     return parser_.Error("expected variable name", err);
-
   if (!parser_.ExpectToken(Token::EQUALS, err))
     return false;
-
-  string value;
-  if (!parser_.ReadToNewline(&value, err))
+  if (!parser_.ReadToNewline(value, err))
     return false;
-
-  state_->AddBinding(name, value);
-  if (name == "builddir") {
-    builddir_ = value;
-    if (!builddir_.empty() && builddir_[builddir_.size() - 1] != '/')
-      builddir_.push_back('/');
-  }
-
   return true;
 }
 
index 2c1c4d5..b4b6665 100644 (file)
@@ -22,10 +22,10 @@ TEST(Parser, Rules) {
   State state;
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state,
 "rule cat\n"
-"  command cat @in > $out\n"
+"  command cat @in > $out\n"
 "\n"
 "rule date\n"
-"  command date > $out\n"
+"  command date > $out\n"
 "\n"
 "build result: cat in_1.cc in-2.O\n"));
 
@@ -39,7 +39,7 @@ TEST(Parser, Variables) {
   State state;
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state,
 "rule link\n"
-"  command ld $extra $with_under -o $out @in\n"
+"  command ld $extra $with_under -o $out @in\n"
 "\n"
 "extra = -pthread\n"
 "with_under = -under\n"
@@ -54,7 +54,7 @@ TEST(Parser, Continuation) {
   State state;
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state,
 "rule link\n"
-"  command foo bar \\\n"
+"  command foo bar \\\n"
 "    baz\n"
 "\n"
 "build a: link c \\\n"
@@ -114,7 +114,7 @@ TEST(Parser, Errors) {
   {
     ManifestParser parser(&state);
     string err;
-    EXPECT_FALSE(parser.Parse("rule cat\n  command cat ok\n"
+    EXPECT_FALSE(parser.Parse("rule cat\n  command cat ok\n"
                               "build x: cat \\\n :\n",
                               &err));
     EXPECT_EQ("line 4, col 2: expected newline, got ':'", err);
@@ -126,7 +126,7 @@ TEST(Parser, BuildDir) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state,
 "builddir = out\n"
 "rule cat\n"
-"  command cat @in > $out\n"
+"  command cat @in > $out\n"
 "build @bin: cat @a.o\n"
 "build @a.o: cat a.cc\n"));
   state.stat_cache()->Dump();
@@ -179,7 +179,7 @@ struct StateTestWithBuiltinRules : public testing::Test {
   StateTestWithBuiltinRules() {
     AssertParse(&state_,
 "rule cat\n"
-"  command cat @in > $out\n");
+"  command cat @in > $out\n");
   }
 
   Node* GetNode(const string& path) {