From 32e04ad4b4a8dec07836b7c9fcf90fe72a006990 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 25 Aug 2018 17:40:21 +0200 Subject: Implement missing pieces in utility libraries support In particular, we can now build static libraries out of utility libraries. --- build2/cc/common.cxx | 257 ++++++++++++++++++++++++++++----------------------- 1 file changed, 139 insertions(+), 118 deletions(-) (limited to 'build2/cc/common.cxx') 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& proc_impl, // Implementation? - const function& 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* chain) const { + small_vector 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 (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 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 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* ns (cast_null> (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* ns (cast_null> (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 () || t.is_a (), 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 (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 () || t.is_a (), 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 (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 ()) xt = &link_member (*l, a, li); // Pick lib*{e,a,s}{}. -- cgit v1.1