aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-08-09 11:31:53 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-08-12 17:04:22 +0200
commit9fa5f73d00905568e8979d0c93ec4a8f645c81d5 (patch)
treef2bf937fa256c0ef2c9bbe05d3655d1985719405
parenta1b2319ff2ddc8a6f139ee364cabe236ca62e23e (diff)
Implement support for C compilation
We now have two new modules: cc (c-common) and c.
-rwxr-xr-xbootstrap2
-rw-r--r--build2/algorithm10
-rw-r--r--build2/algorithm.cxx48
-rw-r--r--build2/algorithm.ixx15
-rw-r--r--build2/algorithm.txx58
-rw-r--r--build2/b.cxx46
-rw-r--r--build2/bin/module36
-rw-r--r--build2/bin/module.cxx224
-rw-r--r--build2/buildfile25
-rw-r--r--build2/c/init37
-rw-r--r--build2/c/init.cxx240
-rw-r--r--build2/c/target22
-rw-r--r--build2/cc/common172
-rw-r--r--build2/cc/compile82
-rw-r--r--build2/cc/compile.cxx (renamed from build2/cxx/compile.cxx)301
-rw-r--r--build2/cc/guess (renamed from build2/cxx/guess)47
-rw-r--r--build2/cc/guess.cxx (renamed from build2/cxx/guess.cxx)288
-rw-r--r--build2/cc/init55
-rw-r--r--build2/cc/init.cxx321
-rw-r--r--build2/cc/install (renamed from build2/cxx/install)22
-rw-r--r--build2/cc/install.cxx (renamed from build2/cxx/install.cxx)20
-rw-r--r--build2/cc/link78
-rw-r--r--build2/cc/link.cxx (renamed from build2/cxx/link.cxx)384
-rw-r--r--build2/cc/module59
-rw-r--r--build2/cc/module.cxx291
-rw-r--r--build2/cc/msvc.cxx (renamed from build2/cxx/msvc.cxx)53
-rw-r--r--build2/cc/target48
-rw-r--r--build2/cc/target.cxx39
-rw-r--r--build2/cc/types32
-rw-r--r--build2/cc/utility (renamed from build2/cxx/common)58
-rw-r--r--build2/cc/utility.cxx (renamed from build2/cxx/common.cxx)54
-rw-r--r--build2/cc/utility.ixx33
-rw-r--r--build2/cc/windows-manifest.cxx (renamed from build2/cxx/windows-manifest.cxx)16
-rw-r--r--build2/cc/windows-rpath.cxx (renamed from build2/cxx/windows-rpath.cxx)17
-rw-r--r--build2/cxx/compile37
-rw-r--r--build2/cxx/link48
-rw-r--r--build2/cxx/module9
-rw-r--r--build2/cxx/module.cxx505
-rw-r--r--build2/cxx/target26
-rw-r--r--build2/cxx/target.cxx24
-rw-r--r--build2/cxx/utility42
-rw-r--r--build2/cxx/utility.cxx109
-rw-r--r--build2/cxx/utility.ixx33
-rw-r--r--build2/diagnostics40
-rw-r--r--build2/diagnostics.cxx6
-rw-r--r--build2/prerequisite3
-rw-r--r--build2/rule11
-rw-r--r--build2/target23
-rw-r--r--build2/target-type24
-rw-r--r--build2/target.cxx6
-rw-r--r--build2/utility39
-rw-r--r--build2/utility.ixx49
-rw-r--r--build2/variable6
53 files changed, 2914 insertions, 1359 deletions
diff --git a/bootstrap b/bootstrap
index 6ac5499..24261b8 100755
--- a/bootstrap
+++ b/bootstrap
@@ -106,6 +106,8 @@ src="build2/*.cxx"
src="$src build2/config/*.cxx"
src="$src build2/dist/*.cxx"
src="$src build2/bin/*.cxx"
+src="$src build2/c/*.cxx"
+src="$src build2/cc/*.cxx"
src="$src build2/cxx/*.cxx"
src="$src build2/cli/*.cxx"
src="$src build2/test/*.cxx"
diff --git a/build2/algorithm b/build2/algorithm
index d52cca9..651ef24 100644
--- a/build2/algorithm
+++ b/build2/algorithm
@@ -201,6 +201,15 @@ namespace build2
T*
execute_prerequisites (action, target&, const timestamp&);
+ target*
+ execute_prerequisites (const target_type&,
+ action, target&, const timestamp&);
+
+ template <typename T>
+ T*
+ execute_prerequisites (const target_type&,
+ action, target&, const timestamp&);
+
// Return noop_recipe instead of using this function directly.
//
target_state
@@ -243,6 +252,5 @@ namespace build2
}
#include <build2/algorithm.ixx>
-#include <build2/algorithm.txx>
#endif // BUILD2_ALGORITHM
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index a7126cc..cae6645 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -454,6 +454,54 @@ namespace build2
return e;
}
+ target*
+ execute_prerequisites (const target_type& tt,
+ action a, target& t, const timestamp& mt)
+ {
+ bool e (mt == timestamp_nonexistent);
+ target* r (nullptr);
+
+ for (target* pt: t.prerequisite_targets)
+ {
+ if (pt == nullptr) // Skip ignored.
+ continue;
+
+ target_state ts (execute (a, *pt));
+
+ if (!e)
+ {
+ // If this is an mtime-based target, then compare timestamps.
+ //
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
+ {
+ timestamp mp (mpt->mtime ());
+
+ // What do we do if timestamps are equal? This can happen, for
+ // example, on filesystems that don't have subsecond resolution.
+ // There is not much we can do here except detect the case where
+ // the prerequisite was changed in this run which means the
+ // action must be executed on the target as well.
+ //
+ if (mt < mp || (mt == mp && ts == target_state::changed))
+ e = true;
+ }
+ else
+ {
+ // Otherwise we assume the prerequisite is newer if it was changed.
+ //
+ if (ts == target_state::changed)
+ e = true;
+ }
+ }
+
+ if (pt->is_a (tt))
+ r = pt;
+ }
+
+ assert (r != nullptr);
+ return e ? r : nullptr;
+ }
+
target_state
noop_action (action a, target& t)
{
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index 17c9d8d..c9c6b02 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -200,4 +200,19 @@ namespace build2
default: return execute_impl (a, t);
}
}
+
+ template <typename T>
+ inline T*
+ execute_prerequisites (action a, target& t, const timestamp& mt)
+ {
+ return static_cast<T*> (execute_prerequisites (T::static_type, a, t, mt));
+ }
+
+ template <typename T>
+ inline T*
+ execute_prerequisites (const target_type& tt,
+ action a, target& t, const timestamp& mt)
+ {
+ return static_cast<T*> (execute_prerequisites (tt, a, t, mt));
+ }
}
diff --git a/build2/algorithm.txx b/build2/algorithm.txx
deleted file mode 100644
index 1431d32..0000000
--- a/build2/algorithm.txx
+++ /dev/null
@@ -1,58 +0,0 @@
-// file : build2/algorithm.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build2
-{
- template <typename T>
- T*
- execute_prerequisites (action a, target& t, const timestamp& mt)
- {
- //@@ Can factor the bulk of it into a non-template code. Can
- // either do a function template that will do dynamic_cast check
- // or can scan the target type info myself. I think latter.
- //
- bool e (mt == timestamp_nonexistent);
- T* r (nullptr);
-
- for (target* pt: t.prerequisite_targets)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- target_state ts (execute (a, *pt));
-
- if (!e)
- {
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- // What do we do if timestamps are equal? This can happen, for
- // example, on filesystems that don't have subsecond resolution.
- // There is not much we can do here except detect the case where
- // the prerequisite was changed in this run which means the
- // action must be executed on the target as well.
- //
- if (mt < mp || (mt == mp && ts == target_state::changed))
- e = true;
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- e = true;
- }
- }
-
- if (T* tmp = dynamic_cast<T*> (pt))
- r = tmp;
- }
-
- assert (r != nullptr);
- return e ? r : nullptr;
- }
-}
diff --git a/build2/b.cxx b/build2/b.cxx
index a14b998..5167cf6 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -41,6 +41,8 @@ using namespace std;
#include <build2/config/module>
#include <build2/dist/module>
#include <build2/bin/module>
+#include <build2/c/init>
+#include <build2/cc/init>
#include <build2/cxx/module>
#include <build2/cli/module>
#include <build2/test/module>
@@ -183,19 +185,37 @@ main (int argc, char* argv[])
// Register builtin modules.
//
- builtin_modules["config"] = module_functions {&config::boot,
- &config::init};
- builtin_modules["dist"] = module_functions {&dist::boot, &dist::init};
- builtin_modules["test"] = module_functions {&test::boot, &test::init};
- builtin_modules["install"] = module_functions {&install::boot,
- &install::init};
-
- builtin_modules["bin"] = module_functions {nullptr, &bin::init};
- builtin_modules["bin.ar"] = module_functions {nullptr, &bin::ar_init};
- builtin_modules["bin.ld"] = module_functions {nullptr, &bin::ld_init};
- builtin_modules["bin.rc"] = module_functions {nullptr, &bin::rc_init};
- builtin_modules["cxx"] = module_functions {nullptr, &cxx::init};
- builtin_modules["cli"] = module_functions {nullptr, &cli::init};
+ {
+ using mf = module_functions;
+ auto& bm (builtin_modules);
+
+ bm["config"] = mf {&config::boot, &config::init};
+ bm["dist"] = mf {&dist::boot, &dist::init};
+ bm["test"] = mf {&test::boot, &test::init};
+ bm["install"] = mf {&install::boot, &install::init};
+
+ bm["bin.config"] = mf {nullptr, &bin::config_init};
+ bm["bin"] = mf {nullptr, &bin::init};
+ bm["bin.ar.config"] = mf {nullptr, &bin::ar_config_init};
+ bm["bin.ar"] = mf {nullptr, &bin::ar_init};
+ bm["bin.ld.config"] = mf {nullptr, &bin::ld_config_init};
+ bm["bin.ld"] = mf {nullptr, &bin::ld_init};
+ bm["bin.rc.config"] = mf {nullptr, &bin::rc_config_init};
+ bm["bin.rc"] = mf {nullptr, &bin::rc_init};
+
+ bm["cc.vars"] = mf {nullptr, &cc::vars_init};
+ bm["cc.config"] = mf {nullptr, &cc::config_init};
+ bm["cc.core"] = mf {nullptr, &cc::core_init};
+ bm["cc"] = mf {nullptr, &cc::init};
+
+ bm["c.config"] = mf {nullptr, &c::config_init};
+ bm["c"] = mf {nullptr, &c::init};
+
+ bm["cxx.config"] = mf {nullptr, &cxx::config_init};
+ bm["cxx"] = mf {nullptr, &cxx::init};
+
+ bm["cli"] = mf {nullptr, &cli::init};
+ }
// Figure out work and home directories.
//
diff --git a/build2/bin/module b/build2/bin/module
index 8fb9274..8cf0255 100644
--- a/build2/bin/module
+++ b/build2/bin/module
@@ -15,6 +15,15 @@ namespace build2
namespace bin
{
bool
+ config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
init (scope&,
scope&,
const location&,
@@ -24,6 +33,15 @@ namespace build2
const variable_map&);
bool
+ ar_config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
ar_init (scope&,
scope&,
const location&,
@@ -33,6 +51,15 @@ namespace build2
const variable_map&);
bool
+ ld_config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
ld_init (scope&,
scope&,
const location&,
@@ -42,6 +69,15 @@ namespace build2
const variable_map&);
bool
+ rc_config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
rc_init (scope&,
scope&,
const location&,
diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx
index 281a2e4..6d9cda7 100644
--- a/build2/bin/module.cxx
+++ b/build2/bin/module.cxx
@@ -33,39 +33,19 @@ namespace build2
static const strings liba_lib {"static"};
static const strings libs_lib {"shared"};
- // Apply the specified stem to the config.bin.pattern. If there is no
- // pattern, then return the stem itself. Assume the pattern is valid,
- // i.e., contains single '*'.
- //
- static string
- apply (const lookup& pattern, const char* stem)
- {
- if (!pattern)
- return stem;
-
- const string& p (cast<string> (pattern));
- size_t i (p.find ('*'));
- assert (i != string::npos);
-
- string r (p, 0, i++);
- r.append (stem);
- r.append (p, i, p.size () - i);
- return r;
- }
-
bool
- init (scope& r,
- scope& b,
- const location& loc,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
+ config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
{
- tracer trace ("bin::init");
+ tracer trace ("bin::config_init");
l5 ([&]{trace << "for " << b.out_path ();});
- // Enter module variables.
+ // Enter configuration variables.
//
if (first)
{
@@ -87,8 +67,6 @@ namespace build2
v.insert<strings> ("bin.liba.lib");
v.insert<strings> ("bin.libs.lib");
v.insert<dir_paths> ("bin.rpath");
-
- v.insert<string> ("bin.libprefix", true);
}
// Configure.
@@ -166,7 +144,7 @@ namespace build2
bool hint (false);
if (v == nullptr)
{
- if (auto l = config_hints[var])
+ if (auto l = hints[var])
{
v = l.value;
hint = true;
@@ -241,7 +219,7 @@ namespace build2
//
if (v == nullptr)
{
- if (auto l = config_hints[var])
+ if (auto l = hints[var])
v = l.value;
}
@@ -276,6 +254,35 @@ namespace build2
}
}
+ return true;
+ }
+
+ bool
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Enter the rest of the variables.
+ //
+ if (first)
+ {
+ auto& v (var_pool);
+
+ v.insert<string> ("bin.libprefix", true);
+ }
+
+ // Load bin.config.
+ //
+ if (!cast_false<bool> (b["bin.config.loaded"]))
+ load_module ("bin.config", r, b, loc, false, hints);
+
// Cache some config values we will be needing below.
//
const string& tclass (cast<string> (r["bin.target.class"]));
@@ -363,24 +370,44 @@ namespace build2
return true;
}
+ // Apply the specified stem to the config.bin.pattern. If there is no
+ // pattern, then return the stem itself. Assume the pattern is valid,
+ // i.e., contains single '*'.
+ //
+ static string
+ apply (const lookup& pattern, const char* stem)
+ {
+ if (!pattern)
+ return stem;
+
+ const string& p (cast<string> (pattern));
+ size_t i (p.find ('*'));
+ assert (i != string::npos);
+
+ string r (p, 0, i++);
+ r.append (stem);
+ r.append (p, i, p.size () - i);
+ return r;
+ }
+
bool
- ar_init (scope& r,
- scope& b,
- const location& loc,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
+ ar_config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
{
- tracer trace ("bin::ar_init");
+ tracer trace ("bin::ar_config_init");
l5 ([&]{trace << "for " << b.out_path ();});
- // Make sure the bin core is loaded.
+ // Make sure bin.config is loaded.
//
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, config_hints);
+ if (!cast_false<bool> (b["bin.config.loaded"]))
+ load_module ("bin.config", r, b, loc, false, hints);
- // Enter module variables.
+ // Enter configuration variables.
//
if (first)
{
@@ -480,23 +507,46 @@ namespace build2
}
bool
- ld_init (scope& r,
+ ar_init (scope& r,
scope& b,
const location& loc,
unique_ptr<module_base>&,
- bool first,
bool,
- const variable_map& config_hints)
+ bool,
+ const variable_map& hints)
{
- tracer trace ("bin::ld_init");
+ tracer trace ("bin::ar_init");
l5 ([&]{trace << "for " << b.out_path ();});
- // Make sure the bin core is loaded.
+ // Make sure the bin core and ar.config are loaded.
//
if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, config_hints);
+ load_module ("bin", r, b, loc, false, hints);
+
+ if (!cast_false<bool> (b["bin.ar.config.loaded"]))
+ load_module ("bin.ar.config", r, b, loc, false, hints);
+
+ return true;
+ }
- // Enter module variables.
+ bool
+ ld_config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ld_config_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Make sure bin.config is loaded.
+ //
+ if (!cast_false<bool> (b["bin.config.loaded"]))
+ load_module ("bin.config", r, b, loc, false, hints);
+
+ // Enter configuration variables.
//
if (first)
{
@@ -542,6 +592,29 @@ namespace build2
r.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
}
+ return true;
+ }
+
+ bool
+ ld_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ld_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Make sure the bin core and ld.config are loaded.
+ //
+ if (!cast_false<bool> (b["bin.loaded"]))
+ load_module ("bin", r, b, loc, false, hints);
+
+ if (!cast_false<bool> (b["bin.ld.config.loaded"]))
+ load_module ("bin.ld.config", r, b, loc, false, hints);
+
const string& lid (cast<string> (r["bin.ld.id"]));
// Register the pdb{} target if using the VC toolchain.
@@ -559,23 +632,23 @@ namespace build2
}
bool
- rc_init (scope& r,
- scope& b,
- const location& loc,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
+ rc_config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
{
- tracer trace ("bin::rc_init");
+ tracer trace ("bin::rc_config_init");
l5 ([&]{trace << "for " << b.out_path ();});
- // Make sure the bin core is loaded.
+ // Make sure bin.config is loaded.
//
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, config_hints);
+ if (!cast_false<bool> (b["bin.config.loaded"]))
+ load_module ("bin.config", r, b, loc, false, hints);
- // Enter module variables.
+ // Enter configuration variables.
//
if (first)
{
@@ -623,5 +696,28 @@ namespace build2
return true;
}
+
+ bool
+ rc_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::rc_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Make sure the bin core and rc.config are loaded.
+ //
+ if (!cast_false<bool> (b["bin.loaded"]))
+ load_module ("bin", r, b, loc, false, hints);
+
+ if (!cast_false<bool> (b["bin.rc.config.loaded"]))
+ load_module ("bin.rc.config", r, b, loc, false, hints);
+
+ return true;
+ }
}
}
diff --git a/build2/buildfile b/build2/buildfile
index 3986015..b0e9cf0 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -5,7 +5,7 @@
import libs = libbutl%lib{butl}
exe{b}: \
- {hxx ixx txx cxx}{ algorithm } \
+ {hxx ixx cxx}{ algorithm } \
{ cxx}{ b } \
{hxx ixx cxx}{ b-options } \
{hxx txx cxx}{ context } \
@@ -38,23 +38,28 @@ exe{b}: \
bin/{hxx cxx}{ module } \
bin/{hxx cxx}{ rule } \
bin/{hxx cxx}{ target } \
+ c/{hxx cxx}{ init } \
+ c/{hxx }{ target } \
+ cc/{hxx }{ common } \
+ cc/{hxx cxx}{ compile } \
+ cc/{hxx cxx}{ guess } \
+ cc/{hxx cxx}{ init } \
+ cc/{hxx cxx}{ install } \
+ cc/{hxx cxx}{ link } \
+ cc/{hxx cxx}{ module } \
+ cc/{ cxx}{ msvc } \
+ cc/{hxx cxx}{ target } \
+ cc/{hxx ixx cxx}{ utility } \
+ cc/{ cxx}{ windows-manifest } \
+ cc/{ cxx}{ windows-rpath } \
cli/{hxx cxx}{ module } \
cli/{hxx cxx}{ rule } \
cli/{hxx cxx}{ target } \
config/{hxx cxx}{ module } \
config/{hxx cxx}{ operation } \
config/{hxx txx cxx}{ utility } \
- cxx/{hxx cxx}{ common } \
- cxx/{hxx cxx}{ compile } \
- cxx/{hxx cxx}{ guess } \
- cxx/{hxx cxx}{ install } \
- cxx/{hxx cxx}{ link } \
cxx/{hxx cxx}{ module } \
- cxx/{ cxx}{ msvc } \
cxx/{hxx cxx}{ target } \
- cxx/{hxx ixx cxx}{ utility } \
- cxx/{ cxx}{ windows-manifest } \
- cxx/{ cxx}{ windows-rpath } \
dist/{hxx cxx}{ module } \
dist/{hxx cxx}{ operation } \
dist/{hxx cxx}{ rule } \
diff --git a/build2/c/init b/build2/c/init
new file mode 100644
index 0000000..04f193e
--- /dev/null
+++ b/build2/c/init
@@ -0,0 +1,37 @@
+// file : build2/c/init -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_C_INIT
+#define BUILD2_C_INIT
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/module>
+
+namespace build2
+{
+ namespace c
+ {
+ bool
+ config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
+ init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+ }
+}
+
+#endif // BUILD2_C_INIT
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
new file mode 100644
index 0000000..7795181
--- /dev/null
+++ b/build2/c/init.cxx
@@ -0,0 +1,240 @@
+// file : build2/c/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/c/init>
+
+#include <build2/scope>
+#include <build2/context>
+#include <build2/diagnostics>
+
+#include <build2/cc/module>
+
+#include <build2/c/target>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace c
+ {
+ using cc::config_module;
+
+ class module: public cc::module
+ {
+ public:
+ explicit
+ module (data&& d): common (move (d)), cc::module (move (d)) {}
+
+ bool
+ translate_std (string&, scope&, const value&) const override;
+ };
+
+ bool module::
+ translate_std (string& s, scope& r, const value& val) const
+ {
+ const string& v (cast<string> (val));
+
+ if (cid == "msvc")
+ {
+ // 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 90, 99, and 11? 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).
+ //
+ // The state of affairs seem to be (from Herb Sutter's blog):
+ //
+ // 10.0 - most of C95 plus a few C99 features
+ // 11.0 - partial support for the C++11 subset of C11
+ // 12.0 - more C11 features from the C++11 subset, most of C99
+ //
+ // So let's say C99 is supported from 10.0 and C11 from 11.0. And C90
+ // is supported by everything we care to support.
+ //
+ if (v != "90")
+ {
+ uint64_t cver (cast<uint64_t> (r[x_version_major]));
+
+ if ((v == "99" && cver < 16) || // Since VS2010/10.0.
+ (v == "11" && cver < 17)) // Since VS2012/11.0.
+ {
+ fail << "C" << v << " is not supported by "
+ << cast<string> (r[x_signature]) <<
+ info << "required by " << project (r) << '@' << r.out_path ();
+ }
+ }
+
+ return false;
+ }
+ else
+ {
+ // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x
+ // for compatibility with older versions of the compilers.
+ //
+ s = "-std=";
+
+ if (v == "90")
+ s += "c90";
+ else if (v == "99")
+ s += "c9x";
+ else if (v == "11")
+ s += "c1x";
+ else
+ s += v; // In case the user specifies something like 'gnu11'.
+
+ return true;
+ }
+ }
+
+ bool
+ config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>& m,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("c::config_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ 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);
+
+ // Enter all the variables and initialize the module data.
+ //
+ auto& v (var_pool);
+
+ cc::config_data d {
+ cc::lang::c,
+
+ "c",
+ "c",
+ "gcc",
+
+ // Note: some overridable, some not.
+ //
+ v.insert<path> ("config.c", true),
+ v.insert<strings> ("config.c.poptions", true),
+ v.insert<strings> ("config.c.coptions", true),
+ v.insert<strings> ("config.c.loptions", true),
+ v.insert<strings> ("config.c.libs", true),
+
+ v.insert<strings> ("c.poptions"),
+ v.insert<strings> ("c.coptions"),
+ v.insert<strings> ("c.loptions"),
+ v.insert<strings> ("c.libs"),
+
+ v["cc.poptions"],
+ v["cc.coptions"],
+ v["cc.loptions"],
+ v["cc.libs"],
+
+ v.insert<strings> ("c.export.poptions"),
+ v.insert<strings> ("c.export.coptions"),
+ v.insert<strings> ("c.export.loptions"),
+ v.insert<strings> ("c.export.libs"),
+
+ v["cc.export.poptions"],
+ v["cc.export.coptions"],
+ v["cc.export.loptions"],
+ v["cc.export.libs"],
+
+ v.insert<string> ("c.std", true),
+
+ v.insert<string> ("c.id"),
+ v.insert<string> ("c.id.type"),
+ v.insert<string> ("c.id.variant"),
+
+ v.insert<string> ("c.version"),
+ v.insert<uint64_t> ("c.version.major"),
+ v.insert<uint64_t> ("c.version.minor"),
+ v.insert<uint64_t> ("c.version.patch"),
+ v.insert<string> ("c.version.build"),
+
+ v.insert<string> ("c.signature"),
+ v.insert<string> ("c.checksum"),
+
+ v.insert<string> ("c.target"),
+ v.insert<string> ("c.target.cpu"),
+ v.insert<string> ("c.target.vendor"),
+ v.insert<string> ("c.target.system"),
+ v.insert<string> ("c.target.version"),
+ v.insert<string> ("c.target.class")
+ };
+
+ assert (m == nullptr);
+ m.reset (new config_module (move (d)));
+ }
+
+ static_cast<config_module&> (*m).init (r, b, loc, first, hints);
+ return true;
+ }
+
+ static const target_type* hdr[] =
+ {
+ &h::static_type,
+ nullptr
+ };
+
+ static const target_type* inc[] =
+ {
+ &h::static_type,
+ &c::static_type,
+ nullptr
+ };
+
+ bool
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>& m,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("c::init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Load c.config.
+ //
+ if (!cast_false<bool> (b["c.config.loaded"]))
+ load_module ("c.config", r, b, loc, false, hints);
+
+ if (first)
+ {
+ config_module& cm (*r.modules.lookup<config_module> ("c.config"));
+
+ cc::data d {
+ cm,
+
+ "c.compile",
+ "c.link",
+ "c.install",
+
+ 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]),
+
+ c::static_type,
+ hdr,
+ inc
+ };
+
+ assert (m == nullptr);
+ m.reset (new module (move (d)));
+ }
+
+ static_cast<module&> (*m).init (r, b, loc, first, hints);
+ return true;
+ }
+ }
+}
diff --git a/build2/c/target b/build2/c/target
new file mode 100644
index 0000000..bf40306
--- /dev/null
+++ b/build2/c/target
@@ -0,0 +1,22 @@
+// file : build2/c/target -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_C_TARGET
+#define BUILD2_C_TARGET
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/cc/target>
+
+namespace build2
+{
+ namespace c
+ {
+ using cc::h;
+ using cc::c;
+ }
+}
+
+#endif // BUILD2_C_TARGET
diff --git a/build2/cc/common b/build2/cc/common
new file mode 100644
index 0000000..95f205a
--- /dev/null
+++ b/build2/cc/common
@@ -0,0 +1,172 @@
+// file : build2/cc/common -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_COMMON
+#define BUILD2_CC_COMMON
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/variable>
+
+#include <build2/cc/types>
+
+namespace build2
+{
+ namespace cc
+ {
+ // Data entries that define a concrete c-family module (e.g., c or cxx).
+ // These classes are used as a virtual bases by the rules as well as the
+ // modules. This way the member variables can be referenced as is, without
+ // any extra decorations (in other words, it is a bunch of data members
+ // that can be shared between several classes/instances).
+ //
+ struct config_data
+ {
+ 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 variable& config_x;
+ const variable& config_x_poptions;
+ const variable& config_x_coptions;
+ const variable& config_x_loptions;
+ const variable& config_x_libs;
+
+ const variable& x_poptions;
+ const variable& x_coptions;
+ const variable& x_loptions;
+ const variable& x_libs;
+
+ const variable& c_poptions; // cc.*
+ const variable& c_coptions;
+ const variable& c_loptions;
+ const variable& c_libs;
+
+ const variable& x_export_poptions;
+ const variable& x_export_coptions;
+ const variable& x_export_loptions;
+ const variable& x_export_libs;
+
+ const variable& c_export_poptions; // cc.export.*
+ const variable& c_export_coptions;
+ const variable& c_export_loptions;
+ const variable& c_export_libs;
+
+ const variable& x_std;
+
+ const variable& x_id;
+ const variable& x_id_type;
+ const variable& x_id_variant;
+
+ const variable& x_version;
+ const variable& x_version_major;
+ const variable& x_version_minor;
+ const variable& x_version_patch;
+ const variable& x_version_build;
+
+ const variable& x_signature;
+ const variable& x_checksum;
+
+ const variable& x_target;
+ const variable& x_target_cpu;
+ const variable& x_target_vendor;
+ const variable& x_target_system;
+ const variable& x_target_version;
+ const variable& x_target_class;
+ };
+
+ struct data: config_data
+ {
+ const char* x_compile; // Rule names.
+ const char* x_link;
+ const char* x_install;
+
+ // Cached values for some commonly-used variables.
+ //
+ const string& cid; // x.id
+ const string& ctg; // x.target
+ const string& tsys; // x.target.system
+ const string& tclass; // x.target.class
+
+ const target_type& x_src; // Source target type (c{}, cxx{}).
+
+ // Array of target types that are considered headers. Keep them in the
+ // most likely to appear order and terminate with NULL.
+ //
+ const target_type* const* x_hdr;
+
+ template <typename T>
+ bool
+ x_header (const T& t) const
+ {
+ for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht)
+ if (t.is_a (**ht))
+ return true;
+
+ return false;
+ }
+
+ // Array of target types that can be #include'd. Used to reverse-lookup
+ // extensions to target types. Keep them in the most likely to appear
+ // order and terminate with NULL.
+ //
+ const target_type* const* x_inc;
+
+ // Aggregate-like constructor with from-base support.
+ //
+ data (const config_data& cd,
+ const char* compile,
+ const char* link,
+ const char* install,
+ const string& id,
+ const string& tg,
+ const string& sys,
+ const string& class_,
+ const target_type& src,
+ const target_type* const* hdr,
+ const target_type* const* inc)
+ : config_data (cd),
+ x_compile (compile), x_link (link), x_install (install),
+ cid (id), ctg (tg), tsys (sys), tclass (class_),
+ x_src (src), x_hdr (hdr), x_inc (inc) {}
+ };
+
+ class common: protected data
+ {
+ public:
+ common (data&& d): data (move (d)) {}
+
+ // Language standard (x.std) mapping. T is either target or scope.
+ //
+ template <typename T>
+ void
+ append_std (cstrings& args, scope& root, T& t, string& storage) const
+ {
+ if (auto l = t[x_std])
+ if (translate_std (storage, root, *l))
+ args.push_back (storage.c_str ());
+ }
+
+ template <typename T>
+ void
+ hash_std (sha256& csum, scope& root, T& t) const
+ {
+ string s;
+ if (auto l = t[x_std])
+ if (translate_std (s, root, *l))
+ csum.append (s);
+ }
+
+ // Return true if there is an option (stored in the first argument).
+ //
+ virtual bool
+ translate_std (string&, scope&, const value&) const = 0;
+ };
+ }
+}
+
+#endif // BUILD2_CC_COMMON
diff --git a/build2/cc/compile b/build2/cc/compile
new file mode 100644
index 0000000..6e20836
--- /dev/null
+++ b/build2/cc/compile
@@ -0,0 +1,82 @@
+// file : build2/cc/compile -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_COMPILE
+#define BUILD2_CC_COMPILE
+
+#include <butl/path-map>
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/rule>
+
+#include <build2/cc/types>
+#include <build2/cc/common>
+
+namespace build2
+{
+ class depdb;
+
+ namespace cc
+ {
+ class link;
+
+ class compile: public rule, virtual common
+ {
+ public:
+ compile (data&&, const link&);
+
+ virtual match_result
+ match (action, target&, const string& hint) const;
+
+ virtual recipe
+ apply (action, target&, const match_result&) const;
+
+ target_state
+ perform_update (action, target&) const;
+
+ target_state
+ perform_clean (action, target&) const;
+
+ private:
+ // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
+ // generated headers to directories where they will be generated.
+ //
+ // We are using a prefix map of directories (dir_path_map) instead
+ // of just a map in order also cover sub-paths (e.g., <foo/more/bar>
+ // if we continue with the example). Specifically, we need to make
+ // sure we don't treat foobar as a sub-directory of foo.
+ //
+ // @@ The keys should be normalized.
+ //
+ using prefix_map = butl::dir_path_map<dir_path>;
+
+ void
+ append_prefixes (prefix_map&, target&, const variable&) const;
+
+ void
+ append_lib_prefixes (prefix_map&, target&, lorder) const;
+
+ prefix_map
+ build_prefix_map (target&, lorder) const;
+
+ // Reverse-lookup target type from extension.
+ //
+ const target_type*
+ map_extension (scope&, const string&, const string&) const;
+
+ // Header dependency injection.
+ //
+ void
+ inject (action, target&, lorder, file&, scope&, depdb&) const;
+
+ private:
+ const link& link_;
+ const string rule_id;
+ };
+ }
+}
+
+#endif // BUILD2_CC_COMPILE
diff --git a/build2/cxx/compile.cxx b/build2/cc/compile.cxx
index 56c518b..b5bcc50 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -1,16 +1,12 @@
-// file : build2/cxx/compile.cxx -*- C++ -*-
+// file : build2/cc/compile.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/cxx/compile>
+#include <build2/cc/compile>
-#include <map>
-#include <limits> // numeric_limits
#include <cstdlib> // exit()
#include <iostream> // cerr
-#include <butl/path-map>
-
#include <build2/depdb>
#include <build2/scope>
#include <build2/context>
@@ -19,26 +15,32 @@
#include <build2/diagnostics>
#include <build2/bin/target>
-#include <build2/cxx/target>
-
-#include <build2/cxx/link>
-#include <build2/cxx/common>
-#include <build2/cxx/utility>
+#include <build2/cc/link> // search_library()
+#include <build2/cc/target> // h
+#include <build2/cc/utility>
using namespace std;
using namespace butl;
namespace build2
{
- namespace cxx
+ namespace cc
{
using namespace bin;
+ compile::
+ compile (data&& d, const link& l)
+ : common (move (d)),
+ link_ (l),
+ rule_id (string (x) += ".compile 1")
+ {
+ }
+
match_result compile::
match (action a, target& t, const string&) const
{
- tracer trace ("cxx::compile::match");
+ tracer trace (x, "compile::match");
// @@ TODO:
//
@@ -46,37 +48,29 @@ namespace build2
// - if path already assigned, verify extension?
//
- // See if we have a C++ source file. Iterate in reverse so that
- // a source file specified for an obj*{} member overrides the one
- // specified for the group. Also "see through" groups.
+ // See if we have a source file. Iterate in reverse so that a source
+ // file specified for an obj*{} member overrides the one specified for
+ // the group. Also "see through" groups.
//
for (prerequisite_member p: reverse_group_prerequisite_members (a, t))
{
- if (p.is_a<cxx> ())
+ if (p.is_a (x_src))
return p;
}
- l4 ([&]{trace << "no c++ source file for target " << t;});
+ l4 ([&]{trace << "no " << x_lang << " source file for target " << t;});
return nullptr;
}
- static void
- inject_prerequisites (action, target&, lorder, cxx&, scope&, depdb&);
-
recipe compile::
apply (action a, target& xt, const match_result& mr) const
{
- tracer trace ("cxx::compile");
+ tracer trace (x, "compile::apply");
file& t (static_cast<file&> (xt));
scope& bs (t.base_scope ());
scope& rs (*bs.root_scope ());
-
- const string& cid (cast<string> (rs["cxx.id"]));
- const string& tsys (cast<string> (rs["cxx.target.system"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
otype ct (compile_type (t));
// Derive file name from target name.
@@ -129,22 +123,21 @@ namespace build2
//
fsdir* dir (inject_fsdir (a, t));
- // Search and match all the existing prerequisites. The injection
- // code (below) takes care of the ones it is adding.
+ // Search and match all the existing prerequisites. The injection code
+ // takes care of the ones it is adding.
//
- // When cleaning, ignore prerequisites that are not in the same
- // or a subdirectory of our project root.
+ // When cleaning, ignore prerequisites that are not in the same or a
+ // subdirectory of our project root.
//
optional<dir_paths> lib_paths; // Extract lazily.
for (prerequisite_member p: group_prerequisite_members (a, t))
{
// A dependency on a library is there so that we can get its
- // cxx.export.poptions. In particular, making sure it is
- // executed before us will only restrict parallelism. But we
- // do need to pre-match it in order to get its
- // prerequisite_targets populated. This is the "library
- // meta-information protocol". See also append_lib_options()
+ // *.export.poptions. In particular, making sure it is executed before
+ // us will only restrict parallelism. But we do need to pre-match it
+ // in order to get its prerequisite_targets populated. This is the
+ // "library meta-information protocol". See also append_lib_options()
// above.
//
if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
@@ -156,7 +149,7 @@ namespace build2
// any, they would be set by search_library()).
//
if (p.proj () == nullptr ||
- link::search_library (lib_paths, p.prerequisite) == nullptr)
+ link_.search_library (lib_paths, p.prerequisite) == nullptr)
{
match_only (a, p.search ());
}
@@ -184,11 +177,7 @@ namespace build2
// t.prerequisite_targets since we used standard search() and match()
// above.
//
- // @@ Ugly.
- //
- cxx& st (
- dynamic_cast<cxx&> (
- mr.target != nullptr ? *mr.target : *mr.prerequisite->target));
+ file& src (mr.as_target<file> ());
// Make sure the output directory exists.
//
@@ -208,14 +197,14 @@ namespace build2
// First should come the rule name/version.
//
- if (dd.expect ("cxx.compile 1") != nullptr)
+ if (dd.expect (rule_id) != nullptr)
l4 ([&]{trace << "rule mismatch forcing update of " << t;});
// Then the compiler checksum. Note that here we assume it
// incorporates the (default) target so that if the compiler changes
// but only in what it targets, then the checksum will still change.
//
- if (dd.expect (cast<string> (rs["cxx.checksum"])) != nullptr)
+ if (dd.expect (cast<string> (rs[x_checksum])) != nullptr)
l4 ([&]{trace << "compiler mismatch forcing update of " << t;});
// Then the options checksum.
@@ -225,7 +214,7 @@ namespace build2
//
sha256 cs;
- // Hash cxx.export.poptions from prerequisite libraries.
+ // Hash *.export.poptions from prerequisite libraries.
//
lorder lo (link_order (bs, ct));
for (prerequisite& p: group_prerequisites (t))
@@ -236,12 +225,16 @@ namespace build2
pt = &link_member (*l, lo);
if (pt->is_a<liba> () || pt->is_a<libs> ())
- hash_lib_options (cs, *pt, "cxx.export.poptions", lo);
+ hash_lib_options (cs, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
}
- hash_options (cs, t, "cxx.poptions");
- hash_options (cs, t, "cxx.coptions");
- hash_std (cs, rs, cid, t);
+ hash_options (cs, t, c_poptions);
+ hash_options (cs, t, x_poptions);
+ hash_options (cs, t, c_coptions);
+ hash_options (cs, t, x_coptions);
+ hash_std (cs, rs, t);
if (ct == otype::s)
{
@@ -256,7 +249,7 @@ namespace build2
// Finally the source file.
//
- if (dd.expect (st.path ()) != nullptr)
+ if (dd.expect (src.path ()) != nullptr)
l4 ([&]{trace << "source file mismatch forcing update of " << t;});
// If any of the above checks resulted in a mismatch (different
@@ -266,65 +259,51 @@ namespace build2
if (dd.writing () || dd.mtime () > t.mtime ())
t.mtime (timestamp_nonexistent);
- inject_prerequisites (a, t, lo, st, mr.prerequisite->scope, dd);
+ inject (a, t, lo, src, mr.prerequisite->scope, dd);
dd.close ();
}
switch (a)
{
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: return noop_recipe; // Configure update.
+ case perform_update_id:
+ return [this] (action a, target& t) {return perform_update (a, t);};
+ case perform_clean_id:
+ return [this] (action a, target& t) {return perform_clean (a, t);};
+ default:
+ return noop_recipe; // Configure update.
}
}
// Reverse-lookup target type from extension.
//
- static const target_type*
- map_extension (scope& s, const string& n, const string& e)
+ const target_type* compile::
+ map_extension (scope& s, const string& n, const string& e) const
{
- // We will just have to try all of the possible ones, in the
- // "most likely to match" order.
+ // We will just have to try all of the possible ones, in the "most
+ // likely to match" order.
//
- const variable& var (var_pool.find ("extension"));
+ const variable& var (var_pool["extension"]);
- auto test = [&s, &n, &e, &var] (const target_type& tt)
- -> const target_type*
+ auto test = [&s, &n, &e, &var] (const target_type& tt) -> bool
{
if (auto l = s.find (var, tt, n))
if (cast<string> (l) == e)
- return &tt;
+ return true;
- return nullptr;
+ return false;
};
- if (auto r = test (hxx::static_type)) return r;
- if (auto r = test (h::static_type)) return r;
- if (auto r = test (ixx::static_type)) return r;
- if (auto r = test (txx::static_type)) return r;
- if (auto r = test (cxx::static_type)) return r;
- if (auto r = test (c::static_type)) return r;
+ for (const target_type* const* p (x_inc); *p != nullptr; ++p)
+ if (test (**p)) return *p;
return nullptr;
}
- // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
- // generated headers to directories where they will be generated.
- //
- // We are using a prefix map of directories (dir_path_map) instead
- // of just a map in order also cover sub-paths (e.g., <foo/more/bar>
- // if we continue with the example). Specifically, we need to make
- // sure we don't treat foobar as a sub-directory of foo.
- //
- // @@ The keys should be canonicalized.
- //
- using prefix_map = dir_path_map<dir_path>;
-
- static void
- append_prefixes (prefix_map& m, target& t, const char* var)
+ void compile::
+ append_prefixes (prefix_map& m, target& t, const variable& var) const
{
- tracer trace ("cxx::append_prefixes");
+ tracer trace (x, "append_prefixes");
// If this target does not belong to any project (e.g, an
// "imported as installed" library), then it can't possibly
@@ -411,11 +390,11 @@ namespace build2
}
}
- // Append library prefixes based on the cxx.export.poptions variables
+ // Append library prefixes based on the *.export.poptions variables
// recursively, prerequisite libraries first.
//
- static void
- append_lib_prefixes (prefix_map& m, target& l, lorder lo)
+ void compile::
+ append_lib_prefixes (prefix_map& m, target& l, lorder lo) const
{
for (target* t: l.prerequisite_targets)
{
@@ -429,17 +408,17 @@ namespace build2
append_lib_prefixes (m, *t, lo);
}
- append_prefixes (m, l, "cxx.export.poptions");
+ append_prefixes (m, l, c_export_poptions);
+ append_prefixes (m, l, x_export_poptions);
}
- static prefix_map
- build_prefix_map (target& t, lorder lo)
+ auto compile::
+ build_prefix_map (target& t, lorder lo) const -> prefix_map
{
prefix_map m;
- // First process the include directories from prerequisite
- // libraries. Note that here we don't need to see group
- // members (see apply()).
+ // First process the include directories from prerequisite libraries.
+ // Note that here we don't need to see group members (see apply()).
//
for (prerequisite& p: group_prerequisites (t))
{
@@ -454,7 +433,8 @@ namespace build2
// Then process our own.
//
- append_prefixes (m, t, "cxx.poptions");
+ append_prefixes (m, t, c_poptions);
+ append_prefixes (m, t, x_poptions);
return m;
}
@@ -521,11 +501,10 @@ namespace build2
return r;
}
- // Extract the include path from the VC++ /showIncludes output line.
- // Return empty string if the line is not an include note or include
- // error. Set the good_error flag if it is an include error (which means
- // the process will terminate with the error status that needs to be
- // ignored).
+ // Extract the include path from the VC /showIncludes output line. Return
+ // empty string if the line is not an include note or include error. Set
+ // the good_error flag if it is an include error (which means the process
+ // will terminate with the error status that needs to be ignored).
//
static string
next_show (const string& l, bool& good_error)
@@ -534,7 +513,7 @@ namespace build2
//
assert (!good_error);
- // VC++ /showIncludes output. The first line is the file being
+ // VC /showIncludes output. The first line is the file being
// compiled. Then we have the list of headers, one per line, in this
// form (text can presumably be translated):
//
@@ -634,11 +613,15 @@ namespace build2
}
}
- static void
- inject_prerequisites (action a, target& t, lorder lo,
- cxx& s, scope& ds, depdb& dd)
+ void compile::
+ inject (action a,
+ target& t,
+ lorder lo,
+ file& src,
+ scope& ds,
+ depdb& dd) const
{
- tracer trace ("cxx::compile::inject_prerequisites");
+ tracer trace (x, "compile::inject");
l6 ([&]{trace << "target: " << t;});
@@ -647,28 +630,24 @@ namespace build2
//
auto g (
make_exception_guard (
- [&s]()
+ [&src]()
{
- info << "while extracting header dependencies from " << s;
+ info << "while extracting header dependencies from " << src;
}));
scope& rs (t.root_scope ());
- const string& cid (cast<string> (rs["cxx.id"]));
// Initialize lazily, only if required.
//
cstrings args;
- string cxx_std; // Storage.
+ string std; // Storage.
- auto init_args = [&t, lo, &s, &rs, &cid, &args, &cxx_std] ()
+ auto init_args = [&t, lo, &src, &rs, &args, &std, this] ()
{
- const path& cxx (cast<path> (rs["config.cxx"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
+ args.push_back (cast<path> (rs[config_x]).string ().c_str ());
- args.push_back (cxx.string ().c_str ());
-
- // Add cxx.export.poptions from prerequisite libraries. Note
- // that here we don't need to see group members (see apply()).
+ // Add *.export.poptions from prerequisite libraries. Note that here
+ // we don't need to see group members (see apply()).
//
for (prerequisite& p: group_prerequisites (t))
{
@@ -678,15 +657,20 @@ namespace build2
pt = &link_member (*l, lo);
if (pt->is_a<liba> () || pt->is_a<libs> ())
- append_lib_options (args, *pt, "cxx.export.poptions", lo);
+ append_lib_options (args, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
}
- append_options (args, t, "cxx.poptions");
+ append_options (args, t, c_poptions);
+ append_options (args, t, x_poptions);
- // Some C++ options (e.g., -std, -m) affect the preprocessor.
+ // Some compile options (e.g., -std, -m) affect the preprocessor.
//
- append_options (args, t, "cxx.coptions");
- append_std (args, rs, cid, t, cxx_std);
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
+
+ append_std (args, rs, t, std);
if (t.is_a<objs> ())
{
@@ -703,15 +687,15 @@ namespace build2
// See perform_update() for details on overriding the default
// exceptions and runtime.
//
- if (!find_option_prefix ("/EH", args))
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
args.push_back ("/EHsc");
if (!find_option_prefixes ({"/MD", "/MT"}, args))
args.push_back ("/MD");
args.push_back ("/EP"); // Preprocess to stdout.
- args.push_back ("/TP"); // Preprocess as C++.
args.push_back ("/showIncludes"); // Goes to sterr becasue of /EP.
+ args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as.
}
else
{
@@ -734,7 +718,7 @@ namespace build2
// @@ We will also have to use absolute -I paths to guarantee
// that. Or just detect relative paths and error out?
//
- args.push_back (s.path ().string ().c_str ());
+ args.push_back (src.path ().string ().c_str ());
args.push_back (nullptr);
};
@@ -826,7 +810,7 @@ namespace build2
// from the depdb cache or from the compiler run. Return whether the
// extraction process should be restarted.
//
- auto add = [&trace, &update, &pm, a, &t, lo, &ds, &dd]
+ auto add = [&trace, &update, &pm, a, &t, lo, &ds, &dd, this]
(path f, bool cache) -> bool
{
if (!f.absolute ())
@@ -926,8 +910,8 @@ namespace build2
out = out_src (d, *rs);
}
- // If it is outside any project, or the project doesn't have
- // such an extension, assume it is a plain old C header.
+ // If it is outside any project, or the project doesn't have such an
+ // extension, assume it is a plain old C header.
//
if (tt == nullptr)
tt = &h::static_type;
@@ -980,7 +964,7 @@ namespace build2
// But, before we do all that, make sure the source file itself if up to
// date.
//
- if (update (s, dd.mtime ()))
+ if (update (src, dd.mtime ()))
{
// If the file got updated or is newer than the database, then we
// cannot rely on the cache any further. However, the cached data
@@ -1099,7 +1083,7 @@ namespace build2
// this case the first line (and everything after it) is
// presumably diagnostics.
//
- if (l != s.path ().leaf ().string ())
+ if (l != src.path ().leaf ().string ())
{
text << l;
bad_error = true;
@@ -1205,8 +1189,8 @@ namespace build2
}
}
- // In case of VC++, we are parsing stderr and if things go
- // south, we need to copy the diagnostics for the user to see.
+ // In case of VC, we are parsing stderr and if things go south,
+ // we need to copy the diagnostics for the user to see.
//
// Note that the eof check is important: if the stream is at
// eof, this and all subsequent writes to cerr will fail (and
@@ -1227,12 +1211,14 @@ namespace build2
throw failed ();
}
else if (bad_error)
- fail << "expected error exist status from C++ compiler";
+ fail << "expected error exist status from " << x_lang
+ << " compiler";
}
catch (const ifdstream::failure&)
{
pr.wait ();
- fail << "unable to read C++ compiler header dependency output";
+ fail << "unable to read " << x_lang << " compiler header "
+ << "dependency output";
}
}
catch (const process_error& e)
@@ -1258,24 +1244,19 @@ namespace build2
msvc_filter_cl (ifdstream&, const path& src);
target_state compile::
- perform_update (action a, target& xt)
+ perform_update (action a, target& xt) const
{
file& t (static_cast<file&> (xt));
- cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
+ file* s (execute_prerequisites<file> (x_src, a, t, t.mtime ()));
if (s == nullptr)
return target_state::unchanged;
scope& bs (t.base_scope ());
scope& rs (*bs.root_scope ());
-
- const path& cxx (cast<path> (rs["config.cxx"]));
- const string& cid (cast<string> (rs["cxx.id"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
otype ct (compile_type (t));
- cstrings args {cxx.string ().c_str ()};
+ cstrings args {cast<path> (rs[config_x]).string ().c_str ()};
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
@@ -1283,8 +1264,8 @@ namespace build2
path relo (relative (t.path ()));
path rels (relative (s->path ()));
- // Add cxx.export.poptions from prerequisite libraries. Note that
- // here we don't need to see group members (see apply()).
+ // Add *.export.poptions from prerequisite libraries. Note that here we
+ // don't need to see group members (see apply()).
//
lorder lo (link_order (bs, ct));
for (prerequisite& p: group_prerequisites (t))
@@ -1295,15 +1276,19 @@ namespace build2
pt = &link_member (*l, lo);
if (pt->is_a<liba> () || pt->is_a<libs> ())
- append_lib_options (args, *pt, "cxx.export.poptions", lo);
+ append_lib_options (args, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
}
- append_options (args, t, "cxx.poptions");
- append_options (args, t, "cxx.coptions");
+ append_options (args, t, c_poptions);
+ append_options (args, t, x_poptions);
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
string std, out, out1; // Storage.
- append_std (args, rs, cid, t, std);
+ append_std (args, rs, t, std);
if (cid == "msvc")
{
@@ -1311,7 +1296,7 @@ namespace build2
// in VS2013/12.0. Why do we bother? Because the command line suddenly
// becomes readable.
//
- uint64_t cver (cast<uint64_t> (rs["cxx.version.major"]));
+ uint64_t ver (cast<uint64_t> (rs[x_version_major]));
args.push_back ("/nologo");
@@ -1322,7 +1307,10 @@ namespace build2
// see any relevant options explicitly specified, we take our hands
// off.
//
- if (!find_option_prefix ("/EH", args))
+ // For C looks like no /EH* (exceptions supported but no C++ objects
+ // destroyed) is a reasonable default.
+ //
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
args.push_back ("/EHsc");
// The runtime is a bit more interesting. At first it may seem like a
@@ -1363,7 +1351,7 @@ namespace build2
//
if (find_options ({"/Zi", "/ZI"}, args))
{
- if (cver >= 18)
+ if (ver >= 18)
args.push_back ("/Fd:");
else
out1 = "/Fd";
@@ -1374,7 +1362,7 @@ namespace build2
args.push_back (out1.c_str ());
}
- if (cver >= 18)
+ if (ver >= 18)
{
args.push_back ("/Fo:");
args.push_back (relo.string ().c_str ());
@@ -1385,8 +1373,8 @@ namespace build2
args.push_back (out.c_str ());
}
- args.push_back ("/c"); // Compile only.
- args.push_back ("/TP"); // Compile as C++.
+ args.push_back ("/c"); // Compile only.
+ args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as.
args.push_back (rels.string ().c_str ());
}
else
@@ -1411,7 +1399,7 @@ namespace build2
if (verb >= 2)
print_process (args);
else if (verb)
- text << "c++ " << *s;
+ text << x_name << ' ' << *s;
try
{
@@ -1475,13 +1463,10 @@ namespace build2
}
target_state compile::
- perform_clean (action a, target& xt)
+ perform_clean (action a, target& xt) const
{
file& t (static_cast<file&> (xt));
- scope& rs (t.root_scope ());
- const string& cid (cast<string> (rs["cxx.id"]));
-
initializer_list<const char*> e;
if (cid == "msvc")
@@ -1491,7 +1476,5 @@ namespace build2
return clean_extra (a, t, e);
}
-
- compile compile::instance;
}
}
diff --git a/build2/cxx/guess b/build2/cc/guess
index 63858ad..977e081 100644
--- a/build2/cxx/guess
+++ b/build2/cc/guess
@@ -1,28 +1,29 @@
-// file : build2/cxx/guess -*- C++ -*-
+// file : build2/cc/guess -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#ifndef BUILD2_CXX_GUESS
-#define BUILD2_CXX_GUESS
+#ifndef BUILD2_CC_GUESS
+#define BUILD2_CC_GUESS
#include <build2/types>
#include <build2/utility>
+#include <build2/cc/types>
+
namespace build2
{
- namespace cxx
+ namespace cc
{
- // C++ compiler id consisting of a type and optional variant. If the
- // variant is not empty, then the id is spelled out as 'type-variant',
- // similar to target triplets (this also means that the type cannot
- // contain '-').
+ // Compiler id consisting of a type and optional variant. If the variant
+ // is not empty, then the id is spelled out as 'type-variant', similar to
+ // target triplets (this also means that the type cannot contain '-').
//
// Currently recognized compilers and their ids:
//
- // gcc GCC g++
- // clang Vanilla Clang clang++
- // clang-apple Apple Clang clang++ and the g++ "alias"
- // icc Intel icpc
+ // gcc GCC gcc/g++
+ // clang Vanilla Clang clang/clang++
+ // clang-apple Apple Clang clang/clang++ and the gcc/g++ "alias"
+ // icc Intel icc/icpc
// msvc Microsoft cl.exe
//
struct compiler_id
@@ -43,8 +44,8 @@ namespace build2
return os << id.string ();
}
- // C++ compiler version. Here we map the various compiler version formats
- // to something that resembles the MAJOR.MINOR.PATCH-BUILD form of the
+ // Compiler version. Here we map the various compiler version formats to
+ // something that resembles the MAJOR.MINOR.PATCH-BUILD form of the
// Semantic Versioning. While the MAJOR.MINOR part is relatively
// straightforward, PATCH may be empty and BUILD can contain pretty much
// anything (including spaces).
@@ -74,7 +75,7 @@ namespace build2
std::string build;
};
- // C++ compiler information.
+ // Compiler information.
//
// The signature is normally the -v/--version line that was used to guess
// the compiler id and its version.
@@ -95,6 +96,9 @@ namespace build2
// unlike all the preceding fields, this one takes into account the
// compile options (e.g., -m32).
//
+ // The pattern is the toolchain program pattern that could sometimes be
+ // derived for some toolchains. For example, i686-w64-mingw32-*.
+ //
struct compiler_info
{
compiler_id id;
@@ -102,11 +106,20 @@ namespace build2
string signature;
string checksum;
string target;
+ string pattern;
};
+ // In a sense this is analagous to the language standard which we handle
+ // via a virtual function in common. However, duplicating this hairy ball
+ // of fur in multiple places doesn't seem wise, especially considering
+ // that most of it will be the same, at least for C and C++.
+ //
compiler_info
- guess (const path& cxx, const strings* coptions);
+ guess (lang,
+ const path& xc,
+ const strings* c_coptions,
+ const strings* x_coptions);
}
}
-#endif // BUILD2_CXX_GUESS
+#endif // BUILD2_CC_GUESS
diff --git a/build2/cxx/guess.cxx b/build2/cc/guess.cxx
index 11a832c..d80dddd 100644
--- a/build2/cxx/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -1,8 +1,8 @@
-// file : build2/cxx/guess.cxx -*- C++ -*-
+// file : build2/cc/guess.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/cxx/guess>
+#include <build2/cc/guess>
#include <cstring> // strlen()
@@ -12,7 +12,7 @@ using namespace std;
namespace build2
{
- namespace cxx
+ namespace cc
{
// Pre-guess the compiler type based on the compiler executable name.
// Return empty string if can't make a guess (for example, because the
@@ -20,11 +20,11 @@ namespace build2
// not the variant.
//
static string
- pre_guess (const path& cxx)
+ pre_guess (lang xl, const path& xc)
{
- tracer trace ("cxx::pre_guess");
+ tracer trace ("cc::pre_guess");
- const string s (cxx.leaf ().base ().string ());
+ const string s (xc.leaf ().base ().string ());
size_t n (s.size ());
// Name separator characters (e.g., '-' in 'g++-4.8').
@@ -44,39 +44,55 @@ namespace build2
((p += m) == n || sep (s[p])); // Separated at the end.
};
- if (stem ("g++"))
- return "gcc";
+ // Warn if the user specified a C compiler instead of C++ or vice versa.
+ //
+ lang o; // Other language.
+ const char* as (nullptr); // Actual stem.
+ const char* es (nullptr); // Expected stem.
- if (stem ("clang++"))
- return "clang";
+ switch (xl)
+ {
+ case lang::c:
+ {
+ // Keep msvc last since 'cl' is very generic.
+ //
+ if (stem ("gcc")) return "gcc";
+ if (stem ("clang")) return "clang";
+ if (stem ("icc")) return "icc";
+ if (stem ("cl")) return "msvc";
- if (stem ("icpc"))
- return "icc";
+ if (stem (as = "g++")) es = "gcc";
+ else if (stem (as = "clang++")) es = "clang";
+ else if (stem (as = "icpc")) es = "icc";
+ else if (stem (as = "c++")) es = "cc";
- // Keep this one last since 'cl' is very generic.
- //
- if (stem ("cl"))
- return "msvc";
+ o = lang::cxx;
+ break;
+ }
+ case lang::cxx:
+ {
+ // Keep msvc last since 'cl' is very generic.
+ //
+ if (stem ("g++")) return "gcc";
+ if (stem ("clang++")) return "clang";
+ if (stem ("icpc")) return "icc";
+ if (stem ("cl")) return "msvc";
- // Warn if the user specified a C compiler instead of C++.
- //
- if (stem ("gcc"))
- {
- warn << cxx << " looks like a C compiler" <<
- info << "should it be 'g++' instead of 'gcc'?";
- }
- else if (stem ("clang"))
- {
- warn << cxx << " looks like a C compiler" <<
- info << "should it be 'clang++' instead of 'clang'?";
- }
- else if (stem ("icc"))
- {
- warn << cxx << " looks like a C compiler" <<
- info << "should it be 'icpc' instead of 'icc'?";
+ if (stem (as = "gcc")) es = "g++";
+ else if (stem (as = "clang")) es = "clang++";
+ else if (stem (as = "icc")) es = "icpc";
+ else if (stem (as = "cc")) es = "c++";
+
+ o = lang::c;
+ break;
+ }
}
- l4 ([&]{trace << "unable to guess compiler type of " << cxx;});
+ if (es != nullptr)
+ warn << xc << " looks like a " << o << " compiler" <<
+ info << "should it be '" << es << "' instead of '" << as << "'?";
+
+ l4 ([&]{trace << "unable to guess compiler type of " << xc;});
return "";
}
@@ -95,9 +111,9 @@ namespace build2
};
static guess_result
- guess (const path& cxx, const string& pre)
+ guess (lang, const path& xc, const string& pre)
{
- tracer trace ("cxx::guess");
+ tracer trace ("cc::guess");
guess_result r;
@@ -120,7 +136,8 @@ namespace build2
{
auto f = [] (string& l) -> guess_result
{
- // The g++ -v output will have a line (currently last) in the form:
+ // The gcc/g++ -v output will have a line (currently last) in the
+ // form:
//
// "gcc version X.Y.Z ..."
//
@@ -137,8 +154,8 @@ namespace build2
if (l.compare (0, 4, "gcc ") == 0)
return guess_result {{"gcc", ""}, move (l), ""};
- // The Apple clang++ -v output will have a line (currently first)
- // in the form:
+ // The Apple clang/clang++ -v output will have a line (currently
+ // first) in the form:
//
// "Apple (LLVM|clang) version X.Y.Z ..."
//
@@ -155,19 +172,20 @@ namespace build2
// Apple LLVM version 7.0.2 (clang-700.1.81)
// Apple LLVM version 7.3.0 (clang-703.0.16.1)
//
- // Note that the g++ "alias" for clang++ also includes this line
- // but it is (currently) preceded by "Configured with: ...".
+ // Note that the gcc/g++ "aliases" for clang/clang++ also include
+ // this line but it is (currently) preceded by "Configured with:
+ // ...".
//
- // Check for Apple clang before the vanilla one since the above
- // line also includes "clang".
+ // Check for Apple clang before the vanilla one since the above line
+ // also includes "clang".
//
if (l.compare (0, 6, "Apple ") == 0 &&
(l.compare (6, 5, "LLVM ") == 0 ||
l.compare (6, 6, "clang ") == 0))
return guess_result {{"clang", "apple"}, move (l), ""};
- // The vanilla clang++ -v output will have a line (currently first)
- // in the form:
+ // The vanilla clang/clang++ -v output will have a line (currently
+ // first) in the form:
//
// "[... ]clang version X.Y.Z[-...] ..."
//
@@ -189,12 +207,16 @@ namespace build2
// clang) which makes sense to include into the compiler checksum. So
// ask run() to calculate it for every line of the -v ouput.
//
+ // One notable consequence of this is that if the locale changes
+ // (e.g., via LC_ALL), then the compiler signature will most likely
+ // change as well because of the translated text.
+ //
sha256 cs;
// Suppress all the compiler errors because we may be trying an
// unsupported option.
//
- r = run<guess_result> (cxx, "-v", f, false, false, &cs);
+ r = run<guess_result> (xc, "-v", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -214,6 +236,7 @@ namespace build2
// icpc (ICC) 14.0.0 20130728
// icpc (ICC) 15.0.2 20150121
// icpc (ICC) 16.0.2 20160204
+ // icc (ICC) 16.0.2 20160204
//
if (l.find (" (ICC) ") != string::npos)
return guess_result {{"icc", ""}, move (l), ""};
@@ -221,7 +244,7 @@ namespace build2
return guess_result ();
};
- r = run<guess_result> (cxx, "--version", f, false);
+ r = run<guess_result> (xc, "--version", f, false);
}
// Finally try to run it without any options to detect msvc.
@@ -253,7 +276,7 @@ namespace build2
return guess_result ();
};
- r = run<guess_result> (cxx, f, false);
+ r = run<guess_result> (xc, f, false);
}
if (!r.empty ())
@@ -267,19 +290,23 @@ namespace build2
r = guess_result ();
}
else
- l5 ([&]{trace << cxx << " is " << r.id << ": '"
+ l5 ([&]{trace << xc << " is " << r.id << ": '"
<< r.signature << "'";});
}
else
- l4 ([&]{trace << "unable to determine compiler type of " << cxx;});
+ l4 ([&]{trace << "unable to determine compiler type of " << xc;});
return r;
}
static compiler_info
- guess_gcc (const path& cxx, const strings* coptions, guess_result&& gr)
+ guess_gcc (lang,
+ const path& xc,
+ const strings* c_coptions,
+ const strings* x_coptions,
+ guess_result&& gr)
{
- tracer trace ("cxx::guess_gcc");
+ tracer trace ("cc::guess_gcc");
// Extract the version. The signature line has the following format
// though language words can be translated and even rearranged (see
@@ -356,9 +383,9 @@ namespace build2
// multi-arch support), then use the result. Otherwise, fallback to
// -dumpmachine (older gcc or not multi-arch).
//
- cstrings args {cxx.string ().c_str (), "-print-multiarch"};
- if (coptions != nullptr)
- append_options (args, *coptions);
+ cstrings args {xc.string ().c_str (), "-print-multiarch"};
+ if (c_coptions != nullptr) append_options (args, *c_coptions);
+ if (x_coptions != nullptr) append_options (args, *x_coptions);
args.push_back (nullptr);
// The output of both -print-multiarch and -dumpmachine is a single line
@@ -370,7 +397,7 @@ namespace build2
if (t.empty ())
{
- l5 ([&]{trace << cxx << " doesn's support -print-multiarch, "
+ l5 ([&]{trace << xc << " doesn's support -print-multiarch, "
<< "falling back to -dumpmachine";});
args[1] = "-dumpmachine";
@@ -378,7 +405,7 @@ namespace build2
}
if (t.empty ())
- fail << "unable to extract target architecture from " << cxx
+ fail << "unable to extract target architecture from " << xc
<< " -print-multiarch or -dumpmachine output";
return compiler_info {
@@ -386,11 +413,16 @@ namespace build2
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
- move (t)};
+ move (t),
+ string ()};
}
static compiler_info
- guess_clang (const path& cxx, const strings* coptions, guess_result&& gr)
+ guess_clang (lang,
+ const path& xc,
+ const strings* c_coptions,
+ const strings* x_coptions,
+ guess_result&& gr)
{
// Extract the version. Here we will try to handle both vanilla and
// Apple clang since the signature lines are fairly similar. They have
@@ -466,9 +498,9 @@ namespace build2
// Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine,
// however, respects the compile options (e.g., -m32).
//
- cstrings args {cxx.string ().c_str (), "-dumpmachine"};
- if (coptions != nullptr)
- append_options (args, *coptions);
+ cstrings args {xc.string ().c_str (), "-dumpmachine"};
+ if (c_coptions != nullptr) append_options (args, *c_coptions);
+ if (x_coptions != nullptr) append_options (args, *x_coptions);
args.push_back (nullptr);
// The output of -dumpmachine is a single line containing just the
@@ -477,7 +509,7 @@ namespace build2
string t (run<string> (args.data (), [] (string& l) {return move (l);}));
if (t.empty ())
- fail << "unable to extract target architecture from " << cxx
+ fail << "unable to extract target architecture from " << xc
<< " -dumpmachine output";
return compiler_info {
@@ -485,11 +517,16 @@ namespace build2
move (v),
move (gr.signature),
move (gr.checksum), // Calculated on whole -v output.
- move (t)};
+ move (t),
+ string ()};
}
static compiler_info
- guess_icc (const path& cxx, const strings* coptions, guess_result&& gr)
+ guess_icc (lang xl,
+ const path& xc,
+ const strings* c_coptions,
+ const strings* x_coptions,
+ guess_result&& gr)
{
// Extract the version. If the version has the fourth component, then
// the signature line (extracted with --version) won't include it. So we
@@ -511,6 +548,7 @@ namespace build2
// Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.2.181 Build 20160204
// Intel(R) C++ Intel(R) 64 Compiler for applications running on IA-32, Version 16.0.2.181 Build 20160204
// Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) MIC Architecture, Version 16.0.2.181 Build 20160204
+ // Intel(R) C Intel(R) 64 Compiler for applications running on Intel(R) MIC Architecture, Version 16.0.2.181 Build 20160204
//
// We should probably also assume the language words can be translated
// and even rearranged.
@@ -527,13 +565,14 @@ namespace build2
// The -V output is sent to STDERR.
//
- s = run<string> (cxx, "-V", f, false);
+ s = run<string> (xc, "-V", f, false);
if (s.empty ())
- fail << "unable to extract signature from " << cxx << " -V output";
+ fail << "unable to extract signature from " << xc << " -V output";
- if (s.find ("C++") == string::npos)
- fail << cxx << " does not appear to be the Intel C++ compiler" <<
+ if (s.find (xl == lang::c ? " C " : " C++ ") == string::npos)
+ fail << xc << " does not appear to be the Intel " << xl
+ << " compiler" <<
info << "extracted signature: '" << s << "'";
// Scan the string as words and look for the version. It consist of only
@@ -619,9 +658,9 @@ namespace build2
// "Intel(R)" "64"
// "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux)
//
- cstrings args {cxx.string ().c_str (), "-V"};
- if (coptions != nullptr)
- append_options (args, *coptions);
+ cstrings args {xc.string ().c_str (), "-V"};
+ if (c_coptions != nullptr) append_options (args, *c_coptions);
+ if (x_coptions != nullptr) append_options (args, *x_coptions);
args.push_back (nullptr);
// The -V output is sent to STDERR.
@@ -629,7 +668,7 @@ namespace build2
string t (run<string> (args.data (), f, false));
if (t.empty ())
- fail << "unable to extract target architecture from " << cxx
+ fail << "unable to extract target architecture from " << xc
<< " -V output";
string arch;
@@ -668,10 +707,10 @@ namespace build2
// on which we are running), who knows what will happen in the future.
// So instead we are going to use -dumpmachine and substitute the CPU.
//
- t = run<string> (cxx, "-dumpmachine", [] (string& l) {return move (l);});
+ t = run<string> (xc, "-dumpmachine", [] (string& l) {return move (l);});
if (t.empty ())
- fail << "unable to extract target architecture from " << cxx
+ fail << "unable to extract target architecture from " << xc
<< " -dumpmachine output";
// The first component in the triplet is always CPU.
@@ -692,11 +731,16 @@ namespace build2
move (v),
move (gr.signature),
cs.string (),
- move (arch)};
+ move (arch),
+ string ()};
}
static compiler_info
- guess_msvc (const path&, guess_result&& gr)
+ guess_msvc (lang,
+ const path& xc,
+ const strings*,
+ const strings*,
+ guess_result&& gr)
{
// Extract the version. The signature line has the following format
// though language words can be translated and even rearranged (see
@@ -815,9 +859,9 @@ namespace build2
//
// The (toolchain) VENDOR is also straightforward: 'microsoft'. Why not
// omit it? Two reasons: firstly, there are other compilers with the
- // otherwise same target, for example Intel C++, and it could be useful
- // to distinguish between them. Secondly, by having all four components
- // we remove any parsing ambiguity.
+ // otherwise same target, for example Intel C/C++, and it could be
+ // useful to distinguish between them. Secondly, by having all four
+ // components we remove any parsing ambiguity.
//
// OS-ABI is where things are not as clear cut. The OS part shouldn't
// probably be just 'windows' since we have Win32 and WinCE. And WinRT.
@@ -881,6 +925,31 @@ namespace build2
<< "' to runtime version";
}
+ // Derive the toolchain pattern.
+ //
+ // 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.
+ //
+ string pat;
+
+ if (xc.size () > 2)
+ {
+ const string& l (xc.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 (xc.directory ());
+ p /= "*";
+ p += l.c_str () + 2;
+ pat = move (p).string ();
+ }
+ }
+
// Use the signature line to generate the checksum.
//
sha256 cs (s);
@@ -890,13 +959,17 @@ namespace build2
move (v),
move (gr.signature),
cs.string (),
- move (arch)};
+ move (arch),
+ move (pat)};
}
compiler_info
- guess (const path& cxx, const strings* coptions)
+ guess (lang xl,
+ const path& xc,
+ const strings* c_coptions,
+ const strings* x_coptions)
{
- string pre (pre_guess (cxx));
+ string pre (pre_guess (xl, xc));
guess_result gr;
// If we could pre-guess the type based on the excutable name, then
@@ -904,45 +977,76 @@ namespace build2
//
if (!pre.empty ())
{
- gr = guess (cxx, pre);
+ gr = guess (xl, xc, pre);
if (gr.empty ())
- warn << cxx << " name looks like " << pre << " but it is not";
+ warn << xc << " name looks like " << pre << " but it is not";
}
if (gr.empty ())
- gr = guess (cxx, "");
+ gr = guess (xl, xc, "");
if (gr.empty ())
- fail << "unable to guess C++ compiler type of " << cxx;
+ fail << "unable to guess " << xl << " compiler type of " << xc;
+ compiler_info r;
const compiler_id& id (gr.id);
if (id.type == "gcc")
{
assert (id.variant.empty ());
- return guess_gcc (cxx, coptions, move (gr));
+ r = guess_gcc (xl, xc, c_coptions, x_coptions, move (gr));
}
else if (id.type == "clang")
{
assert (id.variant.empty () || id.variant == "apple");
- return guess_clang (cxx, coptions, move (gr));
+ r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr));
}
else if (id.type == "icc")
{
assert (id.variant.empty ());
- return guess_icc (cxx, coptions, move (gr));
+ r = guess_icc (xl, xc, c_coptions, x_coptions, move (gr));
}
else if (id.type == "msvc")
{
assert (id.variant.empty ());
- return guess_msvc (cxx, move (gr));
+ r = guess_msvc (xl, xc, c_coptions, x_coptions, move (gr));
}
else
- {
assert (false);
- return compiler_info ();
+
+ // Derive binutils pattern unless this has already been done by the
+ // compiler-specific code.
+ //
+ if (r.pattern.empty ())
+ {
+ // When cross-compiling the whole toolchain is normally prefixed with
+ // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}.
+ //
+ // 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 (one way we could
+ // do it is by passing config.bin.{ar,ranlib} as hints).
+ //
+ const string& t (r.target);
+ size_t n (t.size ());
+
+ if (xc.size () > n + 1)
+ {
+ const string& l (xc.leaf ().string ());
+
+ if (l.size () > n + 1 && l.compare (0, n, t) == 0 && l[n] == '-')
+ {
+ path p (xc.directory ());
+ p /= t;
+ p += "-*";
+ r.pattern = move (p).string ();
+ }
+ }
}
+
+ return r;
}
}
}
diff --git a/build2/cc/init b/build2/cc/init
new file mode 100644
index 0000000..d8ebd0e
--- /dev/null
+++ b/build2/cc/init
@@ -0,0 +1,55 @@
+// file : build2/cc/init -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_INIT
+#define BUILD2_CC_INIT
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/module>
+
+namespace build2
+{
+ namespace cc
+ {
+ bool
+ vars_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
+ config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
+ core_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
+ init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+ }
+}
+
+#endif // BUILD2_CC_INIT
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
new file mode 100644
index 0000000..2623c79
--- /dev/null
+++ b/build2/cc/init.cxx
@@ -0,0 +1,321 @@
+// file : build2/cc/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/cc/init>
+
+#include <butl/triplet>
+
+#include <build2/scope>
+#include <build2/context>
+#include <build2/diagnostics>
+
+#include <build2/config/utility>
+
+#include <build2/cc/target>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ bool
+ vars_init (scope& r,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map&)
+ {
+ tracer trace ("cc::vars_init");
+ l5 ([&]{trace << "for " << r.out_path ();});
+
+ assert (first);
+
+ // Enter variables. Note: some overridable, some not.
+ //
+ auto& v (var_pool);
+
+ v.insert<strings> ("config.cc.poptions", true);
+ v.insert<strings> ("config.cc.coptions", true);
+ v.insert<strings> ("config.cc.loptions", true);
+ v.insert<strings> ("config.cc.libs", true);
+
+ v.insert<strings> ("cc.poptions");
+ v.insert<strings> ("cc.coptions");
+ v.insert<strings> ("cc.loptions");
+ v.insert<strings> ("cc.libs");
+
+ v.insert<strings> ("cc.export.poptions");
+ v.insert<strings> ("cc.export.coptions");
+ v.insert<strings> ("cc.export.loptions");
+ v.insert<strings> ("cc.export.libs");
+
+ // Hint variables (not overridable).
+ //
+ v.insert<string> ("config.cc.id");
+ v.insert<string> ("config.cc.target");
+ v.insert<string> ("config.cc.pattern");
+
+ return true;
+ }
+
+ bool
+ config_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cc::config_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Load cc.vars.
+ //
+ if (first)
+ {
+ if (!cast_false<bool> (b["cc.vars.loaded"]))
+ load_module ("cc.vars", r, b, loc);
+ }
+
+ // Configure.
+ //
+ if (first)
+ {
+ // config.cc.id
+ //
+ {
+ // This value must be hinted.
+ //
+ r.assign<string> ("cc.id") = cast<string> (hints["config.cc.id"]);
+ }
+
+ // config.cc.target
+ //
+ {
+ // This value must be hinted and already canonicalized.
+ //
+ const string& s (cast<string> (hints["config.cc.target"]));
+
+ try
+ {
+ //@@ We do it in the hinting module and here. Any way not to
+ // duplicate the effort? Maybe move the splitting here and
+ // simply duplicate the values there?
+ //
+ triplet t (s);
+
+ // Enter as cc.target.{cpu,vendor,system,version,class}.
+ //
+ r.assign<string> ("cc.target") = s;
+ r.assign<string> ("cc.target.cpu") = move (t.cpu);
+ r.assign<string> ("cc.target.vendor") = move (t.vendor);
+ r.assign<string> ("cc.target.system") = move (t.system);
+ r.assign<string> ("cc.target.version") = move (t.version);
+ r.assign<string> ("cc.target.class") = move (t.class_);
+ }
+ catch (const invalid_argument& e)
+ {
+ assert (false); // Should have been caught by the hinting module.
+ }
+ }
+
+ // config.cc.pattern
+ //
+ {
+ // This value could be hinted.
+ //
+ if (auto l = hints["config.cc.pattern"])
+ r.assign<string> ("cc.pattern") = cast<string> (l);
+ }
+
+ // Note that we are not having a config report since it will just
+ // duplicate what has already been printed by the hinting module.
+ }
+
+ // config.cc.{p,c,l}options
+ // config.cc.libs
+ //
+ // @@ Same nonsense as in module.
+ //
+ //
+ b.assign ("cc.poptions") += cast_null<strings> (
+ config::optional (r, "config.cc.poptions"));
+
+ b.assign ("cc.coptions") += cast_null<strings> (
+ config::optional (r, "config.cc.coptions"));
+
+ b.assign ("cc.loptions") += cast_null<strings> (
+ config::optional (r, "config.cc.loptions"));
+
+ b.assign ("cc.libs") += cast_null<strings> (
+ config::optional (r, "config.cc.libs"));
+
+ // Load the bin.config module.
+ //
+ if (!cast_false<bool> (b["bin.config.loaded"]))
+ {
+ // Prepare configuration hints. They are only used on the first load
+ // of bin.config so we only populate them on our first load.
+ //
+ variable_map h;
+ if (first)
+ {
+ h.assign ("config.bin.target") = cast<string> (r["cc.target"]);
+ if (auto l = r["cc.pattern"])
+ h.assign ("config.bin.pattern") = cast<string> (l);
+ }
+
+ load_module ("bin.config", r, b, loc, false, h);
+ }
+
+ // Verify bin's target matches ours (we do it even if we loaded it
+ // ourselves since the target can come from the configuration and not
+ // our hint).
+ //
+ if (first)
+ {
+ const string& ct (cast<string> (r["cc.target"]));
+ const string& bt (cast<string> (r["bin.target"]));
+
+ if (bt != ct)
+ fail (loc) << "cc and bin module target mismatch" <<
+ info << "cc.target is " << ct <<
+ info << "bin.target is " << bt;
+ }
+
+ const string& cid (cast<string> (r["cc.id"]));
+ const string& tsys (cast<string> (r["cc.target.system"]));
+
+ // Load bin.*.config for bin.* modules we may need (see core_init()
+ // below).
+ //
+ if (auto l = r["config.bin.lib"])
+ {
+ if (cast<string> (l) != "shared")
+ {
+ if (!cast_false<bool> (b["bin.ar.config.loaded"]))
+ load_module ("bin.ar.config", r, b, loc);
+ }
+ }
+
+ if (cid == "msvc")
+ {
+ if (!cast_false<bool> (b["bin.ld.config.loaded"]))
+ load_module ("bin.ld.config", r, b, loc);
+ }
+
+ if (tsys == "mingw32")
+ {
+ if (!cast_false<bool> (b["bin.rc.config.loaded"]))
+ load_module ("bin.rc.config", r, b, loc);
+ }
+
+ return true;
+ }
+
+ bool
+ core_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cc::core_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Load cc.config.
+ //
+ if (!cast_false<bool> (b["cc.config.loaded"]))
+ load_module ("cc.config", r, b, loc, false, hints);
+
+ // Load the bin module.
+ //
+ if (!cast_false<bool> (b["bin.loaded"]))
+ load_module ("bin", r, b, loc);
+
+ const string& cid (cast<string> (r["cc.id"]));
+ const string& tsys (cast<string> (r["cc.target.system"]));
+
+ // 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);
+ }
+ }
+
+ // 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);
+ }
+
+ // If our target is MinGW, then we will need the resource compiler
+ // (windres) in order to embed manifests into executables.
+ //
+ if (tsys == "mingw32")
+ {
+ if (!cast_false<bool> (b["bin.rc.loaded"]))
+ load_module ("bin.rc", r, b, loc);
+ }
+
+ return true;
+ }
+
+ bool
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&)
+ {
+ tracer trace ("cc::init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // This module is an "alias" for c.config and cxx.config. Its intended
+ // use is to make sure that the C/C++ configuration is captured in an
+ // amalgamation rather than subprojects.
+ //
+ // We want to order the loading to match what user specified on the
+ // command line (config.c or config.cxx). This way the first loaded
+ // module (with user-specified config.*) will hint the compiler to the
+ // second.
+ //
+ bool lc (!cast_false<bool> (b["c.config.loaded"]));
+ bool lp (!cast_false<bool> (b["cxx.config.loaded"]));
+
+ // If none of them are already loaded, load c first only if config.c
+ // is specified.
+ //
+ if (lc && lp && r["config.c"])
+ {
+ load_module ("c.config", r, b, loc);
+ load_module ("cxx.config", r, b, loc);
+ }
+ else
+ {
+ if (lp) load_module ("cxx.config", r, b, loc);
+ if (lc) load_module ("c.config", r, b, loc);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/build2/cxx/install b/build2/cc/install
index 119ef94..e2be905 100644
--- a/build2/cxx/install
+++ b/build2/cc/install
@@ -1,31 +1,39 @@
-// file : build2/cxx/install -*- C++ -*-
+// file : build2/cc/install -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#ifndef BUILD2_CXX_INSTALL
-#define BUILD2_CXX_INSTALL
+#ifndef BUILD2_CC_INSTALL
+#define BUILD2_CC_INSTALL
#include <build2/types>
#include <build2/utility>
#include <build2/install/rule>
+#include <build2/cc/types>
+#include <build2/cc/common>
+
namespace build2
{
- namespace cxx
+ namespace cc
{
- class install: public build2::install::file_rule
+ class link;
+
+ class install: public build2::install::file_rule, virtual common
{
public:
+ install (data&&, const link&);
+
virtual target*
filter (action, target&, prerequisite_member) const;
virtual match_result
match (action, target&, const string&) const;
- static install instance;
+ private:
+ const link& link_;
};
}
}
-#endif // BUILD2_CXX_INSTALL
+#endif // BUILD2_CC_INSTALL
diff --git a/build2/cxx/install.cxx b/build2/cc/install.cxx
index e07d115..b674886 100644
--- a/build2/cxx/install.cxx
+++ b/build2/cc/install.cxx
@@ -1,23 +1,25 @@
-// file : build2/cxx/install.cxx -*- C++ -*-
+// file : build2/cc/install.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/cxx/install>
+#include <build2/cc/install>
#include <build2/bin/target>
-#include <build2/cxx/link>
-#include <build2/cxx/common>
-#include <build2/cxx/target>
+#include <build2/cc/link> // match()
+#include <build2/cc/utility>
using namespace std;
namespace build2
{
- namespace cxx
+ namespace cc
{
using namespace bin;
+ install::
+ install (data&& d, const link& l): common (move (d)), link_ (l) {}
+
target* install::
filter (action a, target& t, prerequisite_member p) const
{
@@ -25,7 +27,7 @@ namespace build2
{
// Don't install executable's prerequisite headers.
//
- if (p.is_a<hxx> () || p.is_a<ixx> () || p.is_a<txx> () || p.is_a<h> ())
+ if (x_header (p))
return nullptr;
}
@@ -61,10 +63,8 @@ namespace build2
// We only want to handle installation if we are also the
// ones building this target. So first run link's match().
//
- match_result r (link::instance.match (a, t, hint));
+ match_result r (link_.match (a, t, hint));
return r ? install::file_rule::match (a, t, "") : r;
}
-
- install install::instance;
}
}
diff --git a/build2/cc/link b/build2/cc/link
new file mode 100644
index 0000000..8be386f
--- /dev/null
+++ b/build2/cc/link
@@ -0,0 +1,78 @@
+// file : build2/cc/link -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_LINK
+#define BUILD2_CC_LINK
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/rule>
+
+#include <build2/bin/target>
+
+#include <build2/cc/types>
+#include <build2/cc/common>
+
+namespace build2
+{
+ namespace cc
+ {
+ class link: public rule, virtual common
+ {
+ public:
+ link (data&&);
+
+ virtual match_result
+ match (action, target&, const string& hint) const;
+
+ virtual recipe
+ apply (action, target&, const match_result&) const;
+
+ target_state
+ perform_update (action, target&) const;
+
+ target_state
+ perform_clean (action, target&) const;
+
+ private:
+ friend class compile;
+
+ // Extract system library search paths from GCC or compatible (Clang,
+ // Intel) using the -print-search-dirs option.
+ //
+ void
+ gcc_library_search_paths (scope&, dir_paths&) const;
+
+ // Extract system library search paths from VC (msvc.cxx).
+ //
+ void
+ msvc_library_search_paths (scope&, dir_paths&) const;
+
+ dir_paths
+ extract_library_paths (scope&) const;
+
+ // Alternative search logic for VC (msvc.cxx).
+ //
+ bin::liba*
+ msvc_search_static (const path&, const dir_path&, prerequisite&) const;
+
+ bin::libs*
+ msvc_search_shared (const path&, const dir_path&, prerequisite&) const;
+
+ target*
+ search_library (optional<dir_paths>&, prerequisite&) const;
+
+ // Windows-specific (windows-manifest.cxx).
+ //
+ path
+ windows_manifest (file&, bool rpath_assembly) const;
+
+ private:
+ const string rule_id;
+ };
+ }
+}
+
+#endif // BUILD2_CC_LINK
diff --git a/build2/cxx/link.cxx b/build2/cc/link.cxx
index d19d6b1..4bebc6f 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cc/link.cxx
@@ -1,8 +1,8 @@
-// file : build2/cxx/link.cxx -*- C++ -*-
+// file : build2/cc/link.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/cxx/link>
+#include <build2/cc/link>
#include <cstdlib> // exit()
#include <iostream> // cerr
@@ -18,35 +18,43 @@
#include <build2/diagnostics>
#include <build2/bin/target>
-#include <build2/cxx/target>
-#include <build2/cxx/common>
-#include <build2/cxx/utility>
+#include <build2/cc/target> // c
+#include <build2/cc/utility>
using namespace std;
using namespace butl;
namespace build2
{
- namespace cxx
+ namespace cc
{
using namespace bin;
+ link::
+ link (data&& d)
+ : common (move (d)),
+ rule_id (string (x) += ".link 1")
+ {
+ }
+
// Extract system library search paths from GCC or compatible (Clang,
- // Intel C++) using the -print-search-dirs option.
+ // Intel) using the -print-search-dirs option.
//
- static void
- gcc_library_search_paths (scope& bs, const string& cid, dir_paths& r)
+ void link::
+ gcc_library_search_paths (scope& bs, dir_paths& r) const
{
scope& rs (*bs.root_scope ());
cstrings args;
- string std_storage;
-
- args.push_back (cast<path> (rs["config.cxx"]).string ().c_str ());
- append_options (args, bs, "cxx.coptions");
- append_std (args, rs, cid, bs, std_storage);
- append_options (args, bs, "cxx.loptions");
+ string std; // Storage.
+
+ args.push_back (cast<path> (rs[config_x]).string ().c_str ());
+ append_options (args, bs, c_coptions);
+ append_options (args, bs, x_coptions);
+ append_std (args, rs, bs, std);
+ append_options (args, bs, c_loptions);
+ append_options (args, bs, x_loptions);
args.push_back ("-print-search-dirs");
args.push_back (nullptr);
@@ -80,7 +88,8 @@ namespace build2
catch (const ifdstream::failure&)
{
pr.wait ();
- fail << "error reading C++ compiler -print-search-dirs output";
+ fail << "error reading " << x_lang << " compiler -print-search-dirs "
+ << "output";
}
}
catch (const process_error& e)
@@ -94,7 +103,8 @@ namespace build2
}
if (l.empty ())
- fail << "unable to extract C++ compiler system library paths";
+ fail << "unable to extract " << x_lang << " compiler system library "
+ << "search paths";
// Now the fun part: figuring out which delimiter is used. Normally it
// is ':' but on Windows it is ';' (or can be; who knows for sure). Also
@@ -127,23 +137,16 @@ namespace build2
}
}
- // Extract system library search paths from MSVC.
- //
- void
- msvc_library_search_paths (scope&, const string&, dir_paths&); // msvc.cxx
-
dir_paths link::
- extract_library_paths (scope& bs)
+ extract_library_paths (scope& bs) const
{
dir_paths r;
- scope& rs (*bs.root_scope ());
- const string& cid (cast<string> (rs["cxx.id"]));
// Extract user-supplied search paths (i.e., -L, /LIBPATH).
//
- if (auto l = bs["cxx.loptions"])
+ auto extract = [&r, this] (const value& val)
{
- const auto& v (cast<strings> (l));
+ const auto& v (cast<strings> (val));
for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
{
@@ -184,28 +187,23 @@ namespace build2
if (!d.relative ())
r.push_back (move (d));
}
- }
+ };
+
+ if (auto l = bs[c_loptions]) extract (*l);
+ if (auto l = bs[x_loptions]) extract (*l);
if (cid == "msvc")
- msvc_library_search_paths (bs, cid, r);
+ msvc_library_search_paths (bs, r);
else
- gcc_library_search_paths (bs, cid, r);
+ gcc_library_search_paths (bs, r);
return r;
}
- // Alternative search for VC (msvc.cxx).
- //
- liba*
- msvc_search_static (const path& ld, const dir_path&, prerequisite&);
-
- libs*
- msvc_search_shared (const path& ld, const dir_path&, prerequisite&);
-
target* link::
- search_library (optional<dir_paths>& spc, prerequisite& p)
+ search_library (optional<dir_paths>& spc, prerequisite& p) const
{
- tracer trace ("cxx::link::search_library");
+ tracer trace (x, "link::search_library");
// @@ This is hairy enough to warrant a separate implementation for
// Windows.
@@ -216,11 +214,6 @@ namespace build2
if (p.target != nullptr)
return p.target;
- scope& rs (*p.scope.root_scope ());
- const string& cid (cast<string> (rs["cxx.id"]));
- const string& tsys (cast<string> (rs["cxx.target.system"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
bool l (p.is_a<lib> ());
const string* ext (l ? nullptr : p.ext); // Only for liba/libs.
@@ -426,6 +419,7 @@ namespace build2
//
if (cid == "msvc")
{
+ scope& rs (*p.scope.root_scope ());
const path& ld (cast<path> (rs["config.bin.ld"]));
if (s == nullptr && !sn.empty ())
@@ -449,40 +443,42 @@ namespace build2
// handle DLL export). The absence of either of these macros would mean
// some other build system that cannot distinguish between the two.
//
- auto add_macro = [] (target& t, const char* suffix)
+ auto add_macro = [this] (target& t, const char* suffix)
{
- // If there is already a value, don't add anything, we don't want to
- // be accumulating defines nor messing with custom values.
+ // If there is already a value (either in cc.export or x.export),
+ // don't add anything: we don't want to be accumulating defines nor
+ // messing with custom values. And if we are adding, then use the
+ // generic cc.export.
//
- auto p (t.vars.insert ("cxx.export.poptions"));
-
- if (p.second)
+ if (!t.vars[x_export_poptions])
{
- // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED},
- // where <name> is the target name. Here we want to strike a balance
- // between being unique and not too noisy.
- //
- string d ("-DLIB");
+ auto p (t.vars.insert (c_export_poptions));
- auto upcase_sanitize = [] (char c) -> char
+ if (p.second)
{
- if (c == '-' || c == '+' || c == '.')
- return '_';
- else
- return ucase (c);
- };
+ // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED},
+ // where <name> is the target name. Here we want to strike a
+ // balance between being unique and not too noisy.
+ //
+ string d ("-DLIB");
- transform (t.name.begin (),
- t.name.end (),
- back_inserter (d),
- upcase_sanitize);
+ auto upcase_sanitize = [] (char c)
+ {
+ return (c == '-' || c == '+' || c == '.') ? '_' : ucase (c);
+ };
+
+ transform (t.name.begin (),
+ t.name.end (),
+ back_inserter (d),
+ upcase_sanitize);
- d += '_';
- d += suffix;
+ d += '_';
+ d += suffix;
- strings o;
- o.push_back (move (d));
- p.first.get () = move (o);
+ strings o;
+ o.push_back (move (d));
+ p.first.get () = move (o);
+ }
}
};
@@ -521,7 +517,7 @@ namespace build2
match_result link::
match (action a, target& t, const string& hint) const
{
- tracer trace ("cxx::link::match");
+ tracer trace (x, "link::match");
// @@ TODO:
//
@@ -537,16 +533,16 @@ namespace build2
otype lt (link_type (t));
- // Scan prerequisites and see if we can work with what we've got.
+ // Scan prerequisites and see if we can work with what we've got. Note
+ // that X could be C. We handle this by always checking for X first.
//
- bool seen_cxx (false), seen_c (false), seen_obj (false),
- seen_lib (false);
+ bool seen_x (false), seen_c (false), seen_obj (false), seen_lib (false);
for (prerequisite_member p: group_prerequisite_members (a, t))
{
- if (p.is_a<cxx> ())
+ if (p.is_a (x_src))
{
- seen_cxx = seen_cxx || true;
+ seen_x = seen_x || true;
}
else if (p.is_a<c> ())
{
@@ -585,12 +581,12 @@ namespace build2
}
}
- // We will only chain a C source if there is also a C++ source or we
- // were explicitly told to.
+ // We will only chain a C source if there is also an X source or we were
+ // explicitly told to.
//
- if (seen_c && !seen_cxx && hint < "cxx")
+ if (seen_c && !seen_x && hint < x)
{
- l4 ([&]{trace << "C prerequisite(s) without C++ or hint";});
+ l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint";});
return nullptr;
}
@@ -635,23 +631,19 @@ namespace build2
}
}
- return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr;
+ return seen_x || seen_c || seen_obj || seen_lib ? &t : nullptr;
}
recipe link::
apply (action a, target& xt, const match_result&) const
{
- tracer trace ("cxx::link::apply");
+ tracer trace (x, "link::apply");
file& t (static_cast<file&> (xt));
scope& bs (t.base_scope ());
scope& rs (*bs.root_scope ());
- const string& cid (cast<string> (rs["cxx.id"]));
- const string& tsys (cast<string> (rs["cxx.target.system"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
otype lt (link_type (t));
lorder lo (link_order (bs, lt));
@@ -767,7 +759,8 @@ namespace build2
//
if (lt != otype::a &&
cid == "msvc" &&
- find_option ("/DEBUG", t, "cxx.loptions", true))
+ (find_option ("/DEBUG", t, c_loptions, true) ||
+ find_option ("/DEBUG", t, x_loptions, true)))
{
// Add after the import library if any.
//
@@ -789,11 +782,11 @@ namespace build2
optional<dir_paths> lib_paths; // Extract lazily.
- // Process prerequisites: do rule chaining for C and C++ source
- // files as well as search and match.
+ // Process prerequisites: do rule chaining for C and X source files as
+ // well as search and match.
//
- // When cleaning, ignore prerequisites that are not in the same
- // or a subdirectory of our project root.
+ // When cleaning, ignore prerequisites that are not in the same or a
+ // subdirectory of our project root.
//
const target_type& ott (lt == otype::e ? obje::static_type :
lt == otype::a ? obja::static_type :
@@ -803,7 +796,7 @@ namespace build2
{
target* pt (nullptr);
- if (!p.is_a<c> () && !p.is_a<cxx> ())
+ if (!p.is_a (x_src) && !p.is_a<c> ())
{
// Handle imported libraries.
//
@@ -843,6 +836,9 @@ namespace build2
continue;
}
+ // The rest is rule chaining.
+ //
+
// Which scope shall we use to resolve the root? Unlikely, but
// possible, the prerequisite is from a different project
// altogether. So we are going to use the target's project.
@@ -853,15 +849,15 @@ namespace build2
//
bool group (!p.prerequisite.belongs (t)); // Group's prerequisite.
- const prerequisite_key& cp (p.key ()); // c(xx){} prerequisite key.
+ const prerequisite_key& cp (p.key ()); // C-source (X or C) key.
const target_type& tt (group ? obj::static_type : ott);
- // Come up with the obj*{} target. The c(xx){} prerequisite directory
+ // Come up with the obj*{} target. The source prerequisite directory
// can be relative (to the scope) or absolute. If it is relative, then
// use it as is. If absolute, then translate it to the corresponding
- // directory under out_root. While the c(xx){} directory is most
- // likely under src_root, it is also possible it is under out_root
- // (e.g., generated source).
+ // directory under out_root. While the source directory is most likely
+ // under src_root, it is also possible it is under out_root (e.g.,
+ // generated source).
//
dir_path d;
{
@@ -890,16 +886,16 @@ namespace build2
//
if (a.operation () == clean_id && !ot.dir.sub (rs.out_path ()))
{
- // If we shouldn't clean obj{}, then it is fair to assume
- // we shouldn't clean cxx{} either (generated source will
- // be in the same directory as obj{} and if not, well, go
- // find yourself another build system ;-)).
+ // If we shouldn't clean obj{}, then it is fair to assume we
+ // shouldn't clean the source either (generated source will be in
+ // the same directory as obj{} and if not, well, go find yourself
+ // another build system ;-)).
//
continue; // Skip.
}
- // If we have created the obj{} target group, pick one of its
- // members; the rest would be primarily concerned with it.
+ // If we have created the obj{} target group, pick one of its members;
+ // the rest would be primarily concerned with it.
//
if (group)
{
@@ -933,43 +929,45 @@ namespace build2
for (prerequisite_member p1:
reverse_group_prerequisite_members (a, *pt))
{
- // Ignore some known target types (fsdir, headers, libraries).
+ // Most of the time we will have just a single source so fast-path
+ // that case.
//
- if (p1.is_a<fsdir> () ||
- p1.is_a<h> () ||
- (p.is_a<cxx> () && (p1.is_a<hxx> () ||
- p1.is_a<ixx> () ||
- p1.is_a<txx> ())) ||
- p1.is_a<lib> () ||
- p1.is_a<liba> () ||
- p1.is_a<libs> ())
+ if (p1.is_a (x_src))
{
- continue;
- }
-
- if (!p1.is_a<cxx> ())
- fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << *pt <<
- info << "unexpected existing prerequisite type " << p1 <<
- info << "specify corresponding obj{} target explicitly";
+ if (!found)
+ {
+ build2::match (a, *pt); // Now p1 should be resolved.
- if (!found)
- {
- build2::match (a, *pt); // Now p1 should be resolved.
+ // Searching our own prerequisite is ok.
+ //
+ if (&p.search () != &p1.search ())
+ fail << "synthesized target for prerequisite " << cp << " "
+ << "would be incompatible with existing target " << *pt <<
+ info << "existing prerequisite " << p1 << " does not match "
+ << cp <<
+ info << "specify corresponding " << tt.name << "{} target "
+ << "explicitly";
+
+ found = true;
+ }
- // Searching our own prerequisite is ok.
- //
- if (&p.search () != &p1.search ())
- fail << "synthesized target for prerequisite " << cp << " would "
- << "be incompatible with existing target " << *pt <<
- info << "existing prerequisite " << p1 << " does not match "
- << cp <<
- info << "specify corresponding " << tt.name << "{} target "
- << "explicitly";
-
- found = true;
- // Check the rest of the prerequisites.
+ continue; // Check the rest of the prerequisites.
}
+
+ // Ignore some known target types (fsdir, headers, libraries).
+ //
+ if (p1.is_a<fsdir> () ||
+ p1.is_a<lib> () ||
+ p1.is_a<liba> () ||
+ p1.is_a<libs> () ||
+ (p.is_a (x_src) && x_header (p1)) ||
+ (p.is_a<c> () && p1.is_a<h> ()))
+ continue;
+
+ fail << "synthesized target for prerequisite " << cp
+ << " would be incompatible with existing target " << *pt <<
+ info << "unexpected existing prerequisite type " << p1 <<
+ info << "specify corresponding obj{} target explicitly";
}
if (!found)
@@ -978,12 +976,12 @@ namespace build2
//
ot.prerequisites.emplace_back (p.as_prerequisite (trace));
- // Add our lib*{} prerequisites to the object file (see
- // cxx.export.poptions above for details).
+ // Add our lib*{} prerequisites to the object file (see the export.*
+ // machinery for details).
//
// Note that we don't resolve lib{} to liba{}/libs{} here instead
// leaving it to whoever (e.g., the compile rule) will be needing
- // cxx.export.*. One reason for doing it there is that the object
+ // *.export.*. One reason for doing it there is that the object
// target might be specified explicitly by the user in which case
// they will have to specify the set of lib{} prerequisites and it's
// much cleaner to do as lib{} rather than liba{}/libs{}.
@@ -1007,9 +1005,12 @@ namespace build2
switch (a)
{
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: return noop_recipe; // Configure update.
+ case perform_update_id:
+ return [this] (action a, target& t) {return perform_update (a, t);};
+ case perform_clean_id:
+ return [this] (action a, target& t) {return perform_clean (a, t);};
+ default:
+ return noop_recipe; // Configure update.
}
}
@@ -1059,47 +1060,38 @@ namespace build2
}
}
- // See windows-manifest.cxx.
- //
- path
- windows_manifest (file&, bool rpath_assembly);
-
// See windows-rpath.cxx.
//
timestamp
windows_rpath_timestamp (file&);
void
- windows_rpath_assembly (file&, timestamp, bool scratch);
-
- const char*
- msvc_machine (const string& cpu); // msvc.cxx
+ windows_rpath_assembly (file&, const string& cpu, timestamp, bool scratch);
// Filter link.exe noise (msvc.cxx).
//
void
msvc_filter_link (ifdstream&, const file&, otype);
+ // Translate target CPU to /MACHINE option.
+ //
+ const char*
+ msvc_machine (const string& cpu); // msvc.cxx
+
target_state link::
- perform_update (action a, target& xt)
+ perform_update (action a, target& xt) const
{
- tracer trace ("cxx::link::perform_update");
+ tracer trace (x, "link::perform_update");
file& t (static_cast<file&> (xt));
+ scope& rs (t.root_scope ());
otype lt (link_type (t));
// Update prerequisites.
//
bool update (execute_prerequisites (a, t, t.mtime ()));
- scope& rs (t.root_scope ());
-
- const string& cid (cast<string> (rs["cxx.id"]));
- const string& tgt (cast<string> (rs["cxx.target"]));
- const string& tsys (cast<string> (rs["cxx.target.system"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
// If targeting Windows, take care of the manifest.
//
path manifest; // Manifest itself (msvc) or compiled object file.
@@ -1213,12 +1205,12 @@ namespace build2
// First should come the rule name/version.
//
- if (dd.expect ("cxx.link 1") != nullptr)
+ if (dd.expect (rule_id) != nullptr)
l4 ([&]{trace << "rule mismatch forcing update of " << t;});
lookup ranlib;
- // Then the linker checksum (ar/ranlib or C++ compiler).
+ // Then the linker checksum (ar/ranlib or the compiler).
//
if (lt == otype::a)
{
@@ -1244,16 +1236,16 @@ namespace build2
//
const string& cs (
cast<string> (
- rs[cid == "msvc" ? "bin.ld.checksum" : "cxx.checksum"]));
+ rs[cid == "msvc" ? var_pool["bin.ld.checksum"] : x_checksum]));
if (dd.expect (cs) != nullptr)
l4 ([&]{trace << "linker mismatch forcing update of " << t;});
}
// Next check the target. While it might be incorporated into the linker
- // checksum, it also might not (e.g., MS link.exe).
+ // checksum, it also might not (e.g., VC link.exe).
//
- if (dd.expect (tgt) != nullptr)
+ if (dd.expect (ctg) != nullptr)
l4 ([&]{trace << "target mismatch forcing update of " << t;});
// Start building the command line. While we don't yet know whether we
@@ -1264,7 +1256,7 @@ namespace build2
// second is simpler. Let's got with the simpler for now (actually it's
// kind of a hybrid).
//
- cstrings args {nullptr}; // Reserve one for config.bin.ar/config.cxx.
+ cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x.
// Storage.
//
@@ -1277,8 +1269,8 @@ namespace build2
if (cid == "msvc") ;
else
{
- // If the user asked for ranlib, don't try to do its function with -s.
- // Some ar implementations (e.g., the LLVM one) doesn't support
+ // If the user asked for ranlib, don't try to do its function with
+ // -s. Some ar implementations (e.g., the LLVM one) don't support
// leading '-'.
//
args.push_back (ranlib ? "rc" : "rcs");
@@ -1288,16 +1280,18 @@ namespace build2
{
if (cid == "msvc")
{
- // We are using link.exe directly so we don't pass the C++ compiler
+ // We are using link.exe directly so don't pass the compiler
// options.
}
else
{
- append_options (args, t, "cxx.coptions");
- append_std (args, rs, cid, t, std);
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
+ append_std (args, rs, t, std);
}
- append_options (args, t, "cxx.loptions");
+ append_options (args, t, c_loptions);
+ append_options (args, t, x_loptions);
// Handle soname/rpath.
//
@@ -1309,7 +1303,7 @@ namespace build2
auto l (t["bin.rpath"]);
if (l && !l->empty ())
- fail << tgt << " does not support rpath";
+ fail << ctg << " does not support rpath";
}
else
{
@@ -1443,7 +1437,10 @@ namespace build2
// Treat them as inputs, not options.
//
if (lt != otype::a)
- hash_options (cs, t, "cxx.libs");
+ {
+ hash_options (cs, t, c_libs);
+ hash_options (cs, t, x_libs);
+ }
if (dd.expect (cs.string ()) != nullptr)
l4 ([&]{trace << "file set mismatch forcing update of " << t;});
@@ -1451,8 +1448,8 @@ namespace build2
// If any of the above checks resulted in a mismatch (different linker,
// options or input file set), or if the database is newer than the
- // target (interrupted update) then force the target update. Also
- // note this situation in the "from scratch" flag.
+ // target (interrupted update) then force the target update. Also note
+ // this situation in the "from scratch" flag.
//
bool scratch (false);
if (dd.writing () || dd.mtime () > t.mtime ())
@@ -1484,14 +1481,13 @@ namespace build2
{
// lib.exe has /LIBPATH but it's not clear/documented what it's
// used for. Perhaps for link-time code generation (/LTCG)? If
- // that's the case, then we may need to pass cxx.loptions.
+ // that's the case, then we may need to pass *.loptions.
//
args.push_back ("/NOLOGO");
// Add /MACHINE.
//
- args.push_back (
- msvc_machine (cast<string> (rs["cxx.target.cpu"])));
+ args.push_back (msvc_machine (cast<string> (rs[x_target_cpu])));
out = "/OUT:" + relt.string ();
args.push_back (out.c_str ());
@@ -1518,8 +1514,7 @@ namespace build2
// Add /MACHINE.
//
- args.push_back (
- msvc_machine (cast<string> (rs["cxx.target.cpu"])));
+ args.push_back (msvc_machine (cast<string> (rs[x_target_cpu])));
// Unless explicitly enabled with /INCREMENTAL, disable
// incremental linking (it is implicitly enabled if /DEBUG is
@@ -1593,14 +1588,17 @@ namespace build2
// @@ An executable can have an import library and VS seems to
// always name it. I wonder what would trigger its generation?
- // Could it be the presence of export symbols?
-
+ // Could it be the presence of export symbols? Yes, link.exe
+ // will generate the import library iff there are exported
+ // symbols. Which means there could be a DLL without an import
+ // library (which we currently don't handle very well).
+ //
out = "/OUT:" + relt.string ();
args.push_back (out.c_str ());
}
else
{
- args[0] = cast<path> (rs["config.cxx"]).string ().c_str ();
+ args[0] = cast<path> (rs[config_x]).string ().c_str ();
// Add the option that triggers building a shared library and take
// care of any extras (e.g., import library).
@@ -1676,7 +1674,10 @@ namespace build2
args.push_back (sargs[i].c_str ());
if (lt != otype::a)
- append_options (args, t, "cxx.libs");
+ {
+ append_options (args, t, c_libs);
+ append_options (args, t, x_libs);
+ }
args.push_back (nullptr);
@@ -1777,7 +1778,10 @@ namespace build2
if (lt == otype::e && tclass == "windows")
{
if (a.outer_operation () != install_id)
- windows_rpath_assembly (t, rpath_timestamp, scratch);
+ windows_rpath_assembly (t,
+ cast<string> (rs[x_target_cpu]),
+ rpath_timestamp,
+ scratch);
}
rm.cancel ();
@@ -1791,14 +1795,10 @@ namespace build2
}
target_state link::
- perform_clean (action a, target& xt)
+ perform_clean (action a, target& xt) const
{
file& t (static_cast<file&> (xt));
- scope& rs (t.root_scope ());
- const string& tsys (cast<string> (rs["cxx.target.system"]));
- const string& tclass (cast<string> (rs["cxx.target.class"]));
-
initializer_list<const char*> e;
switch (link_type (t))
@@ -1846,7 +1846,5 @@ namespace build2
return clean_extra (a, t, e);
}
-
- link link::instance;
}
}
diff --git a/build2/cc/module b/build2/cc/module
new file mode 100644
index 0000000..bed7673
--- /dev/null
+++ b/build2/cc/module
@@ -0,0 +1,59 @@
+// file : build2/cc/module -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_MODULE
+#define BUILD2_CC_MODULE
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/module>
+#include <build2/variable>
+
+#include <build2/cc/common>
+
+#include <build2/cc/compile>
+#include <build2/cc/link>
+#include <build2/cc/install>
+
+namespace build2
+{
+ namespace cc
+ {
+ class config_module: public module_base, public virtual config_data
+ {
+ public:
+ explicit
+ config_module (config_data&& d) : config_data (move (d)) {}
+
+ void
+ init (scope&,
+ scope&,
+ const location&,
+ bool first,
+ const variable_map&);
+ };
+
+ class module: public module_base, protected virtual common,
+ link, compile, install
+ {
+ public:
+ explicit
+ module (data&& d)
+ : common (move (d)),
+ link (move (d)),
+ compile (move (d), *this),
+ install (move (d), *this) {}
+
+ void
+ init (scope&,
+ scope&,
+ const location&,
+ bool first,
+ const variable_map&);
+ };
+ }
+}
+
+#endif // BUILD2_CC_MODULE
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
new file mode 100644
index 0000000..3a7dad2
--- /dev/null
+++ b/build2/cc/module.cxx
@@ -0,0 +1,291 @@
+// file : build2/cc/module.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/cc/module>
+
+#include <iomanip> // left, setw()
+
+#include <butl/triplet>
+
+#include <build2/scope>
+#include <build2/context>
+#include <build2/diagnostics>
+
+#include <build2/bin/target>
+
+#include <build2/config/utility>
+#include <build2/install/utility>
+
+#include <build2/cc/guess>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ void config_module::
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ bool first,
+ const variable_map&)
+ {
+ tracer trace (x, "config_init");
+
+ // Configure.
+ //
+ string pattern; // Toolchain pattern.
+
+ if (first)
+ {
+ const variable& config_c_coptions (var_pool["config.cc.coptions"]);
+
+ // config.x
+ //
+ auto p (config::required (r, config_x, path (x_default)));
+
+ // Figure out which compiler we are dealing with, its target, etc.
+ //
+ const path& xc (cast<path> (p.first));
+ compiler_info ci (
+ guess (x_lang,
+ xc,
+ cast_null<strings> (r[config_c_coptions]),
+ cast_null<strings> (r[config_x_coptions])));
+
+ // If this is a new value (e.g., we are configuring), then print the
+ // report at verbosity level 2 and up (-v).
+ //
+ if (verb >= (p.second ? 2 : 3))
+ {
+ text << x << ' ' << project (r) << '@' << r.out_path () << '\n'
+ << " " << left << setw (11) << x << xc << '\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 (x_id) = ci.id.string ();
+ r.assign (x_id_type) = move (ci.id.type);
+ r.assign (x_id_variant) = move (ci.id.variant);
+
+ r.assign (x_version) = move (ci.version.string);
+ r.assign (x_version_major) = ci.version.major;
+ r.assign (x_version_minor) = ci.version.minor;
+ r.assign (x_version_patch) = ci.version.patch;
+ r.assign (x_version_build) = move (ci.version.build);
+
+ r.assign (x_signature) = move (ci.signature);
+ r.assign (x_checksum) = move (ci.checksum);
+
+ pattern = move (ci.pattern);
+
+ // Split/canonicalize the target. First see if the user asked us to
+ // use config.sub.
+ //
+ 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);
+
+ l5 ([&]{trace << "canonical target: '" << canon << "'; "
+ << "class: " << t.class_;});
+
+ // Enter as x.target.{cpu,vendor,system,version,class}.
+ //
+ r.assign (x_target) = move (canon);
+ r.assign (x_target_cpu) = move (t.cpu);
+ r.assign (x_target_vendor) = move (t.vendor);
+ r.assign (x_target_system) = move (t.system);
+ r.assign (x_target_version) = move (t.version);
+ r.assign (x_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 " << x_lang << "compiler target '"
+ << ci.target << "': " << e.what () <<
+ info << "consider using the --config-sub option";
+ }
+ }
+
+ // config.x.{p,c,l}options
+ // config.x.libs
+ //
+ // These are optional. We also merge them into the corresponding
+ // x.* 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:
+ //
+ // x.coptions = <overridable options> # Note: '='.
+ // using x
+ // x.coptions += <overriding options> # Note: '+='.
+ //
+ b.assign (x_poptions) += cast_null<strings> (
+ config::optional (r, config_x_poptions));
+
+ b.assign (x_coptions) += cast_null<strings> (
+ config::optional (r, config_x_coptions));
+
+ b.assign (x_loptions) += cast_null<strings> (
+ config::optional (r, config_x_loptions));
+
+ b.assign (x_libs) += cast_null<strings> (
+ config::optional (r, config_x_libs));
+
+ // Load cc.config.
+ //
+ if (!cast_false<bool> (b["cc.config.loaded"]))
+ {
+ // Prepare configuration hints. They are only used on the first load
+ // of cc.config so we only populate them on our first load.
+ //
+ variable_map h;
+ if (first)
+ {
+ h.assign ("config.cc.id") = cast<string> (r[x_id]);
+ h.assign ("config.cc.target") = cast<string> (r[x_target]);
+ if (!pattern.empty ())
+ h.assign ("config.cc.pattern") = move (pattern);
+ }
+
+ load_module ("cc.config", r, b, loc, false, h);
+ }
+ else if (first)
+ {
+ // If cc.config is already loaded, verify its configuration matched
+ // ours since it could have been loaded by another c-family module.
+ //
+ auto check = [&r, &loc, this](const char* cv,
+ const variable& xv,
+ const char* w)
+ {
+ const string& c (cast<string> (r[cv]));
+ const string& x (cast<string> (r[xv]));
+
+ if (c != x)
+ fail (loc) << "cc and " << x << " module " << w << " mismatch" <<
+ info << cv << " is " << c <<
+ info << xv.name << " is " << x;
+ };
+
+ // Note that we don't require that patterns match. Presumably, if the
+ // toolchain id and target are the same, then where exactly the tools
+ // (e.g., ar) come from doesn't really matter.
+ //
+ check ("cc.id", x_id, "toolchain id");
+ check ("cc.target", x_target, "target");
+ }
+ }
+
+ void module::
+ init (scope& r,
+ scope& b,
+ const location& loc,
+ bool,
+ const variable_map&)
+ {
+ tracer trace (x, "init");
+
+ // Load cc.core. Besides other things, this will load bin (core) plus
+ // extra bin.* modules we may need.
+ //
+ if (!cast_false<bool> (b["cc.core.loaded"]))
+ load_module ("cc.core", r, b, loc);
+
+ // Register target types and configure their "installability".
+ //
+ {
+ using namespace install;
+
+ auto& t (b.target_types);
+
+ t.insert (x_src);
+
+ // Install headers into install.include.
+ //
+ for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht)
+ {
+ t.insert (**ht);
+ install_path (**ht, b, dir_path ("include"));
+ }
+ }
+
+ // Register rules.
+ //
+ {
+ 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)?
+ //
+ compile& cr (*this);
+ link& lr (*this);
+ install& ir (*this);
+
+ r.insert<obje> (perform_update_id, x_compile, cr);
+ r.insert<obje> (perform_clean_id, x_compile, cr);
+ r.insert<obje> (configure_update_id, x_compile, cr);
+
+ r.insert<exe> (perform_update_id, x_link, lr);
+ r.insert<exe> (perform_clean_id, x_link, lr);
+ r.insert<exe> (configure_update_id, x_link, lr);
+
+ r.insert<exe> (perform_install_id, x_install, ir);
+
+ // 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, x_compile, cr);
+ r.insert<obja> (perform_clean_id, x_compile, cr);
+ r.insert<obja> (configure_update_id, x_compile, cr);
+
+ r.insert<liba> (perform_update_id, x_link, lr);
+ r.insert<liba> (perform_clean_id, x_link, lr);
+ r.insert<liba> (configure_update_id, x_link, lr);
+
+ r.insert<liba> (perform_install_id, x_install, ir);
+ }
+
+ r.insert<objs> (perform_update_id, x_compile, cr);
+ r.insert<objs> (perform_clean_id, x_compile, cr);
+ r.insert<objs> (configure_update_id, x_compile, cr);
+
+ r.insert<libs> (perform_update_id, x_link, lr);
+ r.insert<libs> (perform_clean_id, x_link, lr);
+ r.insert<libs> (configure_update_id, x_link, lr);
+
+ r.insert<libs> (perform_install_id, x_install, ir);
+ }
+ }
+ }
+}
diff --git a/build2/cxx/msvc.cxx b/build2/cc/msvc.cxx
index 9798046..84020d0 100644
--- a/build2/cxx/msvc.cxx
+++ b/build2/cc/msvc.cxx
@@ -1,4 +1,4 @@
-// file : build2/cxx/msvc.cxx -*- C++ -*-
+// file : build2/cc/msvc.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
@@ -11,14 +11,18 @@
#include <build2/filesystem>
#include <build2/diagnostics>
-#include <build2/cxx/common>
+#include <build2/bin/target>
+
+#include <build2/cc/types>
+
+#include <build2/cc/link>
using namespace std;
using namespace butl;
namespace build2
{
- namespace cxx
+ namespace cc
{
using namespace bin;
@@ -82,8 +86,8 @@ namespace build2
// Extract system library search paths from MSVC.
//
- void
- msvc_library_search_paths (scope&, const string&, dir_paths&)
+ void link::
+ msvc_library_search_paths (scope&, dir_paths&) const
{
// The linker doesn't seem to have any built-in paths and all of them
// come from the LIB environment variable.
@@ -212,16 +216,17 @@ namespace build2
template <typename T>
static T*
- search_library (const path& ld,
- const dir_path& d,
- prerequisite& p,
- otype lt,
- const char* pfx,
- const char* sfx)
+ msvc_search_library (const char* mod,
+ const path& ld,
+ const dir_path& d,
+ prerequisite& p,
+ otype lt,
+ const char* pfx,
+ const char* sfx)
{
// Pretty similar logic to link::search_library().
//
- tracer trace ("cxx::msvc_search_library");
+ tracer trace (mod, "msvc_search_library");
// Assemble the file path.
//
@@ -269,14 +274,17 @@ namespace build2
return nullptr;
}
- liba*
- msvc_search_static (const path& ld, const dir_path& d, prerequisite& p)
+ liba* link::
+ msvc_search_static (const path& ld,
+ const dir_path& d,
+ prerequisite& p) const
{
liba* r (nullptr);
- auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool
+ auto search = [&r, &ld, &d, &p, this] (const char* pf, const char* sf)
+ -> bool
{
- r = search_library<liba> (ld, d, p, otype::a, pf, sf);
+ r = msvc_search_library<liba> (x, ld, d, p, otype::a, pf, sf);
return r != nullptr;
};
@@ -293,17 +301,20 @@ namespace build2
search ("", "_static") ? r : nullptr;
}
- libs*
- msvc_search_shared (const path& ld, const dir_path& d, prerequisite& p)
+ libs* link::
+ msvc_search_shared (const path& ld,
+ const dir_path& d,
+ prerequisite& p) const
{
- tracer trace ("cxx::msvc_search_shared");
+ tracer trace (x, "link::msvc_search_shared");
libs* r (nullptr);
- auto search = [&r, &ld, &d, &p, &trace] (
+ auto search = [&r, &ld, &d, &p, &trace, this] (
const char* pf, const char* sf) -> bool
{
- if (libi* i = search_library<libi> (ld, d, p, otype::s, pf, sf))
+ if (libi* i =
+ msvc_search_library<libi> (x, ld, d, p, otype::s, pf, sf))
{
r = &targets.insert<libs> (d, dir_path (), p.name, nullptr, trace);
diff --git a/build2/cc/target b/build2/cc/target
new file mode 100644
index 0000000..2d8125b
--- /dev/null
+++ b/build2/cc/target
@@ -0,0 +1,48 @@
+// file : build2/cc/target -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_TARGET
+#define BUILD2_CC_TARGET
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/target>
+
+namespace build2
+{
+ namespace cc
+ {
+ // There is hardly a c-family compilation without a C header inclusion.
+ // As a result, this target type is registered for any c-family module.
+ //
+ class h: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // This one we define in cc but the target type is only registered by the
+ // c module. This way we can implement rule chaining without jumping
+ // through too many hoops (like resolving target type dynamically) but
+ // also without relaxing things too much (i.e., the user still won't be
+ // able to refer to c{} without loading the c module).
+ //
+ class c: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+ }
+}
+
+#endif // BUILD2_CC_TARGET
diff --git a/build2/cc/target.cxx b/build2/cc/target.cxx
new file mode 100644
index 0000000..7c2bb24
--- /dev/null
+++ b/build2/cc/target.cxx
@@ -0,0 +1,39 @@
+// file : build2/cc/target.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/cc/target>
+
+using namespace std;
+
+namespace build2
+{
+ namespace cc
+ {
+ extern const char ext_var[] = "extension"; // VC 19 rejects constexpr.
+
+ extern const char h_ext_def[] = "h";
+ const target_type h::static_type
+ {
+ "h",
+ &file::static_type,
+ &target_factory<h>,
+ &target_extension_var<ext_var, h_ext_def>,
+ nullptr,
+ &search_file,
+ false
+ };
+
+ extern const char c_ext_def[] = "c";
+ const target_type c::static_type
+ {
+ "c",
+ &file::static_type,
+ &target_factory<c>,
+ &target_extension_var<ext_var, c_ext_def>,
+ nullptr,
+ &search_file,
+ false
+ };
+ }
+}
diff --git a/build2/cc/types b/build2/cc/types
new file mode 100644
index 0000000..9cacc60
--- /dev/null
+++ b/build2/cc/types
@@ -0,0 +1,32 @@
+// file : build2/cc/types -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CC_TYPES
+#define BUILD2_CC_TYPES
+
+#include <build2/types>
+#include <build2/utility>
+
+namespace build2
+{
+ namespace cc
+ {
+ // Compiler language.
+ //
+ enum class lang {c, cxx};
+
+ ostream&
+ operator<< (ostream&, lang); // utility.ixx
+
+ // Compile/link output type (executable, static, or shared).
+ //
+ enum class otype {e, a, s};
+
+ // Library link order.
+ //
+ enum class lorder {a, s, a_s, s_a};
+ }
+}
+
+#endif // BUILD2_CC_TYPES
diff --git a/build2/cxx/common b/build2/cc/utility
index 77f1149..ae19d56 100644
--- a/build2/cxx/common
+++ b/build2/cc/utility
@@ -1,45 +1,34 @@
-// file : build2/cxx/common -*- C++ -*-
+// file : build2/cc/utility -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#ifndef BUILD2_CXX_COMMON
-#define BUILD2_CXX_COMMON
+#ifndef BUILD2_CC_UTILITY
+#define BUILD2_CC_UTILITY
#include <build2/types>
#include <build2/utility>
+#include <build2/target>
#include <build2/bin/target>
+#include <build2/cc/types>
+
namespace build2
{
- namespace cxx
+ struct variable;
+
+ namespace cc
{
- // Compile/link output type (executable, static, or shared).
+ // Compile/link output type.
//
- enum class otype {e, a, s};
-
- inline otype
- compile_type (target& t)
- {
- return
- t.is_a<bin::obje> () ? otype::e :
- t.is_a<bin::obja> () ? otype::a :
- otype::s;
- }
-
- inline otype
- link_type (target& t)
- {
- return
- t.is_a<bin::exe> () ? otype::e :
- t.is_a<bin::liba> () ? otype::a :
- otype::s;
- }
+ otype
+ compile_type (target&);
+
+ otype
+ link_type (target&);
// Library link order.
//
- enum class lorder {a, s, a_s, s_a};
-
// The reason we pass scope and not the target is because this function is
// called not only for exe/lib but also for obj as part of the library
// meta-information protocol implementation. Normally the bin.*.lib values
@@ -54,7 +43,22 @@ namespace build2
//
target&
link_member (bin::lib&, lorder);
+
+ // Append or hash library options from a pair of *.export.* variables
+ // (first one is cc.export.*) recursively, prerequisite libraries first.
+ //
+ void
+ append_lib_options (cstrings&, target&, lorder,
+ const variable&,
+ const variable&);
+
+ void
+ hash_lib_options (sha256&, target&, lorder,
+ const variable&,
+ const variable&);
}
}
-#endif // BUILD2_CXX_COMMON
+#include <build2/cc/utility.ixx>
+
+#endif // BUILD2_CC_UTILITY
diff --git a/build2/cxx/common.cxx b/build2/cc/utility.cxx
index ec724a5..773ba8f 100644
--- a/build2/cxx/common.cxx
+++ b/build2/cc/utility.cxx
@@ -1,25 +1,27 @@
-// file : build2/cxx/common.cxx -*- C++ -*-
+// file : build2/cc/utility.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <build2/cxx/common>
+#include <build2/cc/utility>
#include <build2/variable>
-#include <build2/algorithm>
+#include <build2/algorithm> // search()
+
+#include <build2/bin/target>
using namespace std;
namespace build2
{
- namespace cxx
+ namespace cc
{
using namespace bin;
lorder
link_order (scope& bs, otype ot)
{
- // Initialize to suppress 'may be used uninitialized' warning produced by
- // MinGW GCC 5.4.0.
+ // Initialize to suppress 'may be used uninitialized' warning produced
+ // by MinGW GCC 5.4.0.
//
const char* var (nullptr);
@@ -69,5 +71,45 @@ namespace build2
return *r;
}
+
+ void
+ append_lib_options (cstrings& args, target& l, lorder lo,
+ const variable& cv,
+ const variable& xv)
+ {
+ using namespace bin;
+
+ for (target* t: l.prerequisite_targets)
+ {
+ if (lib* l = t->is_a<lib> ())
+ t = &link_member (*l, lo); // Pick one of the members.
+
+ if (t->is_a<liba> () || t->is_a<libs> ())
+ append_lib_options (args, *t, lo, cv, xv);
+ }
+
+ append_options (args, l, cv);
+ append_options (args, l, xv);
+ }
+
+ void
+ hash_lib_options (sha256& csum, target& l, lorder lo,
+ const variable& cv,
+ const variable& xv)
+ {
+ using namespace bin;
+
+ for (target* t: l.prerequisite_targets)
+ {
+ if (lib* l = t->is_a<lib> ())
+ t = &link_member (*l, lo); // Pick one of the members.
+
+ if (t->is_a<liba> () || t->is_a<libs> ())
+ hash_lib_options (csum, *t, lo, cv, xv);
+ }
+
+ hash_options (csum, l, cv);
+ hash_options (csum, l, xv);
+ }
}
}
diff --git a/build2/cc/utility.ixx b/build2/cc/utility.ixx
new file mode 100644
index 0000000..e7eb565
--- /dev/null
+++ b/build2/cc/utility.ixx
@@ -0,0 +1,33 @@
+// file : build2/cc/utility.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace build2
+{
+ namespace cc
+ {
+ inline ostream&
+ operator<< (ostream& os, lang l)
+ {
+ return os << (l == lang::c ? "C" : "C++");
+ }
+
+ inline otype
+ compile_type (target& t)
+ {
+ return
+ t.is_a<bin::obje> () ? otype::e :
+ t.is_a<bin::obja> () ? otype::a :
+ otype::s;
+ }
+
+ inline otype
+ link_type (target& t)
+ {
+ return
+ t.is_a<bin::exe> () ? otype::e :
+ t.is_a<bin::liba> () ? otype::a :
+ otype::s;
+ }
+ }
+}
diff --git a/build2/cxx/windows-manifest.cxx b/build2/cc/windows-manifest.cxx
index 915610d..0666ef5 100644
--- a/build2/cxx/windows-manifest.cxx
+++ b/build2/cc/windows-manifest.cxx
@@ -1,4 +1,4 @@
-// file : build2/cxx/windows-manifest.cxx -*- C++ -*-
+// file : build2/cc/windows-manifest.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
@@ -9,12 +9,14 @@
#include <build2/filesystem>
#include <build2/diagnostics>
+#include <build2/cc/link>
+
using namespace std;
using namespace butl;
namespace build2
{
- namespace cxx
+ namespace cc
{
// Translate the compiler target CPU value to the processorArchitecture
// attribute value.
@@ -36,16 +38,14 @@ namespace build2
// Generate a Windows manifest and if necessary create/update the manifest
// file corresponding to the exe{} target. Return the manifest file path.
//
- path
- windows_manifest (file& t, bool rpath_assembly)
+ path link::
+ windows_manifest (file& t, bool rpath_assembly) const
{
- tracer trace ("cxx::windows_manifest");
+ tracer trace (x, "windows_manifest");
scope& rs (t.root_scope ());
- const char* pa (
- windows_manifest_arch (
- cast<string> (rs["cxx.target.cpu"])));
+ const char* pa (windows_manifest_arch (cast<string> (rs[x_target_cpu])));
string m;
diff --git a/build2/cxx/windows-rpath.cxx b/build2/cc/windows-rpath.cxx
index b52315c..ea20a5c 100644
--- a/build2/cxx/windows-rpath.cxx
+++ b/build2/cc/windows-rpath.cxx
@@ -1,4 +1,4 @@
-// file : build2/cxx/windows-rpath.cxx -*- C++ -*-
+// file : build2/cc/windows-rpath.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
@@ -19,7 +19,7 @@ using namespace butl;
namespace build2
{
- namespace cxx
+ namespace cc
{
// Provide limited emulation of the rpath functionality on Windows using a
// side-by-side assembly. In a nutshell, the idea is to create an assembly
@@ -104,7 +104,10 @@ namespace build2
// manifest file.
//
void
- windows_rpath_assembly (file& t, timestamp ts, bool scratch)
+ windows_rpath_assembly (file& t,
+ const string& tcpu,
+ timestamp ts,
+ bool scratch)
{
// Assembly paths and name.
//
@@ -129,8 +132,6 @@ namespace build2
return;
}
- scope& rs (t.root_scope ());
-
// Next collect the set of DLLs that will be in our assembly. We need to
// do this recursively which means we may end up with duplicates. Also,
// it is possible that there aren't/no longer are any DLLs which means
@@ -157,9 +158,7 @@ namespace build2
mkdir (ad, 3);
}
- const char* pa (
- windows_manifest_arch (
- cast<string> (rs["cxx.target.cpu"])));
+ const char* pa (windows_manifest_arch (tcpu));
if (verb >= 3)
text << "cat >" << am;
@@ -176,7 +175,7 @@ namespace build2
<< " processorArchitecture='" << pa << "'\n"
<< " version='0.0.0.0'/>\n";
- scope& as (*rs.weak_scope ()); // Amalgamation scope.
+ scope& as (*t.root_scope ().weak_scope ()); // Amalgamation scope.
auto link = [&as, &ad] (const path& f, const path& l)
{
diff --git a/build2/cxx/compile b/build2/cxx/compile
deleted file mode 100644
index 16c62e6..0000000
--- a/build2/cxx/compile
+++ /dev/null
@@ -1,37 +0,0 @@
-// file : build2/cxx/compile -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CXX_COMPILE
-#define BUILD2_CXX_COMPILE
-
-#include <build2/types>
-#include <build2/utility>
-
-#include <build2/rule>
-
-namespace build2
-{
- namespace cxx
- {
- class compile: public rule
- {
- public:
- virtual match_result
- match (action, target&, const string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static target_state
- perform_clean (action, target&);
-
- static compile instance;
- };
- }
-}
-
-#endif // BUILD2_CXX_COMPILE
diff --git a/build2/cxx/link b/build2/cxx/link
deleted file mode 100644
index 4f00ea0..0000000
--- a/build2/cxx/link
+++ /dev/null
@@ -1,48 +0,0 @@
-// file : build2/cxx/link -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CXX_LINK
-#define BUILD2_CXX_LINK
-
-#include <build2/types>
-#include <build2/utility>
-
-#include <build2/rule>
-
-#include <build2/bin/target>
-
-namespace build2
-{
- namespace cxx
- {
- class link: public rule
- {
- public:
- virtual match_result
- match (action, target&, const string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static target_state
- perform_clean (action, target&);
-
- static link instance;
-
- private:
- friend class compile;
-
- static target*
- search_library (optional<dir_paths>&, prerequisite&);
-
- static dir_paths
- extract_library_paths (scope&);
- };
- }
-}
-
-#endif // BUILD2_CXX_LINK
diff --git a/build2/cxx/module b/build2/cxx/module
index 37466ef..8c1a01f 100644
--- a/build2/cxx/module
+++ b/build2/cxx/module
@@ -15,6 +15,15 @@ namespace build2
namespace cxx
{
bool
+ config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
init (scope&,
scope&,
const location&,
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;
}
}
diff --git a/build2/cxx/target b/build2/cxx/target
index 154ec24..0239c25 100644
--- a/build2/cxx/target
+++ b/build2/cxx/target
@@ -9,11 +9,15 @@
#include <build2/utility>
#include <build2/target>
+#include <build2/cc/target>
namespace build2
{
namespace cxx
{
+ using cc::h;
+ using cc::c;
+
class hxx: public file
{
public:
@@ -53,28 +57,6 @@ namespace build2
static const target_type static_type;
virtual const target_type& dynamic_type () const {return static_type;}
};
-
- //@@ TMP: should be in c-common or some such.
- //
- class h: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class c: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
}
}
diff --git a/build2/cxx/target.cxx b/build2/cxx/target.cxx
index 22ace50..30afd89 100644
--- a/build2/cxx/target.cxx
+++ b/build2/cxx/target.cxx
@@ -59,29 +59,5 @@ namespace build2
&search_file,
false
};
-
- extern const char h_ext_def[] = "h";
- const target_type h::static_type
- {
- "h",
- &file::static_type,
- &target_factory<h>,
- &target_extension_var<ext_var, h_ext_def>,
- nullptr,
- &search_file,
- false
- };
-
- extern const char c_ext_def[] = "c";
- const target_type c::static_type
- {
- "c",
- &file::static_type,
- &target_factory<c>,
- &target_extension_var<ext_var, c_ext_def>,
- nullptr,
- &search_file,
- false
- };
}
}
diff --git a/build2/cxx/utility b/build2/cxx/utility
deleted file mode 100644
index 7333af6..0000000
--- a/build2/cxx/utility
+++ /dev/null
@@ -1,42 +0,0 @@
-// file : build2/cxx/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_CXX_UTILITY
-#define BUILD2_CXX_UTILITY
-
-#include <build2/types>
-#include <build2/utility>
-
-#include <build2/target>
-
-#include <build2/cxx/common>
-
-namespace build2
-{
- namespace cxx
- {
- // T is either target or scope.
- //
- template <typename T>
- void
- append_std (cstrings&, scope& rs, const string& cid, T&, string& storage);
-
- template <typename T>
- void
- hash_std (sha256&, scope& rs, const string& cid, T&);
-
- // Append or hash library options from one of the cxx.export.* variables
- // recursively, prerequisite libraries first.
- //
- void
- append_lib_options (cstrings&, target&, const char* variable, lorder);
-
- void
- hash_lib_options (sha256&, target&, const char* variable, lorder);
- }
-}
-
-#include <build2/cxx/utility.ixx>
-
-#endif // BUILD2_CXX_UTILITY
diff --git a/build2/cxx/utility.cxx b/build2/cxx/utility.cxx
deleted file mode 100644
index 7aae6ac..0000000
--- a/build2/cxx/utility.cxx
+++ /dev/null
@@ -1,109 +0,0 @@
-// file : build2/cxx/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/cxx/utility>
-
-#include <build2/bin/target>
-
-using namespace std;
-
-namespace build2
-{
- namespace cxx
- {
- // Return true if there is an option (stored in s).
- //
- bool
- translate_std (scope& rs, const string& cid, const value& val, string& s)
- {
- const string& v (cast<string> (val));
-
- if (cid == "msvc")
- {
- // 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).
- //
- // For now we are not going to bother doing this for C++03.
- //
- if (v != "98" && v != "03")
- {
- uint64_t cver (cast<uint64_t> (rs["cxx.version.major"]));
-
- // @@ Is mapping for 14 and 17 correct? Maybe Update 2 for 14?
- //
- 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.
- {
- fail << "C++" << v << " is not supported by "
- << cast<string> (rs["cxx.signature"]) <<
- info << "required by " << rs.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
- s += v; // In case the user specifies something like 'gnu++17'.
-
- return true;
- }
- }
-
- void
- append_lib_options (cstrings& args, target& l, const char* var, lorder lo)
- {
- using namespace bin;
-
- for (target* t: l.prerequisite_targets)
- {
- if (lib* l = t->is_a<lib> ())
- t = &link_member (*l, lo); // Pick one of the members.
-
- if (t->is_a<liba> () || t->is_a<libs> ())
- append_lib_options (args, *t, var, lo);
- }
-
- append_options (args, l, var);
- }
-
- void
- hash_lib_options (sha256& csum, target& l, const char* var, lorder lo)
- {
- using namespace bin;
-
- for (target* t: l.prerequisite_targets)
- {
- if (lib* l = t->is_a<lib> ())
- t = &link_member (*l, lo); // Pick one of the members.
-
- if (t->is_a<liba> () || t->is_a<libs> ())
- hash_lib_options (csum, *t, var, lo);
- }
-
- hash_options (csum, l, var);
- }
- }
-}
diff --git a/build2/cxx/utility.ixx b/build2/cxx/utility.ixx
deleted file mode 100644
index c624e87..0000000
--- a/build2/cxx/utility.ixx
+++ /dev/null
@@ -1,33 +0,0 @@
-// file : build2/cxx/utility.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build2
-{
- namespace cxx
- {
- bool
- translate_std (scope&, const string&, const value&, string&);
-
- template <typename T>
- inline void
- append_std (cstrings& args, scope& rs, const string& cid, T& t, string& s)
- {
- if (auto l = t["cxx.std"])
- if (translate_std (rs, cid, *l, s))
- args.push_back (s.c_str ());
- }
-
- template <typename T>
- inline void
- hash_std (sha256& csum, scope& rs, const string& cid, T& t)
- {
- if (auto l = t["cxx.std"])
- {
- string s;
- if (translate_std (rs, cid, *l, s))
- csum.append (s);
- }
- }
- }
-}
diff --git a/build2/diagnostics b/build2/diagnostics
index 8aedf13..5441df1 100644
--- a/build2/diagnostics
+++ b/build2/diagnostics
@@ -259,14 +259,18 @@ namespace build2
struct simple_prologue_base
{
explicit
- simple_prologue_base (const char* type, const char* name, uint16_t sverb)
- : type_ (type), name_ (name), sverb_ (sverb) {}
+ simple_prologue_base (const char* type,
+ const char* mod,
+ const char* name,
+ uint16_t sverb)
+ : type_ (type), mod_ (mod), name_ (name), sverb_ (sverb) {}
void
operator() (const diag_record& r) const;
private:
const char* type_;
+ const char* mod_;
const char* name_;
const uint16_t sverb_;
};
@@ -290,16 +294,18 @@ namespace build2
struct location_prologue_base
{
location_prologue_base (const char* type,
+ const char* mod,
const char* name,
const location& l,
uint16_t sverb)
- : type_ (type), name_ (name), loc_ (l), sverb_ (sverb) {}
+ : type_ (type), mod_ (mod), name_ (name), loc_ (l), sverb_ (sverb) {}
void
operator() (const diag_record& r) const;
private:
const char* type_;
+ const char* mod_;
const char* name_;
const location loc_;
const uint16_t sverb_;
@@ -311,23 +317,24 @@ namespace build2
explicit
basic_mark_base (uint16_t (*sverb) (),
const char* type,
+ const char* mod = nullptr,
const char* name = nullptr,
const void* data = nullptr,
diag_epilogue epilogue = nullptr)
: sverb_ (sverb),
- type_ (type), name_ (name), data_ (data),
+ type_ (type), mod_ (mod), name_ (name), data_ (data),
epilogue_ (epilogue) {}
simple_prologue
operator() () const
{
- return simple_prologue (epilogue_, type_, name_, sverb_ ());
+ return simple_prologue (epilogue_, type_, mod_, name_, sverb_ ());
}
location_prologue
operator() (const location& l) const
{
- return location_prologue (epilogue_, type_, name_, l, sverb_ ());
+ return location_prologue (epilogue_, type_, mod_, name_, l, sverb_ ());
}
template <typename L>
@@ -335,12 +342,13 @@ namespace build2
operator() (const L& l) const
{
return location_prologue (
- epilogue_, type_, name_, get_location (l, data_), sverb_ ());
+ epilogue_, type_, mod_, name_, get_location (l, data_), sverb_ ());
}
protected:
uint16_t (*sverb_) ();
const char* type_;
+ const char* mod_;
const char* name_;
const void* data_;
const diag_epilogue epilogue_;
@@ -358,8 +366,15 @@ namespace build2
{
explicit
trace_mark_base (const char* name, const void* data = nullptr)
- : basic_mark_base ([]() {return stream_verb_max;}, "trace", name, data)
- {}
+ : trace_mark_base (nullptr, name, data) {}
+
+ trace_mark_base (const char* mod,
+ const char* name,
+ const void* data = nullptr)
+ : basic_mark_base ([]() {return stream_verb_max;},
+ "trace",
+ mod, name,
+ data) {}
};
typedef diag_mark<trace_mark_base> trace_mark;
@@ -372,8 +387,11 @@ namespace build2
{
explicit
fail_mark_base (const void* data = nullptr)
- : basic_mark_base (&stream_verb_map, "error", nullptr, data, &epilogue)
- {}
+ : basic_mark_base (&stream_verb_map,
+ "error",
+ nullptr, nullptr,
+ data,
+ &epilogue) {}
static void
epilogue (const diag_record&) {throw E ();}
diff --git a/build2/diagnostics.cxx b/build2/diagnostics.cxx
index 7b60d05..1265f73 100644
--- a/build2/diagnostics.cxx
+++ b/build2/diagnostics.cxx
@@ -97,6 +97,9 @@ namespace build2
if (type_ != nullptr)
r << type_ << ": ";
+ if (mod_ != nullptr)
+ r << mod_ << "::";
+
if (name_ != nullptr)
r << name_ << ": ";
}
@@ -119,6 +122,9 @@ namespace build2
if (type_ != nullptr)
r << type_ << ": ";
+ if (mod_ != nullptr)
+ r << mod_ << "::";
+
if (name_ != nullptr)
r << name_ << ": ";
}
diff --git a/build2/prerequisite b/build2/prerequisite
index 7c913f3..e53bb41 100644
--- a/build2/prerequisite
+++ b/build2/prerequisite
@@ -130,6 +130,9 @@ namespace build2
template <typename T>
bool
is_a () const {return type.is_a<T> ();}
+
+ bool
+ is_a (const target_type_type& tt) const {return type.is_a (tt);}
};
inline bool
diff --git a/build2/rule b/build2/rule
index 03e4bf2..6d4a580 100644
--- a/build2/rule
+++ b/build2/rule
@@ -13,6 +13,9 @@
namespace build2
{
+ // @@ This is an ugly mess that is overdue for redesign. Perhaps
+ // something similar to variable value storage?
+ //
class match_result
{
public:
@@ -56,6 +59,14 @@ namespace build2
{
return target != nullptr || prerequisite != nullptr;
}
+
+ template <typename T>
+ T&
+ as_target () const
+ {
+ return static_cast<T&> (
+ target != nullptr ? *target : *prerequisite->target);
+ }
};
class rule
diff --git a/build2/target b/build2/target
index 7a50a19..103b719 100644
--- a/build2/target
+++ b/build2/target
@@ -68,6 +68,11 @@ namespace build2
// this case the returned by the recipe value is still used as the resulting
// target state so it should match the group's state.
//
+ // Note that max size for the "small capture optimization" in std::function
+ // ranges (in pointer sizes) from 0 (GCC prior to 5) to 2 (GCC 5) to 6 (VC
+ // 14u2). With the size ranging (in bytes for 64-bit target) from 32 (GCC)
+ // to 64 (VC).
+ //
using recipe_function = target_state (action, target&);
using recipe = function<recipe_function>;
@@ -471,6 +476,9 @@ namespace build2
const T*
is_a () const {return dynamic_cast<const T*> (this);}
+ bool
+ is_a (const target_type& tt) const {return type ().is_a (tt);}
+
// Dynamic derivation to support define.
//
const target_type* derived_type = nullptr;
@@ -622,8 +630,9 @@ namespace build2
//
struct prerequisite_member
{
- typedef build2::target target_type;
- typedef build2::prerequisite prerequisite_type;
+ using target_type = build2::target;
+ using prerequisite_type = build2::prerequisite;
+ using target_type_type = build2::target_type;
prerequisite_ref& prerequisite;
target_type* target;
@@ -637,6 +646,14 @@ namespace build2
: prerequisite.get ().is_a<T> ();
}
+ bool
+ is_a (const target_type_type& tt) const
+ {
+ return target != nullptr
+ ? target->is_a (tt)
+ : prerequisite.get ().is_a (tt);
+ }
+
prerequisite_key
key () const
{
@@ -645,7 +662,7 @@ namespace build2
: prerequisite.get ().key ();
}
- const build2::target_type&
+ const target_type_type&
type () const
{
return target != nullptr ? target->type () : prerequisite.get ().type;
diff --git a/build2/target-type b/build2/target-type
index 6006b2c..6140ab1 100644
--- a/build2/target-type
+++ b/build2/target-type
@@ -46,12 +46,18 @@ namespace build2
target* (*search) (const prerequisite_key&);
bool see_through; // A group with the default "see through" semantics.
- bool
- is_a (const target_type&) const; // Defined in target.cxx
-
template <typename T>
bool
is_a () const {return is_a (T::static_type);}
+
+ bool
+ is_a (const target_type& tt) const
+ {
+ return this == &tt || (base != nullptr && is_a_base (tt));
+ }
+
+ bool
+ is_a_base (const target_type&) const; // Defined in target.cxx
};
inline bool
@@ -97,12 +103,16 @@ namespace build2
class target_type_map: public target_type_map_base
{
public:
- void
- insert (const target_type& tt) {emplace (tt.name, target_type_ref (tt));}
+ const target_type&
+ insert (const target_type& tt)
+ {
+ emplace (tt.name, target_type_ref (tt));
+ return tt;
+ }
template <typename T>
- void
- insert () {insert (T::static_type);}
+ const target_type&
+ insert () {return insert (T::static_type);}
};
}
diff --git a/build2/target.cxx b/build2/target.cxx
index a0283b0..7a33dbc 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -18,10 +18,10 @@ namespace build2
// target_type
//
bool target_type::
- is_a (const target_type& tt) const
+ is_a_base (const target_type& tt) const
{
- for (const target_type* p (this); p != nullptr; p = p->base)
- if (*p == tt)
+ for (const target_type* b (base); b != nullptr; b = b->base)
+ if (*b == tt)
return true;
return false;
diff --git a/build2/utility b/build2/utility
index 4c49be5..3ecceed 100644
--- a/build2/utility
+++ b/build2/utility
@@ -146,13 +146,23 @@ namespace build2
// Append all the values from a variable to the C-string list. T is either
// target or scope. The variable is expected to be of type strings.
//
+ struct variable;
+
+ template <typename T>
+ void
+ append_options (cstrings&, T&, const variable&);
+
+ template <typename T>
+ void
+ append_options (cstrings&, T&, const char*);
+
template <typename T>
void
- append_options (cstrings&, T&, const char* variable);
+ hash_options (sha256&, T&, const variable&);
template <typename T>
void
- hash_options (sha256&, T&, const char* variable);
+ hash_options (sha256&, T&, const char*);
// As above but from the strings value directly.
//
@@ -178,6 +188,13 @@ namespace build2
bool
find_option (const char* option,
T&,
+ const variable&,
+ bool ignore_case = false);
+
+ template <typename T>
+ bool
+ find_option (const char* option,
+ T&,
const char* variable,
bool ignore_case = false);
@@ -194,6 +211,13 @@ namespace build2
//
template <typename T>
bool
+ find_options (initializer_list<const char*>,
+ T&,
+ const variable&,
+ bool = false);
+
+ template <typename T>
+ bool
find_options (initializer_list<const char*>, T&, const char*, bool = false);
bool
@@ -209,6 +233,10 @@ namespace build2
//
template <typename T>
bool
+ find_option_prefix (const char* prefix, T&, const variable&, bool = false);
+
+ template <typename T>
+ bool
find_option_prefix (const char* prefix, T&, const char*, bool = false);
bool
@@ -226,6 +254,13 @@ namespace build2
bool
find_option_prefixes (initializer_list<const char*>,
T&,
+ const variable&,
+ bool = false);
+
+ template <typename T>
+ bool
+ find_option_prefixes (initializer_list<const char*>,
+ T&,
const char*,
bool = false);
diff --git a/build2/utility.ixx b/build2/utility.ixx
index f957875..956a726 100644
--- a/build2/utility.ixx
+++ b/build2/utility.ixx
@@ -35,6 +35,13 @@ namespace build2
template <typename T>
inline void
+ append_options (cstrings& args, T& s, const variable& var)
+ {
+ append_options (args, s[var]);
+ }
+
+ template <typename T>
+ inline void
append_options (cstrings& args, T& s, const char* var)
{
append_options (args, s[var]);
@@ -42,14 +49,27 @@ namespace build2
template <typename T>
inline void
- hash_options (sha256& csum, T& s, const char* var)
+ hash_options (sha256& csum, T& s, const variable& var)
{
+ hash_options (csum, s[var]);
+ }
+ template <typename T>
+ inline void
+ hash_options (sha256& csum, T& s, const char* var)
+ {
hash_options (csum, s[var]);
}
template <typename T>
inline bool
+ find_option (const char* o, T& s, const variable& var, bool ic)
+ {
+ return find_option (o, s[var], ic);
+ }
+
+ template <typename T>
+ inline bool
find_option (const char* o, T& s, const char* var, bool ic)
{
return find_option (o, s[var], ic);
@@ -59,6 +79,16 @@ namespace build2
inline bool
find_options (initializer_list<const char*> os,
T& s,
+ const variable& var,
+ bool ic)
+ {
+ return find_options (os, s[var], ic);
+ }
+
+ template <typename T>
+ inline bool
+ find_options (initializer_list<const char*> os,
+ T& s,
const char* var,
bool ic)
{
@@ -67,6 +97,13 @@ namespace build2
template <typename T>
inline bool
+ find_option_prefix (const char* p, T& s, const variable& var, bool ic)
+ {
+ return find_option_prefix (p, s[var], ic);
+ }
+
+ template <typename T>
+ inline bool
find_option_prefix (const char* p, T& s, const char* var, bool ic)
{
return find_option_prefix (p, s[var], ic);
@@ -76,6 +113,16 @@ namespace build2
inline bool
find_option_prefixes (initializer_list<const char*> ps,
T& s,
+ const variable& var,
+ bool ic)
+ {
+ return find_option_prefixes (ps, s[var], ic);
+ }
+
+ template <typename T>
+ inline bool
+ find_option_prefixes (initializer_list<const char*> ps,
+ T& s,
const char* var,
bool ic)
{
diff --git a/build2/variable b/build2/variable
index 9888aa0..d6be976 100644
--- a/build2/variable
+++ b/build2/variable
@@ -691,7 +691,11 @@ namespace build2
}
const variable&
- find (const string& name);
+ find (const string& name); //@@ TODO: Move to operator[], remove.
+ //@@ ranmae var_pool to varpool or vpool?
+
+ const variable&
+ operator[] (const string& name) {return find (name);}
using variable_pool_base::clear;