From 7c57f2a85aa520db784a36ced65ec5c832dbfbc8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 24 Jun 2022 05:44:37 +0200 Subject: Add ability to get common interface options via $x.lib_poptions() Specifically, the output target type may now be omitted for utility libraries (libul{} and libu[eas]{}). In this case, only "common interface" options will be returned for lib{} dependencies. This is primarily useful for obtaining poptions to be passed to tools other than C/C++ compilers (for example, Qt moc). --- libbuild2/cc/common.cxx | 80 +++++++++++++++++++++++++++++------------- libbuild2/cc/common.hxx | 6 +++- libbuild2/cc/compile-rule.cxx | 35 ++++++++++++------ libbuild2/cc/compile-rule.hxx | 4 +-- libbuild2/cc/functions.cxx | 77 +++++++++++++++++++++++++++++----------- libbuild2/cc/link-rule.cxx | 15 ++++++-- libbuild2/cc/pkgconfig.cxx | 10 ++++-- libbuild2/cc/windows-rpath.cxx | 8 +++-- 8 files changed, 169 insertions(+), 66 deletions(-) diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx index cd89c79..976127f 100644 --- a/libbuild2/cc/common.cxx +++ b/libbuild2/cc/common.cxx @@ -39,6 +39,11 @@ namespace build2 // 3. dependency libs (prerequisite_targets, left to right, depth-first) // 4. dependency libs (*.libs variables). // + // If proc_opt_group is true, then pass to proc_opt the group rather than + // the member if a member was picked (according to linfo) form a group. + // This is useful when we only want to see the common options set on the + // group. + // // If either proc_opt or proc_lib return false, then any further // processing of this library or its dependencies is skipped. This can be // used to "prune" the graph traversal in case of duplicates. Note that @@ -72,10 +77,14 @@ namespace build2 // not to pick the liba/libs{} member for installed libraries instead // passing the lib{} group itself. This can be used to match the semantics // of file_rule which, when matching prerequisites, does not pick the - // liba/libs{} member (naturally) but just matches the lib{} group. + // liba/libs{} member (naturally) but just matches the lib{} group. Note + // that currently this truly only works for installed lib{} since non- + // installed ones don't have cc.type set. See proc_opt_group for an + // alternative way to (potentially) achieve the desired semantics. // // Note that if top_li is present, then the target passed to proc_impl, - // proc_lib, and proc_opt is always a file. + // proc_lib, and proc_opt (unless proc_opt_group is true) is always a + // file. // // The dedup argument is part of the interface dependency deduplication // functionality, similar to $x.deduplicate_export_libs(). Note, however, @@ -87,7 +96,7 @@ namespace build2 const scope& top_bs, optional top_li, const dir_paths& top_sysd, - const mtime_target& l, // liba/libs{} or lib{} + const mtime_target& l, // liba/libs{}, libux{}, or lib{} bool la, lflags lf, const function& proc_opt, // *.export. - bool self /*= false*/, // Call proc_lib on l? + bool self, // Call proc_lib on l? + bool proc_opt_group, // Call proc_opt on group instead of member? library_cache* cache) const { library_cache cache_storage; @@ -115,8 +125,9 @@ namespace build2 chain.push_back (nullptr); process_libraries_impl (a, top_bs, top_li, top_sysd, - l, la, lf, - proc_impl, proc_lib, proc_opt, self, + nullptr, l, la, lf, + proc_impl, proc_lib, proc_opt, + self, proc_opt_group, cache, &chain, nullptr); } @@ -126,6 +137,7 @@ namespace build2 const scope& top_bs, optional top_li, const dir_paths& top_sysd, + const target* lg, const mtime_target& l, bool la, lflags lf, @@ -142,6 +154,7 @@ namespace build2 bool com, bool exp)>& proc_opt, bool self, + bool proc_opt_group, library_cache* cache, small_vector* chain, small_vector* dedup) const @@ -237,12 +250,14 @@ namespace build2 // if (proc_opt) { + const target& ol (proc_opt_group && lg != nullptr ? *lg : l); + // If all we know is it's a C-common library, then in both cases // we only look for cc.export.*. // if (cc) { - if (!proc_opt (l, t, true, true)) break; + if (!proc_opt (ol, t, true, true)) break; } else { @@ -259,24 +274,24 @@ namespace build2 // // Note: options come from *.export.* variables. // - if (!proc_opt (l, t, false, true) || - !proc_opt (l, t, true, true)) break; + if (!proc_opt (ol, t, false, true) || + !proc_opt (ol, t, true, true)) break; } else { // For default export we use the same options as were used // to build the library. // - if (!proc_opt (l, t, false, false) || - !proc_opt (l, t, true, false)) break; + if (!proc_opt (ol, t, false, false) || + !proc_opt (ol, t, true, false)) break; } } else { // Interface: only add *.export.* (interface dependencies). // - if (!proc_opt (l, t, false, true) || - !proc_opt (l, t, true, true)) break; + if (!proc_opt (ol, t, false, true) || + !proc_opt (ol, t, true, true)) break; } } } @@ -380,12 +395,17 @@ namespace build2 (la = (f = pt->is_a ())) || ( f = pt->is_a ())) { + // See link_rule for details. + // + const target* g ((pt.include & 4) != 0 ? f->group : nullptr); + if (sysd == nullptr) find_sysd (); if (!li) find_linfo (); process_libraries_impl (a, bs, *li, *sysd, - *f, la, pt.data, - proc_impl, proc_lib, proc_opt, true, + g, *f, la, pt.data, + proc_impl, proc_lib, proc_opt, + true /* self */, proc_opt_group, cache, chain, nullptr); } } @@ -480,7 +500,7 @@ namespace build2 return make_pair (n, s); }; - auto proc_intf = [&l, cache, chain, + auto proc_intf = [&l, proc_opt_group, cache, chain, &proc_impl, &proc_lib, &proc_lib_name, &proc_opt, &sysd, &usrd, &find_sysd, &find_linfo, &sense_fragment, @@ -530,7 +550,7 @@ namespace build2 if (sysd == nullptr) find_sysd (); if (!li) find_linfo (); - const mtime_target& t ( + pair p ( resolve_library (a, bs, n, @@ -539,6 +559,9 @@ namespace build2 *sysd, usrd, cache)); + const mtime_target& t (p.first); + const target* g (p.second); + // Deduplicate. // // Note that dedup_start makes sure we only consider our @@ -599,8 +622,9 @@ namespace build2 // process_libraries_impl ( a, bs, *li, *sysd, - t, t.is_a () || t.is_a (), 0, - proc_impl, proc_lib, proc_opt, true, + g, t, t.is_a () || t.is_a (), 0, + proc_impl, proc_lib, proc_opt, + true /* self */, proc_opt_group, cache, chain, dedup); } @@ -746,9 +770,10 @@ namespace build2 // // If li is absent, then don't pick the liba/libs{} member, returning the // lib{} target itself. If li is present, then the returned target is - // always a file. + // always a file. The second half of the returned pair is the group, if + // the member was picked. // - const mtime_target& common:: + pair common:: resolve_library (action a, const scope& s, const name& cn, @@ -769,7 +794,8 @@ namespace build2 // large number of times (see Boost for an extreme example of this). // // Note also that for non-utility libraries we know that only the link - // order from linfo is used. + // order from linfo is used. While not caching it and always picking an + // alternative could also work, we cache it to avoid the lookup. // if (cache != nullptr) { @@ -789,7 +815,7 @@ namespace build2 })); if (i != cache->end ()) - return i->lib; + return pair {i->lib, i->group}; } else cache = nullptr; // Do not cache. @@ -831,18 +857,22 @@ namespace build2 // If this is lib{}/libul{}, pick appropriate member unless we were // instructed not to. // + const target* g (nullptr); if (li) { if (const libx* l = xt->is_a ()) + { + g = xt; xt = link_member (*l, a, *li); // Pick lib*{e,a,s}{}. + } } auto& t (xt->as ()); if (cache != nullptr) - cache->push_back (library_cache_entry {lo, cn.type, cn.value, t}); + cache->push_back (library_cache_entry {lo, cn.type, cn.value, t, g}); - return t; + return pair {t, g}; } // Note that pk's scope should not be NULL (even if dir is absolute). diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index 1e74b22..a5a4859 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -300,6 +300,7 @@ namespace build2 string type; // name::type string value; // name::value reference_wrapper lib; + const target* group; }; using library_cache = small_vector; @@ -319,6 +320,7 @@ namespace build2 lflags, const string*, bool)>&, const function&, bool = false, + bool = false, library_cache* = nullptr) const; void @@ -327,6 +329,7 @@ namespace build2 const scope&, optional, const dir_paths&, + const target*, const mtime_target&, bool, lflags, @@ -336,6 +339,7 @@ namespace build2 lflags, const string*, bool)>&, const function&, bool, + bool, library_cache*, small_vector*, small_vector*) const; @@ -365,7 +369,7 @@ namespace build2 } public: - const mtime_target& + pair resolve_library (action, const scope&, const name&, diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index f5d50ec..7059f15 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -489,13 +489,16 @@ namespace build2 // Append or hash library options from a pair of *.export.* variables // (first is x.* then cc.*) recursively, prerequisite libraries first. + // If common is true, then only append common options from the lib{} + // groups. // template void compile_rule:: append_library_options (appended_libraries& ls, T& args, const scope& bs, const scope* is, // Internal scope. - action a, const file& l, bool la, linfo li, + action a, const file& l, bool la, + linfo li, bool common, library_cache* lib_cache) const { struct data @@ -509,7 +512,7 @@ namespace build2 // auto imp = [] (const target& l, bool la) {return la && l.is_a ();}; - auto opt = [&d, this] (const target& lt, + auto opt = [&d, this] (const target& l, // Note: could be lib{} const string& t, bool com, bool exp) { // Note that in our model *.export.poptions are always "interface", @@ -518,8 +521,6 @@ namespace build2 if (!exp) // Ignore libux. return true; - const file& l (lt.as ()); - // Suppress duplicates. // // Compilation is the simple case: we can add the options on the first @@ -678,16 +679,24 @@ namespace build2 process_libraries (a, bs, li, sys_lib_dirs, l, la, 0, // lflags unused. - imp, nullptr, opt, false /* self */, lib_cache); + imp, nullptr, opt, + false /* self */, + common /* proc_opt_group */, + lib_cache); } void compile_rule:: append_library_options (appended_libraries& ls, strings& args, const scope& bs, - action a, const file& l, bool la, linfo li) const + action a, const file& l, bool la, + linfo li, + bool common) const { + // @@ Is this a good idea? We don't know which tool will be using + // these... + // const scope* is (isystem (*this) ? effective_iscope (bs) : nullptr); - append_library_options (ls, args, bs, is, a, l, la, li, nullptr); + append_library_options (ls, args, bs, is, a, l, la, li, common, nullptr); } template @@ -728,7 +737,9 @@ namespace build2 append_library_options (ls, args, bs, iscope (), - a, *f, la, li, + a, *f, la, + li, + false /* common */, &lc); } } @@ -810,7 +821,9 @@ namespace build2 process_libraries (a, bs, li, sys_lib_dirs, pt->as (), la, 0, // lflags unused. - impf, nullptr, optf, false /* self */, + impf, nullptr, optf, + false /* self */, + false /* proc_opt_group */, &lib_cache); } } @@ -6222,7 +6235,9 @@ namespace build2 // process_libraries (a, bs, nullopt, sys_lib_dirs, *f, la, 0, // lflags unused. - imp, lib, nullptr, true /* self */, + imp, lib, nullptr, + true /* self */, + false /* proc_opt_group */, &lib_cache); if (lt != nullptr) diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx index 49d33eb..f3e4a9b 100644 --- a/libbuild2/cc/compile-rule.hxx +++ b/libbuild2/cc/compile-rule.hxx @@ -66,7 +66,7 @@ namespace build2 void append_library_options (appended_libraries&, strings&, const scope&, - action, const file&, bool, linfo) const; + action, const file&, bool, linfo, bool) const; optional find_system_header (const path&) const; @@ -87,7 +87,7 @@ namespace build2 append_library_options (appended_libraries&, T&, const scope&, const scope*, - action, const file&, bool, linfo, + action, const file&, bool, linfo, bool, library_cache*) const; template diff --git a/libbuild2/cc/functions.cxx b/libbuild2/cc/functions.cxx index 9b7a3d9..a0a0b4f 100644 --- a/libbuild2/cc/functions.cxx +++ b/libbuild2/cc/functions.cxx @@ -85,14 +85,26 @@ namespace build2 return value (move (r)); } - // Common thunk for $x.lib_*(, [, ...]) functions. + // Common thunk for $x.lib_*(...) functions. + // + // The two supported function signatures are: + // + // $x.lib_*(, [, ...]]) + // + // $x.lib_*() + // + // For the first signature, the passed targets cannot be library groups + // (so they are always file-based) and linfo is always present. + // + // For the second signature, targets can only be utility libraries + // (including the libul{} group). // struct lib_thunk_data { const char* x; void (*f) (void*, strings&, const vector_view&, const module&, const scope&, - action, const file&, bool, linfo); + action, const target&, bool, optional); }; static value @@ -120,13 +132,15 @@ namespace build2 if (m == nullptr) fail << f.name << " called without " << d.x << " module loaded"; - // We can assume these are present due to function's types signature. + // We can assume this is present due to function's types signature. // names& ts_ns (vs[0].as ()); // - names& ot_ns (vs[1].as ()); // - linfo li; + optional li; + if (vs.size () > 1) { + names& ot_ns (vs[1].as ()); // + string t (convert (move (ot_ns))); const target_type* tt (bs->find_target_type (t)); @@ -172,21 +186,22 @@ namespace build2 name& n (*i), o; const target& t (to_target (*bs, move (n), move (n.pair ? *++i : o))); - const file* f; bool la (false); - - if ((la = (f = t.is_a ())) || - (la = (f = t.is_a ())) || - ( (f = t.is_a ()))) + if (li + ? ((la = t.is_a ()) || + (la = t.is_a ()) || + ( t.is_a ())) + : ((la = t.is_a ()) || + ( t.is_a ()))) { if (!t.matched (a)) fail << t << " is not matched" << info << "make sure this target is listed as prerequisite"; - d.f (ls, r, vs, *m, *bs, a, *f, la, li); + d.f (ls, r, vs, *m, *bs, a, t, la, li); } else - fail << t << " is not a library target"; + fail << t << " is not a library of expected type"; } return value (move (r)); @@ -213,12 +228,18 @@ namespace build2 void compile_rule:: functions (function_family& f, const char* x) { - // $.lib_poptions(, ) + // $.lib_poptions([, ]) // // Return the preprocessor options that should be passed when compiling // sources that depend on the specified libraries. The second argument // is the output target type (obje, objs, etc). // + // The output target type may be omitted for utility libraries (libul{} + // or libu[eas]{}). In this case, only "common interface" options will + // be returned for lib{} dependencies. This is primarily useful for + // obtaining poptions to be passed to tools other than C/C++ compilers + // (for example, Qt moc). + // // Note that passing multiple targets at once is not a mere convenience: // this also allows for more effective duplicate suppression. // @@ -230,17 +251,31 @@ namespace build2 // Note that this function is not pure. // f.insert (".lib_poptions", false). - insert ( + insert> ( &lib_thunk, lib_thunk_data { x, [] (void* ls, strings& r, const vector_view&, const module& m, const scope& bs, - action a, const file& l, bool la, linfo li) + action a, const target& l, bool la, optional li) { + // If this is libul{}, get the matched member (see bin::libul_rule + // for details). + // + const file& f ( + la || li + ? l.as () + : (la = true, + l.prerequisite_targets[a].back ().target->as ())); + + bool common (!li); + + if (!li) + li = link_info (bs, link_type (f).type); + m.append_library_options ( *static_cast (ls), r, - bs, a, l, la, li); + bs, a, f, la, *li, common); }}); // $.find_system_header() @@ -318,7 +353,7 @@ namespace build2 x, [] (void* ls, strings& r, const vector_view& vs, const module& m, const scope& bs, - action a, const file& l, bool la, linfo li) + action a, const target& l, bool la, optional li) { lflags lf (0); bool rel (true); @@ -342,7 +377,8 @@ namespace build2 m.append_libraries ( *static_cast (ls), r, nullptr /* sha256 */, nullptr /* update */, timestamp_unknown, - bs, a, l, la, lf, li, nullopt /* for_install */, self, rel); + bs, a, l.as (), la, lf, *li, + nullopt /* for_install */, self, rel); }}); // $.lib_rpaths(, [, [, ]]) @@ -374,13 +410,12 @@ namespace build2 x, [] (void* ls, strings& r, const vector_view& vs, const module& m, const scope& bs, - action a, const file& l, bool la, linfo li) + action a, const target& l, bool la, optional li) { bool link (vs.size () > 2 ? convert (vs[2]) : false); bool self (vs.size () > 3 ? convert (vs[3]) : true); m.rpath_libraries (*static_cast (ls), r, - bs, - a, l, la, li, link, self); + bs, a, l.as (), la, *li, link, self); }}); // $cxx.obj_modules() diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index fff8716..0081fe2 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -1138,10 +1138,14 @@ namespace build2 m = 3; // Mark so it is not matched. // If this is the lib{}/libul{} group, then pick the appropriate - // member. + // member. Also note this in prerequisite_target::include (used + // by process_libraries()). // if (const libx* l = pt->is_a ()) + { pt = link_member (*l, a, li); + pto.include |= 4; + } } else { @@ -2360,7 +2364,9 @@ namespace build2 process_libraries (a, bs, li, sys_lib_dirs, l, la, - lf, imp, lib, opt, self, + lf, imp, lib, opt, + self, + false /* proc_opt_group */, lib_cache); } @@ -2574,7 +2580,10 @@ namespace build2 process_libraries (a, bs, li, sys_lib_dirs, l, la, 0 /* lflags */, - imp, lib, nullptr, false /* self */, lib_cache); + imp, lib, nullptr, + false /* self */, + false /* proc_opt_group */, + lib_cache); } void link_rule:: diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx index a48d99c..1fd96da 100644 --- a/libbuild2/cc/pkgconfig.cxx +++ b/libbuild2/cc/pkgconfig.cxx @@ -1967,7 +1967,10 @@ namespace build2 library_cache lib_cache; process_libraries (a, bs, li, sys_lib_dirs, l, la, 0, // Link flags. - imp, lib, opt, !binless /* self */, &lib_cache); + imp, lib, opt, + !binless /* self */, + false /* proc_opt_group */, // @@ !priv? + &lib_cache); for (const string& a: args) os << ' ' << a; @@ -1989,7 +1992,10 @@ namespace build2 process_libraries (a, bs, li, sys_lib_dirs, l, la, 0, // Link flags. - imp, lib, opt, false /* self */, &lib_cache); + imp, lib, opt, + false /* self */, + false /* proc_opt_group */, // @@ !priv? + &lib_cache); for (const string& a: args) os << ' ' << a; diff --git a/libbuild2/cc/windows-rpath.cxx b/libbuild2/cc/windows-rpath.cxx index a8bf104..9387078 100644 --- a/libbuild2/cc/windows-rpath.cxx +++ b/libbuild2/cc/windows-rpath.cxx @@ -139,7 +139,9 @@ namespace build2 ( f = pt->is_a ())) process_libraries (a, bs, li, sys_lib_dirs, *f, la, pt.data, - imp, lib, nullptr, true /* self */, + imp, lib, nullptr, + true /* self */, + false /* proc_opt_group */, &lib_cache); } @@ -264,7 +266,9 @@ namespace build2 ( f = pt->is_a ())) process_libraries (a, bs, li, sys_lib_dirs, *f, la, pt.data, - imp, lib, nullptr, true /* self */, + imp, lib, nullptr, + true /* self */, + false /* proc_opt_group */, &lib_cache); } -- cgit v1.1