// file : build2/cc/module.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include // left, setw() #include #include #include #include #include #include #include using namespace std; using namespace butl; namespace build2 { namespace cc { void config_module:: init (scope& rs, const location& loc, const variable_map&) { tracer trace (x, "config_init"); bool cc_loaded (cast_false (rs["cc.core.config.loaded"])); // Configure. // compiler_info ci; // For program patterns. // Adjust module priority (compiler). Also order cc module before us // (we don't want to use priorities for that in case someone manages // to slot in-between). // if (!cc_loaded) config::save_module (rs, "cc", 250); config::save_module (rs, x, 250); const variable& config_c_coptions (var_pool["config.cc.coptions"]); // config.x // // Normally we will have a persistent configuration and computing the // default value every time will be a waste. So try without a default // first. // auto p (config::omitted (rs, config_x)); if (!p.first) { // If someone already loaded cc.core.config then use its toolchain // id and (optional) pattern to guess an appropriate default (e.g., // for {gcc, *-4.9} we will get g++-4.9). // path d (cc_loaded ? guess_default (x_lang, cast (rs["cc.id"]), cast_null (rs["cc.pattern"])) : path (x_default)); // If this value was hinted, save it as commented out so that if the // user changes the source of the pattern, this one will get updated // as well. // p = config::required (rs, config_x, d, false, cc_loaded ? config::save_commented : 0); } // Figure out which compiler we are dealing with, its target, etc. // const path& xc (cast (*p.first)); ci = guess (x_lang, xc, cast_null (rs[config_c_coptions]), cast_null (rs[config_x_coptions])); // Split/canonicalize the target. First see if the user asked us to // use config.sub. // target_triplet tt; { string ct; if (ops.config_sub_specified ()) { ct = run (ops.config_sub (), ci.target.c_str (), [] (string& l) {return move (l);}); l5 ([&]{trace << "config.sub target: '" << ct << "'";}); } try { tt = target_triplet (ct.empty () ? ci.target : ct); l5 ([&]{trace << "canonical target: '" << tt.string () << "'; " << "class: " << tt.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 << info << "consider using the --config-sub option"; } } // Translate x_std value (if any) to the compiler option(s) (if any). // tstd = translate_std (ci, rs, cast_null (rs[x_std])); // Extract system library search paths from the compiler and determine // additional system include search paths. // dir_paths lib_dirs; dir_paths inc_dirs; if (ci.id.value () == compiler_id::msvc) lib_dirs = msvc_library_search_paths (ci.path, rs); else { lib_dirs = gcc_library_search_paths (ci.path, rs); #ifndef _WIN32 // Many platforms don't search in /usr/local/lib by default (but do // for headers in /usr/local/include). So add it as the last option. // lib_dirs.push_back (dir_path ("/usr/local/lib")); // FreeBSD is at least consistent: it searches in neither. Quoting its // wiki: "FreeBSD can't even find libraries that it installed." So // let's help it a bit. Note that we don't add it for all the // platforms for good measure because this will actually appear as // usually unnecessary noise on the command line. // if (tt.system == "freebsd") inc_dirs.push_back (dir_path ("/usr/local/include")); #endif } // 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)) { diag_record dr (text); { dr << x << ' ' << project (rs) << '@' << rs.out_path () << '\n' << " " << left << setw (11) << x << ci.path << '\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'; } if (!ci.version.build.empty ()) { dr << " build " << ci.version.build << '\n'; } { const string& ct (tt.string ()); // Canonical target. dr << " signature " << ci.signature << '\n' << " target " << ct; if (ct != ci.target) dr << " (" << ci.target << ")"; dr << '\n'; } if (!tstd.empty ()) { dr << " std "; // One less space. for (const string& o: tstd) dr << ' ' << o; dr << '\n'; } if (!ci.cc_pattern.empty ()) // bin_pattern printed by bin { dr << " pattern " << ci.cc_pattern << '\n'; } if (verb >= 3 && !inc_dirs.empty ()) { dr << " inc dirs\n"; for (const dir_path& d: inc_dirs) dr << " " << d << '\n'; } if (verb >= 3 && !lib_dirs.empty ()) { dr << " lib dirs\n"; for (const dir_path& d: lib_dirs) dr << " " << d << '\n'; } { dr << " checksum " << ci.checksum; } } rs.assign (x_path) = move (ci.path); rs.assign (x_sys_lib_dirs) = move (lib_dirs); rs.assign (x_sys_inc_dirs) = move (inc_dirs); cid = ci.id.value (); rs.assign (x_id) = ci.id.string (); rs.assign (x_id_type) = move (ci.id.type); rs.assign (x_id_variant) = move (ci.id.variant); rs.assign (x_version) = move (ci.version.string); rs.assign (x_version_major) = ci.version.major; rs.assign (x_version_minor) = ci.version.minor; rs.assign (x_version_patch) = ci.version.patch; rs.assign (x_version_build) = move (ci.version.build); rs.assign (x_signature) = move (ci.signature); rs.assign (x_checksum) = move (ci.checksum); // Also enter as x.target.{cpu,vendor,system,version,class} for // convenience of access. // rs.assign (x_target_cpu) = tt.cpu; rs.assign (x_target_vendor) = tt.vendor; rs.assign (x_target_system) = tt.system; rs.assign (x_target_version) = tt.version; rs.assign (x_target_class) = tt.class_; rs.assign (x_target) = move (tt); // config.x.{p,c,l}options // config.x.libs // // 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 = # Note: '='. // using x // x.coptions += # Note: '+='. // rs.assign (x_poptions) += cast_null ( config::optional (rs, config_x_poptions)); rs.assign (x_coptions) += cast_null ( config::optional (rs, config_x_coptions)); rs.assign (x_loptions) += cast_null ( config::optional (rs, config_x_loptions)); rs.assign (x_libs) += cast_null ( config::optional (rs, config_x_libs)); // Load cc.core.config. // if (!cc_loaded) { // Prepare configuration hints. // variable_map h; // Note that all these variables have already been registered. // h.assign ("config.cc.id") = cast (rs[x_id]); h.assign ("config.cc.target") = cast (rs[x_target]); if (!ci.cc_pattern.empty ()) h.assign ("config.cc.pattern") = move (ci.cc_pattern); if (!ci.bin_pattern.empty ()) h.assign ("config.bin.pattern") = move (ci.bin_pattern); load_module (rs, rs, "cc.core.config", loc, false, h); } else { // If cc.core.config is already loaded, verify its configuration // matched ours since it could have been loaded by another c-family // module. // // Note that we don't require that patterns match. Presumably, if the // toolchain id and target are the same, then where exactly the tools // come from doesn't really matter. // { const auto& cv (cast (rs["cc.id"])); const auto& xv (cast (rs[x_id])); if (cv != xv) fail (loc) << "cc and " << x << " module toolchain mismatch" << info << "cc.id is " << cv << info << x_id.name << " is " << xv; } { const auto& cv (cast (rs["cc.target"])); const auto& xv (cast (rs[x_target])); if (cv != xv) fail (loc) << "cc and " << x << " module target mismatch" << info << "cc.target is " << cv << info << x_target.name << " is " << xv; } } } void module:: init (scope& rs, const location& loc, 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 (rs["cc.core.loaded"])) load_module (rs, rs, "cc.core", loc); // Register target types and configure their "installability". // bool install_loaded (cast_false (rs["install.loaded"])); { using namespace install; auto& t (rs.target_types); t.insert (x_src); // Note: module (x_mod) is in x_hdr. for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) { t.insert (**ht); // Install headers into install.include. // if (install_loaded) install_path (rs, **ht, dir_path ("include")); } } // Register rules. // { using namespace bin; auto& r (rs.rules); // We register for configure so that we detect unresolved imports // during configuration rather that later, e.g., during update. // const compile& cr (*this); const link& lr (*this); r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); if (modules) { r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); r.insert (configure_update_id, x_compile, cr); } r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); // Note that while libu*{} are not installable, we need to see through // them in case they depend on stuff that we need to install (see the // install rule implementations for details). // if (install_loaded) { const file_install& fr (*this); const alias_install& ar (*this); r.insert (perform_install_id, x_install, fr); r.insert (perform_uninstall_id, x_uninstall, fr); r.insert (perform_install_id, x_install, fr); r.insert (perform_uninstall_id, x_uninstall, fr); r.insert (perform_install_id, x_install, fr); r.insert (perform_uninstall_id, x_uninstall, fr); r.insert (perform_install_id, x_install, ar); r.insert (perform_uninstall_id, x_uninstall, ar); r.insert (perform_install_id, x_install, ar); r.insert (perform_uninstall_id, x_uninstall, ar); r.insert (perform_install_id, x_install, ar); r.insert (perform_uninstall_id, x_uninstall, ar); } } } } }