aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-11-22 06:27:33 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-11-22 06:27:33 +0200
commitf19959de304afaff2b3d539c9bef1f493ede5fbd (patch)
tree66cbbbea6eb866b59154f8985d60d7d96fc3e3e2
parentcf652392b0d0a88899ac4178851affef43af1631 (diff)
Add support for Objective-C/C++ compilation in cc module
-rw-r--r--NEWS25
-rw-r--r--libbuild2/c/init.cxx47
-rw-r--r--libbuild2/c/init.hxx2
-rw-r--r--libbuild2/c/target.hxx1
-rw-r--r--libbuild2/cc/common.hxx29
-rw-r--r--libbuild2/cc/compile-rule.cxx54
-rw-r--r--libbuild2/cc/compile-rule.hxx2
-rw-r--r--libbuild2/cc/install-rule.cxx6
-rw-r--r--libbuild2/cc/link-rule.cxx79
-rw-r--r--libbuild2/cc/module.cxx3
-rw-r--r--libbuild2/cc/target.cxx19
-rw-r--r--libbuild2/cc/target.hxx16
-rw-r--r--libbuild2/cxx/init.cxx47
-rw-r--r--libbuild2/cxx/init.hxx2
-rw-r--r--libbuild2/cxx/target.cxx18
-rw-r--r--libbuild2/cxx/target.hxx16
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<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<m> ();
+
+ // 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 <typename T>
+ 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 <typename T>
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<h> () :
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<file> ());
+ // 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<const target*, 256>;
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<exe> ())
@@ -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<libue> ())
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<c> () ||
+ else if (p.is_a<c> () ||
+ (x_obj != nullptr && p.is_a<m> ()) ||
// Header-only C library.
(library && p.is_a<h> ()))
{
@@ -997,9 +999,9 @@ namespace build2
//
#if 1
if (!um)
- um = (p.is_a (x_src) ||
- p.is_a<c> () ||
- (x_mod != nullptr && p.is_a (*x_mod)) ||
+ um = (p.is_a (x_src) || p.is_a<c> () ||
+ (x_mod != nullptr && p.is_a (*x_mod)) ||
+ (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a<m> ())) ||
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<c> ())
+ if (mod ||
+ p.is_a (x_src) || p.is_a<c> () ||
+ (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a<m> ())))
{
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<libx> () ||
p.is_a<liba> () ||
@@ -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<hbmix> () &&
cast_false<bool> ((*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<c> ())
+ if (mod
+ ? p1.is_a (*x_mod)
+ : (p1.is_a (x_src) || p1.is_a<c> () ||
+ (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ()))))
{
src = true;
continue; // Check the rest of the prerequisites.
@@ -1888,8 +1895,11 @@ namespace build2
p1.is_a<libx> () ||
p1.is_a<liba> () || p1.is_a<libs> () || p1.is_a<libux> () ||
p1.is_a<bmi> () || p1.is_a<bmix> () ||
- (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) ||
- (p.is_a<c> () && p1.is_a<h> ()))
+ ((mod ||
+ p.is_a (x_src) ||
+ (x_obj != nullptr && p.is_a (*x_obj))) && x_header (p1)) ||
+ ((p.is_a<c> () ||
+ (x_obj != nullptr && p.is_a<m> ())) && p1.is_a<h> ()))
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<c> ())
+ if (mod
+ ? p1.is_a (*x_mod)
+ : (p1.is_a (x_src) || p1.is_a<c> () ||
+ (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ()))))
{
// 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<m>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<m_ext_def>,
+ &target_pattern_var<m_ext_def>,
+ 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<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<mm> ();
+
+ // 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 <libbuild2/cxx/target.hxx>
-#include <libbuild2/context.hxx>
-
-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<mm>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<mm_ext_def>,
+ &target_pattern_var<mm_ext_def>,
+ 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;
+ };
}
}