aboutsummaryrefslogtreecommitdiff
path: root/build2/config
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-07-04 19:12:15 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-07-05 14:24:43 +0300
commit57b10c06925d0bdf6ffb38488ee908f085109e95 (patch)
treef2103684d319650c3302aef9d7a70dd64ff2a347 /build2/config
parent30b4eda196e090aa820d312e6a9435a4ae84c303 (diff)
Move config, dist, test, and install modules into library
Diffstat (limited to 'build2/config')
-rw-r--r--build2/config/init.cxx148
-rw-r--r--build2/config/init.hxx31
-rw-r--r--build2/config/module.cxx54
-rw-r--r--build2/config/module.hxx93
-rw-r--r--build2/config/operation.cxx997
-rw-r--r--build2/config/operation.hxx29
-rw-r--r--build2/config/utility.cxx307
-rw-r--r--build2/config/utility.hxx177
-rw-r--r--build2/config/utility.txx66
9 files changed, 0 insertions, 1902 deletions
diff --git a/build2/config/init.cxx b/build2/config/init.cxx
deleted file mode 100644
index bd2d573..0000000
--- a/build2/config/init.cxx
+++ /dev/null
@@ -1,148 +0,0 @@
-// file : build2/config/init.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/config/init.hxx>
-
-#include <libbuild2/file.hxx>
-#include <libbuild2/rule.hxx>
-#include <libbuild2/scope.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/filesystem.hxx> // exists()
-#include <libbuild2/diagnostics.hxx>
-
-#include <build2/config/module.hxx>
-#include <build2/config/utility.hxx>
-#include <build2/config/operation.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace config
- {
- bool
- boot (scope& rs, const location&, unique_ptr<module_base>& mod)
- {
- tracer trace ("config::boot");
-
- l5 ([&]{trace << "for " << rs;});
-
- const string& mname (current_mname);
- const string& oname (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.
- //
- if (( mname == "configure" || mname == "create") ||
- (mname.empty () && (oname == "configure" || oname == "create")))
- {
- unique_ptr<module> m (new module);
-
- // Adjust priority for the import pseudo-module so that
- // config.import.* values come first in config.build.
- //
- m->save_module ("import", INT32_MIN);
-
- mod = move (m);
- }
-
- // Register meta-operations. Note that we don't register create_id
- // since it will be pre-processed into configure.
- //
- rs.insert_meta_operation (configure_id, mo_configure);
- rs.insert_meta_operation (disfigure_id, mo_disfigure);
-
- return true; // Initialize first (load config.build).
- }
-
- bool
- init (scope& rs,
- scope&,
- const location& l,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
- {
- tracer trace ("config::init");
-
- if (!first)
- {
- warn (l) << "multiple config module initializations";
- return true;
- }
-
- const dir_path& out_root (rs.out_path ());
- l5 ([&]{trace << "for " << out_root;});
-
- assert (config_hints.empty ()); // We don't known any hints.
-
- auto& vp (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).
- //
- const variable& c_v (vp.insert<uint64_t> ("config.version", false));
-
- {
- path f (config_file (rs));
-
- if (exists (f))
- {
- // 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 (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);
- }
- }
-
- // Register alias and fallback rule for the configure meta-operation.
- //
- // We need this rule for out-of-any-project dependencies (e.g.,
- // libraries imported from /usr/lib). We are registring it on the
- // global scope similar to builtin rules.
- //
- {
- auto& r (rs.global ().rules);
- r.insert<mtime_target> (
- configure_id, 0, "config.file", file_rule::instance);
- }
- {
- auto& r (rs.rules);
-
- //@@ outer
- r.insert<alias> (configure_id, 0, "config.alias", alias_rule::instance);
-
- // This allows a custom configure rule while doing nothing by default.
- //
- r.insert<target> (configure_id, 0, "config", noop_rule::instance);
- r.insert<file> (configure_id, 0, "config.file", noop_rule::instance);
- }
-
- return true;
- }
- }
-}
diff --git a/build2/config/init.hxx b/build2/config/init.hxx
deleted file mode 100644
index 5a9b66d..0000000
--- a/build2/config/init.hxx
+++ /dev/null
@@ -1,31 +0,0 @@
-// file : build2/config/init.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CONFIG_INIT_HXX
-#define BUILD2_CONFIG_INIT_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-
-namespace build2
-{
- namespace config
- {
- bool
- boot (scope&, const location&, unique_ptr<module_base>&);
-
- bool
- init (scope&,
- scope&,
- const location&,
- unique_ptr<module_base>&,
- bool,
- bool,
- const variable_map&);
- }
-}
-
-#endif // BUILD2_CONFIG_INIT_HXX
diff --git a/build2/config/module.cxx b/build2/config/module.cxx
deleted file mode 100644
index 7c3aae4..0000000
--- a/build2/config/module.cxx
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : build2/config/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/config/module.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace config
- {
- void module::
- save_variable (const variable& var, uint64_t flags)
- {
- const string& n (var.name);
-
- // First try to find the module with the name that is the longest
- // prefix of this variable name.
- //
- auto& sm (saved_modules);
- auto i (sm.find_sup (n));
-
- // If no module matched, then create one based on the variable name.
- //
- if (i == sm.end ())
- {
- // @@ For now with 'config.' prefix.
- //
- i = sm.insert (string (n, 0, n.find ('.', 7)));
- }
-
- // Don't insert duplicates. The config.import vars are particularly
- // susceptible to duplication.
- //
- saved_variables& sv (i->second);
- auto j (sv.find (var));
-
- if (j == sv.end ())
- sv.push_back (saved_variable {var, flags});
- else
- assert (j->flags == flags);
- }
-
- void module::
- save_module (const char* name, int prio)
- {
- saved_modules.insert (string ("config.") += name, prio);
- }
-
- const string module::name ("config");
- const uint64_t module::version (1);
- }
-}
diff --git a/build2/config/module.hxx b/build2/config/module.hxx
deleted file mode 100644
index 0c78b18..0000000
--- a/build2/config/module.hxx
+++ /dev/null
@@ -1,93 +0,0 @@
-// file : build2/config/module.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CONFIG_MODULE_HXX
-#define BUILD2_CONFIG_MODULE_HXX
-
-#include <map>
-
-#include <libbutl/prefix-map.mxx>
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-#include <libbuild2/variable.hxx>
-
-namespace build2
-{
- namespace config
- {
- // An ordered list of modules each with an ordered list of list of
- // config.* variables and their "save flags" (see save_variable()) that
- // are used (as opposed to just being specified) in this configuration.
- // Populated by the config utility functions (required(), optional())
- // and saved in the order populated.
- //
- struct saved_variable
- {
- reference_wrapper<const variable> var;
- uint64_t flags;
- };
-
- struct saved_variables: vector<saved_variable>
- {
- // Normally each module only have a handful of config variables and we
- // only do this during configuration so for now we do linear search
- // instead of adding a map.
- //
- const_iterator
- find (const variable& var) const
- {
- return std::find_if (
- begin (),
- end (),
- [&var] (const saved_variable& v) {return var == v.var;});
- }
- };
-
- struct saved_modules: butl::prefix_map<string, saved_variables, '.'>
- {
- // Priority order with INT32_MIN being the highest. Modules with the
- // same priority are saved in the order inserted.
- //
- // Generally, the idea is that we want higher-level modules at the top
- // of the file since that's the configuration that we usualy want to
- // change. So we have the following priority bands/defaults:
- //
- // 101-200/150 - code generators (e.g., yacc, bison)
- // 201-300/250 - compilers (e.g., C, C++),
- // 301-400/350 - binutils (ar, ld)
- //
- std::multimap<std::int32_t, const_iterator> order;
-
- iterator
- insert (string name, int prio = 0)
- {
- auto p (emplace (move (name), saved_variables ()));
-
- if (p.second)
- order.emplace (prio, p.first);
-
- return p.first;
- }
- };
-
- struct module: module_base
- {
- config::saved_modules saved_modules;
-
- void
- save_variable (const variable&, uint64_t flags = 0);
-
- void
- save_module (const char* name, int prio = 0);
-
- static const string name;
- static const uint64_t version;
- };
- }
-}
-
-#endif // BUILD2_CONFIG_MODULE_HXX
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
deleted file mode 100644
index ff5b44a..0000000
--- a/build2/config/operation.cxx
+++ /dev/null
@@ -1,997 +0,0 @@
-// file : build2/config/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/config/operation.hxx>
-
-#include <set>
-
-#include <libbuild2/file.hxx>
-#include <libbuild2/spec.hxx>
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/algorithm.hxx>
-#include <libbuild2/filesystem.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <build2/config/module.hxx>
-#include <build2/config/utility.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace config
- {
- // configure
- //
- static void
- save_src_root (const scope& root)
- {
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- path f (out_root / root.root_extra->src_root_file);
-
- if (verb >= 2)
- text << "cat >" << f;
-
- try
- {
- ofdstream ofs (f);
-
- ofs << "# Created automatically by the config module." << endl
- << "#" << endl
- << "src_root = ";
- to_stream (ofs, name (src_root), true, '@'); // Quote.
- ofs << endl;
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
-
- static void
- save_out_root (const scope& root)
- {
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- path f (src_root / root.root_extra->out_root_file);
-
- if (verb)
- text << (verb >= 2 ? "cat >" : "save ") << f;
-
- try
- {
- ofdstream ofs (f);
-
- ofs << "# Created automatically by the config module." << endl
- << "#" << endl
- << "out_root = ";
- to_stream (ofs, name (out_root), true, '@'); // Quote.
- ofs << endl;
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
-
- using project_set = set<const scope*>; // Use pointers to get comparison.
-
- static void
- save_config (const scope& root, const project_set& projects)
- {
- path f (config_file (root));
-
- if (verb)
- text << (verb >= 2 ? "cat >" : "save ") << f;
-
- const module& mod (*root.lookup_module<const module> (module::name));
-
- try
- {
- ofdstream ofs (f);
-
- ofs << "# Created automatically by the config module, but feel " <<
- "free to edit." << endl
- << "#" << endl;
-
- ofs << "config.version = " << module::version << endl;
-
- if (auto l = root.vars[var_amalgamation])
- {
- const dir_path& d (cast<dir_path> (l));
-
- ofs << endl
- << "# Base configuration inherited from " << d << endl
- << "#" << endl;
- }
-
- // Save config variables.
- //
- names storage;
-
- for (auto p: mod.saved_modules.order)
- {
- const string& sname (p.second->first);
- const saved_variables& svars (p.second->second);
-
- bool first (true); // Separate modules with a blank line.
- for (const saved_variable& sv: svars)
- {
- const variable& var (sv.var);
-
- pair<lookup, size_t> org (root.find_original (var));
- pair<lookup, size_t> ovr (var.overrides == nullptr
- ? org
- : root.find_override (var, org));
- const lookup& l (ovr.first);
-
- // We definitely write values that are set on our root scope or
- // are global overrides. Anything in-between is presumably
- // inherited. We might also not have any value at all (see
- // unconfigured()).
- //
- if (!l.defined ())
- continue;
-
- if (!(l.belongs (root) || l.belongs (*global_scope)))
- {
- // This is presumably an inherited value. But it could also be
- // some left-over garbage. For example, an amalgamation could
- // have used a module but then dropped it while its config
- // values are still lingering in config.build. They are probably
- // still valid and we should probably continue using them but we
- // definitely want to move them to our config.build since they
- // will be dropped from the amalgamation's config.build. Let's
- // also warn the user just in case.
- //
- // There is also another case that falls under this now that
- // overrides are by default amalgamation-wide rather than just
- // "project and subprojects": we may be (re-)configuring a
- // subproject but the override is now set on the outer project's
- // root.
- //
- bool found (false);
- const scope* r (&root);
- while ((r = r->parent_scope ()->root_scope ()) != nullptr)
- {
- if (l.belongs (*r))
- {
- // Find the config module.
- //
- if (auto* m = r->lookup_module<const module> (module::name))
- {
- // Find the corresponding saved module.
- //
- auto i (m->saved_modules.find (sname));
-
- if (i != m->saved_modules.end ())
- {
- // Find the variable.
- //
- const saved_variables& sv (i->second);
- found = sv.find (var) != sv.end ();
-
- // Handle that other case: if this is an override but
- // the outer project itself is not being configured,
- // then we need to save this override.
- //
- // One problem with using the already configured project
- // set is that the outer project may be configured only
- // after us in which case both projects will save the
- // value. But perhaps this is a feature, not a bug since
- // this is how project-local (%) override behaves.
- //
- if (found &&
- org.first != ovr.first &&
- projects.find (r) == projects.end ())
- found = false;
- }
- }
-
- break;
- }
- }
-
- if (found) // Inherited.
- continue;
-
- location loc (&f);
-
- // If this value is not defined in a project's root scope, then
- // something is broken.
- //
- if (r == nullptr)
- fail (loc) << "inherited variable " << var << " value "
- << "is not from a root scope";
-
- // If none of the outer project's configurations use this value,
- // then we warn and save as our own. One special case where we
- // don't want to warn the user is if the variable is overriden.
- //
- if (org.first == ovr.first)
- {
- diag_record dr;
- dr << warn (loc) << "saving previously inherited variable "
- << var;
-
- dr << info (loc) << "because project " << *r
- << " no longer uses it in its configuration";
-
- if (verb >= 2)
- {
- dr << info (loc) << "variable value: ";
-
- if (*l)
- {
- storage.clear ();
- dr << "'" << reverse (*l, storage) << "'";
- }
- else
- dr << "[null]";
- }
- }
- }
-
- const string& n (var.name);
- const value& v (*l);
-
- // We will only write config.*.configured if it is false (true is
- // implied by its absence). We will also ignore false values if
- // there is any other value for this module (see unconfigured()).
- //
- if (n.size () > 11 &&
- n.compare (n.size () - 11, 11, ".configured") == 0)
- {
- if (cast<bool> (v) || svars.size () != 1)
- continue;
- }
-
- // If we got here then we are saving this variable. Handle the
- // blank line.
- //
- if (first)
- {
- ofs << endl;
- first = false;
- }
-
- // Handle the save_commented flag.
- //
- if ((org.first.defined () && org.first->extra) && // Default value.
- org.first == ovr.first && // Not overriden.
- (sv.flags & save_commented) == save_commented)
- {
- ofs << '#' << n << " =" << endl;
- continue;
- }
-
- if (v)
- {
- storage.clear ();
- names_view ns (reverse (v, storage));
-
- ofs << n;
-
- if (ns.empty ())
- ofs << " =";
- else
- {
- ofs << " = ";
- to_stream (ofs, ns, true, '@'); // Quote.
- }
-
- ofs << endl;
- }
- else
- ofs << n << " = [null]" << endl;
- }
- }
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
-
- static void
- configure_project (action a, const scope& root, project_set& projects)
- {
- tracer trace ("configure_project");
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- if (!projects.insert (&root).second)
- {
- l5 ([&]{trace << "skipping already configured " << out_root;});
- return;
- }
-
- // Make sure the directories exist.
- //
- if (out_root != src_root)
- {
- mkdir_p (out_root / root.root_extra->build_dir);
- mkdir (out_root / root.root_extra->bootstrap_dir, 2);
- }
-
- // We distinguish between a complete configure and operation-
- // specific.
- //
- if (a.operation () == default_id)
- {
- l5 ([&]{trace << "completely configuring " << out_root;});
-
- // Save src-root.build unless out_root is the same as src.
- //
- if (out_root != src_root)
- save_src_root (root);
-
- // Save config.build.
- //
- save_config (root, projects);
- }
- else
- {
- }
-
- // Configure subprojects that have been loaded.
- //
- if (auto l = root.vars[var_subprojects])
- {
- for (auto p: cast<subprojects> (l))
- {
- const dir_path& pd (p.second);
- dir_path out_nroot (out_root / pd);
- const scope& nroot (scopes.find (out_nroot));
-
- // @@ Strictly speaking we need to check whether the config
- // module was loaded for this subproject.
- //
- if (nroot.out_path () != out_nroot) // This subproject not loaded.
- continue;
-
- configure_project (a, nroot, projects);
- }
- }
- }
-
- static void
- configure_forward (const scope& root, project_set& projects)
- {
- tracer trace ("configure_forward");
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- if (!projects.insert (&root).second)
- {
- l5 ([&]{trace << "skipping already configured " << src_root;});
- return;
- }
-
- mkdir (src_root / root.root_extra->bootstrap_dir, 2); // Make sure exists.
- save_out_root (root);
-
- // Configure subprojects. Since we don't load buildfiles if configuring
- // a forward, we do it for all known subprojects.
- //
- if (auto l = root.vars[var_subprojects])
- {
- for (auto p: cast<subprojects> (l))
- {
- dir_path out_nroot (out_root / p.second);
- const scope& nroot (scopes.find (out_nroot));
- assert (nroot.out_path () == out_nroot);
-
- configure_forward (nroot, projects);
- }
- }
- }
-
- operation_id (*pre) (const values&, meta_operation_id, const location&);
-
- static operation_id
- configure_operation_pre (const values&, operation_id o)
- {
- // Don't translate default to update. In our case unspecified
- // means configure everything.
- //
- return o;
- }
-
- // The (vague) idea is that in the future we may turn this into to some
- // sort of key-value sequence (similar to the config initializer idea),
- // for example:
- //
- // configure(out/@src/, forward foo bar@123)
- //
- // Though using commas instead spaces and '=' instead of '@' would have
- // been nicer.
- //
- static bool
- forward (const values& params,
- const char* mo = nullptr,
- const location& l = location ())
- {
- if (params.size () == 1)
- {
- const names& ns (cast<names> (params[0]));
-
- if (ns.size () == 1 && ns[0].simple () && ns[0].value == "forward")
- return true;
- else if (!ns.empty ())
- fail (l) << "unexpected parameter '" << ns << "' for "
- << "meta-operation " << mo;
- }
- else if (!params.empty ())
- fail (l) << "unexpected parameters for meta-operation " << mo;
-
- return false;
- }
-
- static void
- configure_pre (const values& params, const location& l)
- {
- forward (params, "configure", l); // Validate.
- }
-
- static void
- configure_load (const values& params,
- scope& root,
- const path& buildfile,
- const dir_path& out_base,
- const dir_path& src_base,
- const location& l)
- {
- if (forward (params))
- {
- // We don't need to load the buildfiles in order to configure
- // forwarding but in order to configure subprojects we have to
- // bootstrap them (similar to disfigure).
- //
- create_bootstrap_inner (root);
-
- if (root.out_path () == root.src_path ())
- fail (l) << "forwarding to source directory " << root.src_path ();
- }
- else
- load (params, root, buildfile, out_base, src_base, l); // Normal load.
- }
-
- static void
- configure_search (const values& params,
- const scope& root,
- const scope& base,
- const path& bf,
- const target_key& tk,
- const location& l,
- action_targets& ts)
- {
- if (forward (params))
- {
- // For forwarding we only collect the projects (again, similar to
- // disfigure).
- //
- ts.push_back (&root);
- }
- else
- search (params, root, base, bf, tk, l, ts); // Normal search.
- }
-
- static void
- configure_match (const values&, action, action_targets&, uint16_t, bool)
- {
- // Don't match anything -- see execute ().
- }
-
- static void
- configure_execute (const values& params,
- action a,
- action_targets& ts,
- uint16_t,
- bool)
- {
- bool fwd (forward (params));
-
- project_set projects;
-
- for (const action_target& at: ts)
- {
- if (fwd)
- {
- // Forward configuration.
- //
- const scope& root (*static_cast<const scope*> (at.target));
- configure_forward (root, projects);
- continue;
- }
-
- // Normal configuration.
- //
- // Match rules to configure every operation supported by each project.
- // Note that we are not calling operation_pre/post() callbacks here
- // since the meta operation is configure and we know what we are
- // doing.
- //
- // Note that we cannot do this in parallel. We cannot parallelize the
- // outer loop because we should match for a single action at a time.
- // And we cannot swap the loops because the list of operations is
- // target-specific. However, inside match(), things can proceed in
- // parallel.
- //
- const target& t (at.as_target ());
- const scope* rs (t.base_scope ().root_scope ());
-
- if (rs == nullptr)
- fail << "out of project target " << t;
-
- const operations& ops (rs->root_extra->operations);
-
- for (operation_id id (default_id + 1); // Skip default_id.
- id < ops.size ();
- ++id)
- {
- if (const operation_info* oif = ops[id])
- {
- // Skip aliases (e.g., update-for-install).
- //
- if (oif->id != id)
- continue;
-
- set_current_oif (*oif);
-
- phase_lock pl (run_phase::match);
- match (action (configure_id, id), t);
- }
- }
-
- configure_project (a, *rs, projects);
- }
- }
-
- const meta_operation_info mo_configure {
- configure_id,
- "configure",
- "configure",
- "configuring",
- "configured",
- "is configured",
- true, // bootstrap_outer
- &configure_pre, // meta-operation pre
- &configure_operation_pre,
- &configure_load, // normal load unless configuring forward
- &configure_search, // normal search unless configuring forward
- &configure_match,
- &configure_execute,
- nullptr, // operation post
- nullptr, // meta-operation post
- nullptr // include
- };
-
- // disfigure
- //
-
- static bool
- disfigure_project (action a, const scope& root, project_set& projects)
- {
- tracer trace ("disfigure_project");
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- if (!projects.insert (&root).second)
- {
- l5 ([&]{trace << "skipping already disfigured " << out_root;});
- return false;
- }
-
- bool r (false); // Keep track of whether we actually did anything.
-
- // Disfigure subprojects. Since we don't load buildfiles during
- // disfigure, we do it for all known subprojects.
- //
- if (auto l = root.vars[var_subprojects])
- {
- for (auto p: cast<subprojects> (l))
- {
- const dir_path& pd (p.second);
- dir_path out_nroot (out_root / pd);
- const scope& nroot (scopes.find (out_nroot));
- assert (nroot.out_path () == out_nroot); // See disfigure_load().
-
- r = disfigure_project (a, nroot, projects) || r;
-
- // We use mkdir_p() to create the out_root of a subproject
- // which means there could be empty parent directories left
- // behind. Clean them up.
- //
- if (!pd.simple () && out_root != src_root)
- {
- for (dir_path d (pd.directory ());
- !d.empty ();
- d = d.directory ())
- {
- rmdir_status s (rmdir (out_root / d, 2));
-
- if (s == rmdir_status::not_empty)
- break; // No use trying do remove parent ones.
-
- r = (s == rmdir_status::success) || r;
- }
- }
- }
- }
-
- // We distinguish between a complete disfigure and operation-
- // specific.
- //
- if (a.operation () == default_id)
- {
- l5 ([&]{trace << "completely disfiguring " << out_root;});
-
- r = rmfile (config_file (root)) || r;
-
- if (out_root != src_root)
- {
- r = rmfile (out_root / root.root_extra->src_root_file, 2) || r;
-
- // Clean up the directories.
- //
- // Note: try to remove the root/ hooks directory if it is empty.
- //
- r = rmdir (out_root / root.root_extra->root_dir, 2) || r;
- r = rmdir (out_root / root.root_extra->bootstrap_dir, 2) || r;
- r = rmdir (out_root / root.root_extra->build_dir, 2) || r;
-
- switch (rmdir (out_root))
- {
- case rmdir_status::not_empty:
- {
- // We used to issue a warning but it is actually a valid usecase
- // to leave the build output around in case, for example, of a
- // reconfigure.
- //
- if (verb)
- info << "directory " << out_root << " is "
- << (out_root == work
- ? "current working directory"
- : "not empty") << ", not removing";
- break;
- }
- case rmdir_status::success:
- r = true;
- default:
- break;
- }
- }
- }
- else
- {
- }
-
- return r;
- }
-
- static bool
- disfigure_forward (const scope& root, project_set& projects)
- {
- // Pretty similar logic to disfigure_project().
- //
- tracer trace ("disfigure_forward");
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- if (!projects.insert (&root).second)
- {
- l5 ([&]{trace << "skipping already disfigured " << src_root;});
- return false;
- }
-
- bool r (false);
-
- if (auto l = root.vars[var_subprojects])
- {
- for (auto p: cast<subprojects> (l))
- {
- dir_path out_nroot (out_root / p.second);
- const scope& nroot (scopes.find (out_nroot));
- assert (nroot.out_path () == out_nroot);
-
- r = disfigure_forward (nroot, projects) || r;
- }
- }
-
- // Remove the out-root.build file and try to remove the bootstrap/
- // directory if it is empty.
- //
- r = rmfile (src_root / root.root_extra->out_root_file) || r;
- r = rmdir (src_root / root.root_extra->bootstrap_dir, 2) || r;
-
- return r;
- }
-
- static void
- disfigure_pre (const values& params, const location& l)
- {
- forward (params, "disfigure", l); // Validate.
- }
-
- static operation_id
- disfigure_operation_pre (const values&, operation_id o)
- {
- // Don't translate default to update. In our case unspecified
- // means disfigure everything.
- //
- return o;
- }
-
- static void
- disfigure_load (const values&,
- scope& root,
- const path&,
- const dir_path&,
- const dir_path&,
- const location&)
- {
- // Since we don't load buildfiles during disfigure but still want to
- // disfigure all the subprojects (see disfigure_project() below), we
- // bootstrap all the known subprojects.
- //
- create_bootstrap_inner (root);
- }
-
- static void
- disfigure_search (const values&,
- const scope& root,
- const scope&,
- const path&,
- const target_key&,
- const location&,
- action_targets& ts)
- {
- ts.push_back (&root);
- }
-
- static void
- disfigure_match (const values&, action, action_targets&, uint16_t, bool)
- {
- }
-
- static void
- disfigure_execute (const values& params,
- action a,
- action_targets& ts,
- uint16_t diag,
- bool)
- {
- tracer trace ("disfigure_execute");
-
- bool fwd (forward (params));
-
- project_set projects;
-
- // Note: doing everything in the load phase (disfigure_project () does
- // modify the build state).
- //
- for (const action_target& at: ts)
- {
- const scope& root (*static_cast<const scope*> (at.target));
-
- if (!(fwd
- ? disfigure_forward ( root, projects)
- : disfigure_project (a, root, projects)))
- {
- // Create a dir{$out_root/} target to signify the project's root in
- // diagnostics. Not very clean but seems harmless.
- //
- target& t (
- targets.insert (dir::static_type,
- fwd ? root.src_path () : root.out_path (),
- dir_path (), // Out tree.
- "",
- nullopt,
- true, // Implied.
- trace).first);
-
- if (verb != 0 && diag >= 2)
- info << diag_done (a, t);
- }
- }
- }
-
- const meta_operation_info mo_disfigure {
- disfigure_id,
- "disfigure",
- "disfigure",
- "disfiguring",
- "disfigured",
- "is disfigured",
- false, // bootstrap_outer
- disfigure_pre, // meta-operation pre
- &disfigure_operation_pre,
- &disfigure_load,
- &disfigure_search,
- &disfigure_match,
- &disfigure_execute,
- nullptr, // operation post
- nullptr, // meta-operation post
- nullptr // include
- };
-
- // create
- //
- static void
- save_config (const dir_path& d, const variable_overrides& var_ovs)
- {
- // Since there aren't any sub-projects yet, any config.import.* values
- // that the user may want to specify won't be saved in config.build. So
- // let's go ahead and mark them all to be saved. To do this, however, we
- // need the config module (which is where this information is stored).
- // And the module is created by init() during bootstrap. So what we are
- // going to do is bootstrap the newly created project, similar to the
- // way main() does it.
- //
- scope& gs (*scope::global_);
- scope& rs (load_project (gs, d, d, false /* fwd */, false /* load */));
- module& m (*rs.lookup_module<module> (module::name));
-
- // Save all the global config.import.* variables.
- //
- variable_pool& vp (var_pool.rw (rs));
- for (auto p (gs.vars.find_namespace (vp.insert ("config.import")));
- p.first != p.second;
- ++p.first)
- {
- const variable& var (p.first->first);
-
- // Annoyingly, this can be (always is?) one of the overrides
- // (__override, __prefix, etc).
- //
- size_t n (var.override ());
- m.save_variable (n != 0 ? *vp.find (string (var.name, 0, n)) : var);
- }
-
- // Now project-specific. For now we just save all of them and let
- // save_config() above weed out the ones that don't apply.
- //
- for (const variable_override& vo: var_ovs)
- {
- const variable& var (vo.var);
-
- if (var.name.compare (0, 14, "config.import.") == 0)
- m.save_variable (var);
- }
- }
-
- const string&
- preprocess_create (const variable_overrides& var_ovs,
- values& params,
- vector_view<opspec>& spec,
- bool lifted,
- const location& l)
- {
- tracer trace ("preprocess_create");
-
- // The overall plan is to create the project(s), update the buildspec,
- // clear the parameters, and then continue as if we were the configure
- // meta-operation.
-
- // Start with process parameters. The first parameter, if any, is a list
- // of root.build modules. The second parameter, if any, is a list of
- // bootstrap.build modules. If the second is not specified, then the
- // default is test, dist, and install (config is mandatory).
- //
- strings bmod {"test", "dist", "install"};
- strings rmod;
- try
- {
- size_t n (params.size ());
-
- if (n > 0)
- rmod = convert<strings> (move (params[0]));
-
- if (n > 1)
- bmod = convert<strings> (move (params[1]));
-
- if (n > 2)
- fail (l) << "unexpected parameters for meta-operation create";
- }
- catch (const invalid_argument& e)
- {
- fail (l) << "invalid module name: " << e.what ();
- }
-
- current_oname = empty_string; // Make sure valid.
-
- // Now handle each target in each operation spec.
- //
- for (const opspec& os: spec)
- {
- // First do some sanity checks: there should be no explicit operation
- // and our targets should all be directories.
- //
- if (!lifted && !os.name.empty ())
- fail (l) << "explicit operation specified for meta-operation create";
-
- for (const targetspec& ts: os)
- {
- const name& tn (ts.name);
-
- // Figure out the project directory. This logic must be consistent
- // with find_target_type() and other places (grep for "..").
- //
- dir_path d;
-
- if (tn.simple () &&
- (tn.empty () || tn.value == "." || tn.value == ".."))
- d = dir_path (tn.value);
- else if (tn.directory ())
- d = tn.dir;
- else if (tn.typed () && tn.type == "dir")
- d = tn.dir / dir_path (tn.value);
- else
- fail(l) << "non-directory target '" << ts << "' in "
- << "meta-operation create";
-
- if (d.relative ())
- d = work / d;
-
- d.normalize (true);
-
- // If src_base was explicitly specified, make sure it is the same as
- // the project directory.
- //
- if (!ts.src_base.empty ())
- {
- dir_path s (ts.src_base);
-
- if (s.relative ())
- s = work / s;
-
- s.normalize (true);
-
- if (s != d)
- fail(l) << "different src/out directories for target '" << ts
- << "' in meta-operation create";
- }
-
- l5 ([&]{trace << "creating project in " << d;});
-
- // For now we disable amalgamating this project. Sooner or later
- // someone will probably want to do this, though (i.e., nested
- // configurations).
- //
- create_project (d,
- dir_path (), /* amalgamation */
- bmod,
- "", /* root_pre */
- rmod,
- "", /* root_post */
- true, /* config */
- true, /* buildfile */
- "the create meta-operation");
-
- save_config (d, var_ovs);
- }
- }
-
- params.clear ();
- return mo_configure.name;
- }
- }
-}
diff --git a/build2/config/operation.hxx b/build2/config/operation.hxx
deleted file mode 100644
index 9f426ca..0000000
--- a/build2/config/operation.hxx
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : build2/config/operation.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CONFIG_OPERATION_HXX
-#define BUILD2_CONFIG_OPERATION_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/operation.hxx>
-
-namespace build2
-{
- namespace config
- {
- extern const meta_operation_info mo_configure;
- extern const meta_operation_info mo_disfigure;
-
- const string&
- preprocess_create (const variable_overrides&,
- values&,
- vector_view<opspec>&,
- bool,
- const location&);
- }
-}
-
-#endif // BUILD2_CONFIG_OPERATION_HXX
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
deleted file mode 100644
index 1ce07f7..0000000
--- a/build2/config/utility.cxx
+++ /dev/null
@@ -1,307 +0,0 @@
-// file : build2/config/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/config/utility.hxx>
-
-#include <libbuild2/file.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/filesystem.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <build2/config/module.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace config
- {
- pair<lookup, bool>
- omitted (scope& r, const variable& var)
- {
- // This is a stripped-down version of the required() twisted
- // implementation.
-
- pair<lookup, size_t> org (r.find_original (var));
-
- bool n (false); // New flag.
- lookup l (org.first);
-
- // Treat an inherited value that was set to default as new.
- //
- if (l.defined () && l->extra)
- n = true;
-
- if (var.overrides != nullptr)
- {
- pair<lookup, size_t> ovr (r.find_override (var, move (org)));
-
- if (l != ovr.first) // Overriden?
- {
- // Override is always treated as new.
- //
- n = true;
- l = move (ovr.first);
- }
- }
-
- if (l.defined () && current_mif->id == configure_id)
- save_variable (r, var);
-
- return pair<lookup, bool> (l, n);
- }
-
- lookup
- optional (scope& r, const variable& var)
- {
- if (current_mif->id == configure_id)
- save_variable (r, var);
-
- auto l (r[var]);
- return l.defined ()
- ? l
- : lookup (r.assign (var), var, r); // NULL.
- }
-
- bool
- specified (scope& r, const string& n)
- {
- // Search all outer scopes for any value in this namespace.
- //
- // What about "pure" overrides, i.e., those without any original values?
- // Well, they will also be found since their names have the original
- // variable as a prefix. But do they apply? Yes, since we haven't found
- // any original values, they will be "visible"; see find_override() for
- // details.
- //
- const variable& vns (var_pool.rw (r).insert ("config." + n));
- for (scope* s (&r); s != nullptr; s = s->parent_scope ())
- {
- for (auto p (s->vars.find_namespace (vns));
- p.first != p.second;
- ++p.first)
- {
- const variable& var (p.first->first);
-
- // Ignore config.*.configured.
- //
- if (var.name.size () < 11 ||
- var.name.compare (var.name.size () - 11, 11, ".configured") != 0)
- return true;
- }
- }
-
- return false;
- }
-
- bool
- unconfigured (scope& rs, const string& n)
- {
- // Pattern-typed in boot() as bool.
- //
- const variable& var (
- var_pool.rw (rs).insert ("config." + n + ".configured"));
-
- if (current_mif->id == configure_id)
- save_variable (rs, var);
-
- auto l (rs[var]); // Include inherited values.
- return l && !cast<bool> (l);
- }
-
- bool
- unconfigured (scope& rs, const string& n, bool v)
- {
- // Pattern-typed in boot() as bool.
- //
- const variable& var (
- var_pool.rw (rs).insert ("config." + n + ".configured"));
-
- if (current_mif->id == configure_id)
- save_variable (rs, var);
-
- value& x (rs.assign (var));
-
- if (x.null || cast<bool> (x) != !v)
- {
- x = !v;
- return true;
- }
- else
- return false;
- }
-
- void
- save_variable (scope& r, const variable& var, uint64_t flags)
- {
- if (current_mif->id != configure_id)
- return;
-
- // The project might not be using the config module. But then how
- // could we be configuring it? Good question.
- //
- if (module* m = r.lookup_module<module> (module::name))
- m->save_variable (var, flags);
- }
-
- void
- save_module (scope& r, const char* name, int prio)
- {
- if (current_mif->id != configure_id)
- return;
-
- if (module* m = r.lookup_module<module> (module::name))
- m->save_module (name, prio);
- }
-
- void
- create_project (const dir_path& d,
- const build2::optional<dir_path>& amal,
- const strings& bmod,
- const string& rpre,
- const strings& rmod,
- const string& rpos,
- bool config,
- bool buildfile,
- const char* who,
- uint16_t verbosity)
- {
- string hdr ("# Generated by " + string (who) + ". Edit if you know"
- " what you are doing.\n"
- "#");
-
- // If the directory exists, verify it's empty. Otherwise, create it.
- //
- if (exists (d))
- {
- if (!empty (d))
- fail << "directory " << d << " exists and is not empty";
- }
- else
- mkdir_p (d, verbosity);
-
- // Create the build/ subdirectory.
- //
- // Note that for now we use the standard build file/directory scheme.
- //
- mkdir (d / std_build_dir, verbosity);
-
- // Write build/bootstrap.build.
- //
- {
- path f (d / std_bootstrap_file);
-
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << f;
-
- try
- {
- ofdstream ofs (f);
-
- ofs << hdr << endl
- << "project =" << endl;
-
- if (amal)
- {
- ofs << "amalgamation =";
-
- if (!amal->empty ())
- ofs << ' ' << amal->representation ();
-
- ofs << endl;
- }
-
- ofs << endl;
-
- if (config)
- ofs << "using config" << endl;
-
- for (const string& m: bmod)
- {
- if (!config || m != "config")
- ofs << "using " << m << endl;
- }
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
-
- // Write build/root.build.
- //
- {
- path f (d / std_root_file);
-
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << f;
-
- try
- {
- ofdstream ofs (f);
-
- ofs << hdr << endl;
-
- if (!rpre.empty ())
- ofs << rpre << endl
- << endl;
-
- for (const string& cm: rmod)
- {
- // If the module name start with '?', then use optional load.
- //
- bool opt (cm.front () == '?');
- string m (cm, opt ? 1 : 0);
-
- // Append .config unless the module name ends with '.', in which
- // case strip it.
- //
- if (m.back () == '.')
- m.pop_back ();
- else
- m += ".config";
-
- ofs << "using" << (opt ? "?" : "") << " " << m << endl;
- }
-
- if (!rpos.empty ())
- ofs << endl
- << rpre << endl;
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
-
- // Write root buildfile.
- //
- if (buildfile)
- {
- path f (d / std_buildfile_file);
-
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << f;
-
- try
- {
- ofdstream ofs (f);
-
- ofs << hdr << endl
- << "./: {*/ -build/}" << endl;
-
- ofs.close ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write " << f << ": " << e;
- }
- }
- }
- }
-}
diff --git a/build2/config/utility.hxx b/build2/config/utility.hxx
deleted file mode 100644
index 5e4eac2..0000000
--- a/build2/config/utility.hxx
+++ /dev/null
@@ -1,177 +0,0 @@
-// file : build2/config/utility.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CONFIG_UTILITY_HXX
-#define BUILD2_CONFIG_UTILITY_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/variable.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-namespace build2
-{
- class scope;
-
- namespace config
- {
- // Set, if necessary, a required config.* variable.
- //
- // If override is true and the variable doesn't come from this root scope
- // or from the command line (i.e., it is inherited from the amalgamtion),
- // then its value is "overridden" to the default value on this root scope.
- // See save_variable() for more information on save_flags.
- //
- // Return the reference to the value as well as the indication of whether
- // the value is "new", that is, it was set to the default value (inherited
- // or not, including overrides). We also treat command line overrides
- // (inherited or not) as new. This flag is usually used to test that the
- // new value is valid, print report, etc. We return the value as lookup
- // (always defined) to pass alone its location (could be used to detect
- // inheritance, etc).
- //
- // Note also that if save_flags has save_commented, then a default value
- // is never considered "new" since for such variables absence of a value
- // means the default value.
- //
- template <typename T>
- pair<lookup, bool>
- required (scope& root,
- const variable&,
- const T& default_value,
- bool override = false,
- uint64_t save_flags = 0);
-
- // Note that the variable is expected to have already been registered.
- //
- template <typename T>
- inline pair<lookup, bool>
- required (scope& root,
- const string& name,
- const T& default_value,
- bool override = false,
- uint64_t save_flags = 0)
- {
- return required (
- root, var_pool[name], default_value, override, save_flags);
- }
-
- inline pair<lookup, bool>
- required (scope& root,
- const string& name,
- const char* default_value,
- bool override = false,
- uint64_t save_flags = 0)
- {
- return required (
- root, name, string (default_value), override, save_flags);
- }
-
- // As above, but leave the unspecified value as undefined rather than
- // setting it to the default value.
- //
- // This can be useful when we don't have a default value but may figure
- // out some fallback. See config.bin.target for an example.
- //
- pair<lookup, bool>
- omitted (scope& root, const variable&);
-
- // Note that the variable is expected to have already been registered.
- //
- inline pair<lookup, bool>
- omitted (scope& root, const string& name)
- {
- return omitted (root, var_pool[name]);
- }
-
- // Set, if necessary, an optional config.* variable. In particular, an
- // unspecified variable is set to NULL which is used to distinguish
- // between the "configured as unspecified" and "not yet configured" cases.
- //
- // Return the value (as always defined lookup), which can be NULL.
- //
- // @@ Rename since clashes with the optional class template.
- //
- lookup
- optional (scope& root, const variable&);
-
- // Note that the variable is expected to have already been registered.
- //
- inline lookup
- optional (scope& root, const string& name)
- {
- return optional (root, var_pool[name]);
- }
-
- // Check whether there are any variables specified from the config
- // namespace. The idea is that we can check if there are any, say,
- // config.install.* values. If there are none, then we can assume
- // this functionality is not (yet) used and omit writing a whole
- // bunch of NULL config.install.* values to the config.build file.
- // We call it omitted/delayed configuration.
- //
- // Note that this function detects and ignores the special
- // config.*.configured variable which may be used by a module to
- // "remember" that it is unconfigured (e.g., in order to avoid re-
- // running the tests, etc).
- //
- bool
- specified (scope& root, const string& name);
-
- // Check if there is a false config.*.configured value. This mechanism can
- // be used to "remember" that the module is left unconfigured in order to
- // avoid re-running the tests, etc.
- //
- bool
- unconfigured (scope& root, const string& name);
-
- // Set the config.*.configured value. Note that you only need to set it to
- // false. It will be automatically ignored if there are any other config.*
- // values for this module. Return true if this sets a new value.
- //
- bool
- unconfigured (scope& root, const string& name, bool);
-
- // Enter the variable so that it is saved during configuration. See
- // config::module for details.
- //
- const uint64_t save_commented = 0x01; // Save default value as commented.
-
- void
- save_variable (scope& root, const variable&, uint64_t flags = 0);
-
- // Establish module order/priority. See config::module for details.
- //
- void
- save_module (scope& root, const char* name, int prio = 0);
-
- // Create a project in the specified directory.
- //
- void
- create_project (const dir_path& d,
- const build2::optional<dir_path>& amalgamation,
- const strings& boot_modules, // Bootstrap modules.
- const string& root_pre, // Extra root.build text.
- const strings& root_modules, // Root modules.
- const string& root_post, // Extra root.build text.
- bool config, // Load config module.
- bool buildfile, // Create root buildfile.
- const char* who, // Who is creating it.
- uint16_t verbosity = 1); // Diagnostic verbosity.
-
- inline path
- config_file (const scope& root)
- {
- return (root.out_path () /
- root.root_extra->build_dir /
- "config." + root.root_extra->build_ext);
- }
- }
-}
-
-#include <build2/config/utility.txx>
-
-#endif // BUILD2_CONFIG_UTILITY_HXX
diff --git a/build2/config/utility.txx b/build2/config/utility.txx
deleted file mode 100644
index 84650d9..0000000
--- a/build2/config/utility.txx
+++ /dev/null
@@ -1,66 +0,0 @@
-// file : build2/config/utility.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/context.hxx>
-
-namespace build2
-{
- namespace config
- {
- template <typename T>
- pair<lookup, bool>
- required (scope& root,
- const variable& var,
- const T& def_val,
- bool def_ovr,
- uint64_t save_flags)
- {
- // Note: see also omitted() if changing anything here.
-
- if (current_mif->id == configure_id)
- save_variable (root, var, save_flags);
-
- pair<lookup, size_t> org (root.find_original (var));
-
- bool n (false); // New flag.
- lookup l (org.first);
-
- // The interaction with command line overrides can get tricky. For
- // example, the override to defaul value could make (non-recursive)
- // command line override in the outer scope no longer apply. So what we
- // are going to do is first ignore overrides and perform the normal
- // logic on the original. Then we apply the overrides on the result.
- //
- if (!l.defined () || (def_ovr && !l.belongs (root)))
- {
- value& v (root.assign (var) = def_val);
- v.extra = true; // Default value flag.
-
- n = (save_flags & save_commented) == 0; // Absence means default.
- l = lookup (v, var, root);
- org = make_pair (l, 1); // Lookup depth is 1 since it's in root.vars.
- }
- // Treat an inherited value that was set to default as new.
- //
- else if (l->extra)
- n = (save_flags & save_commented) == 0; // Absence means default.
-
- if (var.overrides != nullptr)
- {
- pair<lookup, size_t> ovr (root.find_override (var, move (org)));
-
- if (l != ovr.first) // Overriden?
- {
- // Override is always treated as new.
- //
- n = true;
- l = move (ovr.first);
- }
- }
-
- return pair<lookup, bool> (l, n);
- }
- }
-}