aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-07-28 13:46:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-07-28 13:46:26 +0200
commit1c7cbb302b1c6e41eb0c5cecfc655532f1919cba (patch)
tree84375e8be2bfe00b2387f02cab8dcca396019299
parent24402ed431c1780914576f72350f8796308cb59b (diff)
Implement support for linking whole archive
-rw-r--r--build2/algorithm.cxx34
-rw-r--r--build2/algorithm.hxx14
-rw-r--r--build2/algorithm.ixx49
-rw-r--r--build2/bin/init.cxx18
-rw-r--r--build2/cc/common.cxx23
-rw-r--r--build2/cc/common.hxx3
-rw-r--r--build2/cc/compile.cxx6
-rw-r--r--build2/cc/link.cxx109
-rw-r--r--build2/cc/link.hxx4
-rw-r--r--build2/cc/types.hxx6
-rw-r--r--build2/cc/utility.cxx15
-rw-r--r--build2/cc/windows-rpath.cxx12
-rw-r--r--tests/cc/libu/testscript34
13 files changed, 237 insertions, 90 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index fa9787b..5e18441 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -19,32 +19,6 @@ using namespace butl;
namespace build2
{
- const target*
- search_existing (const prerequisite& p)
- {
- assert (phase == run_phase::match); // Could be relaxed.
-
- const target* r (p.target.load (memory_order_consume));
-
- if (r == nullptr)
- {
- const prerequisite_key& pk (p.key ());
- r = pk.proj ? import_existing (pk) : search_existing_target (pk);
-
- if (r != nullptr)
- {
- const target* e (nullptr);
- if (!p.target.compare_exchange_strong (
- e, r,
- memory_order_release,
- memory_order_consume))
- assert (e == r);
- }
- }
-
- return r;
- }
-
const target&
search (const target& t, const prerequisite_key& pk)
{
@@ -62,6 +36,14 @@ namespace build2
return create_new_target (pk);
}
+ const target*
+ search_existing (const prerequisite_key& pk)
+ {
+ assert (phase == run_phase::match || phase == run_phase::execute);
+
+ return pk.proj ? import_existing (pk) : search_existing_target (pk);
+ }
+
const target&
search (const target& t, name n, const scope& s)
{
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 58bdf2f..2006e2a 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -27,13 +27,16 @@ namespace build2
// As above but only search for an already existing target.
//
const target*
- search_existing (const target&, const prerequisite&);
+ search_existing (const prerequisite&);
// As above but specify the prerequisite to search as a key.
//
const target&
search (const target&, const prerequisite_key&);
+ const target*
+ search_existing (const prerequisite_key&);
+
// Uniform search interface for prerequisite/prerequisite_member.
//
inline const target&
@@ -59,6 +62,15 @@ namespace build2
const scope* = nullptr, // NULL means dir is absolute.
const optional<string>& proj = nullopt);
+ const target*
+ search_existing (const target_type& type,
+ const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ const string* ext = nullptr,
+ const scope* = nullptr,
+ const optional<string>& proj = nullopt);
+
// As above but specify the target type as template argument.
//
template <typename T>
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index 53501d3..892c832 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -29,6 +29,31 @@ namespace build2
return *r;
}
+ inline const target*
+ search_existing (const prerequisite& p)
+ {
+ assert (phase == run_phase::match || phase == run_phase::execute);
+
+ const target* r (p.target.load (memory_order_consume));
+
+ if (r == nullptr)
+ {
+ r = search_existing (p.key ());
+
+ if (r != nullptr)
+ {
+ const target* e (nullptr);
+ if (!p.target.compare_exchange_strong (
+ e, r,
+ memory_order_release,
+ memory_order_consume))
+ assert (e == r);
+ }
+ }
+
+ return r;
+ }
+
inline const target&
search (const target& t, const target_type& tt, const prerequisite_key& k)
{
@@ -54,9 +79,27 @@ namespace build2
proj,
{
&type,
- &dir,
- &out,
- &name,
+ &dir, &out, &name,
+ ext != nullptr ? optional<string> (*ext) : nullopt
+ },
+ scope});
+ }
+
+ inline const target*
+ search_existing (const target_type& type,
+ const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ const string* ext,
+ const scope* scope,
+ const optional<string>& proj)
+ {
+ return search_existing (
+ prerequisite_key {
+ proj,
+ {
+ &type,
+ &dir, &out, &name,
ext != nullptr ? optional<string> (*ext) : nullopt
},
scope});
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index fb9dd71..e145a23 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -76,6 +76,24 @@ namespace build2
vp.insert<strings> ("bin.libs.lib");
vp.insert<dir_paths> ("bin.rpath");
+ // Link whole archive. Note: non-overridable with target visibility.
+ //
+ // The lookup semantics is as follows: we first look for a prerequisite-
+ // specific value, then for a target-specific value in the library being
+ // linked, and then for target type/pattern-specific value starting from
+ // the scope of the target being linked-to. In that final lookup we do
+ // not look in the target being linked-to itself since that is used to
+ // indicate how this target should be linked to other targets. For
+ // example:
+ //
+ // exe{test}: liba{foo}
+ // liba{foo}: libu{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{}.
+ //
+ vp.insert<bool> ("bin.whole", false, variable_visibility::target);
+
vp.insert<string> ("bin.lib.prefix");
vp.insert<string> ("bin.lib.suffix");
vp.insert<string> ("bin.exe.prefix");
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index 8dabb07..195c3b7 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -52,10 +52,12 @@ namespace build2
const dir_paths& top_sysd,
const file& l,
bool la,
+ lflags lf,
const function<bool (const file&,
bool la)>& proc_impl, // Implementation?
const function<void (const file*, // Can be NULL.
const string& path, // Library path.
+ lflags, // Link flags.
bool sys)>& proc_lib, // True if system library.
const function<void (const file&,
const string& type, // cc.type
@@ -185,7 +187,7 @@ namespace build2
? cast_false<bool> (l.vars[c_system])
: !p.empty () && sys (top_sysd, p.string ()));
- proc_lib (&l, p.string (), s);
+ proc_lib (&l, p.string (), lf, s);
}
const scope& bs (t == nullptr || cc ? top_bs : l.base_scope ());
@@ -220,20 +222,20 @@ namespace build2
//
if (impl && !c_e_libs.defined () && !x_e_libs.defined ())
{
- for (const target* p: l.prerequisite_targets)
+ for (auto pt: l.prerequisite_targets)
{
bool a;
const file* f;
- if ((a = (f = p->is_a<liba> ())) ||
- (a = (f = p->is_a<libux> ())) ||
- ( f = p->is_a<libs> ()))
+ if ((a = (f = pt->is_a<liba> ())) ||
+ (a = (f = pt->is_a<libux> ())) ||
+ ( f = pt->is_a<libs> ()))
{
if (sysd == nullptr) find_sysd ();
if (!li) find_linfo ();
process_libraries (act, bs, *li, *sysd,
- *f, a,
+ *f, a, pt.data,
proc_impl, proc_lib, proc_opt, true);
}
}
@@ -286,7 +288,7 @@ namespace build2
// .pc files.
//
if (proc_lib)
- proc_lib (nullptr, n.value, sys_simple (n.value));
+ proc_lib (nullptr, n.value, 0, sys_simple (n.value));
}
else
{
@@ -316,8 +318,11 @@ namespace build2
// 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 (act, bs, *li, *sysd,
- t, t.is_a<liba> () || t.is_a<libux> (),
+ t, t.is_a<liba> () || t.is_a<libux> (), 0,
proc_impl, proc_lib, proc_opt, true);
}
}
@@ -336,7 +341,7 @@ namespace build2
// This is something like -lpthread or shell32.lib so should be a
// valid path.
//
- proc_lib (nullptr, n, sys_simple (n));
+ proc_lib (nullptr, n, 0, sys_simple (n));
}
};
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 6e1d8b9..951863a 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -198,8 +198,9 @@ namespace build2
const dir_paths&,
const file&,
bool,
+ lflags,
const function<bool (const file&, bool)>&,
- const function<void (const file*, const string&, bool)>&,
+ const function<void (const file*, const string&, lflags, bool)>&,
const function<void (const file&, const string&, bool, bool)>&,
bool = false) const;
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
index 8fa3296..fbf6a19 100644
--- a/build2/cc/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -294,7 +294,7 @@ namespace build2
continue;
process_libraries (act, bs, li, sys_lib_dirs,
- pt->as<file> (), a,
+ pt->as<file> (), a, 0, // Hack: lflags unused.
nullptr, nullptr, optf);
}
}
@@ -338,7 +338,7 @@ namespace build2
continue;
process_libraries (act, bs, li, sys_lib_dirs,
- pt->as<file> (), a,
+ pt->as<file> (), a, 0, // Hack: lflags unused.
nullptr, nullptr, optf);
}
}
@@ -385,7 +385,7 @@ namespace build2
continue;
process_libraries (act, bs, li, sys_lib_dirs,
- pt->as<file> (), a,
+ pt->as<file> (), a, 0, // Hack: lflags unused.
nullptr, nullptr, optf);
}
}
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx
index 7b14621..babbbed 100644
--- a/build2/cc/link.cxx
+++ b/build2/cc/link.cxx
@@ -632,7 +632,8 @@ namespace build2
size_t i (start), n (t.prerequisite_targets.size ());
for (prerequisite_member p: group_prerequisite_members (act, t))
{
- const target*& pt (t.prerequisite_targets[i++]);
+ const target*& pt (t.prerequisite_targets[i].target);
+ uintptr_t& pd (t.prerequisite_targets[i++].data);
if (pt == nullptr)
continue;
@@ -778,6 +779,38 @@ namespace build2
m = 2; // Needs verification.
}
}
+ else // lib*{}
+ {
+ // If this is a static library, see if we need to link it whole.
+ // Note that we have to do it after match since we rely on the
+ // group link-up.
+ //
+ bool u;
+ if ((u = pt->is_a<libux> ()) || pt->is_a<liba> ())
+ {
+ const variable& var (var_pool["bin.whole"]); // @@ Cache.
+
+ // See the bin module for the lookup semantics discussion. Note
+ // that the variable is not overridable so we omit find_override()
+ // calls.
+ //
+ //@@ TODO: prerequisite-specific lookup.
+ //
+ lookup l (pt->find_original (var, true).first);
+ if (!l.defined ())
+ {
+ bool g (pt->group != nullptr);
+ l = bs.find_original (var,
+ &pt->type (),
+ &pt->name,
+ (g ? &pt->group->type () : nullptr),
+ (g ? &pt->group->name : nullptr)).first;
+ }
+
+ if (l ? cast<bool> (*l) : u)
+ pd |= lflag_whole;
+ }
+ }
mark (pt, m);
}
@@ -875,28 +908,47 @@ namespace build2
void link::
append_libraries (strings& args,
- const file& l, bool la,
+ const file& l, bool la, lflags lf,
const scope& bs, action act, linfo li) const
{
// Note: lack of the "small function object" optimization will really
// kill us here since we are called in a loop.
//
- bool win (tclass == "windows");
-
auto imp = [] (const file&, bool la) {return la;};
- auto lib = [&args, win] (const file* f, const string& p, bool)
+ auto lib = [&args, this] (const file* l, const string& p, lflags f, bool)
{
- if (f != 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 (win && f->member != nullptr && f->is_a<libs> ())
- f = &f->member->as<file> ();
+ if (l->member != nullptr && l->is_a<libs> () && tclass == "windows")
+ l = &l->member->as<file> ();
+
+ string p (relative (l->path ()).string ());
- args.push_back (relative (f->path ()).string ());
+ if (f & lflag_whole)
+ {
+ if (tsys == "win32-msvc")
+ {
+ p.insert (0, "/WHOLEARCHIVE:"); // Only available from VC14U2.
+ }
+ else if (tsys == "darwin")
+ {
+ p.insert (0, "-Wl,-force_load,");
+ }
+ else
+ {
+ args.push_back ("-Wl,--whole-archive");
+ args.push_back (move (p));
+ args.push_back ("-Wl,--no-whole-archive");
+ return;
+ }
+ }
+
+ args.push_back (move (p));
}
else
args.push_back (p);
@@ -921,30 +973,29 @@ namespace build2
};
process_libraries (
- act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true);
+ act, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
}
void link::
hash_libraries (sha256& cs,
- const file& l, bool la,
+ const file& l, bool la, lflags lf,
const scope& bs, action act, linfo li) const
{
- bool win (tclass == "windows");
-
auto imp = [] (const file&, bool la) {return la;};
- auto lib = [&cs, win] (const file* f, const string& p, bool)
+ auto lib = [&cs, this] (const file* l, const string& p, lflags f, bool)
{
- if (f != 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 (win && f->member != nullptr && f->is_a<libs> ())
- f = &f->member->as<file> ();
+ if (l->member != nullptr && l->is_a<libs> () && tclass == "windows")
+ l = &l->member->as<file> ();
- cs.append (f->path ().string ());
+ cs.append (f);
+ cs.append (l->path ().string ());
}
else
cs.append (p);
@@ -967,7 +1018,7 @@ namespace build2
};
process_libraries (
- act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true);
+ act, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
}
void link::
@@ -1013,7 +1064,7 @@ namespace build2
bool for_install;
} d {args, for_install};
- auto lib = [&d, this] (const file* l, const string& f, bool sys)
+ auto lib = [&d, this] (const file* l, const string& f, lflags, bool sys)
{
// We don't rpath system libraries. Why, you may ask? There are many
// good reasons and I have them written on a napkin somewhere...
@@ -1068,9 +1119,9 @@ 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&, bool)> libf (lib);
+ const function<void (const file*, const string&, lflags, bool)> libf (lib);
- for (const target* pt: t.prerequisite_targets)
+ for (auto pt: t.prerequisite_targets)
{
bool a;
const file* f;
@@ -1090,7 +1141,7 @@ namespace build2
}
process_libraries (act, bs, li, sys_lib_dirs,
- *f, a,
+ *f, a, pt.data,
impf, libf, nullptr);
}
}
@@ -1421,8 +1472,10 @@ namespace build2
{
sha256 cs;
- for (const target* pt: t.prerequisite_targets)
+ for (auto p: t.prerequisite_targets)
{
+ const target* pt (p.target);
+
// If this is bmi*{}, then obj*{} is its ad hoc member.
//
if (modules)
@@ -1446,7 +1499,7 @@ namespace build2
// and implementation (static), recursively.
//
if (a || s)
- hash_libraries (cs, *f, a, bs, act, li);
+ hash_libraries (cs, *f, a, p.data, bs, act, li);
else
cs.append (f->path ().string ());
}
@@ -1669,8 +1722,10 @@ namespace build2
// The same logic as during hashing above.
//
- for (const target* pt: t.prerequisite_targets)
+ for (auto p: t.prerequisite_targets)
{
+ const target* pt (p.target);
+
if (modules)
{
if (pt->is_a<bmie> () || pt->is_a<bmia> () || pt->is_a<bmis> ())
@@ -1692,7 +1747,7 @@ namespace build2
// and implementation (static), recursively.
//
if (a || s)
- append_libraries (sargs, *f, a, bs, act, li);
+ append_libraries (sargs, *f, a, p.data, bs, act, li);
else
sargs.push_back (relative (f->path ()).string ()); // string()&&
}
diff --git a/build2/cc/link.hxx b/build2/cc/link.hxx
index 0256774..ed28eca 100644
--- a/build2/cc/link.hxx
+++ b/build2/cc/link.hxx
@@ -75,12 +75,12 @@ namespace build2
//
void
append_libraries (strings&,
- const file&, bool,
+ const file&, bool, lflags,
const scope&, action, linfo) const;
void
hash_libraries (sha256&,
- const file&, bool,
+ const file&, bool, lflags,
const scope&, action, linfo) const;
void
diff --git a/build2/cc/types.hxx b/build2/cc/types.hxx
index b575dc7..48ffa05 100644
--- a/build2/cc/types.hxx
+++ b/build2/cc/types.hxx
@@ -91,6 +91,12 @@ namespace build2
otype type;
lorder order;
};
+
+ // Prerequisite link flags.
+ //
+ using lflags = uintptr_t; // To match prerequisite_target::data.
+
+ const lflags lflag_whole = 0x00000001U; // Link whole liba{}/libux{}.
}
}
diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx
index fa5061e..7a2b7fe 100644
--- a/build2/cc/utility.cxx
+++ b/build2/cc/utility.cxx
@@ -43,12 +43,15 @@ namespace build2
{
if (const libu* u = x.is_a<libu> ())
{
- otype ot (li.type);
- return search (*u,
- ot == otype::e ? libue::static_type :
- ot == otype::a ? libua::static_type :
- libus::static_type,
- u->dir, u->out, u->name);
+ const target_type& tt (li.type == otype::e ? libue::static_type :
+ li.type == otype::a ? libua::static_type :
+ libus::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);
}
else
{
diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx
index 1fc195a..2f4f31f 100644
--- a/build2/cc/windows-rpath.cxx
+++ b/build2/cc/windows-rpath.cxx
@@ -58,7 +58,7 @@ namespace build2
//
auto imp = [] (const file&, bool) {return true;};
- auto lib = [&r] (const file* l, const string& f, bool sys)
+ auto lib = [&r] (const file* l, const string& f, lflags, bool sys)
{
// We don't rpath system libraries.
//
@@ -102,7 +102,7 @@ namespace build2
r = t;
};
- for (const target* pt: t.prerequisite_targets)
+ for (auto pt: t.prerequisite_targets)
{
const file* f;
const liba* a;
@@ -110,7 +110,7 @@ namespace build2
if ((f = a = pt->is_a<liba> ()) ||
(f = pt->is_a<libs> ()))
process_libraries (act, bs, li, sys_lib_dirs,
- *f, a != nullptr,
+ *f, a != nullptr, pt.data,
imp, lib, nullptr, true);
}
@@ -130,7 +130,7 @@ namespace build2
auto imp = [] (const file&, bool) {return true;};
- auto lib = [&r] (const file* l, const string& f, bool sys)
+ auto lib = [&r] (const file* l, const string& f, lflags, bool sys)
{
if (sys)
return;
@@ -184,7 +184,7 @@ namespace build2
}
};
- for (const target* pt: t.prerequisite_targets)
+ for (auto pt: t.prerequisite_targets)
{
const file* f;
const liba* a;
@@ -192,7 +192,7 @@ namespace build2
if ((f = a = pt->is_a<liba> ()) ||
(f = pt->is_a<libs> ()))
process_libraries (act, bs, li, sys_lib_dirs,
- *f, a != nullptr,
+ *f, a != nullptr, pt.data,
imp, lib, nullptr, true);
}
diff --git a/tests/cc/libu/testscript b/tests/cc/libu/testscript
index 454a443..78a3eb3 100644
--- a/tests/cc/libu/testscript
+++ b/tests/cc/libu/testscript
@@ -7,6 +7,10 @@ test.arguments = config.cxx="$recall($cxx.path)"
.include ../../common.test
++cat <<EOI >+build/bootstrap.build
+using test
+EOI
+
+cat <<EOI >=build/root.build
cxx.std = latest
@@ -14,6 +18,8 @@ using cxx
hxx{*}: extension = hxx
cxx{*}: extension = cxx
+
+exe{*}: test = true
EOI
# Common source files that are symlinked in the test directories if used.
@@ -23,24 +29,40 @@ EOI
# define LIBFOO_EXPORT
#endif
- LIBFOO_EXPORT void f ();
+ LIBFOO_EXPORT extern int f;
EOI
+cat <<EOI >=foo.cxx
- void f () {}
+ #include <foo.hxx>
+ int f;
+ EOI
+
++cat <<EOI >=bar.cxx
+ #include <foo.hxx>
+ struct b { b () {++f;} } b_;
EOI
+cat <<EOI >=driver.cxx
+ #include <cassert>
#include <foo.hxx>
- int main () {f ();}
+ int main () {assert (f != 0);}
+ EOI
+
+: basic
+:
+ln -s ../foo.hxx ../foo.cxx ../bar.cxx ../driver.cxx ./;
+$* test clean <<EOI
+ cc.poptions += "-I$src_base"
+ exe{foo}: cxx{driver} libu{foo}
+ libu{foo}: cxx{foo bar}
EOI
: members
:
: Test building individual libuX{} members.
:
-ln -s ../foo.hxx ../foo.cxx ../driver.cxx ./;
-$* update clean <<EOI
+ln -s ../foo.hxx ../foo.cxx ../bar.cxx ../driver.cxx ./;
+$* test clean <<EOI
cc.poptions += "-I$src_base"
# {exe liba libs}{foo}
@@ -50,5 +72,5 @@ $* update clean <<EOI
liba{foo}: libua{foo}
libs{foo}: libus{foo}
- libu{foo}: cxx{foo}
+ libu{foo}: cxx{foo bar}
EOI