aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/config/init.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/config/init.cxx')
-rw-r--r--libbuild2/config/init.cxx108
1 files changed, 78 insertions, 30 deletions
diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx
index 6998017..4e1890a 100644
--- a/libbuild2/config/init.cxx
+++ b/libbuild2/config/init.cxx
@@ -6,6 +6,7 @@
#include <libbuild2/file.hxx>
#include <libbuild2/rule.hxx>
+#include <libbuild2/lexer.hxx>
#include <libbuild2/scope.hxx>
#include <libbuild2/context.hxx>
#include <libbuild2/filesystem.hxx> // exists()
@@ -32,9 +33,18 @@ namespace build2
const string& mname (rs.ctx.current_mname);
const string& oname (rs.ctx.current_oname);
- // Only create the module if we are configuring or creating. This is a
- // bit tricky since the build2 core may not yet know if this is the
- // case. But we know.
+ // While config.import (see below) could theoretically be specified in a
+ // buildfile, config.export is expected to always be specified as a
+ // command line override.
+ //
+ // Note: must be entered during bootstrap since we need it in
+ // configure_execute().
+ //
+ vp.insert<path> ("config.export", true /* ovr */);
+
+ // Only create the module if we are configuring or creating or if it was
+ // forced with config.module (useful if we need to call $config.export()
+ // during other meta-operations).
//
if (( mname == "configure" || mname == "create") ||
(mname.empty () && (oname == "configure" || oname == "create")))
@@ -80,42 +90,80 @@ namespace build2
assert (config_hints.empty ()); // We don't known any hints.
+ // Note that the config.<name>* variables belong to the module <name>.
+ // So the only "special" variables we can allocate in config.* are
+ // config.config.*, names that have been "gifted" to us by other modules
+ // (like config.version) as well as names that we have reserved to not
+ // be valid module names (build, import, export).
+ //
auto& vp (rs.ctx.var_pool.rw (rs));
- // Load config.build if one exists (we don't need to worry about
- // disfigure since we will never be init'ed).
+ auto& c_v (vp.insert<uint64_t> ("config.version", false /*ovr*/));
+ auto& c_i (vp.insert<paths> ("config.import", true /* ovr */));
+
+ // Load config.build if one exists followed by extra files specified in
+ // config.import (we don't need to worry about disfigure since we will
+ // never be init'ed).
//
- const variable& c_v (vp.insert<uint64_t> ("config.version", false));
+ auto load_config = [&rs, &c_v] (const path& f, const location& l)
+ {
+ // Check the config version. We assume that old versions cannot
+ // understand new configs and new versions are incompatible with old
+ // configs.
+ //
+ // We extract the value manually instead of loading and then checking
+ // in order to be able to fixup/migrate the file which we may want to
+ // do in the future.
+ //
+
+ // This is tricky for stdin since we cannot reopen it (or put more
+ // than one character back). So what we are going to do is continue
+ // reading after extracting the variable. One side effect of this is
+ // that we won't have the config.version variable entered in the scope
+ // but that is harmless (we could do it manually if necessary).
+ //
+ ifdstream ifs;
+ lexer lex (open_file_or_stdin (f, ifs), f);
+
+ // Assume missing version is 0.
+ //
+ auto p (extract_variable (rs.ctx, lex, c_v));
+ uint64_t v (p.second ? cast<uint64_t> (p.first) : 0);
+
+ if (v != module::version)
+ fail (l) << "incompatible config file " << f <<
+ info << "config file version " << v
+ << (p.second ? "" : " (missing)") <<
+ info << "config module version " << module::version <<
+ info << "consider reconfiguring " << project (rs) << '@'
+ << rs.out_path ();
+
+ source (rs, rs, lex);
+ };
{
path f (config_file (rs));
if (exists (f))
+ load_config (f, l);
+ }
+
+ if (lookup l = rs[c_i])
+ {
+ // Only load files that were specified on our root scope as well as
+ // global overrides. This way we can use our override "positioning"
+ // machinery (i.e., where the override applies) to decide where the
+ // extra config is loaded. The resulting semantics feels quite natural
+ // and consistent with command line variable overrides:
+ //
+ // b config.import=.../config.build # outermost amalgamation
+ // b ./config.import=.../config.build # this project
+ // b !config.import=.../config.build # every project
+ //
+ if (l.belongs (rs) || l.belongs (rs.ctx.global_scope))
{
- // Check the config version. We assume that old versions cannot
- // understand new configs and new versions are incompatible with old
- // configs.
- //
- // We extract the value manually instead of loading and then
- // checking in order to be able to fixup/migrate the file which we
- // may want to do in the future.
- //
- {
- // Assume missing version is 0.
- //
- auto p (extract_variable (rs.ctx, f, c_v));
- uint64_t v (p.second ? cast<uint64_t> (p.first) : 0);
-
- if (v != module::version)
- fail (l) << "incompatible config file " << f <<
- info << "config file version " << v
- << (p.second ? "" : " (missing)") <<
- info << "config module version " << module::version <<
- info << "consider reconfiguring " << project (rs) << '@'
- << out_root;
- }
-
- source (rs, rs, f);
+ for (const path& f: cast<paths> (l))
+ load_config (f, location (&f));
}
}