From 99dfd1924b8a7bb5bdd0df132416ff8de6c382fa Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 10 Nov 2017 20:41:51 +0200 Subject: Relax substitution requirements, add alternative symbol in version .in support Give this (legacy) version.h.in: Can now do: h{version}: in{version} file{$src_root/manifest} h{version}: in.symbol = '@' h{version}: FOO = $project.version --- build2/version/init.cxx | 10 +++++++ build2/version/module.hxx | 2 ++ build2/version/rule.cxx | 76 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/build2/version/init.cxx b/build2/version/init.cxx index 7d08642..c0ce4cd 100644 --- a/build2/version/init.cxx +++ b/build2/version/init.cxx @@ -272,6 +272,16 @@ namespace build2 } } + // Enter variables. Note: some overridable, some not. + // + auto& vp (var_pool.rw (rs)); + + // Alternative variable substitution symbols. + // + // @@ Note: should be moved to the 'in' module once we have it. + // + m.in_symbol = &vp.insert ("in.symbol"); + // Register rules. // { diff --git a/build2/version/module.hxx b/build2/version/module.hxx index 45b1a47..4f9f66d 100644 --- a/build2/version/module.hxx +++ b/build2/version/module.hxx @@ -27,6 +27,8 @@ namespace build2 butl::standard_version version; dependency_constraints dependencies; + const variable* in_symbol = nullptr; + module (butl::standard_version v, dependency_constraints d) : version (move (v)), dependencies (move (d)) {} }; diff --git a/build2/version/rule.cxx b/build2/version/rule.cxx index ceeb003..476f327 100644 --- a/build2/version/rule.cxx +++ b/build2/version/rule.cxx @@ -231,6 +231,18 @@ namespace build2 const scope& rs (t.root_scope ()); const module& m (*rs.modules.lookup (module::name)); + // The substitution symbol can be overridden with the in.symbol + // variable. + // + char sym ('$'); + if (const string* s = cast_null (t[m.in_symbol])) + { + if (s->size () == 1) + sym = s->front (); + else + fail << "invalid substitution symbol '" << *s << "'"; + } + // Determine if anything needs to be updated. // timestamp mt (t.load_mtime ()); @@ -250,9 +262,15 @@ namespace build2 // First should come the rule name/version. // - if (dd.expect ("version.in 1") != nullptr) + if (dd.expect ("version.in 2") != nullptr) l4 ([&]{trace << "rule mismatch forcing update of " << t;}); + // Then the substitution symbol. + // + if (dd.expect (string (1, sym)) != nullptr) + l4 ([&]{trace << "substitution symbol mismatch forcing update of" + << t;}); + // Then the .in file. // if (dd.expect (i.path ()) != nullptr) @@ -268,6 +286,10 @@ namespace build2 if (dd.writing () || dd.mtime () > mt) update = true; + //@@ TODO: what if one of the substituted non-version values is + // changes. See how we handle this in the .in module. In fact, + // it feels like this should be an extended version of in. + dd.close (); } @@ -279,19 +301,29 @@ namespace build2 const string& proj (cast (rs.vars[var_project])); // Perform substitutions for the project itself (normally the version.* - // variables but we allow anything set on the root scope). + // variables but we allow anything set on the target and up). // - auto subst_self = [&rs, &t] (const location& l, const string& s) + auto subst_self = [&t] (const location& l, const string& s) -> string { - if (lookup x = rs.vars[s]) + if (lookup x = t[s]) { - // Call the string() function to convert the value. - // value v (*x); - return convert ( - functions.call ( - t.base_scope (), "string", vector_view (&v, 1), l)); + // For typed values call the string() function to convert it. + // + try + { + return convert ( + v.type == nullptr + ? move (v) + : functions.call ( + t.base_scope (), "string", vector_view (&v, 1), l)); + } + catch (const invalid_argument& e) + { + fail (l) << e << + info << "while substituting '" << s << "'" << endf; + } } else fail (l) << "undefined project variable '" << s << "'" << endf; @@ -505,7 +537,7 @@ namespace build2 { d = 1; - if (s[b] != '$') + if (s[b] != sym) continue; // Find the other end. @@ -513,9 +545,9 @@ namespace build2 size_t e (b + 1); for (; e != (n = s.size ()); ++e) { - if (s[e] == '$') + if (s[e] == sym) { - if (e + 1 != n && s[e + 1] == '$') // Escape. + if (e + 1 != n && s[e + 1] == sym) // Escape. s.erase (e, 1); // Keep one, erase the other. else break; @@ -523,7 +555,7 @@ namespace build2 } if (e == n) - fail (l) << "unterminated '$'"; + fail (l) << "unterminated '" << sym << "'"; if (e - b == 1) // Escape. { @@ -534,14 +566,22 @@ namespace build2 // We have a substition with b pointing to the opening $ and e -- // to the closing. Split it into the package name and the trailer. // + // We used to bail if there is no package component but now we + // treat it the same as project. This can be useful when trying + // to reuse existing .in files (e.g., from autoconf, etc). + // + string sn, st; size_t p (s.find ('.', b + 1)); - if (p == string::npos || p > e) - fail (l) << "invalid substitution: missing package name"; + if (p != string::npos && p < e) + { + sn.assign (s, b + 1, p - b - 1); + st.assign (s, p + 1, e - p - 1); + } + else + st.assign (s, b + 1, e - b - 1); - string sn (s, b + 1, p - b - 1); - string st (s, p + 1, e - p - 1); - string sr (sn == proj + string sr (sn.empty () || sn == proj ? subst_self (l, st) : subst_dep (l, sn, st)); -- cgit v1.1