aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-08-26 16:37:16 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-08-26 16:37:16 +0200
commitf0edc0e2b67fa43c4e2410c7d3d8f1841d576749 (patch)
tree25ca1dd8c20649a1a9a35355670a1f5c8b99695c
parente347540d400c552917ca7067c4571f1028f6e1af (diff)
Add pkg-config support for import installed
Redesign library importing/exporting while at it.
-rw-r--r--build2/b.cxx4
-rw-r--r--build2/bin/init.cxx4
-rw-r--r--build2/buildfile140
-rw-r--r--build2/c/init.cxx2
-rw-r--r--build2/cc/common4
-rw-r--r--build2/cc/compile8
-rw-r--r--build2/cc/compile.cxx108
-rw-r--r--build2/cc/init.cxx20
-rw-r--r--build2/cc/link28
-rw-r--r--build2/cc/link.cxx395
-rw-r--r--build2/cc/msvc.cxx1
-rw-r--r--build2/cc/pkgconfig.cxx446
-rw-r--r--build2/cc/utility13
-rw-r--r--build2/cc/utility.cxx40
-rw-r--r--build2/context.txx2
-rw-r--r--build2/cxx/init.cxx2
-rw-r--r--build2/pkgconfig/init37
-rw-r--r--build2/pkgconfig/init.cxx159
-rw-r--r--build2/utility2
-rw-r--r--build2/utility.cxx11
-rw-r--r--tests/import/installed/buildfile4
21 files changed, 1122 insertions, 308 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 81e1b2f..bb4f0fe 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -47,6 +47,7 @@ using namespace std;
#include <build2/cli/init>
#include <build2/test/init>
#include <build2/install/init>
+#include <build2/pkgconfig/init>
using namespace build2;
@@ -228,6 +229,9 @@ main (int argc, char* argv[])
bm["bin.rc.config"] = mf {nullptr, &bin::rc_config_init};
bm["bin.rc"] = mf {nullptr, &bin::rc_init};
+ bm["pkgconfig.config"] = mf {nullptr, &pkgconfig::config_init};
+ bm["pkgconfig"] = mf {nullptr, &pkgconfig::init};
+
bm["cc.core.vars"] = mf {nullptr, &cc::core_vars_init};
bm["cc.core.config"] = mf {nullptr, &cc::core_config_init};
bm["cc.core"] = mf {nullptr, &cc::core_init};
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index 125bc7d..bf7dd69 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -30,8 +30,8 @@ namespace build2
// Default config.bin.*.lib values.
//
static const strings exe_lib {"shared", "static"};
- static const strings liba_lib {"static"};
- static const strings libs_lib {"shared"};
+ static const strings liba_lib {"static", "shared"};
+ static const strings libs_lib {"shared", "static"};
bool
config_init (scope& r,
diff --git a/build2/buildfile b/build2/buildfile
index 3aa515f..ad5a023 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -4,75 +4,77 @@
import libs = libbutl%lib{butl}
-exe{b}: \
- {hxx ixx cxx}{ algorithm } \
- { cxx}{ b } \
- {hxx ixx cxx}{ b-options } \
- {hxx txx cxx}{ context } \
- {hxx cxx}{ depdb } \
- {hxx cxx}{ diagnostics } \
- {hxx cxx}{ dump } \
- {hxx ixx cxx}{ file } \
- {hxx txx cxx}{ filesystem } \
- {hxx cxx}{ lexer } \
- {hxx cxx}{ module } \
- {hxx ixx cxx}{ name } \
- {hxx cxx}{ operation } \
- {hxx cxx}{ parser } \
- {hxx cxx}{ prerequisite } \
- {hxx cxx}{ rule } \
- {hxx }{ rule-map } \
- {hxx cxx}{ scope } \
- {hxx cxx}{ search } \
- {hxx cxx}{ spec } \
- {hxx ixx txx cxx}{ target } \
- {hxx }{ target-key } \
- {hxx }{ target-type } \
- {hxx cxx}{ token } \
- {hxx }{ types } \
- {hxx cxx}{ types-parsers } \
- {hxx ixx txx cxx}{ utility } \
- {hxx ixx txx cxx}{ variable } \
- {hxx }{ version } \
- bin/{hxx cxx}{ guess } \
- bin/{hxx cxx}{ init } \
- bin/{hxx cxx}{ rule } \
- bin/{hxx cxx}{ target } \
- c/{hxx cxx}{ init } \
- c/{hxx }{ target } \
- cc/{hxx }{ common } \
- cc/{hxx cxx}{ compile } \
- cc/{hxx cxx}{ guess } \
- cc/{hxx cxx}{ init } \
- cc/{hxx cxx}{ install } \
- cc/{hxx cxx}{ link } \
- cc/{hxx cxx}{ module } \
- cc/{ cxx}{ msvc } \
- cc/{hxx cxx}{ target } \
- cc/{hxx }{ types } \
- cc/{hxx ixx cxx}{ utility } \
- cc/{ cxx}{ windows-manifest } \
- cc/{ cxx}{ windows-rpath } \
- cli/{hxx cxx}{ init } \
- cli/{hxx cxx}{ rule } \
- cli/{hxx cxx}{ target } \
- config/{hxx cxx}{ init } \
- config/{hxx }{ module } \
- config/{hxx cxx}{ operation } \
- config/{hxx txx cxx}{ utility } \
- cxx/{hxx cxx}{ init } \
- cxx/{hxx cxx}{ target } \
- dist/{hxx cxx}{ init } \
- dist/{hxx cxx}{ operation } \
- dist/{hxx cxx}{ rule } \
-install/{hxx cxx}{ init } \
-install/{hxx cxx}{ operation } \
-install/{hxx cxx}{ rule } \
-install/{hxx }{ utility } \
- test/{hxx cxx}{ init } \
- test/{hxx cxx}{ operation } \
- test/{hxx cxx}{ rule } \
-$libs
+exe{b}: \
+ {hxx ixx cxx}{ algorithm } \
+ { cxx}{ b } \
+ {hxx ixx cxx}{ b-options } \
+ {hxx txx cxx}{ context } \
+ {hxx cxx}{ depdb } \
+ {hxx cxx}{ diagnostics } \
+ {hxx cxx}{ dump } \
+ {hxx ixx cxx}{ file } \
+ {hxx txx cxx}{ filesystem } \
+ {hxx cxx}{ lexer } \
+ {hxx cxx}{ module } \
+ {hxx ixx cxx}{ name } \
+ {hxx cxx}{ operation } \
+ {hxx cxx}{ parser } \
+ {hxx cxx}{ prerequisite } \
+ {hxx cxx}{ rule } \
+ {hxx }{ rule-map } \
+ {hxx cxx}{ scope } \
+ {hxx cxx}{ search } \
+ {hxx cxx}{ spec } \
+ {hxx ixx txx cxx}{ target } \
+ {hxx }{ target-key } \
+ {hxx }{ target-type } \
+ {hxx cxx}{ token } \
+ {hxx }{ types } \
+ {hxx cxx}{ types-parsers } \
+ {hxx ixx txx cxx}{ utility } \
+ {hxx ixx txx cxx}{ variable } \
+ {hxx }{ version } \
+ bin/{hxx cxx}{ guess } \
+ bin/{hxx cxx}{ init } \
+ bin/{hxx cxx}{ rule } \
+ bin/{hxx cxx}{ target } \
+ c/{hxx cxx}{ init } \
+ c/{hxx }{ target } \
+ cc/{hxx }{ common } \
+ cc/{hxx cxx}{ compile } \
+ cc/{hxx cxx}{ guess } \
+ cc/{hxx cxx}{ init } \
+ cc/{hxx cxx}{ install } \
+ cc/{hxx cxx}{ link } \
+ cc/{hxx cxx}{ module } \
+ cc/{ cxx}{ msvc } \
+ cc/{ cxx}{ pkgconfig } \
+ cc/{hxx cxx}{ target } \
+ cc/{hxx }{ types } \
+ cc/{hxx ixx cxx}{ utility } \
+ cc/{ cxx}{ windows-manifest } \
+ cc/{ cxx}{ windows-rpath } \
+ cli/{hxx cxx}{ init } \
+ cli/{hxx cxx}{ rule } \
+ cli/{hxx cxx}{ target } \
+ config/{hxx cxx}{ init } \
+ config/{hxx }{ module } \
+ config/{hxx cxx}{ operation } \
+ config/{hxx txx cxx}{ utility } \
+ cxx/{hxx cxx}{ init } \
+ cxx/{hxx cxx}{ target } \
+ dist/{hxx cxx}{ init } \
+ dist/{hxx cxx}{ operation } \
+ dist/{hxx cxx}{ rule } \
+pkgconfig/{hxx cxx}{ init } \
+ install/{hxx cxx}{ init } \
+ install/{hxx cxx}{ operation } \
+ install/{hxx cxx}{ rule } \
+ install/{hxx }{ utility } \
+ test/{hxx cxx}{ init } \
+ test/{hxx cxx}{ operation } \
+ test/{hxx cxx}{ rule } \
+ $libs
# Pass our compiler target to be used as build2 host.
#
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index be72655..c846ae1 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -228,6 +228,8 @@ namespace build2
cast<string> (r[cm.x_target_system]),
cast<string> (r[cm.x_target_class]),
+ cast_null<process_path> (r["pkgconfig.path"]),
+
c::static_type,
hdr,
inc
diff --git a/build2/cc/common b/build2/cc/common
index ff6841f..c25683c 100644
--- a/build2/cc/common
+++ b/build2/cc/common
@@ -96,6 +96,8 @@ namespace build2
const string& tsys; // x.target.system
const string& tclass; // x.target.class
+ const process_path* pkgconfig; // pkgconfig.path (can be NULL).
+
const target_type& x_src; // Source target type (c{}, cxx{}).
// Array of target types that are considered headers. Keep them in the
@@ -131,6 +133,7 @@ namespace build2
const string& tg,
const string& sys,
const string& class_,
+ const process_path* pkgc,
const target_type& src,
const target_type* const* hdr,
const target_type* const* inc)
@@ -140,6 +143,7 @@ namespace build2
x_install (install),
x_uninstall (uninstall),
cid (id), ctg (tg), tsys (sys), tclass (class_),
+ pkgconfig (pkgc),
x_src (src), x_hdr (hdr), x_inc (inc) {}
};
diff --git a/build2/cc/compile b/build2/cc/compile
index 2f1a0eb..5bff6b1 100644
--- a/build2/cc/compile
+++ b/build2/cc/compile
@@ -41,6 +41,12 @@ namespace build2
perform_clean (action, target&) const;
private:
+ void
+ append_lib_options (cstrings&, target&) const;
+
+ void
+ hash_lib_options (sha256&, target&) const;
+
// Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
// generated headers to directories where they will be generated.
//
@@ -57,7 +63,7 @@ namespace build2
append_prefixes (prefix_map&, target&, const variable&) const;
void
- append_lib_prefixes (prefix_map&, target&, lorder) const;
+ append_lib_prefixes (prefix_map&, target&) const;
prefix_map
build_prefix_map (target&, lorder) const;
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
index 56122e8..fdd4231 100644
--- a/build2/cc/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -62,6 +62,49 @@ namespace build2
return nullptr;
}
+ // Append or hash library options from a pair of *.export.* variables
+ // (first one is cc.export.*) recursively, prerequisite libraries first.
+ //
+ void compile::
+ append_lib_options (cstrings& args, target& xt) const
+ {
+ file& l (static_cast<file&> (xt));
+
+ auto opt = [&args, this] (file& l, const string& t, bool com, bool exp)
+ {
+ assert (exp);
+
+ const variable& var (
+ com
+ ? c_export_poptions
+ : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
+
+ append_options (args, l, var);
+ };
+
+ link_.process_libraries (l, l.is_a<liba> (), true, nullptr, opt);
+ }
+
+ void compile::
+ hash_lib_options (sha256& cs, target& xt) const
+ {
+ file& l (static_cast<file&> (xt));
+
+ auto opt = [&cs, this] (file& l, const string& t, bool com, bool exp)
+ {
+ assert (exp);
+
+ const variable& var (
+ com
+ ? c_export_poptions
+ : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
+
+ hash_options (cs, l, var);
+ };
+
+ link_.process_libraries (l, l.is_a<liba> (), true, nullptr, opt);
+ }
+
recipe compile::
apply (action a, target& xt, const match_result& mr) const
{
@@ -131,14 +174,18 @@ namespace build2
//
optional<dir_paths> lib_paths; // Extract lazily.
+ lorder lo;
+ if (a.operation () == update_id)
+ lo = link_order (bs, ct);
+
for (prerequisite_member p: group_prerequisite_members (a, t))
{
// A dependency on a library is there so that we can get its
// *.export.poptions. In particular, making sure it is executed before
// us will only restrict parallelism. But we do need to pre-match it
- // in order to get its prerequisite_targets populated. This is the
- // "library meta-information protocol". See also append_lib_options()
- // above.
+ // in order to get its imports resolved and prerequisite_targets
+ // populated. This is the "library meta-information protocol". See
+ // also append_lib_options().
//
if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
{
@@ -149,7 +196,8 @@ namespace build2
// any, they would be set by search_library()).
//
if (p.proj () == nullptr ||
- link_.search_library (lib_paths, p.prerequisite) == nullptr)
+ link_.search_library (
+ lib_paths, p.prerequisite, lo) == nullptr)
{
match_only (a, p.search ());
}
@@ -216,18 +264,16 @@ namespace build2
// Hash *.export.poptions from prerequisite libraries.
//
- lorder lo (link_order (bs, ct));
for (prerequisite& p: group_prerequisites (t))
{
target* pt (p.target); // Already searched and matched.
if (lib* l = pt->is_a<lib> ())
pt = &link_member (*l, lo);
+ else if (!pt->is_a<liba> () && !pt->is_a<libs> ())
+ continue;
- if (pt->is_a<liba> () || pt->is_a<libs> ())
- hash_lib_options (cs, *pt, lo,
- c_export_poptions,
- x_export_poptions);
+ hash_lib_options (cs, *pt);
}
hash_options (cs, t, c_poptions);
@@ -399,22 +445,23 @@ namespace build2
// recursively, prerequisite libraries first.
//
void compile::
- append_lib_prefixes (prefix_map& m, target& l, lorder lo) const
+ append_lib_prefixes (prefix_map& m, target& xt) const
{
- for (target* t: l.prerequisite_targets)
+ file& l (static_cast<file&> (xt));
+
+ auto opt = [&m, this] (file& l, const string& t, bool com, bool exp)
{
- if (t == nullptr)
- continue;
+ assert (exp);
- if (lib* l = t->is_a<lib> ())
- t = &link_member (*l, lo); // Pick one of the members.
+ const variable& var (
+ com
+ ? c_export_poptions
+ : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
- if (t->is_a<liba> () || t->is_a<libs> ())
- append_lib_prefixes (m, *t, lo);
- }
+ append_prefixes (m, l, var);
+ };
- append_prefixes (m, l, c_export_poptions);
- append_prefixes (m, l, x_export_poptions);
+ link_.process_libraries (l, l.is_a<liba> (), true, nullptr, opt);
}
auto compile::
@@ -430,10 +477,11 @@ namespace build2
target* pt (p.target); // Already searched and matched.
if (lib* l = pt->is_a<lib> ())
- pt = &link_member (*l, lo); // Pick one of the members.
+ pt = &link_member (*l, lo);
+ else if (!pt->is_a<liba> () && !pt->is_a<libs> ())
+ continue;
- if (pt->is_a<liba> () || pt->is_a<libs> ())
- append_lib_prefixes (m, *pt, lo);
+ append_lib_prefixes (m, *pt);
}
// Then process our own.
@@ -657,11 +705,10 @@ namespace build2
if (lib* l = pt->is_a<lib> ())
pt = &link_member (*l, lo);
+ else if (!pt->is_a<liba> () && !pt->is_a<libs> ())
+ continue;
- if (pt->is_a<liba> () || pt->is_a<libs> ())
- append_lib_options (args, *pt, lo,
- c_export_poptions,
- x_export_poptions);
+ append_lib_options (args, *pt);
}
append_options (args, t, c_poptions);
@@ -1330,11 +1377,10 @@ namespace build2
if (lib* l = pt->is_a<lib> ())
pt = &link_member (*l, lo);
+ else if (!pt->is_a<liba> () && !pt->is_a<libs> ())
+ continue;
- if (pt->is_a<liba> () || pt->is_a<libs> ())
- append_lib_options (args, *pt, lo,
- c_export_poptions,
- x_export_poptions);
+ append_lib_options (args, *pt);
}
append_options (args, t, c_poptions);
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index 0f1fda7..066367d 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -65,6 +65,9 @@ namespace build2
// "c", "cxx"). Currenly only set for libraries and is used to decide
// which *.libs to use during static linking.
//
+ // It can also be the special "cc" value which means a C-common library
+ // but specific language is not known. Used in import installed logic.
+ //
v.insert<string> ("cc.type");
return true;
@@ -228,6 +231,23 @@ namespace build2
load_module ("bin.rc.config", r, b, loc);
}
+ // Load (optionally) the pkgconfig.config module.
+ //
+ // @@ At some point we may also want to verify that targets matched
+ // if it has already been loaded (by someone) else. Currently it
+ // doesn't set pkgconfig.target. Perhaps only set if it was used
+ // to derive the program name?
+ //
+ if (first && !cast_false<bool> (b["pkgconfig.config.loaded"]))
+ {
+ // Prepare configuration hints.
+ //
+ variable_map h;
+ h.assign ("config.pkgconfig.target") = cast<string> (r["cc.target"]);
+
+ load_module ("pkgconfig.config", r, r, loc, true, h);
+ }
+
return true;
}
diff --git a/build2/cc/link b/build2/cc/link
index aaf1cd7..474c299 100644
--- a/build2/cc/link
+++ b/build2/cc/link
@@ -40,6 +40,15 @@ namespace build2
friend class compile;
void
+ process_libraries (file&,
+ bool,
+ bool,
+ const function<void (const path&)>&,
+ const function<void (file&,
+ const string&,
+ bool,
+ bool)>&) const;
+ void
append_libraries (strings&, file&, bool) const;
void
@@ -62,6 +71,15 @@ namespace build2
dir_paths
extract_library_paths (scope&) const;
+ bool
+ pkgconfig_extract (scope&,
+ file&,
+ const string*,
+ const string&,
+ const dir_path&,
+ optional<dir_paths>&,
+ lorder) const;
+
// Alternative search logic for VC (msvc.cxx).
//
bin::liba*
@@ -75,10 +93,12 @@ namespace build2
const prerequisite_key&) const;
target*
- search_library (optional<dir_paths>& spc, prerequisite& p) const
+ search_library (optional<dir_paths>& spc,
+ prerequisite& p,
+ lorder lo) const
{
if (p.target == nullptr) // First check the cache.
- p.target = search_library (spc, p.key ());
+ p.target = search_library (spc, p.key (), lo);
return p.target;
}
@@ -86,7 +106,9 @@ namespace build2
// Note that pk's scope should not be NULL (even if dir is absolute).
//
target*
- search_library (optional<dir_paths>&, const prerequisite_key&) const;
+ search_library (optional<dir_paths>&,
+ const prerequisite_key&,
+ lorder) const;
// Windows-specific (windows-manifest.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<dir_paths>& spc, const prerequisite_key& p) const
+ search_library (optional<dir_paths>& 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<dir_paths> 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<void (const path&)>& proc_lib,
+ const function<void (file&,
+ const string& type,
+ bool com, /* cc. */
+ bool exp) /* export. */>& 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<string> (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<liba> ()) != nullptr)
- || (f = p->is_a<libs> ()) != 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<string> (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<liba> ()) != nullptr)
+ || (f = p->is_a<libs> ()) != 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<lorder> lo; // Calculate lazily.
- optional<dir_paths> spc; // Extract lazily.
- auto append = [&args, &l, &bs, &lo, &spc, this] (const variable& var)
- {
- const names* ns (cast_null<names> (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<lorder> lo; // Calculate lazily.
+ optional<dir_paths> 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<names> (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<liba> (), 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<strings> (lu));
+ if (ns == nullptr || ns->empty ())
+ return;
- if ((a = (f = p->is_a<liba> ()) != nullptr)
- || (f = p->is_a<libs> ()) != 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<string> (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<lorder> lo; // Calculate lazily.
- optional<dir_paths> 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<names> (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)
{
diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx
index d088c5b..216bbf2 100644
--- a/build2/cc/msvc.cxx
+++ b/build2/cc/msvc.cxx
@@ -14,7 +14,6 @@
#include <build2/bin/target>
#include <build2/cc/types>
-
#include <build2/cc/link>
using namespace std;
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
new file mode 100644
index 0000000..2122ea5
--- /dev/null
+++ b/build2/cc/pkgconfig.cxx
@@ -0,0 +1,446 @@
+// file : build2/cc/msvc.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/scope>
+#include <build2/target>
+#include <build2/context>
+#include <build2/variable>
+#include <build2/filesystem>
+#include <build2/diagnostics>
+
+#include <build2/bin/target>
+
+#include <build2/cc/link>
+#include <build2/cc/types>
+#include <build2/cc/utility>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ using namespace bin;
+
+ // 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,
+ // extract poptions, loptions, and libs, set the corresponding *.export.*
+ // variables on lib, and return true.
+ //
+ // System library search paths (those extracted from the compiler) are
+ // passed in sys_sp and should already be extracted.
+ //
+ // Note that scope and link order should be "top-level" from the
+ // search_library() POV.
+ //
+ bool link::
+ pkgconfig_extract (scope& s,
+ file& lt,
+ const string* proj,
+ const string& stem,
+ const dir_path& libd,
+ optional<dir_paths>& sys_sp,
+ lorder lo) const
+ {
+ tracer trace (x, "link::pkgconfig_extract");
+
+ assert (sys_sp);
+ assert (pkgconfig != nullptr);
+
+ // Check if we have the pkgconfig/ subdirectory in this library's
+ // directory.
+ //
+ dir_path pkgd (dir_path (libd) /= "pkgconfig");
+
+ if (!dir_exists (pkgd))
+ return false;
+
+ // Now 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).
+ //
+ // Given the (general) import in the form <proj>%lib{<stem>}, we will
+ // first try <stem>.pc, then lib<stem>.pc. Maybe it also makes sense to
+ // try <proj>.pc, just in case. Though, according to pkg-config docs,
+ // the .pc file should correspond to a library, not project. But then
+ // you get something like zlib which calls it zlib.pc. So let's just do
+ // it.
+ //
+ path f;
+ f = pkgd;
+ f /= stem;
+ f += ".pc";
+
+ if (!file_exists (f))
+ {
+ f = pkgd;
+ f /= "lib";
+ f += stem;
+ f += ".pc";
+
+ if (!file_exists (f))
+ {
+ if (proj != nullptr)
+ {
+ f = pkgd;
+ f /= *proj;
+ f += ".pc";
+
+ if (!file_exists (f))
+ return false;
+ }
+ else
+ return false;
+ }
+ }
+
+ // Ok, we are in business. Time to run pkg-config. To keep things
+ // simple, we run it twice, first time for --cflag, then for --libs.
+ //
+ bool la (lt.is_a<liba> ());
+
+ const char* args[] = {
+ pkgconfig->initial,
+ nullptr, // --cflags/--libs
+ (la ? "--static" : f.string ().c_str ()),
+ (la ? f.string ().c_str () : nullptr),
+ nullptr
+ };
+
+ auto retline = [] (string& s) -> string {return move (s);};
+
+ args[1] = "--cflags";
+ string cstr (run<string> (*pkgconfig, args, retline));
+
+ args[1] = "--libs";
+ string lstr (run<string> (*pkgconfig, args, retline));
+
+ // On Windows pkg-config (at least the MSYS2 one which we are using)
+ // will escape backslahses in paths. In fact, it may escape things
+ // even on non-Windows platforms, for example, spaces. So we use a
+ // slightly modified version of next_word().
+ //
+ auto next = [] (const string& s, size_t& b, size_t& e) -> string
+ {
+ string r;
+ size_t n (s.size ());
+
+ if (b != e)
+ b = e;
+
+ // Skip leading delimiters.
+ //
+ for (; b != n && s[b] == ' '; ++b) ;
+
+ if (b == n)
+ {
+ e = n;
+ return r;
+ }
+
+ // Find first trailing delimiter while taking care of escapes.
+ //
+ r = s[b];
+ for (e = b + 1; e != n && s[e] != ' '; ++e)
+ {
+ if (s[e] == '\\')
+ {
+ if (++e == n)
+ fail << "dangling escape in pkg-config output '" << s << "'";
+ }
+
+ r += s[e];
+ }
+
+ return r;
+ };
+
+ // Parse --cflags into poptions.
+ //
+ {
+ strings pops;
+
+ bool arg (false);
+ string o;
+ for (size_t b (0), e (0); !(o = next (cstr, b, e)).empty (); )
+ {
+ if (arg)
+ {
+ pops.push_back (move (o));
+ arg = false;
+ continue;
+ }
+
+ size_t n (o.size ());
+
+ // We only keep -I, -D and -U.
+ //
+ if (n >= 2 &&
+ o[0] == '-' &&
+ (o[1] == 'I' || o[1] == 'D' || o[1] == 'U'))
+ {
+ pops.push_back (move (o));
+ arg = (n == 2);
+ continue;
+ }
+
+ l4 ([&]{trace << "ignoring " << f << " --cflags option " << o;});
+ }
+
+ if (arg)
+ fail << "argument expected after " << pops.back () <<
+ info << "while parsing pkg-config --cflags output of " << f;
+
+ if (!pops.empty ())
+ {
+ auto p (lt.vars.insert (c_export_poptions));
+
+ // 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 (p.second)
+ p.first.get () = move (pops);
+ }
+ }
+
+ // Parse --libs into loptions/libs.
+ //
+ {
+ strings lops;
+ names 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.
+ //
+ // 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.
+ //
+
+ bool arg (false), first (true), known (true), have_L;
+ string o;
+ for (size_t b (0), e (0); !(o = next (lstr, b, e)).empty (); )
+ {
+ if (arg)
+ {
+ // Can only be an argument for an loption.
+ //
+ lops.push_back (move (o));
+ arg = false;
+ continue;
+ }
+
+ size_t n (o.size ());
+
+ // See if this is -L.
+ //
+ if (n >= 2 && o[0] == '-' && o[1] == 'L')
+ {
+ have_L = true;
+ lops.push_back (move (o));
+ arg = (n == 2);
+ continue;
+ }
+
+ // See if that's -l or just the library name/path.
+ //
+ 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.
+ //
+ if (first)
+ {
+ first = false;
+ continue;
+ }
+
+ libs.push_back (name (move (o), false));
+ continue;
+ }
+
+ // Otherwise we assume it is some other loption.
+ //
+ known = false;
+ lops.push_back (move (o));
+ }
+
+ if (arg)
+ fail << "argument expected after " << lops.back () <<
+ info << "while parsing pkg-config --libs output of " << f;
+
+ if (first)
+ fail << "library expected in '" << lstr << "'" <<
+ info << "while parsing pkg-config --libs output of " << f;
+
+ // Resolve -lfoo into the library file path using our import installed
+ // machinery (i.e., we are going to call search_library() that will
+ // probably call us again, and so on).
+ //
+ // The reason we do it is the link order. For general libraries it
+ // shouldn't matter if we imported them via an export stub, direct
+ // import installed, or via a .pc file (which we could have generated
+ // from the export stub). The exception is "system libraries" (which
+ // are really the extension of libc) such as -lm, -ldl, -lpthread,
+ // etc. Those we will detect and leave as -l*.
+ //
+ // If we managed to resolve all the -l's (sans system), then we can
+ // omit -L's for nice and tidy command line.
+ //
+ bool all (true);
+ optional<dir_paths> sp; // Populate lazily.
+
+ for (name& n: libs)
+ {
+ string& l (n.value);
+
+ // These ones are common/standard/POSIX.
+ //
+ if (l[0] != '-' || // e.g., shell32.lib
+ l == "-lm" ||
+ l == "-ldl" ||
+ l == "-lrt" ||
+ l == "-lpthread")
+ continue;
+
+ // Note: these list are most likely incomplete.
+ //
+ if (tclass == "linux")
+ {
+ // Some extras from libc (see libc6-dev) and other places.
+ //
+ if (l == "-lanl" ||
+ l == "-lcrypt" ||
+ l == "-lnsl" ||
+ l == "-lresolv" ||
+ l == "-lgcc")
+ continue;
+ }
+ else if (tclass == "macosx")
+ {
+ if (l == "-lSystem")
+ continue;
+ }
+
+ // Prepare the search paths.
+ //
+ if (have_L && !sp)
+ {
+ sp = dir_paths ();
+
+ // First enter the -L paths from the .pc file so that they take
+ // precedence.
+ //
+ for (auto i (lops.begin ()); i != lops.end (); ++i)
+ {
+ const string& o (*i);
+
+ if (o.size () >= 2 && o[0] == '-' && o[1] == 'L')
+ {
+ string p;
+
+ if (o.size () == 2)
+ p = *++i; // We've verified it's there.
+ else
+ p = string (o, 2);
+
+ dir_path d (move (p));
+
+ if (d.relative ())
+ fail << "relative -L directory in '" << lstr << "'" <<
+ info << "while parsing pkg-config --libs output of " << f;
+
+ sp->push_back (move (d));
+ }
+ }
+
+ // Then append system paths.
+ //
+ sp->insert (sp->end (), sys_sp->begin (), sys_sp->end ());
+ }
+
+ // @@ OUT: for now we assume out is undetermined, just like in
+ // link::resolve_library().
+ //
+ dir_path out;
+ string name (l, 2); // Sans -l.
+ const string* ext (nullptr);
+
+ prerequisite_key pk {
+ nullptr, {&lib::static_type, &out, &out, &name, ext}, &s};
+
+ if (lib* lt = static_cast<lib*> (
+ search_library (have_L ? sp : sys_sp, pk, lo)))
+ {
+ file& f (static_cast<file&> (link_member (*lt, lo)));
+ l = f.path ().string ();
+ }
+ else
+ // If we couldn't find the library, then leave it as -l.
+ //
+ all = false;
+ }
+
+ // If all the -l's resolved and no other options, then drop all the
+ // -L's. If we have unknown options, then leave them in to be safe.
+ //
+ if (all && known)
+ lops.clear ();
+
+ if (!lops.empty ())
+ {
+ if (cid == "msvc")
+ {
+ // Translate -L to /LIBPATH.
+ //
+ for (auto i (lops.begin ()); i != lops.end (); )
+ {
+ string& o (*i);
+ size_t n (o.size ());
+
+ if (n >= 2 && o[0] == '-' && o[1] == 'L')
+ {
+ o.replace (0, 2, "/LIBPATH:");
+
+ if (n == 2)
+ {
+ o += *++i; // We've verified it's there.
+ i = lops.erase (i);
+ continue;
+ }
+ }
+
+ ++i;
+ }
+ }
+
+ auto p (lt.vars.insert (c_export_loptions));
+
+ if (p.second)
+ p.first.get () = move (lops);
+ }
+
+ if (!libs.empty ())
+ {
+ auto p (lt.vars.insert (c_export_libs));
+
+ if (p.second)
+ p.first.get () = move (libs);
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/build2/cc/utility b/build2/cc/utility
index ae19d56..1ef65d8 100644
--- a/build2/cc/utility
+++ b/build2/cc/utility
@@ -43,19 +43,6 @@ namespace build2
//
target&
link_member (bin::lib&, lorder);
-
- // Append or hash library options from a pair of *.export.* variables
- // (first one is cc.export.*) recursively, prerequisite libraries first.
- //
- void
- append_lib_options (cstrings&, target&, lorder,
- const variable&,
- const variable&);
-
- void
- hash_lib_options (sha256&, target&, lorder,
- const variable&,
- const variable&);
}
}
diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx
index 773ba8f..7be86e2 100644
--- a/build2/cc/utility.cxx
+++ b/build2/cc/utility.cxx
@@ -71,45 +71,5 @@ namespace build2
return *r;
}
-
- void
- append_lib_options (cstrings& args, target& l, lorder lo,
- const variable& cv,
- const variable& xv)
- {
- using namespace bin;
-
- for (target* t: l.prerequisite_targets)
- {
- if (lib* l = t->is_a<lib> ())
- t = &link_member (*l, lo); // Pick one of the members.
-
- if (t->is_a<liba> () || t->is_a<libs> ())
- append_lib_options (args, *t, lo, cv, xv);
- }
-
- append_options (args, l, cv);
- append_options (args, l, xv);
- }
-
- void
- hash_lib_options (sha256& csum, target& l, lorder lo,
- const variable& cv,
- const variable& xv)
- {
- using namespace bin;
-
- for (target* t: l.prerequisite_targets)
- {
- if (lib* l = t->is_a<lib> ())
- t = &link_member (*l, lo); // Pick one of the members.
-
- if (t->is_a<liba> () || t->is_a<libs> ())
- hash_lib_options (csum, *t, lo, cv, xv);
- }
-
- hash_options (csum, l, cv);
- hash_options (csum, l, xv);
- }
}
}
diff --git a/build2/context.txx b/build2/context.txx
index 6ff61c9..f40ecdc 100644
--- a/build2/context.txx
+++ b/build2/context.txx
@@ -12,7 +12,7 @@ namespace build2
const dir_path& b (*relative_base);
- if (b.empty ())
+ if (p.simple () || b.empty ())
return p;
if (p.sub (b))
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index 8202139..e504284 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -235,6 +235,8 @@ namespace build2
cast<string> (r[cm.x_target_system]),
cast<string> (r[cm.x_target_class]),
+ cast_null<process_path> (r["pkgconfig.path"]),
+
cxx::static_type,
hdr,
inc
diff --git a/build2/pkgconfig/init b/build2/pkgconfig/init
new file mode 100644
index 0000000..53821ae
--- /dev/null
+++ b/build2/pkgconfig/init
@@ -0,0 +1,37 @@
+// file : build2/pkgconfig/init -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_PKGCONFIG_INIT
+#define BUILD2_PKGCONFIG_INIT
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/module>
+
+namespace build2
+{
+ namespace pkgconfig
+ {
+ bool
+ config_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+
+ bool
+ init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
+ }
+}
+
+#endif // BUILD2_PKGCONFIG_INIT
diff --git a/build2/pkgconfig/init.cxx b/build2/pkgconfig/init.cxx
new file mode 100644
index 0000000..b7100e3
--- /dev/null
+++ b/build2/pkgconfig/init.cxx
@@ -0,0 +1,159 @@
+// file : build2/pkgconfig/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/pkgconfig/init>
+
+#include <build2/scope>
+#include <build2/target>
+#include <build2/variable>
+#include <build2/diagnostics>
+
+#include <build2/config/utility>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace pkgconfig
+ {
+ bool
+ config_init (scope& rs,
+ scope& bs,
+ const location& l,
+ unique_ptr<module_base>&,
+ bool,
+ bool optional,
+ const variable_map& hints)
+ {
+ tracer trace ("pkgconfig::config_init");
+ l5 ([&]{trace << "for " << bs.out_path ();});
+
+ if (&rs != &bs)
+ fail (l) << "pkgconfig.config loaded for non-root scope";
+
+ // Enter variables.
+ //
+ // config.pkgconfig.target is a hint.
+ //
+ auto& vp (var_pool);
+
+ const variable& c_x (vp.insert<path> ("config.pkgconfig", true));
+ const variable& c_x_tgt (vp.insert<string> ("config.pkgconfig.target"));
+ const variable& x_path (vp.insert<process_path> ("pkgconfig.path"));
+
+ // Configure.
+ //
+
+ // Adjust module priority (between compilers and binutils).
+ //
+ config::save_module (rs, "pkgconfig", 325);
+
+ process_path pp;
+ bool new_val (false); // Set any new values?
+
+ auto p (config::omitted (rs, c_x));
+
+ if (const value* v = p.first)
+ {
+ const path& x (cast<path> (*v));
+
+ try
+ {
+ // If this is a user-specified value, then it's non-optional.
+ //
+ pp = process::path_search (x, true);
+ new_val = p.second;
+ }
+ catch (const process_error& e)
+ {
+ fail << "unable to execute " << x << ": " << e.what ();
+ }
+ }
+
+ string d; // Default name (pp.initial may be its shallow copy).
+
+ // If we have a target hint, then next try <triplet>-pkg-config.
+ //
+ if (pp.empty ())
+ {
+ if (const string* t = cast_null<string> (hints[c_x_tgt]))
+ {
+ d = *t;
+ d += "-pkg-config";
+
+ l5 ([&]{trace << "trying " << d;});
+ pp = process::try_path_search (d, true);
+ }
+ }
+
+ // Finallly, try just pkg-config.
+ //
+ if (pp.empty ())
+ {
+ d = "pkg-config";
+
+ l5 ([&]{trace << "trying " << d;});
+ pp = process::try_path_search (d, true);
+ }
+
+ bool conf (!pp.empty ());
+
+ if (!conf && !optional)
+ fail (l) << "unable to find pkg-config program";
+
+ // Config report.
+ //
+ if (verb >= (new_val ? 2 : 3))
+ {
+ diag_record dr (text);
+ dr << "pkgconfig " << project (rs) << '@' << rs.out_path () << '\n';
+
+ if (conf)
+ dr << " pkg-config " << pp;
+ else
+ dr << " pkg-config " << "not found, leaving unconfigured";
+ }
+
+ if (conf)
+ rs.assign (x_path) = move (pp);
+
+ return conf;
+ }
+
+ bool
+ init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool optional,
+ const variable_map& hints)
+ {
+ tracer trace ("pkgconfig::init");
+ l5 ([&]{trace << "for " << bs.out_path ();});
+
+ // Load pkgconfig.config.
+ //
+ if (!cast_false<bool> (rs["pkgconfig.config.loaded"]))
+ {
+ if (!load_module ("pkgconfig.config", rs, rs, loc, optional, hints))
+ return false;
+ }
+ else if (!cast_false<bool> (rs["pkgconfig.config.configured"]))
+ {
+ if (!optional)
+ fail << "pkgconfig module could not be configured" <<
+ info << "re-run with -V option for more information";
+
+ return false;
+ }
+
+ // For now pkgconfig and pkgconfig.config is pretty much the same.
+ //
+
+ return true;
+ }
+ }
+}
diff --git a/build2/utility b/build2/utility
index 3ccca61..7f05dd0 100644
--- a/build2/utility
+++ b/build2/utility
@@ -80,7 +80,7 @@ namespace build2
// Start a process with the specified arguments printing the command at
// verbosity level 3 and higher. Redirect STDOUT to a pipe. If error is
- // false, then redirecting STDERR to STDOUT (this can used to suppress
+ // false, then redirecting STDERR to STDOUT (this can be used to suppress
// diagnostics from the child process). Issue diagnostics and throw failed
// in case of an error.
//
diff --git a/build2/utility.cxx b/build2/utility.cxx
index ef90084..db0d6b2 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -31,10 +31,15 @@ namespace build2
ostream&
operator<< (ostream& os, const process_path& p)
{
- os << p.recall_string ();
+ if (p.empty ())
+ os << "<empty>";
+ else
+ {
+ os << p.recall_string ();
- if (!p.effect.empty ())
- os << '@' << p.effect.string (); // Suppress relative().
+ if (!p.effect.empty ())
+ os << '@' << p.effect.string (); // Suppress relative().
+ }
return os;
}
diff --git a/tests/import/installed/buildfile b/tests/import/installed/buildfile
index be051b2..ae9d705 100644
--- a/tests/import/installed/buildfile
+++ b/tests/import/installed/buildfile
@@ -1,6 +1,8 @@
using cxx
-import libs += lib{z}
+import libs += zlib%lib{z}
+import libs += libsqlite3%lib{sqlite3}
+import libs += libpq%lib{pq}
#lib{driver}: cxx{driver} $libs