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
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,
NONE,
IDENT,
RULE,
- COMMAND,
BUILD,
NEWLINE,
EQUALS,
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 "'='";
}
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
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);
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:
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;
}
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"));
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"
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"
{
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);
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();
StateTestWithBuiltinRules() {
AssertParse(&state_,
"rule cat\n"
-" command cat @in > $out\n");
+" command = cat @in > $out\n");
}
Node* GetNode(const string& path) {