From e2ba7fa123213fcc5673119b8d993d7967a1fb24 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 7 Sep 2017 18:09:44 +0200 Subject: Implement {c,cxx}.guess modules These can be loaded before {c,cxx} to guess the compiler. Based on this information we can then choose the standard, experimental features, etc. For example: using cxx.guess if ($cxx.id == 'clang') cxx.features.modules = false cxx.std = experimental using cxx --- build2/b.cxx | 2 ++ build2/c/init.cxx | 62 ++++++++++++++++++++++++++++++++----------- build2/c/init.hxx | 9 +++++++ build2/cc/module.cxx | 75 ++++++++++++++++++++++++++++------------------------ build2/cc/module.hxx | 15 +++++++++-- build2/cxx/init.cxx | 61 +++++++++++++++++++++++++++++++----------- build2/cxx/init.hxx | 9 +++++++ 7 files changed, 165 insertions(+), 68 deletions(-) (limited to 'build2') diff --git a/build2/b.cxx b/build2/b.cxx index 95bf876..0520bd8 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -333,9 +333,11 @@ main (int argc, char* argv[]) bm["cc.config"] = mf {nullptr, &cc::config_init}; bm["cc"] = mf {nullptr, &cc::init}; + bm["c.guess"] = mf {nullptr, &c::guess_init}; bm["c.config"] = mf {nullptr, &c::config_init}; bm["c"] = mf {nullptr, &c::init}; + bm["cxx.guess"] = mf {nullptr, &cxx::guess_init}; bm["cxx.config"] = mf {nullptr, &cxx::config_init}; bm["cxx"] = mf {nullptr, &cxx::init}; diff --git a/build2/c/init.cxx b/build2/c/init.cxx index 79cfec7..1190eae 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -30,7 +30,7 @@ namespace build2 config_module (config_data&& d) : config_data (move (d)), cc::config_module (move (d)) {} - strings + virtual strings translate_std (const compiler_info&, scope&, const string*) const override; @@ -106,22 +106,24 @@ namespace build2 return r; } + // See cc::module for details on guess_init vs config_init. + // bool - config_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr& mod, - bool, - bool, - const variable_map& hints) + guess_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr& mod, + bool, + bool, + const variable_map& hints) { - tracer trace ("c::config_init"); + tracer trace ("c::guess_init"); l5 ([&]{trace << "for " << bs.out_path ();}); // We only support root loading (which means there can only be one). // if (&rs != &bs) - fail (loc) << "c.config module must be loaded in project root"; + fail (loc) << "c.guess module must be loaded in project root"; // Load cc.core.vars so that we can cache all the cc.* variables. // @@ -207,10 +209,38 @@ namespace build2 assert (mod == nullptr); config_module* m (new config_module (move (d))); mod.reset (m); - m->init (rs, loc, hints); + m->guess (rs, loc, hints); return true; } + bool + config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("c::config_init"); + l5 ([&]{trace << "for " << bs.out_path ();}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "c.config module must be loaded in project root"; + + // Load c.guess. + // + if (!cast_false (rs["c.guess.loaded"])) + load_module (rs, rs, "c.guess", loc, false, hints); + + config_module& cm (*rs.modules.lookup ("c.guess")); + cm.init (rs, loc, hints); + return true; + } + + static const target_type* const hdr[] = { &h::static_type, @@ -246,7 +276,7 @@ namespace build2 if (!cast_false (rs["c.config.loaded"])) load_module (rs, rs, "c.config", loc, false, hints); - config_module& cm (*rs.modules.lookup ("c.config")); + config_module& cm (*rs.modules.lookup ("c.guess")); cc::data d { cm, @@ -256,10 +286,10 @@ namespace build2 "c.install", "c.uninstall", - cm.cid, - cast (rs[cm.x_id_variant]), - cast (rs[cm.x_version_major]), - cast (rs[cm.x_version_minor]), + cm.ci.id.value (), + cm.ci.id.variant, + cm.ci.version.major, + cm.ci.version.minor, cast (rs[cm.x_path]), cast (rs[cm.x_target]), diff --git a/build2/c/init.hxx b/build2/c/init.hxx index e18a5df..5fd7c84 100644 --- a/build2/c/init.hxx +++ b/build2/c/init.hxx @@ -15,6 +15,15 @@ namespace build2 namespace c { bool + guess_init (scope&, + scope&, + const location&, + unique_ptr&, + bool, + bool, + const variable_map&); + + bool config_init (scope&, scope&, const location&, diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index 56b74e9..a18271c 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -25,16 +25,12 @@ namespace build2 namespace cc { void config_module:: - init (scope& rs, const location& loc, const variable_map&) + guess (scope& rs, const location&, const variable_map&) { - tracer trace (x, "config_init"); + tracer trace (x, "guess_init"); bool cc_loaded (cast_false (rs["cc.core.config.loaded"])); - // Configure. - // - compiler_info ci; // For program patterns. - // Adjust module priority (compiler). Also order cc module before us // (we don't want to use priorities for that in case someone manages // to slot in-between). @@ -81,10 +77,10 @@ namespace build2 // Figure out which compiler we are dealing with, its target, etc. // const path& xc (cast (*p.first)); - ci = guess (x_lang, - xc, - cast_null (rs[config_c_coptions]), - cast_null (rs[config_x_coptions])); + ci = cc::guess (x_lang, + xc, + cast_null (rs[config_c_coptions]), + cast_null (rs[config_x_coptions])); // Split/canonicalize the target. First see if the user asked us to // use config.sub. @@ -118,6 +114,39 @@ namespace build2 } } + // Assign value to variables that describe the compile. + // + rs.assign (x_id) = ci.id.string (); + rs.assign (x_id_type) = ci.id.type; + rs.assign (x_id_variant) = ci.id.variant; + + rs.assign (x_version) = ci.version.string; + rs.assign (x_version_major) = ci.version.major; + rs.assign (x_version_minor) = ci.version.minor; + rs.assign (x_version_patch) = ci.version.patch; + rs.assign (x_version_build) = ci.version.build; + + // Also enter as x.target.{cpu,vendor,system,version,class} for + // convenience of access. + // + rs.assign (x_target_cpu) = tt.cpu; + rs.assign (x_target_vendor) = tt.vendor; + rs.assign (x_target_system) = tt.system; + rs.assign (x_target_version) = tt.version; + rs.assign (x_target_class) = tt.class_; + + rs.assign (x_target) = move (tt); + + new_ = p.second; + } + + void config_module:: + init (scope& rs, const location& loc, const variable_map&) + { + tracer trace (x, "config_init"); + + const target_triplet& tt (cast (rs[x_target])); + // Translate x_std value (if any) to the compiler option(s) (if any). // tstd = translate_std (ci, rs, cast_null (rs[x_std])); @@ -154,7 +183,7 @@ namespace build2 // If this is a new value (e.g., we are configuring), then print the // report at verbosity level 2 and up (-v). // - if (verb >= (p.second ? 2 : 3)) + if (verb >= (new_ ? 2 : 3)) { diag_record dr (text); @@ -220,31 +249,9 @@ namespace build2 rs.assign (x_sys_lib_dirs) = move (lib_dirs); rs.assign (x_sys_inc_dirs) = move (inc_dirs); - cid = ci.id.value (); - rs.assign (x_id) = ci.id.string (); - rs.assign (x_id_type) = move (ci.id.type); - rs.assign (x_id_variant) = move (ci.id.variant); - - rs.assign (x_version) = move (ci.version.string); - rs.assign (x_version_major) = ci.version.major; - rs.assign (x_version_minor) = ci.version.minor; - rs.assign (x_version_patch) = ci.version.patch; - rs.assign (x_version_build) = move (ci.version.build); - rs.assign (x_signature) = move (ci.signature); rs.assign (x_checksum) = move (ci.checksum); - // Also enter as x.target.{cpu,vendor,system,version,class} for - // convenience of access. - // - rs.assign (x_target_cpu) = tt.cpu; - rs.assign (x_target_vendor) = tt.vendor; - rs.assign (x_target_system) = tt.system; - rs.assign (x_target_version) = tt.version; - rs.assign (x_target_class) = tt.class_; - - rs.assign (x_target) = move (tt); - // config.x.{p,c,l}options // config.x.libs // @@ -275,7 +282,7 @@ namespace build2 // Load cc.core.config. // - if (!cc_loaded) + if (!cast_false (rs["cc.core.config.loaded"])) { // Prepare configuration hints. // diff --git a/build2/cc/module.hxx b/build2/cc/module.hxx index c34b0e5..08f573b 100644 --- a/build2/cc/module.hxx +++ b/build2/cc/module.hxx @@ -29,6 +29,15 @@ namespace build2 explicit config_module (config_data&& d) : config_data (move (d)) {} + // We split the configuration process into into two parts: guessing the + // compiler information and the actual configuration. This allows one to + // adjust configuration (say the standard or enabled experimental + // features) base on the compiler information by first loading the + // guess module. + // + void + guess (scope&, const location&, const variable_map&); + void init (scope&, const location&, const variable_map&); @@ -40,8 +49,7 @@ namespace build2 translate_std (const compiler_info&, scope&, const string*) const = 0; strings tstd; - - compiler_id::value_type cid; + compiler_info ci; // Note: some members are moved from. private: dir_paths @@ -49,6 +57,9 @@ namespace build2 dir_paths msvc_library_search_paths (process_path&, scope&) const; // msvc.cxx + + private: + bool new_; // See guess() and init() for details. }; class module: public module_base, public virtual common, diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 5bd14ee..4a72c90 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -30,7 +30,7 @@ namespace build2 config_module (config_data&& d) : config_data (move (d)), cc::config_module (move (d)) {} - strings + virtual strings translate_std (const compiler_info&, scope&, const string*) const override; @@ -285,22 +285,24 @@ namespace build2 return r; } + // See cc::module for details on guess_init vs config_init. + // bool - config_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr& mod, - bool, - bool, - const variable_map& hints) + guess_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr& mod, + bool, + bool, + const variable_map& hints) { - tracer trace ("cxx::config_init"); + tracer trace ("cxx::guess_init"); l5 ([&]{trace << "for " << bs.out_path ();}); // We only support root loading (which means there can only be one). // if (&rs != &bs) - fail (loc) << "cxx.config module must be loaded in project root"; + fail (loc) << "cxx.guess module must be loaded in project root"; // Load cc.core.vars so that we can cache all the cc.* variables. // @@ -396,7 +398,34 @@ namespace build2 assert (mod == nullptr); config_module* m (new config_module (move (d))); mod.reset (m); - m->init (rs, loc, hints); + m->guess (rs, loc, hints); + return true; + } + + bool + config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("cxx::config_init"); + l5 ([&]{trace << "for " << bs.out_path ();}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx.config module must be loaded in project root"; + + // Load cxx.guess. + // + if (!cast_false (rs["cxx.guess.loaded"])) + load_module (rs, rs, "cxx.guess", loc, false, hints); + + config_module& cm (*rs.modules.lookup ("cxx.guess")); + cm.init (rs, loc, hints); return true; } @@ -444,7 +473,7 @@ namespace build2 if (!cast_false (rs["cxx.config.loaded"])) load_module (rs, rs, "cxx.config", loc, false, hints); - config_module& cm (*rs.modules.lookup ("cxx.config")); + config_module& cm (*rs.modules.lookup ("cxx.guess")); auto& vp (var_pool.rw (rs)); @@ -467,10 +496,10 @@ namespace build2 "cxx.install", "cxx.uninstall", - cm.cid, - cast (rs[cm.x_id_variant]), - cast (rs[cm.x_version_major]), - cast (rs[cm.x_version_minor]), + cm.ci.id.value (), + cm.ci.id.variant, + cm.ci.version.major, + cm.ci.version.minor, cast (rs[cm.x_path]), cast (rs[cm.x_target]), diff --git a/build2/cxx/init.hxx b/build2/cxx/init.hxx index 5ba4041..a952bf8 100644 --- a/build2/cxx/init.hxx +++ b/build2/cxx/init.hxx @@ -15,6 +15,15 @@ namespace build2 namespace cxx { bool + guess_init (scope&, + scope&, + const location&, + unique_ptr&, + bool, + bool, + const variable_map&); + + bool config_init (scope&, scope&, const location&, -- cgit v1.1