diff options
Diffstat (limited to 'build')
-rw-r--r-- | build/parser | 4 | ||||
-rw-r--r-- | build/parser.cxx | 83 | ||||
-rw-r--r-- | build/target.cxx | 56 |
3 files changed, 115 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 (); |