// file : libbuild2/c/init.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // 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) : config_data (move (d)), cc::config_module (move (d)) {} virtual strings translate_std (const compiler_info&, scope&, const string*) const override; }; using cc::module; strings config_module:: translate_std (const compiler_info& ci, scope& rs, const string* v) const { strings r; 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. r.push_back (move (o)); } break; } } return r; } 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, unique_ptr& mod, bool, bool, const variable_map& hints) { 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. // if (!cast_false (rs["cc.core.vars.loaded"])) load_module (rs, rs, "cc.core.vars", loc); // Enter all the variables and initialize the module data. // auto& v (rs.ctx.var_pool.rw (rs)); cc::config_data d { cc::lang::c, "c", "c", BUILD2_DEFAULT_C, ".i", hinters, // Note: some overridable, some not. // // NOTE: remember to update documentation if changing anything here. // v.insert ("config.c", true), v.insert ("config.c.id", true), v.insert ("config.c.version", true), v.insert ("config.c.target", true), v.insert ("config.c.std", true), v.insert ("config.c.poptions", true), v.insert ("config.c.coptions", true), v.insert ("config.c.loptions", true), v.insert ("config.c.aoptions", true), v.insert ("config.c.libs", true), nullptr /* config.c.translatable_headers */, v.insert ("c.path"), v.insert ("c.mode"), v.insert ("c.sys_lib_dirs"), v.insert ("c.sys_inc_dirs"), v.insert ("c.std", variable_visibility::project), v.insert ("c.poptions"), v.insert ("c.coptions"), v.insert ("c.loptions"), v.insert ("c.aoptions"), v.insert ("c.libs"), nullptr /* c.translatable_headers */, v["cc.poptions"], v["cc.coptions"], v["cc.loptions"], v["cc.aoptions"], v["cc.libs"], v.insert ("c.export.poptions"), v.insert ("c.export.coptions"), v.insert ("c.export.loptions"), v.insert> ("c.export.libs"), v["cc.export.poptions"], v["cc.export.coptions"], v["cc.export.loptions"], v["cc.export.libs"], v.insert_alias (v["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. v["cc.runtime"], v["cc.stdlib"], v["cc.type"], v["cc.system"], v["cc.module_name"], v["cc.reprocess"], v.insert ("c.preprocessed"), // See cxx.preprocessed. nullptr, // No __symexport (no modules). v.insert ("c.id"), v.insert ("c.id.type"), v.insert ("c.id.variant"), v.insert ("c.class"), &v.insert ("c.version"), &v.insert ("c.version.major"), &v.insert ("c.version.minor"), &v.insert ("c.version.patch"), &v.insert ("c.version.build"), &v.insert ("c.variant_version"), &v.insert ("c.variant_version.major"), &v.insert ("c.variant_version.minor"), &v.insert ("c.variant_version.patch"), &v.insert ("c.variant_version.build"), v.insert ("c.signature"), v.insert ("c.checksum"), v.insert ("c.pattern"), v.insert ("c.target"), v.insert ("c.target.cpu"), v.insert ("c.target.vendor"), v.insert ("c.target.system"), v.insert ("c.target.version"), v.insert ("c.target.class") }; // Alias some cc. variables as c. // v.insert_alias (d.c_runtime, "c.runtime"); assert (mod == nullptr); config_module* m (new config_module (move (d))); mod.reset (m); m->guess (rs, loc, hints); return true; } bool config_init (scope& rs, scope& bs, const location& loc, unique_ptr&, bool, bool, const variable_map& hints) { 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. // if (!cast_false (rs["c.guess.loaded"])) load_module (rs, rs, "c.guess", loc, false, hints); config_module& cm (*rs.lookup_module ("c.guess")); cm.init (rs, loc, 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, unique_ptr& mod, bool, bool, const variable_map& hints) { 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. // if (!cast_false (rs["c.config.loaded"])) load_module (rs, rs, "c.config", loc, false, hints); config_module& cm (*rs.lookup_module ("c.guess")); 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]), cm.tstd, 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.sys_lib_dirs_extra, cm.sys_inc_dirs_extra, c::static_type, nullptr, // No C modules yet. hdr, inc }; assert (mod == nullptr); module* m; mod.reset (m = new module (move (d))); m->init (rs, loc, 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; } } }