diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-30 14:31:40 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-30 14:36:18 +0200 |
commit | b2374e3174e13682fcfa3ffe3fc62f2fd161a7cc (patch) | |
tree | bd39bda41e2ee352b73d80b84153ba95b80e84c4 | |
parent | 1fb88fce9681e88f4140457cfe00a998d9c2588d (diff) |
Implement target type/pattern-specific variables
For example:
cxx{*-options}: dist = true
1. Only single '*' wildcard is supported, matches 0 or more characters.
2. If target type is not specified, it defaults to any target.
3. Appending (+=) is not allowed.
4. The value is expanded immediately in the context of the scope.
5. The more specific pattern (i.e., with the shortest "stem") is preferred.
If the stem has the same length, then the last defined (but not redefined)
pattern is used. This will probably have to change to become an error.
See tests/variable/type-pattern for more examples.
-rw-r--r-- | build/parser | 4 | ||||
-rw-r--r-- | build/parser.cxx | 83 | ||||
-rw-r--r-- | build/target.cxx | 56 | ||||
-rw-r--r-- | tests/variable/type-pattern/buildfile | 33 |
4 files changed, 148 insertions, 28 deletions
diff --git a/build/parser b/build/parser index 04066ff..e37e68a 100644 --- a/build/parser +++ b/build/parser @@ -25,6 +25,7 @@ namespace build { public: typedef build::names names_type; + typedef build::variable variable_type; parser (): fail (&path_) {} @@ -77,6 +78,9 @@ namespace build variable_name (names_type&&, const location&); names_type + variable_value (token&, token_type&, const variable_type&); + + names_type eval (token&, token_type&); // If chunk is true, then parse the smallest but complete, name-wise, diff --git a/build/parser.cxx b/build/parser.cxx index 4ce9fba..bc6caa9 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -279,7 +279,7 @@ namespace build // if (tt == type::equal || tt == type::plus_equal) { - string var (variable_name (move (pns), ploc)); + string v (variable_name (move (pns), ploc)); // Enter the target/scope and set it as current. // @@ -307,19 +307,61 @@ namespace build scope* ocs (scope_); switch_scope (p); - variable (t, tt, move (var), tt); + variable (t, tt, move (v), tt); scope_ = ocs; root_ = ors; } else { - target* ot (target_); - target_ = &enter_target (move (n)); - - variable (t, tt, move (var), tt); + // Figure out if this is a target or type/pattern specific + // variable. + // + size_t p (n.value.find ('*')); - target_ = ot; + if (p == string::npos) + { + target* ot (target_); + target_ = &enter_target (move (n)); + variable (t, tt, move (v), tt); + target_ = ot; + } + else + { + // See tests/variable/type-pattern. + // + if (!n.dir.empty ()) + fail (nloc) << "directory in target type/pattern " << n; + + if (n.value.find ('*', p + 1) != string::npos) + fail (nloc) << "multiple wildcards in target type/pattern " + << n; + + // Resolve target type. If none is specified, use the root + // of the hierarchy. + // + const target_type* ti ( + n.untyped () + ? &target::static_type + : scope_->find_target_type (n.type.c_str ())); + + if (ti == nullptr) + fail (nloc) << "unknown target type " << n.type; + + if (tt == type::plus_equal) + fail (t) << "append to target type/pattern-specific " + << "variable " << v; + + const auto& var (variable_pool.find (move (v))); + + // Note: expand variables in the value in the context of + // the scope. + // + names_type vns (variable_value (t, tt, var)); + value& val ( + scope_->target_vars[*ti][move (n.value)].assign (var).first); + val.assign (move (vns), var); + } } } // Dependency declaration. @@ -797,19 +839,10 @@ namespace build void parser:: variable (token& t, token_type& tt, string name, token_type kind) { - bool assign (kind == type::equal); const auto& var (variable_pool.find (move (name))); + names_type vns (variable_value (t, tt, var)); - if (var.pairs != '\0') - lexer_->mode (lexer_mode::pairs, var.pairs); - else - lexer_->mode (lexer_mode::value); - - next (t, tt); - names_type vns (tt != type::newline && tt != type::eos - ? names (t, tt) - : names_type ()); - if (assign) + if (kind == type::equal) { value& v (target_ != nullptr ? target_->assign (var) @@ -825,6 +858,20 @@ namespace build } } + names parser:: + variable_value (token& t, token_type& tt, const variable_type& var) + { + if (var.pairs != '\0') + lexer_->mode (lexer_mode::pairs, var.pairs); + else + lexer_->mode (lexer_mode::value); + + next (t, tt); + return (tt != type::newline && tt != type::eos + ? names (t, tt) + : names_type ()); + } + parser::names_type parser:: eval (token& t, token_type& tt) { diff --git a/build/target.cxx b/build/target.cxx index 9b20273..22c9ad4 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -137,7 +137,7 @@ namespace build { if (!s->target_vars.empty ()) { - auto find_specific = [&var, s] (const target& t) -> result + auto find_specific = [this, &var, s] (const target& t) -> result { // Search across target type hierarchy. // @@ -148,15 +148,51 @@ namespace build if (i == s->target_vars.end ()) continue; - //@@ TODO: match pattern. For now, we only handle '*'. - - auto j (i->second.find ("*")); - - if (j == i->second.end ()) - continue; - - if (auto p = j->second.find (var)) - return result (p, &j->second); + // Try to match the pattern, starting from the longest values + // so that the more "specific" patterns (i.e., those that cover + // fewer characters with the wildcard) take precedence. See + // tests/variable/type-pattern. + // + const variable_pattern_map& m (i->second); + + for (auto j (m.rbegin ()); j != m.rend (); ++j) + { + const string& p (j->first); + + size_t nn (name.size ()); + size_t pn (p.size ()); + + if (nn < pn - 1) // One for '*'. + continue; + + size_t w (p.find ('*')); + assert (w != string::npos); + + // Compare prefix. + // + if (w != 0 && + name.compare (0, w, p, 0, w) != 0) + continue; + + ++w; // First suffix character. + pn -= w; // Suffix length. + + // Compare suffix. + // + if (pn != 0 && + name.compare (nn - pn, pn, p, w, pn) != 0) + continue; + + // Ok, this pattern matches. But is there a variable? + // + if (const value* v = j->second.find (var)) + { + //@@ TODO: should we detect ambiguity? 'foo-*' '*-foo' and + // 'foo-foo'? Right now the last defined will be used. + // + return result (v, &j->second); + } + } } return result (); diff --git a/tests/variable/type-pattern/buildfile b/tests/variable/type-pattern/buildfile new file mode 100644 index 0000000..8c353a5 --- /dev/null +++ b/tests/variable/type-pattern/buildfile @@ -0,0 +1,33 @@ +#dir/foo{*}: x = y # directory +#foo{*.*}: x = y # multiple wildcards +#foo{*}: x = y # unknown target type +#file{*}: x += y # append + +# Use --verbose 6 to examine. +# + +dir{*}: x = y + +x = z +dir{*-foo}: x = $x # 'z' + +x = G +file{*-foo}: x = x +file{xfoo}: x = $x # 'G' +file{-foo}: x = $x # 'x' +file{x-foo}: x = $x # 'x' +file{bar-*-foo}: x = X +file{bar-x}: x = $x # 'G' +file{bar--foo}: x = $x # 'X' +file{bar-x-foo}: x = $x # 'X' + +file{*-fox}: x = 1 +file{fox-*}: x = 2 +file{fox-fox}: x = $x # '2' +file{*-fox}: x = 3 +file{fox-x-fox}: x = $x # still '2'! + +*-foz: x = z # any target +file{x-foz}: x = $x # 'z' + +./: |