From 1c7cbb302b1c6e41eb0c5cecfc655532f1919cba Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 28 Jul 2017 13:46:26 +0200 Subject: Implement support for linking whole archive --- build2/cc/common.cxx | 23 ++++++---- build2/cc/common.hxx | 3 +- build2/cc/compile.cxx | 6 +-- build2/cc/link.cxx | 109 +++++++++++++++++++++++++++++++++----------- build2/cc/link.hxx | 4 +- build2/cc/types.hxx | 6 +++ build2/cc/utility.cxx | 15 +++--- build2/cc/windows-rpath.cxx | 12 ++--- 8 files changed, 124 insertions(+), 54 deletions(-) (limited to 'build2/cc') 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& proc_impl, // Implementation? const function& proc_lib, // True if system library. const function (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 ())) || - (a = (f = p->is_a ())) || - ( f = p->is_a ())) + if ((a = (f = pt->is_a ())) || + (a = (f = pt->is_a ())) || + ( f = pt->is_a ())) { 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 () || t.is_a (), + t, t.is_a () || t.is_a (), 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&, - const function&, + const function&, const function&, 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 (), a, + pt->as (), 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 (), a, + pt->as (), 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 (), a, + pt->as (), 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 ()) || pt->is_a ()) + { + 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 (*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 ()) - f = &f->member->as (); + if (l->member != nullptr && l->is_a () && tclass == "windows") + l = &l->member->as (); + + 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 ()) - f = &f->member->as (); + if (l->member != nullptr && l->is_a () && tclass == "windows") + l = &l->member->as (); - 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 impf (imp); - const function libf (lib); + const function 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 () || pt->is_a () || pt->is_a ()) @@ -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 ()) { - 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 ()) || (f = pt->is_a ())) 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 ()) || (f = pt->is_a ())) process_libraries (act, bs, li, sys_lib_dirs, - *f, a != nullptr, + *f, a != nullptr, pt.data, imp, lib, nullptr, true); } -- cgit v1.1