From 9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 5 Jan 2016 11:55:15 +0200 Subject: Rename build directory/namespace to build2 --- build2/config/module | 26 +++ build2/config/module.cxx | 90 +++++++++ build2/config/operation | 19 ++ build2/config/operation.cxx | 455 ++++++++++++++++++++++++++++++++++++++++++++ build2/config/utility | 128 +++++++++++++ build2/config/utility.cxx | 92 +++++++++ build2/config/utility.ixx | 17 ++ build2/config/utility.txx | 45 +++++ 8 files changed, 872 insertions(+) create mode 100644 build2/config/module create mode 100644 build2/config/module.cxx create mode 100644 build2/config/operation create mode 100644 build2/config/operation.cxx create mode 100644 build2/config/utility create mode 100644 build2/config/utility.cxx create mode 100644 build2/config/utility.ixx create mode 100644 build2/config/utility.txx (limited to 'build2/config') diff --git a/build2/config/module b/build2/config/module new file mode 100644 index 0000000..5888376 --- /dev/null +++ b/build2/config/module @@ -0,0 +1,26 @@ +// file : build2/config/module -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CONFIG_MODULE +#define BUILD2_CONFIG_MODULE + +#include +#include + +#include + +namespace build2 +{ + namespace config + { + extern "C" void + config_boot (scope&, const location&, unique_ptr&); + + extern "C" bool + config_init ( + scope&, scope&, const location&, unique_ptr&, bool, bool); + } +} + +#endif // BUILD2_CONFIG_MODULE diff --git a/build2/config/module.cxx b/build2/config/module.cxx new file mode 100644 index 0000000..3d03a51 --- /dev/null +++ b/build2/config/module.cxx @@ -0,0 +1,90 @@ +// file : build2/config/module.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace config + { + //@@ Same as in operation.cxx + // + static const path config_file ("build/config.build"); + + extern "C" void + config_boot (scope& root, const location&, unique_ptr&) + { + tracer trace ("config::boot"); + + const dir_path& out_root (root.out_path ()); + level5 ([&]{trace << "for " << out_root;}); + + // Register meta-operations. + // + root.meta_operations.insert (configure_id, configure); + root.meta_operations.insert (disfigure_id, disfigure); + + // Load config.build if one exists. + // + // Note that we have to do this during bootstrap since the order in + // which the modules will be initialized is unspecified. So it is + // possible that some module which needs the configuration will get + // called first. + // + path f (out_root / config_file); + + if (file_exists (f)) + source (f, root, root); + } + + extern "C" bool + config_init (scope& root, + scope&, + const location& l, + std::unique_ptr&, + bool first, + bool) + { + tracer trace ("config::init"); + + if (!first) + { + warn (l) << "multiple config module initializations"; + return true; + } + + level5 ([&]{trace << "for " << root.out_path ();}); + + // 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). + // + global_scope->rules.insert ( + configure_id, 0, "config.file", file_rule::instance); + + auto& r (root.rules); + + r.insert (configure_id, 0, "config", fallback_rule::instance); + r.insert (configure_id, 0, "config.file", fallback_rule::instance); + r.insert (configure_id, 0, "config.alias", alias_rule::instance); + } + + return true; + } + } +} diff --git a/build2/config/operation b/build2/config/operation new file mode 100644 index 0000000..63eba7e --- /dev/null +++ b/build2/config/operation @@ -0,0 +1,19 @@ +// file : build2/config/operation -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CONFIG_OPERATION +#define BUILD2_CONFIG_OPERATION + +#include + +namespace build2 +{ + namespace config + { + extern meta_operation_info configure; + extern meta_operation_info disfigure; + } +} + +#endif // BUILD2_CONFIG_OPERATION diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx new file mode 100644 index 0000000..96d9be7 --- /dev/null +++ b/build2/config/operation.cxx @@ -0,0 +1,455 @@ +// file : build2/config/operation.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace config + { + static const path config_file ("build/config.build"); + + // configure + // + static operation_id + configure_operation_pre (operation_id o) + { + // Don't translate default to update. In our case unspecified + // means configure everything. + // + return o; + } + + static void + save_src_root (const dir_path& out_root, const dir_path& src_root) + { + path f (out_root / src_root_file); + + if (verb) + text << (verb >= 2 ? "config::save_src_root " : "save ") << f; + + try + { + ofstream ofs (f.string ()); + if (!ofs.is_open ()) + fail << "unable to open " << f; + + ofs.exceptions (ofstream::failbit | ofstream::badbit); + + //@@ TODO: quote path + // + ofs << "# Created automatically by the config module." << endl + << "#" << endl + << "src_root = " << src_root << endl; + } + catch (const ofstream::failure&) + { + fail << "unable to write " << f; + } + } + + static void + save_config (scope& root) + { + const dir_path& out_root (root.out_path ()); + path f (out_root / config_file); + + if (verb) + text << (verb >= 2 ? "config::save_config " : "save ") << f; + + try + { + ofstream ofs (f.string ()); + if (!ofs.is_open ()) + fail << "unable to open " << f; + + ofs.exceptions (ofstream::failbit | ofstream::badbit); + + ofs << "# Created automatically by the config module, but" << endl + << "# feel free to edit." << endl + << "#" << endl; + + if (auto l = root.vars["amalgamation"]) + { + const dir_path& d (as (*l)); + + ofs << "# Base configuration inherited from " << d << endl + << "#" << endl; + } + + // Save all the variables in the config namespace that are set + // on the project's root scope. + // + for (auto p (root.vars.find_namespace ("config")); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + const value& val (p.first->second); + const string& n (var.name); + + // Skip special variables. + // + if (n == "config.loaded" || + n == "config.configured") + continue; + + // We will only write config.*.configured if it is false + // (true is implied by its absence). + // + if (n.size () > 11 && + n.compare (n.size () - 11, 11, ".configured") == 0) + { + if (val == nullptr || as (val)) + continue; + } + + // Warn the user if the value that we are saving differs + // from the one they specified on the command line. + // + auto l ((*global_scope)[var]); + if (l.defined () && *l != val) + { + warn << "variable " << var.name << " configured value " + << "differs from command line value" << + info << "reconfigure the project to use command line value"; + } + + if (val) + { + ofs << var.name << " = " << val.data_ << endl; + //text << var.name << " = " << val.data_; + } + else + { + ofs << var.name << " = #[null]" << endl; // @@ TODO: [null] + //text << var.name << " = [null]"; + } + } + } + catch (const ofstream::failure&) + { + fail << "unable to write " << f; + } + } + + static void + configure_project (action a, scope& root) + { + tracer trace ("configure_project"); + + const dir_path& out_root (root.out_path ()); + const dir_path& src_root (root.src_path ()); + + // Make sure the directories exist. + // + if (out_root != src_root) + { + mkdir_p (out_root); + mkdir (out_root / build_dir); + mkdir (out_root / bootstrap_dir); + } + + // We distinguish between a complete configure and operation- + // specific. + // + if (a.operation () == default_id) + { + level5 ([&]{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 (out_root, src_root); + + // Save config.build. + // + save_config (root); + } + else + { + } + + // Configure subprojects that have been loaded. + // + if (auto l = root.vars["subprojects"]) + { + for (auto p: as (*l)) + { + const dir_path& pd (p.second); + dir_path out_nroot (out_root / pd); + 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); + } + } + } + + static void + configure_match (action, action_targets&) + { + // Don't match anything -- see execute (). + } + + static void + configure_execute (action a, const action_targets& ts, bool) + { + // 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. + // + for (void* v: ts) + { + target& t (*static_cast (v)); + scope* rs (t.base_scope ().root_scope ()); + + if (rs == nullptr) + fail << "out of project target " << t; + + for (operations::size_type id (default_id + 1); // Skip default_id + id < rs->operations.size (); + ++id) + { + const operation_info* oi (rs->operations[id]); + if (oi == nullptr) + continue; + + current_inner_oif = oi; + current_outer_oif = nullptr; + current_mode = oi->mode; + dependency_count = 0; + + match (action (configure_id, id), t); + } + + configure_project (a, *rs); + } + } + + meta_operation_info configure { + "configure", + "configure", + "configuring", + "is configured", + nullptr, // meta-operation pre + &configure_operation_pre, + &load, // normal load + &search, // normal search + &configure_match, + &configure_execute, + nullptr, // operation post + nullptr // meta-operation post + }; + + // disfigure + // + static operation_id + disfigure_operation_pre (operation_id o) + { + // Don't translate default to update. In our case unspecified + // means disfigure everything. + // + return o; + } + + static void + disfigure_load (const path& bf, + scope&, + const dir_path&, + const dir_path&, + const location&) + { + tracer trace ("disfigure_load"); + level6 ([&]{trace << "skipping " << bf;}); + } + + static void + disfigure_search (scope& root, + const target_key&, + const location&, + action_targets& ts) + { + tracer trace ("disfigure_search"); + level6 ([&]{trace << "collecting " << root.out_path ();}); + ts.push_back (&root); + } + + static void + disfigure_match (action, action_targets&) {} + + static bool + disfigure_project (action a, scope& root) + { + tracer trace ("disfigure_project"); + + bool m (false); // Keep track of whether we actually did anything. + + const dir_path& out_root (root.out_path ()); + const dir_path& src_root (root.src_path ()); + + // Disfigure subprojects. Since we don't load buildfiles during + // disfigure, we do it for all known subprojects. + // + if (auto l = root.vars["subprojects"]) + { + for (auto p: as (*l)) + { + const dir_path& pd (p.second); + + // Create and bootstrap subproject's root scope. + // + dir_path out_nroot (out_root / pd); + + // The same logic for src_root as in create_bootstrap_inner(). + // + scope& nroot (create_root (out_nroot, dir_path ())); + bootstrap_out (nroot); + + value& val (nroot.assign ("src_root")); + + if (!val) + val = is_src_root (out_nroot) ? out_nroot : (src_root / pd); + + setup_root (nroot); + + bootstrap_src (nroot); + + m = disfigure_project (a, nroot) || m; + + // 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)); + + if (s == rmdir_status::not_empty) + break; // No use trying do remove parent ones. + + m = (s == rmdir_status::success) || m; + } + } + } + } + + // We distinguish between a complete disfigure and operation- + // specific. + // + if (a.operation () == default_id) + { + level5 ([&]{trace << "completely disfiguring " << out_root;}); + + m = rmfile (out_root / config_file) || m; + + if (out_root != src_root) + { + m = rmfile (out_root / src_root_file) || m; + + // Clean up the directories. + // + m = rmdir (out_root / bootstrap_dir) || m; + m = rmdir (out_root / build_dir) || m; + + switch (rmdir (out_root)) + { + case rmdir_status::not_empty: + { + warn << "directory " << out_root << " is " + << (out_root == work + ? "current working directory" + : "not empty") << ", not removing"; + break; + } + case rmdir_status::success: + m = true; + default: + break; + } + } + } + else + { + } + + return m; + } + + static void + disfigure_execute (action a, const action_targets& ts, bool quiet) + { + tracer trace ("disfigure_execute"); + + for (void* v: ts) + { + scope& root (*static_cast (v)); + + if (!disfigure_project (a, root)) + { + // 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, root.out_path (), "", nullptr, trace).first); + + if (!quiet) + info << diag_done (a, t); + } + } + } + + static void + disfigure_meta_operation_post () + { + tracer trace ("disfigure_meta_operation_post"); + + // Reset the dependency state since anything that could have been + // loaded earlier using a previous configuration is now invalid. + // + level6 ([&]{trace << "resetting dependency state";}); + reset (); + } + + meta_operation_info disfigure { + "disfigure", + "disfigure", + "disfiguring", + "is disfigured", + nullptr, // meta-operation pre + &disfigure_operation_pre, + &disfigure_load, + &disfigure_search, + &disfigure_match, + &disfigure_execute, + nullptr, // operation post + &disfigure_meta_operation_post + }; + } +} diff --git a/build2/config/utility b/build2/config/utility new file mode 100644 index 0000000..608cf5e --- /dev/null +++ b/build2/config/utility @@ -0,0 +1,128 @@ +// file : build2/config/utility -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CONFIG_UTILITY +#define BUILD2_CONFIG_UTILITY + +#include +#include // pair +#include // reference_wrapper + +#include +#include +#include + +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, then its value is "overridden" + // for this root scope. + // + // Return the reference to the value as well as the indication of + // whether the variable has actually been set. + // + template + std::pair, bool> + required (scope& root, + const variable&, + const T& default_value, + bool override = false); + + template + inline std::pair, bool> + required (scope& root, + const std::string& name, + const T& default_value, + bool override = false) + { + return required (root, var_pool.find (name), default_value, override); + } + + inline std::pair, bool> + required (scope& root, + const std::string& name, + const char* default_value, + bool override = false) + { + return required (root, name, std::string (default_value), override); + } + + // 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, which can be NULL. + // + const value& + optional (scope& root, const variable&); + + inline const value& + optional (scope& root, const std::string& var) + { + return optional (root, var_pool.find (var)); + } + + // As above but assumes the value is dir_path and makes it + // absolute if the value specified on the command line is + // relative. + // + const value& + optional_absolute (scope& root, const variable&); + + inline const value& + optional_absolute (scope& root, const std::string& var) + { + return optional_absolute (root, var_pool.find (var)); + } + + // 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. + // + bool + specified (scope& root, const std::string& ns); + + // @@ Why are these here? + // + + // Add all the values from a variable to the C-string list. T is + // either target or scope. The variable is expected to be of type + // strings. + // + template + void + append_options (cstrings& args, T& s, const char* var); + + // As above but from the strings value directly. + // + void + append_options (cstrings& args, const const_strings_value&); + + // Check if a specified option is present in the variable value. + // T is either target or scope. + // + template + bool + find_option (const char* option, T& s, const char* var); + } +} + +#include +#include + +#endif // BUILD2_CONFIG_UTILITY diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx new file mode 100644 index 0000000..361bd5e --- /dev/null +++ b/build2/config/utility.cxx @@ -0,0 +1,92 @@ +// file : build2/config/utility.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +namespace build2 +{ + namespace config + { + const value& + optional (scope& root, const variable& var) + { + auto l (root[var]); + + return l.defined () + ? l.belongs (*global_scope) ? (root.assign (var) = *l) : *l + : root.assign (var); // NULL + } + + const value& + optional_absolute (scope& root, const variable& var) + { + auto l (root[var]); + + if (!l.defined ()) + return root.assign (var); // NULL + + if (!l.belongs (*global_scope)) // Value from (some) root scope. + return *l; + + // Make the command-line value absolute. This is necessary to avoid + // a warning issued by the config module about global/root scope + // value mismatch. + // + value& v (const_cast (*l)); + + if (v && !v.empty ()) + { + dir_path& d (as (v)); + + if (d.relative ()) + { + d = work / d; + d.normalize (); + } + } + + return root.assign (var) = v; + } + + bool + specified (scope& r, const string& ns) + { + // Search all outer scopes for any value in this namespace. + // + for (scope* s (&r); s != nullptr; s = s->parent_scope ()) + { + for (auto p (s->vars.find_namespace (ns)); + 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; + } + + void + append_options (cstrings& args, const const_strings_value& sv) + { + if (!sv.empty ()) + { + args.reserve (args.size () + sv.size ()); + + for (const string& s: sv) + args.push_back (s.c_str ()); + } + } + } +} diff --git a/build2/config/utility.ixx b/build2/config/utility.ixx new file mode 100644 index 0000000..b665248 --- /dev/null +++ b/build2/config/utility.ixx @@ -0,0 +1,17 @@ +// file : build2/config/utility.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + namespace config + { + template + inline void + append_options (cstrings& args, T& s, const char* var) + { + if (auto l = s[var]) + append_options (args, as (*l)); + } + } +} diff --git a/build2/config/utility.txx b/build2/config/utility.txx new file mode 100644 index 0000000..c88a34f --- /dev/null +++ b/build2/config/utility.txx @@ -0,0 +1,45 @@ +// file : build2/config/utility.txx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +namespace build2 +{ + namespace config + { + template + std::pair, bool> + required (scope& root, const variable& var, const T& def_value, bool ovr) + { + using result = std::pair, bool>; + + if (auto l = root[var]) + { + if (l.belongs (*global_scope)) + return result (root.assign (var) = *l, true); + + if (!ovr || l.belongs (root)) + return result (*l, false); + } + + return result (root.assign (var) = def_value, true); + } + + template + bool + find_option (const char* option, T& s, const char* var) + { + if (auto l = s[var]) + { + for (const std::string& s: as (*l)) + { + if (s == option) + return true; + } + } + + return false; + } + } +} -- cgit v1.1