From ac87e3827b214c0ba1bd1c17a2d8a18026637d3f Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 26 Aug 2019 12:06:03 +0300 Subject: Move cxx build system module to separate library --- bootstrap-mingw.bat | 2 +- bootstrap-msvc.bat | 2 +- bootstrap.gmake | 7 +- bootstrap.sh | 2 +- build2/b.cxx | 8 +- build2/buildfile | 11 +- build2/cli/init.cxx | 2 +- build2/cli/target.hxx | 2 +- build2/cxx/init.cxx | 629 ------------------------------------------- build2/cxx/init.hxx | 46 ---- build2/cxx/target.cxx | 85 ------ build2/cxx/target.hxx | 78 ------ libbuild2/buildfile | 8 +- libbuild2/cxx/buildfile | 90 +++++++ libbuild2/cxx/export.hxx | 38 +++ libbuild2/cxx/init.cxx | 646 +++++++++++++++++++++++++++++++++++++++++++++ libbuild2/cxx/init.hxx | 32 +++ libbuild2/cxx/target.cxx | 85 ++++++ libbuild2/cxx/target.hxx | 80 ++++++ libbuild2/module.cxx | 1 + tests/libbuild2/buildfile | 2 +- tests/libbuild2/driver.cxx | 2 + 22 files changed, 989 insertions(+), 869 deletions(-) delete mode 100644 build2/cxx/init.cxx delete mode 100644 build2/cxx/init.hxx delete mode 100644 build2/cxx/target.cxx delete mode 100644 build2/cxx/target.hxx create mode 100644 libbuild2/cxx/buildfile create mode 100644 libbuild2/cxx/export.hxx create mode 100644 libbuild2/cxx/init.cxx create mode 100644 libbuild2/cxx/init.hxx create mode 100644 libbuild2/cxx/target.cxx create mode 100644 libbuild2/cxx/target.hxx diff --git a/bootstrap-mingw.bat b/bootstrap-mingw.bat index 43cc84f..128bf58 100644 --- a/bootstrap-mingw.bat +++ b/bootstrap-mingw.bat @@ -61,7 +61,6 @@ if "_%libbutl%_" == "__" ( rem All the source directories. rem set "src=build2" -set "src=%src% build2\cxx" set "src=%src% libbuild2" set "src=%src% libbuild2\config" @@ -72,6 +71,7 @@ set "src=%src% libbuild2\install" set "src=%src% libbuild2\bin" set "src=%src% libbuild2\c" set "src=%src% libbuild2\cc" +set "src=%src% libbuild2\cxx" set "src=%src% libbuild2\version" set "src=%src% libbuild2\in" diff --git a/bootstrap-msvc.bat b/bootstrap-msvc.bat index 8da8769..1c860a2 100644 --- a/bootstrap-msvc.bat +++ b/bootstrap-msvc.bat @@ -92,7 +92,6 @@ if "_%libbutl%_" == "__" ( rem All the source directories. rem set "src=build2" -set "src=%src% build2\cxx" set "src=%src% libbuild2" set "src=%src% libbuild2\config" @@ -103,6 +102,7 @@ set "src=%src% libbuild2\install" set "src=%src% libbuild2\bin" set "src=%src% libbuild2\c" set "src=%src% libbuild2\cc" +set "src=%src% libbuild2\cxx" set "src=%src% libbuild2\version" set "src=%src% libbuild2\in" diff --git a/bootstrap.gmake b/bootstrap.gmake index 0e3126e..0587004 100644 --- a/bootstrap.gmake +++ b/bootstrap.gmake @@ -129,9 +129,6 @@ endif # # Note: list nested subdirectories first (used in clean). # -build2_sub := \ -cxx - libbuild2_sub := \ config \ dist \ @@ -141,11 +138,11 @@ install \ bin \ c \ cc \ +cxx \ version \ in build2_src := $(wildcard $(src_root)/build2/*.cxx) -build2_src += $(foreach d,$(build2_sub),$(wildcard $(src_root)/build2/$d/*.cxx)) libbuild2_src := $(wildcard $(src_root)/libbuild2/*.cxx) libbuild2_src += $(foreach d,$(libbuild2_sub),$(wildcard $(src_root)/libbuild2/$d/*.cxx)) libbutl_src := $(wildcard $(libbutl)/libbutl/*.cxx) @@ -196,5 +193,5 @@ cleano: clean: cleano rm -f $(out_root)/build2/b-boot$(exe) ifeq ($(in_tree),false) - rm -fd $(foreach d,$(build2_sub),$(out_root)/build2/$d) $(out_root)/build2 $(foreach d,$(libbuild2_sub),$(out_root)/libbuild2/$d) $(out_root)/libbuild2 $(libbutl_out) + rm -fd $(out_root)/build2 $(foreach d,$(libbuild2_sub),$(out_root)/libbuild2/$d) $(out_root)/libbuild2 $(libbutl_out) endif diff --git a/bootstrap.sh b/bootstrap.sh index 368a549..753f538 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -117,7 +117,6 @@ if test -z "$libbutl"; then fi src="build2/*.cxx" -src="$src build2/cxx/*.cxx" src="$src libbuild2/*.cxx" src="$src libbuild2/config/*.cxx" @@ -128,6 +127,7 @@ src="$src libbuild2/install/*.cxx" src="$src libbuild2/bin/*.cxx" src="$src libbuild2/c/*.cxx" src="$src libbuild2/cc/*.cxx" +src="$src libbuild2/cxx/*.cxx" src="$src libbuild2/version/*.cxx" src="$src libbuild2/in/*.cxx" diff --git a/build2/b.cxx b/build2/b.cxx index 1fd6b5a..a4e799c 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -56,6 +56,7 @@ #include #include #include +#include #include #ifndef BUILD2_BOOTSTRAP @@ -64,8 +65,6 @@ # include #endif -#include - using namespace butl; using namespace std; @@ -522,13 +521,10 @@ main (int argc, char* argv[]) load (&bin::build2_bin_load); load (&cc::build2_cc_load); load (&c::build2_c_load); + load (&cxx::build2_cxx_load); load (&version::build2_version_load); load (&in::build2_in_load); - TMP_LOAD (cxx_guess, "cxx.guess", cxx::guess_init); - TMP_LOAD (cxx_config, "cxx.config", cxx::config_init); - TMP_LOAD (cxx, "cxx", cxx::init); - #ifndef BUILD2_BOOTSTRAP TMP_LOAD (cli_config, "cli.config", cli::config_init); TMP_LOAD (cli, "cli", cli::init); diff --git a/build2/buildfile b/build2/buildfile index 7f4187e..fd9cec6 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -7,7 +7,7 @@ import libs = libbutl%lib{butl} include ../libbuild2/ libs += ../libbuild2/lib{build2} -for m: bash bin c cc in version +for m: bash bin c cc cxx in version { include ../libbuild2/$m/ libs += ../libbuild2/$m/lib{build2-$m} @@ -43,15 +43,6 @@ for t: cxx{**.test...} # obj{b}: cxx.poptions += -DBUILD2_HOST_TRIPLET=\"$cxx.target\" -# Pass native C and C++ compiler paths (not forgetting to escape backslashes -# on Windows). These are used as defaults for BUILD2_DEFAULT_*. -# -if ($cxx.target == $build.host) -{ - cxx/obj{init}: cxx.poptions += \ - -DBUILD2_NATIVE_CXX=\"$regex.replace($recall($cxx.path), '\\', '\\\\')\" -} - if ($cxx.target.class != 'windows') { if ($cxx.target.class == 'linux') diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx index 3aebe8e..7b7a519 100644 --- a/build2/cli/init.cxx +++ b/build2/cli/init.cxx @@ -11,7 +11,7 @@ #include -#include +#include #include #include diff --git a/build2/cli/target.hxx b/build2/cli/target.hxx index c6aa266..cf66a44 100644 --- a/build2/cli/target.hxx +++ b/build2/cli/target.hxx @@ -10,7 +10,7 @@ #include -#include +#include namespace build2 { diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx deleted file mode 100644 index fefa7b9..0000000 --- a/build2/cxx/init.cxx +++ /dev/null @@ -1,629 +0,0 @@ -// file : build2/cxx/init.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include - -#include -#include - -#include - -#ifndef BUILD2_DEFAULT_CXX -# ifdef BUILD2_NATIVE_CXX -# define BUILD2_DEFAULT_CXX BUILD2_NATIVE_CXX -# else -# define BUILD2_DEFAULT_CXX "" -# endif -#endif - -using namespace std; -using namespace butl; - -namespace build2 -{ - namespace cxx - { - using cc::compiler_id; - using cc::compiler_type; - using cc::compiler_class; - using cc::compiler_info; - - class config_module: public cc::config_module - { - public: - explicit - config_module (config_data&& d) - : config_data (move (d)), cc::config_module (move (d)) {} - - virtual strings - translate_std (const compiler_info&, - scope&, - const string*) const override; - }; - - using cc::module; - - strings config_module:: - translate_std (const compiler_info& ci, scope& rs, const string* v) const - { - strings r; - - compiler_type ct (ci.id.type); - compiler_class cl (ci.class_); - uint64_t mj (ci.version.major); - uint64_t mi (ci.version.minor); - uint64_t p (ci.version.patch); - - // Features. - // - auto enter = [&rs] (const char* v) -> const variable& - { - return rs.ctx.var_pool.rw (rs).insert ( - v, variable_visibility::project); - }; - - // NOTE: see also module sidebuild subproject if changing anything about - // modules here. - - //bool concepts (false); auto& v_c (enter ("cxx.features.concepts")); - bool modules (false); auto& v_m (enter ("cxx.features.modules")); - - // Translate "latest" and "experimental" to the compiler/version- - // appropriate option(s). Experimental is normally like latest with - // extra stuff enabled via additional flags. Otherwise translate the - // standard value. - // - bool experimental ( v != nullptr && *v == "experimental"); - bool latest (experimental || (v != nullptr && *v == "latest")); - - string o; - - switch (cl) - { - case compiler_class::msvc: - { - // C++ standard-wise, with VC you got what you got up until 14.2. - // Starting with 14.3 there is now the /std: switch which defaults - // to c++14 but can be set to c++latest. And from 15.3 it can be - // c++17. - // - // The question is also whether we should verify that the requested - // standard is provided by this VC version. And if so, from which - // version should we say VC supports 11, 14, and 17? We should - // probably be as loose as possible here since the author will - // always be able to tighten (but not loosen) this in the buildfile - // (i.e., detect unsupported versions). - // - // For now we are not going to bother doing this for C++03. - // - if (latest) - { - if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) - o = "/std:c++latest"; - } - else if (v == nullptr) - ; - else if (*v != "98" && *v != "03") - { - bool sup (false); - - if (*v == "11") // C++11 since VS2010/10.0. - { - sup = mj >= 16; - } - else if (*v == "14") // C++14 since VS2015/14.0. - { - sup = mj >= 19; - } - else if (*v == "17") // C++17 since VS2015/14.0u2. - { - // Note: the VC15 compiler version is 19.10. - // - sup = (mj > 19 || - (mj == 19 && (mi > 0 || (mi == 0 && p >= 23918)))); - } - - if (!sup) - fail << "C++" << *v << " is not supported by " << ci.signature << - info << "required by " << project (rs) << '@' << rs; - - if (mj > 19 || (mj == 19 && mi >= 11)) // 15.3 - { - if (*v == "14") o = "/std:c++14"; - else if (*v == "17") o = "/std:c++17"; - } - else if (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215))) // 14.3 - { - if (*v == "14") o = "/std:c++14"; - else if (*v == "17") o = "/std:c++latest"; - } - } - - if (!o.empty ()) - r.push_back (move (o)); - - break; - } - case compiler_class::gcc: - { - if (latest) - { - switch (ct) - { - case compiler_type::gcc: - { - if (mj >= 8) o = "-std=c++2a"; // 20 - else if (mj >= 5) o = "-std=c++1z"; // 17 - else if (mj == 4 && mi >= 8) o = "-std=c++1y"; // 14 - else if (mj == 4 && mi >= 4) o = "-std=c++0x"; // 11 - - break; - } - case compiler_type::clang: - { - // Remap Apple versions to vanilla Clang based on the - // following release point. Note that Apple no longer - // discloses the mapping so it's a guesswork and we try to be - // conservative. For details see: - // - // https://gist.github.com/yamaya/2924292 - // - // 5.1 -> 3.4 - // 6.0 -> 3.5 - // 7.0 -> 3.7 - // 7.3 -> 3.8 - // 8.0 -> 3.9 - // 9.0 -> 4.0 (later ones could be 5.0) - // 9.1 -> ? - // 10.0 -> ? - // - // Note that this mapping is also used to enable experimental - // features below. - // - if (ci.id.variant == "apple") - { - if (mj >= 9) {mj = 4; mi = 0;} - else if (mj == 8) {mj = 3; mi = 9;} - else if (mj == 7 && mi >= 3) {mj = 3; mi = 8;} - else if (mj == 7) {mj = 3; mi = 7;} - else if (mj == 6) {mj = 3; mi = 5;} - else if (mj == 5 && mi >= 1) {mj = 3; mi = 4;} - else {mj = 3; mi = 0;} - } - - if (mj >= 5) o = "-std=c++2a"; - else if (mj > 3 || (mj == 3 && mi >= 5)) o = "-std=c++1z"; - else if (mj == 3 && mi >= 4) o = "-std=c++1y"; - else /* ??? */ o = "-std=c++0x"; - - break; - } - case compiler_type::icc: - { - if (mj >= 17) o = "-std=c++1z"; - else if (mj > 15 || (mj == 15 && p >= 3)) o = "-std=c++1y"; - else /* ??? */ o = "-std=c++0x"; - - break; - } - default: - assert (false); - } - } - else if (v == nullptr) - ; - else - { - // Translate 11 to 0x, 14 to 1y, 17 to 1z, and 20 to 2a for - // compatibility with older versions of the compilers. - // - o = "-std="; - - if (*v == "98") o += "c++98"; - else if (*v == "03") o += "c++03"; - else if (*v == "11") o += "c++0x"; - else if (*v == "14") o += "c++1y"; - else if (*v == "17") o += "c++1z"; - else if (*v == "20") o += "c++2a"; - else o += *v; // In case the user specifies e.g., 'gnu++17'. - } - - if (!o.empty ()) - r.push_back (move (o)); - - break; - } - } - - if (experimental) - { - switch (ct) - { - case compiler_type::msvc: - { - // Starting with 15.5 (19.12) Visual Studio-created projects - // default to the strict mode. However, this flag currently tends - // to trigger too many compiler bugs. So for now we leave it - // to the experimenters to enjoy. - // - if (mj > 19 || (mj == 19 && mi >= 12)) - r.push_back ("/permissive-"); - - break; - } - default: - break; - } - - // Unless disabled by the user, try to enable C++ modules. Here we use - // a tri-state: - // - // - false - disabled - // - unspecified - enabled if practically usable - // - true - enabled even if practically unusable - // - lookup l; - if (!(l = rs[v_m]) || cast (l)) - { - switch (ct) - { - case compiler_type::msvc: - { - // While modules are supported in VC15.0 (19.10), there is a bug - // in separate interface/implementation unit support which makes - // them pretty much unusable. This has been fixed in VC15.3 - // (19.11). And VC15.5 (19.12) supports the 'export module M;' - // syntax. - // - if (mj > 19 || (mj == 19 && mi >= (l ? 10 : 12))) - { - r.push_back ( - mj > 19 || mi > 11 - ? "/D__cpp_modules=201704" // p0629r0 (export module M;) - : "/D__cpp_modules=201703"); // n4647 ( module M;) - - r.push_back ("/experimental:module"); - modules = true; - } - break; - } - case compiler_type::gcc: - { - // We now use extended/experimental module mapper support which - // is currently only available in our c++-modules-ex branch. - // But let's allow forcing it to plain c++-modules in case - // things got merged, etc. - // - if (mj >= 10 && - ci.version.build.find (l - ? "c++-modules" - : "c++-modules-ex") != string::npos) - { - // Currently defines __cpp_modules=201810 which is said to - // correspond to p1103 (merged modules). - // - r.push_back ("-fmodules-ts"); - modules = true; - } - break; - } - case compiler_type::clang: - { - // Enable starting with Clang 6.0.0. - // - // Note that we are using Apple to vanilla Clang version re-map - // from above so may need to update things there as well. - // - // Also see Clang modules support hack in cc::compile. - // - // @@ Clang 9 enables modules by default in C++2a. We should - // probably reflect this in the modules value. - // - if (mj >= 6) - { - r.push_back ("-D__cpp_modules=201704"); // p0629r0 - r.push_back ("-fmodules-ts"); - modules = true; - } - break; - } - case compiler_type::icc: - break; // No modules support yet. - } - } - } - - rs.assign (v_m) = modules; - //rs.assign (v_c) = concepts; - - return r; - } - - static const char* const hinters[] = {"c", nullptr}; - - // See cc::module for details on guess_init vs config_init. - // - bool - guess_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr& mod, - bool, - bool, - const variable_map& hints) - { - tracer trace ("cxx::guess_init"); - l5 ([&]{trace << "for " << bs;}); - - // We only support root loading (which means there can only be one). - // - if (&rs != &bs) - fail (loc) << "cxx.guess module must be loaded in project root"; - - // Load cc.core.vars so that we can cache all the cc.* variables. - // - if (!cast_false (rs["cc.core.vars.loaded"])) - load_module (rs, rs, "cc.core.vars", loc); - - // Enter all the variables and initialize the module data. - // - auto& v (rs.ctx.var_pool.rw (rs)); - - cc::config_data d { - cc::lang::cxx, - - "cxx", - "c++", - BUILD2_DEFAULT_CXX, - ".ii", - - hinters, - - // Note: some overridable, some not. - // - v.insert ("config.cxx", true), - v.insert ("config.cxx.id", true), - v.insert ("config.cxx.version", true), - v.insert ("config.cxx.target", true), - v.insert ("config.cxx.std", true), - v.insert ("config.cxx.poptions", true), - v.insert ("config.cxx.coptions", true), - v.insert ("config.cxx.loptions", true), - v.insert ("config.cxx.aoptions", true), - v.insert ("config.cxx.libs", true), - - // List of importable headers. Inclusion of such headers is translated - // to the corresponding header unit imports. - // - // A header can be specified either as an absolute and normalized path - // or as a <>-style include name. The latter kind is automatically - // translated to the absolute form based on the compiler's system - // header search paths (as opposed to -I). Note also that all entries - // must be specified before loading the cxx module. - // - &v.insert ("config.cxx.importable_headers", true), - - v.insert ("cxx.path"), - v.insert ("cxx.sys_lib_dirs"), - v.insert ("cxx.sys_inc_dirs"), - - v.insert ("cxx.std", variable_visibility::project), - - v.insert ("cxx.poptions"), - v.insert ("cxx.coptions"), - v.insert ("cxx.loptions"), - v.insert ("cxx.aoptions"), - v.insert ("cxx.libs"), - - &v.insert ("cxx.importable_headers"), - - v["cc.poptions"], - v["cc.coptions"], - v["cc.loptions"], - v["cc.aoptions"], - v["cc.libs"], - - v.insert ("cxx.export.poptions"), - v.insert ("cxx.export.coptions"), - v.insert ("cxx.export.loptions"), - v.insert> ("cxx.export.libs"), - - v["cc.export.poptions"], - v["cc.export.coptions"], - v["cc.export.loptions"], - v["cc.export.libs"], - - v.insert ("cxx.stdlib"), - - v["cc.runtime"], - v["cc.stdlib"], - - v["cc.type"], - v["cc.system"], - v["cc.module_name"], - v["cc.reprocess"], - - // Ability to signal that source is already (partially) preprocessed. - // Valid values are 'none' (not preprocessed), 'includes' (no #include - // directives in source), 'modules' (as above plus no module - // declaration depends on preprocessor, e.g., #ifdef, etc), and 'all' - // (the source is fully preprocessed). Note that for 'all' the source - // can still contain comments and line continuations. Note also that - // for some compilers (e.g., VC) there is no way to signal that the - // source is already preprocessed. - // - // What about header unit imports? Well, they are in a sense - // standardized precompiled headers so we treat them as includes. - // - v.insert ("cxx.preprocessed"), - - nullptr, // cxx.features.symexport (set in init() below). - - v.insert ("cxx.id"), - v.insert ("cxx.id.type"), - v.insert ("cxx.id.variant"), - - v.insert ("cxx.class"), - - v.insert ("cxx.version"), - v.insert ("cxx.version.major"), - v.insert ("cxx.version.minor"), - v.insert ("cxx.version.patch"), - v.insert ("cxx.version.build"), - - v.insert ("cxx.signature"), - v.insert ("cxx.checksum"), - - v.insert ("cxx.pattern"), - - v.insert ("cxx.target"), - - v.insert ("cxx.target.cpu"), - v.insert ("cxx.target.vendor"), - v.insert ("cxx.target.system"), - v.insert ("cxx.target.version"), - v.insert ("cxx.target.class") - }; - - // Alias some cc. variables as cxx. - // - v.insert_alias (d.c_runtime, "cxx.runtime"); - v.insert_alias (d.c_module_name, "cxx.module_name"); - - assert (mod == nullptr); - config_module* m (new config_module (move (d))); - mod.reset (m); - m->guess (rs, loc, hints); - return true; - } - - bool - config_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - bool, - bool, - const variable_map& hints) - { - tracer trace ("cxx::config_init"); - l5 ([&]{trace << "for " << bs;}); - - // We only support root loading (which means there can only be one). - // - if (&rs != &bs) - fail (loc) << "cxx.config module must be loaded in project root"; - - // Load cxx.guess. - // - if (!cast_false (rs["cxx.guess.loaded"])) - load_module (rs, rs, "cxx.guess", loc, false, hints); - - config_module& cm (*rs.lookup_module ("cxx.guess")); - cm.init (rs, loc, hints); - return true; - } - - static const target_type* const hdr[] = - { - &hxx::static_type, - &ixx::static_type, - &txx::static_type, - &mxx::static_type, - nullptr - }; - - static const target_type* const inc[] = - { - &hxx::static_type, - &h::static_type, - &ixx::static_type, - &txx::static_type, - &mxx::static_type, - &cxx::static_type, - &c::static_type, - nullptr - }; - - bool - init (scope& rs, - scope& bs, - const location& loc, - unique_ptr& mod, - bool, - bool, - const variable_map& hints) - { - tracer trace ("cxx::init"); - l5 ([&]{trace << "for " << bs;}); - - // We only support root loading (which means there can only be one). - // - if (&rs != &bs) - fail (loc) << "cxx module must be loaded in project root"; - - // Load cxx.config. - // - if (!cast_false (rs["cxx.config.loaded"])) - load_module (rs, rs, "cxx.config", loc, false, hints); - - config_module& cm (*rs.lookup_module ("cxx.guess")); - - auto& vp (rs.ctx.var_pool.rw (rs)); - - bool modules (cast (rs["cxx.features.modules"])); - - bool symexport (false); - if (modules) - { - auto& var (vp.insert ("cxx.features.symexport", - variable_visibility::project)); - symexport = cast_false (rs[var]); - cm.x_symexport = &var; - } - - cc::data d { - cm, - - "cxx.compile", - "cxx.link", - "cxx.install", - "cxx.uninstall", - - cm.ci_->id.type, - cm.ci_->id.variant, - cm.ci_->class_, - cm.ci_->version.major, - cm.ci_->version.minor, - cast (rs[cm.x_path]), - cast (rs[cm.x_target]), - - cm.tstd, - - modules, - symexport, - - cast (rs[cm.x_sys_lib_dirs]), - cast (rs[cm.x_sys_inc_dirs]), - - cm.sys_lib_dirs_extra, - cm.sys_inc_dirs_extra, - - cxx::static_type, - modules ? &mxx::static_type : nullptr, - hdr, - inc - }; - - assert (mod == nullptr); - module* m; - mod.reset (m = new module (move (d))); - m->init (rs, loc, hints); - return true; - } - } -} diff --git a/build2/cxx/init.hxx b/build2/cxx/init.hxx deleted file mode 100644 index 83553e6..0000000 --- a/build2/cxx/init.hxx +++ /dev/null @@ -1,46 +0,0 @@ -// file : build2/cxx/init.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CXX_INIT_HXX -#define BUILD2_CXX_INIT_HXX - -#include -#include - -#include - -namespace build2 -{ - namespace cxx - { - bool - guess_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - config_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - } -} - -#endif // BUILD2_CXX_INIT_HXX diff --git a/build2/cxx/target.cxx b/build2/cxx/target.cxx deleted file mode 100644 index 45463f3..0000000 --- a/build2/cxx/target.cxx +++ /dev/null @@ -1,85 +0,0 @@ -// file : build2/cxx/target.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include - -using namespace std; - -namespace build2 -{ - namespace cxx - { - extern const char hxx_ext_def[] = "hxx"; - const target_type hxx::static_type - { - "hxx", - &cc::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - - extern const char ixx_ext_def[] = "ixx"; - const target_type ixx::static_type - { - "ixx", - &cc::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - - extern const char txx_ext_def[] = "txx"; - const target_type txx::static_type - { - "txx", - &cc::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - - extern const char cxx_ext_def[] = "cxx"; - const target_type cxx::static_type - { - "cxx", - &cc::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - - extern const char mxx_ext_def[] = "mxx"; - const target_type mxx::static_type - { - "mxx", - &cc::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - } -} diff --git a/build2/cxx/target.hxx b/build2/cxx/target.hxx deleted file mode 100644 index 40dd810..0000000 --- a/build2/cxx/target.hxx +++ /dev/null @@ -1,78 +0,0 @@ -// file : build2/cxx/target.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CXX_TARGET_HXX -#define BUILD2_CXX_TARGET_HXX - -#include -#include - -#include -#include - -namespace build2 -{ - namespace cxx - { - using cc::h; - using cc::c; - - class hxx: public cc::cc - { - public: - using cc::cc; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - class ixx: public cc::cc - { - public: - using cc::cc; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - class txx: public cc::cc - { - public: - using cc::cc; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - class cxx: public cc::cc - { - public: - using cc::cc; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - // The module interface unit is both like a header (e.g., we need to - // install it) and like a source (we need to compile it). Plus, to - // support dual use (modules/headers) it could actually be #include'd - // (and even in both cases e.g., by different codebases). - // - class mxx: public cc::cc - { - public: - using cc::cc; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - } -} - -#endif // BUILD2_CXX_TARGET_HXX diff --git a/libbuild2/buildfile b/libbuild2/buildfile index 75731fc..8d40c28 100644 --- a/libbuild2/buildfile +++ b/libbuild2/buildfile @@ -2,10 +2,10 @@ # copyright : Copyright (c) 2014-2019 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -# NOTE: remember to update bundled_modules in libbuild2/modules.cxx if adding -# a new module. +# NOTE: remember to update bundled_modules in libbuild2/module.cxx if adding a +# new module. # -./: lib{build2} bash/ bin/ c/ cc/ in/ version/ +./: lib{build2} bash/ bin/ c/ cc/ cxx/ in/ version/ import int_libs = libbutl%lib{butl} @@ -88,7 +88,7 @@ cross = ($cxx.target.cpu != $build.host.cpu || \ if! $cross obj{context}: cxx.poptions += \ - "-DBUILD2_IMPORT_PATH=\"$regex.replace($out_root, '\\', '\\\\')\"" + -DBUILD2_IMPORT_PATH=\"$regex.replace($out_root, '\\', '\\\\')\" obja{context}: cxx.poptions += -DLIBBUILD2_STATIC_BUILD objs{context}: cxx.poptions += -DLIBBUILD2_SHARED_BUILD diff --git a/libbuild2/cxx/buildfile b/libbuild2/cxx/buildfile new file mode 100644 index 0000000..debb574 --- /dev/null +++ b/libbuild2/cxx/buildfile @@ -0,0 +1,90 @@ +# file : libbuild2/cxx/buildfile +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +import int_libs = libbutl%lib{butl} + +include ../ +int_libs += ../lib{build2} + +include ../cc/ +int_libs += ../cc/lib{build2-cc} + +./: lib{build2-cxx}: libul{build2-cxx}: {hxx ixx txx cxx}{** -**.test...} \ + $int_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libul{build2-cxx}: bin.whole = false +} + +# Build options. +# +obja{*}: cxx.poptions += -DLIBBUILD2_CXX_STATIC_BUILD +objs{*}: cxx.poptions += -DLIBBUILD2_CXX_SHARED_BUILD + +# Note that we used to compare complete target triplets but that proved too +# strict (see libbuild2/buildfile for details). +# +cross = ($cxx.target.cpu != $build.host.cpu || \ + $cxx.target.system != $build.host.system) + +# Pass native C++ compiler path (not forgetting to escape backslashes on +# Windows). It is used as defaults for BUILD2_DEFAULT_CXX. +# +if! $cross +{ + obj{init}: cxx.poptions += \ + -DBUILD2_NATIVE_CXX=\"$regex.replace($recall($cxx.path), '\\', '\\\\')\" + + obja{init}: cxx.poptions += -DLIBBUILD2_CXX_STATIC_BUILD + objs{init}: cxx.poptions += -DLIBBUILD2_CXX_SHARED_BUILD +} + +# Export options. +# +lib{build2-cxx}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $int_libs +} + +liba{build2-cxx}: cxx.export.poptions += -DLIBBUILD2_CXX_STATIC +libs{build2-cxx}: cxx.export.poptions += -DLIBBUILD2_CXX_SHARED + +# For pre-releases use the complete version to make sure they cannot be used +# in place of another pre-release or the final version. See the version module +# for details on the version.* variable values. +# +# And because this is a build system module, we also embed the same value as +# the interface version (note that we cannot use build.version.interface for +# bundled modules because we could be built with a different version of the +# build system). +# +ver = ($version.pre_release \ + ? "$version.project_id" \ + : "$version.major.$version.minor") + +lib{build2-cxx}: bin.lib.version = @"-$ver" +libs{build2-cxx}: bin.lib.load_suffix = "-$ver" + +# Install into the libbuild2/cxx/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/libbuild2/cxx/ + install.subdirs = true +} diff --git a/libbuild2/cxx/export.hxx b/libbuild2/cxx/export.hxx new file mode 100644 index 0000000..64df2f7 --- /dev/null +++ b/libbuild2/cxx/export.hxx @@ -0,0 +1,38 @@ +// file : libbuild2/cxx/export.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#pragma once + +// Normally we don't export class templates (but do complete specializations), +// inline functions, and classes with only inline member functions. Exporting +// classes that inherit from non-exported/imported bases (e.g., std::string) +// will end up badly. The only known workarounds are to not inherit or to not +// export. Also, MinGW GCC doesn't like seeing non-exported functions being +// used before their inline definition. The workaround is to reorder code. In +// the end it's all trial and error. + +#if defined(LIBBUILD2_CXX_STATIC) // Using static. +# define LIBBUILD2_CXX_SYMEXPORT +#elif defined(LIBBUILD2_CXX_STATIC_BUILD) // Building static. +# define LIBBUILD2_CXX_SYMEXPORT +#elif defined(LIBBUILD2_CXX_SHARED) // Using shared. +# ifdef _WIN32 +# define LIBBUILD2_CXX_SYMEXPORT __declspec(dllimport) +# else +# define LIBBUILD2_CXX_SYMEXPORT +# endif +#elif defined(LIBBUILD2_CXX_SHARED_BUILD) // Building shared. +# ifdef _WIN32 +# define LIBBUILD2_CXX_SYMEXPORT __declspec(dllexport) +# else +# define LIBBUILD2_CXX_SYMEXPORT +# endif +#else +// If none of the above macros are defined, then we assume we are being used +// by some third-party build system that cannot/doesn't signal the library +// type. Note that this fallback works for both static and shared but in case +// of shared will be sub-optimal compared to having dllimport. +// +# define LIBBUILD2_CXX_SYMEXPORT // Using static or shared. +#endif diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx new file mode 100644 index 0000000..24d2cb9 --- /dev/null +++ b/libbuild2/cxx/init.cxx @@ -0,0 +1,646 @@ +// file : libbuild2/cxx/init.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include + +#include +#include + +#include + +#ifndef BUILD2_DEFAULT_CXX +# ifdef BUILD2_NATIVE_CXX +# define BUILD2_DEFAULT_CXX BUILD2_NATIVE_CXX +# else +# define BUILD2_DEFAULT_CXX "" +# endif +#endif + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace cxx + { + using cc::compiler_id; + using cc::compiler_type; + using cc::compiler_class; + using cc::compiler_info; + + class config_module: public cc::config_module + { + public: + explicit + config_module (config_data&& d) + : config_data (move (d)), cc::config_module (move (d)) {} + + virtual strings + translate_std (const compiler_info&, + scope&, + const string*) const override; + }; + + using cc::module; + + strings config_module:: + translate_std (const compiler_info& ci, scope& rs, const string* v) const + { + strings r; + + compiler_type ct (ci.id.type); + compiler_class cl (ci.class_); + uint64_t mj (ci.version.major); + uint64_t mi (ci.version.minor); + uint64_t p (ci.version.patch); + + // Features. + // + auto enter = [&rs] (const char* v) -> const variable& + { + return rs.ctx.var_pool.rw (rs).insert ( + v, variable_visibility::project); + }; + + // NOTE: see also module sidebuild subproject if changing anything about + // modules here. + + //bool concepts (false); auto& v_c (enter ("cxx.features.concepts")); + bool modules (false); auto& v_m (enter ("cxx.features.modules")); + + // Translate "latest" and "experimental" to the compiler/version- + // appropriate option(s). Experimental is normally like latest with + // extra stuff enabled via additional flags. Otherwise translate the + // standard value. + // + bool experimental ( v != nullptr && *v == "experimental"); + bool latest (experimental || (v != nullptr && *v == "latest")); + + string o; + + switch (cl) + { + case compiler_class::msvc: + { + // C++ standard-wise, with VC you got what you got up until 14.2. + // Starting with 14.3 there is now the /std: switch which defaults + // to c++14 but can be set to c++latest. And from 15.3 it can be + // c++17. + // + // The question is also whether we should verify that the requested + // standard is provided by this VC version. And if so, from which + // version should we say VC supports 11, 14, and 17? We should + // probably be as loose as possible here since the author will + // always be able to tighten (but not loosen) this in the buildfile + // (i.e., detect unsupported versions). + // + // For now we are not going to bother doing this for C++03. + // + if (latest) + { + if (mj > 19 || (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215)))) + o = "/std:c++latest"; + } + else if (v == nullptr) + ; + else if (*v != "98" && *v != "03") + { + bool sup (false); + + if (*v == "11") // C++11 since VS2010/10.0. + { + sup = mj >= 16; + } + else if (*v == "14") // C++14 since VS2015/14.0. + { + sup = mj >= 19; + } + else if (*v == "17") // C++17 since VS2015/14.0u2. + { + // Note: the VC15 compiler version is 19.10. + // + sup = (mj > 19 || + (mj == 19 && (mi > 0 || (mi == 0 && p >= 23918)))); + } + + if (!sup) + fail << "C++" << *v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs; + + if (mj > 19 || (mj == 19 && mi >= 11)) // 15.3 + { + if (*v == "14") o = "/std:c++14"; + else if (*v == "17") o = "/std:c++17"; + } + else if (mj == 19 && (mi > 0 || (mi == 0 && p >= 24215))) // 14.3 + { + if (*v == "14") o = "/std:c++14"; + else if (*v == "17") o = "/std:c++latest"; + } + } + + if (!o.empty ()) + r.push_back (move (o)); + + break; + } + case compiler_class::gcc: + { + if (latest) + { + switch (ct) + { + case compiler_type::gcc: + { + if (mj >= 8) o = "-std=c++2a"; // 20 + else if (mj >= 5) o = "-std=c++1z"; // 17 + else if (mj == 4 && mi >= 8) o = "-std=c++1y"; // 14 + else if (mj == 4 && mi >= 4) o = "-std=c++0x"; // 11 + + break; + } + case compiler_type::clang: + { + // Remap Apple versions to vanilla Clang based on the + // following release point. Note that Apple no longer + // discloses the mapping so it's a guesswork and we try to be + // conservative. For details see: + // + // https://gist.github.com/yamaya/2924292 + // + // 5.1 -> 3.4 + // 6.0 -> 3.5 + // 7.0 -> 3.7 + // 7.3 -> 3.8 + // 8.0 -> 3.9 + // 9.0 -> 4.0 (later ones could be 5.0) + // 9.1 -> ? + // 10.0 -> ? + // + // Note that this mapping is also used to enable experimental + // features below. + // + if (ci.id.variant == "apple") + { + if (mj >= 9) {mj = 4; mi = 0;} + else if (mj == 8) {mj = 3; mi = 9;} + else if (mj == 7 && mi >= 3) {mj = 3; mi = 8;} + else if (mj == 7) {mj = 3; mi = 7;} + else if (mj == 6) {mj = 3; mi = 5;} + else if (mj == 5 && mi >= 1) {mj = 3; mi = 4;} + else {mj = 3; mi = 0;} + } + + if (mj >= 5) o = "-std=c++2a"; + else if (mj > 3 || (mj == 3 && mi >= 5)) o = "-std=c++1z"; + else if (mj == 3 && mi >= 4) o = "-std=c++1y"; + else /* ??? */ o = "-std=c++0x"; + + break; + } + case compiler_type::icc: + { + if (mj >= 17) o = "-std=c++1z"; + else if (mj > 15 || (mj == 15 && p >= 3)) o = "-std=c++1y"; + else /* ??? */ o = "-std=c++0x"; + + break; + } + default: + assert (false); + } + } + else if (v == nullptr) + ; + else + { + // Translate 11 to 0x, 14 to 1y, 17 to 1z, and 20 to 2a for + // compatibility with older versions of the compilers. + // + o = "-std="; + + if (*v == "98") o += "c++98"; + else if (*v == "03") o += "c++03"; + else if (*v == "11") o += "c++0x"; + else if (*v == "14") o += "c++1y"; + else if (*v == "17") o += "c++1z"; + else if (*v == "20") o += "c++2a"; + else o += *v; // In case the user specifies e.g., 'gnu++17'. + } + + if (!o.empty ()) + r.push_back (move (o)); + + break; + } + } + + if (experimental) + { + switch (ct) + { + case compiler_type::msvc: + { + // Starting with 15.5 (19.12) Visual Studio-created projects + // default to the strict mode. However, this flag currently tends + // to trigger too many compiler bugs. So for now we leave it + // to the experimenters to enjoy. + // + if (mj > 19 || (mj == 19 && mi >= 12)) + r.push_back ("/permissive-"); + + break; + } + default: + break; + } + + // Unless disabled by the user, try to enable C++ modules. Here we use + // a tri-state: + // + // - false - disabled + // - unspecified - enabled if practically usable + // - true - enabled even if practically unusable + // + lookup l; + if (!(l = rs[v_m]) || cast (l)) + { + switch (ct) + { + case compiler_type::msvc: + { + // While modules are supported in VC15.0 (19.10), there is a bug + // in separate interface/implementation unit support which makes + // them pretty much unusable. This has been fixed in VC15.3 + // (19.11). And VC15.5 (19.12) supports the 'export module M;' + // syntax. + // + if (mj > 19 || (mj == 19 && mi >= (l ? 10 : 12))) + { + r.push_back ( + mj > 19 || mi > 11 + ? "/D__cpp_modules=201704" // p0629r0 (export module M;) + : "/D__cpp_modules=201703"); // n4647 ( module M;) + + r.push_back ("/experimental:module"); + modules = true; + } + break; + } + case compiler_type::gcc: + { + // We now use extended/experimental module mapper support which + // is currently only available in our c++-modules-ex branch. + // But let's allow forcing it to plain c++-modules in case + // things got merged, etc. + // + if (mj >= 10 && + ci.version.build.find (l + ? "c++-modules" + : "c++-modules-ex") != string::npos) + { + // Currently defines __cpp_modules=201810 which is said to + // correspond to p1103 (merged modules). + // + r.push_back ("-fmodules-ts"); + modules = true; + } + break; + } + case compiler_type::clang: + { + // Enable starting with Clang 6.0.0. + // + // Note that we are using Apple to vanilla Clang version re-map + // from above so may need to update things there as well. + // + // Also see Clang modules support hack in cc::compile. + // + // @@ Clang 9 enables modules by default in C++2a. We should + // probably reflect this in the modules value. + // + if (mj >= 6) + { + r.push_back ("-D__cpp_modules=201704"); // p0629r0 + r.push_back ("-fmodules-ts"); + modules = true; + } + break; + } + case compiler_type::icc: + break; // No modules support yet. + } + } + } + + rs.assign (v_m) = modules; + //rs.assign (v_c) = concepts; + + return r; + } + + static const char* const hinters[] = {"c", nullptr}; + + // See cc::module for details on guess_init vs config_init. + // + bool + guess_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr& mod, + bool, + bool, + const variable_map& hints) + { + tracer trace ("cxx::guess_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx.guess module must be loaded in project root"; + + // Load cc.core.vars so that we can cache all the cc.* variables. + // + if (!cast_false (rs["cc.core.vars.loaded"])) + load_module (rs, rs, "cc.core.vars", loc); + + // Enter all the variables and initialize the module data. + // + auto& v (rs.ctx.var_pool.rw (rs)); + + cc::config_data d { + cc::lang::cxx, + + "cxx", + "c++", + BUILD2_DEFAULT_CXX, + ".ii", + + hinters, + + // Note: some overridable, some not. + // + v.insert ("config.cxx", true), + v.insert ("config.cxx.id", true), + v.insert ("config.cxx.version", true), + v.insert ("config.cxx.target", true), + v.insert ("config.cxx.std", true), + v.insert ("config.cxx.poptions", true), + v.insert ("config.cxx.coptions", true), + v.insert ("config.cxx.loptions", true), + v.insert ("config.cxx.aoptions", true), + v.insert ("config.cxx.libs", true), + + // List of importable headers. Inclusion of such headers is translated + // to the corresponding header unit imports. + // + // A header can be specified either as an absolute and normalized path + // or as a <>-style include name. The latter kind is automatically + // translated to the absolute form based on the compiler's system + // header search paths (as opposed to -I). Note also that all entries + // must be specified before loading the cxx module. + // + &v.insert ("config.cxx.importable_headers", true), + + v.insert ("cxx.path"), + v.insert ("cxx.sys_lib_dirs"), + v.insert ("cxx.sys_inc_dirs"), + + v.insert ("cxx.std", variable_visibility::project), + + v.insert ("cxx.poptions"), + v.insert ("cxx.coptions"), + v.insert ("cxx.loptions"), + v.insert ("cxx.aoptions"), + v.insert ("cxx.libs"), + + &v.insert ("cxx.importable_headers"), + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.aoptions"], + v["cc.libs"], + + v.insert ("cxx.export.poptions"), + v.insert ("cxx.export.coptions"), + v.insert ("cxx.export.loptions"), + v.insert> ("cxx.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v.insert ("cxx.stdlib"), + + v["cc.runtime"], + v["cc.stdlib"], + + v["cc.type"], + v["cc.system"], + v["cc.module_name"], + v["cc.reprocess"], + + // Ability to signal that source is already (partially) preprocessed. + // Valid values are 'none' (not preprocessed), 'includes' (no #include + // directives in source), 'modules' (as above plus no module + // declaration depends on preprocessor, e.g., #ifdef, etc), and 'all' + // (the source is fully preprocessed). Note that for 'all' the source + // can still contain comments and line continuations. Note also that + // for some compilers (e.g., VC) there is no way to signal that the + // source is already preprocessed. + // + // What about header unit imports? Well, they are in a sense + // standardized precompiled headers so we treat them as includes. + // + v.insert ("cxx.preprocessed"), + + nullptr, // cxx.features.symexport (set in init() below). + + v.insert ("cxx.id"), + v.insert ("cxx.id.type"), + v.insert ("cxx.id.variant"), + + v.insert ("cxx.class"), + + v.insert ("cxx.version"), + v.insert ("cxx.version.major"), + v.insert ("cxx.version.minor"), + v.insert ("cxx.version.patch"), + v.insert ("cxx.version.build"), + + v.insert ("cxx.signature"), + v.insert ("cxx.checksum"), + + v.insert ("cxx.pattern"), + + v.insert ("cxx.target"), + + v.insert ("cxx.target.cpu"), + v.insert ("cxx.target.vendor"), + v.insert ("cxx.target.system"), + v.insert ("cxx.target.version"), + v.insert ("cxx.target.class") + }; + + // Alias some cc. variables as cxx. + // + v.insert_alias (d.c_runtime, "cxx.runtime"); + v.insert_alias (d.c_module_name, "cxx.module_name"); + + assert (mod == nullptr); + config_module* m (new config_module (move (d))); + mod.reset (m); + m->guess (rs, loc, hints); + return true; + } + + bool + config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + bool, + bool, + const variable_map& hints) + { + tracer trace ("cxx::config_init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx.config module must be loaded in project root"; + + // Load cxx.guess. + // + if (!cast_false (rs["cxx.guess.loaded"])) + load_module (rs, rs, "cxx.guess", loc, false, hints); + + config_module& cm (*rs.lookup_module ("cxx.guess")); + cm.init (rs, loc, hints); + return true; + } + + static const target_type* const hdr[] = + { + &hxx::static_type, + &ixx::static_type, + &txx::static_type, + &mxx::static_type, + nullptr + }; + + static const target_type* const inc[] = + { + &hxx::static_type, + &h::static_type, + &ixx::static_type, + &txx::static_type, + &mxx::static_type, + &cxx::static_type, + &c::static_type, + nullptr + }; + + bool + init (scope& rs, + scope& bs, + const location& loc, + unique_ptr& mod, + bool, + bool, + const variable_map& hints) + { + tracer trace ("cxx::init"); + l5 ([&]{trace << "for " << bs;}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx module must be loaded in project root"; + + // Load cxx.config. + // + if (!cast_false (rs["cxx.config.loaded"])) + load_module (rs, rs, "cxx.config", loc, false, hints); + + config_module& cm (*rs.lookup_module ("cxx.guess")); + + auto& vp (rs.ctx.var_pool.rw (rs)); + + bool modules (cast (rs["cxx.features.modules"])); + + bool symexport (false); + if (modules) + { + auto& var (vp.insert ("cxx.features.symexport", + variable_visibility::project)); + symexport = cast_false (rs[var]); + cm.x_symexport = &var; + } + + cc::data d { + cm, + + "cxx.compile", + "cxx.link", + "cxx.install", + "cxx.uninstall", + + cm.ci_->id.type, + cm.ci_->id.variant, + cm.ci_->class_, + cm.ci_->version.major, + cm.ci_->version.minor, + cast (rs[cm.x_path]), + cast (rs[cm.x_target]), + + cm.tstd, + + modules, + symexport, + + cast (rs[cm.x_sys_lib_dirs]), + cast (rs[cm.x_sys_inc_dirs]), + + cm.sys_lib_dirs_extra, + cm.sys_inc_dirs_extra, + + cxx::static_type, + modules ? &mxx::static_type : nullptr, + hdr, + inc + }; + + assert (mod == nullptr); + module* m; + mod.reset (m = new module (move (d))); + m->init (rs, loc, hints); + return true; + } + + static const module_functions mod_functions[] = + { + // NOTE: don't forget to also update the documentation in init.hxx if + // changing anything here. + + {"cxx.guess", nullptr, guess_init}, + {"cxx.config", nullptr, config_init}, + {"cxx", nullptr, init}, + {nullptr, nullptr, nullptr} + }; + + const module_functions* + build2_cxx_load () + { + return mod_functions; + } + } +} diff --git a/libbuild2/cxx/init.hxx b/libbuild2/cxx/init.hxx new file mode 100644 index 0000000..a6398de --- /dev/null +++ b/libbuild2/cxx/init.hxx @@ -0,0 +1,32 @@ +// file : libbuild2/cxx/init.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_CXX_INIT_HXX +#define LIBBUILD2_CXX_INIT_HXX + +#include +#include + +#include + +#include + +namespace build2 +{ + namespace cxx + { + // Module `cxx` does not require bootstrapping. + // + // Submodules: + // + // `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. + // + extern "C" LIBBUILD2_CXX_SYMEXPORT const module_functions* + build2_cxx_load (); + } +} + +#endif // LIBBUILD2_CXX_INIT_HXX diff --git a/libbuild2/cxx/target.cxx b/libbuild2/cxx/target.cxx new file mode 100644 index 0000000..25dfa3a --- /dev/null +++ b/libbuild2/cxx/target.cxx @@ -0,0 +1,85 @@ +// file : libbuild2/cxx/target.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +namespace build2 +{ + namespace cxx + { + extern const char hxx_ext_def[] = "hxx"; + const target_type hxx::static_type + { + "hxx", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + + extern const char ixx_ext_def[] = "ixx"; + const target_type ixx::static_type + { + "ixx", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + + extern const char txx_ext_def[] = "txx"; + const target_type txx::static_type + { + "txx", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + + extern const char cxx_ext_def[] = "cxx"; + const target_type cxx::static_type + { + "cxx", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + + extern const char mxx_ext_def[] = "mxx"; + const target_type mxx::static_type + { + "mxx", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + } +} diff --git a/libbuild2/cxx/target.hxx b/libbuild2/cxx/target.hxx new file mode 100644 index 0000000..3cf2882 --- /dev/null +++ b/libbuild2/cxx/target.hxx @@ -0,0 +1,80 @@ +// file : libbuild2/cxx/target.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_CXX_TARGET_HXX +#define LIBBUILD2_CXX_TARGET_HXX + +#include +#include + +#include +#include + +#include + +namespace build2 +{ + namespace cxx + { + using cc::h; + using cc::c; + + class LIBBUILD2_CXX_SYMEXPORT hxx: public cc::cc + { + public: + using cc::cc; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class LIBBUILD2_CXX_SYMEXPORT ixx: public cc::cc + { + public: + using cc::cc; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class LIBBUILD2_CXX_SYMEXPORT txx: public cc::cc + { + public: + using cc::cc; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class LIBBUILD2_CXX_SYMEXPORT cxx: public cc::cc + { + public: + using cc::cc; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + // The module interface unit is both like a header (e.g., we need to + // install it) and like a source (we need to compile it). Plus, to + // support dual use (modules/headers) it could actually be #include'd + // (and even in both cases e.g., by different codebases). + // + class LIBBUILD2_CXX_SYMEXPORT mxx: public cc::cc + { + public: + using cc::cc; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + } +} + +#endif // LIBBUILD2_CXX_TARGET_HXX diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx index 37bc817..5cfb1ec 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -41,6 +41,7 @@ namespace build2 "bin", "c", "cc", + "cxx", "in", "version" }; diff --git a/tests/libbuild2/buildfile b/tests/libbuild2/buildfile index 1d7faf7..f1104d2 100644 --- a/tests/libbuild2/buildfile +++ b/tests/libbuild2/buildfile @@ -4,7 +4,7 @@ import libs = build2%lib{build2} -for m: bash bin c cc in version +for m: bash bin c cc cxx in version import libs += build2%lib{build2-$m} exe{driver}: {hxx cxx}{*} $libs testscript diff --git a/tests/libbuild2/driver.cxx b/tests/libbuild2/driver.cxx index e17cdc5..a14d145 100644 --- a/tests/libbuild2/driver.cxx +++ b/tests/libbuild2/driver.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ main (int, char* argv[]) bin::build2_bin_load (); cc::build2_cc_load (); c::build2_c_load (); + cxx::build2_cxx_load (); version::build2_version_load (); in::build2_in_load (); bash::build2_bash_load (); -- cgit v1.1