aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-07-31 18:42:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-07-31 18:44:07 +0200
commita84ff43b183181e0a12c6d5e31c1f366d39ce2fe (patch)
tree3dce54b64fd8e9917ed034a3e2c9e20f057eece4 /build2
parent89f5bc3b423a1269a60ccca05174226c8de40357 (diff)
Experimental (and probably broken) pkg-config generation support
Diffstat (limited to 'build2')
-rw-r--r--build2/algorithm.cxx23
-rw-r--r--build2/algorithm.hxx27
-rw-r--r--build2/algorithm.ixx8
-rw-r--r--build2/bin/rule.cxx12
-rw-r--r--build2/cc/common.cxx7
-rw-r--r--build2/cc/common.hxx15
-rw-r--r--build2/cc/init.cxx7
-rw-r--r--build2/cc/install.cxx33
-rw-r--r--build2/cc/install.hxx9
-rw-r--r--build2/cc/link.cxx120
-rw-r--r--build2/cc/link.hxx2
-rw-r--r--build2/cc/pkgconfig.cxx341
-rw-r--r--build2/install/init.cxx10
-rw-r--r--build2/install/rule.cxx60
-rw-r--r--build2/install/rule.hxx8
-rw-r--r--build2/install/utility.hxx6
-rw-r--r--build2/pkgconfig/init.cxx24
-rw-r--r--build2/pkgconfig/target.cxx54
-rw-r--r--build2/pkgconfig/target.hxx48
-rw-r--r--build2/target.cxx20
-rw-r--r--build2/target.hxx27
-rw-r--r--build2/variable.cxx2
-rw-r--r--build2/variable.hxx9
-rw-r--r--build2/variable.txx22
24 files changed, 674 insertions, 220 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index dc1c418..29bae6d 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -262,19 +262,20 @@ namespace build2
n += s;
}
- const target& m (t.member != nullptr // Might already be there.
- ? *t.member
+ const_ptr<target>* mp (&t.member);
+ for (auto p (*mp); p != nullptr && !p->is_a (tt); mp = &p->member) ;
+
+ const target& m (*mp != nullptr // Might already be there.
+ ? **mp
: search (t, tt, t.dir, t.out, n));
target_lock l (lock (a, m));
assert (l.target != nullptr); // Someone messing with ad hoc members?
- if (t.member == nullptr)
- t.member = l.target;
+ if (*mp == nullptr)
+ *mp = l.target;
else
- // Basic sanity check.
- //
- assert (t.member->type () == tt && t.member->name == n);
+ assert ((*mp)->name == n); // Basic sanity check.
return l;
};
@@ -513,7 +514,7 @@ namespace build2
catch (const failed&)
{
// As a sanity measure clear the target data since it can be incomplete
- // or invalid (mark()/unmark() should give you some for ideas).
+ // or invalid (mark()/unmark() should give you some ideas).
//
t.clear_data ();
t.prerequisite_targets.clear ();
@@ -619,6 +620,10 @@ namespace build2
// Fall through to apply.
}
+ // @@ Doing match without execute messes up our target_count. Does
+ // not seem like it will be easy to fix (we don't know whether
+ // someone else will execute this target).
+ //
case target::offset_matched:
{
// Apply (locked).
@@ -1234,7 +1239,7 @@ namespace build2
noop_action (action a, const target& t)
{
text << "noop action triggered for " << diag_doing (a, t);
- assert (false); // We shouldn't be called, see target::recipe().
+ assert (false); // We shouldn't be called (see target::recipe()).
return target_state::unchanged;
}
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 2006e2a..931b396 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -136,14 +136,37 @@ namespace build2
target_lock
lock (action, const target&);
- // Add an ad hoc member. If the suffix is specified, it is added (as an
- // extension) to the member's target name. Return the locked member target.
+ // Add an ad hoc member to the end of the chain assuming that an already
+ // existing member of this target type is the same.
+ //
+ // If the suffix is specified, it is added (as an extension) to the member's
+ // target name. Return the locked member target.
//
target_lock
add_adhoc_member (action, target&,
const target_type&,
const char* suffix = nullptr);
+ template <typename T>
+ inline target_lock
+ add_adhoc_member (action a, target& t, const char* s = nullptr)
+ {
+ return add_adhoc_member (a, t, T::static_type, s);
+ }
+
+ // Find an ad hoc member of the specified target type returning NULL if not
+ // found.
+ //
+ const target*
+ find_adhoc_member (const target&, const target_type&);
+
+ template <typename T>
+ inline const T*
+ find_adhoc_member (const target& t)
+ {
+ return static_cast<const T*> (find_adhoc_member (t, T::static_type));
+ }
+
// Match and apply a rule to the action/target with ambiguity detection.
// Increment the target's dependents count, which means that you should call
// this function with the intent to also call execute(). Return the target
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index 892c832..5680288 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -7,6 +7,14 @@
namespace build2
{
+ inline const target*
+ find_adhoc_member (const target& t, const target_type& tt)
+ {
+ const target* m (t.member);
+ for (; m != nullptr && !m->is_a (tt); m = m->member) ;
+ return m;
+ }
+
inline const target&
search (const target& t, const prerequisite& p)
{
diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx
index bb9036b..7d32e26 100644
--- a/build2/bin/rule.cxx
+++ b/build2/bin/rule.cxx
@@ -38,7 +38,7 @@ namespace build2
// our prerequisites.
//
match_result lib_rule::
- match (action act, target& xt, const string&) const
+ match (action, target& xt, const string&) const
{
lib& t (xt.as<lib> ());
@@ -57,15 +57,7 @@ namespace build2
t.a = a ? &search<liba> (t, t.dir, t.out, t.name) : nullptr;
t.s = s ? &search<libs> (t, t.dir, t.out, t.name) : nullptr;
- match_result mr (true);
-
- // If there is an outer operation, indicate that we match
- // unconditionally so that we don't override ourselves.
- //
- if (act.outer_operation () != 0)
- mr.recipe_action = action (act.meta_operation (), act.operation ());
-
- return mr;
+ return true;
}
recipe lib_rule::
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index ce8415c..6f51662 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -869,8 +869,11 @@ namespace build2
// 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 (
- act, *p.scope, *lt, a, s, p.proj, name, *pd, sysd))
+ if (pkgconfig == nullptr ||
+ !pkgconfig_load (act, *p.scope,
+ *lt, a, s,
+ p.proj, name,
+ *pd, sysd))
{
if (a != nullptr) add_macro (*a, "STATIC");
if (s != nullptr) add_macro (*s, "SHARED");
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index 951863a..66c226c 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -266,15 +266,12 @@ namespace build2
extract_library_dirs (const scope&) const;
bool
- pkgconfig_extract (action,
- const scope&,
- bin::lib&,
- bin::liba*,
- bin::libs*,
- const optional<string>&,
- const string&,
- const dir_path&,
- const dir_paths&) const; // pkgconfig.cxx
+ pkgconfig_load (action, const scope&,
+ bin::lib&, bin::liba*, bin::libs*,
+ const optional<string>&,
+ const string&,
+ const dir_path&,
+ const dir_paths&) const; // pkgconfig.cxx
// Alternative search logic for VC (msvc.cxx).
//
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index ea42469..8dceda7 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -248,14 +248,15 @@ namespace build2
load_module (rs, rs, "bin.rc.config", loc);
}
- // Load (optionally) the pkgconfig.config module.
+ // Load (optionally) the pkgconfig module. Note that it registers the
+ // pc{} target whether the pkg-config utility is found or not.
//
// @@ 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 (!cast_false<bool> (rs["pkgconfig.config.loaded"]))
+ if (!cast_false<bool> (rs["pkgconfig.loaded"]))
{
// Prepare configuration hints.
//
@@ -266,7 +267,7 @@ namespace build2
const variable& t (vp.insert ("config.pkgconfig.target"));
h.assign (t) = cast<target_triplet> (rs["cc.target"]);
- load_module (rs, rs, "pkgconfig.config", loc, true, h);
+ load_module (rs, rs, "pkgconfig", loc, true, h);
}
return true;
diff --git a/build2/cc/install.cxx b/build2/cc/install.cxx
index a0dc9b8..c65e7f6 100644
--- a/build2/cc/install.cxx
+++ b/build2/cc/install.cxx
@@ -94,12 +94,43 @@ namespace build2
{
file* f;
if ((f = t.is_a<libs> ()) != nullptr && tclass != "windows")
- t.data (link_.derive_libs_paths (*f));
+ {
+ const string* p (cast_null<string> (t["bin.lib.prefix"]));
+ const string* s (cast_null<string> (t["bin.lib.suffix"]));
+ t.data (
+ link_.derive_libs_paths (*f,
+ p != nullptr ? p->c_str (): nullptr,
+ s != nullptr ? s->c_str (): nullptr));
+ }
}
return r;
}
+ target_state file_install::
+ update_extra (action act, const target& t) const
+ {
+ // (Re)generate pkg-config's .pc file. While the target itself might be
+ // up-to-date from a previous run, there is no guarantee that .pc exists
+ // or also up-to-date. So to keep things simple we just regenerate it
+ // unconditionally.
+ //
+ // Also, if you are wondering why don't we just always produce this .pc,
+ // install or no install, the reason is unless and until we are updating
+ // for install, we have no idea where to things will be installed.
+ //
+ bool a;
+ const file* f;
+
+ if ((a = (f = t.is_a<liba> ())) ||
+ ( f = t.is_a<libs> ()))
+ {
+ pkgconfig_save (act, *f, a);
+ }
+
+ return target_state::unchanged;
+ }
+
void file_install::
install_extra (const file& t, const install_dir& id) const
{
diff --git a/build2/cc/install.hxx b/build2/cc/install.hxx
index a846fc8..2dd2b71 100644
--- a/build2/cc/install.hxx
+++ b/build2/cc/install.hxx
@@ -41,6 +41,15 @@ namespace build2
virtual bool
uninstall_extra (const file&, const install_dir&) const override;
+ virtual target_state
+ update_extra (action, const target&) const override;
+
+ private:
+ // pkg-config's .pc file generation (in pkgconfig.cxx).
+ //
+ void
+ pkgconfig_save (action, const file&, bool) const;
+
private:
const link& link_;
};
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx
index 1cb3a11..08b5314 100644
--- a/build2/cc/link.cxx
+++ b/build2/cc/link.cxx
@@ -19,6 +19,7 @@
#include <build2/diagnostics.hxx>
#include <build2/bin/target.hxx>
+#include <build2/pkgconfig/target.hxx>
#include <build2/cc/target.hxx> // c
#include <build2/cc/utility.hxx>
@@ -142,34 +143,33 @@ namespace build2
}
auto link::
- derive_libs_paths (file& ls) const -> libs_paths
+ derive_libs_paths (file& ls, const char* pfx, const char* sfx) const
+ -> libs_paths
{
const char* ext (nullptr);
- const char* pfx (nullptr);
- const char* sfx (nullptr);
bool win (tclass == "windows");
if (win)
{
if (tsys == "mingw32")
- pfx = "lib";
+ {
+ if (pfx == nullptr)
+ pfx = "lib";
+ }
ext = "dll";
}
- else if (tclass == "macos")
- {
- pfx = "lib";
- ext = "dylib";
- }
else
{
- pfx = "lib";
- ext = "so";
- }
+ if (pfx == nullptr)
+ pfx = "lib";
- if (auto l = ls["bin.lib.prefix"]) pfx = cast<string> (l).c_str ();
- if (auto l = ls["bin.lib.suffix"]) sfx = cast<string> (l).c_str ();
+ if (tclass == "macos")
+ ext = "dylib";
+ else
+ ext = "so";
+ }
// First sort out which extension we are using.
//
@@ -315,14 +315,6 @@ namespace build2
// Derive file name(s) and add ad hoc group members.
//
-
- // Add if necessary and lock an ad hoc group member.
- //
- auto add_adhoc = [act, &bs] (target& t, const char* type) -> target_lock
- {
- return add_adhoc_member (act, t, *bs.find_target_type (type));
- };
-
{
target_lock libi; // Have to hold until after PDB member addition.
@@ -391,6 +383,11 @@ namespace build2
}
else
{
+ if (auto l = t[ot == otype::e ? "bin.exe.prefix" : "bin.lib.prefix"])
+ p = cast<string> (l).c_str ();
+ if (auto l = t[ot == otype::e ? "bin.exe.suffix" : "bin.lib.suffix"])
+ s = cast<string> (l).c_str ();
+
switch (ot)
{
case otype::e:
@@ -400,9 +397,6 @@ namespace build2
else
e = "";
- if (auto l = t["bin.exe.prefix"]) p = cast<string> (l).c_str ();
- if (auto l = t["bin.exe.suffix"]) s = cast<string> (l).c_str ();
-
t.derive_path (e, p, s);
break;
}
@@ -412,13 +406,10 @@ namespace build2
e = "lib";
else
{
- p = "lib";
+ if (p == nullptr) p = "lib";
e = "a";
}
- if (auto l = t["bin.lib.prefix"]) p = cast<string> (l).c_str ();
- if (auto l = t["bin.lib.suffix"]) s = cast<string> (l).c_str ();
-
t.derive_path (e, p, s);
break;
}
@@ -428,9 +419,9 @@ namespace build2
// the DLL and we add libi{} import library as its member.
//
if (tclass == "windows")
- libi = add_adhoc (t, "libi");
+ libi = add_adhoc_member<bin::libi> (act, t);
- t.data (derive_libs_paths (t)); // Cache in target.
+ t.data (derive_libs_paths (t, p, s)); // Cache in target.
if (libi)
match_recipe (libi, group_recipe); // Set recipe and unlock.
@@ -438,26 +429,57 @@ namespace build2
break;
}
}
- }
- // PDB
- //
- if (!lt.static_library () &&
- cid == compiler_id::msvc &&
- (find_option ("/DEBUG", t, c_loptions, true) ||
- find_option ("/DEBUG", t, x_loptions, true)))
- {
- // Add after the import library if any.
+ // Add VC's .pdb.
//
- target_lock pdb (
- add_adhoc (t.member == nullptr ? t : *t.member, "pdb"));
+ if (ot != otype::a &&
+ cid == compiler_id::msvc &&
+ (find_option ("/DEBUG", t, c_loptions, true) ||
+ find_option ("/DEBUG", t, x_loptions, true)))
+ {
+ // Note: add after the import library if any.
+ //
+ target_lock pdb (
+ add_adhoc_member (act, t, *bs.find_target_type ("pdb")));
+
+ // We call it foo.{exe,dll}.pdb rather than just foo.pdb because
+ // we can have both foo.exe and foo.dll in the same directory.
+ //
+ pdb.target->as<file> ().derive_path (t.path (), "pdb");
+
+ match_recipe (pdb, group_recipe); // Set recipe and unlock.
+ }
- // We call it foo.{exe,dll}.pdb rather than just foo.pdb because we
- // can have both foo.exe and foo.dll in the same directory.
+ // Add pkg-config's .pc file.
//
- pdb.target->as<file> ().derive_path (t.path (), "pdb");
+ // Note that we do it here regardless of whether we are installing
+ // or not for two reasons. Firstly, it is not easy to detect this
+ // situation in apply() since the action may (and is) overridden to
+ // unconditional install. Secondly, always having the member takes
+ // care of cleanup automagically. The actual generation happens in
+ // the install rule.
+ //
+ if (ot != otype::e)
+ {
+ target_lock pc (
+ add_adhoc_member (
+ act, t,
+ ot == otype::a
+ ? pkgconfig::pca::static_type
+ : pkgconfig::pcs::static_type));
+
+ // Note that here we always use the lib name prefix, even on
+ // Windows with VC. The reason is the user needs a consistent name
+ // across platforms by which they can refere to the library. This
+ // is also the reason why we use the static/shared suffixes rather
+ // that a./.lib/.so/.dylib/.dll.
+ //
+ pc.target->as<file> ().derive_path (nullptr,
+ (p == nullptr ? "lib" : p),
+ s);
- match_recipe (pdb, group_recipe); // Set recipe and unlock.
+ match_recipe (pc, group_recipe); // Set recipe and unlock.
+ }
}
}
@@ -1164,12 +1186,12 @@ namespace build2
{
tracer trace (x, "link::perform_update");
- const file& t (xt.as<file> ());
- const path& tp (t.path ());
-
auto oop (act.outer_operation ());
bool for_install (oop == install_id || oop == uninstall_id);
+ const file& t (xt.as<file> ());
+ const path& tp (t.path ());
+
const scope& bs (t.base_scope ());
const scope& rs (*bs.root_scope ());
diff --git a/build2/cc/link.hxx b/build2/cc/link.hxx
index c4c9c9e..cadc82c 100644
--- a/build2/cc/link.hxx
+++ b/build2/cc/link.hxx
@@ -70,7 +70,7 @@ namespace build2
};
libs_paths
- derive_libs_paths (file&) const;
+ derive_libs_paths (file&, const char*, const char*) const;
// Library handling.
//
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index 80f418c..3fc40ad 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -6,15 +6,20 @@
#include <build2/target.hxx>
#include <build2/context.hxx>
#include <build2/variable.hxx>
+#include <build2/algorithm.hxx>
#include <build2/filesystem.hxx>
#include <build2/diagnostics.hxx>
+#include <build2/install/utility.hxx>
+
#include <build2/bin/target.hxx>
+#include <build2/pkgconfig/target.hxx>
#include <build2/cc/types.hxx>
#include <build2/cc/utility.hxx>
#include <build2/cc/common.hxx>
+#include <build2/cc/install.hxx>
using namespace std;
using namespace butl;
@@ -27,7 +32,7 @@ namespace build2
// 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.*
+ // load poptions, loptions, and libs, set the corresponding *.export.*
// variables on targets, and return true.
//
// System library search paths (those extracted from the compiler) are
@@ -37,35 +42,36 @@ namespace build2
// search_library() POV.
//
bool common::
- pkgconfig_extract (action act,
- const scope& s,
- lib& lt,
- liba* at,
- libs* st,
- const optional<string>& proj,
- const string& stem,
- const dir_path& libd,
- const dir_paths& sysd) const
+ pkgconfig_load (action act,
+ const scope& s,
+ lib& lt,
+ liba* at,
+ libs* st,
+ const optional<string>& proj,
+ const string& stem,
+ const dir_path& libd,
+ const dir_paths& sysd) const
{
- tracer trace (x, "pkgconfig_extract");
+ tracer trace (x, "pkgconfig_load");
assert (pkgconfig != nullptr);
assert (at != nullptr || st != nullptr);
// 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).
+ // names). Suffix is our ".shared" or ".static" extension.
//
- path f;
- auto search = [&f, &proj, &stem, &libd] (const dir_path& dir) -> bool
+ auto search_dir = [&proj, &stem, &libd] (const dir_path& dir,
+ const string& sfx) -> path
{
- // Check if we have this directory inrelative to this library's
- // directory.
+ // Check if we have this subdirectory in this library's directory.
//
dir_path pkgd (dir_path (libd) /= dir);
if (!exists (pkgd))
- return false;
+ return path ();
+
+ 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
@@ -73,70 +79,90 @@ namespace build2
// 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
+ // first try lib<stem>.pc, then <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.
//
f = pkgd;
- f /= stem;
+ f /= "lib";
+ f += stem;
+ f += sfx;
f += ".pc";
-
if (exists (f))
- return true;
+ return f;
f = pkgd;
- f /= "lib";
- f += stem;
+ f /= stem;
+ f += sfx;
f += ".pc";
-
if (exists (f))
- return true;
+ return f;
if (proj)
{
f = pkgd;
f /= *proj;
+ f += sfx;
f += ".pc";
-
if (exists (f))
- return true;
+ return f;
}
- return false;
+ return path ();
};
- // First always check the pkgconfig/ subdirectory in this library's
- // 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.
- //
- if (!search (dir_path ("pkgconfig")))
+ auto search = [&search_dir, this] () -> pair<path, path>
{
- // Platform-specific locations.
+ pair<path, path> r;
+
+ auto check = [&r, &search_dir] (const dir_path& d) -> bool
+ {
+ // First look for static/shared-specific files.
+ //
+ r.first = search_dir (d, ".static");
+ r.second = search_dir (d, ".shared");
+
+ if (!r.first.empty () || !r.second.empty ())
+ return true;
+
+ // Then the common.
+ //
+ r.first = r.second = search_dir (d, string ());
+ return !r.first.empty ();
+ };
+
+ // First always check the pkgconfig/ subdirectory in this library's
+ // 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.
//
- if (tsys == "freebsd")
+ if (!check (dir_path ("pkgconfig")))
{
- // On FreeBSD .pc files go to libdata/pkgconfig/, not lib/pkgconfig/.
+ // Platform-specific locations.
//
- if (!search ((dir_path ("..") /= "libdata") /= "pkgconfig"))
- return false;
+ if (tsys == "freebsd")
+ {
+ // On FreeBSD .pc files go to libdata/pkgconfig/, not lib/pkgconfig/.
+ //
+ check ((dir_path ("..") /= "libdata") /= "pkgconfig");
+ }
}
- else
- return false;
- }
- // Ok, we are in business. Time to run pkg-config. To keep things
- // simple, we run it multiple times, for --cflag/--libs and --static.
+ return r;
+ };
+
+ // To keep things simple, we run pkg-config multiple times, for
+ // --cflag/--libs and --static.
//
- auto extract = [&f, this] (const char* op, bool impl) -> string
+ auto extract = [this] (const path& f, const char* o, bool a) -> string
{
const char* args[] = {
pkgconfig->recall_string (),
- op, // --cflags/--libs
- (impl ? "--static" : f.string ().c_str ()),
- (impl ? f.string ().c_str () : nullptr),
+ o, // --cflags/--libs
+ (a ? "--static" : f.string ().c_str ()),
+ (a ? f.string ().c_str () : nullptr),
nullptr
};
@@ -144,10 +170,9 @@ namespace build2
*pkgconfig, args, [] (string& s) -> string {return move (s);});
};
- // 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().
+ // On Windows pkg-config 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
{
@@ -184,12 +209,13 @@ namespace build2
return r;
};
- // First extract --cflags and set them as lib{}:export.poptions (i.e.,
- // they will be common for both liba{} and libs{}; later when we do
- // split .pc files, we will have to run this twice).
+ // 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, &extract, &next, this]
+ (target& t, const path& f, bool a)
{
- string cstr (extract ("--cflags", false));
+ string cstr (extract (f, "--cflags", a));
strings pops;
string o;
@@ -243,7 +269,7 @@ namespace build2
if (!pops.empty ())
{
- auto p (lt.vars.insert (c_export_poptions));
+ auto p (t.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).
@@ -253,13 +279,15 @@ namespace build2
if (p.second)
p.first.get () = move (pops);
}
- }
+ };
- // Now parse --libs into loptions/libs (interface and implementation).
+ // Parse --libs into loptions/libs (interface and implementation).
//
- auto parse_libs = [act, &s, &f, sysd, &next, this] (
- const string& lstr, target& t)
+ auto parse_libs = [act, &s, sysd, &extract, &next, this]
+ (target& t, const path& f, bool a)
{
+ string lstr (extract (f, "--libs", a));
+
strings lops;
vector<name> libs;
@@ -274,7 +302,6 @@ namespace build2
// 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 (); )
@@ -438,8 +465,9 @@ namespace build2
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 the -l's resolved and there were 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 ();
@@ -487,20 +515,187 @@ namespace build2
}
};
- {
- string lstr_int (extract ("--libs", false));
- string lstr_imp (extract ("--libs", true));
+ pair<path, path> ps (search ());
+ const path& ap (ps.first);
+ const path& sp (ps.second);
- parse_libs (lstr_int, lt);
+ if (ap.empty () && sp.empty ())
+ return false;
- // Currently, these will result in the same values but it will be
- // different once we support split .pc files.
- //
- if (at != nullptr) parse_libs (lstr_imp, *at);
- if (st != nullptr) parse_libs (lstr_imp, *st);
+ if (at != nullptr && !ap.empty ())
+ {
+ parse_cflags (*at, ap, true);
+ parse_libs (*at, ap, true);
+ }
+
+ if (st != nullptr && !sp.empty ())
+ {
+ parse_cflags (*st, sp, false);
+ parse_libs (lt, sp, false); // Note: setting on lib{} (interface).
}
return true;
}
+
+ void file_install::
+ pkgconfig_save (action act, const file& l, bool la) const
+ {
+ tracer trace (x, "pkgconfig_save");
+
+ const scope& bs (l.base_scope ());
+ const scope& rs (*bs.root_scope ());
+
+ auto* pc (find_adhoc_member<pkgconfig::pc> (l));
+ assert (pc != nullptr);
+
+ const path& p (pc->path ());
+
+ if (verb >= 2)
+ text << "cat >" << p;
+
+ try
+ {
+ ofdstream os (p);
+ auto_rmfile arm (p);
+
+ // @@ version may not be string, need to reverse.
+ //
+ os << "Name: " << cast<string> (rs.vars[var_project]) << endl;
+ os << "Version: " << cast<string> (rs.vars["version"]) << endl;
+ os << "Description: @@ TODO" << endl;
+
+ // By default we assume things go into install.{include, lib}.
+ //
+ // @@ TODO: quoting/whitespace escaping.
+ // @@ TODO: support whole archive?
+ //
+ using install::resolve_dir;
+
+ dir_path id (resolve_dir (l, cast<dir_path> (l["install.include"])));
+
+ auto save_poptions = [&l, &os] (const variable& var)
+ {
+ if (const strings* v = cast_null<strings> (l[var]))
+ {
+ for (auto i (v->begin ()); i != v->end (); ++i)
+ {
+ const string& o (*i);
+ size_t n (o.size ());
+
+ // Filter out -I (both -I<dir> and -I <dir> forms).
+ //
+ if (n >= 2 && o[0] == '-' && o[1] == 'I')
+ {
+ if (n == 2)
+ ++i;
+
+ continue;
+ }
+
+ os << ' ' << o;
+ }
+ }
+ };
+
+ // Given a library save its -l-style library name.
+ //
+ auto save_library = [&os] (const file& l)
+ {
+ // Use the .pc file name to derive the -l library name (in case of
+ // the shared library, l.path() may contain version).
+ //
+ auto* pc (find_adhoc_member<pkgconfig::pc> (l));
+ assert (pc != nullptr);
+
+ // We also want to strip the lib prefix unless it is part of the
+ // target name while keeping custom library prefix/suffix, if any.
+ //
+ string n (pc->path ().leaf ().base ().base ().string ());
+ if (n.size () > 3 &&
+ path::traits::compare (n.c_str (), 3, "lib", 3) == 0 &&
+ path::traits::compare (n.c_str (), n.size (),
+ l.name.c_str (), l.name.size ()) != 0)
+ n.erase (0, 3);
+
+ os << " -l" << n;
+ };
+
+ // Cflags.
+ //
+ os << "Cflags:";
+ os << " -I" << id;
+ save_poptions (c_export_poptions);
+ save_poptions (x_export_poptions);
+ os << endl;
+
+ // Libs.
+ //
+ dir_path ld (resolve_dir (l, cast<dir_path> (l["install.lib"])));
+
+ os << "Libs:";
+ os << " -L" << ld;
+
+ // Now process ourselves as if we were being linked to something (so
+ // pretty similar to link::append_libraries()).
+ //
+ auto imp = [] (const file&, bool la) {return la;};
+
+ auto lib = [&os, &save_library] (const file* l,
+ const string& p,
+ lflags,
+ bool)
+ {
+ if (l != nullptr)
+ {
+ if (l->is_a<libs> () || l->is_a<liba> ()) // See through libux.
+ save_library (*l);
+ }
+ else
+ os << ' ' << p; // Something "system'y", pass as is.
+ };
+
+ auto opt = [&os] (const file&,
+ const string&,
+ bool, bool)
+ {
+ //@@ TODO: should we filter -L similar to -I?
+
+ /*
+ // If we need an interface value, then use the group (lib{}).
+ //
+ if (const target* g = exp && l.is_a<libs> () ? l.group : &l)
+ {
+ 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, *g, var);
+ }
+ */
+ };
+
+ process_libraries (
+ act,
+ bs,
+ linfo {otype::e, la ? lorder::a_s : lorder::s_a}, // System-default.
+ sys_lib_dirs,
+ l, la,
+ 0, // Link flags.
+ imp, lib, opt,
+ true);
+
+ os << endl;
+
+ os.close ();
+ arm.cancel ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << p << ": " << e;
+ }
+ }
}
}
diff --git a/build2/install/init.cxx b/build2/install/init.cxx
index 1d51be2..0e8118a 100644
--- a/build2/install/init.cxx
+++ b/build2/install/init.cxx
@@ -141,10 +141,11 @@ namespace build2
static const dir_path dir_root ("root");
- static const dir_path dir_sbin (dir_path ("exec_root") /= "sbin");
- static const dir_path dir_bin (dir_path ("exec_root") /= "bin");
- static const dir_path dir_lib (dir_path ("exec_root") /= "lib");
- static const dir_path dir_libexec (dir_path ("exec_root") /= "libexec");
+ static const dir_path dir_sbin (dir_path ("exec_root") /= "sbin");
+ static const dir_path dir_bin (dir_path ("exec_root") /= "bin");
+ static const dir_path dir_lib (dir_path ("exec_root") /= "lib");
+ static const dir_path dir_libexec (dir_path ("exec_root") /= "libexec");
+ static const dir_path dir_pkgconfig (dir_path ("lib") /= "pkgconfig");
static const dir_path dir_data (dir_path ("data_root") /= "share");
static const dir_path dir_include (dir_path ("data_root") /= "include");
@@ -240,6 +241,7 @@ namespace build2
set_dir (s, rs, "bin", dir_bin);
set_dir (s, rs, "lib", dir_lib);
set_dir (s, rs, "libexec", dir_path (dir_libexec) /= n, true);
+ set_dir (s, rs, "pkgconfig", dir_pkgconfig, false, "644");
set_dir (s, rs, "data", dir_path (dir_data) /= n, true);
set_dir (s, rs, "include", dir_include);
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
index f2fc233..6b75cea 100644
--- a/build2/install/rule.cxx
+++ b/build2/install/rule.cxx
@@ -199,35 +199,30 @@ namespace build2
//
if (a.operation () == update_id)
{
- // Save the prerequisite targets that we found since the
- // call to match_delegate() below will wipe them out.
+ // Save the prerequisite targets that we found since the call to
+ // match_delegate() below will wipe them out.
//
prerequisite_targets pts;
+ pts.swap (t.prerequisite_targets);
- if (!t.prerequisite_targets.empty ())
- pts.swap (t.prerequisite_targets);
-
- // Find the "real" update rule, that is, the rule that would
- // have been found if we signalled that we do not match from
- // match() above.
+ // Find the "real" update rule, that is, the rule that would have been
+ // found if we signalled that we do not match from match() above.
//
recipe d (match_delegate (a, t, *this).first);
- // If we have no installable prerequisites, then simply redirect
- // to it.
- //
- if (pts.empty ())
- return d;
-
- // Ok, the worst case scenario: we need to cause update of
- // prerequisite targets and also delegate to the real update.
- //
- return [pts = move (pts), d = move (d)] (
- action a, const target& t) mutable -> target_state
+ return [pts = move (pts), d = move (d), this]
+ (action a, const target& t) mutable -> target_state
{
- // Do the target update first.
+ // Do the target update first (we cannot call noop_recipe).
//
- target_state r (execute_delegate (d, a, t));
+ recipe_function** f (d.target<recipe_function*> ());
+ target_state r (f != nullptr && *f == &noop_action
+ ? target_state::unchanged
+ : execute_delegate (d, a, t));
+
+ // Then the extra hook.
+ //
+ r |= update_extra (a, t);
// Swap our prerequisite targets back in and execute.
//
@@ -251,10 +246,21 @@ namespace build2
}
void file_rule::
- install_extra (const file&, const install_dir&) const {}
+ install_extra (const file&, const install_dir&) const
+ {
+ }
bool file_rule::
- uninstall_extra (const file&, const install_dir&) const {return false;}
+ uninstall_extra (const file&, const install_dir&) const
+ {
+ return false;
+ }
+
+ target_state file_rule::
+ update_extra (action, const target&) const
+ {
+ return target_state::unchanged;
+ }
struct install_dir
{
@@ -296,7 +302,7 @@ namespace build2
//
for (const scope* p (&s); p != nullptr; p = p->parent_scope ())
{
- if (l.belongs (*p)) // Ok since no target/type in lookup.
+ if (l.belongs (*p, true)) // Include target type/pattern-specific.
{
// The target can be in out or src.
//
@@ -388,6 +394,12 @@ namespace build2
return rs;
}
+ dir_path
+ resolve_dir (const target& t, dir_path d)
+ {
+ return move (resolve (t, move (d)).back ().dir);
+ }
+
// On Windows we use MSYS2 install.exe and MSYS2 by default ignores
// filesystem permissions (noacl mount option). And this means, for
// example, that .exe that we install won't be runnable by Windows (MSYS2
diff --git a/build2/install/rule.hxx b/build2/install/rule.hxx
index ad0d8ec..617beab 100644
--- a/build2/install/rule.hxx
+++ b/build2/install/rule.hxx
@@ -70,6 +70,12 @@ namespace build2
virtual bool
uninstall_extra (const file&, const install_dir&) const;
+ // Extra update for install hooks. It is executed after the target has
+ // been updated but only for those that will actually be installed.
+ //
+ virtual target_state
+ update_extra (action, const target&) const;
+
// Installation "commands".
//
// If verbose is false, then only print the command at verbosity level 2
@@ -98,7 +104,7 @@ namespace build2
const path& name,
bool verbose);
- private:
+ protected:
target_state
perform_install (action, const target&) const;
diff --git a/build2/install/utility.hxx b/build2/install/utility.hxx
index dfd1915..21c0c44 100644
--- a/build2/install/utility.hxx
+++ b/build2/install/utility.hxx
@@ -51,6 +51,12 @@ namespace build2
{
return install_mode (s, T::static_type, move (m));
}
+
+ // Resolve relative installation directory path (e.g., include/libfoo) to
+ // its absolute directory path (e.g., /usr/include/libfoo).
+ //
+ dir_path
+ resolve_dir (const target&, dir_path); // rule.cxx
}
}
diff --git a/build2/pkgconfig/init.cxx b/build2/pkgconfig/init.cxx
index fa22421..9526aa7 100644
--- a/build2/pkgconfig/init.cxx
+++ b/build2/pkgconfig/init.cxx
@@ -10,6 +10,9 @@
#include <build2/diagnostics.hxx>
#include <build2/config/utility.hxx>
+#include <build2/install/utility.hxx>
+
+#include <build2/pkgconfig/target.hxx>
using namespace std;
using namespace butl;
@@ -128,7 +131,7 @@ namespace build2
bool
init (scope& rs,
- scope& bs,
+ scope&,
const location& loc,
unique_ptr<module_base>&,
bool,
@@ -136,14 +139,15 @@ namespace build2
const variable_map& hints)
{
tracer trace ("pkgconfig::init");
- l5 ([&]{trace << "for " << bs.out_path ();});
+ l5 ([&]{trace << "for " << rs.out_path ();});
// Load pkgconfig.config.
//
+ bool conf (true);
if (!cast_false<bool> (rs["pkgconfig.config.loaded"]))
{
if (!load_module (rs, rs, "pkgconfig.config", loc, optional, hints))
- return false;
+ conf = false;
}
else if (!cast_false<bool> (rs["pkgconfig.config.configured"]))
{
@@ -151,13 +155,21 @@ namespace build2
fail << "pkgconfig module could not be configured" <<
info << "re-run with -V option for more information";
- return false;
+ conf = false;
}
- // For now pkgconfig and pkgconfig.config is pretty much the same.
+ // Register the target type and configure its default "installability".
+ //
+ // Note that we do it whether we found pkg-config or not since these are
+ // used to produce .pc files which we do regardless.
//
+ rs.target_types.insert<pca> ();
+ rs.target_types.insert<pcs> ();
- return true;
+ if (cast_false<bool> (rs["install.loaded"]))
+ install::install_path<pc> (rs, dir_path ("pkgconfig"));
+
+ return conf;
}
}
}
diff --git a/build2/pkgconfig/target.cxx b/build2/pkgconfig/target.cxx
new file mode 100644
index 0000000..7752f58
--- /dev/null
+++ b/build2/pkgconfig/target.cxx
@@ -0,0 +1,54 @@
+// file : build2/pkgconfig/target.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/pkgconfig/target.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace pkgconfig
+ {
+ const target_type pc::static_type
+ {
+ "pc",
+ &file::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ extern const char pca_ext[] = "static.pc"; // VC14 rejects constexpr.
+
+ const target_type pca::static_type
+ {
+ "pca",
+ &pc::static_type,
+ &file_factory<pca, pca_ext>,
+ &target_extension_fix<pca_ext>,
+ &target_pattern_fix<pca_ext>,
+ &target_print_0_ext_verb, // Fixed extension, no use printing.
+ &file_search,
+ false
+ };
+
+ extern const char pcs_ext[] = "shared.pc"; // VC14 rejects constexpr.
+
+ const target_type pcs::static_type
+ {
+ "pcs",
+ &pc::static_type,
+ &file_factory<pcs, pcs_ext>,
+ &target_extension_fix<pcs_ext>,
+ &target_pattern_fix<pcs_ext>,
+ &target_print_0_ext_verb, // Fixed extension, no use printing.
+ &file_search,
+ false
+ };
+ }
+}
diff --git a/build2/pkgconfig/target.hxx b/build2/pkgconfig/target.hxx
new file mode 100644
index 0000000..feb8d80
--- /dev/null
+++ b/build2/pkgconfig/target.hxx
@@ -0,0 +1,48 @@
+// file : build2/pkgconfig/target.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_PKGCONFIG_TARGET_HXX
+#define BUILD2_PKGCONFIG_TARGET_HXX
+
+#include <build2/types.hxx>
+#include <build2/utility.hxx>
+
+#include <build2/target.hxx>
+
+namespace build2
+{
+ namespace pkgconfig
+ {
+ class pc: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ };
+
+ class pca: public pc // .static.pc
+ {
+ public:
+ using pc::pc;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class pcs: public pc // .shared.pc
+ {
+ public:
+ using pc::pc;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+ }
+}
+
+#endif // BUILD2_PKGCONFIG_TARGET_HXX
diff --git a/build2/target.cxx b/build2/target.cxx
index 3ee9850..40c73c0 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -700,26 +700,6 @@ namespace build2
false
};
- template <typename T, const char* ext>
- static pair<target*, optional<string>>
- file_factory (const target_type& tt,
- dir_path d,
- dir_path o,
- string n,
- optional<string> e)
- {
- // A generic file target type doesn't imply any extension while a very
- // specific one (say man1) may have a fixed extension. So if one wasn't
- // specified and this is not a dynamically derived target type, then set
- // it to fixed ext rather than unspecified. For file{} we make it empty
- // which means we treat file{foo} as file{foo.}.
- //
- if (!e && ext != nullptr && tt.factory == &file_factory<T, ext>)
- e = string (ext);
-
- return make_pair (new T (move (d), move (o), move (n)), move (e));
- }
-
extern const char file_ext_var[] = "extension"; // VC14 rejects constexpr.
extern const char file_ext_def[] = "";
diff --git a/build2/target.hxx b/build2/target.hxx
index 7cc7f95..3af533f 100644
--- a/build2/target.hxx
+++ b/build2/target.hxx
@@ -250,7 +250,8 @@ namespace build2
// - Member variable lookup skips the ad hoc group (since the group is
// the first member, this is normally what we want).
//
- // Use add_adhoc_member() from algorithms to add an ad hoc member.
+ // Use add_adhoc_member(), find_adhoc_member() from algorithms to manage
+ // ad hoc members.
//
const_ptr<target> member = nullptr;
@@ -1686,8 +1687,8 @@ namespace build2
virtual const target_type& dynamic_type () const {return static_type;}
};
- // Common implementation of the target factory, extension, and
- // search functions.
+ // Common implementation of the target factory, extension, and search
+ // functions.
//
template <typename T>
pair<target*, optional<string>>
@@ -1700,6 +1701,26 @@ namespace build2
return make_pair (new T (move (d), move (o), move (n)), move (e));
}
+ template <typename T, const char* ext>
+ static pair<target*, optional<string>>
+ file_factory (const target_type& tt,
+ dir_path d,
+ dir_path o,
+ string n,
+ optional<string> e)
+ {
+ // A generic file target type doesn't imply any extension while a very
+ // specific one (say man1) may have a fixed extension. So if one wasn't
+ // specified and this is not a dynamically derived target type, then set
+ // it to fixed ext rather than unspecified. For file{} itself we make it
+ // empty which means we treat file{foo} as file{foo.}.
+ //
+ if (!e && ext != nullptr && tt.factory == &file_factory<T, ext>)
+ e = string (ext);
+
+ return make_pair (new T (move (d), move (o), move (n)), move (e));
+ }
+
// Return fixed target extension.
//
template <const char* ext>
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 8e32b30..fbdedb3 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -1359,7 +1359,7 @@ namespace build2
vm.typify (const_cast<variable_map::value_data&> (*v), var);
}
- return lookup (v, &vm);
+ return lookup (*v, vm);
}
}
}
diff --git a/build2/variable.hxx b/build2/variable.hxx
index f0218fe..decc300 100644
--- a/build2/variable.hxx
+++ b/build2/variable.hxx
@@ -336,13 +336,18 @@ namespace build2
const value_type* operator-> () const {return value;}
// Return true if this value belongs to the specified scope or target.
- // Note that it can also be a target type/pattern-specific value (in
- // which case it won't belong to either).
+ // Note that it can also be a target type/pattern-specific value in which
+ // case it won't belong to either unless we pass true as a second argument
+ // to consider it belonging to a scope (note that this test is expensive).
//
template <typename T>
bool
belongs (const T& x) const {return vars == &x.vars;}
+ template <typename T>
+ bool
+ belongs (const T& x, bool target_type_pattern) const;
+
lookup (): value (nullptr), vars (nullptr) {}
template <typename T>
diff --git a/build2/variable.txx b/build2/variable.txx
index 1373948..f75ffd6 100644
--- a/build2/variable.txx
+++ b/build2/variable.txx
@@ -6,6 +6,28 @@
namespace build2
{
+ template <typename T>
+ bool lookup::
+ belongs (const T& x, bool t) const
+ {
+ if (vars == &x.vars)
+ return true;
+
+ if (t)
+ {
+ for (const auto& p1: x.target_vars) // variable_type_map
+ {
+ for (const auto& p2: p1.second) // variable_pattern_map
+ {
+ if (vars == &p2.second)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
// This one will be SFINAE'd out unless T is a simple value.
//
template <typename T>