diff options
Diffstat (limited to 'libbuild2/in')
-rw-r--r-- | libbuild2/in/init.cxx | 27 | ||||
-rw-r--r-- | libbuild2/in/rule.cxx | 90 | ||||
-rw-r--r-- | libbuild2/in/rule.hxx | 10 | ||||
-rw-r--r-- | libbuild2/in/target.cxx | 20 |
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 }; |