aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/c/init.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/c/init.cxx')
-rw-r--r--libbuild2/c/init.cxx382
1 files changed, 336 insertions, 46 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx
index 227afb2..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,21 +101,31 @@ 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).
//
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;
}
}
@@ -108,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));
@@ -123,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.
@@ -149,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,
@@ -175,14 +287,19 @@ namespace build2
vp.insert<strings> ("config.c.loptions"),
vp.insert<strings> ("config.c.aoptions"),
vp.insert<strings> ("config.c.libs"),
- nullptr /* config.c.translatable_headers */,
+
+ // See config.cxx.internal.scope for details.
+ //
+ vp.insert<string> ("config.c.internal.scope"),
+
+ nullptr /* config.c.translate_include */,
vp.insert<process_path_ex> ("c.path"),
vp.insert<strings> ("c.mode"),
vp.insert<path> ("c.config.path"),
vp.insert<strings> ("c.config.mode"),
vp.insert<dir_paths> ("c.sys_lib_dirs"),
- vp.insert<dir_paths> ("c.sys_inc_dirs"),
+ vp.insert<dir_paths> ("c.sys_hdr_dirs"),
vp.insert<string> ("c.std"),
@@ -192,7 +309,10 @@ namespace build2
vp.insert<strings> ("c.aoptions"),
vp.insert<strings> ("c.libs"),
- nullptr /* c.translatable_headers */,
+ vp.insert<string> ("c.internal.scope"),
+ vp.insert<strings> ("c.internal.libs"),
+
+ nullptr /* c.translate_include */,
vp["cc.poptions"],
vp["cc.coptions"],
@@ -204,13 +324,16 @@ namespace build2
vp.insert<strings> ("c.export.coptions"),
vp.insert<strings> ("c.export.loptions"),
vp.insert<vector<name>> ("c.export.libs"),
- vp.insert<vector<name>> ("c.export.imp_libs"),
+ vp.insert<vector<name>> ("c.export.impl_libs"),
vp["cc.export.poptions"],
vp["cc.export.coptions"],
vp["cc.export.loptions"],
vp["cc.export.libs"],
- vp["cc.export.imp_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.
@@ -220,7 +343,9 @@ namespace build2
vp["cc.type"],
vp["cc.system"],
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).
@@ -259,7 +384,11 @@ namespace build2
// Alias some cc. variables as c.
//
- vp.insert_alias (d.c_runtime, "c.runtime");
+ 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);
@@ -291,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,
@@ -331,39 +447,207 @@ 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,
+ cm.x_info->variant_version ? cm.x_info->variant_version->major : 0,
+ cm.x_info->variant_version ? cm.x_info->variant_version->minor : 0,
cast<process_path> (rs[cm.x_path]),
cast<strings> (rs[cm.x_mode]),
cast<target_triplet> (rs[cm.x_target]),
+ cm.env_checksum,
false, // No C modules yet.
false, // No __symexport support since no modules.
+ cm.iscope,
+ cm.iscope_current,
+
+ cast_null<strings> (rs["cc.internal.libs"]),
+ cast_null<strings> (rs[cm.x_internal_libs]),
+
cast<dir_paths> (rs[cm.x_sys_lib_dirs]),
- cast<dir_paths> (rs[cm.x_sys_inc_dirs]),
+ cast<dir_paths> (rs[cm.x_sys_hdr_dirs]),
cm.x_info->sys_mod_dirs ? &cm.x_info->sys_mod_dirs->first : nullptr,
cm.sys_lib_dirs_mode,
- cm.sys_inc_dirs_mode,
+ cm.sys_hdr_dirs_mode,
cm.sys_mod_dirs_mode,
cm.sys_lib_dirs_extra,
- cm.sys_inc_dirs_extra,
+ cm.sys_hdr_dirs_extra,
c::static_type,
nullptr, // No C modules yet.
+ c_inc::static_type,
hdr,
inc
};
- auto& m (extra.set_module (new module (move (d))));
- m.init (rs, loc, extra.hints);
+ 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;
}
@@ -373,10 +657,16 @@ namespace build2
// 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*