allow implicit deps
authorEvan Martin <martine@danga.com>
Sun, 23 Jan 2011 04:51:52 +0000 (20:51 -0800)
committerEvan Martin <martine@danga.com>
Sun, 23 Jan 2011 04:51:52 +0000 (20:51 -0800)
build.ninja
manual.asciidoc
src/build_test.cc
src/parsers.cc
src/parsers.h
src/parsers_test.cc

index 1a3c6b3..f49a306 100644 (file)
@@ -80,8 +80,8 @@ rule asciidoc
   description = ASCIIDOC $in
 
 build manual.html: asciidoc manual.asciidoc
-build doc: phony | manual.html
+build doc: phony || manual.html
 
 # Use the built-in phony rule and an order-only dependency
 # to make building "all" build all targets.
-build all: phony | ninja ninja_test graph.png doc
+build all: phony || ninja ninja_test graph.png doc
index 258782c..1f98b6f 100644 (file)
@@ -219,7 +219,9 @@ A file is a series of declarations.  A declaration can be one of:
 
 2. A build edge, which looks like +build _output1_ _output2_:
    _rulename_ _input1_ _input2_+. +
-   Order-only dependencies may be tacked on the end with +_|
+   Implicit dependencies may be tacked on the end with +_|
+   _dependency1_ _dependency2_+.
+   Order-only dependencies may be tacked on the end with +_||
    _dependency1_ _dependency2_+.
 
 3. Variable declarations, which look like +_variable_ = _value_+.
@@ -273,22 +275,33 @@ Build dependencies
 ~~~~~~~~~~~~~~~~~~
 There are three types of build dependencies which are subtly different.
 
-1. Explicit dependencies, as listed in a build line.  These are
+1. _Explicit dependencies_, as listed in a build line.  These are
    available as the `$in` variable in the rule.  Changes in these files
    cause the output to be rebuilt; if these file are missing and
    ninja doesn't know how to build them, the build is aborted.
++
+This is the standard form of dependency to be used for e.g. the
+source file of a compile command.
 
-2. Implicit dependencies, as picked up from a `depfile` attribute on
-   a rule.  Changes in these files cause the output to be rebuilt; if
-   they are missing, they are just skipped.
-
-3. Order-only dependencies, expressed with the syntax `| dep1 dep2` on
-   the end of a build line.  When these are missing, the output is not
-   rebuilt until they are built, but once they are available further
-   changes to the files do not affect the output.  Order-only
-   dependencies can be useful for bootstrapping implicit dependencies:
-   for example, to generate a header file before starting a subsequent
-   compilation step.
+2. _Implicit dependencies_, either as picked up from a `depfile`
+   attribute on a rule or from the syntax +| _dep1_ _dep2_+ on the end of
+   a build line.  Changes in these files cause the output to be
+   rebuilt; if they are missing, they are just skipped.
++
+This is for expressing dependencies that don't show up on the
+command line of the command; for example, for a rule that runs a
+script, the script should be an implicit dependency.
+
+3. _Order-only dependencies_, expressed with the syntax +|| _dep1_
+   _dep2_+ on the end of a build line.  When these are missing, the
+   output is not rebuilt until they are built, but once they are
+   available further changes to the files do not affect the output.
++
+Order-only dependencies can be useful for bootstrapping dependencies
+that are only discovered during build time: for example, to generate a
+header file before starting a subsequent compilation step.  (Once the
+header is used in compilation, a generated dependency file will then
+express the implicit dependency.)
 
 Evaluation and scoping
 ~~~~~~~~~~~~~~~~~~~~~~
index 2588174..71c950b 100644 (file)
@@ -469,7 +469,7 @@ TEST_F(BuildTest, OrderOnlyDeps) {
   string err;
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
 "rule cc\n  command = cc $in\n  depfile = $out.d\n"
-"build foo.o: cc foo.c | otherfile\n"));
+"build foo.o: cc foo.c || otherfile\n"));
   fs_.Create("foo.c", now_, "");
   fs_.Create("otherfile", now_, "");
   fs_.Create("foo.o.d", now_, "foo.o: blah.h bar.h\n");
index 7b810fe..f309854 100644 (file)
@@ -20,6 +20,7 @@ string Token::AsString() const {
   case EQUALS:   return "'='";
   case COLON:    return "':'";
   case PIPE:     return "'|'";
+  case PIPE2:    return "'||'";
   case TEOF:     return "eof";
   case INDENT:   return "indenting in";
   case OUTDENT:  return "indenting out";
@@ -181,8 +182,13 @@ Token::Type Tokenizer::PeekToken() {
     token_.type_ = Token::EQUALS;
     ++cur_;
   } else if (*cur_ == '|') {
-    token_.type_ = Token::PIPE;
-    ++cur_;
+    if (cur_ + 1 < end_ && cur_[1] == '|') {
+      token_.type_ = Token::PIPE2;
+      cur_ += 2;
+    } else {
+      token_.type_ = Token::PIPE;
+      ++cur_;
+    }
   } else if (*cur_ == '\n') {
     token_.type_ = Token::NEWLINE;
     ++cur_;
@@ -413,7 +419,7 @@ bool ManifestParser::ParseEdge(string* err) {
   }
 
   // Add all order-only deps, counting how many as we go.
-  int order_only = 0;
+  int implicit = 0;
   if (tokenizer_.PeekToken() == Token::PIPE) {
     tokenizer_.ConsumeToken();
     for (;;) {
@@ -421,6 +427,19 @@ bool ManifestParser::ParseEdge(string* err) {
       if (!tokenizer_.ReadIdent(&in))
         break;
       ins.push_back(in);
+      ++implicit;
+    }
+  }
+
+  // Add all order-only deps, counting how many as we go.
+  int order_only = 0;
+  if (tokenizer_.PeekToken() == Token::PIPE2) {
+    tokenizer_.ConsumeToken();
+    for (;;) {
+      string in;
+      if (!tokenizer_.ReadIdent(&in))
+        break;
+      ins.push_back(in);
       ++order_only;
     }
   }
@@ -467,6 +486,7 @@ bool ManifestParser::ParseEdge(string* err) {
     state_->AddIn(edge, *i);
   for (vector<string>::iterator i = outs.begin(); i != outs.end(); ++i)
     state_->AddOut(edge, *i);
+  edge->implicit_deps_ = implicit;
   edge->order_only_deps_ = order_only;
 
   return true;
index ec9a677..55417cd 100644 (file)
@@ -21,6 +21,7 @@ struct Token {
     EQUALS,
     COLON,
     PIPE,
+    PIPE2,
     INDENT,
     OUTDENT,
     TEOF
index 82ef8f3..1ba34da 100644 (file)
@@ -289,10 +289,22 @@ TEST_F(ParserTest, Include) {
   EXPECT_EQ("inner", state.bindings_.LookupVariable("var"));
 }
 
-TEST_F(ParserTest, OrderOnly) {
+TEST_F(ParserTest, Implicit) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(
 "rule cat\n  command = cat $in > $out\n"
 "build foo: cat bar | baz\n"));
+
+  Edge* edge = state.LookupNode("foo")->in_edge_;
+  ASSERT_TRUE(edge->is_implicit(1));
+}
+
+TEST_F(ParserTest, OrderOnly) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n  command = cat $in > $out\n"
+"build foo: cat bar || baz\n"));
+
+  Edge* edge = state.LookupNode("foo")->in_edge_;
+  ASSERT_TRUE(edge->is_order_only(1));
 }
 
 TEST(MakefileParser, Basic) {