aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-09-05 11:35:00 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-09-05 11:41:05 +0200
commitda863cd132e6d6024707027459b371c2bff9ca71 (patch)
tree7500f30cb662fbe01ad8e4dde858e45ca141b948 /build2
parent9c0dc1f4957420688cf2c1afe79fa2f53f2a6abf (diff)
Adjust pkg-config logic to cover binless libraries
Diffstat (limited to 'build2')
-rw-r--r--build2/cc/common.cxx64
-rw-r--r--build2/cc/common.hxx37
-rw-r--r--build2/cc/pkgconfig.cxx264
3 files changed, 234 insertions, 131 deletions
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index 23c37ff..b20c2d8 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -532,7 +532,7 @@ namespace build2
bool l (p.is_a<lib> ());
const optional<string>& ext (l ? nullopt : p.tk.ext); // Only liba/libs.
- // Then figure out what we need to search for.
+ // First figure out what we need to search for.
//
const string& name (*p.tk.name);
@@ -613,10 +613,12 @@ namespace build2
liba* a (nullptr);
libs* s (nullptr);
+ pair<path, path> pc; // pkg-config .pc file paths.
+
path f; // Reuse the buffer.
const dir_path* pd (nullptr);
- auto search =[&a, &s,
+ auto search =[&a, &s, &pc,
&an, &ae,
&sn, &se,
&name, ext,
@@ -738,6 +740,39 @@ namespace build2
a = msvc_search_static (ld, d, p, exist);
}
+ // Look for binary-less libraries via pkg-config .pc files. Note that
+ // it is possible we have already found one of them as binfull but the
+ // other is binless.
+ //
+ {
+ bool na (a == nullptr && !an.empty ()); // Need static.
+ bool ns (s == nullptr && !sn.empty ()); // Need shared.
+
+ if (na || ns)
+ {
+ pair<path, path> r (pkgconfig_search (d, p.proj, name));
+
+ if (na && !r.first.empty ())
+ {
+ insert_library (a, name, d, nullopt, exist, trace);
+ a->mtime (timestamp_unreal);
+ a->path (empty_path);
+ }
+
+ if (ns && !r.second.empty ())
+ {
+ insert_library (s, name, d, nullopt, exist, trace);
+ s->mtime (timestamp_unreal);
+ s->path (empty_path);
+ }
+
+ // Only keep these .pc paths if we found anything via them.
+ //
+ if ((na && a != nullptr) || (ns && s != nullptr))
+ pc = move (r);
+ }
+ }
+
return a != nullptr || s != nullptr;
};
@@ -890,19 +925,24 @@ namespace build2
if (ll && (a != nullptr || s != nullptr))
{
- // Try to extract library information from pkg-config. We 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.
+ // Try to extract library information from pkg-config. We only add the
+ // default macro if we could not extract more precise information. The
+ // idea is that in .pc files that we generate, we copy those macros
+ // (or custom ones) from *.export.poptions.
//
- if (!pkgconfig_load (act, *p.scope,
- *lt, a, s,
- p.proj, name,
- *pd, sysd, *usrd))
+ if (pc.first.empty () && pc.second.empty ())
{
- if (a != nullptr) add_macro (*a, "STATIC");
- if (s != nullptr) add_macro (*s, "SHARED");
+ if (!pkgconfig_load (act, *p.scope,
+ *lt, a, s,
+ p.proj, name,
+ *pd, sysd, *usrd))
+ {
+ if (a != nullptr) add_macro (*a, "STATIC");
+ if (s != nullptr) add_macro (*s, "SHARED");
+ }
}
+ else
+ pkgconfig_load (act, *p.scope, *lt, a, s, pc, *pd, sysd, *usrd);
}
// If we have the lock (meaning this is the first time), set the
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 952e383..2a1a2dd 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -287,15 +287,6 @@ namespace build2
dir_paths
extract_library_dirs (const scope&) const;
- bool
- pkgconfig_load (action, const scope&,
- bin::lib&, bin::liba*, bin::libs*,
- const optional<project_name>&,
- const string&,
- const dir_path&,
- const dir_paths&,
- const dir_paths&) const; // pkgconfig.cxx
-
// Alternative search logic for VC (msvc.cxx).
//
bin::liba*
@@ -310,6 +301,34 @@ namespace build2
const prerequisite_key&,
bool existing) const;
+ // The pkg-config file searching and loading (pkgconfig.cxx)
+ //
+ using pkgconfig_callback = function<bool (dir_path&& d)>;
+
+ bool
+ pkgconfig_search (const dir_path&, const pkgconfig_callback&) const;
+
+ pair<path, path>
+ pkgconfig_search (const dir_path&,
+ const optional<project_name>&,
+ const string&) const;
+
+ void
+ pkgconfig_load (action, const scope&,
+ bin::lib&, bin::liba*, bin::libs*,
+ const pair<path, path>&,
+ const dir_path&,
+ const dir_paths&,
+ const dir_paths&) const;
+
+ bool
+ pkgconfig_load (action, const scope&,
+ bin::lib&, bin::liba*, bin::libs*,
+ const optional<project_name>&,
+ const string&,
+ const dir_path&,
+ const dir_paths&,
+ const dir_paths&) const;
};
}
}
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index 795e5d7..9a4f002 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -450,68 +450,48 @@ namespace build2
// .pc files, always returning false (see above for the reasoning).
//
#ifndef BUILD2_BOOTSTRAP
+
+ // Iterate over pkgconf directories that correspond to the specified
+ // library directory, passing them to the callback function, until the
+ // function returns false.
+ //
bool common::
- pkgconfig_load (action a,
- const scope& s,
- lib& lt,
- liba* at,
- libs* st,
- const optional<project_name>& proj,
- const string& stem,
- const dir_path& libd,
- const dir_paths& top_sysd,
- const dir_paths& top_usrd) const
+ pkgconfig_search (const dir_path& d, const pkgconfig_callback& f) const
{
- tracer trace (x, "pkgconfig_load");
-
- assert (at != nullptr || st != nullptr);
+ dir_path pd (d);
- // Iterate over pkgconf directories that correspond to the specified
- // library's directory, passing them to the callback function, until
- // the function returns false.
- //
- // First always check the pkgconfig/ subdirectory in this library's
+ // First always check the pkgconfig/ subdirectory in this library
// directory. Even on platforms where this is not the canonical place,
// .pc files of autotools-based packages installed by the user often
// still end up there.
//
- using callback = function<bool (dir_path&& d)>;
- auto pkgconf_dir = [this] (const dir_path& d, const callback& f) -> bool
- {
- dir_path pd (d);
- if (exists (pd /= "pkgconfig") && !f (move (pd)))
- return false;
-
- // Platform-specific locations.
- //
- if (tsys == "freebsd")
- {
- // On FreeBSD .pc files go to libdata/pkgconfig/, not lib/pkgconfig/.
- //
- pd = d;
- if (exists (((pd /= "..") /= "libdata") /= "pkgconfig") &&
- !f (move (pd)))
- return false;
- }
-
- return true;
- };
+ if (exists (pd /= "pkgconfig") && !f (move (pd)))
+ return false;
- // Same as above but iterate over pkgconfig directories for multiple
- // library directories.
+ // Platform-specific locations.
//
- auto pkgconf_dirs = [&pkgconf_dir] (const dir_paths& ds,
- const callback& f) -> bool
+ if (tsys == "freebsd")
{
- for (const auto& d: ds)
- {
- if (!pkgconf_dir (d, f))
- return false;
- }
+ // On FreeBSD .pc files go to libdata/pkgconfig/, not lib/pkgconfig/.
+ //
+ pd = d;
+ if (exists (((pd /= "..") /= "libdata") /= "pkgconfig") &&
+ !f (move (pd)))
+ return false;
+ }
- return true;
- };
+ return true;
+ }
+ // Search for the .pc files in the pkgconf directories that correspond to
+ // the specified library directory. If found, return static (first) and
+ // shared (second) library .pc files.
+ //
+ pair<path, path> common::
+ pkgconfig_search (const dir_path& libd,
+ const optional<project_name>& proj,
+ const string& stem) const
+ {
// When it comes to looking for .pc files we have to decide where to
// search (which directory(ies)) as well as what to search for (which
// names). Suffix is our ".shared" or ".static" extension.
@@ -521,10 +501,10 @@ namespace build2
{
path f;
- // See if there is a corresponding .pc file. About half of them called
- // foo.pc and half libfoo.pc (and one of the pkg-config's authors
- // suggests that some of you should call yours foolib.pc, just to keep
- // things interesting, you know).
+ // See if there is a corresponding .pc file. About half of them are
+ // called foo.pc and half libfoo.pc (and one of the pkg-config's
+ // authors suggests that some of you should call yours foolib.pc, just
+ // to keep things interesting, you know).
//
// Given the (general) import in the form <proj>%lib{<stem>}, we will
// first try lib<stem>.pc, then <stem>.pc. Maybe it also makes sense
@@ -561,39 +541,81 @@ namespace build2
return path ();
};
- auto search = [&libd, &search_dir, &pkgconf_dir] () -> pair<path, path>
- {
- pair<path, path> r;
+ pair<path, path> r;
- // Return false (and so stop to iterate) if .pc file is found.
+ // Return false (and so stop the iteration) if a .pc file is found.
+ //
+ // Note that we rely on the "small function object" optimization here.
+ //
+ auto check = [&r, &search_dir] (dir_path&& d) -> bool
+ {
+ // First look for static/shared-specific files.
//
- // Note that we rely on "small function object" optimization here.
+ r.first = search_dir (d, ".static");
+ r.second = search_dir (d, ".shared");
+
+ if (!r.first.empty () || !r.second.empty ())
+ return false;
+
+ // Then the common.
//
- auto check = [&r, &search_dir] (dir_path&& d) -> bool
- {
- // First look for static/shared-specific files.
- //
- r.first = search_dir (d, ".static");
- r.second = search_dir (d, ".shared");
+ r.first = r.second = search_dir (d, string ());
+ return r.first.empty ();
+ };
- if (!r.first.empty () || !r.second.empty ())
- return false;
+ pkgconfig_search (libd, check);
+ return r;
+ };
- // Then the common.
- //
- r.first = r.second = search_dir (d, string ());
- return r.first.empty ();
- };
+ bool common::
+ pkgconfig_load (action a,
+ const scope& s,
+ lib& lt,
+ liba* at,
+ libs* st,
+ const optional<project_name>& proj,
+ const string& stem,
+ const dir_path& libd,
+ const dir_paths& top_sysd,
+ const dir_paths& top_usrd) const
+ {
+ assert (at != nullptr || st != nullptr);
- pkgconf_dir (libd, check);
- return r;
- };
+ pair<path, path> p (pkgconfig_search (libd, proj, stem));
+
+ if (p.first.empty () && p.second.empty ())
+ return false;
+
+ pkgconfig_load (a, s, lt, at, st, p, libd, top_sysd, top_usrd);
+ return true;
+ }
+
+ void common::
+ pkgconfig_load (action a,
+ const scope& s,
+ lib& lt,
+ liba* at,
+ libs* st,
+ const pair<path, path>& paths,
+ const dir_path& libd,
+ const dir_paths& top_sysd,
+ const dir_paths& top_usrd) const
+ {
+ tracer trace (x, "pkgconfig_load");
+
+ assert (at != nullptr || st != nullptr);
+
+ const path& ap (paths.first);
+ const path& sp (paths.second);
+
+ assert (!ap.empty () || !sp.empty ());
// Extract --cflags and set them as lib?{}:export.poptions. Note that we
// still pass --static in case this is pkgconf which has Cflags.private.
//
- auto parse_cflags =
- [&trace, this] (target& t, const pkgconf& pc, bool la)
+ auto parse_cflags = [&trace, this] (target& t,
+ const pkgconf& pc,
+ bool la)
{
strings pops;
@@ -647,22 +669,26 @@ namespace build2
// Parse --libs into loptions/libs (interface and implementation). If
// ps is not NULL, add each resolves library target as a prerequisite.
//
- auto parse_libs = [a, &s, top_sysd, this]
- (target& t, const pkgconf& pc, bool la, prerequisites* ps)
+ auto parse_libs = [a, &s, top_sysd, this] (target& t,
+ bool binless,
+ const pkgconf& pc,
+ bool la,
+ prerequisites* ps)
{
strings lops;
vector<name> libs;
// Normally we will have zero or more -L's followed by one or more
- // -l's, with the first one being the library itself. But sometimes
- // we may have other linker options, for example, -Wl,... or
- // -pthread. It's probably a bad idea to ignore them. Also,
- // theoretically, we could have just the library name/path.
+ // -l's, with the first one being the library itself, unless the
+ // library is binless. But sometimes we may have other linker options,
+ // for example, -Wl,... or -pthread. It's probably a bad idea to
+ // ignore them. Also, theoretically, we could have just the library
+ // name/path.
//
// The tricky part, of course, is to know whether what follows after
// an option we don't recognize is its argument or another option or
- // library. What we do at the moment is stop recognizing just
- // library names (without -l) after seeing an unknown option.
+ // library. What we do at the moment is stop recognizing just library
+ // names (without -l) after seeing an unknown option.
//
bool arg (false), first (true), known (true), have_L;
for (auto& o: pc.libs (la))
@@ -693,14 +719,17 @@ namespace build2
if ((known && o[0] != '-') ||
(n > 2 && o[0] == '-' && o[1] == 'l'))
{
- // First one is the library itself, which we skip. Note that we
- // don't verify this and theoretically it could be some other
- // library, but we haven't encountered such a beast yet.
+ // Unless binless, the first one is the library itself, which we
+ // skip. Note that we don't verify this and theoretically it could
+ // be some other library, but we haven't encountered such a beast
+ // yet.
//
if (first)
{
first = false;
- continue;
+
+ if (!binless)
+ continue;
}
// @@ If by some reason this is the library itself (doesn't go
@@ -739,7 +768,7 @@ namespace build2
return r;
};
- if (first)
+ if (first && !binless)
fail << "library expected in '" << lflags () << "'" <<
info << "while parsing pkg-config --libs " << pc.path;
@@ -1015,13 +1044,6 @@ namespace build2
}
};
- pair<path, path> pp (search ());
- const path& ap (pp.first);
- const path& sp (pp.second);
-
- if (ap.empty () && sp.empty ())
- return false;
-
// For now we only populate prerequisites for lib{}. To do it for
// liba{} would require weeding out duplicates that are already in
// lib{}.
@@ -1035,7 +1057,7 @@ namespace build2
//
dir_paths pc_dirs;
- // Note that we rely on "small function object" optimization here.
+ // Note that we rely on the "small function object" optimization here.
//
auto add_pc_dir = [&pc_dirs] (dir_path&& d) -> bool
{
@@ -1043,9 +1065,9 @@ namespace build2
return true;
};
- pkgconf_dir (libd, add_pc_dir);
- pkgconf_dirs (top_usrd, add_pc_dir);
- pkgconf_dirs (top_sysd, add_pc_dir);
+ pkgconfig_search (libd, add_pc_dir);
+ for (const dir_path& d: top_usrd) pkgconfig_search (d, add_pc_dir);
+ for (const dir_path& d: top_sysd) pkgconfig_search (d, add_pc_dir);
// First sort out the interface dependencies (which we are setting on
// lib{}). If we have the shared .pc variant, then we use that.
@@ -1053,6 +1075,7 @@ namespace build2
// the saving logic).
//
pkgconf& ipc (sp.empty () ? apc : spc); // Interface package info.
+ bool ibl ((sp.empty () ? st->path () : at->path ()).empty ()); // Binless.
bool pa (at != nullptr && !ap.empty ());
if (pa || sp.empty ())
@@ -1062,12 +1085,12 @@ namespace build2
if (ps || ap.empty ())
spc = pkgconf (sp, pc_dirs, sys_lib_dirs, sys_inc_dirs);
- parse_libs (lt, ipc, false, &prs);
+ parse_libs (lt, ibl, ipc, false, &prs);
if (pa)
{
parse_cflags (*at, apc, true);
- parse_libs (*at, apc, true, nullptr);
+ parse_libs (*at, at->path ().empty (), apc, true, nullptr);
}
if (ps)
@@ -1090,12 +1113,18 @@ namespace build2
// file rule won't match.
//
lt.mtime (file_mtime (ipc.path));
-
- return true;
}
#else
+ pair<path, path> common::
+ pkgconfig_search (const dir_path&,
+ const optional<project_name>&,
+ const string&) const
+ {
+ return pair<path, path> ();
+ }
+
bool common::
pkgconfig_load (action,
const scope&,
@@ -1111,6 +1140,20 @@ namespace build2
return false;
}
+ void common::
+ pkgconfig_load (action,
+ const scope&,
+ lib&,
+ liba*,
+ libs*,
+ const pair<path, path>&,
+ const dir_path&,
+ const dir_paths&,
+ const dir_paths&) const
+ {
+ assert (false); // Should never be called.
+ }
+
#endif
void link_rule::
@@ -1132,9 +1175,7 @@ namespace build2
using install::resolve_dir;
dir_path idir (resolve_dir (l, cast<dir_path> (l["install.include"])));
- dir_path ldir (binless
- ? dir_path ()
- : resolve_dir (l, cast<dir_path> (l["install.lib"])));
+ dir_path ldir (resolve_dir (l, cast<dir_path> (l["install.lib"])));
if (verb >= 2)
text << "cat >" << p;
@@ -1258,9 +1299,12 @@ namespace build2
// the second position, not first. Gets even trickier with
// Libs.private split.
//
- if (!binless)
{
os << "Libs:";
+
+ // While we don't need it for a binless library itselt, it may be
+ // necessary to resolve its binfull dependencies.
+ //
os << " -L" << escape (ldir.string ());
// Now process ourselves as if we were being linked to something (so
@@ -1317,7 +1361,7 @@ namespace build2
process_libraries (a, bs, li, sys_lib_dirs,
l, la, 0, // Link flags.
- imp, lib, opt, true);
+ imp, lib, opt, !binless);
os << endl;
if (la)