diff options
Diffstat (limited to 'libbuild2/c/init.cxx')
-rw-r--r-- | libbuild2/c/init.cxx | 338 |
1 files changed, 302 insertions, 36 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index be001a8..8bc2f7d 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -6,9 +6,12 @@ #include <libbuild2/scope.hxx> #include <libbuild2/diagnostics.hxx> +#include <libbuild2/install/utility.hxx> + #include <libbuild2/cc/guess.hxx> #include <libbuild2/cc/module.hxx> +#include <libbuild2/cc/target.hxx> // pc* #include <libbuild2/c/target.hxx> #ifndef BUILD2_DEFAULT_C @@ -27,6 +30,7 @@ namespace build2 namespace c { using cc::compiler_id; + using cc::compiler_type; using cc::compiler_class; using cc::compiler_info; @@ -53,6 +57,26 @@ namespace build2 strings& mode, const string* v) const { + // The standard is `NN` but can also be `gnuNN`. + + // This helper helps recognize both NN and [cC]NN to avoid an endless + // stream of user questions. It can also be used to recognize Nx in + // addition to NN (e.g., "23" and "2x"). + // + auto stdcmp = [v] (const char* nn, const char* nx = nullptr) + { + if (v != nullptr) + { + const char* s (v->c_str ()); + if (s[0] == 'c' || s[0] == 'C') + s += 1; + + return strcmp (s, nn) == 0 || (nx != nullptr && strcmp (s, nx) == 0); + } + + return false; + }; + switch (ci.class_) { case compiler_class::msvc: @@ -77,7 +101,12 @@ namespace build2 // 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. + // And it's still early days for C2X. Specifically, there is not + // much about C2X in MSVC in the official places and the following + // page shows that it's pretty much unimplement at the time of the + // MSVC 17.6 release: + // + // https://en.cppreference.com/w/c/compiler_support/23 // // From version 16.8 VC now supports /std:c11 and /std:c17 options // which enable C11/17 conformance. However, as of version 16.10, @@ -86,17 +115,17 @@ namespace build2 // if (v == nullptr) ; - else if (*v != "90") + else if (!stdcmp ("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" )) + if ((stdcmp ("99") && cver < 16) || // Since VS2010/10.0. + ((stdcmp ("11") || + stdcmp ("17") || + stdcmp ("18")) && cver < 18) || // Since VS????/11.0. + (stdcmp ("23", "2x") )) { - fail << "C" << *v << " is not supported by " << ci.signature << + fail << "C " << *v << " is not supported by " << ci.signature << info << "required by " << project (rs) << '@' << rs; } } @@ -113,12 +142,12 @@ namespace build2 { 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"; + if (stdcmp ("23", "2x")) o += "c2x"; // GCC 9, Clang 9 (8?). + else if (stdcmp ("17") || + stdcmp ("18")) o += "c17"; // GCC 8, Clang 6. + else if (stdcmp ("11")) o += "c1x"; + else if (stdcmp ("99")) o += "c9x"; + else if (stdcmp ("90")) o += "c90"; else o += *v; // In case the user specifies `gnuNN` or some such. mode.insert (mode.begin (), move (o)); @@ -128,6 +157,79 @@ namespace build2 } } + // See cc::data::x_{hdr,inc} for background. + // + static const target_type* const hdr[] = + { + &h::static_type, + nullptr + }; + + // Note that we include S{} here because .S files can include each other. + // (And maybe from inline assembler instructions?) + // + static const target_type* const inc[] = + { + &h::static_type, + &c::static_type, + &m::static_type, + &S::static_type, + &c_inc::static_type, + nullptr + }; + + bool + types_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::types_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.types module must be loaded in project root"; + + // Register target types and configure their "installability". + // + using namespace install; + + bool install_loaded (cast_false<bool> (rs["install.loaded"])); + + // Note: not registering m{} or S{} (they are registered seperately + // by the respective optional .types submodules). + // + rs.insert_target_type<c> (); + + auto insert_hdr = [&rs, install_loaded] (const target_type& tt) + { + rs.insert_target_type (tt); + + // Install headers into install.include. + // + if (install_loaded) + install_path (rs, tt, dir_path ("include")); + }; + + for (const target_type* const* ht (hdr); *ht != nullptr; ++ht) + insert_hdr (**ht); + + // @@ PERF: maybe factor this to cc.types? + // + rs.insert_target_type<cc::pc> (); + rs.insert_target_type<cc::pca> (); + rs.insert_target_type<cc::pcs> (); + + if (install_loaded) + install_path<cc::pc> (rs, dir_path ("pkgconfig")); + + return true; + } + static const char* const hinters[] = {"cxx", nullptr}; // See cc::module for details on guess_init vs config_init. @@ -154,15 +256,20 @@ namespace build2 // Enter all the variables and initialize the module data. // - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); cc::config_data d { cc::lang::c, "c", "c", + "obj-c", BUILD2_DEFAULT_C, ".i", + ".mi", hinters, @@ -225,6 +332,9 @@ namespace build2 vp["cc.export.libs"], vp["cc.export.impl_libs"], + vp["cc.pkgconfig.include"], + vp["cc.pkgconfig.lib"], + vp.insert_alias (vp["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. vp["cc.runtime"], @@ -235,6 +345,7 @@ namespace build2 vp["cc.module_name"], vp["cc.importable"], vp["cc.reprocess"], + vp["cc.serialize"], vp.insert<string> ("c.preprocessed"), // See cxx.preprocessed. nullptr, // No __symexport (no modules). @@ -276,6 +387,9 @@ namespace build2 vp.insert_alias (d.c_runtime, "c.runtime"); vp.insert_alias (d.c_importable, "c.importable"); + vp.insert_alias (d.c_pkgconfig_include, "c.pkgconfig.include"); + vp.insert_alias (d.c_pkgconfig_lib, "c.pkgconfig.lib"); + auto& m (extra.set_module (new config_module (move (d)))); m.guess (rs, loc, extra.hints); @@ -306,19 +420,6 @@ namespace build2 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, @@ -346,10 +447,8 @@ namespace build2 "c.compile", "c.link", "c.install", - "c.uninstall", - cm.x_info->id.type, - cm.x_info->id.variant, + cm.x_info->id, cm.x_info->class_, cm.x_info->version.major, cm.x_info->version.minor, @@ -382,25 +481,192 @@ namespace build2 c::static_type, nullptr, // No C modules yet. + c_inc::static_type, hdr, inc }; - auto& m (extra.set_module (new module (move (d)))); + auto& m (extra.set_module (new module (move (d), rs))); m.init (rs, loc, extra.hints, *cm.x_info); return true; } + bool + objc_types_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::objc_types_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.objc.types module must be loaded in project root"; + + // Register the m{} target type. + // + rs.insert_target_type<m> (); + + return true; + } + + bool + objc_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::objc_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.objc module must be loaded in project root"; + + module* mod (rs.find_module<module> ("c")); + + if (mod == nullptr) + fail (loc) << "c.objc module must be loaded after c module"; + + // Register the target type and "enable" it in the module. + // + // Note that we must register the target type regardless of whether the + // C compiler is capable of compiling Objective-C. But we enable only + // if it is. + // + // Note: see similar code in the cxx module. + // + load_module (rs, rs, "c.objc.types", loc); + + // Note that while Objective-C is supported by MinGW GCC, it's unlikely + // Clang supports it when targeting MSVC or Emscripten. But let's keep + // the check simple for now. + // + if (mod->ctype == compiler_type::gcc || + mod->ctype == compiler_type::clang) + mod->x_obj = &m::static_type; + + return true; + } + + bool + as_cpp_types_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::as_cpp_types_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.as-cpp.types module must be loaded in project root"; + + // Register the S{} target type. + // + rs.insert_target_type<S> (); + + return true; + } + + bool + as_cpp_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::as_cpp_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.as-cpp module must be loaded in project root"; + + module* mod (rs.find_module<module> ("c")); + + if (mod == nullptr) + fail (loc) << "c.as-cpp module must be loaded after c module"; + + // Register the target type and "enable" it in the module. + // + // Note that we must register the target type regardless of whether the + // C compiler is capable of compiling Assember with C preprocessor. But + // we enable only if it is. + // + load_module (rs, rs, "c.as-cpp.types", loc); + + if (mod->ctype == compiler_type::gcc || + mod->ctype == compiler_type::clang) + mod->x_asp = &S::static_type; + + return true; + } + + bool + predefs_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("c::predefs_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "c.predefs module must be loaded in project root"; + + module* mod (rs.find_module<module> ("c")); + + if (mod == nullptr) + fail (loc) << "c.predefs module must be loaded after c module"; + + // Register the c.predefs rule. + // + // Why invent a separate module instead of just always registering it in + // the c module? The reason is performance: this rule will be called for + // every C header. + // + cc::predefs_rule& r (*mod); + + rs.insert_rule<h> (perform_update_id, r.rule_name, r); + rs.insert_rule<h> (perform_clean_id, r.rule_name, r); + rs.insert_rule<h> (configure_update_id, r.rule_name, r); + + 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} + {"c.types", nullptr, types_init}, + {"c.guess", nullptr, guess_init}, + {"c.config", nullptr, config_init}, + {"c.objc.types", nullptr, objc_types_init}, + {"c.objc", nullptr, objc_init}, + {"c.as-cpp.types", nullptr, as_cpp_types_init}, + {"c.as-cpp", nullptr, as_cpp_init}, + {"c.predefs", nullptr, predefs_init}, + {"c", nullptr, init}, + {nullptr, nullptr, nullptr} }; const module_functions* |