// file : build2/cxx/module.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace butl; namespace build2 { namespace cxx { bool init (scope& r, scope& b, const location& loc, unique_ptr&, 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); // Note: some overridable, some not. // v.insert ("config.cxx", true); v.insert ("config.cxx.poptions", true); v.insert ("config.cxx.coptions", true); v.insert ("config.cxx.loptions", true); v.insert ("config.cxx.libs", true); v.insert ("cxx.poptions"); v.insert ("cxx.coptions"); v.insert ("cxx.loptions"); v.insert ("cxx.libs"); v.insert ("cxx.export.poptions"); v.insert ("cxx.export.coptions"); v.insert ("cxx.export.loptions"); v.insert ("cxx.export.libs"); v.insert ("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 = # Note: '='. // using cxx // cxx.coptions += # Note: '+='. // b.assign ("cxx.poptions") += cast_null ( config::optional (r, "config.cxx.poptions")); b.assign ("cxx.coptions") += cast_null ( config::optional (r, "config.cxx.coptions")); b.assign ("cxx.loptions") += cast_null ( config::optional (r, "config.cxx.loptions")); b.assign ("cxx.libs") += cast_null ( config::optional (r, "config.cxx.libs")); // 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; // config.cxx // if (first) { auto p (config::required (r, "config.cxx", path ("g++"))); // Figure out which compiler we are dealing with, its target, etc. // const path& cxx (cast (p.first)); compiler_info ci (guess (cxx, cast_null (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). // if (verb >= (p.second ? 2 : 3)) { 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 ("cxx.id") = ci.id.string (); r.assign ("cxx.id.type") = move (ci.id.type); r.assign ("cxx.id.variant") = move (ci.id.variant); r.assign ("cxx.version") = move (ci.version.string); r.assign ("cxx.version.major") = ci.version.major; r.assign ("cxx.version.minor") = ci.version.minor; r.assign ("cxx.version.patch") = ci.version.patch; r.assign ("cxx.version.build") = move (ci.version.build); r.assign ("cxx.signature") = move (ci.signature); r.assign ("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 (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. // if (cxx.size () > 2) { 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 (); } } } 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 ()); 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 (); } } } if (!pattern.empty ()) bin_hints.assign ("config.bin.pattern") = move (pattern); // Split/canonicalize the target. // // Did the user ask us to use config.sub? // if (ops.config_sub_specified ()) { ci.target = run (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_;}); // Pass the target we extracted from the C++ compiler as a config // hint to the bin module. // bin_hints.assign ("config.bin.target") = canon; // Enter as cxx.target.{cpu,vendor,system,version,class}. // r.assign ("cxx.target") = move (canon); r.assign ("cxx.target.cpu") = move (t.cpu); r.assign ("cxx.target.vendor") = move (t.vendor); r.assign ("cxx.target.system") = move (t.system); r.assign ("cxx.target.version") = move (t.version); r.assign ("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 (r["cxx.id"])); const string& tsys (cast (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 (b["bin.loaded"])) load_module ("bin", r, b, loc, false, bin_hints); // Verify bin's target matches ours. // { const string& bt (cast (r["bin.target"])); const string& ct (cast (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; } // Load the bin.ar module unless we were asked to only build shared // libraries. // if (auto l = r["config.bin.lib"]) { if (cast (l) != "shared") { if (!cast_false (b["bin.ar.loaded"])) load_module ("bin.ar", r, b, loc, false, bin_hints); } } // In the VC world you link things directly with link.exe so load the // bin.ld module. // if (cid == "msvc") { if (!cast_false (b["bin.ld.loaded"])) load_module ("bin.ld", r, b, loc, false, bin_hints); } // 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 (b["bin.rc.loaded"])) load_module ("bin.rc", r, b, loc, false, bin_hints); } // Register target types. // { auto& t (b.target_types); t.insert (); t.insert (); t.insert (); t.insert (); t.insert (); t.insert (); } // 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)? // r.insert (perform_update_id, "cxx.compile", compile::instance); r.insert (perform_clean_id, "cxx.compile", compile::instance); r.insert (configure_update_id, "cxx.compile", compile::instance); r.insert (perform_update_id, "cxx.link", link::instance); r.insert (perform_clean_id, "cxx.link", link::instance); r.insert (configure_update_id, "cxx.link", link::instance); r.insert (perform_install_id, "cxx.install", install::instance); // Only register static object/library rules if the bin.ar module is // loaded (by us or by the user). // if (cast_false (b["bin.ar.loaded"])) { r.insert (perform_update_id, "cxx.compile", compile::instance); r.insert (perform_clean_id, "cxx.compile", compile::instance); r.insert (configure_update_id, "cxx.compile", compile::instance); r.insert (perform_update_id, "cxx.link", link::instance); r.insert (perform_clean_id, "cxx.link", link::instance); r.insert (configure_update_id, "cxx.link", link::instance); r.insert (perform_install_id, "cxx.install", install::instance); } r.insert (perform_update_id, "cxx.compile", compile::instance); r.insert (perform_clean_id, "cxx.compile", compile::instance); r.insert (configure_update_id, "cxx.compile", compile::instance); r.insert (perform_update_id, "cxx.link", link::instance); r.insert (perform_clean_id, "cxx.link", link::instance); r.insert (configure_update_id, "cxx.link", link::instance); r.insert (perform_install_id, "cxx.install", install::instance); } // Configure "installability" of our target types. // using namespace install; install_path (b, dir_path ("include")); // Into install.include. install_path (b, dir_path ("include")); install_path (b, dir_path ("include")); install_path (b, dir_path ("include")); return true; } } }