the full command or its description; if a command fails, the full command
line will always be printed before the command's output.
-Special variables
-~~~~~~~~~~~~~~~~~
-`builddir` is a directory for intermediate build output. (The name
-comes from autoconf.) It is special in a few ways:
-
-1. It gets a shorter alias: `@`.
-2. It is implicitly a directory and has a `/` appended if needed.
-3. It may be used in the filenames of a `build` line.
-
-You must still be explicit in your rules. In the following
-example, the files prefixed with `@` will end up in the `out/`
-subdirectory.
-
-----------------
-builddir = out
-build @intermediate_file: combine @generated_file source_file
-
-# Equivalent rule:
-# build out/intermediate_file: combine out/generated_file source_file
-----------------
-
-XXX I hacked in a special `$root` only understood in builddir -- fix
-the semantics here, is this path relative or absolute?
-
Evaluation and scoping
~~~~~~~~~~~~~~~~~~~~~~
XXX talk about where variables live, nested scopes etc
State state;
RealFileReader file_reader;
ManifestParser parser(&state, &file_reader);
- parser.set_root(cwd);
string err;
if (!parser.Load(input_file, &err)) {
fprintf(stderr, "error loading '%s': %s\n", input_file, err.c_str());
('a' <= c && c <= 'z') ||
('+' <= c && c <= '9') || // +,-./ and numbers
('A' <= c && c <= 'Z') ||
- (c == '_') || (c == '@') || (c == '$');
+ (c == '_') || (c == '$');
}
bool Tokenizer::ExpectToken(Token::Type expected, string* err) {
string name, value;
if (!ParseLet(&name, &value, err))
return false;
-
env_->AddBinding(name, value);
- if (name == "builddir") {
- builddir_ = value;
- if (builddir_.substr(0, 5) == "$root") {
- builddir_ = root_ + builddir_.substr(5);
- }
- if (!builddir_.empty() && builddir_[builddir_.size() - 1] != '/')
- builddir_.push_back('/');
- }
break;
}
case Token::TEOF:
if (!tokenizer_.ReadToNewline(value, err))
return false;
- // Do @ -> builddir substitution.
- size_t ofs;
- while ((ofs = value->find('@')) != string::npos) {
- value->replace(ofs, 1, builddir_);
- ofs += builddir_.size();
- }
-
return true;
}
string eval_err;
if (!eval.Parse(*i, &eval_err))
return tokenizer_.Error(eval_err, err);
- *i = ExpandFile(eval.Evaluate(env));
+ *i = CanonicalizePath(eval.Evaluate(env));
}
}
return true;
}
-
-string ManifestParser::ExpandFile(const string& file) {
- string out = file;
- if (!file.empty() && file[0] == '@')
- out = builddir_ + file.substr(1);
- return CanonicalizePath(out);
-}
-
};
ManifestParser(State* state, FileReader* file_reader);
- void set_root(const string& root) { root_ = root; }
bool Load(const string& filename, string* err);
bool Parse(const string& input, string* err);
bool ParseEdge(string* err);
bool ParseSubNinja(string* err);
- string ExpandFile(const string& file);
-
State* state_;
BindingEnv* env_;
FileReader* file_reader_;
Tokenizer tokenizer_;
string builddir_;
- string root_; // Absolute path to root ninja file.
};
#endif // NINJA_PARSERS_H_
}
}
-TEST_F(ParserTest, BuildDir) {
- ASSERT_NO_FATAL_FAILURE(AssertParse(
-"default_test = @foo\n"
-"builddir = out\n"
-"rule cat\n"
-" command = cat @otherfile $in > $out\n"
-"build @bin: cat @a.o\n"
-"build @a.o: cat a.cc\n"));
- EXPECT_EQ("foo", state.bindings_.LookupVariable("default_test"));
- ASSERT_TRUE(state.LookupNode("out/a.o"));
- const Rule* rule = state.LookupRule("cat");
- ASSERT_TRUE(rule);
- EXPECT_EQ("cat out/otherfile $in > $out", rule->command_.unparsed());
-}
-
-TEST_F(ParserTest, BuildDirRoot) {
- ManifestParser parser(&state, this);
- parser.set_root("/root_test");
- string err;
- ASSERT_TRUE(parser.Parse(
-"builddir = $root/out\n"
-"rule cat\n"
-" command = cat @otherfile $in > $out\n"
-"build @a.o: cat a.cc\n", &err));
- ASSERT_EQ("", err);
- ASSERT_TRUE(state.LookupNode("/root_test/out/a.o"));
-}
-
TEST_F(ParserTest, SubNinja) {
files_["test.ninja"] =
"var = inner\n"
- "build @inner: varref\n";
+ "build $builddir/inner: varref\n";
ASSERT_NO_FATAL_FAILURE(AssertParse(
"builddir = some_dir/\n"
"rule varref\n"
" command = varref $var\n"
"var = outer\n"
-"build @outer: varref\n"
+"build $builddir/outer: varref\n"
"subninja test.ninja\n"
-"build @outer2: varref\n"));
+"build $builddir/outer2: varref\n"));
ASSERT_EQ(1, files_read_.size());
EXPECT_EQ("test.ninja", files_read_[0]);