// file : build2/cc/init.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include using namespace std; using namespace butl; namespace build2 { namespace cc { bool core_vars_init (scope& rs, scope&, const location& loc, unique_ptr&, bool first, bool, const variable_map&) { tracer trace ("cc::core_vars_init"); l5 ([&]{trace << "for " << rs.out_path ();}); assert (first); // Load bin.vars (we need its config.bin.target/pattern for hints). // if (!cast_false (rs["bin.vars.loaded"])) load_module (rs, rs, "bin.vars", loc); // Enter variables. Note: some overridable, some not. // auto& v (var_pool.rw (rs)); v.insert ("config.cc.poptions", true); v.insert ("config.cc.coptions", true); v.insert ("config.cc.loptions", true); v.insert ("config.cc.libs", true); v.insert ("cc.poptions"); v.insert ("cc.coptions"); v.insert ("cc.loptions"); v.insert ("cc.libs"); v.insert ("cc.export.poptions"); v.insert ("cc.export.coptions"); v.insert ("cc.export.loptions"); v.insert> ("cc.export.libs"); // Hint variables (not overridable). // v.insert ("config.cc.id"); v.insert ("config.cc.pattern"); v.insert ("config.cc.target"); // Target type, for example, "C library" or "C++ library". Should be set // on the target by the matching rule to the name of the module (e.g., // "c", "cxx"). Currenly only set for libraries and is used to decide // which *.libs to use during static linking. // // It can also be the special "cc" value which means a C-common library // but specific language is not known. Used in import installed logic. // v.insert ("cc.type"); // If set and is true, then this (imported) library has been found in a // system library search directory. // v.insert ("cc.system"); // C++ module name. Should be set on the bmi{} target by the matching // rule. // v.insert ("cc.module_name"); // Ability to disable using preprocessed output for compilation. // v.insert ("config.cc.reprocess", true); v.insert ("cc.reprocess"); return true; } bool core_config_init (scope& rs, scope&, const location& loc, unique_ptr&, bool first, bool, const variable_map& hints) { tracer trace ("cc::core_config_init"); l5 ([&]{trace << "for " << rs.out_path ();}); assert (first); auto& vp (var_pool.rw (rs)); // Load cc.core.vars. // if (!cast_false (rs["cc.core.vars.loaded"])) load_module (rs, rs, "cc.core.vars", loc); // Configure. // // Adjust module priority (compiler). // config::save_module (rs, "cc", 250); // config.cc.id // { // This value must be hinted. // rs.assign ("cc.id") = cast (hints["config.cc.id"]); } // config.cc.target // { // This value must be hinted. // const auto& t (cast (hints["config.cc.target"])); // Also enter as cc.target.{cpu,vendor,system,version,class} for // convenience of access. // rs.assign ("cc.target.cpu") = t.cpu; rs.assign ("cc.target.vendor") = t.vendor; rs.assign ("cc.target.system") = t.system; rs.assign ("cc.target.version") = t.version; rs.assign ("cc.target.class") = t.class_; rs.assign ("cc.target") = t; } // config.cc.pattern // { // This value could be hinted. // if (auto l = hints["config.cc.pattern"]) rs.assign ("cc.pattern") = cast (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. // // rs.assign ("cc.poptions") += cast_null ( config::optional (rs, "config.cc.poptions")); rs.assign ("cc.coptions") += cast_null ( config::optional (rs, "config.cc.coptions")); rs.assign ("cc.loptions") += cast_null ( config::optional (rs, "config.cc.loptions")); rs.assign ("cc.libs") += cast_null ( config::optional (rs, "config.cc.libs")); if (lookup l = config::omitted (rs, "config.cc.reprocess").first) rs.assign ("cc.reprocess") = *l; // Load the bin.config module. // if (!cast_false (rs["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) { // Note that all these variables have already been registered. // h.assign ("config.bin.target") = cast (rs["cc.target"]).string (); if (auto l = hints["config.bin.pattern"]) h.assign ("config.bin.pattern") = cast (l); } load_module (rs, rs, "bin.config", 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 auto& ct (cast (rs["cc.target"])); const auto& bt (cast (rs["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 (rs["cc.id"])); const string& tsys (cast (rs["cc.target.system"])); // Load bin.*.config for bin.* modules we may need (see core_init() // below). // if (!cast_false (rs["bin.ar.config.loaded"])) load_module (rs, rs, "bin.ar.config", loc); if (cid == "msvc") { if (!cast_false (rs["bin.ld.config.loaded"])) load_module (rs, rs, "bin.ld.config", loc); } if (tsys == "mingw32") { if (!cast_false (rs["bin.rc.config.loaded"])) load_module (rs, rs, "bin.rc.config", loc); } // Load (optionally) the pkgconfig module. Note that it registers the // pc{} target whether the pkg-config utility is found or not. // // @@ At some point we may also want to verify that targets matched // if it has already been loaded (by someone) else. Currently it // doesn't set pkgconfig.target. Perhaps only set if it was used // to derive the program name? // if (!cast_false (rs["pkgconfig.loaded"])) { // Prepare configuration hints. // variable_map h; // Note that this variable has not yet been registered. // const variable& t (vp.insert ("config.pkgconfig.target")); h.assign (t) = cast (rs["cc.target"]); load_module (rs, rs, "pkgconfig", loc, true, h); } return true; } bool core_init (scope& rs, scope&, const location& loc, unique_ptr&, bool first, bool, const variable_map& hints) { tracer trace ("cc::core_init"); l5 ([&]{trace << "for " << rs.out_path ();}); assert (first); // Load cc.core.config. // if (!cast_false (rs["cc.core.config.loaded"])) load_module (rs, rs, "cc.core.config", loc, false, hints); // Load the bin module. // if (!cast_false (rs["bin.loaded"])) load_module (rs, rs, "bin", loc); const string& cid (cast (rs["cc.id"])); const string& tsys (cast (rs["cc.target.system"])); // Load the bin.ar module. // if (!cast_false (rs["bin.ar.loaded"])) load_module (rs, rs, "bin.ar", loc); // In the VC world you link things directly with link.exe so load the // bin.ld module. // if (cid == "msvc") { if (!cast_false (rs["bin.ld.loaded"])) load_module (rs, rs, "bin.ld", 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 (rs["bin.rc.loaded"])) load_module (rs, rs, "bin.rc", loc); } return true; } // The cc module is an "alias" for c and cxx. Its intended use is to make // sure that the C/C++ configuration is captured in an amalgamation rather // than subprojects. // static inline bool init_alias (tracer& trace, scope& rs, scope& bs, const char* m, const char* c, const char* c_loaded, const char* cxx, const char* cxx_loaded, const location& loc, const variable_map& hints) { l5 ([&]{trace << "for " << bs.out_path ();}); // We only support root loading (which means there can only be one). // if (&rs != &bs) fail (loc) << m << " module must be loaded in project root"; // 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 (rs[c_loaded])); bool lp (!cast_false (rs[cxx_loaded])); // If none of them are already loaded, load c first only if config.c // is specified. // if (lc && lp && rs["config.c"]) { load_module (rs, rs, c, loc, false, hints); load_module (rs, rs, cxx, loc, false, hints); } else { if (lp) load_module (rs, rs, cxx, loc, false, hints); if (lc) load_module (rs, rs, c, loc, false, hints); } return true; } bool config_init (scope& rs, scope& bs, const location& loc, unique_ptr&, bool, bool, const variable_map& hints) { tracer trace ("cc::config_init"); return init_alias (trace, rs, bs, "cc.config", "c.config", "c.config.loaded", "cxx.config", "cxx.config.loaded", loc, hints); } bool init (scope& rs, scope& bs, const location& loc, unique_ptr&, bool, bool, const variable_map& hints) { tracer trace ("cc::init"); return init_alias (trace, rs, bs, "cc", "c", "c.loaded", "cxx", "cxx.loaded", loc, hints); } } }