From f19959de304afaff2b3d539c9bef1f493ede5fbd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 22 Nov 2022 06:27:33 +0200 Subject: Add support for Objective-C/C++ compilation in cc module --- NEWS | 25 ++++++++++++++ libbuild2/c/init.cxx | 47 +++++++++++++++++++++++++ libbuild2/c/init.hxx | 2 ++ libbuild2/c/target.hxx | 1 + libbuild2/cc/common.hxx | 29 +++++++++++++--- libbuild2/cc/compile-rule.cxx | 54 +++++++++++++++++------------ libbuild2/cc/compile-rule.hxx | 2 +- libbuild2/cc/install-rule.cxx | 6 ++-- libbuild2/cc/link-rule.cxx | 79 +++++++++++++++++++++++++------------------ libbuild2/cc/module.cxx | 3 ++ libbuild2/cc/target.cxx | 19 ++++++++--- libbuild2/cc/target.hxx | 16 +++++++++ libbuild2/cxx/init.cxx | 47 +++++++++++++++++++++++++ libbuild2/cxx/init.hxx | 2 ++ libbuild2/cxx/target.cxx | 18 +++++++--- libbuild2/cxx/target.hxx | 16 +++++++++ 16 files changed, 294 insertions(+), 72 deletions(-) diff --git a/NEWS b/NEWS index c71f9bd..ef9a8fd 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,30 @@ Version 0.16.0 + * Support for Objective-C/C++ compilation. + + Specifically, the c and cxx modules now provide the c.objc and cxx.objcxx + submodules which can be loaded in order to register the m{}/mm{} target + types and enable Objective-C/C++ compilation in the c and cxx compile + rules. Note that c.objc and cxx.objcxx must be loaded after the c and cxx + modules, respectively, and while the m{}/mm{} target types are registered + unconditionally, compilation is only enabled if the C/C++ compiler + supports Objective-C/C++ for this target platform. Typical usage: + + # root.build + # + using cxx + using cxx.objcxx + + # buildfile + # + lib{hello}: {hxx cxx}{*} + lib{hello}: mm{*}: include = ($cxx.target.class == 'macos') + + Note also that while there is support for linking Objective-C/C++ + executables and libraries, this is done using the C/C++ compiler driver + and no attempt to automatically link any necessary Objective-C runtime + (such as -lobjc) is made. + * Low verbosity diagnostics rework. The low verbosity (level 1) rule diagnostics format has been adjusted to diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index 0b6c85b..2dbd534 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -27,6 +27,7 @@ namespace build2 namespace c { using cc::compiler_id; + using cc::compiler_type; using cc::compiler_class; using cc::compiler_info; @@ -164,8 +165,10 @@ namespace build2 "c", "c", + "obj-c", BUILD2_DEFAULT_C, ".i", + ".mi", hinters, @@ -325,6 +328,7 @@ namespace build2 { &h::static_type, &c::static_type, + &m::static_type, nullptr }; @@ -400,6 +404,48 @@ namespace build2 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 ("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. + // + rs.insert_target_type (); + + // 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; + } + static const module_functions mod_functions[] = { // NOTE: don't forget to also update the documentation in init.hxx if @@ -408,6 +454,7 @@ namespace build2 {"c.guess", nullptr, guess_init}, {"c.config", nullptr, config_init}, {"c", nullptr, init}, + {"c.objc", nullptr, objc_init}, {nullptr, nullptr, nullptr} }; diff --git a/libbuild2/c/init.hxx b/libbuild2/c/init.hxx index 2662bb1..f324c31 100644 --- a/libbuild2/c/init.hxx +++ b/libbuild2/c/init.hxx @@ -22,6 +22,8 @@ namespace build2 // `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.objc` -- registers m{} target type and enables Objective-C + // compilation. // 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..308bda9 100644 --- a/libbuild2/c/target.hxx +++ b/libbuild2/c/target.hxx @@ -15,6 +15,7 @@ namespace build2 { using cc::h; using cc::c; + using cc::m; } } diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index c11fa7a..4ad0e22 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -32,10 +32,12 @@ namespace build2 { lang x_lang; - const char* x; // Module name ("c", "cxx"). - const char* x_name; // Compiler name ("c", "c++"). - const char* x_default; // Compiler default ("gcc", "g++"). - const char* x_pext; // Preprocessed source extension (".i", ".ii"). + const char* x; // Module name ("c", "cxx"). + const char* x_name; // Compiler name ("c", "c++"). + const char* x_obj_name; // Same for Objective-X ("obj-c", "obj-c++"). + const char* x_default; // Compiler default ("gcc", "g++"). + const char* x_pext; // Preprocessed source extension (".i", ".ii"). + const char* x_obj_pext; // Same for Objective-X (".mi", ".mii"). // Array of modules that can hint us the toolchain, terminate with // NULL. @@ -210,8 +212,22 @@ namespace build2 size_t sys_lib_dirs_extra; // First trailing extra entry (size if none). size_t sys_hdr_dirs_extra; + // Note that x_obj is patched in by the x.objx module. So it stays NULL + // if Objective-X compilation is not enabled. + // const target_type& x_src; // Source target type (c{}, cxx{}). const target_type* x_mod; // Module target type (mxx{}), if any. + const target_type* x_obj; // Objective-X target type (m{}, mm{}). + + // Check if an object (target, prerequisite, etc) is an Objective-X + // source. + // + template + bool + x_objective (const T& t) const + { + return x_obj != nullptr && t.is_a (*x_obj); + } // Array of target types that are considered the X-language headers // (excluding h{} except for C). Keep them in the most likely to appear @@ -219,6 +235,8 @@ namespace build2 // const target_type* const* x_hdr; + // Check if an object (target, prerequisite, etc) is a header. + // template bool x_header (const T& t, bool c_hdr = true) const @@ -283,7 +301,8 @@ namespace build2 sys_lib_dirs_mode (slm), sys_hdr_dirs_mode (shm), sys_mod_dirs_mode (smm), sys_lib_dirs_extra (sle), sys_hdr_dirs_extra (she), - x_src (src), x_mod (mod), x_hdr (hdr), x_inc (inc) {} + x_src (src), x_mod (mod), x_obj (nullptr), + x_hdr (hdr), x_inc (inc) {} }; class LIBBUILD2_CC_SYMEXPORT common: public data diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 5693334..6ca1f0a 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -232,7 +232,8 @@ namespace build2 // Note that we don't really need this for clean (where we only need // unrefined unit type) so we could make this update-only. But let's keep - // it simple for now. + // it simple for now. Note that now we do need the source prerequisite + // type in clean to deal with Objective-X. // struct compile_rule::match_data { @@ -369,11 +370,13 @@ namespace build2 case unit_type::non_modular: case unit_type::module_impl: { + bool obj (x_objective (md.src)); + o1 = "-x"; switch (x_lang) { - case lang::c: o2 = "c"; break; - case lang::cxx: o2 = "c++"; break; + case lang::c: o2 = obj ? "objective-c" : "c"; break; + case lang::cxx: o2 = obj ? "objective-c++" : "c++"; break; } break; } @@ -475,7 +478,7 @@ namespace build2 // if (ut == unit_type::module_header ? p.is_a (**x_hdr) || p.is_a () : ut == unit_type::module_intf ? p.is_a (*x_mod) : - p.is_a (x_src)) + p.is_a (x_src) || (x_obj != nullptr && p.is_a (*x_obj))) { // Save in the target's auxiliary storage. // @@ -1530,10 +1533,13 @@ namespace build2 switch (a) { case perform_update_id: return move (md); - case perform_clean_id: return [this] (action a, const target& t) + case perform_clean_id: { - return perform_clean (a, t); - }; + return [this, srct = &md.src.type ()] (action a, const target& t) + { + return perform_clean (a, t, *srct); + }; + } default: return noop_recipe; // Configure update. } } @@ -3012,6 +3018,10 @@ namespace build2 file_cache::entry psrc; bool puse (true); + // Preprocessed file extension. + // + const char* pext (x_objective (src) ? x_obj_pext : x_pext); + // Preprocesor mode that preserves as much information as possible while // still performing inclusions. Also serves as a flag indicating whether // this compiler uses the separate preprocess and compile setup. @@ -3210,7 +3220,7 @@ namespace build2 // Return NULL if the dependency information goes to stdout and a // pointer to the temporary file path otherwise. // - auto init_args = [a, &t, ot, li, reprocess, + auto init_args = [a, &t, ot, li, reprocess, pext, &src, &md, &psrc, &sense_diag, &mod_mapper, &bs, pp, &env, &args, &args_gen, &args_i, &out, &drm, &so_map, this] @@ -3436,7 +3446,7 @@ namespace build2 msvc_sanitize_cl (args); - psrc = ctx.fcache.create (t.path () + x_pext, !modules); + psrc = ctx.fcache.create (t.path () + pext, !modules); if (fc) { @@ -3583,7 +3593,7 @@ namespace build2 // Preprocessor output. // - psrc = ctx.fcache.create (t.path () + x_pext, !modules); + psrc = ctx.fcache.create (t.path () + pext, !modules); args.push_back ("-o"); args.push_back (psrc.path ().string ().c_str ()); } @@ -3878,7 +3888,7 @@ namespace build2 if (modules && (ctype != compiler_type::msvc || md.type != unit_type::module_intf)) { - result.first = ctx.fcache.create_existing (t.path () + x_pext); + result.first = ctx.fcache.create_existing (t.path () + pext); result.second = true; } @@ -7247,7 +7257,7 @@ namespace build2 // @@ TODO: why don't we print env (here and/or below)? Also link rule. // if (verb == 1) - print_diag (x_name, s, t); + print_diag (x_objective (s) ? x_obj_name : x_name, s, t); else if (verb == 2) print_process (args); @@ -7470,25 +7480,25 @@ namespace build2 } target_state compile_rule:: - perform_clean (action a, const target& xt) const + perform_clean (action a, const target& xt, const target_type& srct) const { const file& t (xt.as ()); + // Preprocessed file extension. + // + const char* pext (x_objective (srct) ? x_obj_pext : x_pext); + // Compressed preprocessed file extension. // - auto cpext = [this, &t, s = string ()] () mutable -> const char* - { - return (s = t.ctx.fcache.compressed_extension (x_pext)).c_str (); - }; + string cpext (t.ctx.fcache.compressed_extension (pext)); clean_extras extras; - switch (ctype) { - case compiler_type::gcc: extras = {".d", x_pext, cpext (), ".t"}; break; - case compiler_type::clang: extras = {".d", x_pext, cpext ()}; break; - case compiler_type::msvc: extras = {".d", x_pext, cpext (), ".idb", ".pdb"};break; - case compiler_type::icc: extras = {".d"}; break; + case compiler_type::gcc: extras = {".d", pext, cpext.c_str (), ".t"}; break; + case compiler_type::clang: extras = {".d", pext, cpext.c_str ()}; break; + case compiler_type::msvc: extras = {".d", pext, cpext.c_str (), ".idb", ".pdb"}; break; + case compiler_type::icc: extras = {".d"}; break; } return perform_clean_extra (a, t, extras); diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx index cfe0dd7..a9a22c4 100644 --- a/libbuild2/cc/compile-rule.hxx +++ b/libbuild2/cc/compile-rule.hxx @@ -58,7 +58,7 @@ namespace build2 perform_update (action, const target&, match_data&) const; target_state - perform_clean (action, const target&) const; + perform_clean (action, const target&, const target_type&) const; public: using appended_libraries = small_vector; diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index 5118332..640612c 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -90,7 +90,8 @@ namespace build2 { return (x_header (p) || p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && p.is_a (*x_obj))); }; if (t.is_a ()) @@ -338,7 +339,8 @@ namespace build2 { return (x_header (p) || p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && p.is_a (*x_obj))); }; if (t.is_a ()) diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 24dc373..9bf86e6 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -290,12 +290,14 @@ namespace build2 if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && p.is_a (*x_obj)) || // Header-only X library (or library with C source and X header). (library && x_header (p, false /* c_hdr */))) { r.seen_x = true; } - else if (p.is_a () || + else if (p.is_a () || + (x_obj != nullptr && p.is_a ()) || // Header-only C library. (library && p.is_a ())) { @@ -997,9 +999,9 @@ namespace build2 // #if 1 if (!um) - um = (p.is_a (x_src) || - p.is_a () || - (x_mod != nullptr && p.is_a (*x_mod)) || + um = (p.is_a (x_src) || p.is_a () || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a ())) || x_header (p, true)); #endif @@ -1023,12 +1025,14 @@ namespace build2 // 2 - mod // 3 - obj/bmi and also lib not to be cleaned (and other stuff) // - uint8_t m (0); + uint8_t mk (0); bool mod (x_mod != nullptr && p.is_a (*x_mod)); bool hdr (false); - if (mod || p.is_a (x_src) || p.is_a ()) + if (mod || + p.is_a (x_src) || p.is_a () || + (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a ()))) { binless = binless && (mod ? user_binless : false); @@ -1116,7 +1120,7 @@ namespace build2 } pt = &r.first; - m = mod ? 2 : 1; + mk = mod ? 2 : 1; } else if (p.is_a () || p.is_a () || @@ -1134,7 +1138,7 @@ namespace build2 pt = &p.search (t); if (skip (*pt)) - m = 3; // Mark so it is not matched. + mk = 3; // Mark so it is not matched. // If this is the lib{}/libul{} group, then pick the appropriate // member. Also note this in prerequisite_target::include (used @@ -1206,7 +1210,7 @@ namespace build2 !pt->is_a () && cast_false ((*pt)[b_binless]))); - m = 3; + mk = 3; } if (user_binless && !binless) @@ -1226,18 +1230,18 @@ namespace build2 if (*um) { - if (m != 3) + if (mk != 3) fail << "unable to update during match prerequisite " << p << info << "updating this type of prerequisites during match is " << "not supported by this rule"; - m = 0; + mk = 0; pto.include |= prerequisite_target::include_udm; update_match = true; } } - mark (pt, m); + mark (pt, mk); } // Match lib{} first and then update during match (the only unmarked) in @@ -1711,20 +1715,20 @@ namespace build2 // 1 - completion // 2 - verification // - uint8_t m (unmark (pt)); + uint8_t mk (unmark (pt)); - if (m == 3) // obj/bmi or lib not to be cleaned + if (mk == 3) // obj/bmi or lib not to be cleaned { - m = 1; // Just completion. + mk = 1; // Just completion. // Note that if this is a library not to be cleaned, we keep it // marked for completion (see the next phase). } - else if (m == 1 || m == 2) // Source/module chain. + else if (mk == 1 || mk == 2) // Source/module chain. { - bool mod (m == 2); + bool mod (mk == 2); // p is_a x_mod - m = 1; + mk = 1; const target& rt (*pt); bool group (!p.prerequisite.belongs (t)); // Group's prerequisite. @@ -1875,7 +1879,10 @@ namespace build2 // Most of the time we will have just a single source so fast- // path that case. // - if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a ()) + if (mod + ? p1.is_a (*x_mod) + : (p1.is_a (x_src) || p1.is_a () || + (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a ())))) { src = true; continue; // Check the rest of the prerequisites. @@ -1888,8 +1895,11 @@ namespace build2 p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || - (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || - (p.is_a () && p1.is_a ())) + ((mod || + p.is_a (x_src) || + (x_obj != nullptr && p.is_a (*x_obj))) && x_header (p1)) || + ((p.is_a () || + (x_obj != nullptr && p.is_a ())) && p1.is_a ())) continue; fail << "synthesized dependency for prerequisite " << p @@ -1902,11 +1912,11 @@ namespace build2 if (!src) fail << "synthesized dependency for prerequisite " << p << " would be incompatible with existing target " << *pt << - info << "no existing c/" << x_name << " source prerequisite" << + info << "no existing c/" << x_lang << " source prerequisite" << info << "specify corresponding " << rtt.name << "{} " << "dependency explicitly"; - m = 2; // Needs verification. + mk = 2; // Needs verification. } } else // lib*{} or update during match @@ -1948,7 +1958,7 @@ namespace build2 } } - mark (pt, m); + mark (pt, mk); } // Process prerequisites, pass 3: match everything and verify chains. @@ -1964,7 +1974,7 @@ namespace build2 bool adhoc (pts[i].adhoc ()); const target*& pt (pts[i++]); - uint8_t m; + uint8_t mk; if (pt == nullptr) { @@ -1974,13 +1984,13 @@ namespace build2 continue; pt = &p.search (t); - m = 1; // Mark for completion. + mk = 1; // Mark for completion. } else { - m = unmark (pt); + mk = unmark (pt); - if (m == 0) + if (mk == 0) continue; // Already matched. // If this is a library not to be cleaned, we can finally blank it @@ -1994,7 +2004,7 @@ namespace build2 } match_async (a, *pt, ctx.count_busy (), t[a].task_count); - mark (pt, m); + mark (pt, mk); } wg.wait (); @@ -2009,15 +2019,15 @@ namespace build2 // Skipped or not marked for completion. // - uint8_t m; - if (pt == nullptr || (m = unmark (pt)) == 0) + uint8_t mk; + if (pt == nullptr || (mk = unmark (pt)) == 0) continue; match_complete (a, *pt); // Nothing else to do if not marked for verification. // - if (m == 1) + if (mk == 1) continue; // Finish verifying the existing dependency (which is now matched) @@ -2029,7 +2039,10 @@ namespace build2 for (prerequisite_member p1: group_prerequisite_members (a, *pt)) { - if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a ()) + if (mod + ? p1.is_a (*x_mod) + : (p1.is_a (x_src) || p1.is_a () || + (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a ())))) { // Searching our own prerequisite is ok, p1 must already be // resolved. diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 9063ebc..aa9a526 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -962,6 +962,9 @@ namespace build2 { using namespace install; + // Note: not registering x_obj (it's registered seperately by the + // x.objx module). + // rs.insert_target_type (x_src); auto insert_hdr = [&rs, install_loaded] (const target_type& tt) diff --git a/libbuild2/cc/target.cxx b/libbuild2/cc/target.cxx index 3f71eb1..d743752 100644 --- a/libbuild2/cc/target.cxx +++ b/libbuild2/cc/target.cxx @@ -25,7 +25,6 @@ namespace build2 }; extern const char h_ext_def[] = "h"; - const target_type h::static_type { "h", @@ -40,7 +39,6 @@ namespace build2 }; extern const char c_ext_def[] = "c"; - const target_type c::static_type { "c", @@ -54,8 +52,21 @@ namespace build2 target_type::flag::none }; - extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. + extern const char m_ext_def[] = "m"; + const target_type m::static_type + { + "m", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + target_type::flag::none + }; + extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. const target_type pc::static_type { "pc", @@ -70,7 +81,6 @@ namespace build2 }; extern const char pca_ext[] = "static.pc"; // VC14 rejects constexpr. - const target_type pca::static_type { "pca", @@ -85,7 +95,6 @@ namespace build2 }; extern const char pcs_ext[] = "shared.pc"; // VC14 rejects constexpr. - const target_type pcs::static_type { "pcs", diff --git a/libbuild2/cc/target.hxx b/libbuild2/cc/target.hxx index fbac790..87df326 100644 --- a/libbuild2/cc/target.hxx +++ b/libbuild2/cc/target.hxx @@ -68,6 +68,22 @@ namespace build2 static const target_type static_type; }; + // Objective-C source file (the same rationale for having it here as for + // c{} above). + // + class LIBBUILD2_CC_SYMEXPORT m: public cc + { + public: + m (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; + // pkg-config file targets. // class LIBBUILD2_CC_SYMEXPORT pc: public file // .pc (common) diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx index 5396056..fd6d04c 100644 --- a/libbuild2/cxx/init.cxx +++ b/libbuild2/cxx/init.cxx @@ -479,8 +479,10 @@ namespace build2 "cxx", "c++", + "obj-c++", BUILD2_DEFAULT_CXX, ".ii", + ".mii", hinters, @@ -790,6 +792,8 @@ namespace build2 &mxx::static_type, &cxx::static_type, &c::static_type, + &mm::static_type, + &m::static_type, nullptr }; @@ -877,6 +881,48 @@ namespace build2 return true; } + bool + objcxx_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("cxx::objcxx_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "cxx.objcxx module must be loaded in project root"; + + module* mod (rs.find_module ("cxx")); + + if (mod == nullptr) + fail (loc) << "cxx.objcxx module must be loaded after cxx 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 c module. + // + rs.insert_target_type (); + + // 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 = &mm::static_type; + + return true; + } + static const module_functions mod_functions[] = { // NOTE: don't forget to also update the documentation in init.hxx if @@ -885,6 +931,7 @@ namespace build2 {"cxx.guess", nullptr, guess_init}, {"cxx.config", nullptr, config_init}, {"cxx", nullptr, init}, + {"cxx.objcxx", nullptr, objcxx_init}, {nullptr, nullptr, nullptr} }; diff --git a/libbuild2/cxx/init.hxx b/libbuild2/cxx/init.hxx index 094fea4..0e42cbe 100644 --- a/libbuild2/cxx/init.hxx +++ b/libbuild2/cxx/init.hxx @@ -22,6 +22,8 @@ namespace build2 // `cxx.guess` -- registers and sets some variables. // `cxx.config` -- loads cxx.guess and sets more variables. // `cxx` -- loads cxx.config and registers target types and rules. + // `cxx.objcxx` -- registers mm{} target type and enables Objective-C++ + // compilation. // extern "C" LIBBUILD2_CXX_SYMEXPORT const module_functions* build2_cxx_load (); diff --git a/libbuild2/cxx/target.cxx b/libbuild2/cxx/target.cxx index fc50f67..5ead620 100644 --- a/libbuild2/cxx/target.cxx +++ b/libbuild2/cxx/target.cxx @@ -3,10 +3,6 @@ #include -#include - -using namespace std; - namespace build2 { namespace cxx @@ -80,5 +76,19 @@ namespace build2 &file_search, target_type::flag::none }; + + extern const char mm_ext_def[] = "mm"; + const target_type mm::static_type + { + "mm", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + target_type::flag::none + }; } } diff --git a/libbuild2/cxx/target.hxx b/libbuild2/cxx/target.hxx index b20bf00..fc85f75 100644 --- a/libbuild2/cxx/target.hxx +++ b/libbuild2/cxx/target.hxx @@ -18,6 +18,7 @@ namespace build2 { using cc::h; using cc::c; + using cc::m; class LIBBUILD2_CXX_SYMEXPORT hxx: public cc::cc { @@ -88,6 +89,21 @@ namespace build2 public: static const target_type static_type; }; + + // Objective-C++ source file. + // + class LIBBUILD2_CXX_SYMEXPORT mm: public cc::cc + { + public: + mm (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; } } -- cgit v1.1