From f0edc0e2b67fa43c4e2410c7d3d8f1841d576749 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 26 Aug 2016 16:37:16 +0200 Subject: Add pkg-config support for import installed Redesign library importing/exporting while at it. --- build2/cc/link.cxx | 395 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 253 insertions(+), 142 deletions(-) (limited to 'build2/cc/link.cxx') diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 7e639ed..a4fa1dd 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -202,7 +202,9 @@ namespace build2 } target* link:: - search_library (optional& spc, const prerequisite_key& p) const + search_library (optional& spc, + const prerequisite_key& p, + lorder lo) const { tracer trace (x, "link::search_library"); @@ -437,7 +439,8 @@ namespace build2 // Add the "using static/shared library" macro (used, for example, to // handle DLL export). The absence of either of these macros would mean - // some other build system that cannot distinguish between the two. + // some other build system that cannot distinguish between the two (and + // no pkg-config information). // auto add_macro = [this] (target& t, const char* suffix) { @@ -446,6 +449,11 @@ namespace build2 // messing with custom values. And if we are adding, then use the // generic cc.export. // + // The only way we could already have this value is if this same + // library was also imported as a project (as opposed to installed). + // Unlikely but possible. In this case the values were set by the + // export stub and we shouldn't touch them. + // if (!t.vars[x_export_poptions]) { auto p (t.vars.insert (c_export_poptions)); @@ -478,11 +486,38 @@ namespace build2 } }; - if (a != nullptr) - add_macro (*a, "STATIC"); + // Mark as a "cc" library unless already marked. + // + auto mark_cc = [this] (target& t) -> bool + { + auto p (t.vars.insert (c_type)); + + if (p.second) + p.first.get () = string ("cc"); + + return p.second; + }; + + // If the library already has cc.type, then assume it was either already + // imported or was matched by a rule. + // + if (a != nullptr && mark_cc (*a)) + { + // Only add the default macro if we could not extract more precise + // information. The idea is that when we auto-generate .pc files, we + // will copy those macros (or custom ones) from *.export.poptions. + // + if (pkgconfig == nullptr || + !pkgconfig_extract (*p.scope, *a, p.proj, name, *pd, spc, lo)) + add_macro (*a, "STATIC"); + } - if (s != nullptr) - add_macro (*s, "SHARED"); + if (s != nullptr && mark_cc (*s)) + { + if (pkgconfig == nullptr || + !pkgconfig_extract (*p.scope, *s, p.proj, name, *pd, spc, lo)) + add_macro (*s, "SHARED"); + } if (l) { @@ -606,6 +641,8 @@ namespace build2 if (t.group != nullptr) t.group->prerequisite_targets.clear (); // lib{}'s + scope& bs (t.base_scope ()); + lorder lo (link_order (bs, lt)); optional lib_paths; // Extract lazily. for (prerequisite_member p: group_prerequisite_members (a, t)) @@ -617,7 +654,7 @@ namespace build2 // Handle imported libraries. // if (p.proj () != nullptr) - pt = search_library (lib_paths, p.prerequisite); + pt = search_library (lib_paths, p.prerequisite, lo); if (pt == nullptr) { @@ -810,7 +847,7 @@ namespace build2 // Handle imported libraries. // if (p.proj () != nullptr) - pt = search_library (lib_paths, p.prerequisite); + pt = search_library (lib_paths, p.prerequisite, lo); // The rest is the same basic logic as in search_and_match(). // @@ -1023,9 +1060,12 @@ namespace build2 } } - // Recursively append/hash prerequisite libraries. Only interface + // Recursively process prerequisite libraries. Only interface // (*.export.libs) for shared libraries, interface and implementation - // (both prerequisite and from *.libs) for static libraries. + // (both prerequisite and from *.libs, unless overriden) for static + // libraries (unless iface_only is true, in which case we use + // *.export.libs even for static libraries which means *.export.libs + // should be set on lib{}, not libs{}). // // Note that here we assume that an interface library is also an // implementation (since we don't use *.export.libs in static link). We @@ -1034,72 +1074,111 @@ namespace build2 // listed as a prerequisite of this library). // void link:: - append_libraries (strings& args, file& l, bool la) const + process_libraries ( + file& l, + bool la, + bool iface_only, + const function& proc_lib, + const function& proc_opt) const { - for (target* p: l.prerequisite_targets) + // 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. + // + const string* t (cast_null (l.vars[c_type])); + + lookup c_e_libs; + lookup x_e_libs; + + if (t != nullptr) { - bool a; - file* f; + // If static, then the explicit export override should be set on the + // liba{} target itself. Note also that we only check for *.libs. If + // one doesn't have any libraries but needs to set, say, *.loptions, + // then *.libs should be set to NULL or empty (this is why we check + // for result being defined). + // + // @@ Should we set it in import installed then? + // + c_e_libs = la && !iface_only + ? l.vars[c_export_libs] + : l[c_export_libs]; - if ((a = (f = p->is_a ()) != nullptr) - || (f = p->is_a ()) != nullptr) + if (*t != "cc") { - if (la) - args.push_back (relative (f->path ()).string ()); // string()&& + const variable& var (*t == x + ? x_export_libs + : var_pool[*t + ".export.libs"]); - append_libraries (args, *f, a); + x_e_libs = la && !iface_only ? l.vars[var] : l[var]; } } - if (la) + // Only go into prerequisites (implementation dependencies) if this is a + // static library and it's not using explicit export. For shared library + // or if iface_only, we are only interested in interface dependencies + // which come from the *.export.libs below. + // + if (la && !iface_only && !c_e_libs.defined () && !x_e_libs.defined ()) { - // See what type of library this is (C, C++, etc). Use it do decide - // which x.libs variable name. If it is not a C-common library, then - // it probably doesn't have cc.libs either. - // - if (const string* t = cast_null (l.vars[c_type])) + for (target* p: l.prerequisite_targets) { - // @@ Shouldn't, ideally, we filter loptions to only include -L? - // - append_options (args, l, c_loptions); - append_options (args, l, c_libs); - - append_options (args, - l, - *t == x ? x_loptions : var_pool[*t + ".loptions"]); - append_options (args, - l, - *t == x ? x_libs : var_pool[*t + ".libs"]); + bool a; + file* f; + + if ((a = (f = p->is_a ()) != nullptr) + || (f = p->is_a ()) != nullptr) + { + if (proc_lib) + proc_lib (f->path ()); + + process_libraries (*f, a, iface_only, proc_lib, proc_opt); + } } } - else - { - scope* bs (nullptr); // Resolve lazily. - optional lo; // Calculate lazily. - optional spc; // Extract lazily. - auto append = [&args, &l, &bs, &lo, &spc, this] (const variable& var) - { - const names* ns (cast_null (l[var])); - if (ns == nullptr || ns->empty ()) - return; + // Process libraries (recursively) from *.export.libs (of type names) + // handling import, etc. + // + scope* bs (nullptr); // Resolve lazily. + optional lo; // Calculate lazily. + optional spc; // Extract lazily. - args.reserve (args.size () + ns->size ()); + auto proc_int = + [&l, la, iface_only, &proc_lib, &proc_opt, &bs, &lo, &spc, this] ( + const lookup& lu) + { + const names* ns (cast_null (lu)); + if (ns == nullptr || ns->empty ()) + return; - for (const name& n: *ns) + for (const name& n: *ns) + { + if (n.simple ()) { - if (n.simple ()) - args.push_back (n.value); - else - { - if (bs == nullptr) - bs = &l.base_scope (); + // This is something like -lpthread or shell32.lib so should be + // a valid path. + // + if (proc_lib) + proc_lib (path (n.value)); + } + else + { + // This is a potentially project-qualified target. + // + if (bs == nullptr) + bs = &l.base_scope (); - if (!lo) - lo = link_order (*bs, otype::s); // We know it's libs{}. + if (!lo) + lo = link_order (*bs, la ? otype::a : otype::s); - file& t (resolve_library (n, *bs, *lo, spc)); + file& t (resolve_library (n, *bs, *lo, spc)); + 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 @@ -1107,116 +1186,148 @@ namespace build2 // if (t.path ().empty ()) fail << "target " << t << " is out of date" << - info << "mentioned in " << var.name << " of target " << l << + info << "mentioned in *.export.libs of target " << l << info << "is it a prerequisite of " << l << "?"; - args.push_back (relative (t.path ()).string ()); + proc_lib (t.path ()); } - } - }; - - // @@ Should we also pick one based on cc.type? And also *.poptions in - // compile? Feels right. - // - append_options (args, l, c_export_loptions); - append (c_export_libs); - append_options (args, l, x_export_loptions); - append (x_export_libs); - } - } + // Process it recursively. + // + process_libraries ( + t, t.is_a (), iface_only, proc_lib, proc_opt); + } + } + }; - void link:: - hash_libraries (sha256& cs, file& l, bool la) const - { - for (target* p: l.prerequisite_targets) + // Process libraries from *.libs (of type strings). + // + auto proc_imp = [&proc_lib] (const lookup& lu) { - bool a; - file* f; + const strings* ns (cast_null (lu)); + if (ns == nullptr || ns->empty ()) + return; - if ((a = (f = p->is_a ()) != nullptr) - || (f = p->is_a ()) != nullptr) + for (const string& n: *ns) { - if (la) - cs.append (f->path ().string ()); - - hash_libraries (cs, *f, a); + // This is something like -lpthread or shell32.lib so should be a + // valid path. + // + proc_lib (path (n)); } - } + }; + + // 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; - if (la) + // If all we know it's a C-common library, then in both cases we only + // look for cc.export.libs. + // + if (*t == "cc") { - // See what type of library this is (C, C++, etc). Use it do decide - // which x.libs variable name. If it is not a C-common library, then - // it probably doesn't have cc.libs either. - // - if (const string* t = cast_null (l.vars[c_type])) - { - // @@ Shouldn't, ideally, we filter loptions to only include -L? - // - hash_options (cs, l, c_loptions); - hash_options (cs, l, c_libs); - - hash_options (cs, - l, - *t == x ? x_loptions : var_pool[*t + ".loptions"]); - hash_options (cs, - l, - *t == x ? x_libs : var_pool[*t + ".libs"]); - } + proc_opt (l, *t, true, true); + if (c_e_libs) proc_int (c_e_libs); } else { - scope* bs (nullptr); // Resolve lazily. - optional lo; // Calculate lazily. - optional spc; // Extract lazily. + bool same (*t == x); // Same as us. + auto& vp (var_pool); - auto hash = [&cs, &l, &bs, &lo, &spc, this] (const variable& var) + if (la && !iface_only) { - const names* ns (cast_null (l[var])); - if (ns == nullptr || ns->empty ()) - return; + // Static: as discussed above, here we can have two situations: + // explicit export or default export. + // + if (c_e_libs.defined () || x_e_libs.defined ()) + { + // NOTE: should this not be from l.vars rather than l? Or perhaps + // we can assume non-common values will be set on libs{}/liba{}. + // + proc_opt (l, *t, true, true); + if (c_e_libs) proc_int (c_e_libs); - for (const name& n: *ns) + proc_opt (l, *t, false, true); + if (x_e_libs) proc_int (x_e_libs); + } + else { - if (n.simple ()) - cs.append (n.value); - else - { - if (bs == nullptr) - bs = &l.base_scope (); + // 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. + // + proc_opt (l, *t, true, false); + if (proc_lib) proc_imp (l[c_libs]); + + proc_opt (l, *t, false, false); + if (proc_lib) proc_imp (l[same ? x_libs : vp[*t + ".libs"]]); + } + } + else + { + // Shared or iface_only: only add *.export.* (interface + // dependencies). + // + proc_opt (l, *t, true, true); + if (c_e_libs) proc_int (c_e_libs); + + proc_opt (l, *t, false, true); + if (x_e_libs) proc_int (x_e_libs); + } + } + } - if (!lo) - lo = link_order (*bs, otype::s); // We know it's libs{}. + void link:: + append_libraries (strings& args, file& l, bool la) const + { + auto lib = [&args] (const path& l) + { + args.push_back (relative (l).string ()); + }; - file& t (resolve_library (n, *bs, *lo, spc)); + auto opt = [&args, this] (file& l, const string& t, bool com, bool exp) + { + const variable& var ( + com + ? (exp ? c_export_loptions : c_loptions) + : (t == x + ? (exp ? x_export_loptions : x_loptions) + : var_pool[t + (exp ? ".export.loptions" : ".loptions")])); + + append_options (args, l, var); + }; - // 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 (t.path ().empty ()) - fail << "target " << t << " is out of date" << - info << "mentioned in " << var.name << " of target " << l << - info << "is it a prerequisite of " << l << "?"; + process_libraries (l, la, false, lib, opt); + } - cs.append (t.path ().string ()); - } - } - }; + void link:: + hash_libraries (sha256& cs, file& l, bool la) const + { + auto lib = [&cs] (const path& l) + { + cs.append (l.string ()); + }; - hash_options (cs, l, c_export_loptions); - hash (c_export_libs); + auto opt = [&cs, this] (file& l, const string& t, bool com, bool exp) + { + const variable& var ( + com + ? (exp ? c_export_loptions : c_loptions) + : (t == x + ? (exp ? x_export_loptions : x_loptions) + : var_pool[t + (exp ? ".export.loptions" : ".loptions")])); + + hash_options (cs, l, var); + }; - hash_options (cs, l, x_export_loptions); - hash (x_export_libs); - } + process_libraries (l, la, false, lib, opt); } - // The name can be a simple value (e.g., -lpthread or shell32.lib), an - // absolute target name (e.g., /tmp/libfoo/lib{foo}) or a potentially - // project-qualified relative target name (e.g., libfoo%lib{foo}). + // The name can be an absolute target name (e.g., /tmp/libfoo/lib{foo}) or + // a potentially project-qualified relative target name (e.g., + // libfoo%lib{foo}). // // Note that the scope, search paths, and the link order should all be // derived from the library target that mentioned this name. This way we @@ -1257,7 +1368,7 @@ namespace build2 dir_path out; prerequisite_key pk {n.proj, {tt, &n.dir, &out, &n.value, ext}, &s}; - xt = search_library (spc, pk); + xt = search_library (spc, pk, lo); if (xt == nullptr) { -- cgit v1.1