diff options
-rw-r--r-- | build2/version/init.cxx | 36 | ||||
-rw-r--r-- | build2/version/module.hxx | 3 | ||||
-rw-r--r-- | build2/version/rule.cxx | 82 |
3 files changed, 103 insertions, 18 deletions
diff --git a/build2/version/init.cxx b/build2/version/init.cxx index c0ce4cd..69d7401 100644 --- a/build2/version/init.cxx +++ b/build2/version/init.cxx @@ -272,15 +272,37 @@ namespace build2 } } - // Enter variables. Note: some overridable, some not. + // Enter variables. // - auto& vp (var_pool.rw (rs)); + { + 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<string> ("in.symbol"); + // @@ Note: these should be moved to the 'in' module once we have it. + // + + // Alternative variable substitution symbol. + // + m.in_symbol = &vp.insert<string> ("in.symbol"); + + // Substitution mode. Valid values are 'strict' (default) and 'lax'. + // In the strict mode every substitution symbol is expected to start a + // substitution with the double symbol (e.g., $$) serving as an + // escape sequence. + // + // In the lax mode a pair of substitution symbols is only treated as a + // substitution if what's between them looks like a build2 variable + // name (i.e., doesn't contain spaces, etc). Everything else, + // including unterminated substitution symbols is copied as is. Note + // also that in this mode the double symbol is not treated as an + // escape sequence. + // + // The lax mode is mostly useful when trying to reuse existing .in + // files, for example from autoconf. Note, however, that the lax mode + // is still stricter than the autoconf's semantics which also leaves + // unknown substitutions as is. + // + m.in_substitution = &vp.insert<string> ("in.substitution"); + } // Register rules. // diff --git a/build2/version/module.hxx b/build2/version/module.hxx index 4f9f66d..190d8d7 100644 --- a/build2/version/module.hxx +++ b/build2/version/module.hxx @@ -27,7 +27,8 @@ namespace build2 butl::standard_version version; dependency_constraints dependencies; - const variable* in_symbol = nullptr; + const variable* in_symbol = nullptr; // in.symbol + const variable* in_substitution = nullptr; // in.substitution 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 476f327..962a75e 100644 --- a/build2/version/rule.cxx +++ b/build2/version/rule.cxx @@ -243,6 +243,18 @@ namespace build2 fail << "invalid substitution symbol '" << *s << "'"; } + // The substitution mode can be overridden with the in.substitution + // variable. + // + bool strict (true); + if (const string* s = cast_null<string> (t[m.in_substitution])) + { + if (*s == "lax") + strict = false; + else if (*s != "strict") + fail << "invalid substitution mode '" << *s << "'"; + } + // Determine if anything needs to be updated. // timestamp mt (t.load_mtime ()); @@ -262,7 +274,7 @@ namespace build2 // First should come the rule name/version. // - if (dd.expect ("version.in 2") != nullptr) + if (dd.expect ("version.in 3") != nullptr) l4 ([&]{trace << "rule mismatch forcing update of " << t;}); // Then the substitution symbol. @@ -271,6 +283,12 @@ namespace build2 l4 ([&]{trace << "substitution symbol mismatch forcing update of" << t;}); + // Then the substitution mode. + // + if (dd.expect (strict ? "strict" : "lax") != nullptr) + l4 ([&]{trace << "substitution mode mismatch forcing update of" + << t;}); + // Then the .in file. // if (dd.expect (i.path ()) != nullptr) @@ -531,7 +549,7 @@ namespace build2 const location l (&ip, ln); // Not tracking column for now. // Scan the line looking for substiutions in the $<pkg>.<rest>$ - // form. Treat $$ as an escape sequence. + // form. In the strict mode treat $$ as an escape sequence. // for (size_t b (0), n, d; b != (n = s.size ()); b += d) { @@ -540,6 +558,11 @@ namespace build2 if (s[b] != sym) continue; + // Note that in the lax mode these should still be substitutions: + // + // @project@@ + // @@project@ + // Find the other end. // size_t e (b + 1); @@ -547,7 +570,7 @@ namespace build2 { if (s[e] == sym) { - if (e + 1 != n && s[e + 1] == sym) // Escape. + if (strict && e + 1 != n && s[e + 1] == sym) // Escape. s.erase (e, 1); // Keep one, erase the other. else break; @@ -555,20 +578,59 @@ namespace build2 } if (e == n) - fail (l) << "unterminated '" << sym << "'"; + { + if (strict) + fail (l) << "unterminated '" << sym << "'" << endf; + + break; + } - if (e - b == 1) // Escape. + if (e - b == 1) // Escape (or just double symbol in the lax mode). { - s.erase (b, 1); // Keep one, erase the other. + if (strict) + s.erase (b, 1); // Keep one, erase the other. + continue; } - // 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 have a (potential in the lax mode) substition with b + // pointing to the opening symbol and e -- to the closing. + // + if (!strict) + { + // Scan the fragment to make sure it is a variable name (that + // is, it can be expanded as just $<name>; see lexer's variable + // mode for details). + // + size_t i; + for (i = b + 1; i != e; ) + { + bool f (i == b + 1); // First. + char c (s[i++]); + bool l (i == e); // Last. + + if (c == '_' || (f ? alpha (c) : alnum (c))) + continue; + + if (c == '.' && !l) + continue; + + i = string::npos; + break; + } + + if (i == string::npos) + { + d = e - b + 1; // Ignore this substitution. + continue; + } + } + + // 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). + // 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)); |