aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-08-25 17:40:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-08-25 17:40:21 +0200
commit32e04ad4b4a8dec07836b7c9fcf90fe72a006990 (patch)
tree8a0ce76d3a2f644396348fbf0d3d5d00bab82a0c
parent236497a013790c879e9e798bd68083252b43b057 (diff)
Implement missing pieces in utility libraries support
In particular, we can now build static libraries out of utility libraries.
-rw-r--r--build2/bin/init.cxx189
-rw-r--r--build2/bin/rule.cxx24
-rw-r--r--build2/bin/rule.hxx12
-rw-r--r--build2/bin/target.cxx108
-rw-r--r--build2/bin/target.hxx24
-rw-r--r--build2/cc/common.cxx257
-rw-r--r--build2/cc/common.hxx5
-rw-r--r--build2/cc/install-rule.cxx4
-rw-r--r--build2/cc/link-rule.cxx245
-rw-r--r--build2/cc/msvc.cxx18
-rw-r--r--build2/cc/pkgconfig.cxx4
-rw-r--r--build2/cc/utility.cxx31
-rw-r--r--build2/cc/utility.hxx2
-rw-r--r--build2/cc/windows-rpath.cxx14
14 files changed, 624 insertions, 313 deletions
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index 7e022fda..6d99e26 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -38,7 +38,7 @@ namespace build2
static const strings libs_lib {"shared", "static"};
bool
- vars_init (scope& r,
+ vars_init (scope& rs,
scope&,
const location&,
unique_ptr<module_base>&,
@@ -47,7 +47,7 @@ namespace build2
const variable_map&)
{
tracer trace ("bin::vars_init");
- l5 ([&]{trace << "for " << r.out_path ();});
+ l5 ([&]{trace << "for " << rs.out_path ();});
assert (first);
@@ -56,7 +56,7 @@ namespace build2
// Target is a string and not target_triplet because it can be
// specified by the user.
//
- auto& vp (var_pool.rw (r));
+ auto& vp (var_pool.rw (rs));
vp.insert<string> ("config.bin.target", true);
vp.insert<string> ("config.bin.pattern", true);
@@ -91,7 +91,7 @@ namespace build2
// example:
//
// exe{test}: liba{foo}
- // liba{foo}: libu{foo1 foo2}
+ // liba{foo}: libua{foo1 foo2}
// liba{foo}: bin.whole = false # Affects test but not foo1 and foo2.
//
// If unspecified, defaults to false for liba{} and to true for libux{}.
@@ -121,6 +121,11 @@ namespace build2
tracer trace ("bin::config_init");
l5 ([&]{trace << "for " << bs.out_path ();});
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << "bin.config module must be loaded in project root";
+
// Load bin.vars.
//
if (!cast_false<bool> (rs["bin.vars.loaded"]))
@@ -149,7 +154,7 @@ namespace build2
// config.bin.lib
//
{
- value& v (bs.assign ("bin.lib"));
+ value& v (rs.assign ("bin.lib"));
if (!v)
v = *required (rs, "config.bin.lib", "both").first;
}
@@ -157,7 +162,7 @@ namespace build2
// config.bin.exe.lib
//
{
- value& v (bs.assign ("bin.exe.lib"));
+ value& v (rs.assign ("bin.exe.lib"));
if (!v)
v = *required (rs, "config.bin.exe.lib", exe_lib).first;
}
@@ -165,7 +170,7 @@ namespace build2
// config.bin.liba.lib
//
{
- value& v (bs.assign ("bin.liba.lib"));
+ value& v (rs.assign ("bin.liba.lib"));
if (!v)
v = *required (rs, "config.bin.liba.lib", liba_lib).first;
}
@@ -173,7 +178,7 @@ namespace build2
// config.bin.libs.lib
//
{
- value& v (bs.assign ("bin.libs.lib"));
+ value& v (rs.assign ("bin.libs.lib"));
if (!v)
v = *required (rs, "config.bin.libs.lib", libs_lib).first;
}
@@ -183,7 +188,7 @@ namespace build2
// This one is optional and we merge it into bin.rpath, if any.
// See the cxx module for details on merging.
//
- bs.assign ("bin.rpath") += cast_null<dir_paths> (
+ rs.assign ("bin.rpath") += cast_null<dir_paths> (
optional (rs, "config.bin.rpath"));
// config.bin.{lib,exe}.{prefix,suffix}
@@ -196,13 +201,13 @@ namespace build2
lookup p (omitted (rs, "config.bin.prefix").first);
lookup s (omitted (rs, "config.bin.suffix").first);
- auto set = [&rs, &bs] (const char* bv, const char* cv, lookup l)
+ auto set = [&rs] (const char* bv, const char* cv, lookup l)
{
if (lookup o = omitted (rs, cv).first)
l = o;
if (l)
- bs.assign (bv) = *l;
+ rs.assign (bv) = *l;
};
set ("bin.lib.prefix", "config.bin.lib.prefix", p);
@@ -364,8 +369,8 @@ namespace build2
// Load bin.config.
//
- if (!cast_false<bool> (bs["bin.config.loaded"]))
- load_module (rs, bs, "bin.config", loc, false, hints);
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, rs, "bin.config", loc, false, hints);
// Cache some config values we will be needing below.
//
@@ -391,6 +396,7 @@ namespace build2
t.insert<bmis> ();
t.insert<libu> ();
+ t.insert<libul> ();
t.insert<libue> ();
t.insert<libua> ();
t.insert<libus> ();
@@ -466,6 +472,9 @@ namespace build2
r.insert<libu> (perform_update_id, "bin.libu", fail_);
r.insert<libu> (perform_clean_id, "bin.libu", fail_);
+ r.insert<libul> (perform_update_id, "bin.libul", fail_);
+ r.insert<libul> (perform_clean_id, "bin.libul", fail_);
+
// Similar to alias.
//
@@ -493,8 +502,8 @@ namespace build2
}
bool
- ar_config_init (scope& r,
- scope& b,
+ ar_config_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool first,
@@ -502,18 +511,18 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::ar_config_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure bin.config is loaded.
//
- if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module (r, b, "bin.config", loc, false, hints);
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, bs, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool.rw (r));
+ auto& v (var_pool.rw (rs));
v.insert<process_path> ("bin.rc.path");
v.insert<process_path> ("bin.ranlib.path");
@@ -543,12 +552,12 @@ namespace build2
// Use the target to decide on the default binutils program names.
//
- const string& tsys (cast<string> (r["bin.target.system"]));
+ const string& tsys (cast<string> (rs["bin.target.system"]));
const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar");
// This can be either a pattern or a fallback search directory.
//
- const string* pat (cast_null<string> (r["bin.pattern"]));
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
// Don't save the default value to config.build so that if the user
@@ -557,7 +566,7 @@ namespace build2
//
auto ap (
config::required (
- r,
+ rs,
"config.bin.ar",
path (apply_pattern (ar_d, fb ? nullptr : pat)),
false,
@@ -565,7 +574,7 @@ namespace build2
auto rp (
config::required (
- r,
+ rs,
"config.bin.ranlib",
nullptr,
false,
@@ -585,7 +594,7 @@ namespace build2
diag_record dr (text);
{
- dr << "bin.ar " << project (r) << '@' << r.out_path () << '\n'
+ dr << "bin.ar " << project (rs) << '@' << rs.out_path () << '\n'
<< " ar " << ari.ar_path << '\n'
<< " id " << ari.ar_id << '\n'
<< " version " << ari.ar_version.string () << '\n'
@@ -614,28 +623,28 @@ namespace build2
}
}
- r.assign<process_path> ("bin.ar.path") = move (ari.ar_path);
- r.assign<string> ("bin.ar.id") = move (ari.ar_id);
- r.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
- r.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
+ rs.assign<process_path> ("bin.ar.path") = move (ari.ar_path);
+ rs.assign<string> ("bin.ar.id") = move (ari.ar_id);
+ rs.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
+ rs.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
{
semantic_version& v (ari.ar_version);
- r.assign<string> ("bin.ar.version") = v.string ();
- r.assign<uint64_t> ("bin.ar.version.major") = v.major;
- r.assign<uint64_t> ("bin.ar.version.minor") = v.minor;
- r.assign<uint64_t> ("bin.ar.version.patch") = v.patch;
- r.assign<string> ("bin.ar.version.build") = move (v.build);
+ rs.assign<string> ("bin.ar.version") = v.string ();
+ rs.assign<uint64_t> ("bin.ar.version.major") = v.major;
+ rs.assign<uint64_t> ("bin.ar.version.minor") = v.minor;
+ rs.assign<uint64_t> ("bin.ar.version.patch") = v.patch;
+ rs.assign<string> ("bin.ar.version.build") = move (v.build);
}
if (ranlib != nullptr)
{
- r.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
- r.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
- r.assign<string> ("bin.ranlib.signature") =
+ rs.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
+ rs.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
+ rs.assign<string> ("bin.ranlib.signature") =
move (ari.ranlib_signature);
- r.assign<string> ("bin.ranlib.checksum") =
+ rs.assign<string> ("bin.ranlib.checksum") =
move (ari.ranlib_checksum);
}
}
@@ -644,8 +653,8 @@ namespace build2
}
bool
- ar_init (scope& r,
- scope& b,
+ ar_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool,
@@ -653,22 +662,22 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::ar_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure the bin core and ar.config are loaded.
//
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module (r, b, "bin", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
- if (!cast_false<bool> (b["bin.ar.config.loaded"]))
- load_module (r, b, "bin.ar.config", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.ar.config.loaded"]))
+ load_module (rs, bs, "bin.ar.config", loc, false, hints);
return true;
}
bool
- ld_config_init (scope& r,
- scope& b,
+ ld_config_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool first,
@@ -676,18 +685,18 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::ld_config_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure bin.config is loaded.
//
- if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module (r, b, "bin.config", loc, false, hints);
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, rs, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool.rw (r));
+ auto& v (var_pool.rw (rs));
v.insert<process_path> ("bin.ld.path");
v.insert<path> ("config.bin.ld", true);
@@ -701,17 +710,17 @@ namespace build2
//
// Use the target to decide on the default ld name.
//
- const string& tsys (cast<string> (r["bin.target.system"]));
+ const string& tsys (cast<string> (rs["bin.target.system"]));
const char* ld_d (tsys == "win32-msvc" ? "link" : "ld");
// This can be either a pattern or a fallback search directory.
//
- const string* pat (cast_null<string> (r["bin.pattern"]));
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
auto p (
config::required (
- r,
+ rs,
"config.bin.ld",
path (apply_pattern (ld_d, fb ? nullptr : pat)),
false,
@@ -725,25 +734,25 @@ namespace build2
//
if (verb >= (p.second ? 2 : 3))
{
- text << "bin.ld " << project (r) << '@' << r.out_path () << '\n'
+ text << "bin.ld " << project (rs) << '@' << rs.out_path () << '\n'
<< " ld " << ldi.path << '\n'
<< " id " << ldi.id << '\n'
<< " signature " << ldi.signature << '\n'
<< " checksum " << ldi.checksum;
}
- r.assign<process_path> ("bin.ld.path") = move (ldi.path);
- r.assign<string> ("bin.ld.id") = move (ldi.id);
- r.assign<string> ("bin.ld.signature") = move (ldi.signature);
- r.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
+ rs.assign<process_path> ("bin.ld.path") = move (ldi.path);
+ rs.assign<string> ("bin.ld.id") = move (ldi.id);
+ rs.assign<string> ("bin.ld.signature") = move (ldi.signature);
+ rs.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
}
return true;
}
bool
- ld_init (scope& r,
- scope& b,
+ ld_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool,
@@ -751,17 +760,17 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::ld_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure the bin core and ld.config are loaded.
//
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module (r, b, "bin", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
- if (!cast_false<bool> (b["bin.ld.config.loaded"]))
- load_module (r, b, "bin.ld.config", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.ld.config.loaded"]))
+ load_module (rs, bs, "bin.ld.config", loc, false, hints);
- const string& lid (cast<string> (r["bin.ld.id"]));
+ const string& lid (cast<string> (rs["bin.ld.id"]));
// Register the pdb{} target if using the VC toolchain.
//
@@ -769,17 +778,17 @@ namespace build2
if (lid == "msvc")
{
- const target_type& pdb (b.derive_target_type<file> ("pdb").first);
- install_path (b, pdb, dir_path ("bin")); // Goes to install.bin
- install_mode (b, pdb, "644"); // But not executable.
+ const target_type& pdb (bs.derive_target_type<file> ("pdb").first);
+ install_path (bs, pdb, dir_path ("bin")); // Goes to install.bin
+ install_mode (bs, pdb, "644"); // But not executable.
}
return true;
}
bool
- rc_config_init (scope& r,
- scope& b,
+ rc_config_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool first,
@@ -787,18 +796,18 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::rc_config_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure bin.config is loaded.
//
- if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module (r, b, "bin.config", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.config.loaded"]))
+ load_module (rs, bs, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool.rw (r));
+ auto& v (var_pool.rw (rs));
v.insert<process_path> ("bin.rc.path");
v.insert<path> ("config.bin.rc", true);
@@ -812,17 +821,17 @@ namespace build2
//
// Use the target to decide on the default rc name.
//
- const string& tsys (cast<string> (r["bin.target.system"]));
+ const string& tsys (cast<string> (rs["bin.target.system"]));
const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres");
// This can be either a pattern or a fallback search directory.
//
- const string* pat (cast_null<string> (r["bin.pattern"]));
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
auto p (
config::required (
- r,
+ rs,
"config.bin.rc",
path (apply_pattern (rc_d, fb ? nullptr : pat)),
false,
@@ -836,25 +845,25 @@ namespace build2
//
if (verb >= (p.second ? 2 : 3))
{
- text << "bin.rc " << project (r) << '@' << r.out_path () << '\n'
+ text << "bin.rc " << project (rs) << '@' << rs.out_path () << '\n'
<< " rc " << rci.path << '\n'
<< " id " << rci.id << '\n'
<< " signature " << rci.signature << '\n'
<< " checksum " << rci.checksum;
}
- r.assign<process_path> ("bin.rc.path") = move (rci.path);
- r.assign<string> ("bin.rc.id") = move (rci.id);
- r.assign<string> ("bin.rc.signature") = move (rci.signature);
- r.assign<string> ("bin.rc.checksum") = move (rci.checksum);
+ rs.assign<process_path> ("bin.rc.path") = move (rci.path);
+ rs.assign<string> ("bin.rc.id") = move (rci.id);
+ rs.assign<string> ("bin.rc.signature") = move (rci.signature);
+ rs.assign<string> ("bin.rc.checksum") = move (rci.checksum);
}
return true;
}
bool
- rc_init (scope& r,
- scope& b,
+ rc_init (scope& rs,
+ scope& bs,
const location& loc,
unique_ptr<module_base>&,
bool,
@@ -862,15 +871,15 @@ namespace build2
const variable_map& hints)
{
tracer trace ("bin::rc_init");
- l5 ([&]{trace << "for " << b.out_path ();});
+ l5 ([&]{trace << "for " << bs.out_path ();});
// Make sure the bin core and rc.config are loaded.
//
- if (!cast_false<bool> (b["bin.loaded"]))
- load_module (r, b, "bin", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
- if (!cast_false<bool> (b["bin.rc.config.loaded"]))
- load_module (r, b, "bin.rc.config", loc, false, hints);
+ if (!cast_false<bool> (bs["bin.rc.config.loaded"]))
+ load_module (rs, bs, "bin.rc.config", loc, false, hints);
return true;
}
diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx
index 9427697..b609c9a 100644
--- a/build2/bin/rule.cxx
+++ b/build2/bin/rule.cxx
@@ -37,15 +37,10 @@ namespace build2
// The whole logic is pretty much as if we had our two group members as
// our prerequisites.
//
- bool lib_rule::
- match (action, target& xt, const string&) const
+ lib_rule::members lib_rule::
+ build_members (const scope& rs)
{
- lib& t (xt.as<lib> ());
-
- // Get the library type to build. If not set for a target, this should
- // be configured at the project scope by init().
- //
- const string& type (cast<string> (t["bin.lib"]));
+ const string& type (cast<string> (rs["bin.lib"]));
bool a (type == "static" || type == "both");
bool s (type == "shared" || type == "both");
@@ -54,8 +49,17 @@ namespace build2
fail << "unknown library type: " << type <<
info << "'static', 'shared', or 'both' expected";
- t.a = a ? &search<liba> (t, t.dir, t.out, t.name) : nullptr;
- t.s = s ? &search<libs> (t, t.dir, t.out, t.name) : nullptr;
+ return members {a, s};
+ }
+
+ bool lib_rule::
+ match (action, target& xt, const string&) const
+ {
+ lib& t (xt.as<lib> ());
+
+ members bm (build_members (t.root_scope ()));
+ t.a = bm.a ? &search<liba> (t, t.dir, t.out, t.name) : nullptr;
+ t.s = bm.s ? &search<libs> (t, t.dir, t.out, t.name) : nullptr;
return true;
}
diff --git a/build2/bin/rule.hxx b/build2/bin/rule.hxx
index d656f62..ab8d64b 100644
--- a/build2/bin/rule.hxx
+++ b/build2/bin/rule.hxx
@@ -44,6 +44,18 @@ namespace build2
static target_state
perform (action, const target&);
+
+ // Return library types to build according to the bin.lib value (set
+ // on project's root scope by init()).
+ //
+ struct members
+ {
+ bool a; // static
+ bool s; // shared
+ };
+
+ static members
+ build_members (const scope&);
};
}
}
diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx
index 0560b8e..6c62258 100644
--- a/build2/bin/target.cxx
+++ b/build2/bin/target.cxx
@@ -71,7 +71,7 @@ namespace build2
// running serial. For the members it is also safe to set the group during
// creation.
- // obj*{}, bmi*{}, libu*{} member factory.
+ // obj*{} and bmi*{} member factory.
//
template <typename M, typename G>
static target*
@@ -111,11 +111,11 @@ namespace build2
false
};
- const target_type libue::static_type
+ const target_type obja::static_type
{
- "libue",
- &libux::static_type,
- &m_factory<libue, libu>,
+ "obja",
+ &objx::static_type,
+ &m_factory<obja, obj>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -124,11 +124,11 @@ namespace build2
false
};
- const target_type obja::static_type
+ const target_type bmia::static_type
{
- "obja",
- &objx::static_type,
- &m_factory<obja, obj>,
+ "bmia",
+ &bmix::static_type,
+ &m_factory<bmia, bmi>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -137,11 +137,11 @@ namespace build2
false
};
- const target_type bmia::static_type
+ const target_type objs::static_type
{
- "bmia",
- &bmix::static_type,
- &m_factory<bmia, bmi>,
+ "objs",
+ &objx::static_type,
+ &m_factory<objs, obj>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -150,11 +150,11 @@ namespace build2
false
};
- const target_type libua::static_type
+ const target_type bmis::static_type
{
- "libua",
- &libux::static_type,
- &m_factory<libua, libu>,
+ "bmis",
+ &bmix::static_type,
+ &m_factory<bmis, bmi>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -163,11 +163,33 @@ namespace build2
false
};
- const target_type objs::static_type
+ // libu*{} member factory.
+ //
+ template <typename M>
+ static target*
+ libux_factory (const target_type&, dir_path dir, dir_path out, string n)
{
- "objs",
- &objx::static_type,
- &m_factory<objs, obj>,
+ const target* g (targets.find<libu> (dir, out, n));
+
+ if (const target* g2 = targets.find<libul> (dir, out, n))
+ {
+ if (g != 0)
+ fail << "both " << *g << " and " << g2 << " targets declared";
+
+ g = g2;
+ }
+
+ M* m (new M (move (dir), move (out), move (n)));
+ m->group = g;
+
+ return m;
+ }
+
+ const target_type libue::static_type
+ {
+ "libue",
+ &libux::static_type,
+ &libux_factory<libue>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -176,11 +198,12 @@ namespace build2
false
};
- const target_type bmis::static_type
+
+ const target_type libua::static_type
{
- "bmis",
- &bmix::static_type,
- &m_factory<bmis, bmi>,
+ "libua",
+ &libux::static_type,
+ &libux_factory<libua>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -193,7 +216,7 @@ namespace build2
{
"libus",
&libux::static_type,
- &m_factory<libus, libu>,
+ &libux_factory<libus>,
nullptr, /* fixed_extension */
&target_extension_var<var_extension, nullptr>,
&target_pattern_var<var_extension, nullptr>,
@@ -255,6 +278,39 @@ namespace build2
false
};
+ // The same as g_factory() but without E.
+ //
+ static target*
+ libul_factory (const target_type&, dir_path dir, dir_path out, string n)
+ {
+ libua* a (phase == run_phase::load
+ ? const_cast<libua*> (targets.find<libua> (dir, out, n))
+ : nullptr);
+ libus* s (phase == run_phase::load
+ ? const_cast<libus*> (targets.find<libus> (dir, out, n))
+ : nullptr);
+
+ libul* g (new libul (move (dir), move (out), move (n)));
+
+ if (a != nullptr) a->group = g;
+ if (s != nullptr) s->group = g;
+
+ return g;
+ }
+
+ const target_type libul::static_type
+ {
+ "libul",
+ &libx::static_type,
+ &libul_factory,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
const target_type libu::static_type
{
"libu",
diff --git a/build2/bin/target.hxx b/build2/bin/target.hxx
index f0b427f..0bcdf90 100644
--- a/build2/bin/target.hxx
+++ b/build2/bin/target.hxx
@@ -135,7 +135,7 @@ namespace build2
virtual const target_type& dynamic_type () const {return static_type;}
};
- // Common base for lib{} and libu{} groups.
+ // Common base for lib{} and libul{}/libu{} groups.
//
// We use mtime_target as a base for the "trust me it exists" functionality
// which we use, for example, to have installed lib{} prerequisites that
@@ -150,13 +150,21 @@ namespace build2
static const target_type static_type;
};
- // The libu{} target group (utility library).
+ // The libul{}/libu{} target groups (utility library).
//
- // All the members are static libraries that differ base on the kind of
+ // All the members are static libraries that differ based on the kind of
// object files they contains. Note that the group is more like obj{}
// rather than lib{} in that one does not build the group directly rather
// picking a suitable member.
//
+ // libul{} is a "library utility library" in that the choice of members is
+ // libua{} or libus{}, even when linking an executable (normally a unit
+ // test).
+ //
+ // libu{} is a general utility library with all three types of members. It
+ // would normally be used when you want to build both a library from
+ // libua{}/libus{} and an executable from libue{}.
+ //
class libux: public file // Common base of all libuX{} static libraries.
{
public:
@@ -196,6 +204,16 @@ namespace build2
virtual const target_type& dynamic_type () const {return static_type;}
};
+ class libul: public libx
+ {
+ public:
+ using libx::libx;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
class libu: public libx
{
public:
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index c6ef3bd..23c37ff 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -41,9 +41,14 @@ namespace build2
//
// 1. options
// 2. lib itself (if self is true)
- // 3. dependency libs (prerequisite_targets)
+ // 3. dependency libs (prerequisite_targets, left to right, depth-first)
// 4. dependency libs (*.libs variables).
//
+ // The first argument to proc_lib is a pointer to the last element of an
+ // array that contains the current library dependency chain all the way to
+ // the library passes to process_libraries(). The first element of this
+ // array is NULL.
+ //
void common::
process_libraries (
action a,
@@ -55,7 +60,7 @@ namespace build2
lflags lf,
const function<bool (const file&,
bool la)>& proc_impl, // Implementation?
- const function<void (const file*, // Can be NULL.
+ const function<void (const file* const*, // Can be NULL.
const string& path, // Library path.
lflags, // Link flags.
bool sys)>& proc_lib, // True if system library.
@@ -63,8 +68,16 @@ namespace build2
const string& type, // cc.type
bool com, // cc. or x.
bool exp)>& proc_opt, // *.export.
- bool self /*= false*/) const // Call proc_lib on l?
+ bool self /*= false*/, // Call proc_lib on l?
+ small_vector<const file*, 16>* chain) const
{
+ small_vector<const file*, 16> chain_storage;
+ if (chain == nullptr)
+ {
+ chain = &chain_storage;
+ chain->push_back (nullptr);
+ }
+
// See what type of library this is (C, C++, etc). Use it do decide
// which x.libs variable name to use. If it's unknown, then we only
// look into prerequisites.
@@ -177,6 +190,8 @@ namespace build2
//
if (self && proc_lib)
{
+ chain->push_back (&l);
+
// Note that while normally the path is assigned, in case of an import
// stub the path to the DLL may not be known and so the path will be
// empty (but proc_lib() will use the import stub).
@@ -187,7 +202,7 @@ namespace build2
? cast_false<bool> (l.vars[c_system])
: !p.empty () && sys (top_sysd, p.string ()));
- proc_lib (&l, p.string (), lf, s);
+ proc_lib (&chain->back (), p.string (), lf, s);
}
const scope& bs (t == nullptr || cc ? top_bs : l.base_scope ());
@@ -242,7 +257,7 @@ namespace build2
process_libraries (a, bs, *li, *sysd,
*f, la, pt.data,
- proc_impl, proc_lib, proc_opt, true);
+ proc_impl, proc_lib, proc_opt, true, chain);
}
}
}
@@ -251,148 +266,154 @@ namespace build2
// handling import, etc.
//
// If it is not a C-common library, then it probably doesn't have any of
- // the *.libs and we are done.
- //
- if (t == nullptr)
- return;
-
- optional<dir_paths> usrd; // Extract lazily.
-
- // Determine if a "simple path" is a system library.
+ // the *.libs.
//
- auto sys_simple = [&sysd, &sys, &find_sysd] (const string& p) -> bool
+ if (t != nullptr)
{
- bool s (!path::traits::absolute (p));
+ optional<dir_paths> usrd; // Extract lazily.
- if (!s)
+ // Determine if a "simple path" is a system library.
+ //
+ auto sys_simple = [&sysd, &sys, &find_sysd] (const string& p) -> bool
{
- if (sysd == nullptr) find_sysd ();
+ bool s (!path::traits::absolute (p));
- s = sys (*sysd, p);
- }
+ if (!s)
+ {
+ if (sysd == nullptr) find_sysd ();
- return s;
- };
+ s = sys (*sysd, p);
+ }
- auto proc_int = [&l,
- &proc_impl, &proc_lib, &proc_opt,
- &sysd, &usrd,
- &find_sysd, &find_linfo, &sys_simple,
- &bs, a, &li, this] (const lookup& lu)
- {
- const vector<name>* ns (cast_null<vector<name>> (lu));
- if (ns == nullptr || ns->empty ())
- return;
+ return s;
+ };
- for (const name& n: *ns)
+ auto proc_int = [&l,
+ &proc_impl, &proc_lib, &proc_opt, chain,
+ &sysd, &usrd,
+ &find_sysd, &find_linfo, &sys_simple,
+ &bs, a, &li, this] (const lookup& lu)
{
- if (n.simple ())
- {
- // This is something like -lpthread or shell32.lib so should be a
- // valid path. But it can also be an absolute library path (e.g.,
- // something that in the future will come from our -static/-shared
- // .pc files.
- //
- if (proc_lib)
- proc_lib (nullptr, n.value, 0, sys_simple (n.value));
- }
- else
+ const vector<name>* ns (cast_null<vector<name>> (lu));
+ if (ns == nullptr || ns->empty ())
+ return;
+
+ for (const name& n: *ns)
{
- // This is a potentially project-qualified target.
- //
- if (sysd == nullptr) find_sysd ();
- if (!li) find_linfo ();
+ if (n.simple ())
+ {
+ // This is something like -lpthread or shell32.lib so should be
+ // a valid path. But it can also be an absolute library path
+ // (e.g., something that in the future will come from our
+ // -static/-shared .pc files.
+ //
+ if (proc_lib)
+ proc_lib (nullptr, n.value, 0, sys_simple (n.value));
+ }
+ else
+ {
+ // This is a potentially project-qualified target.
+ //
+ if (sysd == nullptr) find_sysd ();
+ if (!li) find_linfo ();
- const file& t (resolve_library (a, bs, n, *li, *sysd, usrd));
+ const file& t (resolve_library (a, bs, n, *li, *sysd, usrd));
- if (proc_lib)
- {
- // This can happen if the target is mentioned in *.export.libs
- // (i.e., it is an interface dependency) but not in the
- // library's prerequisites (i.e., it is not an implementation
- // dependency).
+ if (proc_lib)
+ {
+ // This can happen if the target is mentioned in *.export.libs
+ // (i.e., it is an interface dependency) but not in the
+ // library's prerequisites (i.e., it is not an implementation
+ // dependency).
+ //
+ // Note that we used to just check for path being assigned but
+ // on Windows import-installed DLLs may legally have empty
+ // paths.
+ //
+ if (t.mtime () == timestamp_unknown)
+ fail << "interface dependency " << t << " is out of date" <<
+ info << "mentioned in *.export.libs of target " << l <<
+ info << "is it a prerequisite of " << l << "?";
+ }
+
+ // Process it recursively.
//
- // Note that we used to just check for path being assigned but
- // on Windows import-installed DLLs may legally have empty
- // paths.
+ // @@ Where can we get the link flags? Should we try to find
+ // them in the library's prerequisites? What about installed
+ // stuff?
//
- if (t.mtime () == timestamp_unknown)
- fail << "interface dependency " << t << " is out of date" <<
- info << "mentioned in *.export.libs of target " << l <<
- info << "is it a prerequisite of " << l << "?";
+ process_libraries (a, bs, *li, *sysd,
+ t, t.is_a<liba> () || t.is_a<libux> (), 0,
+ proc_impl, proc_lib, proc_opt, true, chain);
}
+ }
+ };
- // Process it recursively.
- //
- // @@ Where can we get the link flags? Should we try to find them
- // in the library's prerequisites? What about installed stuff?
+ // Process libraries from *.libs (of type strings).
+ //
+ auto proc_imp = [&proc_lib, &sys_simple] (const lookup& lu)
+ {
+ const strings* ns (cast_null<strings> (lu));
+ if (ns == nullptr || ns->empty ())
+ return;
+
+ for (const string& n: *ns)
+ {
+ // This is something like -lpthread or shell32.lib so should be a
+ // valid path.
//
- process_libraries (a, bs, *li, *sysd,
- t, t.is_a<liba> () || t.is_a<libux> (), 0,
- proc_impl, proc_lib, proc_opt, true);
+ proc_lib (nullptr, n, 0, sys_simple (n));
}
- }
- };
-
- // Process libraries from *.libs (of type strings).
- //
- auto proc_imp = [&proc_lib, &sys_simple] (const lookup& lu)
- {
- const strings* ns (cast_null<strings> (lu));
- if (ns == nullptr || ns->empty ())
- return;
+ };
- for (const string& n: *ns)
+ // Note: the same structure as when processing options above.
+ //
+ // If all we know is it's a C-common library, then in both cases we
+ // only look for cc.export.libs.
+ //
+ if (cc)
{
- // This is something like -lpthread or shell32.lib so should be a
- // valid path.
- //
- proc_lib (nullptr, n, 0, sys_simple (n));
+ if (c_e_libs) proc_int (c_e_libs);
}
- };
-
- // Note: the same structure as when processing options above.
- //
- // If all we know is it's a C-common library, then in both cases we only
- // look for cc.export.libs.
- //
- if (cc)
- {
- if (c_e_libs) proc_int (c_e_libs);
- }
- else
- {
- if (impl)
+ else
{
- // Interface and implementation: as discussed above, we can have two
- // situations: overriden export or default export.
- //
- if (c_e_libs.defined () || x_e_libs.defined ())
+ if (impl)
{
- if (c_e_libs) proc_int (c_e_libs);
- if (x_e_libs) proc_int (x_e_libs);
+ // Interface and implementation: as discussed above, we can have
+ // two situations: overriden export or default export.
+ //
+ if (c_e_libs.defined () || x_e_libs.defined ())
+ {
+ if (c_e_libs) proc_int (c_e_libs);
+ if (x_e_libs) proc_int (x_e_libs);
+ }
+ else
+ {
+ // For default export we use the same options/libs as were used
+ // to build the library. Since libraries in (non-export) *.libs
+ // are not targets, we don't need to recurse.
+ //
+ if (proc_lib)
+ {
+ proc_imp (l[c_libs]);
+ proc_imp (l[same ? x_libs : vp[*t + ".libs"]]);
+ }
+ }
}
else
{
- // For default export we use the same options/libs as were used to
- // build the library. Since libraries in (non-export) *.libs are
- // not targets, we don't need to recurse.
+ // Interface: only add *.export.* (interface dependencies).
//
- if (proc_lib)
- {
- proc_imp (l[c_libs]);
- proc_imp (l[same ? x_libs : vp[*t + ".libs"]]);
- }
+ if (c_e_libs) proc_int (c_e_libs);
+ if (x_e_libs) proc_int (x_e_libs);
}
}
- else
- {
- // Interface: only add *.export.* (interface dependencies).
- //
- if (c_e_libs) proc_int (c_e_libs);
- if (x_e_libs) proc_int (x_e_libs);
- }
}
+
+ // Remove this library from the chain.
+ //
+ if (self && proc_lib)
+ chain->pop_back ();
}
// The name can be an absolute target name (e.g., /tmp/libfoo/lib{foo}) or
@@ -456,7 +477,7 @@ namespace build2
fail << "unable to find library " << pk;
}
- // If this is lib{}/libu{}, pick appropriate member.
+ // If this is lib{}/libu*{}, pick appropriate member.
//
if (const libx* l = xt->is_a<libx> ())
xt = &link_member (*l, a, li); // Pick lib*{e,a,s}{}.
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 18824cb..d809050 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -220,9 +220,10 @@ namespace build2
bool,
lflags,
const function<bool (const file&, bool)>&,
- const function<void (const file*, const string&, lflags, bool)>&,
+ const function<void (const file* const*, const string&, lflags, bool)>&,
const function<void (const file&, const string&, bool, bool)>&,
- bool = false) const;
+ bool = false,
+ small_vector<const file*, 16>* = nullptr) const;
const target*
search_library (action a,
diff --git a/build2/cc/install-rule.cxx b/build2/cc/install-rule.cxx
index ee290e3..9e6b93f 100644
--- a/build2/cc/install-rule.cxx
+++ b/build2/cc/install-rule.cxx
@@ -53,8 +53,8 @@ namespace build2
{
const target* pt (&search (t, p));
- // If this is the lib{}/libu{} group, pick a member which we would
- // link. For libu{} we want the "see through" logic.
+ // If this is the lib{}/libu*{} group, pick a member which we would
+ // link. For libu*{} we want the "see through" logic.
//
if (const libx* l = pt->is_a<libx> ())
pt = &link_member (*l, a, link_info (t.base_scope (), ot));
diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx
index 0bfef09..5ebb97a 100644
--- a/build2/cc/link-rule.cxx
+++ b/build2/cc/link-rule.cxx
@@ -130,14 +130,19 @@ namespace build2
}
if (!(seen_x || seen_c || seen_obj || seen_lib))
+ {
+ l4 ([&]{trace << "no " << x_lang << ", C, or obj/lib prerequisite "
+ << "for target " << t;});
return false;
+ }
// We will only chain a C source if there is also an X source or we were
// explicitly told to.
//
if (seen_c && !seen_x && hint < x)
{
- l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint";});
+ l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint "
+ << "for target " << t;});
return false;
}
@@ -1070,47 +1075,126 @@ namespace build2
const file& l, bool la, lflags lf,
const scope& bs, action a, linfo li) const
{
- // Note: lack of the "small function object" optimization will really
- // kill us here since we are called in a loop.
- //
+ struct data
+ {
+ strings& args;
+ const file& l;
+ action a;
+ linfo li;
+ } d {args, l, a, li};
+
auto imp = [] (const file&, bool la) {return la;};
- auto lib = [&args, this] (const file* l, const string& p, lflags f, bool)
+ auto lib = [&d, this] (const file* const* lc,
+ const string& p,
+ lflags f,
+ bool)
{
- if (l != nullptr)
+ const file* l (lc != nullptr ? *lc : nullptr);
+
+ if (l == nullptr)
{
- // On Windows a shared library is a DLL with the import library as a
- // first ad hoc group member. MinGW though can link directly to DLLs
- // (see search_library() for details).
- //
- if (l->member != nullptr && l->is_a<libs> () && tclass == "windows")
- l = &l->member->as<file> ();
+ d.args.push_back (p);
+ }
+ else
+ {
+ bool lu (l->is_a<libux> ());
- string p (relative (l->path ()).string ());
+ // The utility/non-utility case is tricky. Consider these two
+ // scenarios:
+ //
+ // exe -> (libu1-e -> libu1-e) -> (liba) -> libu-a -> (liba1)
+ // exe -> (liba) -> libu1-a -> libu1-a -> (liba1) -> libu-a1
+ //
+ // Libraries that should be linked are in '()'. That is, we need to
+ // link the initial sequence of utility libraries and then, after
+ // encountering a first non-utility, only link non-utilities
+ // (because they already contain their utility's object files).
+ //
+ if (lu)
+ {
+ for (ptrdiff_t i (-1); lc[i] != nullptr; --i)
+ if (!lc[i]->is_a<libux> ())
+ return;
+ }
- if (f & lflag_whole)
+ if (d.li.type == otype::a)
{
- if (tsys == "win32-msvc")
- {
- p.insert (0, "/WHOLEARCHIVE:"); // Only available from VC14U2.
- }
- else if (tsys == "darwin")
+ // Linking a utility library to a static library.
+ //
+ // Note that utility library prerequisites of utility libraries
+ // are automatically handled by process_libraries(). So all we
+ // have to do is implement the "thin archive" logic.
+ //
+ // We may also end up trying to link a non-utility library to a
+ // static library via a utility library (direct linking is taken
+ // care of by perform_update()). So we cut it off here.
+ //
+ if (!lu)
+ return;
+
+ for (const target* pt: l->prerequisite_targets[d.a])
{
- p.insert (0, "-Wl,-force_load,");
+ if (pt == nullptr)
+ continue;
+
+ if (modules)
+ {
+ if (pt->is_a<bmix> ())
+ pt = pt->member;
+ }
+
+ // We could have dependency diamonds with utility libraries.
+ // Repeats will be handled by the linker (in fact, it could be
+ // required to repeat them to satisfy all the symbols) but here
+ // we have to suppress duplicates ourselves.
+ //
+ if (const file* f = pt->is_a<objx> ())
+ {
+ string p (relative (f->path ()).string ());
+ if (find (d.args.begin (), d.args.end (), p) == d.args.end ())
+ d.args.push_back (move (p));
+ }
}
- else
+ }
+ else
+ {
+ // Linking a library to a shared library or executable.
+ //
+
+ // On Windows a shared library is a DLL with the import library as
+ // a first ad hoc group member. MinGW though can link directly to
+ // DLLs (see search_library() for details).
+ //
+ if (l->member != nullptr &&
+ l->is_a<libs> () &&
+ tclass == "windows")
+ l = &l->member->as<file> ();
+
+ string p (relative (l->path ()).string ());
+
+ if (f & lflag_whole)
{
- args.push_back ("-Wl,--whole-archive");
- args.push_back (move (p));
- args.push_back ("-Wl,--no-whole-archive");
- return;
+ if (tsys == "win32-msvc")
+ {
+ p.insert (0, "/WHOLEARCHIVE:"); // Only available from VC14U2.
+ }
+ else if (tsys == "darwin")
+ {
+ p.insert (0, "-Wl,-force_load,");
+ }
+ else
+ {
+ d.args.push_back ("-Wl,--whole-archive");
+ d.args.push_back (move (p));
+ d.args.push_back ("-Wl,--no-whole-archive");
+ return;
+ }
}
- }
- args.push_back (move (p));
+ d.args.push_back (move (p));
+ }
}
- else
- args.push_back (p);
};
auto opt = [&args, this] (
@@ -1149,12 +1233,43 @@ namespace build2
const dir_path& out_root;
bool& update;
timestamp mt;
- } d {cs, bs.root_scope ()->out_path (), update, mt};
+ linfo li;
+ } d {cs, bs.root_scope ()->out_path (), update, mt, li};
- auto lib = [&d, this] (const file* l, const string& p, lflags f, bool)
+ auto lib = [&d, this] (const file* const* lc,
+ const string& p,
+ lflags f,
+ bool)
{
- if (l != nullptr)
+ const file* l (lc != nullptr ? *lc : nullptr);
+
+ if (l == nullptr)
{
+ d.cs.append (p);
+ }
+ else
+ {
+ bool lu (l->is_a<libux> ());
+
+ if (lu)
+ {
+ for (ptrdiff_t i (-1); lc[i] != nullptr; --i)
+ if (!lc[i]->is_a<libux> ())
+ return;
+ }
+
+ // We also don't need to do anything special for linking a utility
+ // library to a static library. If any of its object files (or the
+ // set of its object files) changes, then the library will have to
+ // be updated as well. In other words, we use the library timestamp
+ // as a proxy for all of its member's timestamps.
+ //
+ // We do need to cut of the static to static linking, just as in
+ // append_libraries().
+ //
+ if (d.li.type == otype::a && !lu)
+ return;
+
// Check if this library renders us out of date.
//
d.update = d.update || l->newer (d.mt);
@@ -1169,8 +1284,6 @@ namespace build2
d.cs.append (f);
hash_path (d.cs, l->path (), d.out_root);
}
- else
- d.cs.append (p);
};
auto opt = [&cs, this] (
@@ -1238,8 +1351,13 @@ namespace build2
bool for_install;
} d {args, for_install};
- auto lib = [&d, this] (const file* l, const string& f, lflags, bool sys)
+ auto lib = [&d, this] (const file* const* lc,
+ const string& f,
+ lflags,
+ bool sys)
{
+ const file* l (lc != nullptr ? *lc : nullptr);
+
// We don't rpath system libraries. Why, you may ask? There are many
// good reasons and I have them written on a napkin somewhere...
//
@@ -1293,7 +1411,8 @@ namespace build2
// In case we don't have the "small function object" optimization.
//
const function<bool (const file&, bool)> impf (imp);
- const function<void (const file*, const string&, lflags, bool)> libf (lib);
+ const function<
+ void (const file* const*, const string&, lflags, bool)> libf (lib);
for (const prerequisite_target& pt: t.prerequisite_targets[a])
{
@@ -1544,7 +1663,10 @@ namespace build2
if (lt.static_library ())
{
- if (tsys == "win32-msvc") ;
+ if (tsys == "win32-msvc")
+ {
+ // No options for lib.exe.
+ }
else
{
// If the user asked for ranlib, don't try to do its function with
@@ -1700,7 +1822,7 @@ namespace build2
// pinpoint exactly what is causing the update. On the other hand, the
// checksum is faster and simpler. And we like simple.
//
- const file* def (nullptr); // Cache if present.
+ const file* def (nullptr); // Cached if present.
{
sha256 cs;
@@ -1722,10 +1844,16 @@ namespace build2
const file* f;
bool la (false), ls (false);
- if ((f = pt->is_a<objx> ()) ||
- (!lt.static_library () && // @@ UTL: TODO libua to liba link.
+ // We link utility libraries to everything except other utility
+ // libraries. In case of linking to liba{} we follow the "thin
+ // archive" lead and "see through" to their object file
+ // prerequisites (recursively, until we encounter a non-utility).
+ //
+ if ((f = pt->is_a<objx> ()) ||
+ (!lt.utility &&
+ (la = (f = pt->is_a<libux> ()))) ||
+ (!lt.static_library () &&
((la = (f = pt->is_a<liba> ())) ||
- (la = (f = pt->is_a<libux> ())) ||
(ls = (f = pt->is_a<libs> ())))))
{
// Link all the dependent interface libraries (shared) or interface
@@ -1774,7 +1902,7 @@ namespace build2
if (!manifest.empty ())
hash_path (cs, manifest, rs.out_path ());
- // Treat .libs as inputs, not options.
+ // Treat *.libs variable values as inputs, not options.
//
if (!lt.static_library ())
{
@@ -2020,6 +2148,8 @@ namespace build2
// The same logic as during hashing above.
//
+ // See also a similar loop inside append_libraries().
+ //
for (const prerequisite_target& p: t.prerequisite_targets[a])
{
const target* pt (p.target);
@@ -2036,15 +2166,13 @@ namespace build2
const file* f;
bool la (false), ls (false);
- if ((f = pt->is_a<objx> ()) ||
- (!lt.static_library () && // @@ UTL: TODO libua to liba link.
+ if ((f = pt->is_a<objx> ()) ||
+ (!lt.utility &&
+ (la = (f = pt->is_a<libux> ()))) ||
+ (!lt.static_library () &&
((la = (f = pt->is_a<liba> ())) ||
- (la = (f = pt->is_a<libux> ())) ||
(ls = (f = pt->is_a<libs> ())))))
{
- // Link all the dependent interface libraries (shared) or interface
- // and implementation (static), recursively.
- //
if (la || ls)
append_libraries (sargs, *f, la, p.data, bs, a, li);
else
@@ -2199,6 +2327,27 @@ namespace build2
throw failed ();
}
+ // VC link.exe creates an import library and .exp file for an executable
+ // if any of its object files export any symbols (think a unit test
+ // linking libus{}). And, no, there is no way to suppress it. Well,
+ // there is a way: create a .def file with an empty EXPORTS section,
+ // pass it to lib.exe to create a dummy .exp (and .lib), and then pass
+ // this empty .exp to link.exe. Wanna go this way? Didn't think so.
+ // Having no way to disable this, the next simplest thing seems to be
+ // just cleaning the mess up.
+ //
+ // Note also that if at some point we decide to support such "shared
+ // executables" (-rdynamic, etc), then it will probably have to be a
+ // different target type (exes{}?) since it will need a different set
+ // of object files (-fPIC so probably objs{}), etc.
+ //
+ if (lt.executable () && tsys == "win32-msvc")
+ {
+ path b (relt.base ());
+ try_rmfile (b + ".lib", true /* ignore_errors */);
+ try_rmfile (b + ".exp", true /* ignore_errors */);
+ }
+
if (ranlib)
{
const process_path& rl (cast<process_path> (ranlib));
diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx
index 4346872..9eb3de0 100644
--- a/build2/cc/msvc.cxx
+++ b/build2/cc/msvc.cxx
@@ -65,12 +65,22 @@ namespace build2
{
// " Creating library foo\foo.dll.lib and object foo\foo.dll.exp"
//
- if (lt == otype::s && l.compare (0, 3, " ") == 0)
+ // This can also appear when linking executables if any of the object
+ // files export any symbols.
+ //
+ if (l.compare (0, 3, " ") == 0)
{
- path imp (t.member->as<file> ().path ().leaf ());
+ // Use the actual import library name if this is a library (since we
+ // override this name) and the executable name otherwise (by default
+ // .lib/.exp are named by replacing the .exe extension).
+ //
+ path i (
+ lt == otype::s
+ ? t.member->as<file> ().path ().leaf ()
+ : t.path ().leaf ().base () + ".lib");
- if (l.find (imp.string ()) != string::npos &&
- l.find (imp.base ().string () + ".exp") != string::npos)
+ if (l.find (i.string ()) != string::npos &&
+ l.find (i.base ().string () + ".exp") != string::npos)
continue;
}
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index 55c8b35..d331f7e 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -1265,11 +1265,13 @@ namespace build2
bool priv (false);
auto imp = [&priv] (const file&, bool la) {return priv && la;};
- auto lib = [&os, &save_library] (const file* l,
+ auto lib = [&os, &save_library] (const file* const* c,
const string& p,
lflags,
bool)
{
+ const file* l (c != nullptr ? *c : nullptr);
+
if (l != nullptr)
{
if (l->is_a<libs> () || l->is_a<liba> ()) // See through libux.
diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx
index 830a957..080578c 100644
--- a/build2/cc/utility.cxx
+++ b/build2/cc/utility.cxx
@@ -8,6 +8,7 @@
#include <build2/variable.hxx>
#include <build2/algorithm.hxx> // search()
+#include <build2/bin/rule.hxx>
#include <build2/bin/target.hxx>
using namespace std;
@@ -45,17 +46,35 @@ namespace build2
const target&
link_member (const bin::libx& x, action a, linfo li)
{
- if (const libu* u = x.is_a<libu> ())
+ bool ul;
+
+ if ((ul = x.is_a<libul> ()) || x.is_a<libu> ())
{
- const target_type& tt (li.type == otype::e ? libue::static_type :
- li.type == otype::a ? libua::static_type :
- libus::static_type);
+ // If this is a libul{} and we are linking an executable, then the
+ // member choice should be dictated by the members of lib{} this
+ // libul{} is "primarily" for. If both are being built, then it seems
+ // natural to prefer static over shared since it could be faster (but
+ // I am sure someone will probably want this configurable).
+ //
+ if (ul && li.type == otype::e)
+ {
+ // Utility libraries are project-local which means the primarily
+ // target should be in the same project as us.
+ //
+ li.type = lib_rule::build_members (x.root_scope ()).a
+ ? otype::a
+ : otype::s;
+ }
+
+ const target_type& tt (li.type == otype::a ? libua::static_type :
+ li.type == otype::s ? libus::static_type :
+ libue::static_type);
// Called by the compile rule during execute.
//
return phase == run_phase::match
- ? search (*u, tt, u->dir, u->out, u->name)
- : *search_existing (tt, u->dir, u->out, u->name);
+ ? search (x, tt, x.dir, x.out, x.name)
+ : *search_existing (tt, x.dir, x.out, x.name);
}
else
{
diff --git a/build2/cc/utility.hxx b/build2/cc/utility.hxx
index 18de360..c378e74 100644
--- a/build2/cc/utility.hxx
+++ b/build2/cc/utility.hxx
@@ -54,7 +54,7 @@ namespace build2
}
// Given the link order return the library member to link. That is, liba{}
- // or libs{} for lib{} and libue{}, libua{} or libus{} for libu{}.
+ // or libs{} for lib{} and libue{}, libua{} or libus{} for libu*{}.
//
const target&
link_member (const bin::libx&, action, linfo);
diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx
index c710e66..268bab2 100644
--- a/build2/cc/windows-rpath.cxx
+++ b/build2/cc/windows-rpath.cxx
@@ -59,8 +59,13 @@ namespace build2
//
auto imp = [] (const file&, bool) {return true;};
- auto lib = [&r] (const file* l, const string& f, lflags, bool sys)
+ auto lib = [&r] (const file* const* lc,
+ const string& f,
+ lflags,
+ bool sys)
{
+ const file* l (lc != nullptr ? *lc : nullptr);
+
// We don't rpath system libraries.
//
if (sys)
@@ -135,8 +140,13 @@ namespace build2
auto imp = [] (const file&, bool) {return true;};
- auto lib = [&r, &bs] (const file* l, const string& f, lflags, bool sys)
+ auto lib = [&r, &bs] (const file* const* lc,
+ const string& f,
+ lflags,
+ bool sys)
{
+ const file* l (lc != nullptr ? *lc : nullptr);
+
if (sys)
return;