aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-09-02 10:29:15 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-09-02 10:29:15 +0200
commit8ecc09653d70fe8df4fc9fcd7214ba85e6db9c9c (patch)
tree7c5a8bbe8899faa10100dcf8b09b1e66f7d2900d
parentce69ad7c2aaf517ec3f789300bf1ae29d8f0dc5d (diff)
Add ability to specify `in` rule substitution as key-value pairs
-rw-r--r--doc/manual.cli45
-rw-r--r--libbuild2/bash/rule.cxx3
-rw-r--r--libbuild2/bash/rule.hxx1
-rw-r--r--libbuild2/in/init.cxx16
-rw-r--r--libbuild2/in/rule.cxx47
-rw-r--r--libbuild2/in/rule.hxx5
-rw-r--r--libbuild2/version/rule.cxx4
-rw-r--r--libbuild2/version/rule.hxx1
-rw-r--r--tests/in/testscript29
9 files changed, 137 insertions, 14 deletions
diff --git a/doc/manual.cli b/doc/manual.cli
index 72611bc..ed96915 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -8871,6 +8871,42 @@ target-specific variables. Typed variable values are converted to string
using the corresponding \c{builtin.string()} function overload before
substitution.
+While specifying substitution values as \c{buildfile} variables is usually
+natural, sometimes this may not be possible or convenient. Specifically, we
+may have substitution names that cannot be specified as \c{buildfile}
+variables, for example, because they start with an underscore (and are thus
+reserved) or because they refer to one of the predefined variables. Also, we
+may need to have different groups of substitution values for different cases,
+for example, for different platforms, and it would be convenient to pass such
+groups around as a single value.
+
+To support these requirements the substitution values can alternatively be
+specified as key-value pairs in the \c{in.substitutions} variable. Note that
+entries in this substitution map take precedence over the \c{buildfile}
+variables. For example:
+
+\
+/* config.h.in */
+
+#define _GNU_SOURCE @_GNU_SOURCE@
+#define _POSIX_SOURCE @_POSIX_SOURCE@
+\
+
+\
+# buildfile
+
+h{config}: in{config}
+{
+ in.symbol = '@'
+ in.mode = lax
+
+ in.substitutions = _GNU_SOURCE@0 _POSIX_SOURCE@1
+}
+\
+
+\N|In the above example, the \c{@} characters in \c{in.symbol} and
+\c{in.substitutions} are unrelated.|
+
Using an undefined variable in a substitution is an error. Using a \c{null}
value in a substitution is also an error unless the fallback value is
specified with the \c{in.null} variable. For example:
@@ -8884,6 +8920,15 @@ h{config}: in{config}
}
\
+\N|To specify a \c{null} value using the \c{in.substitutions} mechanism omit
+the value, for example:
+
+\
+in.substitutions = _GNU_SOURCE
+\
+
+|
+
A number of other build system modules, for example,
\l{https://github.com/build2/libbuild2-autoconf/ \c{autoconf}},
\l{#module-version \c{version}}, and \l{#module-bash \c{bash}}, are based on
diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx
index 2abddc3..6e287e0 100644
--- a/libbuild2/bash/rule.cxx
+++ b/libbuild2/bash/rule.cxx
@@ -214,13 +214,14 @@ namespace build2
const string& n,
optional<uint64_t> flags,
bool strict,
+ const substitution_map* smap,
const optional<string>& null) const
{
assert (!flags);
return n.compare (0, 6, "import") == 0 && (n[6] == ' ' || n[6] == '\t')
? substitute_import (l, a, t, trim (string (n, 7)))
- : rule::substitute (l, a, t, n, nullopt, strict, null);
+ : rule::substitute (l, a, t, n, nullopt, strict, smap, null);
}
string in_rule::
diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx
index f7ce5cd..5bd7fa7 100644
--- a/libbuild2/bash/rule.hxx
+++ b/libbuild2/bash/rule.hxx
@@ -52,6 +52,7 @@ namespace build2
const string&,
optional<uint64_t>,
bool,
+ const substitution_map*,
const optional<string>&) const override;
string
diff --git a/libbuild2/in/init.cxx b/libbuild2/in/init.cxx
index 6743e31..8bd5909 100644
--- a/libbuild2/in/init.cxx
+++ b/libbuild2/in/init.cxx
@@ -64,6 +64,22 @@ namespace build2
//
vp.insert_alias (im, "in.substitution");
+ // Substitution map. Substitutions can be specified as key-value pairs
+ // rather than buildfile variables. This map is checked before the
+ // variables. An absent value in key-value has the NULL semantics.
+ //
+ // This mechanism has two primary uses: Firstly, it allows us to have
+ // substitution names that cannot be specified as buildfile variables.
+ // For example, a name may start with an underscore and thus be
+ // reserved or it may refer to one of the predefined variables such a
+ // `include` or `extension` that may have a wrong visibility and/or
+ // type.
+ //
+ // Secondly, this mechanism allows us to encapsulate a group of
+ // substitutions and pass this group around as a single value.
+ //
+ vp.insert<map<string, optional<string>>> ("in.substitutions");
+
// Fallback value to use for NULL value substitutions. If unspecified,
// NULL substitutions are an error.
//
diff --git a/libbuild2/in/rule.cxx b/libbuild2/in/rule.cxx
index 3aa92a2..d07adfc 100644
--- a/libbuild2/in/rule.cxx
+++ b/libbuild2/in/rule.cxx
@@ -116,6 +116,11 @@ namespace build2
fail << "invalid substitution mode '" << *s << "'";
}
+ // Substitution map.
+ //
+ const substitution_map* smap (
+ cast_null<map<string, optional<string>>> (t["in.substitutions"]));
+
// NULL substitutions.
//
optional<string> null;
@@ -251,7 +256,7 @@ namespace build2
substitute (location (ip, ln),
a, t,
name, flags,
- strict, null));
+ strict, smap, null));
assert (v); // Rule semantics change without version increment?
@@ -379,7 +384,7 @@ namespace build2
a, t,
dd, dd_skip,
s, 0,
- nl, sym, strict, null);
+ nl, sym, strict, smap, null);
ofs << s;
}
@@ -445,6 +450,7 @@ namespace build2
const char* nl,
char sym,
bool strict,
+ const substitution_map* smap,
const optional<string>& null) const
{
// Scan the line looking for substiutions in the $<name>$ form. In the
@@ -500,8 +506,7 @@ namespace build2
dd, dd_skip,
string (s, b + 1, e - b -1),
nullopt /* flags */,
- strict,
- null))
+ strict, smap, null))
{
replace_newlines (*val, nl);
@@ -522,9 +527,10 @@ namespace build2
const string& n,
optional<uint64_t> flags,
bool strict,
+ const substitution_map* smap,
const optional<string>& null) const
{
- optional<string> val (substitute (l, a, t, n, flags, strict, null));
+ optional<string> val (substitute (l, a, t, n, flags, strict, smap, null));
if (val)
{
@@ -561,6 +567,7 @@ namespace build2
const string& n,
optional<uint64_t> flags,
bool strict,
+ const substitution_map* smap,
const optional<string>& null) const
{
// In the lax mode scan the fragment to make sure it is a variable name
@@ -585,7 +592,7 @@ namespace build2
}
}
- return lookup (l, a, t, n, flags, null);
+ return lookup (l, a, t, n, flags, smap, null);
}
string rule::
@@ -593,10 +600,32 @@ namespace build2
action, const target& t,
const string& n,
optional<uint64_t> flags,
+ const substitution_map* smap,
const optional<string>& null) const
{
assert (!flags);
+ // First look in the substitution map.
+ //
+ if (smap != nullptr)
+ {
+ auto i (smap->find (n));
+
+ if (i != smap->end ())
+ {
+ if (i->second)
+ return *i->second;
+
+ if (null)
+ return *null;
+
+ fail (loc) << "null value in substitution map entry '" << n << "'" <<
+ info << "use in.null to specify null value substiution string";
+ }
+ }
+
+ // Next look for the buildfile variable.
+ //
auto l (t[n]);
if (l.defined ())
@@ -607,9 +636,9 @@ namespace build2
{
if (null)
return *null;
- else
- fail (loc) << "null value in variable '" << n << "'" <<
- info << "use in.null to specify null value substiution string";
+
+ fail (loc) << "null value in variable '" << n << "'" <<
+ info << "use in.null to specify null value substiution string";
}
// For typed values call string() for conversion.
diff --git a/libbuild2/in/rule.hxx b/libbuild2/in/rule.hxx
index bd0d15a..369fd93 100644
--- a/libbuild2/in/rule.hxx
+++ b/libbuild2/in/rule.hxx
@@ -54,6 +54,7 @@ namespace build2
// Customization hooks and helpers.
//
+ using substitution_map = map<string, optional<string>>;
// Perform prerequisite search.
//
@@ -89,6 +90,7 @@ namespace build2
action, const target&,
const string& name,
optional<uint64_t> flags,
+ const substitution_map*,
const optional<string>& null) const;
// Perform variable substitution. Return nullopt if it should be
@@ -100,6 +102,7 @@ namespace build2
const string& name,
optional<uint64_t> flags,
bool strict,
+ const substitution_map*,
const optional<string>& null) const;
// Call the above version and do any necessary depdb saving.
@@ -111,6 +114,7 @@ namespace build2
const string& name,
optional<uint64_t> flags,
bool strict,
+ const substitution_map*,
const optional<string>& null) const;
// Process a line of input from the specified position performing any
@@ -124,6 +128,7 @@ namespace build2
const char* newline,
char sym,
bool strict,
+ const substitution_map*,
const optional<string>& null) const;
// Replace newlines in a multi-line value with the given newline
diff --git a/libbuild2/version/rule.cxx b/libbuild2/version/rule.cxx
index 1799666..f810ce7 100644
--- a/libbuild2/version/rule.cxx
+++ b/libbuild2/version/rule.cxx
@@ -115,6 +115,7 @@ namespace build2
const target& t,
const string& n,
optional<uint64_t> flags,
+ const substitution_map* smap,
const optional<string>& null) const
{
assert (!flags);
@@ -138,8 +139,7 @@ namespace build2
a,
t,
p == string::npos ? n : string (n, p + 1),
- nullopt,
- null);
+ nullopt, smap, null);
}
string pn (n, 0, p);
diff --git a/libbuild2/version/rule.hxx b/libbuild2/version/rule.hxx
index ba673e5..2903dbf 100644
--- a/libbuild2/version/rule.hxx
+++ b/libbuild2/version/rule.hxx
@@ -34,6 +34,7 @@ namespace build2
const target&,
const string&,
optional<uint64_t>,
+ const substitution_map*,
const optional<string>&) const override;
};
diff --git a/tests/in/testscript b/tests/in/testscript
index c4f534c..5dce1d3 100644
--- a/tests/in/testscript
+++ b/tests/in/testscript
@@ -17,7 +17,9 @@ cat <<EOI >=test.in;
EOI
cat <<EOI >=buildfile;
file{test}: in{test}
- file{test}: foo = FOO
+ {
+ foo = FOO
+ }
EOI
$* <<<buildfile;
cat test >>EOO;
@@ -25,6 +27,27 @@ cat test >>EOO;
EOO
$* clean <<<buildfile
+: substitution-map
+:
+cat <<EOI >=test.in;
+ foo = $_foo$
+ bar = $bar$
+ EOI
+cat <<EOI >=buildfile;
+ file{test}: in{test}
+ {
+ in.substitutions = _foo@FOO
+ in.substitutions += bar@BAR
+ bar = wrong
+ }
+ EOI
+$* <<<buildfile;
+cat test >>EOO;
+ foo = FOO
+ bar = BAR
+ EOO
+$* clean <<<buildfile
+
: lax
:
cat <<EOI >=test.in;
@@ -33,7 +56,9 @@ cat <<EOI >=test.in;
EOI
$* <<EOI &test &test.d;
file{test}: in{test}
- file{test}: in.mode = lax
+ {
+ in.mode = lax
+ }
EOI
cat test >>EOO
$10