aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/pkgconfig.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/pkgconfig.cxx')
-rw-r--r--libbuild2/cc/pkgconfig.cxx489
1 files changed, 28 insertions, 461 deletions
diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx
index 32699a9..69e6916 100644
--- a/libbuild2/cc/pkgconfig.cxx
+++ b/libbuild2/cc/pkgconfig.cxx
@@ -1,13 +1,6 @@
// file : libbuild2/cc/pkgconfig.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-// In order not to complicate the bootstrap procedure with libpkgconf building
-// exclude functionality that involves reading of .pc files.
-//
-#ifndef BUILD2_BOOTSTRAP
-# include <libpkgconf/libpkgconf.h>
-#endif
-
#include <libbuild2/scope.hxx>
#include <libbuild2/target.hxx>
#include <libbuild2/context.hxx>
@@ -25,441 +18,12 @@
#include <libbuild2/cc/utility.hxx>
#include <libbuild2/cc/common.hxx>
+#include <libbuild2/cc/pkgconfig.hxx>
#include <libbuild2/cc/compile-rule.hxx>
#include <libbuild2/cc/link-rule.hxx>
-#ifndef BUILD2_BOOTSTRAP
-
-// Note that the libpkgconf library doesn't provide the version macro that we
-// could use to compile the code conditionally against different API versions.
-// Thus, we need to sense the pkgconf_client_new() function signature
-// ourselves to call it properly.
-//
-namespace details
-{
- void*
- pkgconf_cross_personality_default (); // Never called.
-}
-
-using namespace details;
-
-template <typename H>
-static inline pkgconf_client_t*
-call_pkgconf_client_new (pkgconf_client_t* (*f) (H, void*),
- H error_handler,
- void* error_handler_data)
-{
- return f (error_handler, error_handler_data);
-}
-
-template <typename H, typename P>
-static inline pkgconf_client_t*
-call_pkgconf_client_new (pkgconf_client_t* (*f) (H, void*, P),
- H error_handler,
- void* error_handler_data)
-{
- return f (error_handler,
- error_handler_data,
- ::pkgconf_cross_personality_default ());
-}
-
-#endif
-
-using namespace std;
-using namespace butl;
-
namespace build2
{
-#ifndef BUILD2_BOOTSTRAP
-
- // Load package information from a .pc file. Filter out the -I/-L options
- // that refer to system directories. This makes sure all the system search
- // directories are "pushed" to the back which minimizes the chances of
- // picking up wrong (e.g., old installed version) header/library.
- //
- // Note that the prerequisite package .pc files search order is as follows:
- //
- // - in directory of the specified file
- // - in pc_dirs directories (in the natural order)
- //
- class pkgconf
- {
- public:
- using path_type = build2::path;
-
- path_type path;
-
- public:
- explicit
- pkgconf (path_type,
- const dir_paths& pc_dirs,
- const dir_paths& sys_hdr_dirs,
- const dir_paths& sys_lib_dirs);
-
- // Create a special empty object. Querying package information on such
- // an object is illegal.
- //
- pkgconf () = default;
-
- ~pkgconf ()
- {
- if (client_ != nullptr) // Not empty.
- free ();
- }
-
- // Movable-only type.
- //
- pkgconf (pkgconf&& p)
- : path (move (p.path)),
- client_ (p.client_),
- pkg_ (p.pkg_)
- {
- p.client_ = nullptr;
- p.pkg_ = nullptr;
- }
-
- pkgconf&
- operator= (pkgconf&& p)
- {
- if (this != &p)
- {
- if (client_ != nullptr) // Not empty.
- free ();
-
- path = move (p.path);
- client_ = p.client_;
- pkg_ = p.pkg_;
-
- p.client_ = nullptr;
- p.pkg_ = nullptr;
- }
- return *this;
- }
-
- pkgconf (const pkgconf&) = delete;
- pkgconf& operator= (const pkgconf&) = delete;
-
- strings
- cflags (bool static_) const;
-
- strings
- libs (bool static_) const;
-
- optional<string>
- variable (const char*) const;
-
- optional<string>
- variable (const string& s) const {return variable (s.c_str ());}
-
- private:
- void
- free ();
-
- // Keep them as raw pointers not to deal with API thread-unsafety in
- // deleters and introducing additional mutex locks.
- //
- pkgconf_client_t* client_ = nullptr;
- pkgconf_pkg_t* pkg_ = nullptr;
- };
-
- // Currently the library is not thread-safe, even on the pkgconf_client_t
- // level (see issue #128 for details). While it seems that the obvious
- // thread-safety issues are fixed, the default personality initialization,
- // which is still not thread-safe. So let's keep the mutex for now not to
- // introduce potential issues.
- //
- static mutex pkgconf_mutex;
-
- // The package dependency traversal depth limit.
- //
- static const int pkgconf_max_depth = 100;
-
- // Normally the error_handler() callback can be called multiple times to
- // report a single error (once per message line), to produce a multi-line
- // message like this:
- //
- // Package foo was not found in the pkg-config search path.\n
- // Perhaps you should add the directory containing `foo.pc'\n
- // to the PKG_CONFIG_PATH environment variable\n
- // Package 'foo', required by 'bar', not found\n
- //
- // For the above example callback will be called 4 times. To suppress all
- // the junk we will use PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS to get just:
- //
- // Package 'foo', required by 'bar', not found\n
- //
- // Also disable merging options like -framework into a single fragment, if
- // possible.
- //
- static const int pkgconf_flags =
- PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS
- | PKGCONF_PKG_PKGF_SKIP_PROVIDES
-#ifdef PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS
- | PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS
-#endif
- ;
-
- static bool
- pkgconf_error_handler (const char* msg, const pkgconf_client_t*, const void*)
- {
- error << runtime_error (msg); // Sanitize the message (trailing dot, etc).
- return true;
- }
-
- // Deleters. Note that they are thread-safe.
- //
- struct fragments_deleter
- {
- void operator() (pkgconf_list_t* f) const {pkgconf_fragment_free (f);}
- };
-
- // Convert fragments to strings. Skip the -I/-L options that refer to system
- // directories.
- //
- static strings
- to_strings (const pkgconf_list_t& frags,
- char type,
- const pkgconf_list_t& sysdirs)
- {
- assert (type == 'I' || type == 'L');
-
- strings r;
-
- auto add = [&r] (const pkgconf_fragment_t* frag)
- {
- string s;
- if (frag->type != '\0')
- {
- s += '-';
- s += frag->type;
- }
-
- s += frag->data;
- r.push_back (move (s));
- };
-
- // Option that is separated from its value, for example:
- //
- // -I /usr/lib
- //
- const pkgconf_fragment_t* opt (nullptr);
-
- pkgconf_node_t *node;
- PKGCONF_FOREACH_LIST_ENTRY(frags.head, node)
- {
- auto frag (static_cast<const pkgconf_fragment_t*> (node->data));
-
- // Add the separated option and directory, unless the latest is a system
- // one.
- //
- if (opt != nullptr)
- {
- // Note that we should restore the directory path that was
- // (mis)interpreted as an option, for example:
- //
- // -I -Ifoo
- //
- // In the above example option '-I' is followed by directory '-Ifoo',
- // which is represented by libpkgconf library as fragment 'foo' with
- // type 'I'.
- //
- if (!pkgconf_path_match_list (
- frag->type == '\0'
- ? frag->data
- : (string ({'-', frag->type}) + frag->data).c_str (),
- &sysdirs))
- {
- add (opt);
- add (frag);
- }
-
- opt = nullptr;
- continue;
- }
-
- // Skip the -I/-L option if it refers to a system directory.
- //
- if (frag->type == type)
- {
- // The option is separated from a value, that will (presumably) follow.
- //
- if (*frag->data == '\0')
- {
- opt = frag;
- continue;
- }
-
- if (pkgconf_path_match_list (frag->data, &sysdirs))
- continue;
- }
-
- add (frag);
- }
-
- if (opt != nullptr) // Add the dangling option.
- add (opt);
-
- return r;
- }
-
- // Note that some libpkgconf functions can potentially return NULL, failing
- // to allocate the required memory block. However, we will not check the
- // returned value for NULL as the library doesn't do so, prior to filling the
- // allocated structures. So such a code complication on our side would be
- // useless. Also, for some functions the NULL result has a special semantics,
- // for example "not found".
- //
- pkgconf::
- pkgconf (path_type p,
- const dir_paths& pc_dirs,
- const dir_paths& sys_lib_dirs,
- const dir_paths& sys_hdr_dirs)
- : path (move (p))
- {
- auto add_dirs = [] (pkgconf_list_t& dir_list,
- const dir_paths& dirs,
- bool suppress_dups,
- bool cleanup = false)
- {
- if (cleanup)
- {
- pkgconf_path_free (&dir_list);
- dir_list = PKGCONF_LIST_INITIALIZER;
- }
-
- for (const auto& d: dirs)
- pkgconf_path_add (d.string ().c_str (), &dir_list, suppress_dups);
- };
-
- mlock l (pkgconf_mutex);
-
- // Initialize the client handle.
- //
- unique_ptr<pkgconf_client_t, void (*) (pkgconf_client_t*)> c (
- call_pkgconf_client_new (&pkgconf_client_new,
- pkgconf_error_handler,
- nullptr /* handler_data */),
- [] (pkgconf_client_t* c) {pkgconf_client_free (c);});
-
- pkgconf_client_set_flags (c.get (), pkgconf_flags);
-
- // Note that the system header and library directory lists are
- // automatically pre-filled by the pkgconf_client_new() call (see above).
- // We will re-create these lists from scratch.
- //
- add_dirs (c->filter_libdirs,
- sys_lib_dirs,
- false /* suppress_dups */,
- true /* cleanup */);
-
- add_dirs (c->filter_includedirs,
- sys_hdr_dirs,
- false /* suppress_dups */,
- true /* cleanup */);
-
- // Note that the loaded file directory is added to the (yet empty) search
- // list. Also note that loading of the prerequisite packages is delayed
- // until flags retrieval, and their file directories are not added to the
- // search list.
- //
- pkg_ = pkgconf_pkg_find (c.get (), path.string ().c_str ());
-
- if (pkg_ == nullptr)
- fail << "package '" << path << "' not found or invalid";
-
- // Add the .pc file search directories.
- //
- assert (c->dir_list.length == 1); // Package file directory (see above).
- add_dirs (c->dir_list, pc_dirs, true /* suppress_dups */);
-
- client_ = c.release ();
- }
-
- void pkgconf::
- free ()
- {
- assert (pkg_ != nullptr);
-
- mlock l (pkgconf_mutex);
- pkgconf_pkg_unref (client_, pkg_);
- pkgconf_client_free (client_);
- }
-
- strings pkgconf::
- cflags (bool stat) const
- {
- assert (client_ != nullptr); // Must not be empty.
-
- mlock l (pkgconf_mutex);
-
- pkgconf_client_set_flags (
- client_,
- pkgconf_flags |
-
- // Walk through the private package dependencies (Requires.private)
- // besides the public ones while collecting the flags. Note that we do
- // this for both static and shared linking.
- //
- PKGCONF_PKG_PKGF_SEARCH_PRIVATE |
-
- // Collect flags from Cflags.private besides those from Cflags for the
- // static linking.
- //
- (stat
- ? PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS
- : 0));
-
- pkgconf_list_t f = PKGCONF_LIST_INITIALIZER; // Aggregate initialization.
- int e (pkgconf_pkg_cflags (client_, pkg_, &f, pkgconf_max_depth));
-
- if (e != PKGCONF_PKG_ERRF_OK)
- throw failed (); // Assume the diagnostics is issued.
-
- unique_ptr<pkgconf_list_t, fragments_deleter> fd (&f); // Auto-deleter.
- return to_strings (f, 'I', client_->filter_includedirs);
- }
-
- strings pkgconf::
- libs (bool stat) const
- {
- assert (client_ != nullptr); // Must not be empty.
-
- mlock l (pkgconf_mutex);
-
- pkgconf_client_set_flags (
- client_,
- pkgconf_flags |
-
- // Additionally collect flags from the private dependency packages
- // (see above) and from the Libs.private value for the static linking.
- //
- (stat
- ? PKGCONF_PKG_PKGF_SEARCH_PRIVATE |
- PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS
- : 0));
-
- pkgconf_list_t f = PKGCONF_LIST_INITIALIZER; // Aggregate initialization.
- int e (pkgconf_pkg_libs (client_, pkg_, &f, pkgconf_max_depth));
-
- if (e != PKGCONF_PKG_ERRF_OK)
- throw failed (); // Assume the diagnostics is issued.
-
- unique_ptr<pkgconf_list_t, fragments_deleter> fd (&f); // Auto-deleter.
- return to_strings (f, 'L', client_->filter_libdirs);
- }
-
- optional<string> pkgconf::
- variable (const char* name) const
- {
- assert (client_ != nullptr); // Must not be empty.
-
- mlock l (pkgconf_mutex);
- const char* r (pkgconf_tuple_find (client_, &pkg_->vars, name));
- return r != nullptr ? optional<string> (r) : nullopt;
- }
-
-#endif
-
namespace cc
{
using namespace bin;
@@ -518,6 +82,11 @@ namespace build2
return make_pair (r, d);
}
+ // In order not to complicate the bootstrap procedure with libpkg-config
+ // building, exclude functionality that involves reading of .pc files.
+ //
+#ifndef BUILD2_BOOTSTRAP
+
// Try to find a .pc file in the pkgconfig/ subdirectory of libd, trying
// several names derived from stem. If not found, return false. If found,
// load poptions, loptions, libs, and modules, set the corresponding
@@ -534,9 +103,8 @@ namespace build2
// Also note that the bootstrapped version of build2 will not search for
// .pc files, always returning false (see above for the reasoning).
//
-#ifndef BUILD2_BOOTSTRAP
- // Derive pkgconf search directories from the specified library search
+ // Derive pkg-config search directories from the specified library search
// directory passing them to the callback function for as long as it
// returns false (e.g., not found). Return true if the callback returned
// true.
@@ -580,8 +148,8 @@ namespace build2
return false;
}
- // Search for the .pc files in the pkgconf directories that correspond to
- // the specified library directory. If found, return static (first) and
+ // Search for the .pc files in the pkg-config directories that correspond
+ // to the specified library directory. If found, return static (first) and
// shared (second) library .pc files. If common is false, then only
// consider our .static/.shared files.
//
@@ -730,11 +298,10 @@ namespace build2
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.
+ // Extract --cflags and set them as lib?{}:export.poptions..
//
auto parse_cflags = [&trace, this] (target& t,
- const pkgconf& pc,
+ const pkgconfig& pc,
bool la)
{
strings pops;
@@ -790,7 +357,7 @@ namespace build2
//
auto parse_libs = [this, act, &s, top_sysd] (target& t,
bool binless,
- const pkgconf& pc,
+ const pkgconfig& pc,
bool la,
prerequisites* ps)
{
@@ -852,13 +419,13 @@ namespace build2
continue;
}
- // @@ If by some reason this is the library itself (doesn't go
- // first or libpkgconf parsed libs in some bizarre way) we will
- // have a dependency cycle by trying to lock its target inside
- // search_library() as by now it is already locked. To be safe
- // we probably shouldn't rely on the position and filter out
- // all occurrences of the library itself (by name?) and
- // complain if none were encountered.
+ // @@ If for some reason this is the library itself (doesn't go
+ // first or libpkg-config parsed libs in some bizarre way) we
+ // will have a dependency cycle by trying to lock its target
+ // inside search_library() as by now it is already locked. To
+ // be safe we probably shouldn't rely on the position and
+ // filter out all occurrences of the library itself (by name?)
+ // and complain if none were encountered.
//
// Note also that the same situation can occur if we have a
// binful library for which we could not find the library
@@ -1221,7 +788,7 @@ namespace build2
// specified target.
//
auto parse_metadata = [&next] (target& t,
- pkgconf& pc,
+ pkgconfig& pc,
const string& md,
bool user)
{
@@ -1316,7 +883,7 @@ namespace build2
// prerequisites.
//
auto parse_modules = [&trace, this,
- &next, &s, &lt] (const pkgconf& pc,
+ &next, &s, &lt] (const pkgconfig& pc,
prerequisites& ps)
{
optional<string> val (pc.variable ("cxx.modules"));
@@ -1417,7 +984,7 @@ namespace build2
// the prerequisites.
//
auto parse_headers = [&trace, this,
- &next, &s, &lt] (const pkgconf& pc,
+ &next, &s, &lt] (const pkgconfig& pc,
const target_type& tt,
const char* lang,
prerequisites& ps)
@@ -1471,8 +1038,8 @@ namespace build2
// Load the information from the pkg-config files.
//
- pkgconf apc;
- pkgconf spc;
+ pkgconfig apc;
+ pkgconfig spc;
// Create the .pc files search directory list.
//
@@ -1492,11 +1059,11 @@ namespace build2
bool pa (at != nullptr && !ap.empty ());
if (pa || sp.empty ())
- apc = pkgconf (ap, pc_dirs, sys_lib_dirs, sys_hdr_dirs);
+ apc = pkgconfig (ap, pc_dirs, sys_lib_dirs, sys_hdr_dirs);
bool ps (st != nullptr && !sp.empty ());
if (ps || ap.empty ())
- spc = pkgconf (sp, pc_dirs, sys_lib_dirs, sys_hdr_dirs);
+ spc = pkgconfig (sp, pc_dirs, sys_lib_dirs, sys_hdr_dirs);
// Load the user metadata if we are in the load phase. Otherwise just
// determine if we have metadata.
@@ -1515,7 +1082,7 @@ namespace build2
//
assert (lt.ctx.phase == run_phase::load);
- pkgconf& ipc (ps ? spc : apc); // As below.
+ pkgconfig& ipc (ps ? spc : apc); // As below.
// Since it's not easy to say if things are the same, we load a copy
// into the group and each member, if any.
@@ -1588,7 +1155,7 @@ namespace build2
// static but extract without the --static option (see also the saving
// logic).
//
- pkgconf& ipc (ps ? spc : apc); // Interface package info.
+ pkgconfig& ipc (ps ? spc : apc); // Interface package info.
bool ipc_meta (ps ? spc_meta : apc_meta);
// For now we only populate prerequisites for lib{}. To do it for