diff options
Diffstat (limited to 'libbuild2/c')
-rw-r--r-- | libbuild2/c/init.cxx | 349 | ||||
-rw-r--r-- | libbuild2/c/init.hxx | 19 | ||||
-rw-r--r-- | libbuild2/c/target.hxx | 3 |
3 files changed, 328 insertions, 43 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index 4c051b0..f100abc 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,28 +101,41 @@ 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, // neither SDK nor CRT can be compiled in these modes (see the /std - // option documentation for details/updates). + // option documentation for details/updates). There is also now + // /std:clatest which can be used to enable C23 typeof as of MSVC + // 17.9. So let's map C23 to that. // 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" )) + uint64_t mj (ci.version.major); + uint64_t mi (ci.version.minor); + + if (stdcmp ("99") && mj >= 16) // Since VS2010/10.0. + ; + else if ((stdcmp ("11") || + stdcmp ("17") || + stdcmp ("18")) && mj >= 18) // Since VS????/11.0. + ; + else if (stdcmp ("23", "2x") && + (mj > 19 || (mj == 19 && mi >= 39))) // Since 17.9. { - fail << "C" << *v << " is not supported by " << ci.signature << - info << "required by " << project (rs) << '@' << rs; + mode.insert (mode.begin (), "/std:clatest"); } + else + fail << "C " << *v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs; } break; } @@ -113,12 +150,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 +165,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 +264,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,8 +340,8 @@ namespace build2 vp["cc.export.libs"], vp["cc.export.impl_libs"], - vp["cc.pkconfig.include"], - vp["cc.pkconfig.lib"], + vp["cc.pkgconfig.include"], + vp["cc.pkgconfig.lib"], vp.insert_alias (vp["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib. @@ -238,6 +353,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). @@ -312,19 +428,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, @@ -353,8 +456,7 @@ namespace build2 "c.link", "c.install", - 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, @@ -387,6 +489,7 @@ namespace build2 c::static_type, nullptr, // No C modules yet. + c_inc::static_type, hdr, inc }; @@ -397,15 +500,181 @@ namespace build2 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* diff --git a/libbuild2/c/init.hxx b/libbuild2/c/init.hxx index 2662bb1..38515c1 100644 --- a/libbuild2/c/init.hxx +++ b/libbuild2/c/init.hxx @@ -19,9 +19,22 @@ namespace build2 // // Submodules: // - // `c.guess` -- registers and sets some variables. - // `c.config` -- loads c.guess and sets more variables. - // `c` -- loads c.config and registers target types and rules. + // `c.types` -- registers target types. + // `c.guess` -- registers and sets some variables. + // `c.config` -- loads c.guess and sets more variables. + // `c` -- loads c.{types,config} and registers rules and + // functions. + // + // `c.objc.types` -- registers m{} target type. + // `c.objc` -- loads c.objc.types and enables Objective-C + // compilation. Must be loaded after c. + // + // `c.as-cpp.types` -- registers S{} target type. + // `c.as-cpp` -- loads c.as-cpp.types and enables Assembler with C + // preprocessor compilation. Must be loaded after c. + // + // `c.predefs` -- registers rule for generating a C header with + // predefined compiler macros. Must be loaded after c. // extern "C" LIBBUILD2_C_SYMEXPORT const module_functions* build2_c_load (); diff --git a/libbuild2/c/target.hxx b/libbuild2/c/target.hxx index 333d39f..c9955e3 100644 --- a/libbuild2/c/target.hxx +++ b/libbuild2/c/target.hxx @@ -15,6 +15,9 @@ namespace build2 { using cc::h; using cc::c; + using cc::m; + using cc::S; + using cc::c_inc; } } |