// file : libbuild2/c/init.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <libbuild2/c/init.hxx> #include <libbuild2/scope.hxx> #include <libbuild2/diagnostics.hxx> #include <libbuild2/cc/guess.hxx> #include <libbuild2/cc/module.hxx> #include <libbuild2/c/target.hxx> #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 is a bug-fix version of C11 so here we assume it is the same // as C11. // 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") && cver < 17)) // Since VS2012/11.0. { 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 == "90") o += "c90"; else if (*v == "99") o += "c9x"; else if (*v == "11") o += "c1x"; else if (*v == "17") o += "c17"; // GCC 8, Clang 6. else o += *v; // In case the user specifies e.g., 'gnu11'. 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<module_base>& 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<bool> (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. // v.insert<path> ("config.c", true), v.insert<string> ("config.c.id", true), v.insert<string> ("config.c.version", true), v.insert<string> ("config.c.target", true), v.insert<string> ("config.c.std", true), v.insert<strings> ("config.c.poptions", true), v.insert<strings> ("config.c.coptions", true), v.insert<strings> ("config.c.loptions", true), v.insert<strings> ("config.c.aoptions", true), v.insert<strings> ("config.c.libs", true), nullptr /* config.c.translatable_headers */, v.insert<process_path> ("c.path"), v.insert<dir_paths> ("c.sys_lib_dirs"), v.insert<dir_paths> ("c.sys_inc_dirs"), v.insert<string> ("c.std", variable_visibility::project), v.insert<strings> ("c.poptions"), v.insert<strings> ("c.coptions"), v.insert<strings> ("c.loptions"), v.insert<strings> ("c.aoptions"), v.insert<strings> ("c.libs"), nullptr /* c.translatable_headers */, v["cc.poptions"], v["cc.coptions"], v["cc.loptions"], v["cc.aoptions"], v["cc.libs"], v.insert<strings> ("c.export.poptions"), v.insert<strings> ("c.export.coptions"), v.insert<strings> ("c.export.loptions"), v.insert<vector<name>> ("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<string> ("c.preprocessed"), // See cxx.preprocessed. nullptr, // No __symexport (no modules). v.insert<string> ("c.id"), v.insert<string> ("c.id.type"), v.insert<string> ("c.id.variant"), v.insert<string> ("c.class"), v.insert<string> ("c.version"), v.insert<uint64_t> ("c.version.major"), v.insert<uint64_t> ("c.version.minor"), v.insert<uint64_t> ("c.version.patch"), v.insert<string> ("c.version.build"), v.insert<string> ("c.signature"), v.insert<string> ("c.checksum"), v.insert<string> ("c.pattern"), v.insert<target_triplet> ("c.target"), v.insert<string> ("c.target.cpu"), v.insert<string> ("c.target.vendor"), v.insert<string> ("c.target.system"), v.insert<string> ("c.target.version"), v.insert<string> ("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<module_base>&, 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<bool> (rs["c.guess.loaded"])) load_module (rs, rs, "c.guess", loc, false, hints); config_module& cm (*rs.lookup_module<config_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<module_base>& 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<bool> (rs["c.config.loaded"])) load_module (rs, rs, "c.config", loc, false, hints); config_module& cm (*rs.lookup_module<config_module> ("c.guess")); cc::data d { cm, "c.compile", "c.link", "c.install", "c.uninstall", cm.ci_->id.type, cm.ci_->id.variant, cm.ci_->class_, cm.ci_->version.major, cm.ci_->version.minor, cast<process_path> (rs[cm.x_path]), cast<target_triplet> (rs[cm.x_target]), cm.tstd, false, // No C modules yet. false, // No __symexport support since no modules. cast<dir_paths> (rs[cm.x_sys_lib_dirs]), cast<dir_paths> (rs[cm.x_sys_inc_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; } } }