// file : libbuild2/c/init.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include #ifndef BUILD2_DEFAULT_C # ifdef BUILD2_NATIVE_C # define BUILD2_DEFAULT_C BUILD2_NATIVE_C # else # define BUILD2_DEFAULT_C "" # endif #endif using namespace std; using namespace butl; namespace build2 { namespace c { using cc::compiler_id; using cc::compiler_class; using cc::compiler_info; class config_module: public cc::config_module { public: explicit config_module (config_data&& d): cc::config_module (move (d)) {} virtual void translate_std (const compiler_info&, const target_triplet&, scope&, strings&, const string*) const override; }; using cc::module; void config_module:: translate_std (const compiler_info& ci, const target_triplet&, scope& rs, strings& mode, const string* v) const { switch (ci.class_) { case compiler_class::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. // // C17/18 is a bug-fix version of C11 so here we assume it is the // same as C11. // // And it's still early days for C2X. // if (v == nullptr) ; else if (*v != "90") { uint64_t cver (ci.version.major); if ((*v == "99" && cver < 16) || // Since VS2010/10.0. ((*v == "11" || *v == "17" || *v == "18") && cver < 18) || (*v == "2x" )) { fail << "C" << *v << " is not supported by " << ci.signature << info << "required by " << project (rs) << '@' << rs; } } break; } case compiler_class::gcc: { // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x // for compatibility with older versions of the compilers. // if (v == nullptr) ; else { string o ("-std="); if (*v == "2x") o += "c2x"; // GCC 9, Clang 9 (8?). else if (*v == "17" || *v == "18") o += "c17"; // GCC 8, Clang 6. else if (*v == "11") o += "c1x"; else if (*v == "99") o += "c9x"; else if (*v == "90") o += "c90"; else o += *v; // In case the user specifies `gnuNN` or some such. mode.insert (mode.begin (), move (o)); } break; } } } static const char* const hinters[] = {"cxx", nullptr}; // See cc::module for details on guess_init vs config_init. // bool guess_init (scope& rs, scope& bs, const location& loc, bool, bool, module_init_extra& extra) { tracer trace ("c::guess_init"); l5 ([&]{trace << "for " << bs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "c.guess module must be loaded in project root"; // Load cc.core.vars so that we can cache all the cc.* variables. // load_module (rs, rs, "cc.core.vars", loc); // Enter all the variables and initialize the module data. // auto& vp (rs.var_pool ()); cc::config_data d { cc::lang::c, "c", "c", BUILD2_DEFAULT_C, ".i", hinters, // NOTE: remember to update documentation if changing anything here. // vp.insert ("config.c"), vp.insert ("config.c.id"), vp.insert ("config.c.version"), vp.insert ("config.c.target"), vp.insert ("config.c.std"), vp.insert ("config.c.poptions"), vp.insert ("config.c.coptions"), vp.insert ("config.c.loptions"), vp.insert ("config.c.aoptions"), vp.insert ("config.c.libs"), nullptr /* config.c.translatable_headers */, vp.insert ("c.path"), vp.insert ("c.mode"), vp.insert ("c.sys_lib_dirs"), vp.insert ("c.sys_inc_dirs"), vp.insert ("c.std"), vp.insert ("c.poptions"), vp.insert ("c.coptions"), vp.insert ("c.loptions"), vp.insert ("c.aoptions"), vp.insert ("c.libs"), nullptr /* c.translatable_headers */, vp["cc.poptions"], vp["cc.coptions"], vp["cc.loptions"], vp["cc.aoptions"], vp["cc.libs"], vp.insert ("c.export.poptions"), vp.insert ("c.export.coptions"), vp.insert ("c.export.loptions"), vp.insert> ("c.export.libs"), vp.insert> ("c.export.imp_libs"), vp["cc.export.poptions"], vp["cc.export.coptions"], vp["cc.export.loptions"], vp["cc.export.libs"], vp["cc.export.imp_libs"], vp.insert_alias (vp["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. vp["cc.runtime"], vp["cc.stdlib"], vp["cc.type"], vp["cc.system"], vp["cc.module_name"], vp["cc.reprocess"], vp.insert ("c.preprocessed"), // See cxx.preprocessed. nullptr, // No __symexport (no modules). vp.insert ("c.id"), vp.insert ("c.id.type"), vp.insert ("c.id.variant"), vp.insert ("c.class"), &vp.insert ("c.version"), &vp.insert ("c.version.major"), &vp.insert ("c.version.minor"), &vp.insert ("c.version.patch"), &vp.insert ("c.version.build"), &vp.insert ("c.variant_version"), &vp.insert ("c.variant_version.major"), &vp.insert ("c.variant_version.minor"), &vp.insert ("c.variant_version.patch"), &vp.insert ("c.variant_version.build"), vp.insert ("c.signature"), vp.insert ("c.checksum"), vp.insert ("c.pattern"), vp.insert ("c.target"), vp.insert ("c.target.cpu"), vp.insert ("c.target.vendor"), vp.insert ("c.target.system"), vp.insert ("c.target.version"), vp.insert ("c.target.class") }; // Alias some cc. variables as c. // vp.insert_alias (d.c_runtime, "c.runtime"); auto& m (extra.set_module (new config_module (move (d)))); m.guess (rs, loc, extra.hints); return true; } bool config_init (scope& rs, scope& bs, const location& loc, bool, bool, module_init_extra& extra) { tracer trace ("c::config_init"); l5 ([&]{trace << "for " << bs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "c.config module must be loaded in project root"; // Load c.guess and share its module instance as ours. // extra.module = load_module (rs, rs, "c.guess", loc, extra.hints); extra.module_as ().init (rs, loc, extra.hints); return true; } static const target_type* const hdr[] = { &h::static_type, nullptr }; static const target_type* const inc[] = { &h::static_type, &c::static_type, nullptr }; bool init (scope& rs, scope& bs, const location& loc, bool, bool, module_init_extra& extra) { tracer trace ("c::init"); l5 ([&]{trace << "for " << bs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "c module must be loaded in project root"; // Load c.config. // auto& cm ( load_module (rs, rs, "c.config", loc, extra.hints)); cc::data d { cm, "c.compile", "c.link", "c.install", "c.uninstall", cm.x_info->id.type, cm.x_info->id.variant, cm.x_info->class_, cm.x_info->version.major, cm.x_info->version.minor, cast (rs[cm.x_path]), cast (rs[cm.x_mode]), cast (rs[cm.x_target]), false, // No C modules yet. false, // No __symexport support since no modules. cast (rs[cm.x_sys_lib_dirs]), cast (rs[cm.x_sys_inc_dirs]), cm.x_info->sys_mod_dirs ? &cm.x_info->sys_mod_dirs->first : nullptr, cm.sys_lib_dirs_mode, cm.sys_inc_dirs_mode, cm.sys_mod_dirs_mode, cm.sys_lib_dirs_extra, cm.sys_inc_dirs_extra, c::static_type, nullptr, // No C modules yet. hdr, inc }; auto& m (extra.set_module (new module (move (d)))); m.init (rs, loc, extra.hints); return true; } static const module_functions mod_functions[] = { // NOTE: don't forget to also update the documentation in init.hxx if // changing anything here. {"c.guess", nullptr, guess_init}, {"c.config", nullptr, config_init}, {"c", nullptr, init}, {nullptr, nullptr, nullptr} }; const module_functions* build2_c_load () { return mod_functions; } } }