diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-08-09 11:31:53 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-08-12 17:04:22 +0200 |
commit | 9fa5f73d00905568e8979d0c93ec4a8f645c81d5 (patch) | |
tree | f2bf937fa256c0ef2c9bbe05d3655d1985719405 | |
parent | a1b2319ff2ddc8a6f139ee364cabe236ca62e23e (diff) |
Implement support for C compilation
We now have two new modules: cc (c-common) and c.
-rwxr-xr-x | bootstrap | 2 | ||||
-rw-r--r-- | build2/algorithm | 10 | ||||
-rw-r--r-- | build2/algorithm.cxx | 48 | ||||
-rw-r--r-- | build2/algorithm.ixx | 15 | ||||
-rw-r--r-- | build2/algorithm.txx | 58 | ||||
-rw-r--r-- | build2/b.cxx | 46 | ||||
-rw-r--r-- | build2/bin/module | 36 | ||||
-rw-r--r-- | build2/bin/module.cxx | 224 | ||||
-rw-r--r-- | build2/buildfile | 25 | ||||
-rw-r--r-- | build2/c/init | 37 | ||||
-rw-r--r-- | build2/c/init.cxx | 240 | ||||
-rw-r--r-- | build2/c/target | 22 | ||||
-rw-r--r-- | build2/cc/common | 172 | ||||
-rw-r--r-- | build2/cc/compile | 82 | ||||
-rw-r--r-- | build2/cc/compile.cxx (renamed from build2/cxx/compile.cxx) | 301 | ||||
-rw-r--r-- | build2/cc/guess (renamed from build2/cxx/guess) | 47 | ||||
-rw-r--r-- | build2/cc/guess.cxx (renamed from build2/cxx/guess.cxx) | 288 | ||||
-rw-r--r-- | build2/cc/init | 55 | ||||
-rw-r--r-- | build2/cc/init.cxx | 321 | ||||
-rw-r--r-- | build2/cc/install (renamed from build2/cxx/install) | 22 | ||||
-rw-r--r-- | build2/cc/install.cxx (renamed from build2/cxx/install.cxx) | 20 | ||||
-rw-r--r-- | build2/cc/link | 78 | ||||
-rw-r--r-- | build2/cc/link.cxx (renamed from build2/cxx/link.cxx) | 384 | ||||
-rw-r--r-- | build2/cc/module | 59 | ||||
-rw-r--r-- | build2/cc/module.cxx | 291 | ||||
-rw-r--r-- | build2/cc/msvc.cxx (renamed from build2/cxx/msvc.cxx) | 53 | ||||
-rw-r--r-- | build2/cc/target | 48 | ||||
-rw-r--r-- | build2/cc/target.cxx | 39 | ||||
-rw-r--r-- | build2/cc/types | 32 | ||||
-rw-r--r-- | build2/cc/utility (renamed from build2/cxx/common) | 58 | ||||
-rw-r--r-- | build2/cc/utility.cxx (renamed from build2/cxx/common.cxx) | 54 | ||||
-rw-r--r-- | build2/cc/utility.ixx | 33 | ||||
-rw-r--r-- | build2/cc/windows-manifest.cxx (renamed from build2/cxx/windows-manifest.cxx) | 16 | ||||
-rw-r--r-- | build2/cc/windows-rpath.cxx (renamed from build2/cxx/windows-rpath.cxx) | 17 | ||||
-rw-r--r-- | build2/cxx/compile | 37 | ||||
-rw-r--r-- | build2/cxx/link | 48 | ||||
-rw-r--r-- | build2/cxx/module | 9 | ||||
-rw-r--r-- | build2/cxx/module.cxx | 505 | ||||
-rw-r--r-- | build2/cxx/target | 26 | ||||
-rw-r--r-- | build2/cxx/target.cxx | 24 | ||||
-rw-r--r-- | build2/cxx/utility | 42 | ||||
-rw-r--r-- | build2/cxx/utility.cxx | 109 | ||||
-rw-r--r-- | build2/cxx/utility.ixx | 33 | ||||
-rw-r--r-- | build2/diagnostics | 40 | ||||
-rw-r--r-- | build2/diagnostics.cxx | 6 | ||||
-rw-r--r-- | build2/prerequisite | 3 | ||||
-rw-r--r-- | build2/rule | 11 | ||||
-rw-r--r-- | build2/target | 23 | ||||
-rw-r--r-- | build2/target-type | 24 | ||||
-rw-r--r-- | build2/target.cxx | 6 | ||||
-rw-r--r-- | build2/utility | 39 | ||||
-rw-r--r-- | build2/utility.ixx | 49 | ||||
-rw-r--r-- | build2/variable | 6 |
53 files changed, 2914 insertions, 1359 deletions
@@ -106,6 +106,8 @@ src="build2/*.cxx" src="$src build2/config/*.cxx" src="$src build2/dist/*.cxx" src="$src build2/bin/*.cxx" +src="$src build2/c/*.cxx" +src="$src build2/cc/*.cxx" src="$src build2/cxx/*.cxx" src="$src build2/cli/*.cxx" src="$src build2/test/*.cxx" diff --git a/build2/algorithm b/build2/algorithm index d52cca9..651ef24 100644 --- a/build2/algorithm +++ b/build2/algorithm @@ -201,6 +201,15 @@ namespace build2 T* execute_prerequisites (action, target&, const timestamp&); + target* + execute_prerequisites (const target_type&, + action, target&, const timestamp&); + + template <typename T> + T* + execute_prerequisites (const target_type&, + action, target&, const timestamp&); + // Return noop_recipe instead of using this function directly. // target_state @@ -243,6 +252,5 @@ namespace build2 } #include <build2/algorithm.ixx> -#include <build2/algorithm.txx> #endif // BUILD2_ALGORITHM diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx index a7126cc..cae6645 100644 --- a/build2/algorithm.cxx +++ b/build2/algorithm.cxx @@ -454,6 +454,54 @@ namespace build2 return e; } + target* + execute_prerequisites (const target_type& tt, + action a, target& t, const timestamp& mt) + { + bool e (mt == timestamp_nonexistent); + target* r (nullptr); + + for (target* pt: t.prerequisite_targets) + { + if (pt == nullptr) // Skip ignored. + continue; + + target_state ts (execute (a, *pt)); + + if (!e) + { + // If this is an mtime-based target, then compare timestamps. + // + if (auto mpt = dynamic_cast<const mtime_target*> (pt)) + { + timestamp mp (mpt->mtime ()); + + // What do we do if timestamps are equal? This can happen, for + // example, on filesystems that don't have subsecond resolution. + // There is not much we can do here except detect the case where + // the prerequisite was changed in this run which means the + // action must be executed on the target as well. + // + if (mt < mp || (mt == mp && ts == target_state::changed)) + e = true; + } + else + { + // Otherwise we assume the prerequisite is newer if it was changed. + // + if (ts == target_state::changed) + e = true; + } + } + + if (pt->is_a (tt)) + r = pt; + } + + assert (r != nullptr); + return e ? r : nullptr; + } + target_state noop_action (action a, target& t) { diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx index 17c9d8d..c9c6b02 100644 --- a/build2/algorithm.ixx +++ b/build2/algorithm.ixx @@ -200,4 +200,19 @@ namespace build2 default: return execute_impl (a, t); } } + + template <typename T> + inline T* + execute_prerequisites (action a, target& t, const timestamp& mt) + { + return static_cast<T*> (execute_prerequisites (T::static_type, a, t, mt)); + } + + template <typename T> + inline T* + execute_prerequisites (const target_type& tt, + action a, target& t, const timestamp& mt) + { + return static_cast<T*> (execute_prerequisites (tt, a, t, mt)); + } } diff --git a/build2/algorithm.txx b/build2/algorithm.txx deleted file mode 100644 index 1431d32..0000000 --- a/build2/algorithm.txx +++ /dev/null @@ -1,58 +0,0 @@ -// file : build2/algorithm.txx -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -namespace build2 -{ - template <typename T> - T* - execute_prerequisites (action a, target& t, const timestamp& mt) - { - //@@ Can factor the bulk of it into a non-template code. Can - // either do a function template that will do dynamic_cast check - // or can scan the target type info myself. I think latter. - // - bool e (mt == timestamp_nonexistent); - T* r (nullptr); - - for (target* pt: t.prerequisite_targets) - { - if (pt == nullptr) // Skip ignored. - continue; - - target_state ts (execute (a, *pt)); - - if (!e) - { - // If this is an mtime-based target, then compare timestamps. - // - if (auto mpt = dynamic_cast<const mtime_target*> (pt)) - { - timestamp mp (mpt->mtime ()); - - // What do we do if timestamps are equal? This can happen, for - // example, on filesystems that don't have subsecond resolution. - // There is not much we can do here except detect the case where - // the prerequisite was changed in this run which means the - // action must be executed on the target as well. - // - if (mt < mp || (mt == mp && ts == target_state::changed)) - e = true; - } - else - { - // Otherwise we assume the prerequisite is newer if it was changed. - // - if (ts == target_state::changed) - e = true; - } - } - - if (T* tmp = dynamic_cast<T*> (pt)) - r = tmp; - } - - assert (r != nullptr); - return e ? r : nullptr; - } -} diff --git a/build2/b.cxx b/build2/b.cxx index a14b998..5167cf6 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -41,6 +41,8 @@ using namespace std; #include <build2/config/module> #include <build2/dist/module> #include <build2/bin/module> +#include <build2/c/init> +#include <build2/cc/init> #include <build2/cxx/module> #include <build2/cli/module> #include <build2/test/module> @@ -183,19 +185,37 @@ main (int argc, char* argv[]) // Register builtin modules. // - builtin_modules["config"] = module_functions {&config::boot, - &config::init}; - builtin_modules["dist"] = module_functions {&dist::boot, &dist::init}; - builtin_modules["test"] = module_functions {&test::boot, &test::init}; - builtin_modules["install"] = module_functions {&install::boot, - &install::init}; - - builtin_modules["bin"] = module_functions {nullptr, &bin::init}; - builtin_modules["bin.ar"] = module_functions {nullptr, &bin::ar_init}; - builtin_modules["bin.ld"] = module_functions {nullptr, &bin::ld_init}; - builtin_modules["bin.rc"] = module_functions {nullptr, &bin::rc_init}; - builtin_modules["cxx"] = module_functions {nullptr, &cxx::init}; - builtin_modules["cli"] = module_functions {nullptr, &cli::init}; + { + using mf = module_functions; + auto& bm (builtin_modules); + + bm["config"] = mf {&config::boot, &config::init}; + bm["dist"] = mf {&dist::boot, &dist::init}; + bm["test"] = mf {&test::boot, &test::init}; + bm["install"] = mf {&install::boot, &install::init}; + + bm["bin.config"] = mf {nullptr, &bin::config_init}; + bm["bin"] = mf {nullptr, &bin::init}; + bm["bin.ar.config"] = mf {nullptr, &bin::ar_config_init}; + bm["bin.ar"] = mf {nullptr, &bin::ar_init}; + bm["bin.ld.config"] = mf {nullptr, &bin::ld_config_init}; + bm["bin.ld"] = mf {nullptr, &bin::ld_init}; + bm["bin.rc.config"] = mf {nullptr, &bin::rc_config_init}; + bm["bin.rc"] = mf {nullptr, &bin::rc_init}; + + bm["cc.vars"] = mf {nullptr, &cc::vars_init}; + bm["cc.config"] = mf {nullptr, &cc::config_init}; + bm["cc.core"] = mf {nullptr, &cc::core_init}; + bm["cc"] = mf {nullptr, &cc::init}; + + bm["c.config"] = mf {nullptr, &c::config_init}; + bm["c"] = mf {nullptr, &c::init}; + + bm["cxx.config"] = mf {nullptr, &cxx::config_init}; + bm["cxx"] = mf {nullptr, &cxx::init}; + + bm["cli"] = mf {nullptr, &cli::init}; + } // Figure out work and home directories. // diff --git a/build2/bin/module b/build2/bin/module index 8fb9274..8cf0255 100644 --- a/build2/bin/module +++ b/build2/bin/module @@ -15,6 +15,15 @@ namespace build2 namespace bin { bool + config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool init (scope&, scope&, const location&, @@ -24,6 +33,15 @@ namespace build2 const variable_map&); bool + ar_config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool ar_init (scope&, scope&, const location&, @@ -33,6 +51,15 @@ namespace build2 const variable_map&); bool + ld_config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool ld_init (scope&, scope&, const location&, @@ -42,6 +69,15 @@ namespace build2 const variable_map&); bool + rc_config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool rc_init (scope&, scope&, const location&, diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index 281a2e4..6d9cda7 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -33,39 +33,19 @@ namespace build2 static const strings liba_lib {"static"}; static const strings libs_lib {"shared"}; - // Apply the specified stem to the config.bin.pattern. If there is no - // pattern, then return the stem itself. Assume the pattern is valid, - // i.e., contains single '*'. - // - static string - apply (const lookup& pattern, const char* stem) - { - if (!pattern) - return stem; - - const string& p (cast<string> (pattern)); - size_t i (p.find ('*')); - assert (i != string::npos); - - string r (p, 0, i++); - r.append (stem); - r.append (p, i, p.size () - i); - return r; - } - bool - init (scope& r, - scope& b, - const location& loc, - unique_ptr<module_base>&, - bool first, - bool, - const variable_map& config_hints) + config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) { - tracer trace ("bin::init"); + tracer trace ("bin::config_init"); l5 ([&]{trace << "for " << b.out_path ();}); - // Enter module variables. + // Enter configuration variables. // if (first) { @@ -87,8 +67,6 @@ namespace build2 v.insert<strings> ("bin.liba.lib"); v.insert<strings> ("bin.libs.lib"); v.insert<dir_paths> ("bin.rpath"); - - v.insert<string> ("bin.libprefix", true); } // Configure. @@ -166,7 +144,7 @@ namespace build2 bool hint (false); if (v == nullptr) { - if (auto l = config_hints[var]) + if (auto l = hints[var]) { v = l.value; hint = true; @@ -241,7 +219,7 @@ namespace build2 // if (v == nullptr) { - if (auto l = config_hints[var]) + if (auto l = hints[var]) v = l.value; } @@ -276,6 +254,35 @@ namespace build2 } } + return true; + } + + bool + init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("bin::init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Enter the rest of the variables. + // + if (first) + { + auto& v (var_pool); + + v.insert<string> ("bin.libprefix", true); + } + + // Load bin.config. + // + if (!cast_false<bool> (b["bin.config.loaded"])) + load_module ("bin.config", r, b, loc, false, hints); + // Cache some config values we will be needing below. // const string& tclass (cast<string> (r["bin.target.class"])); @@ -363,24 +370,44 @@ namespace build2 return true; } + // Apply the specified stem to the config.bin.pattern. If there is no + // pattern, then return the stem itself. Assume the pattern is valid, + // i.e., contains single '*'. + // + static string + apply (const lookup& pattern, const char* stem) + { + if (!pattern) + return stem; + + const string& p (cast<string> (pattern)); + size_t i (p.find ('*')); + assert (i != string::npos); + + string r (p, 0, i++); + r.append (stem); + r.append (p, i, p.size () - i); + return r; + } + bool - ar_init (scope& r, - scope& b, - const location& loc, - unique_ptr<module_base>&, - bool first, - bool, - const variable_map& config_hints) + ar_config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) { - tracer trace ("bin::ar_init"); + tracer trace ("bin::ar_config_init"); l5 ([&]{trace << "for " << b.out_path ();}); - // Make sure the bin core is loaded. + // Make sure bin.config is loaded. // - if (!cast_false<bool> (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, config_hints); + if (!cast_false<bool> (b["bin.config.loaded"])) + load_module ("bin.config", r, b, loc, false, hints); - // Enter module variables. + // Enter configuration variables. // if (first) { @@ -480,23 +507,46 @@ namespace build2 } bool - ld_init (scope& r, + ar_init (scope& r, scope& b, const location& loc, unique_ptr<module_base>&, - bool first, bool, - const variable_map& config_hints) + bool, + const variable_map& hints) { - tracer trace ("bin::ld_init"); + tracer trace ("bin::ar_init"); l5 ([&]{trace << "for " << b.out_path ();}); - // Make sure the bin core is loaded. + // Make sure the bin core and ar.config are loaded. // if (!cast_false<bool> (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, config_hints); + load_module ("bin", r, b, loc, false, hints); + + if (!cast_false<bool> (b["bin.ar.config.loaded"])) + load_module ("bin.ar.config", r, b, loc, false, hints); + + return true; + } - // Enter module variables. + bool + ld_config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("bin::ld_config_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Make sure bin.config is loaded. + // + if (!cast_false<bool> (b["bin.config.loaded"])) + load_module ("bin.config", r, b, loc, false, hints); + + // Enter configuration variables. // if (first) { @@ -542,6 +592,29 @@ namespace build2 r.assign<string> ("bin.ld.checksum") = move (ldi.checksum); } + return true; + } + + bool + ld_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("bin::ld_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Make sure the bin core and ld.config are loaded. + // + if (!cast_false<bool> (b["bin.loaded"])) + load_module ("bin", r, b, loc, false, hints); + + if (!cast_false<bool> (b["bin.ld.config.loaded"])) + load_module ("bin.ld.config", r, b, loc, false, hints); + const string& lid (cast<string> (r["bin.ld.id"])); // Register the pdb{} target if using the VC toolchain. @@ -559,23 +632,23 @@ namespace build2 } bool - rc_init (scope& r, - scope& b, - const location& loc, - unique_ptr<module_base>&, - bool first, - bool, - const variable_map& config_hints) + rc_config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) { - tracer trace ("bin::rc_init"); + tracer trace ("bin::rc_config_init"); l5 ([&]{trace << "for " << b.out_path ();}); - // Make sure the bin core is loaded. + // Make sure bin.config is loaded. // - if (!cast_false<bool> (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, config_hints); + if (!cast_false<bool> (b["bin.config.loaded"])) + load_module ("bin.config", r, b, loc, false, hints); - // Enter module variables. + // Enter configuration variables. // if (first) { @@ -623,5 +696,28 @@ namespace build2 return true; } + + bool + rc_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("bin::rc_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Make sure the bin core and rc.config are loaded. + // + if (!cast_false<bool> (b["bin.loaded"])) + load_module ("bin", r, b, loc, false, hints); + + if (!cast_false<bool> (b["bin.rc.config.loaded"])) + load_module ("bin.rc.config", r, b, loc, false, hints); + + return true; + } } } diff --git a/build2/buildfile b/build2/buildfile index 3986015..b0e9cf0 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -5,7 +5,7 @@ import libs = libbutl%lib{butl} exe{b}: \ - {hxx ixx txx cxx}{ algorithm } \ + {hxx ixx cxx}{ algorithm } \ { cxx}{ b } \ {hxx ixx cxx}{ b-options } \ {hxx txx cxx}{ context } \ @@ -38,23 +38,28 @@ exe{b}: \ bin/{hxx cxx}{ module } \ bin/{hxx cxx}{ rule } \ bin/{hxx cxx}{ target } \ + c/{hxx cxx}{ init } \ + c/{hxx }{ target } \ + cc/{hxx }{ common } \ + cc/{hxx cxx}{ compile } \ + cc/{hxx cxx}{ guess } \ + cc/{hxx cxx}{ init } \ + cc/{hxx cxx}{ install } \ + cc/{hxx cxx}{ link } \ + cc/{hxx cxx}{ module } \ + cc/{ cxx}{ msvc } \ + cc/{hxx cxx}{ target } \ + cc/{hxx ixx cxx}{ utility } \ + cc/{ cxx}{ windows-manifest } \ + cc/{ cxx}{ windows-rpath } \ cli/{hxx cxx}{ module } \ cli/{hxx cxx}{ rule } \ cli/{hxx cxx}{ target } \ config/{hxx cxx}{ module } \ config/{hxx cxx}{ operation } \ config/{hxx txx cxx}{ utility } \ - cxx/{hxx cxx}{ common } \ - cxx/{hxx cxx}{ compile } \ - cxx/{hxx cxx}{ guess } \ - cxx/{hxx cxx}{ install } \ - cxx/{hxx cxx}{ link } \ cxx/{hxx cxx}{ module } \ - cxx/{ cxx}{ msvc } \ cxx/{hxx cxx}{ target } \ - cxx/{hxx ixx cxx}{ utility } \ - cxx/{ cxx}{ windows-manifest } \ - cxx/{ cxx}{ windows-rpath } \ dist/{hxx cxx}{ module } \ dist/{hxx cxx}{ operation } \ dist/{hxx cxx}{ rule } \ diff --git a/build2/c/init b/build2/c/init new file mode 100644 index 0000000..04f193e --- /dev/null +++ b/build2/c/init @@ -0,0 +1,37 @@ +// file : build2/c/init -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_C_INIT +#define BUILD2_C_INIT + +#include <build2/types> +#include <build2/utility> + +#include <build2/module> + +namespace build2 +{ + namespace c + { + bool + config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool + init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + } +} + +#endif // BUILD2_C_INIT diff --git a/build2/c/init.cxx b/build2/c/init.cxx new file mode 100644 index 0000000..7795181 --- /dev/null +++ b/build2/c/init.cxx @@ -0,0 +1,240 @@ +// file : build2/c/init.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <build2/c/init> + +#include <build2/scope> +#include <build2/context> +#include <build2/diagnostics> + +#include <build2/cc/module> + +#include <build2/c/target> + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace c + { + using cc::config_module; + + class module: public cc::module + { + public: + explicit + module (data&& d): common (move (d)), cc::module (move (d)) {} + + bool + translate_std (string&, scope&, const value&) const override; + }; + + bool module:: + translate_std (string& s, scope& r, const value& val) const + { + const string& v (cast<string> (val)); + + if (cid == "msvc") + { + // Standard-wise, with VC you get what you get. The question is + // whether we should verify that the requested standard is provided by + // this VC version. And if so, from which version should we say VC + // supports 90, 99, and 11? We should probably be as loose as possible + // here since the author will always be able to tighten (but not + // loosen) this in the buildfile (i.e., detect unsupported versions). + // + // The state of affairs seem to be (from Herb Sutter's blog): + // + // 10.0 - most of C95 plus a few C99 features + // 11.0 - partial support for the C++11 subset of C11 + // 12.0 - more C11 features from the C++11 subset, most of C99 + // + // So let's say C99 is supported from 10.0 and C11 from 11.0. And C90 + // is supported by everything we care to support. + // + if (v != "90") + { + uint64_t cver (cast<uint64_t> (r[x_version_major])); + + if ((v == "99" && cver < 16) || // Since VS2010/10.0. + (v == "11" && cver < 17)) // Since VS2012/11.0. + { + fail << "C" << v << " is not supported by " + << cast<string> (r[x_signature]) << + info << "required by " << project (r) << '@' << r.out_path (); + } + } + + return false; + } + else + { + // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x + // for compatibility with older versions of the compilers. + // + s = "-std="; + + if (v == "90") + s += "c90"; + else if (v == "99") + s += "c9x"; + else if (v == "11") + s += "c1x"; + else + s += v; // In case the user specifies something like 'gnu11'. + + return true; + } + } + + bool + config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>& m, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("c::config_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + if (first) + { + // Load cc.vars so that we can cache all the cc.* variables. + // + if (!cast_false<bool> (b["cc.vars.loaded"])) + load_module ("cc.vars", r, b, loc); + + // Enter all the variables and initialize the module data. + // + auto& v (var_pool); + + cc::config_data d { + cc::lang::c, + + "c", + "c", + "gcc", + + // Note: some overridable, some not. + // + v.insert<path> ("config.c", true), + v.insert<strings> ("config.c.poptions", true), + v.insert<strings> ("config.c.coptions", true), + v.insert<strings> ("config.c.loptions", true), + v.insert<strings> ("config.c.libs", true), + + v.insert<strings> ("c.poptions"), + v.insert<strings> ("c.coptions"), + v.insert<strings> ("c.loptions"), + v.insert<strings> ("c.libs"), + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.libs"], + + v.insert<strings> ("c.export.poptions"), + v.insert<strings> ("c.export.coptions"), + v.insert<strings> ("c.export.loptions"), + v.insert<strings> ("c.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v.insert<string> ("c.std", true), + + v.insert<string> ("c.id"), + v.insert<string> ("c.id.type"), + v.insert<string> ("c.id.variant"), + + v.insert<string> ("c.version"), + v.insert<uint64_t> ("c.version.major"), + v.insert<uint64_t> ("c.version.minor"), + v.insert<uint64_t> ("c.version.patch"), + v.insert<string> ("c.version.build"), + + v.insert<string> ("c.signature"), + v.insert<string> ("c.checksum"), + + v.insert<string> ("c.target"), + v.insert<string> ("c.target.cpu"), + v.insert<string> ("c.target.vendor"), + v.insert<string> ("c.target.system"), + v.insert<string> ("c.target.version"), + v.insert<string> ("c.target.class") + }; + + assert (m == nullptr); + m.reset (new config_module (move (d))); + } + + static_cast<config_module&> (*m).init (r, b, loc, first, hints); + return true; + } + + static const target_type* hdr[] = + { + &h::static_type, + nullptr + }; + + static const target_type* inc[] = + { + &h::static_type, + &c::static_type, + nullptr + }; + + bool + init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>& m, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("c::init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Load c.config. + // + if (!cast_false<bool> (b["c.config.loaded"])) + load_module ("c.config", r, b, loc, false, hints); + + if (first) + { + config_module& cm (*r.modules.lookup<config_module> ("c.config")); + + cc::data d { + cm, + + "c.compile", + "c.link", + "c.install", + + cast<string> (r[cm.x_id]), + cast<string> (r[cm.x_target]), + cast<string> (r[cm.x_target_system]), + cast<string> (r[cm.x_target_class]), + + c::static_type, + hdr, + inc + }; + + assert (m == nullptr); + m.reset (new module (move (d))); + } + + static_cast<module&> (*m).init (r, b, loc, first, hints); + return true; + } + } +} diff --git a/build2/c/target b/build2/c/target new file mode 100644 index 0000000..bf40306 --- /dev/null +++ b/build2/c/target @@ -0,0 +1,22 @@ +// file : build2/c/target -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_C_TARGET +#define BUILD2_C_TARGET + +#include <build2/types> +#include <build2/utility> + +#include <build2/cc/target> + +namespace build2 +{ + namespace c + { + using cc::h; + using cc::c; + } +} + +#endif // BUILD2_C_TARGET diff --git a/build2/cc/common b/build2/cc/common new file mode 100644 index 0000000..95f205a --- /dev/null +++ b/build2/cc/common @@ -0,0 +1,172 @@ +// file : build2/cc/common -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_COMMON +#define BUILD2_CC_COMMON + +#include <build2/types> +#include <build2/utility> + +#include <build2/variable> + +#include <build2/cc/types> + +namespace build2 +{ + namespace cc + { + // Data entries that define a concrete c-family module (e.g., c or cxx). + // These classes are used as a virtual bases by the rules as well as the + // modules. This way the member variables can be referenced as is, without + // any extra decorations (in other words, it is a bunch of data members + // that can be shared between several classes/instances). + // + struct config_data + { + lang x_lang; + + const char* x; // Module name ("c", "cxx"). + const char* x_name; // Compiler name ("c", "c++"). + const char* x_default; // Compiler default ("gcc", "g++"). + + const variable& config_x; + const variable& config_x_poptions; + const variable& config_x_coptions; + const variable& config_x_loptions; + const variable& config_x_libs; + + const variable& x_poptions; + const variable& x_coptions; + const variable& x_loptions; + const variable& x_libs; + + const variable& c_poptions; // cc.* + const variable& c_coptions; + const variable& c_loptions; + const variable& c_libs; + + const variable& x_export_poptions; + const variable& x_export_coptions; + const variable& x_export_loptions; + const variable& x_export_libs; + + const variable& c_export_poptions; // cc.export.* + const variable& c_export_coptions; + const variable& c_export_loptions; + const variable& c_export_libs; + + const variable& x_std; + + const variable& x_id; + const variable& x_id_type; + const variable& x_id_variant; + + const variable& x_version; + const variable& x_version_major; + const variable& x_version_minor; + const variable& x_version_patch; + const variable& x_version_build; + + const variable& x_signature; + const variable& x_checksum; + + const variable& x_target; + const variable& x_target_cpu; + const variable& x_target_vendor; + const variable& x_target_system; + const variable& x_target_version; + const variable& x_target_class; + }; + + struct data: config_data + { + const char* x_compile; // Rule names. + const char* x_link; + const char* x_install; + + // Cached values for some commonly-used variables. + // + const string& cid; // x.id + const string& ctg; // x.target + const string& tsys; // x.target.system + const string& tclass; // x.target.class + + const target_type& x_src; // Source target type (c{}, cxx{}). + + // Array of target types that are considered headers. Keep them in the + // most likely to appear order and terminate with NULL. + // + const target_type* const* x_hdr; + + template <typename T> + bool + x_header (const T& t) const + { + for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) + if (t.is_a (**ht)) + return true; + + return false; + } + + // Array of target types that can be #include'd. Used to reverse-lookup + // extensions to target types. Keep them in the most likely to appear + // order and terminate with NULL. + // + const target_type* const* x_inc; + + // Aggregate-like constructor with from-base support. + // + data (const config_data& cd, + const char* compile, + const char* link, + const char* install, + const string& id, + const string& tg, + const string& sys, + const string& class_, + const target_type& src, + const target_type* const* hdr, + const target_type* const* inc) + : config_data (cd), + x_compile (compile), x_link (link), x_install (install), + cid (id), ctg (tg), tsys (sys), tclass (class_), + x_src (src), x_hdr (hdr), x_inc (inc) {} + }; + + class common: protected data + { + public: + common (data&& d): data (move (d)) {} + + // Language standard (x.std) mapping. T is either target or scope. + // + template <typename T> + void + append_std (cstrings& args, scope& root, T& t, string& storage) const + { + if (auto l = t[x_std]) + if (translate_std (storage, root, *l)) + args.push_back (storage.c_str ()); + } + + template <typename T> + void + hash_std (sha256& csum, scope& root, T& t) const + { + string s; + if (auto l = t[x_std]) + if (translate_std (s, root, *l)) + csum.append (s); + } + + // Return true if there is an option (stored in the first argument). + // + virtual bool + translate_std (string&, scope&, const value&) const = 0; + }; + } +} + +#endif // BUILD2_CC_COMMON diff --git a/build2/cc/compile b/build2/cc/compile new file mode 100644 index 0000000..6e20836 --- /dev/null +++ b/build2/cc/compile @@ -0,0 +1,82 @@ +// file : build2/cc/compile -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_COMPILE +#define BUILD2_CC_COMPILE + +#include <butl/path-map> + +#include <build2/types> +#include <build2/utility> + +#include <build2/rule> + +#include <build2/cc/types> +#include <build2/cc/common> + +namespace build2 +{ + class depdb; + + namespace cc + { + class link; + + class compile: public rule, virtual common + { + public: + compile (data&&, const link&); + + virtual match_result + match (action, target&, const string& hint) const; + + virtual recipe + apply (action, target&, const match_result&) const; + + target_state + perform_update (action, target&) const; + + target_state + perform_clean (action, target&) const; + + private: + // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto- + // generated headers to directories where they will be generated. + // + // We are using a prefix map of directories (dir_path_map) instead + // of just a map in order also cover sub-paths (e.g., <foo/more/bar> + // if we continue with the example). Specifically, we need to make + // sure we don't treat foobar as a sub-directory of foo. + // + // @@ The keys should be normalized. + // + using prefix_map = butl::dir_path_map<dir_path>; + + void + append_prefixes (prefix_map&, target&, const variable&) const; + + void + append_lib_prefixes (prefix_map&, target&, lorder) const; + + prefix_map + build_prefix_map (target&, lorder) const; + + // Reverse-lookup target type from extension. + // + const target_type* + map_extension (scope&, const string&, const string&) const; + + // Header dependency injection. + // + void + inject (action, target&, lorder, file&, scope&, depdb&) const; + + private: + const link& link_; + const string rule_id; + }; + } +} + +#endif // BUILD2_CC_COMPILE diff --git a/build2/cxx/compile.cxx b/build2/cc/compile.cxx index 56c518b..b5bcc50 100644 --- a/build2/cxx/compile.cxx +++ b/build2/cc/compile.cxx @@ -1,16 +1,12 @@ -// file : build2/cxx/compile.cxx -*- C++ -*- +// file : build2/cc/compile.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <build2/cxx/compile> +#include <build2/cc/compile> -#include <map> -#include <limits> // numeric_limits #include <cstdlib> // exit() #include <iostream> // cerr -#include <butl/path-map> - #include <build2/depdb> #include <build2/scope> #include <build2/context> @@ -19,26 +15,32 @@ #include <build2/diagnostics> #include <build2/bin/target> -#include <build2/cxx/target> - -#include <build2/cxx/link> -#include <build2/cxx/common> -#include <build2/cxx/utility> +#include <build2/cc/link> // search_library() +#include <build2/cc/target> // h +#include <build2/cc/utility> using namespace std; using namespace butl; namespace build2 { - namespace cxx + namespace cc { using namespace bin; + compile:: + compile (data&& d, const link& l) + : common (move (d)), + link_ (l), + rule_id (string (x) += ".compile 1") + { + } + match_result compile:: match (action a, target& t, const string&) const { - tracer trace ("cxx::compile::match"); + tracer trace (x, "compile::match"); // @@ TODO: // @@ -46,37 +48,29 @@ namespace build2 // - if path already assigned, verify extension? // - // See if we have a C++ source file. Iterate in reverse so that - // a source file specified for an obj*{} member overrides the one - // specified for the group. Also "see through" groups. + // See if we have a source file. Iterate in reverse so that a source + // file specified for an obj*{} member overrides the one specified for + // the group. Also "see through" groups. // for (prerequisite_member p: reverse_group_prerequisite_members (a, t)) { - if (p.is_a<cxx> ()) + if (p.is_a (x_src)) return p; } - l4 ([&]{trace << "no c++ source file for target " << t;}); + l4 ([&]{trace << "no " << x_lang << " source file for target " << t;}); return nullptr; } - static void - inject_prerequisites (action, target&, lorder, cxx&, scope&, depdb&); - recipe compile:: apply (action a, target& xt, const match_result& mr) const { - tracer trace ("cxx::compile"); + tracer trace (x, "compile::apply"); file& t (static_cast<file&> (xt)); scope& bs (t.base_scope ()); scope& rs (*bs.root_scope ()); - - const string& cid (cast<string> (rs["cxx.id"])); - const string& tsys (cast<string> (rs["cxx.target.system"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - otype ct (compile_type (t)); // Derive file name from target name. @@ -129,22 +123,21 @@ namespace build2 // fsdir* dir (inject_fsdir (a, t)); - // Search and match all the existing prerequisites. The injection - // code (below) takes care of the ones it is adding. + // Search and match all the existing prerequisites. The injection code + // takes care of the ones it is adding. // - // When cleaning, ignore prerequisites that are not in the same - // or a subdirectory of our project root. + // When cleaning, ignore prerequisites that are not in the same or a + // subdirectory of our project root. // optional<dir_paths> lib_paths; // Extract lazily. for (prerequisite_member p: group_prerequisite_members (a, t)) { // A dependency on a library is there so that we can get its - // cxx.export.poptions. In particular, making sure it is - // executed before us will only restrict parallelism. But we - // do need to pre-match it in order to get its - // prerequisite_targets populated. This is the "library - // meta-information protocol". See also append_lib_options() + // *.export.poptions. In particular, making sure it is executed before + // us will only restrict parallelism. But we do need to pre-match it + // in order to get its prerequisite_targets populated. This is the + // "library meta-information protocol". See also append_lib_options() // above. // if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ()) @@ -156,7 +149,7 @@ namespace build2 // any, they would be set by search_library()). // if (p.proj () == nullptr || - link::search_library (lib_paths, p.prerequisite) == nullptr) + link_.search_library (lib_paths, p.prerequisite) == nullptr) { match_only (a, p.search ()); } @@ -184,11 +177,7 @@ namespace build2 // t.prerequisite_targets since we used standard search() and match() // above. // - // @@ Ugly. - // - cxx& st ( - dynamic_cast<cxx&> ( - mr.target != nullptr ? *mr.target : *mr.prerequisite->target)); + file& src (mr.as_target<file> ()); // Make sure the output directory exists. // @@ -208,14 +197,14 @@ namespace build2 // First should come the rule name/version. // - if (dd.expect ("cxx.compile 1") != nullptr) + if (dd.expect (rule_id) != nullptr) l4 ([&]{trace << "rule mismatch forcing update of " << t;}); // Then the compiler checksum. Note that here we assume it // incorporates the (default) target so that if the compiler changes // but only in what it targets, then the checksum will still change. // - if (dd.expect (cast<string> (rs["cxx.checksum"])) != nullptr) + if (dd.expect (cast<string> (rs[x_checksum])) != nullptr) l4 ([&]{trace << "compiler mismatch forcing update of " << t;}); // Then the options checksum. @@ -225,7 +214,7 @@ namespace build2 // sha256 cs; - // Hash cxx.export.poptions from prerequisite libraries. + // Hash *.export.poptions from prerequisite libraries. // lorder lo (link_order (bs, ct)); for (prerequisite& p: group_prerequisites (t)) @@ -236,12 +225,16 @@ namespace build2 pt = &link_member (*l, lo); if (pt->is_a<liba> () || pt->is_a<libs> ()) - hash_lib_options (cs, *pt, "cxx.export.poptions", lo); + hash_lib_options (cs, *pt, lo, + c_export_poptions, + x_export_poptions); } - hash_options (cs, t, "cxx.poptions"); - hash_options (cs, t, "cxx.coptions"); - hash_std (cs, rs, cid, t); + hash_options (cs, t, c_poptions); + hash_options (cs, t, x_poptions); + hash_options (cs, t, c_coptions); + hash_options (cs, t, x_coptions); + hash_std (cs, rs, t); if (ct == otype::s) { @@ -256,7 +249,7 @@ namespace build2 // Finally the source file. // - if (dd.expect (st.path ()) != nullptr) + if (dd.expect (src.path ()) != nullptr) l4 ([&]{trace << "source file mismatch forcing update of " << t;}); // If any of the above checks resulted in a mismatch (different @@ -266,65 +259,51 @@ namespace build2 if (dd.writing () || dd.mtime () > t.mtime ()) t.mtime (timestamp_nonexistent); - inject_prerequisites (a, t, lo, st, mr.prerequisite->scope, dd); + inject (a, t, lo, src, mr.prerequisite->scope, dd); dd.close (); } switch (a) { - case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean; - default: return noop_recipe; // Configure update. + case perform_update_id: + return [this] (action a, target& t) {return perform_update (a, t);}; + case perform_clean_id: + return [this] (action a, target& t) {return perform_clean (a, t);}; + default: + return noop_recipe; // Configure update. } } // Reverse-lookup target type from extension. // - static const target_type* - map_extension (scope& s, const string& n, const string& e) + const target_type* compile:: + map_extension (scope& s, const string& n, const string& e) const { - // We will just have to try all of the possible ones, in the - // "most likely to match" order. + // We will just have to try all of the possible ones, in the "most + // likely to match" order. // - const variable& var (var_pool.find ("extension")); + const variable& var (var_pool["extension"]); - auto test = [&s, &n, &e, &var] (const target_type& tt) - -> const target_type* + auto test = [&s, &n, &e, &var] (const target_type& tt) -> bool { if (auto l = s.find (var, tt, n)) if (cast<string> (l) == e) - return &tt; + return true; - return nullptr; + return false; }; - if (auto r = test (hxx::static_type)) return r; - if (auto r = test (h::static_type)) return r; - if (auto r = test (ixx::static_type)) return r; - if (auto r = test (txx::static_type)) return r; - if (auto r = test (cxx::static_type)) return r; - if (auto r = test (c::static_type)) return r; + for (const target_type* const* p (x_inc); *p != nullptr; ++p) + if (test (**p)) return *p; return nullptr; } - // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto- - // generated headers to directories where they will be generated. - // - // We are using a prefix map of directories (dir_path_map) instead - // of just a map in order also cover sub-paths (e.g., <foo/more/bar> - // if we continue with the example). Specifically, we need to make - // sure we don't treat foobar as a sub-directory of foo. - // - // @@ The keys should be canonicalized. - // - using prefix_map = dir_path_map<dir_path>; - - static void - append_prefixes (prefix_map& m, target& t, const char* var) + void compile:: + append_prefixes (prefix_map& m, target& t, const variable& var) const { - tracer trace ("cxx::append_prefixes"); + tracer trace (x, "append_prefixes"); // If this target does not belong to any project (e.g, an // "imported as installed" library), then it can't possibly @@ -411,11 +390,11 @@ namespace build2 } } - // Append library prefixes based on the cxx.export.poptions variables + // Append library prefixes based on the *.export.poptions variables // recursively, prerequisite libraries first. // - static void - append_lib_prefixes (prefix_map& m, target& l, lorder lo) + void compile:: + append_lib_prefixes (prefix_map& m, target& l, lorder lo) const { for (target* t: l.prerequisite_targets) { @@ -429,17 +408,17 @@ namespace build2 append_lib_prefixes (m, *t, lo); } - append_prefixes (m, l, "cxx.export.poptions"); + append_prefixes (m, l, c_export_poptions); + append_prefixes (m, l, x_export_poptions); } - static prefix_map - build_prefix_map (target& t, lorder lo) + auto compile:: + build_prefix_map (target& t, lorder lo) const -> prefix_map { prefix_map m; - // First process the include directories from prerequisite - // libraries. Note that here we don't need to see group - // members (see apply()). + // First process the include directories from prerequisite libraries. + // Note that here we don't need to see group members (see apply()). // for (prerequisite& p: group_prerequisites (t)) { @@ -454,7 +433,8 @@ namespace build2 // Then process our own. // - append_prefixes (m, t, "cxx.poptions"); + append_prefixes (m, t, c_poptions); + append_prefixes (m, t, x_poptions); return m; } @@ -521,11 +501,10 @@ namespace build2 return r; } - // Extract the include path from the VC++ /showIncludes output line. - // Return empty string if the line is not an include note or include - // error. Set the good_error flag if it is an include error (which means - // the process will terminate with the error status that needs to be - // ignored). + // Extract the include path from the VC /showIncludes output line. Return + // empty string if the line is not an include note or include error. Set + // the good_error flag if it is an include error (which means the process + // will terminate with the error status that needs to be ignored). // static string next_show (const string& l, bool& good_error) @@ -534,7 +513,7 @@ namespace build2 // assert (!good_error); - // VC++ /showIncludes output. The first line is the file being + // VC /showIncludes output. The first line is the file being // compiled. Then we have the list of headers, one per line, in this // form (text can presumably be translated): // @@ -634,11 +613,15 @@ namespace build2 } } - static void - inject_prerequisites (action a, target& t, lorder lo, - cxx& s, scope& ds, depdb& dd) + void compile:: + inject (action a, + target& t, + lorder lo, + file& src, + scope& ds, + depdb& dd) const { - tracer trace ("cxx::compile::inject_prerequisites"); + tracer trace (x, "compile::inject"); l6 ([&]{trace << "target: " << t;}); @@ -647,28 +630,24 @@ namespace build2 // auto g ( make_exception_guard ( - [&s]() + [&src]() { - info << "while extracting header dependencies from " << s; + info << "while extracting header dependencies from " << src; })); scope& rs (t.root_scope ()); - const string& cid (cast<string> (rs["cxx.id"])); // Initialize lazily, only if required. // cstrings args; - string cxx_std; // Storage. + string std; // Storage. - auto init_args = [&t, lo, &s, &rs, &cid, &args, &cxx_std] () + auto init_args = [&t, lo, &src, &rs, &args, &std, this] () { - const path& cxx (cast<path> (rs["config.cxx"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); + args.push_back (cast<path> (rs[config_x]).string ().c_str ()); - args.push_back (cxx.string ().c_str ()); - - // Add cxx.export.poptions from prerequisite libraries. Note - // that here we don't need to see group members (see apply()). + // Add *.export.poptions from prerequisite libraries. Note that here + // we don't need to see group members (see apply()). // for (prerequisite& p: group_prerequisites (t)) { @@ -678,15 +657,20 @@ namespace build2 pt = &link_member (*l, lo); if (pt->is_a<liba> () || pt->is_a<libs> ()) - append_lib_options (args, *pt, "cxx.export.poptions", lo); + append_lib_options (args, *pt, lo, + c_export_poptions, + x_export_poptions); } - append_options (args, t, "cxx.poptions"); + append_options (args, t, c_poptions); + append_options (args, t, x_poptions); - // Some C++ options (e.g., -std, -m) affect the preprocessor. + // Some compile options (e.g., -std, -m) affect the preprocessor. // - append_options (args, t, "cxx.coptions"); - append_std (args, rs, cid, t, cxx_std); + append_options (args, t, c_coptions); + append_options (args, t, x_coptions); + + append_std (args, rs, t, std); if (t.is_a<objs> ()) { @@ -703,15 +687,15 @@ namespace build2 // See perform_update() for details on overriding the default // exceptions and runtime. // - if (!find_option_prefix ("/EH", args)) + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) args.push_back ("/EHsc"); if (!find_option_prefixes ({"/MD", "/MT"}, args)) args.push_back ("/MD"); args.push_back ("/EP"); // Preprocess to stdout. - args.push_back ("/TP"); // Preprocess as C++. args.push_back ("/showIncludes"); // Goes to sterr becasue of /EP. + args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as. } else { @@ -734,7 +718,7 @@ namespace build2 // @@ We will also have to use absolute -I paths to guarantee // that. Or just detect relative paths and error out? // - args.push_back (s.path ().string ().c_str ()); + args.push_back (src.path ().string ().c_str ()); args.push_back (nullptr); }; @@ -826,7 +810,7 @@ namespace build2 // from the depdb cache or from the compiler run. Return whether the // extraction process should be restarted. // - auto add = [&trace, &update, &pm, a, &t, lo, &ds, &dd] + auto add = [&trace, &update, &pm, a, &t, lo, &ds, &dd, this] (path f, bool cache) -> bool { if (!f.absolute ()) @@ -926,8 +910,8 @@ namespace build2 out = out_src (d, *rs); } - // If it is outside any project, or the project doesn't have - // such an extension, assume it is a plain old C header. + // If it is outside any project, or the project doesn't have such an + // extension, assume it is a plain old C header. // if (tt == nullptr) tt = &h::static_type; @@ -980,7 +964,7 @@ namespace build2 // But, before we do all that, make sure the source file itself if up to // date. // - if (update (s, dd.mtime ())) + if (update (src, dd.mtime ())) { // If the file got updated or is newer than the database, then we // cannot rely on the cache any further. However, the cached data @@ -1099,7 +1083,7 @@ namespace build2 // this case the first line (and everything after it) is // presumably diagnostics. // - if (l != s.path ().leaf ().string ()) + if (l != src.path ().leaf ().string ()) { text << l; bad_error = true; @@ -1205,8 +1189,8 @@ namespace build2 } } - // In case of VC++, we are parsing stderr and if things go - // south, we need to copy the diagnostics for the user to see. + // In case of VC, we are parsing stderr and if things go south, + // we need to copy the diagnostics for the user to see. // // Note that the eof check is important: if the stream is at // eof, this and all subsequent writes to cerr will fail (and @@ -1227,12 +1211,14 @@ namespace build2 throw failed (); } else if (bad_error) - fail << "expected error exist status from C++ compiler"; + fail << "expected error exist status from " << x_lang + << " compiler"; } catch (const ifdstream::failure&) { pr.wait (); - fail << "unable to read C++ compiler header dependency output"; + fail << "unable to read " << x_lang << " compiler header " + << "dependency output"; } } catch (const process_error& e) @@ -1258,24 +1244,19 @@ namespace build2 msvc_filter_cl (ifdstream&, const path& src); target_state compile:: - perform_update (action a, target& xt) + perform_update (action a, target& xt) const { file& t (static_cast<file&> (xt)); - cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ())); + file* s (execute_prerequisites<file> (x_src, a, t, t.mtime ())); if (s == nullptr) return target_state::unchanged; scope& bs (t.base_scope ()); scope& rs (*bs.root_scope ()); - - const path& cxx (cast<path> (rs["config.cxx"])); - const string& cid (cast<string> (rs["cxx.id"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - otype ct (compile_type (t)); - cstrings args {cxx.string ().c_str ()}; + cstrings args {cast<path> (rs[config_x]).string ().c_str ()}; // Translate paths to relative (to working directory) ones. This // results in easier to read diagnostics. @@ -1283,8 +1264,8 @@ namespace build2 path relo (relative (t.path ())); path rels (relative (s->path ())); - // Add cxx.export.poptions from prerequisite libraries. Note that - // here we don't need to see group members (see apply()). + // Add *.export.poptions from prerequisite libraries. Note that here we + // don't need to see group members (see apply()). // lorder lo (link_order (bs, ct)); for (prerequisite& p: group_prerequisites (t)) @@ -1295,15 +1276,19 @@ namespace build2 pt = &link_member (*l, lo); if (pt->is_a<liba> () || pt->is_a<libs> ()) - append_lib_options (args, *pt, "cxx.export.poptions", lo); + append_lib_options (args, *pt, lo, + c_export_poptions, + x_export_poptions); } - append_options (args, t, "cxx.poptions"); - append_options (args, t, "cxx.coptions"); + append_options (args, t, c_poptions); + append_options (args, t, x_poptions); + append_options (args, t, c_coptions); + append_options (args, t, x_coptions); string std, out, out1; // Storage. - append_std (args, rs, cid, t, std); + append_std (args, rs, t, std); if (cid == "msvc") { @@ -1311,7 +1296,7 @@ namespace build2 // in VS2013/12.0. Why do we bother? Because the command line suddenly // becomes readable. // - uint64_t cver (cast<uint64_t> (rs["cxx.version.major"])); + uint64_t ver (cast<uint64_t> (rs[x_version_major])); args.push_back ("/nologo"); @@ -1322,7 +1307,10 @@ namespace build2 // see any relevant options explicitly specified, we take our hands // off. // - if (!find_option_prefix ("/EH", args)) + // For C looks like no /EH* (exceptions supported but no C++ objects + // destroyed) is a reasonable default. + // + if (x_lang == lang::cxx && !find_option_prefix ("/EH", args)) args.push_back ("/EHsc"); // The runtime is a bit more interesting. At first it may seem like a @@ -1363,7 +1351,7 @@ namespace build2 // if (find_options ({"/Zi", "/ZI"}, args)) { - if (cver >= 18) + if (ver >= 18) args.push_back ("/Fd:"); else out1 = "/Fd"; @@ -1374,7 +1362,7 @@ namespace build2 args.push_back (out1.c_str ()); } - if (cver >= 18) + if (ver >= 18) { args.push_back ("/Fo:"); args.push_back (relo.string ().c_str ()); @@ -1385,8 +1373,8 @@ namespace build2 args.push_back (out.c_str ()); } - args.push_back ("/c"); // Compile only. - args.push_back ("/TP"); // Compile as C++. + args.push_back ("/c"); // Compile only. + args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as. args.push_back (rels.string ().c_str ()); } else @@ -1411,7 +1399,7 @@ namespace build2 if (verb >= 2) print_process (args); else if (verb) - text << "c++ " << *s; + text << x_name << ' ' << *s; try { @@ -1475,13 +1463,10 @@ namespace build2 } target_state compile:: - perform_clean (action a, target& xt) + perform_clean (action a, target& xt) const { file& t (static_cast<file&> (xt)); - scope& rs (t.root_scope ()); - const string& cid (cast<string> (rs["cxx.id"])); - initializer_list<const char*> e; if (cid == "msvc") @@ -1491,7 +1476,5 @@ namespace build2 return clean_extra (a, t, e); } - - compile compile::instance; } } diff --git a/build2/cxx/guess b/build2/cc/guess index 63858ad..977e081 100644 --- a/build2/cxx/guess +++ b/build2/cc/guess @@ -1,28 +1,29 @@ -// file : build2/cxx/guess -*- C++ -*- +// file : build2/cc/guess -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef BUILD2_CXX_GUESS -#define BUILD2_CXX_GUESS +#ifndef BUILD2_CC_GUESS +#define BUILD2_CC_GUESS #include <build2/types> #include <build2/utility> +#include <build2/cc/types> + namespace build2 { - namespace cxx + namespace cc { - // C++ compiler id consisting of a type and optional variant. If the - // variant is not empty, then the id is spelled out as 'type-variant', - // similar to target triplets (this also means that the type cannot - // contain '-'). + // Compiler id consisting of a type and optional variant. If the variant + // is not empty, then the id is spelled out as 'type-variant', similar to + // target triplets (this also means that the type cannot contain '-'). // // Currently recognized compilers and their ids: // - // gcc GCC g++ - // clang Vanilla Clang clang++ - // clang-apple Apple Clang clang++ and the g++ "alias" - // icc Intel icpc + // gcc GCC gcc/g++ + // clang Vanilla Clang clang/clang++ + // clang-apple Apple Clang clang/clang++ and the gcc/g++ "alias" + // icc Intel icc/icpc // msvc Microsoft cl.exe // struct compiler_id @@ -43,8 +44,8 @@ namespace build2 return os << id.string (); } - // C++ compiler version. Here we map the various compiler version formats - // to something that resembles the MAJOR.MINOR.PATCH-BUILD form of the + // Compiler version. Here we map the various compiler version formats to + // something that resembles the MAJOR.MINOR.PATCH-BUILD form of the // Semantic Versioning. While the MAJOR.MINOR part is relatively // straightforward, PATCH may be empty and BUILD can contain pretty much // anything (including spaces). @@ -74,7 +75,7 @@ namespace build2 std::string build; }; - // C++ compiler information. + // Compiler information. // // The signature is normally the -v/--version line that was used to guess // the compiler id and its version. @@ -95,6 +96,9 @@ namespace build2 // unlike all the preceding fields, this one takes into account the // compile options (e.g., -m32). // + // The pattern is the toolchain program pattern that could sometimes be + // derived for some toolchains. For example, i686-w64-mingw32-*. + // struct compiler_info { compiler_id id; @@ -102,11 +106,20 @@ namespace build2 string signature; string checksum; string target; + string pattern; }; + // In a sense this is analagous to the language standard which we handle + // via a virtual function in common. However, duplicating this hairy ball + // of fur in multiple places doesn't seem wise, especially considering + // that most of it will be the same, at least for C and C++. + // compiler_info - guess (const path& cxx, const strings* coptions); + guess (lang, + const path& xc, + const strings* c_coptions, + const strings* x_coptions); } } -#endif // BUILD2_CXX_GUESS +#endif // BUILD2_CC_GUESS diff --git a/build2/cxx/guess.cxx b/build2/cc/guess.cxx index 11a832c..d80dddd 100644 --- a/build2/cxx/guess.cxx +++ b/build2/cc/guess.cxx @@ -1,8 +1,8 @@ -// file : build2/cxx/guess.cxx -*- C++ -*- +// file : build2/cc/guess.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <build2/cxx/guess> +#include <build2/cc/guess> #include <cstring> // strlen() @@ -12,7 +12,7 @@ using namespace std; namespace build2 { - namespace cxx + namespace cc { // Pre-guess the compiler type based on the compiler executable name. // Return empty string if can't make a guess (for example, because the @@ -20,11 +20,11 @@ namespace build2 // not the variant. // static string - pre_guess (const path& cxx) + pre_guess (lang xl, const path& xc) { - tracer trace ("cxx::pre_guess"); + tracer trace ("cc::pre_guess"); - const string s (cxx.leaf ().base ().string ()); + const string s (xc.leaf ().base ().string ()); size_t n (s.size ()); // Name separator characters (e.g., '-' in 'g++-4.8'). @@ -44,39 +44,55 @@ namespace build2 ((p += m) == n || sep (s[p])); // Separated at the end. }; - if (stem ("g++")) - return "gcc"; + // Warn if the user specified a C compiler instead of C++ or vice versa. + // + lang o; // Other language. + const char* as (nullptr); // Actual stem. + const char* es (nullptr); // Expected stem. - if (stem ("clang++")) - return "clang"; + switch (xl) + { + case lang::c: + { + // Keep msvc last since 'cl' is very generic. + // + if (stem ("gcc")) return "gcc"; + if (stem ("clang")) return "clang"; + if (stem ("icc")) return "icc"; + if (stem ("cl")) return "msvc"; - if (stem ("icpc")) - return "icc"; + if (stem (as = "g++")) es = "gcc"; + else if (stem (as = "clang++")) es = "clang"; + else if (stem (as = "icpc")) es = "icc"; + else if (stem (as = "c++")) es = "cc"; - // Keep this one last since 'cl' is very generic. - // - if (stem ("cl")) - return "msvc"; + o = lang::cxx; + break; + } + case lang::cxx: + { + // Keep msvc last since 'cl' is very generic. + // + if (stem ("g++")) return "gcc"; + if (stem ("clang++")) return "clang"; + if (stem ("icpc")) return "icc"; + if (stem ("cl")) return "msvc"; - // Warn if the user specified a C compiler instead of C++. - // - if (stem ("gcc")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'g++' instead of 'gcc'?"; - } - else if (stem ("clang")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'clang++' instead of 'clang'?"; - } - else if (stem ("icc")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'icpc' instead of 'icc'?"; + if (stem (as = "gcc")) es = "g++"; + else if (stem (as = "clang")) es = "clang++"; + else if (stem (as = "icc")) es = "icpc"; + else if (stem (as = "cc")) es = "c++"; + + o = lang::c; + break; + } } - l4 ([&]{trace << "unable to guess compiler type of " << cxx;}); + if (es != nullptr) + warn << xc << " looks like a " << o << " compiler" << + info << "should it be '" << es << "' instead of '" << as << "'?"; + + l4 ([&]{trace << "unable to guess compiler type of " << xc;}); return ""; } @@ -95,9 +111,9 @@ namespace build2 }; static guess_result - guess (const path& cxx, const string& pre) + guess (lang, const path& xc, const string& pre) { - tracer trace ("cxx::guess"); + tracer trace ("cc::guess"); guess_result r; @@ -120,7 +136,8 @@ namespace build2 { auto f = [] (string& l) -> guess_result { - // The g++ -v output will have a line (currently last) in the form: + // The gcc/g++ -v output will have a line (currently last) in the + // form: // // "gcc version X.Y.Z ..." // @@ -137,8 +154,8 @@ namespace build2 if (l.compare (0, 4, "gcc ") == 0) return guess_result {{"gcc", ""}, move (l), ""}; - // The Apple clang++ -v output will have a line (currently first) - // in the form: + // The Apple clang/clang++ -v output will have a line (currently + // first) in the form: // // "Apple (LLVM|clang) version X.Y.Z ..." // @@ -155,19 +172,20 @@ namespace build2 // Apple LLVM version 7.0.2 (clang-700.1.81) // Apple LLVM version 7.3.0 (clang-703.0.16.1) // - // Note that the g++ "alias" for clang++ also includes this line - // but it is (currently) preceded by "Configured with: ...". + // Note that the gcc/g++ "aliases" for clang/clang++ also include + // this line but it is (currently) preceded by "Configured with: + // ...". // - // Check for Apple clang before the vanilla one since the above - // line also includes "clang". + // Check for Apple clang before the vanilla one since the above line + // also includes "clang". // if (l.compare (0, 6, "Apple ") == 0 && (l.compare (6, 5, "LLVM ") == 0 || l.compare (6, 6, "clang ") == 0)) return guess_result {{"clang", "apple"}, move (l), ""}; - // The vanilla clang++ -v output will have a line (currently first) - // in the form: + // The vanilla clang/clang++ -v output will have a line (currently + // first) in the form: // // "[... ]clang version X.Y.Z[-...] ..." // @@ -189,12 +207,16 @@ namespace build2 // clang) which makes sense to include into the compiler checksum. So // ask run() to calculate it for every line of the -v ouput. // + // One notable consequence of this is that if the locale changes + // (e.g., via LC_ALL), then the compiler signature will most likely + // change as well because of the translated text. + // sha256 cs; // Suppress all the compiler errors because we may be trying an // unsupported option. // - r = run<guess_result> (cxx, "-v", f, false, false, &cs); + r = run<guess_result> (xc, "-v", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -214,6 +236,7 @@ namespace build2 // icpc (ICC) 14.0.0 20130728 // icpc (ICC) 15.0.2 20150121 // icpc (ICC) 16.0.2 20160204 + // icc (ICC) 16.0.2 20160204 // if (l.find (" (ICC) ") != string::npos) return guess_result {{"icc", ""}, move (l), ""}; @@ -221,7 +244,7 @@ namespace build2 return guess_result (); }; - r = run<guess_result> (cxx, "--version", f, false); + r = run<guess_result> (xc, "--version", f, false); } // Finally try to run it without any options to detect msvc. @@ -253,7 +276,7 @@ namespace build2 return guess_result (); }; - r = run<guess_result> (cxx, f, false); + r = run<guess_result> (xc, f, false); } if (!r.empty ()) @@ -267,19 +290,23 @@ namespace build2 r = guess_result (); } else - l5 ([&]{trace << cxx << " is " << r.id << ": '" + l5 ([&]{trace << xc << " is " << r.id << ": '" << r.signature << "'";}); } else - l4 ([&]{trace << "unable to determine compiler type of " << cxx;}); + l4 ([&]{trace << "unable to determine compiler type of " << xc;}); return r; } static compiler_info - guess_gcc (const path& cxx, const strings* coptions, guess_result&& gr) + guess_gcc (lang, + const path& xc, + const strings* c_coptions, + const strings* x_coptions, + guess_result&& gr) { - tracer trace ("cxx::guess_gcc"); + tracer trace ("cc::guess_gcc"); // Extract the version. The signature line has the following format // though language words can be translated and even rearranged (see @@ -356,9 +383,9 @@ namespace build2 // multi-arch support), then use the result. Otherwise, fallback to // -dumpmachine (older gcc or not multi-arch). // - cstrings args {cxx.string ().c_str (), "-print-multiarch"}; - if (coptions != nullptr) - append_options (args, *coptions); + cstrings args {xc.string ().c_str (), "-print-multiarch"}; + if (c_coptions != nullptr) append_options (args, *c_coptions); + if (x_coptions != nullptr) append_options (args, *x_coptions); args.push_back (nullptr); // The output of both -print-multiarch and -dumpmachine is a single line @@ -370,7 +397,7 @@ namespace build2 if (t.empty ()) { - l5 ([&]{trace << cxx << " doesn's support -print-multiarch, " + l5 ([&]{trace << xc << " doesn's support -print-multiarch, " << "falling back to -dumpmachine";}); args[1] = "-dumpmachine"; @@ -378,7 +405,7 @@ namespace build2 } if (t.empty ()) - fail << "unable to extract target architecture from " << cxx + fail << "unable to extract target architecture from " << xc << " -print-multiarch or -dumpmachine output"; return compiler_info { @@ -386,11 +413,16 @@ namespace build2 move (v), move (gr.signature), move (gr.checksum), // Calculated on whole -v output. - move (t)}; + move (t), + string ()}; } static compiler_info - guess_clang (const path& cxx, const strings* coptions, guess_result&& gr) + guess_clang (lang, + const path& xc, + const strings* c_coptions, + const strings* x_coptions, + guess_result&& gr) { // Extract the version. Here we will try to handle both vanilla and // Apple clang since the signature lines are fairly similar. They have @@ -466,9 +498,9 @@ namespace build2 // Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine, // however, respects the compile options (e.g., -m32). // - cstrings args {cxx.string ().c_str (), "-dumpmachine"}; - if (coptions != nullptr) - append_options (args, *coptions); + cstrings args {xc.string ().c_str (), "-dumpmachine"}; + if (c_coptions != nullptr) append_options (args, *c_coptions); + if (x_coptions != nullptr) append_options (args, *x_coptions); args.push_back (nullptr); // The output of -dumpmachine is a single line containing just the @@ -477,7 +509,7 @@ namespace build2 string t (run<string> (args.data (), [] (string& l) {return move (l);})); if (t.empty ()) - fail << "unable to extract target architecture from " << cxx + fail << "unable to extract target architecture from " << xc << " -dumpmachine output"; return compiler_info { @@ -485,11 +517,16 @@ namespace build2 move (v), move (gr.signature), move (gr.checksum), // Calculated on whole -v output. - move (t)}; + move (t), + string ()}; } static compiler_info - guess_icc (const path& cxx, const strings* coptions, guess_result&& gr) + guess_icc (lang xl, + const path& xc, + const strings* c_coptions, + const strings* x_coptions, + guess_result&& gr) { // Extract the version. If the version has the fourth component, then // the signature line (extracted with --version) won't include it. So we @@ -511,6 +548,7 @@ namespace build2 // Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.2.181 Build 20160204 // Intel(R) C++ Intel(R) 64 Compiler for applications running on IA-32, Version 16.0.2.181 Build 20160204 // Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) MIC Architecture, Version 16.0.2.181 Build 20160204 + // Intel(R) C Intel(R) 64 Compiler for applications running on Intel(R) MIC Architecture, Version 16.0.2.181 Build 20160204 // // We should probably also assume the language words can be translated // and even rearranged. @@ -527,13 +565,14 @@ namespace build2 // The -V output is sent to STDERR. // - s = run<string> (cxx, "-V", f, false); + s = run<string> (xc, "-V", f, false); if (s.empty ()) - fail << "unable to extract signature from " << cxx << " -V output"; + fail << "unable to extract signature from " << xc << " -V output"; - if (s.find ("C++") == string::npos) - fail << cxx << " does not appear to be the Intel C++ compiler" << + if (s.find (xl == lang::c ? " C " : " C++ ") == string::npos) + fail << xc << " does not appear to be the Intel " << xl + << " compiler" << info << "extracted signature: '" << s << "'"; // Scan the string as words and look for the version. It consist of only @@ -619,9 +658,9 @@ namespace build2 // "Intel(R)" "64" // "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux) // - cstrings args {cxx.string ().c_str (), "-V"}; - if (coptions != nullptr) - append_options (args, *coptions); + cstrings args {xc.string ().c_str (), "-V"}; + if (c_coptions != nullptr) append_options (args, *c_coptions); + if (x_coptions != nullptr) append_options (args, *x_coptions); args.push_back (nullptr); // The -V output is sent to STDERR. @@ -629,7 +668,7 @@ namespace build2 string t (run<string> (args.data (), f, false)); if (t.empty ()) - fail << "unable to extract target architecture from " << cxx + fail << "unable to extract target architecture from " << xc << " -V output"; string arch; @@ -668,10 +707,10 @@ namespace build2 // on which we are running), who knows what will happen in the future. // So instead we are going to use -dumpmachine and substitute the CPU. // - t = run<string> (cxx, "-dumpmachine", [] (string& l) {return move (l);}); + t = run<string> (xc, "-dumpmachine", [] (string& l) {return move (l);}); if (t.empty ()) - fail << "unable to extract target architecture from " << cxx + fail << "unable to extract target architecture from " << xc << " -dumpmachine output"; // The first component in the triplet is always CPU. @@ -692,11 +731,16 @@ namespace build2 move (v), move (gr.signature), cs.string (), - move (arch)}; + move (arch), + string ()}; } static compiler_info - guess_msvc (const path&, guess_result&& gr) + guess_msvc (lang, + const path& xc, + const strings*, + const strings*, + guess_result&& gr) { // Extract the version. The signature line has the following format // though language words can be translated and even rearranged (see @@ -815,9 +859,9 @@ namespace build2 // // The (toolchain) VENDOR is also straightforward: 'microsoft'. Why not // omit it? Two reasons: firstly, there are other compilers with the - // otherwise same target, for example Intel C++, and it could be useful - // to distinguish between them. Secondly, by having all four components - // we remove any parsing ambiguity. + // otherwise same target, for example Intel C/C++, and it could be + // useful to distinguish between them. Secondly, by having all four + // components we remove any parsing ambiguity. // // OS-ABI is where things are not as clear cut. The OS part shouldn't // probably be just 'windows' since we have Win32 and WinCE. And WinRT. @@ -881,6 +925,31 @@ namespace build2 << "' to runtime version"; } + // Derive the toolchain pattern. + // + // If the compiler name is/starts with 'cl' (e.g., cl.exe, cl-14), + // then replace it with '*' and use it as a pattern for lib, link, + // etc. + // + string pat; + + if (xc.size () > 2) + { + const string& l (xc.leaf ().string ()); + size_t n (l.size ()); + + if (n >= 2 && + (l[0] == 'c' || l[0] == 'C') && + (l[1] == 'l' || l[1] == 'L') && + (n == 2 || l[2] == '.' || l[2] == '-')) + { + path p (xc.directory ()); + p /= "*"; + p += l.c_str () + 2; + pat = move (p).string (); + } + } + // Use the signature line to generate the checksum. // sha256 cs (s); @@ -890,13 +959,17 @@ namespace build2 move (v), move (gr.signature), cs.string (), - move (arch)}; + move (arch), + move (pat)}; } compiler_info - guess (const path& cxx, const strings* coptions) + guess (lang xl, + const path& xc, + const strings* c_coptions, + const strings* x_coptions) { - string pre (pre_guess (cxx)); + string pre (pre_guess (xl, xc)); guess_result gr; // If we could pre-guess the type based on the excutable name, then @@ -904,45 +977,76 @@ namespace build2 // if (!pre.empty ()) { - gr = guess (cxx, pre); + gr = guess (xl, xc, pre); if (gr.empty ()) - warn << cxx << " name looks like " << pre << " but it is not"; + warn << xc << " name looks like " << pre << " but it is not"; } if (gr.empty ()) - gr = guess (cxx, ""); + gr = guess (xl, xc, ""); if (gr.empty ()) - fail << "unable to guess C++ compiler type of " << cxx; + fail << "unable to guess " << xl << " compiler type of " << xc; + compiler_info r; const compiler_id& id (gr.id); if (id.type == "gcc") { assert (id.variant.empty ()); - return guess_gcc (cxx, coptions, move (gr)); + r = guess_gcc (xl, xc, c_coptions, x_coptions, move (gr)); } else if (id.type == "clang") { assert (id.variant.empty () || id.variant == "apple"); - return guess_clang (cxx, coptions, move (gr)); + r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr)); } else if (id.type == "icc") { assert (id.variant.empty ()); - return guess_icc (cxx, coptions, move (gr)); + r = guess_icc (xl, xc, c_coptions, x_coptions, move (gr)); } else if (id.type == "msvc") { assert (id.variant.empty ()); - return guess_msvc (cxx, move (gr)); + r = guess_msvc (xl, xc, c_coptions, x_coptions, move (gr)); } else - { assert (false); - return compiler_info (); + + // Derive binutils pattern unless this has already been done by the + // compiler-specific code. + // + if (r.pattern.empty ()) + { + // When cross-compiling the whole toolchain is normally prefixed with + // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}. + // + // BTW, for GCC we also get gcc-{ar,ranlib} which add support for the + // LTO plugin though it seems more recent GNU binutils (2.25) are able + // to load the plugin when needed automatically. So it doesn't seem we + // should bother trying to support this on our end (one way we could + // do it is by passing config.bin.{ar,ranlib} as hints). + // + const string& t (r.target); + size_t n (t.size ()); + + if (xc.size () > n + 1) + { + const string& l (xc.leaf ().string ()); + + if (l.size () > n + 1 && l.compare (0, n, t) == 0 && l[n] == '-') + { + path p (xc.directory ()); + p /= t; + p += "-*"; + r.pattern = move (p).string (); + } + } } + + return r; } } } diff --git a/build2/cc/init b/build2/cc/init new file mode 100644 index 0000000..d8ebd0e --- /dev/null +++ b/build2/cc/init @@ -0,0 +1,55 @@ +// file : build2/cc/init -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_INIT +#define BUILD2_CC_INIT + +#include <build2/types> +#include <build2/utility> + +#include <build2/module> + +namespace build2 +{ + namespace cc + { + bool + vars_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool + config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool + core_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool + init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + } +} + +#endif // BUILD2_CC_INIT diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx new file mode 100644 index 0000000..2623c79 --- /dev/null +++ b/build2/cc/init.cxx @@ -0,0 +1,321 @@ +// file : build2/cc/init.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <build2/cc/init> + +#include <butl/triplet> + +#include <build2/scope> +#include <build2/context> +#include <build2/diagnostics> + +#include <build2/config/utility> + +#include <build2/cc/target> + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace cc + { + bool + vars_init (scope& r, + scope&, + const location&, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map&) + { + tracer trace ("cc::vars_init"); + l5 ([&]{trace << "for " << r.out_path ();}); + + assert (first); + + // Enter variables. Note: some overridable, some not. + // + auto& v (var_pool); + + v.insert<strings> ("config.cc.poptions", true); + v.insert<strings> ("config.cc.coptions", true); + v.insert<strings> ("config.cc.loptions", true); + v.insert<strings> ("config.cc.libs", true); + + v.insert<strings> ("cc.poptions"); + v.insert<strings> ("cc.coptions"); + v.insert<strings> ("cc.loptions"); + v.insert<strings> ("cc.libs"); + + v.insert<strings> ("cc.export.poptions"); + v.insert<strings> ("cc.export.coptions"); + v.insert<strings> ("cc.export.loptions"); + v.insert<strings> ("cc.export.libs"); + + // Hint variables (not overridable). + // + v.insert<string> ("config.cc.id"); + v.insert<string> ("config.cc.target"); + v.insert<string> ("config.cc.pattern"); + + return true; + } + + bool + config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("cc::config_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Load cc.vars. + // + if (first) + { + if (!cast_false<bool> (b["cc.vars.loaded"])) + load_module ("cc.vars", r, b, loc); + } + + // Configure. + // + if (first) + { + // config.cc.id + // + { + // This value must be hinted. + // + r.assign<string> ("cc.id") = cast<string> (hints["config.cc.id"]); + } + + // config.cc.target + // + { + // This value must be hinted and already canonicalized. + // + const string& s (cast<string> (hints["config.cc.target"])); + + try + { + //@@ We do it in the hinting module and here. Any way not to + // duplicate the effort? Maybe move the splitting here and + // simply duplicate the values there? + // + triplet t (s); + + // Enter as cc.target.{cpu,vendor,system,version,class}. + // + r.assign<string> ("cc.target") = s; + r.assign<string> ("cc.target.cpu") = move (t.cpu); + r.assign<string> ("cc.target.vendor") = move (t.vendor); + r.assign<string> ("cc.target.system") = move (t.system); + r.assign<string> ("cc.target.version") = move (t.version); + r.assign<string> ("cc.target.class") = move (t.class_); + } + catch (const invalid_argument& e) + { + assert (false); // Should have been caught by the hinting module. + } + } + + // config.cc.pattern + // + { + // This value could be hinted. + // + if (auto l = hints["config.cc.pattern"]) + r.assign<string> ("cc.pattern") = cast<string> (l); + } + + // Note that we are not having a config report since it will just + // duplicate what has already been printed by the hinting module. + } + + // config.cc.{p,c,l}options + // config.cc.libs + // + // @@ Same nonsense as in module. + // + // + b.assign ("cc.poptions") += cast_null<strings> ( + config::optional (r, "config.cc.poptions")); + + b.assign ("cc.coptions") += cast_null<strings> ( + config::optional (r, "config.cc.coptions")); + + b.assign ("cc.loptions") += cast_null<strings> ( + config::optional (r, "config.cc.loptions")); + + b.assign ("cc.libs") += cast_null<strings> ( + config::optional (r, "config.cc.libs")); + + // Load the bin.config module. + // + if (!cast_false<bool> (b["bin.config.loaded"])) + { + // Prepare configuration hints. They are only used on the first load + // of bin.config so we only populate them on our first load. + // + variable_map h; + if (first) + { + h.assign ("config.bin.target") = cast<string> (r["cc.target"]); + if (auto l = r["cc.pattern"]) + h.assign ("config.bin.pattern") = cast<string> (l); + } + + load_module ("bin.config", r, b, loc, false, h); + } + + // Verify bin's target matches ours (we do it even if we loaded it + // ourselves since the target can come from the configuration and not + // our hint). + // + if (first) + { + const string& ct (cast<string> (r["cc.target"])); + const string& bt (cast<string> (r["bin.target"])); + + if (bt != ct) + fail (loc) << "cc and bin module target mismatch" << + info << "cc.target is " << ct << + info << "bin.target is " << bt; + } + + const string& cid (cast<string> (r["cc.id"])); + const string& tsys (cast<string> (r["cc.target.system"])); + + // Load bin.*.config for bin.* modules we may need (see core_init() + // below). + // + if (auto l = r["config.bin.lib"]) + { + if (cast<string> (l) != "shared") + { + if (!cast_false<bool> (b["bin.ar.config.loaded"])) + load_module ("bin.ar.config", r, b, loc); + } + } + + if (cid == "msvc") + { + if (!cast_false<bool> (b["bin.ld.config.loaded"])) + load_module ("bin.ld.config", r, b, loc); + } + + if (tsys == "mingw32") + { + if (!cast_false<bool> (b["bin.rc.config.loaded"])) + load_module ("bin.rc.config", r, b, loc); + } + + return true; + } + + bool + core_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("cc::core_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Load cc.config. + // + if (!cast_false<bool> (b["cc.config.loaded"])) + load_module ("cc.config", r, b, loc, false, hints); + + // Load the bin module. + // + if (!cast_false<bool> (b["bin.loaded"])) + load_module ("bin", r, b, loc); + + const string& cid (cast<string> (r["cc.id"])); + const string& tsys (cast<string> (r["cc.target.system"])); + + // Load the bin.ar module unless we were asked to only build shared + // libraries. + // + if (auto l = r["config.bin.lib"]) + { + if (cast<string> (l) != "shared") + { + if (!cast_false<bool> (b["bin.ar.loaded"])) + load_module ("bin.ar", r, b, loc); + } + } + + // In the VC world you link things directly with link.exe so load the + // bin.ld module. + // + if (cid == "msvc") + { + if (!cast_false<bool> (b["bin.ld.loaded"])) + load_module ("bin.ld", r, b, loc); + } + + // If our target is MinGW, then we will need the resource compiler + // (windres) in order to embed manifests into executables. + // + if (tsys == "mingw32") + { + if (!cast_false<bool> (b["bin.rc.loaded"])) + load_module ("bin.rc", r, b, loc); + } + + return true; + } + + bool + init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&) + { + tracer trace ("cc::init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // This module is an "alias" for c.config and cxx.config. Its intended + // use is to make sure that the C/C++ configuration is captured in an + // amalgamation rather than subprojects. + // + // We want to order the loading to match what user specified on the + // command line (config.c or config.cxx). This way the first loaded + // module (with user-specified config.*) will hint the compiler to the + // second. + // + bool lc (!cast_false<bool> (b["c.config.loaded"])); + bool lp (!cast_false<bool> (b["cxx.config.loaded"])); + + // If none of them are already loaded, load c first only if config.c + // is specified. + // + if (lc && lp && r["config.c"]) + { + load_module ("c.config", r, b, loc); + load_module ("cxx.config", r, b, loc); + } + else + { + if (lp) load_module ("cxx.config", r, b, loc); + if (lc) load_module ("c.config", r, b, loc); + } + + return true; + } + } +} diff --git a/build2/cxx/install b/build2/cc/install index 119ef94..e2be905 100644 --- a/build2/cxx/install +++ b/build2/cc/install @@ -1,31 +1,39 @@ -// file : build2/cxx/install -*- C++ -*- +// file : build2/cc/install -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef BUILD2_CXX_INSTALL -#define BUILD2_CXX_INSTALL +#ifndef BUILD2_CC_INSTALL +#define BUILD2_CC_INSTALL #include <build2/types> #include <build2/utility> #include <build2/install/rule> +#include <build2/cc/types> +#include <build2/cc/common> + namespace build2 { - namespace cxx + namespace cc { - class install: public build2::install::file_rule + class link; + + class install: public build2::install::file_rule, virtual common { public: + install (data&&, const link&); + virtual target* filter (action, target&, prerequisite_member) const; virtual match_result match (action, target&, const string&) const; - static install instance; + private: + const link& link_; }; } } -#endif // BUILD2_CXX_INSTALL +#endif // BUILD2_CC_INSTALL diff --git a/build2/cxx/install.cxx b/build2/cc/install.cxx index e07d115..b674886 100644 --- a/build2/cxx/install.cxx +++ b/build2/cc/install.cxx @@ -1,23 +1,25 @@ -// file : build2/cxx/install.cxx -*- C++ -*- +// file : build2/cc/install.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <build2/cxx/install> +#include <build2/cc/install> #include <build2/bin/target> -#include <build2/cxx/link> -#include <build2/cxx/common> -#include <build2/cxx/target> +#include <build2/cc/link> // match() +#include <build2/cc/utility> using namespace std; namespace build2 { - namespace cxx + namespace cc { using namespace bin; + install:: + install (data&& d, const link& l): common (move (d)), link_ (l) {} + target* install:: filter (action a, target& t, prerequisite_member p) const { @@ -25,7 +27,7 @@ namespace build2 { // Don't install executable's prerequisite headers. // - if (p.is_a<hxx> () || p.is_a<ixx> () || p.is_a<txx> () || p.is_a<h> ()) + if (x_header (p)) return nullptr; } @@ -61,10 +63,8 @@ namespace build2 // We only want to handle installation if we are also the // ones building this target. So first run link's match(). // - match_result r (link::instance.match (a, t, hint)); + match_result r (link_.match (a, t, hint)); return r ? install::file_rule::match (a, t, "") : r; } - - install install::instance; } } diff --git a/build2/cc/link b/build2/cc/link new file mode 100644 index 0000000..8be386f --- /dev/null +++ b/build2/cc/link @@ -0,0 +1,78 @@ +// file : build2/cc/link -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_LINK +#define BUILD2_CC_LINK + +#include <build2/types> +#include <build2/utility> + +#include <build2/rule> + +#include <build2/bin/target> + +#include <build2/cc/types> +#include <build2/cc/common> + +namespace build2 +{ + namespace cc + { + class link: public rule, virtual common + { + public: + link (data&&); + + virtual match_result + match (action, target&, const string& hint) const; + + virtual recipe + apply (action, target&, const match_result&) const; + + target_state + perform_update (action, target&) const; + + target_state + perform_clean (action, target&) const; + + private: + friend class compile; + + // Extract system library search paths from GCC or compatible (Clang, + // Intel) using the -print-search-dirs option. + // + void + gcc_library_search_paths (scope&, dir_paths&) const; + + // Extract system library search paths from VC (msvc.cxx). + // + void + msvc_library_search_paths (scope&, dir_paths&) const; + + dir_paths + extract_library_paths (scope&) const; + + // Alternative search logic for VC (msvc.cxx). + // + bin::liba* + msvc_search_static (const path&, const dir_path&, prerequisite&) const; + + bin::libs* + msvc_search_shared (const path&, const dir_path&, prerequisite&) const; + + target* + search_library (optional<dir_paths>&, prerequisite&) const; + + // Windows-specific (windows-manifest.cxx). + // + path + windows_manifest (file&, bool rpath_assembly) const; + + private: + const string rule_id; + }; + } +} + +#endif // BUILD2_CC_LINK diff --git a/build2/cxx/link.cxx b/build2/cc/link.cxx index d19d6b1..4bebc6f 100644 --- a/build2/cxx/link.cxx +++ b/build2/cc/link.cxx @@ -1,8 +1,8 @@ -// file : build2/cxx/link.cxx -*- C++ -*- +// file : build2/cc/link.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <build2/cxx/link> +#include <build2/cc/link> #include <cstdlib> // exit() #include <iostream> // cerr @@ -18,35 +18,43 @@ #include <build2/diagnostics> #include <build2/bin/target> -#include <build2/cxx/target> -#include <build2/cxx/common> -#include <build2/cxx/utility> +#include <build2/cc/target> // c +#include <build2/cc/utility> using namespace std; using namespace butl; namespace build2 { - namespace cxx + namespace cc { using namespace bin; + link:: + link (data&& d) + : common (move (d)), + rule_id (string (x) += ".link 1") + { + } + // Extract system library search paths from GCC or compatible (Clang, - // Intel C++) using the -print-search-dirs option. + // Intel) using the -print-search-dirs option. // - static void - gcc_library_search_paths (scope& bs, const string& cid, dir_paths& r) + void link:: + gcc_library_search_paths (scope& bs, dir_paths& r) const { scope& rs (*bs.root_scope ()); cstrings args; - string std_storage; - - args.push_back (cast<path> (rs["config.cxx"]).string ().c_str ()); - append_options (args, bs, "cxx.coptions"); - append_std (args, rs, cid, bs, std_storage); - append_options (args, bs, "cxx.loptions"); + string std; // Storage. + + args.push_back (cast<path> (rs[config_x]).string ().c_str ()); + append_options (args, bs, c_coptions); + append_options (args, bs, x_coptions); + append_std (args, rs, bs, std); + append_options (args, bs, c_loptions); + append_options (args, bs, x_loptions); args.push_back ("-print-search-dirs"); args.push_back (nullptr); @@ -80,7 +88,8 @@ namespace build2 catch (const ifdstream::failure&) { pr.wait (); - fail << "error reading C++ compiler -print-search-dirs output"; + fail << "error reading " << x_lang << " compiler -print-search-dirs " + << "output"; } } catch (const process_error& e) @@ -94,7 +103,8 @@ namespace build2 } if (l.empty ()) - fail << "unable to extract C++ compiler system library paths"; + fail << "unable to extract " << x_lang << " compiler system library " + << "search paths"; // Now the fun part: figuring out which delimiter is used. Normally it // is ':' but on Windows it is ';' (or can be; who knows for sure). Also @@ -127,23 +137,16 @@ namespace build2 } } - // Extract system library search paths from MSVC. - // - void - msvc_library_search_paths (scope&, const string&, dir_paths&); // msvc.cxx - dir_paths link:: - extract_library_paths (scope& bs) + extract_library_paths (scope& bs) const { dir_paths r; - scope& rs (*bs.root_scope ()); - const string& cid (cast<string> (rs["cxx.id"])); // Extract user-supplied search paths (i.e., -L, /LIBPATH). // - if (auto l = bs["cxx.loptions"]) + auto extract = [&r, this] (const value& val) { - const auto& v (cast<strings> (l)); + const auto& v (cast<strings> (val)); for (auto i (v.begin ()), e (v.end ()); i != e; ++i) { @@ -184,28 +187,23 @@ namespace build2 if (!d.relative ()) r.push_back (move (d)); } - } + }; + + if (auto l = bs[c_loptions]) extract (*l); + if (auto l = bs[x_loptions]) extract (*l); if (cid == "msvc") - msvc_library_search_paths (bs, cid, r); + msvc_library_search_paths (bs, r); else - gcc_library_search_paths (bs, cid, r); + gcc_library_search_paths (bs, r); return r; } - // Alternative search for VC (msvc.cxx). - // - liba* - msvc_search_static (const path& ld, const dir_path&, prerequisite&); - - libs* - msvc_search_shared (const path& ld, const dir_path&, prerequisite&); - target* link:: - search_library (optional<dir_paths>& spc, prerequisite& p) + search_library (optional<dir_paths>& spc, prerequisite& p) const { - tracer trace ("cxx::link::search_library"); + tracer trace (x, "link::search_library"); // @@ This is hairy enough to warrant a separate implementation for // Windows. @@ -216,11 +214,6 @@ namespace build2 if (p.target != nullptr) return p.target; - scope& rs (*p.scope.root_scope ()); - const string& cid (cast<string> (rs["cxx.id"])); - const string& tsys (cast<string> (rs["cxx.target.system"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - bool l (p.is_a<lib> ()); const string* ext (l ? nullptr : p.ext); // Only for liba/libs. @@ -426,6 +419,7 @@ namespace build2 // if (cid == "msvc") { + scope& rs (*p.scope.root_scope ()); const path& ld (cast<path> (rs["config.bin.ld"])); if (s == nullptr && !sn.empty ()) @@ -449,40 +443,42 @@ namespace build2 // handle DLL export). The absence of either of these macros would mean // some other build system that cannot distinguish between the two. // - auto add_macro = [] (target& t, const char* suffix) + auto add_macro = [this] (target& t, const char* suffix) { - // If there is already a value, don't add anything, we don't want to - // be accumulating defines nor messing with custom values. + // If there is already a value (either in cc.export or x.export), + // don't add anything: we don't want to be accumulating defines nor + // messing with custom values. And if we are adding, then use the + // generic cc.export. // - auto p (t.vars.insert ("cxx.export.poptions")); - - if (p.second) + if (!t.vars[x_export_poptions]) { - // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED}, - // where <name> is the target name. Here we want to strike a balance - // between being unique and not too noisy. - // - string d ("-DLIB"); + auto p (t.vars.insert (c_export_poptions)); - auto upcase_sanitize = [] (char c) -> char + if (p.second) { - if (c == '-' || c == '+' || c == '.') - return '_'; - else - return ucase (c); - }; + // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED}, + // where <name> is the target name. Here we want to strike a + // balance between being unique and not too noisy. + // + string d ("-DLIB"); - transform (t.name.begin (), - t.name.end (), - back_inserter (d), - upcase_sanitize); + auto upcase_sanitize = [] (char c) + { + return (c == '-' || c == '+' || c == '.') ? '_' : ucase (c); + }; + + transform (t.name.begin (), + t.name.end (), + back_inserter (d), + upcase_sanitize); - d += '_'; - d += suffix; + d += '_'; + d += suffix; - strings o; - o.push_back (move (d)); - p.first.get () = move (o); + strings o; + o.push_back (move (d)); + p.first.get () = move (o); + } } }; @@ -521,7 +517,7 @@ namespace build2 match_result link:: match (action a, target& t, const string& hint) const { - tracer trace ("cxx::link::match"); + tracer trace (x, "link::match"); // @@ TODO: // @@ -537,16 +533,16 @@ namespace build2 otype lt (link_type (t)); - // Scan prerequisites and see if we can work with what we've got. + // Scan prerequisites and see if we can work with what we've got. Note + // that X could be C. We handle this by always checking for X first. // - bool seen_cxx (false), seen_c (false), seen_obj (false), - seen_lib (false); + bool seen_x (false), seen_c (false), seen_obj (false), seen_lib (false); for (prerequisite_member p: group_prerequisite_members (a, t)) { - if (p.is_a<cxx> ()) + if (p.is_a (x_src)) { - seen_cxx = seen_cxx || true; + seen_x = seen_x || true; } else if (p.is_a<c> ()) { @@ -585,12 +581,12 @@ namespace build2 } } - // We will only chain a C source if there is also a C++ source or we - // were explicitly told to. + // We will only chain a C source if there is also an X source or we were + // explicitly told to. // - if (seen_c && !seen_cxx && hint < "cxx") + if (seen_c && !seen_x && hint < x) { - l4 ([&]{trace << "C prerequisite(s) without C++ or hint";}); + l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint";}); return nullptr; } @@ -635,23 +631,19 @@ namespace build2 } } - return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr; + return seen_x || seen_c || seen_obj || seen_lib ? &t : nullptr; } recipe link:: apply (action a, target& xt, const match_result&) const { - tracer trace ("cxx::link::apply"); + tracer trace (x, "link::apply"); file& t (static_cast<file&> (xt)); scope& bs (t.base_scope ()); scope& rs (*bs.root_scope ()); - const string& cid (cast<string> (rs["cxx.id"])); - const string& tsys (cast<string> (rs["cxx.target.system"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - otype lt (link_type (t)); lorder lo (link_order (bs, lt)); @@ -767,7 +759,8 @@ namespace build2 // if (lt != otype::a && cid == "msvc" && - find_option ("/DEBUG", t, "cxx.loptions", true)) + (find_option ("/DEBUG", t, c_loptions, true) || + find_option ("/DEBUG", t, x_loptions, true))) { // Add after the import library if any. // @@ -789,11 +782,11 @@ namespace build2 optional<dir_paths> lib_paths; // Extract lazily. - // Process prerequisites: do rule chaining for C and C++ source - // files as well as search and match. + // Process prerequisites: do rule chaining for C and X source files as + // well as search and match. // - // When cleaning, ignore prerequisites that are not in the same - // or a subdirectory of our project root. + // When cleaning, ignore prerequisites that are not in the same or a + // subdirectory of our project root. // const target_type& ott (lt == otype::e ? obje::static_type : lt == otype::a ? obja::static_type : @@ -803,7 +796,7 @@ namespace build2 { target* pt (nullptr); - if (!p.is_a<c> () && !p.is_a<cxx> ()) + if (!p.is_a (x_src) && !p.is_a<c> ()) { // Handle imported libraries. // @@ -843,6 +836,9 @@ namespace build2 continue; } + // The rest is rule chaining. + // + // Which scope shall we use to resolve the root? Unlikely, but // possible, the prerequisite is from a different project // altogether. So we are going to use the target's project. @@ -853,15 +849,15 @@ namespace build2 // bool group (!p.prerequisite.belongs (t)); // Group's prerequisite. - const prerequisite_key& cp (p.key ()); // c(xx){} prerequisite key. + const prerequisite_key& cp (p.key ()); // C-source (X or C) key. const target_type& tt (group ? obj::static_type : ott); - // Come up with the obj*{} target. The c(xx){} prerequisite directory + // Come up with the obj*{} target. The source prerequisite directory // can be relative (to the scope) or absolute. If it is relative, then // use it as is. If absolute, then translate it to the corresponding - // directory under out_root. While the c(xx){} directory is most - // likely under src_root, it is also possible it is under out_root - // (e.g., generated source). + // directory under out_root. While the source directory is most likely + // under src_root, it is also possible it is under out_root (e.g., + // generated source). // dir_path d; { @@ -890,16 +886,16 @@ namespace build2 // if (a.operation () == clean_id && !ot.dir.sub (rs.out_path ())) { - // If we shouldn't clean obj{}, then it is fair to assume - // we shouldn't clean cxx{} either (generated source will - // be in the same directory as obj{} and if not, well, go - // find yourself another build system ;-)). + // If we shouldn't clean obj{}, then it is fair to assume we + // shouldn't clean the source either (generated source will be in + // the same directory as obj{} and if not, well, go find yourself + // another build system ;-)). // continue; // Skip. } - // If we have created the obj{} target group, pick one of its - // members; the rest would be primarily concerned with it. + // If we have created the obj{} target group, pick one of its members; + // the rest would be primarily concerned with it. // if (group) { @@ -933,43 +929,45 @@ namespace build2 for (prerequisite_member p1: reverse_group_prerequisite_members (a, *pt)) { - // Ignore some known target types (fsdir, headers, libraries). + // Most of the time we will have just a single source so fast-path + // that case. // - if (p1.is_a<fsdir> () || - p1.is_a<h> () || - (p.is_a<cxx> () && (p1.is_a<hxx> () || - p1.is_a<ixx> () || - p1.is_a<txx> ())) || - p1.is_a<lib> () || - p1.is_a<liba> () || - p1.is_a<libs> ()) + if (p1.is_a (x_src)) { - continue; - } - - if (!p1.is_a<cxx> ()) - fail << "synthesized target for prerequisite " << cp - << " would be incompatible with existing target " << *pt << - info << "unexpected existing prerequisite type " << p1 << - info << "specify corresponding obj{} target explicitly"; + if (!found) + { + build2::match (a, *pt); // Now p1 should be resolved. - if (!found) - { - build2::match (a, *pt); // Now p1 should be resolved. + // Searching our own prerequisite is ok. + // + if (&p.search () != &p1.search ()) + fail << "synthesized target for prerequisite " << cp << " " + << "would be incompatible with existing target " << *pt << + info << "existing prerequisite " << p1 << " does not match " + << cp << + info << "specify corresponding " << tt.name << "{} target " + << "explicitly"; + + found = true; + } - // Searching our own prerequisite is ok. - // - if (&p.search () != &p1.search ()) - fail << "synthesized target for prerequisite " << cp << " would " - << "be incompatible with existing target " << *pt << - info << "existing prerequisite " << p1 << " does not match " - << cp << - info << "specify corresponding " << tt.name << "{} target " - << "explicitly"; - - found = true; - // Check the rest of the prerequisites. + continue; // Check the rest of the prerequisites. } + + // Ignore some known target types (fsdir, headers, libraries). + // + if (p1.is_a<fsdir> () || + p1.is_a<lib> () || + p1.is_a<liba> () || + p1.is_a<libs> () || + (p.is_a (x_src) && x_header (p1)) || + (p.is_a<c> () && p1.is_a<h> ())) + continue; + + fail << "synthesized target for prerequisite " << cp + << " would be incompatible with existing target " << *pt << + info << "unexpected existing prerequisite type " << p1 << + info << "specify corresponding obj{} target explicitly"; } if (!found) @@ -978,12 +976,12 @@ namespace build2 // ot.prerequisites.emplace_back (p.as_prerequisite (trace)); - // Add our lib*{} prerequisites to the object file (see - // cxx.export.poptions above for details). + // Add our lib*{} prerequisites to the object file (see the export.* + // machinery for details). // // Note that we don't resolve lib{} to liba{}/libs{} here instead // leaving it to whoever (e.g., the compile rule) will be needing - // cxx.export.*. One reason for doing it there is that the object + // *.export.*. One reason for doing it there is that the object // target might be specified explicitly by the user in which case // they will have to specify the set of lib{} prerequisites and it's // much cleaner to do as lib{} rather than liba{}/libs{}. @@ -1007,9 +1005,12 @@ namespace build2 switch (a) { - case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean; - default: return noop_recipe; // Configure update. + case perform_update_id: + return [this] (action a, target& t) {return perform_update (a, t);}; + case perform_clean_id: + return [this] (action a, target& t) {return perform_clean (a, t);}; + default: + return noop_recipe; // Configure update. } } @@ -1059,47 +1060,38 @@ namespace build2 } } - // See windows-manifest.cxx. - // - path - windows_manifest (file&, bool rpath_assembly); - // See windows-rpath.cxx. // timestamp windows_rpath_timestamp (file&); void - windows_rpath_assembly (file&, timestamp, bool scratch); - - const char* - msvc_machine (const string& cpu); // msvc.cxx + windows_rpath_assembly (file&, const string& cpu, timestamp, bool scratch); // Filter link.exe noise (msvc.cxx). // void msvc_filter_link (ifdstream&, const file&, otype); + // Translate target CPU to /MACHINE option. + // + const char* + msvc_machine (const string& cpu); // msvc.cxx + target_state link:: - perform_update (action a, target& xt) + perform_update (action a, target& xt) const { - tracer trace ("cxx::link::perform_update"); + tracer trace (x, "link::perform_update"); file& t (static_cast<file&> (xt)); + scope& rs (t.root_scope ()); otype lt (link_type (t)); // Update prerequisites. // bool update (execute_prerequisites (a, t, t.mtime ())); - scope& rs (t.root_scope ()); - - const string& cid (cast<string> (rs["cxx.id"])); - const string& tgt (cast<string> (rs["cxx.target"])); - const string& tsys (cast<string> (rs["cxx.target.system"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - // If targeting Windows, take care of the manifest. // path manifest; // Manifest itself (msvc) or compiled object file. @@ -1213,12 +1205,12 @@ namespace build2 // First should come the rule name/version. // - if (dd.expect ("cxx.link 1") != nullptr) + if (dd.expect (rule_id) != nullptr) l4 ([&]{trace << "rule mismatch forcing update of " << t;}); lookup ranlib; - // Then the linker checksum (ar/ranlib or C++ compiler). + // Then the linker checksum (ar/ranlib or the compiler). // if (lt == otype::a) { @@ -1244,16 +1236,16 @@ namespace build2 // const string& cs ( cast<string> ( - rs[cid == "msvc" ? "bin.ld.checksum" : "cxx.checksum"])); + rs[cid == "msvc" ? var_pool["bin.ld.checksum"] : x_checksum])); if (dd.expect (cs) != nullptr) l4 ([&]{trace << "linker mismatch forcing update of " << t;}); } // Next check the target. While it might be incorporated into the linker - // checksum, it also might not (e.g., MS link.exe). + // checksum, it also might not (e.g., VC link.exe). // - if (dd.expect (tgt) != nullptr) + if (dd.expect (ctg) != nullptr) l4 ([&]{trace << "target mismatch forcing update of " << t;}); // Start building the command line. While we don't yet know whether we @@ -1264,7 +1256,7 @@ namespace build2 // second is simpler. Let's got with the simpler for now (actually it's // kind of a hybrid). // - cstrings args {nullptr}; // Reserve one for config.bin.ar/config.cxx. + cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x. // Storage. // @@ -1277,8 +1269,8 @@ namespace build2 if (cid == "msvc") ; else { - // If the user asked for ranlib, don't try to do its function with -s. - // Some ar implementations (e.g., the LLVM one) doesn't support + // If the user asked for ranlib, don't try to do its function with + // -s. Some ar implementations (e.g., the LLVM one) don't support // leading '-'. // args.push_back (ranlib ? "rc" : "rcs"); @@ -1288,16 +1280,18 @@ namespace build2 { if (cid == "msvc") { - // We are using link.exe directly so we don't pass the C++ compiler + // We are using link.exe directly so don't pass the compiler // options. } else { - append_options (args, t, "cxx.coptions"); - append_std (args, rs, cid, t, std); + append_options (args, t, c_coptions); + append_options (args, t, x_coptions); + append_std (args, rs, t, std); } - append_options (args, t, "cxx.loptions"); + append_options (args, t, c_loptions); + append_options (args, t, x_loptions); // Handle soname/rpath. // @@ -1309,7 +1303,7 @@ namespace build2 auto l (t["bin.rpath"]); if (l && !l->empty ()) - fail << tgt << " does not support rpath"; + fail << ctg << " does not support rpath"; } else { @@ -1443,7 +1437,10 @@ namespace build2 // Treat them as inputs, not options. // if (lt != otype::a) - hash_options (cs, t, "cxx.libs"); + { + hash_options (cs, t, c_libs); + hash_options (cs, t, x_libs); + } if (dd.expect (cs.string ()) != nullptr) l4 ([&]{trace << "file set mismatch forcing update of " << t;}); @@ -1451,8 +1448,8 @@ namespace build2 // If any of the above checks resulted in a mismatch (different linker, // options or input file set), or if the database is newer than the - // target (interrupted update) then force the target update. Also - // note this situation in the "from scratch" flag. + // target (interrupted update) then force the target update. Also note + // this situation in the "from scratch" flag. // bool scratch (false); if (dd.writing () || dd.mtime () > t.mtime ()) @@ -1484,14 +1481,13 @@ namespace build2 { // lib.exe has /LIBPATH but it's not clear/documented what it's // used for. Perhaps for link-time code generation (/LTCG)? If - // that's the case, then we may need to pass cxx.loptions. + // that's the case, then we may need to pass *.loptions. // args.push_back ("/NOLOGO"); // Add /MACHINE. // - args.push_back ( - msvc_machine (cast<string> (rs["cxx.target.cpu"]))); + args.push_back (msvc_machine (cast<string> (rs[x_target_cpu]))); out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); @@ -1518,8 +1514,7 @@ namespace build2 // Add /MACHINE. // - args.push_back ( - msvc_machine (cast<string> (rs["cxx.target.cpu"]))); + args.push_back (msvc_machine (cast<string> (rs[x_target_cpu]))); // Unless explicitly enabled with /INCREMENTAL, disable // incremental linking (it is implicitly enabled if /DEBUG is @@ -1593,14 +1588,17 @@ namespace build2 // @@ An executable can have an import library and VS seems to // always name it. I wonder what would trigger its generation? - // Could it be the presence of export symbols? - + // Could it be the presence of export symbols? Yes, link.exe + // will generate the import library iff there are exported + // symbols. Which means there could be a DLL without an import + // library (which we currently don't handle very well). + // out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); } else { - args[0] = cast<path> (rs["config.cxx"]).string ().c_str (); + args[0] = cast<path> (rs[config_x]).string ().c_str (); // Add the option that triggers building a shared library and take // care of any extras (e.g., import library). @@ -1676,7 +1674,10 @@ namespace build2 args.push_back (sargs[i].c_str ()); if (lt != otype::a) - append_options (args, t, "cxx.libs"); + { + append_options (args, t, c_libs); + append_options (args, t, x_libs); + } args.push_back (nullptr); @@ -1777,7 +1778,10 @@ namespace build2 if (lt == otype::e && tclass == "windows") { if (a.outer_operation () != install_id) - windows_rpath_assembly (t, rpath_timestamp, scratch); + windows_rpath_assembly (t, + cast<string> (rs[x_target_cpu]), + rpath_timestamp, + scratch); } rm.cancel (); @@ -1791,14 +1795,10 @@ namespace build2 } target_state link:: - perform_clean (action a, target& xt) + perform_clean (action a, target& xt) const { file& t (static_cast<file&> (xt)); - scope& rs (t.root_scope ()); - const string& tsys (cast<string> (rs["cxx.target.system"])); - const string& tclass (cast<string> (rs["cxx.target.class"])); - initializer_list<const char*> e; switch (link_type (t)) @@ -1846,7 +1846,5 @@ namespace build2 return clean_extra (a, t, e); } - - link link::instance; } } diff --git a/build2/cc/module b/build2/cc/module new file mode 100644 index 0000000..bed7673 --- /dev/null +++ b/build2/cc/module @@ -0,0 +1,59 @@ +// file : build2/cc/module -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_MODULE +#define BUILD2_CC_MODULE + +#include <build2/types> +#include <build2/utility> + +#include <build2/module> +#include <build2/variable> + +#include <build2/cc/common> + +#include <build2/cc/compile> +#include <build2/cc/link> +#include <build2/cc/install> + +namespace build2 +{ + namespace cc + { + class config_module: public module_base, public virtual config_data + { + public: + explicit + config_module (config_data&& d) : config_data (move (d)) {} + + void + init (scope&, + scope&, + const location&, + bool first, + const variable_map&); + }; + + class module: public module_base, protected virtual common, + link, compile, install + { + public: + explicit + module (data&& d) + : common (move (d)), + link (move (d)), + compile (move (d), *this), + install (move (d), *this) {} + + void + init (scope&, + scope&, + const location&, + bool first, + const variable_map&); + }; + } +} + +#endif // BUILD2_CC_MODULE diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx new file mode 100644 index 0000000..3a7dad2 --- /dev/null +++ b/build2/cc/module.cxx @@ -0,0 +1,291 @@ +// file : build2/cc/module.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <build2/cc/module> + +#include <iomanip> // left, setw() + +#include <butl/triplet> + +#include <build2/scope> +#include <build2/context> +#include <build2/diagnostics> + +#include <build2/bin/target> + +#include <build2/config/utility> +#include <build2/install/utility> + +#include <build2/cc/guess> + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace cc + { + void config_module:: + init (scope& r, + scope& b, + const location& loc, + bool first, + const variable_map&) + { + tracer trace (x, "config_init"); + + // Configure. + // + string pattern; // Toolchain pattern. + + if (first) + { + const variable& config_c_coptions (var_pool["config.cc.coptions"]); + + // config.x + // + auto p (config::required (r, config_x, path (x_default))); + + // Figure out which compiler we are dealing with, its target, etc. + // + const path& xc (cast<path> (p.first)); + compiler_info ci ( + guess (x_lang, + xc, + cast_null<strings> (r[config_c_coptions]), + cast_null<strings> (r[config_x_coptions]))); + + // 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)) + { + text << x << ' ' << project (r) << '@' << r.out_path () << '\n' + << " " << left << setw (11) << x << xc << '\n' + << " id " << ci.id << '\n' + << " version " << ci.version.string << '\n' + << " major " << ci.version.major << '\n' + << " minor " << ci.version.minor << '\n' + << " patch " << ci.version.patch << '\n' + << " build " << ci.version.build << '\n' + << " signature " << ci.signature << '\n' + << " checksum " << ci.checksum << '\n' + << " target " << ci.target; + } + + r.assign (x_id) = ci.id.string (); + r.assign (x_id_type) = move (ci.id.type); + r.assign (x_id_variant) = move (ci.id.variant); + + r.assign (x_version) = move (ci.version.string); + r.assign (x_version_major) = ci.version.major; + r.assign (x_version_minor) = ci.version.minor; + r.assign (x_version_patch) = ci.version.patch; + r.assign (x_version_build) = move (ci.version.build); + + r.assign (x_signature) = move (ci.signature); + r.assign (x_checksum) = move (ci.checksum); + + pattern = move (ci.pattern); + + // Split/canonicalize the target. First see if the user asked us to + // use config.sub. + // + if (ops.config_sub_specified ()) + { + ci.target = run<string> (ops.config_sub (), + ci.target.c_str (), + [] (string& l) {return move (l);}); + l5 ([&]{trace << "config.sub target: '" << ci.target << "'";}); + } + + try + { + string canon; + triplet t (ci.target, canon); + + l5 ([&]{trace << "canonical target: '" << canon << "'; " + << "class: " << t.class_;}); + + // Enter as x.target.{cpu,vendor,system,version,class}. + // + r.assign (x_target) = move (canon); + r.assign (x_target_cpu) = move (t.cpu); + r.assign (x_target_vendor) = move (t.vendor); + r.assign (x_target_system) = move (t.system); + r.assign (x_target_version) = move (t.version); + r.assign (x_target_class) = move (t.class_); + } + catch (const invalid_argument& e) + { + // This is where we suggest that the user specifies --config-sub to + // help us out. + // + fail << "unable to parse " << x_lang << "compiler target '" + << ci.target << "': " << e.what () << + info << "consider using the --config-sub option"; + } + } + + // config.x.{p,c,l}options + // config.x.libs + // + // These are optional. We also merge them into the corresponding + // x.* variables. + // + // The merging part gets a bit tricky if this module has already + // been loaded in one of the outer scopes. By doing the straight + // append we would just be repeating the same options over and + // over. So what we are going to do is only append to a value if + // it came from this scope. Then the usage for merging becomes: + // + // x.coptions = <overridable options> # Note: '='. + // using x + // x.coptions += <overriding options> # Note: '+='. + // + b.assign (x_poptions) += cast_null<strings> ( + config::optional (r, config_x_poptions)); + + b.assign (x_coptions) += cast_null<strings> ( + config::optional (r, config_x_coptions)); + + b.assign (x_loptions) += cast_null<strings> ( + config::optional (r, config_x_loptions)); + + b.assign (x_libs) += cast_null<strings> ( + config::optional (r, config_x_libs)); + + // Load cc.config. + // + if (!cast_false<bool> (b["cc.config.loaded"])) + { + // Prepare configuration hints. They are only used on the first load + // of cc.config so we only populate them on our first load. + // + variable_map h; + if (first) + { + h.assign ("config.cc.id") = cast<string> (r[x_id]); + h.assign ("config.cc.target") = cast<string> (r[x_target]); + if (!pattern.empty ()) + h.assign ("config.cc.pattern") = move (pattern); + } + + load_module ("cc.config", r, b, loc, false, h); + } + else if (first) + { + // If cc.config is already loaded, verify its configuration matched + // ours since it could have been loaded by another c-family module. + // + auto check = [&r, &loc, this](const char* cv, + const variable& xv, + const char* w) + { + const string& c (cast<string> (r[cv])); + const string& x (cast<string> (r[xv])); + + if (c != x) + fail (loc) << "cc and " << x << " module " << w << " mismatch" << + info << cv << " is " << c << + info << xv.name << " is " << x; + }; + + // Note that we don't require that patterns match. Presumably, if the + // toolchain id and target are the same, then where exactly the tools + // (e.g., ar) come from doesn't really matter. + // + check ("cc.id", x_id, "toolchain id"); + check ("cc.target", x_target, "target"); + } + } + + void module:: + init (scope& r, + scope& b, + const location& loc, + bool, + const variable_map&) + { + tracer trace (x, "init"); + + // Load cc.core. Besides other things, this will load bin (core) plus + // extra bin.* modules we may need. + // + if (!cast_false<bool> (b["cc.core.loaded"])) + load_module ("cc.core", r, b, loc); + + // Register target types and configure their "installability". + // + { + using namespace install; + + auto& t (b.target_types); + + t.insert (x_src); + + // Install headers into install.include. + // + for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) + { + t.insert (**ht); + install_path (**ht, b, dir_path ("include")); + } + } + + // Register rules. + // + { + using namespace bin; + + auto& r (b.rules); + + // We register for configure so that we detect unresolved imports + // during configuration rather that later, e.g., during update. + // + // @@ Should we check if install module was loaded (see bin)? + // + compile& cr (*this); + link& lr (*this); + install& ir (*this); + + r.insert<obje> (perform_update_id, x_compile, cr); + r.insert<obje> (perform_clean_id, x_compile, cr); + r.insert<obje> (configure_update_id, x_compile, cr); + + r.insert<exe> (perform_update_id, x_link, lr); + r.insert<exe> (perform_clean_id, x_link, lr); + r.insert<exe> (configure_update_id, x_link, lr); + + r.insert<exe> (perform_install_id, x_install, ir); + + // Only register static object/library rules if the bin.ar module is + // loaded (by us or by the user). + // + if (cast_false<bool> (b["bin.ar.loaded"])) + { + r.insert<obja> (perform_update_id, x_compile, cr); + r.insert<obja> (perform_clean_id, x_compile, cr); + r.insert<obja> (configure_update_id, x_compile, cr); + + r.insert<liba> (perform_update_id, x_link, lr); + r.insert<liba> (perform_clean_id, x_link, lr); + r.insert<liba> (configure_update_id, x_link, lr); + + r.insert<liba> (perform_install_id, x_install, ir); + } + + r.insert<objs> (perform_update_id, x_compile, cr); + r.insert<objs> (perform_clean_id, x_compile, cr); + r.insert<objs> (configure_update_id, x_compile, cr); + + r.insert<libs> (perform_update_id, x_link, lr); + r.insert<libs> (perform_clean_id, x_link, lr); + r.insert<libs> (configure_update_id, x_link, lr); + + r.insert<libs> (perform_install_id, x_install, ir); + } + } + } +} diff --git a/build2/cxx/msvc.cxx b/build2/cc/msvc.cxx index 9798046..84020d0 100644 --- a/build2/cxx/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -1,4 +1,4 @@ -// file : build2/cxx/msvc.cxx -*- C++ -*- +// file : build2/cc/msvc.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file @@ -11,14 +11,18 @@ #include <build2/filesystem> #include <build2/diagnostics> -#include <build2/cxx/common> +#include <build2/bin/target> + +#include <build2/cc/types> + +#include <build2/cc/link> using namespace std; using namespace butl; namespace build2 { - namespace cxx + namespace cc { using namespace bin; @@ -82,8 +86,8 @@ namespace build2 // Extract system library search paths from MSVC. // - void - msvc_library_search_paths (scope&, const string&, dir_paths&) + void link:: + msvc_library_search_paths (scope&, dir_paths&) const { // The linker doesn't seem to have any built-in paths and all of them // come from the LIB environment variable. @@ -212,16 +216,17 @@ namespace build2 template <typename T> static T* - search_library (const path& ld, - const dir_path& d, - prerequisite& p, - otype lt, - const char* pfx, - const char* sfx) + msvc_search_library (const char* mod, + const path& ld, + const dir_path& d, + prerequisite& p, + otype lt, + const char* pfx, + const char* sfx) { // Pretty similar logic to link::search_library(). // - tracer trace ("cxx::msvc_search_library"); + tracer trace (mod, "msvc_search_library"); // Assemble the file path. // @@ -269,14 +274,17 @@ namespace build2 return nullptr; } - liba* - msvc_search_static (const path& ld, const dir_path& d, prerequisite& p) + liba* link:: + msvc_search_static (const path& ld, + const dir_path& d, + prerequisite& p) const { liba* r (nullptr); - auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool + auto search = [&r, &ld, &d, &p, this] (const char* pf, const char* sf) + -> bool { - r = search_library<liba> (ld, d, p, otype::a, pf, sf); + r = msvc_search_library<liba> (x, ld, d, p, otype::a, pf, sf); return r != nullptr; }; @@ -293,17 +301,20 @@ namespace build2 search ("", "_static") ? r : nullptr; } - libs* - msvc_search_shared (const path& ld, const dir_path& d, prerequisite& p) + libs* link:: + msvc_search_shared (const path& ld, + const dir_path& d, + prerequisite& p) const { - tracer trace ("cxx::msvc_search_shared"); + tracer trace (x, "link::msvc_search_shared"); libs* r (nullptr); - auto search = [&r, &ld, &d, &p, &trace] ( + auto search = [&r, &ld, &d, &p, &trace, this] ( const char* pf, const char* sf) -> bool { - if (libi* i = search_library<libi> (ld, d, p, otype::s, pf, sf)) + if (libi* i = + msvc_search_library<libi> (x, ld, d, p, otype::s, pf, sf)) { r = &targets.insert<libs> (d, dir_path (), p.name, nullptr, trace); diff --git a/build2/cc/target b/build2/cc/target new file mode 100644 index 0000000..2d8125b --- /dev/null +++ b/build2/cc/target @@ -0,0 +1,48 @@ +// file : build2/cc/target -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_TARGET +#define BUILD2_CC_TARGET + +#include <build2/types> +#include <build2/utility> + +#include <build2/target> + +namespace build2 +{ + namespace cc + { + // There is hardly a c-family compilation without a C header inclusion. + // As a result, this target type is registered for any c-family module. + // + class h: public file + { + public: + using file::file; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + // This one we define in cc but the target type is only registered by the + // c module. This way we can implement rule chaining without jumping + // through too many hoops (like resolving target type dynamically) but + // also without relaxing things too much (i.e., the user still won't be + // able to refer to c{} without loading the c module). + // + class c: public file + { + public: + using file::file; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + } +} + +#endif // BUILD2_CC_TARGET diff --git a/build2/cc/target.cxx b/build2/cc/target.cxx new file mode 100644 index 0000000..7c2bb24 --- /dev/null +++ b/build2/cc/target.cxx @@ -0,0 +1,39 @@ +// file : build2/cc/target.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <build2/cc/target> + +using namespace std; + +namespace build2 +{ + namespace cc + { + extern const char ext_var[] = "extension"; // VC 19 rejects constexpr. + + extern const char h_ext_def[] = "h"; + const target_type h::static_type + { + "h", + &file::static_type, + &target_factory<h>, + &target_extension_var<ext_var, h_ext_def>, + nullptr, + &search_file, + false + }; + + extern const char c_ext_def[] = "c"; + const target_type c::static_type + { + "c", + &file::static_type, + &target_factory<c>, + &target_extension_var<ext_var, c_ext_def>, + nullptr, + &search_file, + false + }; + } +} diff --git a/build2/cc/types b/build2/cc/types new file mode 100644 index 0000000..9cacc60 --- /dev/null +++ b/build2/cc/types @@ -0,0 +1,32 @@ +// file : build2/cc/types -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_CC_TYPES +#define BUILD2_CC_TYPES + +#include <build2/types> +#include <build2/utility> + +namespace build2 +{ + namespace cc + { + // Compiler language. + // + enum class lang {c, cxx}; + + ostream& + operator<< (ostream&, lang); // utility.ixx + + // Compile/link output type (executable, static, or shared). + // + enum class otype {e, a, s}; + + // Library link order. + // + enum class lorder {a, s, a_s, s_a}; + } +} + +#endif // BUILD2_CC_TYPES diff --git a/build2/cxx/common b/build2/cc/utility index 77f1149..ae19d56 100644 --- a/build2/cxx/common +++ b/build2/cc/utility @@ -1,45 +1,34 @@ -// file : build2/cxx/common -*- C++ -*- +// file : build2/cc/utility -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef BUILD2_CXX_COMMON -#define BUILD2_CXX_COMMON +#ifndef BUILD2_CC_UTILITY +#define BUILD2_CC_UTILITY #include <build2/types> #include <build2/utility> +#include <build2/target> #include <build2/bin/target> +#include <build2/cc/types> + namespace build2 { - namespace cxx + struct variable; + + namespace cc { - // Compile/link output type (executable, static, or shared). + // Compile/link output type. // - enum class otype {e, a, s}; - - inline otype - compile_type (target& t) - { - return - t.is_a<bin::obje> () ? otype::e : - t.is_a<bin::obja> () ? otype::a : - otype::s; - } - - inline otype - link_type (target& t) - { - return - t.is_a<bin::exe> () ? otype::e : - t.is_a<bin::liba> () ? otype::a : - otype::s; - } + otype + compile_type (target&); + + otype + link_type (target&); // Library link order. // - enum class lorder {a, s, a_s, s_a}; - // The reason we pass scope and not the target is because this function is // called not only for exe/lib but also for obj as part of the library // meta-information protocol implementation. Normally the bin.*.lib values @@ -54,7 +43,22 @@ namespace build2 // target& link_member (bin::lib&, lorder); + + // Append or hash library options from a pair of *.export.* variables + // (first one is cc.export.*) recursively, prerequisite libraries first. + // + void + append_lib_options (cstrings&, target&, lorder, + const variable&, + const variable&); + + void + hash_lib_options (sha256&, target&, lorder, + const variable&, + const variable&); } } -#endif // BUILD2_CXX_COMMON +#include <build2/cc/utility.ixx> + +#endif // BUILD2_CC_UTILITY diff --git a/build2/cxx/common.cxx b/build2/cc/utility.cxx index ec724a5..773ba8f 100644 --- a/build2/cxx/common.cxx +++ b/build2/cc/utility.cxx @@ -1,25 +1,27 @@ -// file : build2/cxx/common.cxx -*- C++ -*- +// file : build2/cc/utility.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <build2/cxx/common> +#include <build2/cc/utility> #include <build2/variable> -#include <build2/algorithm> +#include <build2/algorithm> // search() + +#include <build2/bin/target> using namespace std; namespace build2 { - namespace cxx + namespace cc { using namespace bin; lorder link_order (scope& bs, otype ot) { - // Initialize to suppress 'may be used uninitialized' warning produced by - // MinGW GCC 5.4.0. + // Initialize to suppress 'may be used uninitialized' warning produced + // by MinGW GCC 5.4.0. // const char* var (nullptr); @@ -69,5 +71,45 @@ namespace build2 return *r; } + + void + append_lib_options (cstrings& args, target& l, lorder lo, + const variable& cv, + const variable& xv) + { + using namespace bin; + + for (target* t: l.prerequisite_targets) + { + if (lib* l = t->is_a<lib> ()) + t = &link_member (*l, lo); // Pick one of the members. + + if (t->is_a<liba> () || t->is_a<libs> ()) + append_lib_options (args, *t, lo, cv, xv); + } + + append_options (args, l, cv); + append_options (args, l, xv); + } + + void + hash_lib_options (sha256& csum, target& l, lorder lo, + const variable& cv, + const variable& xv) + { + using namespace bin; + + for (target* t: l.prerequisite_targets) + { + if (lib* l = t->is_a<lib> ()) + t = &link_member (*l, lo); // Pick one of the members. + + if (t->is_a<liba> () || t->is_a<libs> ()) + hash_lib_options (csum, *t, lo, cv, xv); + } + + hash_options (csum, l, cv); + hash_options (csum, l, xv); + } } } diff --git a/build2/cc/utility.ixx b/build2/cc/utility.ixx new file mode 100644 index 0000000..e7eb565 --- /dev/null +++ b/build2/cc/utility.ixx @@ -0,0 +1,33 @@ +// file : build2/cc/utility.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + namespace cc + { + inline ostream& + operator<< (ostream& os, lang l) + { + return os << (l == lang::c ? "C" : "C++"); + } + + inline otype + compile_type (target& t) + { + return + t.is_a<bin::obje> () ? otype::e : + t.is_a<bin::obja> () ? otype::a : + otype::s; + } + + inline otype + link_type (target& t) + { + return + t.is_a<bin::exe> () ? otype::e : + t.is_a<bin::liba> () ? otype::a : + otype::s; + } + } +} diff --git a/build2/cxx/windows-manifest.cxx b/build2/cc/windows-manifest.cxx index 915610d..0666ef5 100644 --- a/build2/cxx/windows-manifest.cxx +++ b/build2/cc/windows-manifest.cxx @@ -1,4 +1,4 @@ -// file : build2/cxx/windows-manifest.cxx -*- C++ -*- +// file : build2/cc/windows-manifest.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file @@ -9,12 +9,14 @@ #include <build2/filesystem> #include <build2/diagnostics> +#include <build2/cc/link> + using namespace std; using namespace butl; namespace build2 { - namespace cxx + namespace cc { // Translate the compiler target CPU value to the processorArchitecture // attribute value. @@ -36,16 +38,14 @@ namespace build2 // Generate a Windows manifest and if necessary create/update the manifest // file corresponding to the exe{} target. Return the manifest file path. // - path - windows_manifest (file& t, bool rpath_assembly) + path link:: + windows_manifest (file& t, bool rpath_assembly) const { - tracer trace ("cxx::windows_manifest"); + tracer trace (x, "windows_manifest"); scope& rs (t.root_scope ()); - const char* pa ( - windows_manifest_arch ( - cast<string> (rs["cxx.target.cpu"]))); + const char* pa (windows_manifest_arch (cast<string> (rs[x_target_cpu]))); string m; diff --git a/build2/cxx/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index b52315c..ea20a5c 100644 --- a/build2/cxx/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -1,4 +1,4 @@ -// file : build2/cxx/windows-rpath.cxx -*- C++ -*- +// file : build2/cc/windows-rpath.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file @@ -19,7 +19,7 @@ using namespace butl; namespace build2 { - namespace cxx + namespace cc { // Provide limited emulation of the rpath functionality on Windows using a // side-by-side assembly. In a nutshell, the idea is to create an assembly @@ -104,7 +104,10 @@ namespace build2 // manifest file. // void - windows_rpath_assembly (file& t, timestamp ts, bool scratch) + windows_rpath_assembly (file& t, + const string& tcpu, + timestamp ts, + bool scratch) { // Assembly paths and name. // @@ -129,8 +132,6 @@ namespace build2 return; } - scope& rs (t.root_scope ()); - // Next collect the set of DLLs that will be in our assembly. We need to // do this recursively which means we may end up with duplicates. Also, // it is possible that there aren't/no longer are any DLLs which means @@ -157,9 +158,7 @@ namespace build2 mkdir (ad, 3); } - const char* pa ( - windows_manifest_arch ( - cast<string> (rs["cxx.target.cpu"]))); + const char* pa (windows_manifest_arch (tcpu)); if (verb >= 3) text << "cat >" << am; @@ -176,7 +175,7 @@ namespace build2 << " processorArchitecture='" << pa << "'\n" << " version='0.0.0.0'/>\n"; - scope& as (*rs.weak_scope ()); // Amalgamation scope. + scope& as (*t.root_scope ().weak_scope ()); // Amalgamation scope. auto link = [&as, &ad] (const path& f, const path& l) { diff --git a/build2/cxx/compile b/build2/cxx/compile deleted file mode 100644 index 16c62e6..0000000 --- a/build2/cxx/compile +++ /dev/null @@ -1,37 +0,0 @@ -// file : build2/cxx/compile -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CXX_COMPILE -#define BUILD2_CXX_COMPILE - -#include <build2/types> -#include <build2/utility> - -#include <build2/rule> - -namespace build2 -{ - namespace cxx - { - class compile: public rule - { - public: - virtual match_result - match (action, target&, const string& hint) const; - - virtual recipe - apply (action, target&, const match_result&) const; - - static target_state - perform_update (action, target&); - - static target_state - perform_clean (action, target&); - - static compile instance; - }; - } -} - -#endif // BUILD2_CXX_COMPILE diff --git a/build2/cxx/link b/build2/cxx/link deleted file mode 100644 index 4f00ea0..0000000 --- a/build2/cxx/link +++ /dev/null @@ -1,48 +0,0 @@ -// file : build2/cxx/link -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CXX_LINK -#define BUILD2_CXX_LINK - -#include <build2/types> -#include <build2/utility> - -#include <build2/rule> - -#include <build2/bin/target> - -namespace build2 -{ - namespace cxx - { - class link: public rule - { - public: - virtual match_result - match (action, target&, const string& hint) const; - - virtual recipe - apply (action, target&, const match_result&) const; - - static target_state - perform_update (action, target&); - - static target_state - perform_clean (action, target&); - - static link instance; - - private: - friend class compile; - - static target* - search_library (optional<dir_paths>&, prerequisite&); - - static dir_paths - extract_library_paths (scope&); - }; - } -} - -#endif // BUILD2_CXX_LINK diff --git a/build2/cxx/module b/build2/cxx/module index 37466ef..8c1a01f 100644 --- a/build2/cxx/module +++ b/build2/cxx/module @@ -15,6 +15,15 @@ namespace build2 namespace cxx { bool + config_init (scope&, + scope&, + const location&, + unique_ptr<module_base>&, + bool, + bool, + const variable_map&); + + bool init (scope&, scope&, const location&, diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index f66ef53..fd98114 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -4,23 +4,13 @@ #include <build2/cxx/module> -#include <butl/triplet> - #include <build2/scope> #include <build2/context> #include <build2/diagnostics> -#include <build2/config/utility> -#include <build2/install/utility> - -#include <build2/bin/target> +#include <build2/cc/module> -#include <build2/cxx/link> -#include <build2/cxx/guess> #include <build2/cxx/target> -#include <build2/cxx/compile> -#include <build2/cxx/install> -#include <build2/cxx/utility> using namespace std; using namespace butl; @@ -29,355 +19,228 @@ namespace build2 { namespace cxx { - bool - init (scope& r, - scope& b, - const location& loc, - unique_ptr<module_base>&, - bool first, - bool, - const variable_map& config_hints) - { - tracer trace ("cxx::init"); - l5 ([&]{trace << "for " << b.out_path ();}); - - // Enter module variables. - // - if (first) - { - auto& v (var_pool); + using cc::config_module; - // Note: some overridable, some not. - // - v.insert<path> ("config.cxx", true); - v.insert<strings> ("config.cxx.poptions", true); - v.insert<strings> ("config.cxx.coptions", true); - v.insert<strings> ("config.cxx.loptions", true); - v.insert<strings> ("config.cxx.libs", true); - - v.insert<strings> ("cxx.poptions"); - v.insert<strings> ("cxx.coptions"); - v.insert<strings> ("cxx.loptions"); - v.insert<strings> ("cxx.libs"); - - v.insert<strings> ("cxx.export.poptions"); - v.insert<strings> ("cxx.export.coptions"); - v.insert<strings> ("cxx.export.loptions"); - v.insert<strings> ("cxx.export.libs"); - - v.insert<string> ("cxx.std", true); - } - - // Configure. - // - - assert (config_hints.empty ()); // We don't known any hints. - - // config.cxx.{p,c,l}options - // config.cxx.libs - // - // These are optional. We also merge them into the corresponding - // cxx.* variables. - // - // The merging part gets a bit tricky if this module has already - // been loaded in one of the outer scopes. By doing the straight - // append we would just be repeating the same options over and - // over. So what we are going to do is only append to a value if - // it came from this scope. Then the usage for merging becomes: - // - // cxx.coptions = <overridable options> # Note: '='. - // using cxx - // cxx.coptions += <overriding options> # Note: '+='. - // - b.assign ("cxx.poptions") += cast_null<strings> ( - config::optional (r, "config.cxx.poptions")); - - b.assign ("cxx.coptions") += cast_null<strings> ( - config::optional (r, "config.cxx.coptions")); - - b.assign ("cxx.loptions") += cast_null<strings> ( - config::optional (r, "config.cxx.loptions")); + class module: public cc::module + { + public: + explicit + module (data&& d): common (move (d)), cc::module (move (d)) {} - b.assign ("cxx.libs") += cast_null<strings> ( - config::optional (r, "config.cxx.libs")); + bool + translate_std (string&, scope&, const value&) const override; + }; - // Configuration hints for the bin module. They will only be used on the - // first loading of the bin module (for this project) so we only - // populate them on our first loading. - // - variable_map bin_hints; + bool module:: + translate_std (string& s, scope& r, const value& val) const + { + const string& v (cast<string> (val)); - // config.cxx - // - if (first) + if (cid == "msvc") { - auto p (config::required (r, "config.cxx", path ("g++"))); - - // Figure out which compiler we are dealing with, its target, etc. + // C++ standard-wise, with VC you get what you get. The question is + // whether we should verify that the requested standard is provided by + // this VC version. And if so, from which version should we say VC + // supports 11, 14, and 17? We should probably be as loose as possible + // here since the author will always be able to tighten (but not + // loosen) this in the buildfile (i.e., detect unsupported versions). // - const path& cxx (cast<path> (p.first)); - compiler_info ci (guess (cxx, cast_null<strings> (r["cxx.coptions"]))); - - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // For now we are not going to bother doing this for C++03. // - if (verb >= (p.second ? 2 : 3)) + if (v != "98" && v != "03") { - text << "cxx " << project (r) << '@' << r.out_path () << '\n' - << " cxx " << cxx << '\n' - << " id " << ci.id << '\n' - << " version " << ci.version.string << '\n' - << " major " << ci.version.major << '\n' - << " minor " << ci.version.minor << '\n' - << " patch " << ci.version.patch << '\n' - << " build " << ci.version.build << '\n' - << " signature " << ci.signature << '\n' - << " checksum " << ci.checksum << '\n' - << " target " << ci.target; - } - - r.assign<string> ("cxx.id") = ci.id.string (); - r.assign<string> ("cxx.id.type") = move (ci.id.type); - r.assign<string> ("cxx.id.variant") = move (ci.id.variant); - - r.assign<string> ("cxx.version") = move (ci.version.string); - r.assign<uint64_t> ("cxx.version.major") = ci.version.major; - r.assign<uint64_t> ("cxx.version.minor") = ci.version.minor; - r.assign<uint64_t> ("cxx.version.patch") = ci.version.patch; - r.assign<string> ("cxx.version.build") = move (ci.version.build); + uint64_t cver (cast<uint64_t> (r[x_version_major])); - r.assign<string> ("cxx.signature") = move (ci.signature); - r.assign<string> ("cxx.checksum") = move (ci.checksum); - - // While we still have the original, compiler-reported target, see if - // we can derive a binutils program pattern. - // - // BTW, for GCC we also get gcc-{ar,ranlib} which add support for the - // LTO plugin though it seems more recent GNU binutils (2.25) are able - // to load the plugin when needed automatically. So it doesn't seem we - // should bother trying to support this on our end (the way we could - // do it is by passing config.bin.{ar,ranlib} as hints). - // - string pattern; - - if (cast<string> (r["cxx.id"]) == "msvc") - { - // If the compiler name is/starts with 'cl' (e.g., cl.exe, cl-14), - // then replace it with '*' and use it as a pattern for lib, link, - // etc. + // @@ Is mapping for 14 and 17 correct? Maybe Update 2 for 14? // - if (cxx.size () > 2) + if ((v == "11" && cver < 16) || // C++11 since VS2010/10.0. + (v == "14" && cver < 19) || // C++14 since VS2015/14.0. + (v == "17" && cver < 20)) // C++17 since VS20??/15.0. { - const string& l (cxx.leaf ().string ()); - size_t n (l.size ()); - - if (n >= 2 && - (l[0] == 'c' || l[0] == 'C') && - (l[1] == 'l' || l[1] == 'L') && - (n == 2 || l[2] == '.' || l[2] == '-')) - { - path p (cxx.directory ()); - p /= "*"; - p += l.c_str () + 2; - pattern = move (p).string (); - } + fail << "C++" << v << " is not supported by " + << cast<string> (r[x_signature]) << + info << "required by " << project (r) << '@' << r.out_path (); } } + + return false; + } + else + { + // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with + // older versions of the compilers. + // + s = "-std="; + + if (v == "98") + s += "c++98"; + else if (v == "03") + s += "c++03"; + else if (v == "11") + s += "c++0x"; + else if (v == "14") + s += "c++1y"; + else if (v == "17") + s += "c++1z"; else - { - // When cross-compiling the whole toolchain is normally prefixed - // with the target triplet, e.g., x86_64-w64-mingw32-{g++,ar,ld}. - // - const string& t (ci.target); - size_t n (t.size ()); + s += v; // In case the user specifies something like 'gnu++17'. - if (cxx.size () > n + 1) - { - const string& l (cxx.leaf ().string ()); - - if (l.size () > n + 1 && l.compare (0, n, t) == 0 && l[n] == '-') - { - path p (cxx.directory ()); - p /= t; - p += "-*"; - pattern = move (p).string (); - } - } - } + return true; + } + } - if (!pattern.empty ()) - bin_hints.assign ("config.bin.pattern") = move (pattern); + bool + config_init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>& m, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("cxx::config_init"); + l5 ([&]{trace << "for " << b.out_path ();}); - // Split/canonicalize the target. + if (first) + { + // Load cc.vars so that we can cache all the cc.* variables. // + if (!cast_false<bool> (b["cc.vars.loaded"])) + load_module ("cc.vars", r, b, loc); - // Did the user ask us to use config.sub? + // Enter all the variables and initialize the module data. // - if (ops.config_sub_specified ()) - { - ci.target = run<string> (ops.config_sub (), - ci.target.c_str (), - [] (string& l) {return move (l);}); - l5 ([&]{trace << "config.sub target: '" << ci.target << "'";}); - } - - try - { - string canon; - triplet t (ci.target, canon); + auto& v (var_pool); - l5 ([&]{trace << "canonical target: '" << canon << "'; " - << "class: " << t.class_;}); + cc::config_data d { + cc::lang::cxx, - // Pass the target we extracted from the C++ compiler as a config - // hint to the bin module. - // - bin_hints.assign ("config.bin.target") = canon; + "cxx", + "c++", + "g++", - // Enter as cxx.target.{cpu,vendor,system,version,class}. + // Note: some overridable, some not. // - r.assign<string> ("cxx.target") = move (canon); - r.assign<string> ("cxx.target.cpu") = move (t.cpu); - r.assign<string> ("cxx.target.vendor") = move (t.vendor); - r.assign<string> ("cxx.target.system") = move (t.system); - r.assign<string> ("cxx.target.version") = move (t.version); - r.assign<string> ("cxx.target.class") = move (t.class_); - } - catch (const invalid_argument& e) - { - // This is where we suggest that the user specifies --config-sub to - // help us out. - // - fail << "unable to parse compiler target '" << ci.target << "': " - << e.what () << - info << "consider using the --config-sub option"; - } - } - - const string& cid (cast<string> (r["cxx.id"])); - const string& tsys (cast<string> (r["cxx.target.system"])); - - // 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 (!cast_false<bool> (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, bin_hints); - - // Verify bin's target matches ours. - // - { - const string& bt (cast<string> (r["bin.target"])); - const string& ct (cast<string> (r["cxx.target"])); - - if (bt != ct) - fail (loc) << "bin and cxx module target platform mismatch" << - info << "bin.target is " << bt << - info << "cxx.target is " << ct; + v.insert<path> ("config.cxx", true), + v.insert<strings> ("config.cxx.poptions", true), + v.insert<strings> ("config.cxx.coptions", true), + v.insert<strings> ("config.cxx.loptions", true), + v.insert<strings> ("config.cxx.libs", true), + + v.insert<strings> ("cxx.poptions"), + v.insert<strings> ("cxx.coptions"), + v.insert<strings> ("cxx.loptions"), + v.insert<strings> ("cxx.libs"), + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.libs"], + + v.insert<strings> ("cxx.export.poptions"), + v.insert<strings> ("cxx.export.coptions"), + v.insert<strings> ("cxx.export.loptions"), + v.insert<strings> ("cxx.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v.insert<string> ("cxx.std", true), + + v.insert<string> ("cxx.id"), + v.insert<string> ("cxx.id.type"), + v.insert<string> ("cxx.id.variant"), + + v.insert<string> ("cxx.version"), + v.insert<uint64_t> ("cxx.version.major"), + v.insert<uint64_t> ("cxx.version.minor"), + v.insert<uint64_t> ("cxx.version.patch"), + v.insert<string> ("cxx.version.build"), + + v.insert<string> ("cxx.signature"), + v.insert<string> ("cxx.checksum"), + + v.insert<string> ("cxx.target"), + v.insert<string> ("cxx.target.cpu"), + v.insert<string> ("cxx.target.vendor"), + v.insert<string> ("cxx.target.system"), + v.insert<string> ("cxx.target.version"), + v.insert<string> ("cxx.target.class") + }; + + assert (m == nullptr); + m.reset (new config_module (move (d))); } - // Load the bin.ar module unless we were asked to only build shared - // libraries. - // - if (auto l = r["config.bin.lib"]) - { - if (cast<string> (l) != "shared") - { - if (!cast_false<bool> (b["bin.ar.loaded"])) - load_module ("bin.ar", r, b, loc, false, bin_hints); - } - } + static_cast<config_module&> (*m).init (r, b, loc, first, hints); + return true; + } - // In the VC world you link things directly with link.exe so load the - // bin.ld module. - // - if (cid == "msvc") - { - if (!cast_false<bool> (b["bin.ld.loaded"])) - load_module ("bin.ld", r, b, loc, false, bin_hints); - } + static const target_type* hdr[] = + { + &hxx::static_type, + &ixx::static_type, + &txx::static_type, + &h::static_type, + nullptr + }; + + static const target_type* inc[] = + { + &hxx::static_type, + &ixx::static_type, + &txx::static_type, + &cxx::static_type, + &h::static_type, + &c::static_type, + nullptr + }; - // If our target is MinGW, then we will need the resource compiler - // (windres) in order to embed the manifest. - // - if (tsys == "mingw32") - { - if (!cast_false<bool> (b["bin.rc.loaded"])) - load_module ("bin.rc", r, b, loc, false, bin_hints); - } + bool + init (scope& r, + scope& b, + const location& loc, + unique_ptr<module_base>& m, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("cxx::init"); + l5 ([&]{trace << "for " << b.out_path ();}); - // Register target types. + // Load cxx.config. // - { - auto& t (b.target_types); - - t.insert<h> (); - t.insert<c> (); + if (!cast_false<bool> (b["cxx.config.loaded"])) + load_module ("cxx.config", r, b, loc, false, hints); - t.insert<cxx> (); - t.insert<hxx> (); - t.insert<ixx> (); - t.insert<txx> (); - } - - // Register rules. - // + if (first) { - using namespace bin; - - auto& r (b.rules); - - // We register for configure so that we detect unresolved imports - // during configuration rather that later, e.g., during update. - // - // @@ Should we check if install module was loaded (see bin)? - // - - r.insert<obje> (perform_update_id, "cxx.compile", compile::instance); - r.insert<obje> (perform_clean_id, "cxx.compile", compile::instance); - r.insert<obje> (configure_update_id, "cxx.compile", compile::instance); + config_module& cm (*r.modules.lookup<config_module> ("cxx.config")); - r.insert<exe> (perform_update_id, "cxx.link", link::instance); - r.insert<exe> (perform_clean_id, "cxx.link", link::instance); - r.insert<exe> (configure_update_id, "cxx.link", link::instance); + cc::data d { + cm, - r.insert<exe> (perform_install_id, "cxx.install", install::instance); + "cxx.compile", + "cxx.link", + "cxx.install", - // Only register static object/library rules if the bin.ar module is - // loaded (by us or by the user). - // - if (cast_false<bool> (b["bin.ar.loaded"])) - { - r.insert<obja> (perform_update_id, "cxx.compile", compile::instance); - r.insert<obja> (perform_clean_id, "cxx.compile", compile::instance); - r.insert<obja> (configure_update_id, "cxx.compile", compile::instance); + cast<string> (r[cm.x_id]), + cast<string> (r[cm.x_target]), + cast<string> (r[cm.x_target_system]), + cast<string> (r[cm.x_target_class]), - r.insert<liba> (perform_update_id, "cxx.link", link::instance); - r.insert<liba> (perform_clean_id, "cxx.link", link::instance); - r.insert<liba> (configure_update_id, "cxx.link", link::instance); + cxx::static_type, + hdr, + inc + }; - r.insert<liba> (perform_install_id, "cxx.install", install::instance); - } - - r.insert<objs> (perform_update_id, "cxx.compile", compile::instance); - r.insert<objs> (perform_clean_id, "cxx.compile", compile::instance); - r.insert<objs> (configure_update_id, "cxx.compile", compile::instance); - - r.insert<libs> (perform_update_id, "cxx.link", link::instance); - r.insert<libs> (perform_clean_id, "cxx.link", link::instance); - r.insert<libs> (configure_update_id, "cxx.link", link::instance); - - r.insert<libs> (perform_install_id, "cxx.install", install::instance); + assert (m == nullptr); + m.reset (new module (move (d))); } - // Configure "installability" of our target types. - // - using namespace install; - - install_path<hxx> (b, dir_path ("include")); // Into install.include. - install_path<ixx> (b, dir_path ("include")); - install_path<txx> (b, dir_path ("include")); - install_path<h> (b, dir_path ("include")); - + static_cast<module&> (*m).init (r, b, loc, first, hints); return true; } } diff --git a/build2/cxx/target b/build2/cxx/target index 154ec24..0239c25 100644 --- a/build2/cxx/target +++ b/build2/cxx/target @@ -9,11 +9,15 @@ #include <build2/utility> #include <build2/target> +#include <build2/cc/target> namespace build2 { namespace cxx { + using cc::h; + using cc::c; + class hxx: public file { public: @@ -53,28 +57,6 @@ namespace build2 static const target_type static_type; virtual const target_type& dynamic_type () const {return static_type;} }; - - //@@ TMP: should be in c-common or some such. - // - class h: public file - { - public: - using file::file; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - class c: public file - { - public: - using file::file; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; } } diff --git a/build2/cxx/target.cxx b/build2/cxx/target.cxx index 22ace50..30afd89 100644 --- a/build2/cxx/target.cxx +++ b/build2/cxx/target.cxx @@ -59,29 +59,5 @@ namespace build2 &search_file, false }; - - extern const char h_ext_def[] = "h"; - const target_type h::static_type - { - "h", - &file::static_type, - &target_factory<h>, - &target_extension_var<ext_var, h_ext_def>, - nullptr, - &search_file, - false - }; - - extern const char c_ext_def[] = "c"; - const target_type c::static_type - { - "c", - &file::static_type, - &target_factory<c>, - &target_extension_var<ext_var, c_ext_def>, - nullptr, - &search_file, - false - }; } } diff --git a/build2/cxx/utility b/build2/cxx/utility deleted file mode 100644 index 7333af6..0000000 --- a/build2/cxx/utility +++ /dev/null @@ -1,42 +0,0 @@ -// file : build2/cxx/utility -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CXX_UTILITY -#define BUILD2_CXX_UTILITY - -#include <build2/types> -#include <build2/utility> - -#include <build2/target> - -#include <build2/cxx/common> - -namespace build2 -{ - namespace cxx - { - // T is either target or scope. - // - template <typename T> - void - append_std (cstrings&, scope& rs, const string& cid, T&, string& storage); - - template <typename T> - void - hash_std (sha256&, scope& rs, const string& cid, T&); - - // Append or hash library options from one of the cxx.export.* variables - // recursively, prerequisite libraries first. - // - void - append_lib_options (cstrings&, target&, const char* variable, lorder); - - void - hash_lib_options (sha256&, target&, const char* variable, lorder); - } -} - -#include <build2/cxx/utility.ixx> - -#endif // BUILD2_CXX_UTILITY diff --git a/build2/cxx/utility.cxx b/build2/cxx/utility.cxx deleted file mode 100644 index 7aae6ac..0000000 --- a/build2/cxx/utility.cxx +++ /dev/null @@ -1,109 +0,0 @@ -// file : build2/cxx/utility.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build2/cxx/utility> - -#include <build2/bin/target> - -using namespace std; - -namespace build2 -{ - namespace cxx - { - // Return true if there is an option (stored in s). - // - bool - translate_std (scope& rs, const string& cid, const value& val, string& s) - { - const string& v (cast<string> (val)); - - if (cid == "msvc") - { - // C++ standard-wise, with VC++ you get what you get. The question is - // whether we should verify that the requested standard is provided by - // this VC++ version. And if so, from which version should we say VC++ - // supports 11, 14, and 17? We should probably be as loose as possible - // here since the author will always be able to tighten (but not - // loosen) this in the buildfile (i.e., detect unsupported versions). - // - // For now we are not going to bother doing this for C++03. - // - if (v != "98" && v != "03") - { - uint64_t cver (cast<uint64_t> (rs["cxx.version.major"])); - - // @@ Is mapping for 14 and 17 correct? Maybe Update 2 for 14? - // - if ((v == "11" && cver < 16) || // C++11 since VS2010/10.0. - (v == "14" && cver < 19) || // C++14 since VS2015/14.0. - (v == "17" && cver < 20)) // C++17 since VS20??/15.0. - { - fail << "C++" << v << " is not supported by " - << cast<string> (rs["cxx.signature"]) << - info << "required by " << rs.out_path (); - } - } - - return false; - } - else - { - // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with - // older versions of the compilers. - // - s = "-std="; - - if (v == "98") - s += "c++98"; - else if (v == "03") - s += "c++03"; - else if (v == "11") - s += "c++0x"; - else if (v == "14") - s += "c++1y"; - else if (v == "17") - s += "c++1z"; - else - s += v; // In case the user specifies something like 'gnu++17'. - - return true; - } - } - - void - append_lib_options (cstrings& args, target& l, const char* var, lorder lo) - { - using namespace bin; - - for (target* t: l.prerequisite_targets) - { - if (lib* l = t->is_a<lib> ()) - t = &link_member (*l, lo); // Pick one of the members. - - if (t->is_a<liba> () || t->is_a<libs> ()) - append_lib_options (args, *t, var, lo); - } - - append_options (args, l, var); - } - - void - hash_lib_options (sha256& csum, target& l, const char* var, lorder lo) - { - using namespace bin; - - for (target* t: l.prerequisite_targets) - { - if (lib* l = t->is_a<lib> ()) - t = &link_member (*l, lo); // Pick one of the members. - - if (t->is_a<liba> () || t->is_a<libs> ()) - hash_lib_options (csum, *t, var, lo); - } - - hash_options (csum, l, var); - } - } -} diff --git a/build2/cxx/utility.ixx b/build2/cxx/utility.ixx deleted file mode 100644 index c624e87..0000000 --- a/build2/cxx/utility.ixx +++ /dev/null @@ -1,33 +0,0 @@ -// file : build2/cxx/utility.ixx -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -namespace build2 -{ - namespace cxx - { - bool - translate_std (scope&, const string&, const value&, string&); - - template <typename T> - inline void - append_std (cstrings& args, scope& rs, const string& cid, T& t, string& s) - { - if (auto l = t["cxx.std"]) - if (translate_std (rs, cid, *l, s)) - args.push_back (s.c_str ()); - } - - template <typename T> - inline void - hash_std (sha256& csum, scope& rs, const string& cid, T& t) - { - if (auto l = t["cxx.std"]) - { - string s; - if (translate_std (rs, cid, *l, s)) - csum.append (s); - } - } - } -} diff --git a/build2/diagnostics b/build2/diagnostics index 8aedf13..5441df1 100644 --- a/build2/diagnostics +++ b/build2/diagnostics @@ -259,14 +259,18 @@ namespace build2 struct simple_prologue_base { explicit - simple_prologue_base (const char* type, const char* name, uint16_t sverb) - : type_ (type), name_ (name), sverb_ (sverb) {} + simple_prologue_base (const char* type, + const char* mod, + const char* name, + uint16_t sverb) + : type_ (type), mod_ (mod), name_ (name), sverb_ (sverb) {} void operator() (const diag_record& r) const; private: const char* type_; + const char* mod_; const char* name_; const uint16_t sverb_; }; @@ -290,16 +294,18 @@ namespace build2 struct location_prologue_base { location_prologue_base (const char* type, + const char* mod, const char* name, const location& l, uint16_t sverb) - : type_ (type), name_ (name), loc_ (l), sverb_ (sverb) {} + : type_ (type), mod_ (mod), name_ (name), loc_ (l), sverb_ (sverb) {} void operator() (const diag_record& r) const; private: const char* type_; + const char* mod_; const char* name_; const location loc_; const uint16_t sverb_; @@ -311,23 +317,24 @@ namespace build2 explicit basic_mark_base (uint16_t (*sverb) (), const char* type, + const char* mod = nullptr, const char* name = nullptr, const void* data = nullptr, diag_epilogue epilogue = nullptr) : sverb_ (sverb), - type_ (type), name_ (name), data_ (data), + type_ (type), mod_ (mod), name_ (name), data_ (data), epilogue_ (epilogue) {} simple_prologue operator() () const { - return simple_prologue (epilogue_, type_, name_, sverb_ ()); + return simple_prologue (epilogue_, type_, mod_, name_, sverb_ ()); } location_prologue operator() (const location& l) const { - return location_prologue (epilogue_, type_, name_, l, sverb_ ()); + return location_prologue (epilogue_, type_, mod_, name_, l, sverb_ ()); } template <typename L> @@ -335,12 +342,13 @@ namespace build2 operator() (const L& l) const { return location_prologue ( - epilogue_, type_, name_, get_location (l, data_), sverb_ ()); + epilogue_, type_, mod_, name_, get_location (l, data_), sverb_ ()); } protected: uint16_t (*sverb_) (); const char* type_; + const char* mod_; const char* name_; const void* data_; const diag_epilogue epilogue_; @@ -358,8 +366,15 @@ namespace build2 { explicit trace_mark_base (const char* name, const void* data = nullptr) - : basic_mark_base ([]() {return stream_verb_max;}, "trace", name, data) - {} + : trace_mark_base (nullptr, name, data) {} + + trace_mark_base (const char* mod, + const char* name, + const void* data = nullptr) + : basic_mark_base ([]() {return stream_verb_max;}, + "trace", + mod, name, + data) {} }; typedef diag_mark<trace_mark_base> trace_mark; @@ -372,8 +387,11 @@ namespace build2 { explicit fail_mark_base (const void* data = nullptr) - : basic_mark_base (&stream_verb_map, "error", nullptr, data, &epilogue) - {} + : basic_mark_base (&stream_verb_map, + "error", + nullptr, nullptr, + data, + &epilogue) {} static void epilogue (const diag_record&) {throw E ();} diff --git a/build2/diagnostics.cxx b/build2/diagnostics.cxx index 7b60d05..1265f73 100644 --- a/build2/diagnostics.cxx +++ b/build2/diagnostics.cxx @@ -97,6 +97,9 @@ namespace build2 if (type_ != nullptr) r << type_ << ": "; + if (mod_ != nullptr) + r << mod_ << "::"; + if (name_ != nullptr) r << name_ << ": "; } @@ -119,6 +122,9 @@ namespace build2 if (type_ != nullptr) r << type_ << ": "; + if (mod_ != nullptr) + r << mod_ << "::"; + if (name_ != nullptr) r << name_ << ": "; } diff --git a/build2/prerequisite b/build2/prerequisite index 7c913f3..e53bb41 100644 --- a/build2/prerequisite +++ b/build2/prerequisite @@ -130,6 +130,9 @@ namespace build2 template <typename T> bool is_a () const {return type.is_a<T> ();} + + bool + is_a (const target_type_type& tt) const {return type.is_a (tt);} }; inline bool diff --git a/build2/rule b/build2/rule index 03e4bf2..6d4a580 100644 --- a/build2/rule +++ b/build2/rule @@ -13,6 +13,9 @@ namespace build2 { + // @@ This is an ugly mess that is overdue for redesign. Perhaps + // something similar to variable value storage? + // class match_result { public: @@ -56,6 +59,14 @@ namespace build2 { return target != nullptr || prerequisite != nullptr; } + + template <typename T> + T& + as_target () const + { + return static_cast<T&> ( + target != nullptr ? *target : *prerequisite->target); + } }; class rule diff --git a/build2/target b/build2/target index 7a50a19..103b719 100644 --- a/build2/target +++ b/build2/target @@ -68,6 +68,11 @@ namespace build2 // this case the returned by the recipe value is still used as the resulting // target state so it should match the group's state. // + // Note that max size for the "small capture optimization" in std::function + // ranges (in pointer sizes) from 0 (GCC prior to 5) to 2 (GCC 5) to 6 (VC + // 14u2). With the size ranging (in bytes for 64-bit target) from 32 (GCC) + // to 64 (VC). + // using recipe_function = target_state (action, target&); using recipe = function<recipe_function>; @@ -471,6 +476,9 @@ namespace build2 const T* is_a () const {return dynamic_cast<const T*> (this);} + bool + is_a (const target_type& tt) const {return type ().is_a (tt);} + // Dynamic derivation to support define. // const target_type* derived_type = nullptr; @@ -622,8 +630,9 @@ namespace build2 // struct prerequisite_member { - typedef build2::target target_type; - typedef build2::prerequisite prerequisite_type; + using target_type = build2::target; + using prerequisite_type = build2::prerequisite; + using target_type_type = build2::target_type; prerequisite_ref& prerequisite; target_type* target; @@ -637,6 +646,14 @@ namespace build2 : prerequisite.get ().is_a<T> (); } + bool + is_a (const target_type_type& tt) const + { + return target != nullptr + ? target->is_a (tt) + : prerequisite.get ().is_a (tt); + } + prerequisite_key key () const { @@ -645,7 +662,7 @@ namespace build2 : prerequisite.get ().key (); } - const build2::target_type& + const target_type_type& type () const { return target != nullptr ? target->type () : prerequisite.get ().type; diff --git a/build2/target-type b/build2/target-type index 6006b2c..6140ab1 100644 --- a/build2/target-type +++ b/build2/target-type @@ -46,12 +46,18 @@ namespace build2 target* (*search) (const prerequisite_key&); bool see_through; // A group with the default "see through" semantics. - bool - is_a (const target_type&) const; // Defined in target.cxx - template <typename T> bool is_a () const {return is_a (T::static_type);} + + bool + is_a (const target_type& tt) const + { + return this == &tt || (base != nullptr && is_a_base (tt)); + } + + bool + is_a_base (const target_type&) const; // Defined in target.cxx }; inline bool @@ -97,12 +103,16 @@ namespace build2 class target_type_map: public target_type_map_base { public: - void - insert (const target_type& tt) {emplace (tt.name, target_type_ref (tt));} + const target_type& + insert (const target_type& tt) + { + emplace (tt.name, target_type_ref (tt)); + return tt; + } template <typename T> - void - insert () {insert (T::static_type);} + const target_type& + insert () {return insert (T::static_type);} }; } diff --git a/build2/target.cxx b/build2/target.cxx index a0283b0..7a33dbc 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -18,10 +18,10 @@ namespace build2 // target_type // bool target_type:: - is_a (const target_type& tt) const + is_a_base (const target_type& tt) const { - for (const target_type* p (this); p != nullptr; p = p->base) - if (*p == tt) + for (const target_type* b (base); b != nullptr; b = b->base) + if (*b == tt) return true; return false; diff --git a/build2/utility b/build2/utility index 4c49be5..3ecceed 100644 --- a/build2/utility +++ b/build2/utility @@ -146,13 +146,23 @@ namespace build2 // Append 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. // + struct variable; + + template <typename T> + void + append_options (cstrings&, T&, const variable&); + + template <typename T> + void + append_options (cstrings&, T&, const char*); + template <typename T> void - append_options (cstrings&, T&, const char* variable); + hash_options (sha256&, T&, const variable&); template <typename T> void - hash_options (sha256&, T&, const char* variable); + hash_options (sha256&, T&, const char*); // As above but from the strings value directly. // @@ -178,6 +188,13 @@ namespace build2 bool find_option (const char* option, T&, + const variable&, + bool ignore_case = false); + + template <typename T> + bool + find_option (const char* option, + T&, const char* variable, bool ignore_case = false); @@ -194,6 +211,13 @@ namespace build2 // template <typename T> bool + find_options (initializer_list<const char*>, + T&, + const variable&, + bool = false); + + template <typename T> + bool find_options (initializer_list<const char*>, T&, const char*, bool = false); bool @@ -209,6 +233,10 @@ namespace build2 // template <typename T> bool + find_option_prefix (const char* prefix, T&, const variable&, bool = false); + + template <typename T> + bool find_option_prefix (const char* prefix, T&, const char*, bool = false); bool @@ -226,6 +254,13 @@ namespace build2 bool find_option_prefixes (initializer_list<const char*>, T&, + const variable&, + bool = false); + + template <typename T> + bool + find_option_prefixes (initializer_list<const char*>, + T&, const char*, bool = false); diff --git a/build2/utility.ixx b/build2/utility.ixx index f957875..956a726 100644 --- a/build2/utility.ixx +++ b/build2/utility.ixx @@ -35,6 +35,13 @@ namespace build2 template <typename T> inline void + append_options (cstrings& args, T& s, const variable& var) + { + append_options (args, s[var]); + } + + template <typename T> + inline void append_options (cstrings& args, T& s, const char* var) { append_options (args, s[var]); @@ -42,14 +49,27 @@ namespace build2 template <typename T> inline void - hash_options (sha256& csum, T& s, const char* var) + hash_options (sha256& csum, T& s, const variable& var) { + hash_options (csum, s[var]); + } + template <typename T> + inline void + hash_options (sha256& csum, T& s, const char* var) + { hash_options (csum, s[var]); } template <typename T> inline bool + find_option (const char* o, T& s, const variable& var, bool ic) + { + return find_option (o, s[var], ic); + } + + template <typename T> + inline bool find_option (const char* o, T& s, const char* var, bool ic) { return find_option (o, s[var], ic); @@ -59,6 +79,16 @@ namespace build2 inline bool find_options (initializer_list<const char*> os, T& s, + const variable& var, + bool ic) + { + return find_options (os, s[var], ic); + } + + template <typename T> + inline bool + find_options (initializer_list<const char*> os, + T& s, const char* var, bool ic) { @@ -67,6 +97,13 @@ namespace build2 template <typename T> inline bool + find_option_prefix (const char* p, T& s, const variable& var, bool ic) + { + return find_option_prefix (p, s[var], ic); + } + + template <typename T> + inline bool find_option_prefix (const char* p, T& s, const char* var, bool ic) { return find_option_prefix (p, s[var], ic); @@ -76,6 +113,16 @@ namespace build2 inline bool find_option_prefixes (initializer_list<const char*> ps, T& s, + const variable& var, + bool ic) + { + return find_option_prefixes (ps, s[var], ic); + } + + template <typename T> + inline bool + find_option_prefixes (initializer_list<const char*> ps, + T& s, const char* var, bool ic) { diff --git a/build2/variable b/build2/variable index 9888aa0..d6be976 100644 --- a/build2/variable +++ b/build2/variable @@ -691,7 +691,11 @@ namespace build2 } const variable& - find (const string& name); + find (const string& name); //@@ TODO: Move to operator[], remove. + //@@ ranmae var_pool to varpool or vpool? + + const variable& + operator[] (const string& name) {return find (name);} using variable_pool_base::clear; |