aboutsummaryrefslogtreecommitdiff
path: root/build2/cxx/module.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/cxx/module.cxx')
-rw-r--r--build2/cxx/module.cxx505
1 files changed, 184 insertions, 321 deletions
diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx
index f66ef53..fd98114 100644
--- a/build2/cxx/module.cxx
+++ b/build2/cxx/module.cxx
@@ -4,23 +4,13 @@
#include <build2/cxx/module>
-#include <butl/triplet>
-
#include <build2/scope>
#include <build2/context>
#include <build2/diagnostics>
-#include <build2/config/utility>
-#include <build2/install/utility>
-
-#include <build2/bin/target>
+#include <build2/cc/module>
-#include <build2/cxx/link>
-#include <build2/cxx/guess>
#include <build2/cxx/target>
-#include <build2/cxx/compile>
-#include <build2/cxx/install>
-#include <build2/cxx/utility>
using namespace std;
using namespace butl;
@@ -29,355 +19,228 @@ namespace build2
{
namespace cxx
{
- bool
- init (scope& r,
- scope& b,
- const location& loc,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
- {
- tracer trace ("cxx::init");
- l5 ([&]{trace << "for " << b.out_path ();});
-
- // Enter module variables.
- //
- if (first)
- {
- auto& v (var_pool);
+ using cc::config_module;
- // Note: some overridable, some not.
- //
- v.insert<path> ("config.cxx", true);
- v.insert<strings> ("config.cxx.poptions", true);
- v.insert<strings> ("config.cxx.coptions", true);
- v.insert<strings> ("config.cxx.loptions", true);
- v.insert<strings> ("config.cxx.libs", true);
-
- v.insert<strings> ("cxx.poptions");
- v.insert<strings> ("cxx.coptions");
- v.insert<strings> ("cxx.loptions");
- v.insert<strings> ("cxx.libs");
-
- v.insert<strings> ("cxx.export.poptions");
- v.insert<strings> ("cxx.export.coptions");
- v.insert<strings> ("cxx.export.loptions");
- v.insert<strings> ("cxx.export.libs");
-
- v.insert<string> ("cxx.std", true);
- }
-
- // Configure.
- //
-
- assert (config_hints.empty ()); // We don't known any hints.
-
- // config.cxx.{p,c,l}options
- // config.cxx.libs
- //
- // These are optional. We also merge them into the corresponding
- // cxx.* variables.
- //
- // The merging part gets a bit tricky if this module has already
- // been loaded in one of the outer scopes. By doing the straight
- // append we would just be repeating the same options over and
- // over. So what we are going to do is only append to a value if
- // it came from this scope. Then the usage for merging becomes:
- //
- // cxx.coptions = <overridable options> # Note: '='.
- // using cxx
- // cxx.coptions += <overriding options> # Note: '+='.
- //
- b.assign ("cxx.poptions") += cast_null<strings> (
- config::optional (r, "config.cxx.poptions"));
-
- b.assign ("cxx.coptions") += cast_null<strings> (
- config::optional (r, "config.cxx.coptions"));
-
- b.assign ("cxx.loptions") += cast_null<strings> (
- config::optional (r, "config.cxx.loptions"));
+ class module: public cc::module
+ {
+ public:
+ explicit
+ module (data&& d): common (move (d)), cc::module (move (d)) {}
- b.assign ("cxx.libs") += cast_null<strings> (
- config::optional (r, "config.cxx.libs"));
+ bool
+ translate_std (string&, scope&, const value&) const override;
+ };
- // Configuration hints for the bin module. They will only be used on the
- // first loading of the bin module (for this project) so we only
- // populate them on our first loading.
- //
- variable_map bin_hints;
+ bool module::
+ translate_std (string& s, scope& r, const value& val) const
+ {
+ const string& v (cast<string> (val));
- // config.cxx
- //
- if (first)
+ if (cid == "msvc")
{
- auto p (config::required (r, "config.cxx", path ("g++")));
-
- // Figure out which compiler we are dealing with, its target, etc.
+ // C++ standard-wise, with VC you get what you get. The question is
+ // 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).
//
- const path& cxx (cast<path> (p.first));
- compiler_info ci (guess (cxx, cast_null<strings> (r["cxx.coptions"])));
-
- // If this is a new value (e.g., we are configuring), then print the
- // report at verbosity level 2 and up (-v).
+ // For now we are not going to bother doing this for C++03.
//
- if (verb >= (p.second ? 2 : 3))
+ if (v != "98" && v != "03")
{
- text << "cxx " << project (r) << '@' << r.out_path () << '\n'
- << " cxx " << cxx << '\n'
- << " id " << ci.id << '\n'
- << " version " << ci.version.string << '\n'
- << " major " << ci.version.major << '\n'
- << " minor " << ci.version.minor << '\n'
- << " patch " << ci.version.patch << '\n'
- << " build " << ci.version.build << '\n'
- << " signature " << ci.signature << '\n'
- << " checksum " << ci.checksum << '\n'
- << " target " << ci.target;
- }
-
- r.assign<string> ("cxx.id") = ci.id.string ();
- r.assign<string> ("cxx.id.type") = move (ci.id.type);
- r.assign<string> ("cxx.id.variant") = move (ci.id.variant);
-
- r.assign<string> ("cxx.version") = move (ci.version.string);
- r.assign<uint64_t> ("cxx.version.major") = ci.version.major;
- r.assign<uint64_t> ("cxx.version.minor") = ci.version.minor;
- r.assign<uint64_t> ("cxx.version.patch") = ci.version.patch;
- r.assign<string> ("cxx.version.build") = move (ci.version.build);
+ uint64_t cver (cast<uint64_t> (r[x_version_major]));
- r.assign<string> ("cxx.signature") = move (ci.signature);
- r.assign<string> ("cxx.checksum") = move (ci.checksum);
-
- // While we still have the original, compiler-reported target, see if
- // we can derive a binutils program pattern.
- //
- // BTW, for GCC we also get gcc-{ar,ranlib} which add support for the
- // LTO plugin though it seems more recent GNU binutils (2.25) are able
- // to load the plugin when needed automatically. So it doesn't seem we
- // should bother trying to support this on our end (the way we could
- // do it is by passing config.bin.{ar,ranlib} as hints).
- //
- string pattern;
-
- if (cast<string> (r["cxx.id"]) == "msvc")
- {
- // If the compiler name is/starts with 'cl' (e.g., cl.exe, cl-14),
- // then replace it with '*' and use it as a pattern for lib, link,
- // etc.
+ // @@ Is mapping for 14 and 17 correct? Maybe Update 2 for 14?
//
- if (cxx.size () > 2)
+ if ((v == "11" && cver < 16) || // C++11 since VS2010/10.0.
+ (v == "14" && cver < 19) || // C++14 since VS2015/14.0.
+ (v == "17" && cver < 20)) // C++17 since VS20??/15.0.
{
- const string& l (cxx.leaf ().string ());
- size_t n (l.size ());
-
- if (n >= 2 &&
- (l[0] == 'c' || l[0] == 'C') &&
- (l[1] == 'l' || l[1] == 'L') &&
- (n == 2 || l[2] == '.' || l[2] == '-'))
- {
- path p (cxx.directory ());
- p /= "*";
- p += l.c_str () + 2;
- pattern = move (p).string ();
- }
+ fail << "C++" << v << " is not supported by "
+ << cast<string> (r[x_signature]) <<
+ info << "required by " << project (r) << '@' << r.out_path ();
}
}
+
+ return false;
+ }
+ else
+ {
+ // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with
+ // older versions of the compilers.
+ //
+ s = "-std=";
+
+ if (v == "98")
+ s += "c++98";
+ else if (v == "03")
+ s += "c++03";
+ else if (v == "11")
+ s += "c++0x";
+ else if (v == "14")
+ s += "c++1y";
+ else if (v == "17")
+ s += "c++1z";
else
- {
- // When cross-compiling the whole toolchain is normally prefixed
- // with the target triplet, e.g., x86_64-w64-mingw32-{g++,ar,ld}.
- //
- const string& t (ci.target);
- size_t n (t.size ());
+ s += v; // In case the user specifies something like 'gnu++17'.
- if (cxx.size () > n + 1)
- {
- const string& l (cxx.leaf ().string ());
-
- if (l.size () > n + 1 && l.compare (0, n, t) == 0 && l[n] == '-')
- {
- path p (cxx.directory ());
- p /= t;
- p += "-*";
- pattern = move (p).string ();
- }
- }
- }
+ return true;
+ }
+ }
- if (!pattern.empty ())
- bin_hints.assign ("config.bin.pattern") = move (pattern);
+ bool
+ config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>& m,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cxx::config_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
- // Split/canonicalize the target.
+ if (first)
+ {
+ // Load cc.vars so that we can cache all the cc.* variables.
//
+ if (!cast_false<bool> (b["cc.vars.loaded"]))
+ load_module ("cc.vars", r, b, loc);
- // Did the user ask us to use config.sub?
+ // Enter all the variables and initialize the module data.
//
- if (ops.config_sub_specified ())
- {
- ci.target = run<string> (ops.config_sub (),
- ci.target.c_str (),
- [] (string& l) {return move (l);});
- l5 ([&]{trace << "config.sub target: '" << ci.target << "'";});
- }
-
- try
- {
- string canon;
- triplet t (ci.target, canon);
+ auto& v (var_pool);
- l5 ([&]{trace << "canonical target: '" << canon << "'; "
- << "class: " << t.class_;});
+ cc::config_data d {
+ cc::lang::cxx,
- // Pass the target we extracted from the C++ compiler as a config
- // hint to the bin module.
- //
- bin_hints.assign ("config.bin.target") = canon;
+ "cxx",
+ "c++",
+ "g++",
- // Enter as cxx.target.{cpu,vendor,system,version,class}.
+ // Note: some overridable, some not.
//
- r.assign<string> ("cxx.target") = move (canon);
- r.assign<string> ("cxx.target.cpu") = move (t.cpu);
- r.assign<string> ("cxx.target.vendor") = move (t.vendor);
- r.assign<string> ("cxx.target.system") = move (t.system);
- r.assign<string> ("cxx.target.version") = move (t.version);
- r.assign<string> ("cxx.target.class") = move (t.class_);
- }
- catch (const invalid_argument& e)
- {
- // This is where we suggest that the user specifies --config-sub to
- // help us out.
- //
- fail << "unable to parse compiler target '" << ci.target << "': "
- << e.what () <<
- info << "consider using the --config-sub option";
- }
- }
-
- const string& cid (cast<string> (r["cxx.id"]));
- const string& tsys (cast<string> (r["cxx.target.system"]));
-
- // Initialize the bin module. Only do this if it hasn't already been
- // loaded so that we don't overwrite user's bin.* settings.
- //
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, bin_hints);
-
- // Verify bin's target matches ours.
- //
- {
- const string& bt (cast<string> (r["bin.target"]));
- const string& ct (cast<string> (r["cxx.target"]));
-
- if (bt != ct)
- fail (loc) << "bin and cxx module target platform mismatch" <<
- info << "bin.target is " << bt <<
- info << "cxx.target is " << ct;
+ v.insert<path> ("config.cxx", true),
+ v.insert<strings> ("config.cxx.poptions", true),
+ v.insert<strings> ("config.cxx.coptions", true),
+ v.insert<strings> ("config.cxx.loptions", true),
+ v.insert<strings> ("config.cxx.libs", true),
+
+ v.insert<strings> ("cxx.poptions"),
+ v.insert<strings> ("cxx.coptions"),
+ v.insert<strings> ("cxx.loptions"),
+ v.insert<strings> ("cxx.libs"),
+
+ v["cc.poptions"],
+ v["cc.coptions"],
+ v["cc.loptions"],
+ v["cc.libs"],
+
+ v.insert<strings> ("cxx.export.poptions"),
+ v.insert<strings> ("cxx.export.coptions"),
+ v.insert<strings> ("cxx.export.loptions"),
+ v.insert<strings> ("cxx.export.libs"),
+
+ v["cc.export.poptions"],
+ v["cc.export.coptions"],
+ v["cc.export.loptions"],
+ v["cc.export.libs"],
+
+ v.insert<string> ("cxx.std", true),
+
+ v.insert<string> ("cxx.id"),
+ v.insert<string> ("cxx.id.type"),
+ v.insert<string> ("cxx.id.variant"),
+
+ v.insert<string> ("cxx.version"),
+ v.insert<uint64_t> ("cxx.version.major"),
+ v.insert<uint64_t> ("cxx.version.minor"),
+ v.insert<uint64_t> ("cxx.version.patch"),
+ v.insert<string> ("cxx.version.build"),
+
+ v.insert<string> ("cxx.signature"),
+ v.insert<string> ("cxx.checksum"),
+
+ v.insert<string> ("cxx.target"),
+ v.insert<string> ("cxx.target.cpu"),
+ v.insert<string> ("cxx.target.vendor"),
+ v.insert<string> ("cxx.target.system"),
+ v.insert<string> ("cxx.target.version"),
+ v.insert<string> ("cxx.target.class")
+ };
+
+ assert (m == nullptr);
+ m.reset (new config_module (move (d)));
}
- // Load the bin.ar module unless we were asked to only build shared
- // libraries.
- //
- if (auto l = r["config.bin.lib"])
- {
- if (cast<string> (l) != "shared")
- {
- if (!cast_false<bool> (b["bin.ar.loaded"]))
- load_module ("bin.ar", r, b, loc, false, bin_hints);
- }
- }
+ static_cast<config_module&> (*m).init (r, b, loc, first, hints);
+ return true;
+ }
- // In the VC world you link things directly with link.exe so load the
- // bin.ld module.
- //
- if (cid == "msvc")
- {
- if (!cast_false<bool> (b["bin.ld.loaded"]))
- load_module ("bin.ld", r, b, loc, false, bin_hints);
- }
+ static const target_type* hdr[] =
+ {
+ &hxx::static_type,
+ &ixx::static_type,
+ &txx::static_type,
+ &h::static_type,
+ nullptr
+ };
+
+ static const target_type* inc[] =
+ {
+ &hxx::static_type,
+ &ixx::static_type,
+ &txx::static_type,
+ &cxx::static_type,
+ &h::static_type,
+ &c::static_type,
+ nullptr
+ };
- // If our target is MinGW, then we will need the resource compiler
- // (windres) in order to embed the manifest.
- //
- if (tsys == "mingw32")
- {
- if (!cast_false<bool> (b["bin.rc.loaded"]))
- load_module ("bin.rc", r, b, loc, false, bin_hints);
- }
+ bool
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>& m,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cxx::init");
+ l5 ([&]{trace << "for " << b.out_path ();});
- // Register target types.
+ // Load cxx.config.
//
- {
- auto& t (b.target_types);
-
- t.insert<h> ();
- t.insert<c> ();
+ if (!cast_false<bool> (b["cxx.config.loaded"]))
+ load_module ("cxx.config", r, b, loc, false, hints);
- t.insert<cxx> ();
- t.insert<hxx> ();
- t.insert<ixx> ();
- t.insert<txx> ();
- }
-
- // Register rules.
- //
+ if (first)
{
- using namespace bin;
-
- auto& r (b.rules);
-
- // We register for configure so that we detect unresolved imports
- // during configuration rather that later, e.g., during update.
- //
- // @@ Should we check if install module was loaded (see bin)?
- //
-
- r.insert<obje> (perform_update_id, "cxx.compile", compile::instance);
- r.insert<obje> (perform_clean_id, "cxx.compile", compile::instance);
- r.insert<obje> (configure_update_id, "cxx.compile", compile::instance);
+ config_module& cm (*r.modules.lookup<config_module> ("cxx.config"));
- r.insert<exe> (perform_update_id, "cxx.link", link::instance);
- r.insert<exe> (perform_clean_id, "cxx.link", link::instance);
- r.insert<exe> (configure_update_id, "cxx.link", link::instance);
+ cc::data d {
+ cm,
- r.insert<exe> (perform_install_id, "cxx.install", install::instance);
+ "cxx.compile",
+ "cxx.link",
+ "cxx.install",
- // Only register static object/library rules if the bin.ar module is
- // loaded (by us or by the user).
- //
- if (cast_false<bool> (b["bin.ar.loaded"]))
- {
- r.insert<obja> (perform_update_id, "cxx.compile", compile::instance);
- r.insert<obja> (perform_clean_id, "cxx.compile", compile::instance);
- r.insert<obja> (configure_update_id, "cxx.compile", compile::instance);
+ cast<string> (r[cm.x_id]),
+ cast<string> (r[cm.x_target]),
+ cast<string> (r[cm.x_target_system]),
+ cast<string> (r[cm.x_target_class]),
- r.insert<liba> (perform_update_id, "cxx.link", link::instance);
- r.insert<liba> (perform_clean_id, "cxx.link", link::instance);
- r.insert<liba> (configure_update_id, "cxx.link", link::instance);
+ cxx::static_type,
+ hdr,
+ inc
+ };
- r.insert<liba> (perform_install_id, "cxx.install", install::instance);
- }
-
- r.insert<objs> (perform_update_id, "cxx.compile", compile::instance);
- r.insert<objs> (perform_clean_id, "cxx.compile", compile::instance);
- r.insert<objs> (configure_update_id, "cxx.compile", compile::instance);
-
- r.insert<libs> (perform_update_id, "cxx.link", link::instance);
- r.insert<libs> (perform_clean_id, "cxx.link", link::instance);
- r.insert<libs> (configure_update_id, "cxx.link", link::instance);
-
- r.insert<libs> (perform_install_id, "cxx.install", install::instance);
+ assert (m == nullptr);
+ m.reset (new module (move (d)));
}
- // Configure "installability" of our target types.
- //
- using namespace install;
-
- install_path<hxx> (b, dir_path ("include")); // Into install.include.
- install_path<ixx> (b, dir_path ("include"));
- install_path<txx> (b, dir_path ("include"));
- install_path<h> (b, dir_path ("include"));
-
+ static_cast<module&> (*m).init (r, b, loc, first, hints);
return true;
}
}