aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-11-16 14:00:07 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-11-16 14:00:07 +0200
commit655121741560d62c1ae82c13a9d2aad18f130603 (patch)
tree6b14cd73073a0f2d1ad3ad46cc8d157a838beba9
parent17609d9831e592f5985ed1bfb1ef59f712025ae9 (diff)
Implement support for dependency chains
Now instead of: ./: exe{foo} exe{foo}: cxx{*} We can write: ./: exe{foo}: cxx{*} Or even: ./: exe{foo}: libue{foo}: cxx{*} This can be combined with prerequisite-specific variables (which naturally only apply to the last set of prerequisites in the chain): ./: exe{foo}: libue{foo}: bin.whole = false
-rw-r--r--build2/parser.cxx303
-rw-r--r--build2/parser.hxx9
-rw-r--r--tests/dependency/chain/buildfile5
-rw-r--r--tests/dependency/chain/testscript39
-rw-r--r--tests/variable/prerequisite-specific/testscript13
5 files changed, 227 insertions, 142 deletions
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 5348fbe..1658623 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -509,8 +509,9 @@ namespace build2
// prerequisites. Fall through.
}
- // Dependency declaration (including prerequisite-specific variable
- // assignment) or target-specific variable assignment.
+ // Target-specific variable assignment or dependency declaration,
+ // including a dependency chain and/or prerequisite-specific variable
+ // assignment.
//
if (at.first)
@@ -704,8 +705,8 @@ namespace build2
rg.play (); // Replay.
}
}
- // Dependency declaration potentially followed by prerequisite-
- // specific variable assignment).
+ // Dependency declaration potentially followed by a chain and/or
+ // a prerequisite-specific variable assignment.
//
else
{
@@ -714,138 +715,7 @@ namespace build2
else
attributes_pop ();
- // First enter all the targets (normally we will have just one).
- //
- small_vector<reference_wrapper<target>, 1> tgs;
-
- for (auto i (ns.begin ()), e (ns.end ()); i != e; ++i)
- {
- name& n (*i);
-
- if (n.qualified ())
- fail (nloc) << "project name in target " << n;
-
- name o (n.pair ? move (*++i) : name ());
- enter_target tg (*this, move (n), move (o), false, nloc, trace);
-
- if (default_target_ == nullptr)
- default_target_ = target_;
-
- target_->prerequisites_state_.store (2, memory_order_relaxed);
- target_->prerequisites_.reserve (pns.size ());
- tgs.push_back (*target_);
- }
-
- // Now enter each prerequisite into each target.
- //
- for (auto& pn: pns)
- {
- auto rp (scope_->find_target_type (pn, ploc));
- const target_type* tt (rp.first);
- optional<string>& e (rp.second);
-
- if (tt == nullptr)
- fail (ploc) << "unknown target type " << pn.type;
-
- // Current dir collapses to an empty one.
- //
- if (!pn.dir.empty ())
- pn.dir.normalize (false, true);
-
- // @@ OUT: for now we assume the prerequisite's out is
- // undetermined. The only way to specify an src prerequisite
- // will be with the explicit @-syntax.
- //
- // Perhaps use @file{foo} as a way to specify it is in the out
- // tree, e.g., to suppress any src searches? The issue is what
- // to use for such a special indicator. Also, one can easily and
- // natually suppress any searches by specifying the absolute
- // path.
- //
- prerequisite p (pn.proj,
- *tt,
- move (pn.dir),
- dir_path (),
- move (pn.value),
- move (e),
- *scope_);
-
- for (auto i (tgs.begin ()), e (tgs.end ()); i != e; )
- {
- // Move last prerequisite (which will normally be the only
- // one).
- //
- target& t (*i);
- t.prerequisites_.push_back (
- ++i == e
- ? move (p)
- : prerequisite (p, memory_order_relaxed)); // Serial
- }
- }
-
- // Do we have prerequisite-specific variable assignment?
- //
- if (tt == type::colon)
- {
- // What should we do if there are no prerequisites (for example,
- // because of an empty wildcard result)? We can fail or we can
- // ignore. In most cases, however, this is probably an error
- // (for example, forgetting to checkout a git submodule) so
- // let's not confuse the user and fail (one can always handle
- // the optional prerequisites case with a variable and an if).
- //
- if (pns.empty ())
- fail (ploc) << "no prerequisites in prerequisite-specific "
- << "variable assignment";
-
- // Set the variable in the last pns.size() prerequisites of each
- // target. This code is similar to target-specific case above.
- //
- // @@ TODO prerequisite block (also target block above).
- //
- next (t, tt);
- attributes_push (t, tt);
-
- // @@ PAT: currently we pattern-expand prerequisite-specific
- // vars.
- //
- const location vloc (get_location (t));
- names vns (parse_names (t, tt, pattern_mode::expand));
-
- if (tt != type::assign &&
- tt != type::prepend &&
- tt != type::append)
- fail (t) << "expected assignment instead of " << t;
-
- type at (tt);
-
- const variable& var (parse_variable_name (move (vns), vloc));
- apply_variable_attributes (var);
-
- // We handle multiple targets and/or prerequisites by replaying
- // the tokens (see the target-specific case above for details).
- //
- replay_guard rg (*this, tgs.size () > 1 || pns.size () > 1);
-
- for (auto ti (tgs.begin ()), te (tgs.end ()); ti != te; )
- {
- target& tg (*ti);
- enter_target tgg (*this, tg);
-
- for (size_t pn (tg.prerequisites_.size ()),
- pi (pn - pns.size ()); pi != pn; )
- {
- enter_prerequisite pg (*this, tg.prerequisites_[pi]);
- parse_variable (t, tt, var, at);
-
- if (++pi != pn)
- rg.play (); // Replay.
- }
-
- if (++ti != te)
- rg.play (); // Replay.
- }
- }
+ parse_dependency (t, tt, move (ns), nloc, move (pns), ploc);
}
if (tt == type::newline)
@@ -998,6 +868,167 @@ namespace build2
}
void parser::
+ parse_dependency (token& t, token_type& tt,
+ names&& tns, const location& tloc, // Target names.
+ names&& pns, const location& ploc) // Prereq names.
+ {
+ tracer trace ("parser::parse_dependency", &path_);
+
+ // First enter all the targets (normally we will have just one).
+ //
+ small_vector<reference_wrapper<target>, 1> tgs;
+
+ for (auto i (tns.begin ()), e (tns.end ()); i != e; ++i)
+ {
+ name& n (*i);
+
+ if (n.qualified ())
+ fail (tloc) << "project name in target " << n;
+
+ name o (n.pair ? move (*++i) : name ());
+ enter_target tg (*this, move (n), move (o), false, tloc, trace);
+
+ if (default_target_ == nullptr)
+ default_target_ = target_;
+
+ target_->prerequisites_state_.store (2, memory_order_relaxed);
+ target_->prerequisites_.reserve (pns.size ());
+ tgs.push_back (*target_);
+ }
+
+ // Now enter each prerequisite into each target.
+ //
+ for (auto& pn: pns)
+ {
+ auto rp (scope_->find_target_type (pn, ploc));
+ const target_type* tt (rp.first);
+ optional<string>& e (rp.second);
+
+ if (tt == nullptr)
+ fail (ploc) << "unknown target type " << pn.type;
+
+ // Current dir collapses to an empty one.
+ //
+ if (!pn.dir.empty ())
+ pn.dir.normalize (false, true);
+
+ // @@ OUT: for now we assume the prerequisite's out is undetermined. The
+ // only way to specify an src prerequisite will be with the explicit
+ // @-syntax.
+ //
+ // Perhaps use @file{foo} as a way to specify it is in the out tree,
+ // e.g., to suppress any src searches? The issue is what to use for such
+ // a special indicator. Also, one can easily and natually suppress any
+ // searches by specifying the absolute path.
+ //
+ // Note: we cannot move values out of pn since we may need to pass them
+ // as targets in case of a chain (see below).
+ //
+ prerequisite p (pn.proj,
+ *tt,
+ pn.dir,
+ dir_path (),
+ pn.value,
+ move (e),
+ *scope_);
+
+ for (auto i (tgs.begin ()), e (tgs.end ()); i != e; )
+ {
+ // Move last prerequisite (which will normally be the only one).
+ //
+ target& t (*i);
+ t.prerequisites_.push_back (++i == e
+ ? move (p)
+ : prerequisite (p, memory_order_relaxed));
+ }
+ }
+
+ // Do we have a dependency chain and/or prerequisite-specific variable
+ // assignment?
+ //
+ if (tt != type::colon)
+ return;
+
+ // What should we do if there are no prerequisites (for example, because
+ // of an empty wildcard result)? We can fail or we can ignore. In most
+ // cases, however, this is probably an error (for example, forgetting to
+ // checkout a git submodule) so let's not confuse the user and fail (one
+ // can always handle the optional prerequisites case with a variable and
+ // an if).
+ //
+ if (pns.empty ())
+ fail (ploc) << "no prerequisites in dependency chain or prerequisite-"
+ << "specific variable assignment";
+
+ next (t, tt);
+ auto at (attributes_push (t, tt));
+
+ // @@ PAT: currently we pattern-expand prerequisite-specific vars.
+ //
+ const location loc (get_location (t));
+ names ns (tt != type::newline && tt != type::eos
+ ? parse_names (t, tt, pattern_mode::expand)
+ : names ());
+
+ // Prerequisite-specific variable assignment.
+ //
+ if (tt == type::assign || tt == type::prepend || tt == type::append)
+ {
+ // Set the variable in the last pns.size() prerequisites of each target.
+ // This code is similar to the target-specific variable case.
+ //
+ // @@ TODO prerequisite block (also target block above).
+ //
+ type at (tt);
+
+ const variable& var (parse_variable_name (move (ns), loc));
+ apply_variable_attributes (var);
+
+ // We handle multiple targets and/or prerequisites by replaying the
+ // tokens (see the target-specific case above for details).
+ //
+ replay_guard rg (*this, tgs.size () > 1 || pns.size () > 1);
+
+ for (auto ti (tgs.begin ()), te (tgs.end ()); ti != te; )
+ {
+ target& tg (*ti);
+ enter_target tgg (*this, tg);
+
+ for (size_t pn (tg.prerequisites_.size ()), pi (pn - pns.size ());
+ pi != pn; )
+ {
+ enter_prerequisite pg (*this, tg.prerequisites_[pi]);
+ parse_variable (t, tt, var, at);
+
+ if (++pi != pn)
+ rg.play (); // Replay.
+ }
+
+ if (++ti != te)
+ rg.play (); // Replay.
+ }
+ }
+ //
+ // Dependency chain.
+ //
+ else
+ {
+ if (at.first)
+ fail (at.second) << "attributes before prerequisites";
+ else
+ attributes_pop ();
+
+ // Note that we could have "pre-resolved" these prerequisites to actual
+ // targets or, at least, made they directories absolute. We don't do it
+ // for easy of documentation: with the current semantics we can just say
+ // that the dependency chain is equivalent to specifying each dependency
+ // separately.
+ //
+ parse_dependency (t, tt, move (pns), ploc, move (ns), loc);
+ }
+ }
+
+ void parser::
source (istream& is,
const path& p,
const location& loc,
diff --git a/build2/parser.hxx b/build2/parser.hxx
index dfb4c6b..582f925 100644
--- a/build2/parser.hxx
+++ b/build2/parser.hxx
@@ -67,15 +67,20 @@ namespace build2
// If one is true then parse a single (logical) line (logical means it
// can actually be several lines, e.g., an if-block). Return false if
- // nothing has been parsed (i.e., we are on the same token).
+ // nothing has been parsed (i.e., we are still on the same token).
//
- // Note that after this function returns, the token is the first token on
+ // Note that after this function returns, the token is the first token of
// the next line (or eos).
//
bool
parse_clause (token&, token_type&, bool one = false);
void
+ parse_dependency (token&, token_type&,
+ names&&, const location&,
+ names&&, const location&);
+
+ void
parse_assert (token&, token_type&);
void
diff --git a/tests/dependency/chain/buildfile b/tests/dependency/chain/buildfile
new file mode 100644
index 0000000..a27681e
--- /dev/null
+++ b/tests/dependency/chain/buildfile
@@ -0,0 +1,5 @@
+# file : tests/dependency/chain/buildfile
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: testscript $b
diff --git a/tests/dependency/chain/testscript b/tests/dependency/chain/testscript
new file mode 100644
index 0000000..09ea4a6
--- /dev/null
+++ b/tests/dependency/chain/testscript
@@ -0,0 +1,39 @@
+# file : tests/dependency/chain/testscript
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+.include ../../common.testscript
+
+: basic
+:
+$* <<EOI 2>>/~%EOE%
+./: dir{x}: dir{a}
+dump dir{x}
+EOI
+<stdin>:2:1: dump:
+% .+/dir\{x/\}: .+:dir\{a/\}%
+EOE
+
+: long
+:
+$* <<EOI 2>>/~%EOE%
+./: dir{x}: dir{y}: dir{a}
+dump dir{x} dir{y}
+EOI
+<stdin>:2:1: dump:
+% .+/dir\{x/\}: .+:dir\{y/\}%
+
+% .+/dir\{y/\}: .+:dir\{a/\}%
+EOE
+
+: multiple
+:
+$* <<EOI 2>>/~%EOE%
+./: dir{x} dir{y}: dir{a} dir{b}
+dump dir{x} dir{y}
+EOI
+<stdin>:2:1: dump:
+% .+/dir\{x/\}: .+:dir\{a/\} .+:dir\{b/\}%
+
+% .+/dir\{y/\}: .+:dir\{a/\} .+:dir\{b/\}%
+EOE
diff --git a/tests/variable/prerequisite-specific/testscript b/tests/variable/prerequisite-specific/testscript
index 1c7e7bd..ef14cfc 100644
--- a/tests/variable/prerequisite-specific/testscript
+++ b/tests/variable/prerequisite-specific/testscript
@@ -62,10 +62,15 @@ EOI
}
EOE
-: expect-assignment
+: chain
:
-$* <<EOI 2>>EOE != 0
-dir{x}: dir{a}:
+$* <<EOI 2>>/~%EOE%
+dir{x}: dir{y}: dir{a}: foo = FOO
+dump dir{y}
EOI
-<stdin>:1:16: error: expected name instead of <newline>
+<stdin>:2:1: dump:
+% .+/dir\{y/\}: .+:dir\{a/\}:%
+ {
+ foo = FOO
+ }
EOE