aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-11-11 12:05:07 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-11-11 12:05:07 +0200
commitf2cd04ce05fedce4c5f850f40ee458adfd387c57 (patch)
treee838739c95bef011389dab8c059d2b26cae4afab
parent99dfd1924b8a7bb5bdd0df132416ff8de6c382fa (diff)
Add in.substitution={strict|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.
-rw-r--r--build2/version/init.cxx36
-rw-r--r--build2/version/module.hxx3
-rw-r--r--build2/version/rule.cxx82
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));