aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/in
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/in')
-rw-r--r--libbuild2/in/init.cxx27
-rw-r--r--libbuild2/in/rule.cxx90
-rw-r--r--libbuild2/in/rule.hxx10
-rw-r--r--libbuild2/in/target.cxx20
4 files changed, 126 insertions, 21 deletions
diff --git a/libbuild2/in/init.cxx b/libbuild2/in/init.cxx
index 18071f8..2fb73e1 100644
--- a/libbuild2/in/init.cxx
+++ b/libbuild2/in/init.cxx
@@ -34,7 +34,10 @@ namespace build2
// Enter variables.
//
{
- auto& vp (rs.var_pool ());
+ // All the variables we enter are qualified so go straight for the
+ // public variable pool.
+ //
+ auto& vp (rs.var_pool (true /* public */));
// Alternative variable substitution symbol with '$' being the
// default.
@@ -58,7 +61,27 @@ namespace build2
// is still stricter than the autoconf's semantics which also leaves
// unknown substitutions as is.
//
- vp.insert<string> ("in.substitution");
+ const variable& im (vp.insert<string> ("in.mode"));
+
+ // Original name of this variable for backwards compatibility.
+ //
+ 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 5a6db30..31a9d94 100644
--- a/libbuild2/in/rule.cxx
+++ b/libbuild2/in/rule.cxx
@@ -47,6 +47,13 @@ namespace build2
if (!fi)
l5 ([&]{trace << "no in file prerequisite for target " << t;});
+ // If we match, derive the file name here instead of in apply() to make
+ // it available early for the in{} prerequisite search (see
+ // install::file_rule::apply_impl() for background).
+ //
+ if (fi)
+ t.derive_path ();
+
return fi;
}
@@ -55,9 +62,9 @@ namespace build2
{
file& t (xt.as<file> ());
- // Derive the file name.
+ // Make sure derived rules assign the path in match().
//
- t.derive_path ();
+ assert (!t.path ().empty ());
// Inject dependency on the output directory.
//
@@ -108,7 +115,7 @@ namespace build2
// Substitution mode.
//
bool strict (strict_);
- if (const string* s = cast_null<string> (t["in.substitution"]))
+ if (const string* s = cast_null<string> (t["in.mode"]))
{
if (*s == "lax")
strict = false;
@@ -116,6 +123,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 +263,7 @@ namespace build2
substitute (location (ip, ln),
a, t,
name, flags,
- strict, null));
+ strict, smap, null));
assert (v); // Rule semantics change without version increment?
@@ -294,7 +306,35 @@ namespace build2
if (verb >= 2)
text << program_ << ' ' << ip << " >" << tp;
else if (verb)
- text << program_ << ' ' << ip;
+ {
+ // If we straight print the target, in most cases we will end up with
+ // something ugly like in{version...h.in} (due to the in{} target
+ // type search semantics). There is the `...h` part but also the
+ // `.in` part that is redundant given in{}. So let's tidy this up
+ // a bit if the extension could have been derived by in_search().
+ //
+ target_key ik (i.key ());
+
+ if (ik.ext)
+ {
+ string& ie (*ik.ext);
+ const string* te (t.ext ());
+
+ size_t in (ie.size ());
+ size_t tn (te != nullptr ? te->size () : 0);
+
+ if (in == tn + (tn != 0 ? 1 : 0) + 2) // [<te>.]in
+ {
+ if (ie.compare (in - 2, 2, "in") == 0)
+ {
+ if (tn == 0 || (ie.compare (0, tn, *te) == 0 && ie[tn] == '.'))
+ ie.clear ();
+ }
+ }
+ }
+
+ print_diag (program_.c_str (), move (ik), t);
+ }
// Read and process the file, one line at a time, while updating depdb.
//
@@ -379,7 +419,7 @@ namespace build2
a, t,
dd, dd_skip,
s, 0,
- nl, sym, strict, null);
+ nl, sym, strict, smap, null);
ofs << s;
}
@@ -445,6 +485,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 +541,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 +562,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 +602,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 +627,7 @@ namespace build2
}
}
- return lookup (l, a, t, n, flags, null);
+ return lookup (l, a, t, n, flags, smap, null);
}
string rule::
@@ -593,10 +635,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 +671,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..67c2509 100644
--- a/libbuild2/in/rule.hxx
+++ b/libbuild2/in/rule.hxx
@@ -22,6 +22,11 @@ namespace build2
// cache data (e.g., in match() or apply()) to be used in substitute() and
// lookup() calls.
//
+ // A derived rule is also required to derive the target file name in
+ // match() instead of apply() to make it available early for the in{}
+ // prerequisite search (see install::file_rule::apply_impl() for
+ // background).
+ //
// Note also that currently this rule ignores the dry-run mode (see
// perform_update() for the rationale).
//
@@ -54,6 +59,7 @@ namespace build2
// Customization hooks and helpers.
//
+ using substitution_map = map<string, optional<string>>;
// Perform prerequisite search.
//
@@ -89,6 +95,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 +107,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 +119,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 +133,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/in/target.cxx b/libbuild2/in/target.cxx
index d548453..d664e3a 100644
--- a/libbuild2/in/target.cxx
+++ b/libbuild2/in/target.cxx
@@ -10,7 +10,7 @@ namespace build2
namespace in
{
static const target*
- in_search (const target& xt, const prerequisite_key& cpk)
+ in_search (context& ctx, const target* xt, const prerequisite_key& cpk)
{
// If we have no extension then derive it from our target. Then delegate
// to file_search().
@@ -18,18 +18,26 @@ namespace build2
prerequisite_key pk (cpk);
optional<string>& e (pk.tk.ext);
- if (!e)
+ if (!e && xt != nullptr)
{
- if (const file* t = xt.is_a<file> ())
+ // Why is the extension, say, .h.in and not .in (with .h being in the
+ // name)? While this is mostly academic (in this case things will work
+ // the same either way), conceptually, it is a header template rather
+ // than some file template. In other words, we are adding the second
+ // level classification.
+ //
+ // See also the low verbosity tidying up code in the rule.
+ //
+ if (const file* t = xt->is_a<file> ())
{
const string& te (t->derive_extension ());
e = te + (te.empty () ? "" : ".") + "in";
}
else
- fail << "prerequisite " << pk << " for a non-file target " << xt;
+ fail << "prerequisite " << pk << " for a non-file target " << *xt;
}
- return file_search (xt, pk);
+ return file_search (ctx, xt, pk);
}
static bool
@@ -51,7 +59,7 @@ namespace build2
&target_extension_none,
nullptr, /* default_extension */ // Taken care of by search.
&in_pattern,
- &target_print_1_ext_verb, // Same as file.
+ &target_print_1_ext_verb, // Same as file (but see rule).
&in_search,
target_type::flag::none
};