diff options
Diffstat (limited to 'build2/config')
-rw-r--r-- | build2/config/init.cxx | 148 | ||||
-rw-r--r-- | build2/config/init.hxx | 31 | ||||
-rw-r--r-- | build2/config/module.cxx | 54 | ||||
-rw-r--r-- | build2/config/module.hxx | 93 | ||||
-rw-r--r-- | build2/config/operation.cxx | 997 | ||||
-rw-r--r-- | build2/config/operation.hxx | 29 | ||||
-rw-r--r-- | build2/config/utility.cxx | 307 | ||||
-rw-r--r-- | build2/config/utility.hxx | 177 | ||||
-rw-r--r-- | build2/config/utility.txx | 66 |
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); - } - } -} |