diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-02 11:37:15 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-02 11:37:15 +0200 |
commit | 9891b20350021ce41a950645dd76df20a45c92cc (patch) | |
tree | 0cd27041b0c3413e17b9319ae99e87c5e745b1ff | |
parent | 74212589a797ca75e55f92a522e198915c0dbaf6 (diff) |
Implement optional module loading
The syntax is:
using? cli
Now each module use results in two bool variables: <module>.loaded and
<module>.configured.
Also implement variable visibility (the above two variables are limited
to project).
-rw-r--r-- | build/bin/module | 7 | ||||
-rw-r--r-- | build/bin/module.cxx | 25 | ||||
-rw-r--r-- | build/cli/module | 7 | ||||
-rw-r--r-- | build/cli/module.cxx | 23 | ||||
-rw-r--r-- | build/config/module | 6 | ||||
-rw-r--r-- | build/config/module.cxx | 9 | ||||
-rw-r--r-- | build/config/utility | 7 | ||||
-rw-r--r-- | build/context.cxx | 22 | ||||
-rw-r--r-- | build/cxx/module | 7 | ||||
-rw-r--r-- | build/cxx/module.cxx | 67 | ||||
-rw-r--r-- | build/dist/module | 6 | ||||
-rw-r--r-- | build/dist/module.cxx | 27 | ||||
-rw-r--r-- | build/dist/operation.cxx | 2 | ||||
-rw-r--r-- | build/file.cxx | 5 | ||||
-rw-r--r-- | build/install/module | 6 | ||||
-rw-r--r-- | build/install/module.cxx | 17 | ||||
-rw-r--r-- | build/module | 19 | ||||
-rw-r--r-- | build/module.cxx | 37 | ||||
-rw-r--r-- | build/parser.cxx | 15 | ||||
-rw-r--r-- | build/scope | 21 | ||||
-rw-r--r-- | build/scope.cxx | 38 | ||||
-rw-r--r-- | build/target | 4 | ||||
-rw-r--r-- | build/target.cxx | 15 | ||||
-rw-r--r-- | build/target.txx | 2 | ||||
-rw-r--r-- | build/test/module | 4 | ||||
-rw-r--r-- | build/test/module.cxx | 23 | ||||
-rw-r--r-- | build/test/rule.cxx | 12 | ||||
-rw-r--r-- | build/utility | 3 | ||||
-rw-r--r-- | build/variable | 82 | ||||
-rw-r--r-- | build/variable.cxx | 2 | ||||
-rw-r--r-- | tests/using/buildfile | 5 |
31 files changed, 332 insertions, 193 deletions
diff --git a/build/bin/module b/build/bin/module index b87a0b0..5dd720f 100644 --- a/build/bin/module +++ b/build/bin/module @@ -6,14 +6,17 @@ #define BUILD_BIN_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace bin { - extern "C" void - bin_init (scope&, scope&, const location&, std::unique_ptr<module>&, bool); + extern "C" bool + bin_init ( + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/bin/module.cxx b/build/bin/module.cxx index 25700c6..c7da647 100644 --- a/build/bin/module.cxx +++ b/build/bin/module.cxx @@ -29,12 +29,13 @@ namespace build static const strings liba_lib {"static"}; static const strings libso_lib {"shared"}; - extern "C" void + extern "C" bool bin_init (scope& r, scope& b, const location&, std::unique_ptr<module>&, - bool first) + bool first, + bool) { tracer trace ("bin::init"); level5 ([&]{trace << "for " << b.out_path ();}); @@ -81,15 +82,15 @@ namespace build // if (first) { - variable_pool.find ("config.bin.lib", string_type); - variable_pool.find ("config.bin.exe.lib", strings_type); - variable_pool.find ("config.bin.liba.lib", strings_type); - variable_pool.find ("config.bin.libso.lib", strings_type); - - variable_pool.find ("bin.lib", string_type); - variable_pool.find ("bin.exe.lib", strings_type); - variable_pool.find ("bin.liba.lib", strings_type); - variable_pool.find ("bin.libso.lib", strings_type); + var_pool.find ("config.bin.lib", string_type); + var_pool.find ("config.bin.exe.lib", strings_type); + var_pool.find ("config.bin.liba.lib", strings_type); + var_pool.find ("config.bin.libso.lib", strings_type); + + var_pool.find ("bin.lib", string_type); + var_pool.find ("bin.exe.lib", strings_type); + var_pool.find ("bin.liba.lib", strings_type); + var_pool.find ("bin.libso.lib", strings_type); } // Configure. @@ -164,6 +165,8 @@ namespace build install::path<liba> (b, dir_path ("lib")); // Install into install.lib. install::mode<liba> (b, "644"); + + return true; } } } diff --git a/build/cli/module b/build/cli/module index cf6258f..221e6a0 100644 --- a/build/cli/module +++ b/build/cli/module @@ -6,14 +6,17 @@ #define BUILD_CLI_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace cli { - extern "C" void - cli_init (scope&, scope&, const location&, std::unique_ptr<module>&, bool); + extern "C" bool + cli_init ( + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/cli/module.cxx b/build/cli/module.cxx index 64e95a8..081980b 100644 --- a/build/cli/module.cxx +++ b/build/cli/module.cxx @@ -28,12 +28,13 @@ namespace build { static compile compile_; - extern "C" void + extern "C" bool cli_init (scope& root, scope& base, - const location& l, + const location& loc, std::unique_ptr<module>&, - bool first) + bool first, + bool) { tracer trace ("cli::init"); level5 ([&]{trace << "for " << base.out_path ();}); @@ -44,8 +45,12 @@ namespace build // semantics. So it is better to let the user load cxx // explicitly. // - if (base.find_target_type ("cxx") == nullptr) - fail (l) << "cxx module must be initialized before cli"; + { + auto l (base["cxx.loaded"]); + + if (!l || !as<bool> (*l)) + fail (loc) << "cxx module must be loaded before cli"; + } // Register target types. // @@ -84,10 +89,10 @@ namespace build // if (first) { - variable_pool.find ("config.cli", string_type); //@@ VAR type + var_pool.find ("config.cli", string_type); //@@ VAR type - variable_pool.find ("config.cli.options", strings_type); - variable_pool.find ("cli.options", strings_type); + var_pool.find ("config.cli.options", strings_type); + var_pool.find ("cli.options", strings_type); } // Configure. @@ -156,6 +161,8 @@ namespace build // if (const value& v = config::optional (root, "config.cli.options")) base.assign ("cli.options") += as<strings> (v); + + return true; } } } diff --git a/build/config/module b/build/config/module index 58d9814..530fa7b 100644 --- a/build/config/module +++ b/build/config/module @@ -6,15 +6,17 @@ #define BUILD_CONFIG_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace config { - extern "C" void + extern "C" bool config_init ( - scope&, scope&, const location&, std::unique_ptr<module>&, bool); + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/config/module.cxx b/build/config/module.cxx index bb787a0..b95c6c7 100644 --- a/build/config/module.cxx +++ b/build/config/module.cxx @@ -24,12 +24,13 @@ namespace build // static const path config_file ("build/config.build"); - extern "C" void + extern "C" bool config_init (scope& r, scope& b, const location& l, std::unique_ptr<module>&, - bool first) + bool first, + bool) { tracer trace ("config::init"); @@ -39,7 +40,7 @@ namespace build if (!first) { warn (l) << "multiple config module initializations"; - return; + return true; } const dir_path& out_root (r.out_path ()); @@ -65,6 +66,8 @@ namespace build if (file_exists (f)) source (f, r, r); + + return true; } } } diff --git a/build/config/utility b/build/config/utility index 9a5dc5e..ece7a88 100644 --- a/build/config/utility +++ b/build/config/utility @@ -42,8 +42,7 @@ namespace build const T& default_value, bool override = false) { - return required ( - root, variable_pool.find (name), default_value, override); + return required (root, var_pool.find (name), default_value, override); } inline std::pair<std::reference_wrapper<const value>, bool> @@ -68,7 +67,7 @@ namespace build inline const value& optional (scope& root, const std::string& var) { - return optional (root, variable_pool.find (var)); + return optional (root, var_pool.find (var)); } // As above but assumes the value is dir_path and makes it @@ -81,7 +80,7 @@ namespace build inline const value& optional_absolute (scope& root, const std::string& var) { - return optional_absolute (root, variable_pool.find (var)); + return optional_absolute (root, var_pool.find (var)); } // Check whether there are any variables specified from the diff --git a/build/context.cxx b/build/context.cxx index 699cc3c..7f30c81 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -39,7 +39,7 @@ namespace build targets.clear (); scopes.clear (); - variable_pool.clear (); + var_pool.clear (); // Reset meta/operation tables. Note that the order should match // the id constants in <build/operation>. @@ -59,22 +59,22 @@ namespace build // Enter builtin variables. // - variable_pool.find ("work", dir_path_type); - variable_pool.find ("home", dir_path_type); + var_pool.find ("work", dir_path_type); + var_pool.find ("home", dir_path_type); - variable_pool.find ("src_root", dir_path_type); - variable_pool.find ("out_root", dir_path_type); - variable_pool.find ("src_base", dir_path_type); - variable_pool.find ("out_base", dir_path_type); + var_pool.find ("src_root", dir_path_type); + var_pool.find ("out_root", dir_path_type); + var_pool.find ("src_base", dir_path_type); + var_pool.find ("out_base", dir_path_type); - variable_pool.find ("project", string_type); - variable_pool.find ("amalgamation", dir_path_type); + var_pool.find ("project", string_type); + var_pool.find ("amalgamation", dir_path_type); // Shouldn't be typed since the value requires pre-processing. // - variable_pool.find ("subprojects", nullptr, '='); + var_pool.find ("subprojects", nullptr, '='); - variable_pool.find ("extension", string_type); + var_pool.find ("extension", string_type); // Create global scope. For Win32 this is not a "real" root path. // On POSIX, however, this is a real path. See the comment in diff --git a/build/cxx/module b/build/cxx/module index dcadcdd..a3bd436 100644 --- a/build/cxx/module +++ b/build/cxx/module @@ -6,14 +6,17 @@ #define BUILD_CXX_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace cxx { - extern "C" void - cxx_init (scope&, scope&, const location&, std::unique_ptr<module>&, bool); + extern "C" bool + cxx_init ( + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx index eda3593..4c0e493 100644 --- a/build/cxx/module.cxx +++ b/build/cxx/module.cxx @@ -27,12 +27,13 @@ namespace build { namespace cxx { - extern "C" void + extern "C" bool cxx_init (scope& r, scope& b, - const location& l, + const location& loc, std::unique_ptr<module>&, - bool first) + bool first, + bool) { tracer trace ("cxx::init"); level5 ([&]{trace << "for " << b.out_path ();}); @@ -40,8 +41,12 @@ namespace build // Initialize the bin module. Only do this if it hasn't already // been loaded so that we don't overwrite user's bin.* settings. // - if (b.find_target_type ("obj") == nullptr) - load_module ("bin", r, b, l); + { + auto l (b["bin.loaded"]); + + if (!l || !as<bool> (*l)) + load_module (false, "bin", r, b, loc); + } // Register target types. // @@ -105,31 +110,31 @@ namespace build // if (first) { - variable_pool.find ("config.cxx", string_type); //@@ VAR type - - variable_pool.find ("config.cxx.poptions", strings_type); - variable_pool.find ("config.cxx.coptions", strings_type); - variable_pool.find ("config.cxx.loptions", strings_type); - variable_pool.find ("config.cxx.libs", strings_type); - - variable_pool.find ("cxx.poptions", strings_type); - variable_pool.find ("cxx.coptions", strings_type); - variable_pool.find ("cxx.loptions", strings_type); - variable_pool.find ("cxx.libs", strings_type); - - variable_pool.find ("cxx.export.poptions", strings_type); - variable_pool.find ("cxx.export.coptions", strings_type); - variable_pool.find ("cxx.export.loptions", strings_type); - variable_pool.find ("cxx.export.libs", strings_type); - - variable_pool.find ("cxx.std", string_type); - - variable_pool.find ("h.ext", string_type); - variable_pool.find ("c.ext", string_type); - variable_pool.find ("hxx.ext", string_type); - variable_pool.find ("ixx.ext", string_type); - variable_pool.find ("txx.ext", string_type); - variable_pool.find ("cxx.ext", string_type); + var_pool.find ("config.cxx", string_type); //@@ VAR type + + var_pool.find ("config.cxx.poptions", strings_type); + var_pool.find ("config.cxx.coptions", strings_type); + var_pool.find ("config.cxx.loptions", strings_type); + var_pool.find ("config.cxx.libs", strings_type); + + var_pool.find ("cxx.poptions", strings_type); + var_pool.find ("cxx.coptions", strings_type); + var_pool.find ("cxx.loptions", strings_type); + var_pool.find ("cxx.libs", strings_type); + + var_pool.find ("cxx.export.poptions", strings_type); + var_pool.find ("cxx.export.coptions", strings_type); + var_pool.find ("cxx.export.loptions", strings_type); + var_pool.find ("cxx.export.libs", strings_type); + + var_pool.find ("cxx.std", string_type); + + var_pool.find ("h.ext", string_type); + var_pool.find ("c.ext", string_type); + var_pool.find ("hxx.ext", string_type); + var_pool.find ("ixx.ext", string_type); + var_pool.find ("txx.ext", string_type); + var_pool.find ("cxx.ext", string_type); } // Configure. @@ -220,6 +225,8 @@ namespace build path<txx> (b, dir_path ("include")); path<h> (b, dir_path ("include")); } + + return true; } } } diff --git a/build/dist/module b/build/dist/module index 9fad86f..20082ec 100644 --- a/build/dist/module +++ b/build/dist/module @@ -6,15 +6,17 @@ #define BUILD_DIST_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace dist { - extern "C" void + extern "C" bool dist_init ( - scope&, scope&, const location&, std::unique_ptr<module>&, bool); + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/dist/module.cxx b/build/dist/module.cxx index 9bd058a..a624c20 100644 --- a/build/dist/module.cxx +++ b/build/dist/module.cxx @@ -22,12 +22,13 @@ namespace build { static rule rule_; - extern "C" void + extern "C" bool dist_init (scope& r, scope& b, const location& l, - std::unique_ptr<module>&, - bool first) + unique_ptr<module>&, + bool first, + bool) { tracer trace ("dist::init"); @@ -37,7 +38,7 @@ namespace build if (!first) { warn (l) << "multiple dist module initializations"; - return; + return true; } const dir_path& out_root (r.out_path ()); @@ -58,20 +59,20 @@ namespace build // if (first) { - variable_pool.find ("dist", bool_type); + var_pool.find ("dist", bool_type); - variable_pool.find ("dist.package", string_type); + var_pool.find ("dist.package", string_type); - variable_pool.find ("dist.root", dir_path_type); - variable_pool.find ("config.dist.root", dir_path_type); + var_pool.find ("dist.root", dir_path_type); + var_pool.find ("config.dist.root", dir_path_type); //@@ VAR type // - variable_pool.find ("dist.cmd", string_type); - variable_pool.find ("config.dist.cmd", string_type); + var_pool.find ("dist.cmd", string_type); + var_pool.find ("config.dist.cmd", string_type); - variable_pool.find ("dist.archives", strings_type); - variable_pool.find ("config.dist.archives", strings_type); + var_pool.find ("dist.archives", strings_type); + var_pool.find ("config.dist.archives", strings_type); } // Configuration. @@ -127,6 +128,8 @@ namespace build v = cv; } } + + return true; } } } diff --git a/build/dist/operation.cxx b/build/dist/operation.cxx index 2a84894..0069432 100644 --- a/build/dist/operation.cxx +++ b/build/dist/operation.cxx @@ -199,7 +199,7 @@ namespace build // entered. // action_targets files; - const variable& dist_var (variable_pool.find ("dist")); + const variable& dist_var (var_pool.find ("dist")); for (const auto& pt: targets) { diff --git a/build/file.cxx b/build/file.cxx index 970957b..9595d8f 100644 --- a/build/file.cxx +++ b/build/file.cxx @@ -528,7 +528,7 @@ namespace build // the NULL value indicates that we found no subprojects. // { - const variable& var (variable_pool.find ("subprojects")); + const variable& var (var_pool.find ("subprojects")); auto rp (root.vars.assign(var)); // Set NULL by default. value& v (rp.first); @@ -794,8 +794,7 @@ namespace build if (out_root.empty ()) { const variable& var ( - variable_pool.find ("config.import." + project, - dir_path_type)); + var_pool.find ("config.import." + project, dir_path_type)); if (auto l = iroot[var]) { diff --git a/build/install/module b/build/install/module index 240d034..78004ef 100644 --- a/build/install/module +++ b/build/install/module @@ -6,15 +6,17 @@ #define BUILD_INSTALL_MODULE #include <build/types> +#include <build/utility> + #include <build/module> namespace build { namespace install { - extern "C" void + extern "C" bool install_init ( - scope&, scope&, const location&, std::unique_ptr<module>&, bool); + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/install/module.cxx b/build/install/module.cxx index a0eed61..8711341 100644 --- a/build/install/module.cxx +++ b/build/install/module.cxx @@ -51,7 +51,7 @@ namespace build vn += name; vn += var; const variable& vr ( - variable_pool.find (move (vn), &value_traits<T>::value_type)); + var_pool.find (move (vn), &value_traits<T>::value_type)); cv = dv != nullptr ? &config::required (r, vr, *dv, override).first.get () @@ -62,7 +62,7 @@ namespace build vn += name; vn += var; const variable& vr ( - variable_pool.find (move (vn), &value_traits<T>::value_type)); + var_pool.find (move (vn), &value_traits<T>::value_type)); value& v (r.assign (vr)); @@ -99,12 +99,13 @@ namespace build static alias_rule alias_; static file_rule file_; - extern "C" void + extern "C" bool install_init (scope& r, scope& b, const location& l, - unique_ptr<build::module>&, - bool first) + unique_ptr<module>&, + bool first, + bool) { tracer trace ("install::init"); @@ -114,7 +115,7 @@ namespace build if (!first) { warn (l) << "multiple install module initializations"; - return; + return true; } const dir_path& out_root (r.out_path ()); @@ -135,7 +136,7 @@ namespace build // if (first) { - variable_pool.find ("install", dir_path_type); + var_pool.find ("install", dir_path_type); } // Configuration. @@ -172,6 +173,8 @@ namespace build path<doc> (b, dir_path ("doc")); // Install into install.doc. path<man> (b, dir_path ("man")); // Install into install.man. path<man1> (b, dir_path ("man1")); // Install into install.man1. + + return true; } } } diff --git a/build/module b/build/module index 43ab1e0..984a50f 100644 --- a/build/module +++ b/build/module @@ -21,23 +21,32 @@ namespace build ~module () = default; }; + // Return false if the module configuration (normally based on the default + // values) was unsuccessful but this is not (yet) an error. One example + // would be the optional use of a module. Or a module might remain + // unconfigured for as long as it is actually not used (e.g., install, + // dist). The return value is used to set the <module>.configured variable. + // extern "C" using module_init_function = - void (scope& root, + bool (scope& root, scope& base, const location&, std::unique_ptr<module>&, - bool first); // First time for this project. + bool first, // First time for this project. + bool optional); // Loaded with 'using?' (optional module). using loaded_module_map = std::map<std::string, std::pair<module_init_function*, std::unique_ptr<module>>>; // Load the specified module. Used by the parser but also by some - // modules to load prerequisite modules. + // modules to load prerequisite modules. Return true if the module + // was both successfully loaded and configured. // - void - load_module (const std::string& name, + bool + load_module (bool optional, + const std::string& name, scope& root, scope& base, const location&); diff --git a/build/module.cxx b/build/module.cxx index 0f2b1b2..5f1aeff 100644 --- a/build/module.cxx +++ b/build/module.cxx @@ -7,6 +7,7 @@ #include <utility> // make_pair() #include <build/scope> +#include <build/variable> #include <build/diagnostics> using namespace std; @@ -15,13 +16,16 @@ namespace build { available_module_map builtin_modules; - void - load_module (const string& name, scope& root, scope& base, const location& l) + bool + load_module (bool opt, + const string& name, + scope& rs, + scope& bs, + const location& loc) { - // First see if this modules has already been loaded for this - // project. + // First see if this modules has already been loaded for this project. // - loaded_module_map& lm (root.modules); + loaded_module_map& lm (rs.modules); auto i (lm.find (name)); bool f (i == lm.end ()); @@ -32,11 +36,26 @@ namespace build auto j (builtin_modules.find (name)); if (j == builtin_modules.end ()) - fail (l) << "unknown module " << name; - - i = lm.emplace (name, make_pair (j->second, nullptr)).first; + { + if (!opt) + fail (loc) << "unknown module " << name; + } + else + i = lm.emplace (name, make_pair (j->second, nullptr)).first; } - i->second.first (root, base, l, i->second.second, f); + bool l (i != lm.end ()); + bool c (l && i->second.first (rs, bs, loc, i->second.second, f, opt)); + + const variable& lv (var_pool.find (name + ".loaded", + variable_visibility::project, + bool_type)); + const variable& cv (var_pool.find (name + ".configured", + variable_visibility::project, + bool_type)); + bs.assign (lv) = l; + bs.assign (cv) = c; + + return l && c; } } diff --git a/build/parser.cxx b/build/parser.cxx index 05c79db..c896c71 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -128,7 +128,8 @@ namespace build export_ (t, tt); continue; } - else if (n == "using") + else if (n == "using" || + n == "using?") { using_ (t, tt); continue; @@ -360,7 +361,7 @@ namespace build fail (t) << "append to target type/pattern-specific " << "variable " << v; - const auto& var (variable_pool.find (move (v))); + const auto& var (var_pool.find (move (v))); // Note: expand variables in the value in the context of // the scope. @@ -711,7 +712,7 @@ namespace build if (at == token_type::equal || at == token_type::plus_equal) { - var = &variable_pool.find (t.value); + var = &var_pool.find (t.value); val = at == token_type::equal ? &scope_->assign (*var) : &scope_->append (*var); @@ -782,6 +783,8 @@ namespace build { tracer trace ("parser::using", &path_); + bool optional (t.value.back () == '?'); + // The rest should be a list of module names. Parse them as names // to get variable expansion, etc. // @@ -798,7 +801,7 @@ namespace build if (!n.simple ()) fail (l) << "module name expected instead of " << n; - load_module (n.value, *root_, *scope_, l); + load_module (optional, n.value, *root_, *scope_, l); } if (tt == type::newline) @@ -912,7 +915,7 @@ namespace build void parser:: variable (token& t, token_type& tt, string name, token_type kind) { - const auto& var (variable_pool.find (move (name))); + const auto& var (var_pool.find (move (name))); names_type vns (variable_value (t, tt, var)); if (kind == type::equal) @@ -1366,7 +1369,7 @@ namespace build // Lookup. // - const auto& var (variable_pool.find (move (n))); + const auto& var (var_pool.find (move (n))); auto l (target_ != nullptr ? (*target_)[var] : (*scope_)[var]); // Undefined/NULL namespace variables are not allowed. diff --git a/build/scope b/build/scope index 1cfbc10..e52f615 100644 --- a/build/scope +++ b/build/scope @@ -76,25 +76,34 @@ namespace build // in this scope, do it on the the variables map directly. // build::lookup<const value> - operator[] (const variable&) const; + operator[] (const variable& var) const + { + return lookup (nullptr, nullptr, var); + } build::lookup<const value> operator[] (const std::string& name) const { - return operator[] (variable_pool.find (name)); + return operator[] (var_pool.find (name)); } // As above, but includes target type/pattern-specific variables. // build::lookup<const value> - lookup (const target_type&, const string& name, const variable&) const; + lookup (const target_key& tk, const variable& var) const + { + return lookup (tk.type, tk.name, var); + } build::lookup<const value> - lookup (const target_type& tt, const string& n, const string& var) const + lookup (const target_key& tk, const string& var) const { - return lookup (tt, n, variable_pool.find (var)); + return lookup (tk, var_pool.find (var)); } + build::lookup<const value> + lookup (const target_type*, const string* name, const variable&) const; + // Return a value suitable for assignment (or append if you only // want to append to the value from this scope). If the variable // does not exist in this scope's map, then a new one with the @@ -119,7 +128,7 @@ namespace build value& append (const std::string& name) { - return append (variable_pool.find (name)); + return append (var_pool.find (name)); } // Target type/pattern-specific variables. diff --git a/build/scope.cxx b/build/scope.cxx index 305950b..dfddf20 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -13,37 +13,33 @@ namespace build // scope // lookup<const value> scope:: - operator[] (const variable& var) const + lookup (const target_type* tt, const string* name, const variable& var) const { using result = build::lookup<const value>; - const value* r (nullptr); - const scope* s (this); - - for (; s != nullptr; s = s->parent_scope ()) + for (const scope* s (this); s != nullptr; ) { - if ((r = s->vars.find (var)) != nullptr) - break; - } - - return result (r, &s->vars); - } - - lookup<const value> scope:: - lookup (const target_type& tt, const string& name, const variable& var) const - { - using result = build::lookup<const value>; - - for (const scope* s (this); s != nullptr; s = s->parent_scope ()) - { - if (!s->target_vars.empty ()) + if (tt != nullptr && !s->target_vars.empty ()) { - if (auto l = s->target_vars.lookup (tt, name, var)) + if (auto l = s->target_vars.lookup (*tt, *name, var)) return l; } if (auto r = s->vars.find (var)) return result (r, &s->vars); + + switch (var.visibility) + { + case variable_visibility::scope: + s = nullptr; + break; + case variable_visibility::project: + s = s->root () ? nullptr : s->parent_scope (); + break; + case variable_visibility::normal: + s = s->parent_scope (); + break; + } } return result (); diff --git a/build/target b/build/target index 66daa66..2aea7ae 100644 --- a/build/target +++ b/build/target @@ -283,7 +283,7 @@ namespace build lookup<const value> operator[] (const std::string& name) const { - return operator[] (variable_pool.find (name)); + return operator[] (var_pool.find (name)); } // Return a value suitable for assignment. See class scope for @@ -304,7 +304,7 @@ namespace build value& append (const std::string& name) { - return append (variable_pool.find (name)); + return append (var_pool.find (name)); } public: diff --git a/build/target.cxx b/build/target.cxx index 85eec66..5c31311 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -133,7 +133,7 @@ namespace build // We cannot simply delegate to scope's lookup() since we also need // to check the group. // - for (const scope* s (&base_scope ()); s != nullptr; s = s->parent_scope ()) + for (const scope* s (&base_scope ()); s != nullptr; ) { if (!s->target_vars.empty ()) { @@ -149,6 +149,19 @@ namespace build if (auto r = s->vars.find (var)) return result (r, &s->vars); + + switch (var.visibility) + { + case variable_visibility::scope: + s = nullptr; + break; + case variable_visibility::project: + s = s->root () ? nullptr : s->parent_scope (); + break; + case variable_visibility::normal: + s = s->parent_scope (); + break; + } } return result (); diff --git a/build/target.txx b/build/target.txx index 48bac21..e347f77 100644 --- a/build/target.txx +++ b/build/target.txx @@ -22,7 +22,7 @@ namespace build { // Include target type/pattern-specific variables. // - auto l (s.lookup (*tk.type, *tk.name, var)); + auto l (s.lookup (tk, var)); if (!l) { diff --git a/build/test/module b/build/test/module index 0b6af78..25bb2f2 100644 --- a/build/test/module +++ b/build/test/module @@ -12,9 +12,9 @@ namespace build { namespace test { - extern "C" void + extern "C" bool test_init ( - scope&, scope&, const location&, std::unique_ptr<module>&, bool); + scope&, scope&, const location&, unique_ptr<module>&, bool, bool); } } diff --git a/build/test/module.cxx b/build/test/module.cxx index deda6e9..167c09b 100644 --- a/build/test/module.cxx +++ b/build/test/module.cxx @@ -21,12 +21,13 @@ namespace build { static rule rule_; - extern "C" void + extern "C" bool test_init (scope& r, scope& b, const location& l, - unique_ptr<build::module>&, - bool first) + unique_ptr<module>&, + bool first, + bool) { tracer trace ("test::init"); @@ -36,7 +37,7 @@ namespace build if (!first) { warn (l) << "multiple test module initializations"; - return; + return true; } const dir_path& out_root (r.out_path ()); @@ -66,13 +67,15 @@ namespace build // Enter module variables. // { - variable_pool.find ("test", bool_type); - variable_pool.find ("test.input", name_type); - variable_pool.find ("test.output", name_type); - variable_pool.find ("test.roundtrip", name_type); - variable_pool.find ("test.options", strings_type); - variable_pool.find ("test.arguments", strings_type); + var_pool.find ("test", bool_type); + var_pool.find ("test.input", name_type); + var_pool.find ("test.output", name_type); + var_pool.find ("test.roundtrip", name_type); + var_pool.find ("test.options", strings_type); + var_pool.find ("test.arguments", strings_type); } + + return true; } } } diff --git a/build/test/rule.cxx b/build/test/rule.cxx index ea268a9..3a4c91d 100644 --- a/build/test/rule.cxx +++ b/build/test/rule.cxx @@ -72,7 +72,7 @@ namespace build // if (!l.defined ()) l = t.base_scope ()[ - variable_pool.find (string("test.") + t.type ().name, bool_type)]; + var_pool.find (string("test.") + t.type ().name, bool_type)]; r = l && as<bool> (*l); } @@ -150,13 +150,15 @@ namespace build string n ("test."); n += t.type ().name; - const variable& in (variable_pool.find (n + ".input", name_type)); - const variable& on (variable_pool.find (n + ".output", name_type)); - const variable& rn (variable_pool.find (n + ".roundtrip", name_type)); + const variable& in (var_pool.find (n + ".input", name_type)); + const variable& on (var_pool.find (n + ".output", name_type)); + const variable& rn (var_pool.find (n + ".roundtrip", name_type)); // We should only keep value(s) that were specified together // in the innermost scope. // + // @@ Shouldn't we stop at project root? + // for (scope* s (&bs); s != nullptr; s = s->parent_scope ()) { ol = s->vars[on]; @@ -294,7 +296,7 @@ namespace build var += t.type ().name; var += '.'; var += n; - l = t.base_scope ()[variable_pool.find (var, strings_type)]; + l = t.base_scope ()[var_pool.find (var, strings_type)]; } if (l) diff --git a/build/utility b/build/utility index 7e6b0df..404b33a 100644 --- a/build/utility +++ b/build/utility @@ -7,7 +7,8 @@ #include <tuple> #include <string> -#include <utility> // move() +#include <utility> // move() +#include <cassert> // assert() #include <exception> #include <unordered_set> diff --git a/build/variable b/build/variable index 7976ab9..061004c 100644 --- a/build/variable +++ b/build/variable @@ -7,10 +7,8 @@ #include <map> #include <vector> -#include <string> #include <cstddef> // nullptr_t -#include <utility> // move(), pair, make_pair() -#include <cassert> +#include <utility> // pair, make_pair() #include <iterator> #include <functional> // hash, reference_wrapper #include <type_traits> // conditional, is_reference, remove_reference, etc. @@ -19,6 +17,8 @@ #include <butl/prefix-map> #include <build/types> +#include <build/utility> + #include <build/target-type> namespace build @@ -37,19 +37,23 @@ namespace build bool (*const append) (names&, names, const variable&); }; + enum class variable_visibility + { + scope, // This scope (no outer scopes). + project, // This project (no outer projects). + normal // All outer scopes. + }; + // variable // // The two variables are considered the same if they have the same name. // struct variable { - explicit - variable (std::string n, const value_type* t = nullptr, char p = '\0') - : name (std::move (n)), pairs (p), type (t) {} - std::string name; - char pairs; - const value_type* type; // If NULL, then not (yet) typed. + const value_type* type; // If NULL, then not (yet) typed. + variable_visibility visibility; + char pairs; // Pair symbold or '\0' if not used. }; inline bool @@ -616,13 +620,40 @@ namespace build { // variable_pool // - using variable_set_base = std::unordered_set<variable>; - struct variable_set: private variable_set_base + using variable_pool_base = std::unordered_set<variable>; + struct variable_pool: private variable_pool_base { const variable& - find (std::string name, const build::value_type* t = nullptr, char p = '\0') + find (string name, const build::value_type* t = nullptr, char p = '\0') { - auto r (emplace (std::move (name), t, p)); + return find (name, nullptr, t, p); + } + + const variable& + find (string name, + variable_visibility v, + const build::value_type* t = nullptr, + char p = '\0') + { + return find (name, &v, t, p); + } + + using variable_pool_base::clear; + + private: + const variable& + find (string name, + const variable_visibility* vv, + const build::value_type* t, + char p) + { + auto r ( + insert ( + variable { + std::move (name), + t, + vv != nullptr ? *vv : variable_visibility::normal, + p})); const variable& v (*r.first); // Update type? @@ -630,16 +661,25 @@ namespace build if (!r.second && t != nullptr && v.type != t) { assert (v.type == nullptr); - const_cast<variable&> (v).type = t; // Ok, not changing the key. + const_cast<variable&> (v).type = t; // Not changing the key. + } + + // Change visibility? While this might at first seem like a bad idea, + // it can happen that the variable lookup happens before any values + // were set, in which case the variable will be entered with the + // default visibility. + // + if (!r.second && vv != nullptr && v.visibility != *vv) + { + assert (v.visibility == variable_visibility::normal); // Default. + const_cast<variable&> (v).visibility = *vv; // Not changing the key. } return v; } - - using variable_set_base::clear; }; - extern variable_set variable_pool; + extern variable_pool var_pool; // variable_map // @@ -696,7 +736,7 @@ namespace build lookup<const value> operator[] (const std::string& name) const { - return operator[] (variable_pool.find (name)); + return operator[] (var_pool.find (name)); } // Non-const lookup. Only exposed on the map directly. @@ -710,7 +750,7 @@ namespace build lookup<value> operator[] (const std::string& name) { - return operator[] (variable_pool.find (name)); + return operator[] (var_pool.find (name)); } // The second member in the pair indicates whether the new @@ -733,13 +773,13 @@ namespace build std::pair<std::reference_wrapper<value>, bool> assign (const std::string& name) { - return assign (variable_pool.find (name)); + return assign (var_pool.find (name)); } std::pair<const_iterator, const_iterator> find_namespace (const std::string& ns) const { - auto r (m_.find_prefix (variable_pool.find (ns))); + auto r (m_.find_prefix (var_pool.find (ns))); return std::make_pair (const_iterator (r.first), const_iterator (r.second)); } diff --git a/build/variable.cxx b/build/variable.cxx index 753e3d3..4386c6a 100644 --- a/build/variable.cxx +++ b/build/variable.cxx @@ -341,7 +341,7 @@ namespace build // variable_set // - variable_set variable_pool; + variable_pool var_pool; // variable_type_map // diff --git a/tests/using/buildfile b/tests/using/buildfile new file mode 100644 index 0000000..86baa42 --- /dev/null +++ b/tests/using/buildfile @@ -0,0 +1,5 @@ +using? foo +print $foo.loaded +print $foo.configured + +./: |