aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-09-04 16:10:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-09-04 16:14:08 +0200
commit3fd36c27e9455dae10ed4f569ca4362219bbcbcb (patch)
tree8dd42450651d18fbe463001f19ed04f2ed66d180
parent5007870b52aa549971824959a55ad3bb886f09e0 (diff)
Initial work on binless (binary-less aka header-only) library support
-rw-r--r--build2/algorithm.cxx59
-rw-r--r--build2/algorithm.hxx3
-rw-r--r--build2/cc/common.hxx3
-rw-r--r--build2/cc/install-rule.cxx17
-rw-r--r--build2/cc/link-rule.cxx620
-rw-r--r--build2/cc/link-rule.hxx4
-rw-r--r--build2/cc/pkgconfig.cxx123
-rw-r--r--build2/cc/windows-rpath.cxx4
-rw-r--r--build2/install/rule.cxx35
9 files changed, 514 insertions, 354 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index bfe6943..ef33cbe 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -1952,10 +1952,18 @@ namespace build2
}
};
- auto ei (extra.begin ()), ee (extra.end ());
+ const path& fp (ft.path ());
+ auto ei (extra.begin ()), ee (extra.end ());
if (ei != ee)
- clean_extra (ft, nullptr, *ei++);
+ {
+ if (!fp.empty ())
+ clean_extra (ft, nullptr, *ei);
+
+ ++ei;
+ }
+
+ target_state tr (target_state::unchanged);
// Check if we were asked not to actually remove the files. The extras are
// tricky: some of them, like depdb should definitely be removed. But
@@ -1970,31 +1978,44 @@ namespace build2
//
for (const target* m (ft.member); m != nullptr; m = m->member)
{
- const file* fm (m->is_a<file> ());
- const path* fp (fm != nullptr ? &fm->path () : nullptr);
+ const file* mf (m->is_a<file> ());
+ const path* mp (mf != nullptr ? &mf->path () : nullptr);
- if (fm == nullptr || fp->empty ())
+ if (mf == nullptr || mp->empty ())
continue;
if (ei != ee)
- clean_extra (*fm, fp, *ei++);
+ clean_extra (*mf, mp, *ei++);
+
+ if (!clean)
+ continue;
- target_state r (clean && rmfile (*fp, 3)
- ? target_state::changed
- : target_state::unchanged);
+ // Make this "primary target" for diagnostics/result purposes if the
+ // primary target is unreal.
+ //
+ if (fp.empty ())
+ {
+ if (rmfile (*mp, *mf))
+ tr = target_state::changed;
+ }
+ else
+ {
+ target_state r (rmfile (*mp, 3)
+ ? target_state::changed
+ : target_state::unchanged);
- if (r == target_state::changed && ep.empty ())
- ep = *fp;
+ if (r == target_state::changed && ep.empty ())
+ ep = *mp;
- er |= r;
+ er |= r;
+ }
}
// Now clean the primary target and its prerequisited in the reverse order
// of update: first remove the file, then clean the prerequisites.
//
- target_state tr (clean && rmfile (ft.path (), ft) // Path must be assigned.
- ? target_state::changed
- : target_state::unchanged);
+ if (clean && !fp.empty () && rmfile (fp, ft))
+ tr = target_state::changed;
// Update timestamp in case there are operations after us that could use
// the information.
@@ -2032,13 +2053,17 @@ namespace build2
target_state
perform_clean (action a, const target& t)
{
- return clean_extra (a, t.as<file> (), {nullptr});
+ const file& f (t.as<file> ());
+ assert (!f.path ().empty ());
+ return clean_extra (a, f, {nullptr});
}
target_state
perform_clean_depdb (action a, const target& t)
{
- return clean_extra (a, t.as<file> (), {".d"});
+ const file& f (t.as<file> ());
+ assert (!f.path ().empty ());
+ return clean_extra (a, f, {".d"});
}
target_state
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 430cc9f..e908df8 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -656,6 +656,9 @@ namespace build2
//
// You can also clean extra files derived from ad hoc group members.
//
+ // Note that if the target path is empty then it is assumed "unreal" and
+ // is not cleaned (but its prerequisites/members still are).
+ //
target_state
clean_extra (action, const file&,
initializer_list<initializer_list<const char*>> extra);
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index d809050..952e383 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -144,7 +144,8 @@ namespace build2
const target_type* x_mod; // Module target type (mxx{}), if any.
// Array of target types that are considered headers. Keep them in the
- // most likely to appear order and terminate with NULL.
+ // most likely to appear order with the "header header" first and
+ // terminated with NULL.
//
const target_type* const* x_hdr;
diff --git a/build2/cc/install-rule.cxx b/build2/cc/install-rule.cxx
index d687903..b2b508e 100644
--- a/build2/cc/install-rule.cxx
+++ b/build2/cc/install-rule.cxx
@@ -174,7 +174,7 @@ namespace build2
else // install or uninstall
{
// Derive shared library paths and cache them in the target's aux
- // storage if we are un/installing (used in *_extra() functions
+ // storage if we are un/installing (used in the *_extra() functions
// below).
//
static_assert (sizeof (link_rule::libs_paths) <= target::data_size,
@@ -183,12 +183,15 @@ namespace build2
file* f;
if ((f = t.is_a<libs> ()) != nullptr && tclass != "windows")
{
- 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));
+ if (!f->path ().empty ()) // Not binless.
+ {
+ 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));
+ }
}
}
diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx
index 5ebb97a..b644174 100644
--- a/build2/cc/link-rule.cxx
+++ b/build2/cc/link-rule.cxx
@@ -82,11 +82,14 @@ namespace build2
if (include (a, t, p) != include_type::normal)
continue;
- if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)))
+ if (p.is_a (x_src) ||
+ (lt.library () && p.is_a (*x_hdr[0])) || // Header-only library.
+ (x_mod != nullptr && p.is_a (*x_mod)))
{
seen_x = seen_x || true;
}
- else if (p.is_a<c> ())
+ else if (p.is_a<c> () ||
+ (lt.library () && p.is_a<h> ())) // Header-only library.
{
seen_c = seen_c || true;
}
@@ -297,6 +300,41 @@ namespace build2
return libs_paths {move (lk), move (so), move (in), &re, move (cp)};
}
+ // Look for binary-full utility library recursively until we hit a
+ // non-utility "barier".
+ //
+ static bool
+ find_binfull (action a, const target& t, linfo li)
+ {
+ for (const target* pt: t.prerequisite_targets[a])
+ {
+ if (pt == nullptr || unmark (pt) != 0) // Called after pass 1 below.
+ continue;
+
+ const file* pf;
+
+ // If this is the libu*{} group, then pick the appropriate member.
+ //
+ const libx* ul;
+ if ((ul = pt->is_a<libul> ()) ||
+ (ul = pt->is_a<libu> ()))
+ {
+ pf = &link_member (*ul, a, li).as<file> ();
+ }
+ else if ((pf = pt->is_a<libue> ()) ||
+ (pf = pt->is_a<libus> ()) ||
+ (pf = pt->is_a<libua> ()))
+ ;
+ else
+ continue;
+
+ if (!pf->path ().empty () || find_binfull (a, *pf, li))
+ return true;
+ }
+
+ return false;
+ };
+
recipe link_rule::
apply (action a, target& xt) const
{
@@ -321,218 +359,11 @@ namespace build2
if (lt.library ())
t.vars.assign (c_type) = string (x);
- // Derive file name(s) and add ad hoc group members.
- //
- {
- target_lock libi; // Have to hold until after PDB member addition.
-
- const char* e (nullptr); // Extension.
- const char* p (nullptr); // Prefix.
- const char* s (nullptr); // Suffix.
-
- if (lt.utility)
- {
- // These are all static libraries with names indicating the kind of
- // object files they contain (similar to how we name object files
- // themselves). We add the 'u' extension to avoid clashes with
- // real libraries/import stubs.
- //
- // libue libhello.u.a hello.exe.u.lib
- // libua libhello.a.u.a hello.lib.u.lib
- // libus libhello.so.u.a hello.dll.u.lib hello.dylib.u.lib
- //
- // Note that we currently don't add bin.lib.{prefix,suffix} since
- // these are not installed.
- //
- if (tsys == "win32-msvc")
- {
- switch (ot)
- {
- case otype::e: e = "exe.u.lib"; break;
- case otype::a: e = "lib.u.lib"; break;
- case otype::s: e = "dll.u.lib"; break;
- }
- }
- else
- {
- p = "lib";
-
- if (tsys == "mingw32")
- {
- switch (ot)
- {
- case otype::e: e = "exe.u.a"; break;
- case otype::a: e = "a.u.a"; break;
- case otype::s: e = "dll.u.a"; break;
- }
-
- }
- else if (tsys == "darwin")
- {
- switch (ot)
- {
- case otype::e: e = "u.a"; break;
- case otype::a: e = "a.u.a"; break;
- case otype::s: e = "dylib.u.a"; break;
- }
- }
- else
- {
- switch (ot)
- {
- case otype::e: e = "u.a"; break;
- case otype::a: e = "a.u.a"; break;
- case otype::s: e = "so.u.a"; break;
- }
- }
- }
-
- t.derive_path (e, p, s);
- }
- 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:
- {
- if (tclass == "windows")
- e = "exe";
- else
- e = "";
+ bool binless (lt.library ()); // Binary-less until proven otherwise.
- t.derive_path (e, p, s);
- break;
- }
- case otype::a:
- {
- if (tsys == "win32-msvc")
- e = "lib";
- else
- {
- if (p == nullptr) p = "lib";
- e = "a";
- }
-
- t.derive_path (e, p, s);
- break;
- }
- case otype::s:
- {
- // On Windows libs{} is an ad hoc group. The libs{} itself is
- // the DLL and we add libi{} import library as its member.
- //
- if (tclass == "windows")
- libi = add_adhoc_member<bin::libi> (a, t);
-
- md.libs_data = derive_libs_paths (t, p, s);
-
- if (libi)
- match_recipe (libi, group_recipe); // Set recipe and unlock.
-
- break;
- }
- }
-
- // Add VC's .pdb. Note that we are looking for the link.exe /DEBUG
- // option.
- //
- if (ot != otype::a &&
- tsys == "win32-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 (a, 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.
- }
-
- // Add pkg-config's .pc file.
- //
- // 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 for-install hasn't yet been
- // communicated by install_rule. Secondly, always having the member
- // takes care of cleanup automagically. The actual generation
- // happens in perform_update() below.
- //
- if (ot != otype::e)
- {
- target_lock pc (
- add_adhoc_member (
- a, t,
- ot == otype::a ? pca::static_type : 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 (pc, group_recipe); // Set recipe and unlock.
- }
-
- // Add the Windows rpath emulating assembly directory as fsdir{}.
- //
- // Currently this is used in the backlinking logic and in the future
- // could also be used for clean (though there we may want to clean
- // old assemblies).
- //
- if (ot == otype::e && tclass == "windows")
- {
- // Note that here we cannot determine whether we will actually
- // need one (for_install, library timestamps are not available at
- // this point to call windows_rpath_timestamp()). So we may add
- // the ad hoc target but actually not produce the assembly. So
- // whomever relies on this must check if the directory actually
- // exists (windows_rpath_assembly() does take care to clean it up
- // if not used).
- //
- target_lock dir (
- add_adhoc_member (
- a,
- t,
- fsdir::static_type,
- path_cast<dir_path> (t.path () + ".dlls"),
- t.out,
- string ()));
-
- // By default our backlinking logic will try to symlink the
- // directory and it can even be done on Windows using junctions.
- // The problem is the Windows DLL assembly "logic" refuses to
- // recognize a junction as a valid assembly for some reason. So we
- // are going to resort to copy-link (i.e., a real directory with a
- // bunch on links).
- //
- // Interestingly, the directory symlink works just fine under
- // Wine. So we only resort to copy-link'ing if we are running
- // on Windows.
- //
-#ifdef _WIN32
- dir.target->assign (var_backlink) = "copy";
-#endif
- match_recipe (dir, group_recipe); // Set recipe and unlock.
- }
- }
- }
-
- // Inject dependency on the output directory.
+ // Inject dependency on the output directory. Note that we do it even
+ // for binless libraries since there could be other output (e.g., .pc
+ // files).
//
inject_fsdir (a, t);
@@ -540,6 +371,11 @@ namespace build2
// libraries, search obj/bmi{} targets, and search targets we do rule
// chaining for.
//
+ // Also clear the binless flag if we see any source or object files.
+ // Note that if we don't see any this still doesn't mean the library is
+ // binless since it can depend on a binfull utility library. This we
+ // check below, after matching the libraries.
+ //
// We do libraries first in order to indicate that we will execute these
// targets before matching any of the obj/bmi{}. This makes it safe for
// compile::apply() to unmatch them and therefore not to hinder
@@ -595,6 +431,8 @@ namespace build2
if (mod || p.is_a (x_src) || p.is_a<c> ())
{
+ binless = binless && false;
+
// Rule chaining, part 1.
//
@@ -730,6 +568,8 @@ namespace build2
continue;
}
+ binless = binless && !(pt->is_a<objx> () || pt->is_a<bmix> ());
+
m = 3;
}
@@ -740,6 +580,237 @@ namespace build2
//
match_members (a, t, pts, start);
+ // Check if we have any binfull utility libraries.
+ //
+ binless = binless && !find_binfull (a, t, li);
+
+ // Now that we know for sure whether we are binless, derive file name(s)
+ // and add ad hoc group members. Note that for binless we still need the
+ // .pc member (whose name depends on the libray prefix) so we take care
+ // to not derive the path for the library target itself inside.
+ //
+ {
+ target_lock libi; // Have to hold until after PDB member addition.
+
+ const char* e (nullptr); // Extension.
+ const char* p (nullptr); // Prefix.
+ const char* s (nullptr); // Suffix.
+
+ if (lt.utility)
+ {
+ // These are all static libraries with names indicating the kind of
+ // object files they contain (similar to how we name object files
+ // themselves). We add the 'u' extension to avoid clashes with
+ // real libraries/import stubs.
+ //
+ // libue libhello.u.a hello.exe.u.lib
+ // libua libhello.a.u.a hello.lib.u.lib
+ // libus libhello.so.u.a hello.dll.u.lib hello.dylib.u.lib
+ //
+ // Note that we currently don't add bin.lib.{prefix,suffix} since
+ // these are not installed.
+ //
+ if (tsys == "win32-msvc")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "exe.u.lib"; break;
+ case otype::a: e = "lib.u.lib"; break;
+ case otype::s: e = "dll.u.lib"; break;
+ }
+ }
+ else
+ {
+ p = "lib";
+
+ if (tsys == "mingw32")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "exe.u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "dll.u.a"; break;
+ }
+
+ }
+ else if (tsys == "darwin")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "dylib.u.a"; break;
+ }
+ }
+ else
+ {
+ switch (ot)
+ {
+ case otype::e: e = "u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "so.u.a"; break;
+ }
+ }
+ }
+
+ if (binless)
+ t.path (empty_path);
+ else
+ t.derive_path (e, p, s);
+ }
+ 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:
+ {
+ if (tclass == "windows")
+ e = "exe";
+ else
+ e = "";
+
+ t.derive_path (e, p, s);
+ break;
+ }
+ case otype::a:
+ {
+ if (tsys == "win32-msvc")
+ e = "lib";
+ else
+ {
+ if (p == nullptr) p = "lib";
+ e = "a";
+ }
+
+ if (binless)
+ t.path (empty_path);
+ else
+ t.derive_path (e, p, s);
+
+ break;
+ }
+ case otype::s:
+ {
+ if (binless)
+ t.path (empty_path);
+ else
+ {
+ // On Windows libs{} is an ad hoc group. The libs{} itself is
+ // the DLL and we add libi{} import library as its member.
+ //
+ if (tclass == "windows")
+ libi = add_adhoc_member<bin::libi> (a, t);
+
+ md.libs_data = derive_libs_paths (t, p, s);
+
+ if (libi)
+ match_recipe (libi, group_recipe); // Set recipe and unlock.
+ }
+
+ break;
+ }
+ }
+
+ // Add VC's .pdb. Note that we are looking for the link.exe /DEBUG
+ // option.
+ //
+ if (!binless && ot != otype::a && tsys == "win32-msvc")
+ {
+ if (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 (a, 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.
+ }
+ }
+
+ // Add pkg-config's .pc file.
+ //
+ // Note that we do it regardless of whether we are installing or not
+ // for two reasons. Firstly, it is not easy to detect this situation
+ // here since the for-install hasn't yet been communicated by
+ // install_rule. Secondly, always having this member takes care of
+ // cleanup automagically. The actual generation happens in
+ // perform_update() below.
+ //
+ if (ot != otype::e)
+ {
+ target_lock pc (
+ add_adhoc_member (
+ a, t,
+ ot == otype::a ? pca::static_type : 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 refer 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 (pc, group_recipe); // Set recipe and unlock.
+ }
+
+ // Add the Windows rpath emulating assembly directory as fsdir{}.
+ //
+ // Currently this is used in the backlinking logic and in the future
+ // could also be used for clean (though there we may want to clean
+ // old assemblies).
+ //
+ if (ot == otype::e && tclass == "windows")
+ {
+ // Note that here we cannot determine whether we will actually
+ // need one (for_install, library timestamps are not available at
+ // this point to call windows_rpath_timestamp()). So we may add
+ // the ad hoc target but actually not produce the assembly. So
+ // whomever relies on this must check if the directory actually
+ // exists (windows_rpath_assembly() does take care to clean it up
+ // if not used).
+ //
+ target_lock dir (
+ add_adhoc_member (
+ a,
+ t,
+ fsdir::static_type,
+ path_cast<dir_path> (t.path () + ".dlls"),
+ t.out,
+ string ()));
+
+ // By default our backlinking logic will try to symlink the
+ // directory and it can even be done on Windows using junctions.
+ // The problem is the Windows DLL assembly "logic" refuses to
+ // recognize a junction as a valid assembly for some reason. So we
+ // are going to resort to copy-link (i.e., a real directory with a
+ // bunch on links).
+ //
+ // Interestingly, the directory symlink works just fine under
+ // Wine. So we only resort to copy-link'ing if we are running
+ // on Windows.
+ //
+#ifdef _WIN32
+ dir.target->assign (var_backlink) = "copy";
+#endif
+ match_recipe (dir, group_recipe); // Set recipe and unlock.
+ }
+ }
+ }
+
// Process prerequisites, pass 2: finish rule chaining but don't start
// matching anything yet since that may trigger recursive matching of
// bmi{} targets we haven't completed yet. Hairy, I know.
@@ -1056,6 +1127,8 @@ namespace build2
}
}
+ md.binless = binless;
+
switch (a)
{
case perform_update_id: return [this] (action a, const target& t)
@@ -1098,6 +1171,9 @@ namespace build2
}
else
{
+ if (l->path ().empty ()) // Binless.
+ return;
+
bool lu (l->is_a<libux> ());
// The utility/non-utility case is tricky. Consider these two
@@ -1249,6 +1325,9 @@ namespace build2
}
else
{
+ if (l->path ().empty ()) // Binless.
+ return;
+
bool lu (l->is_a<libux> ());
if (lu)
@@ -1368,6 +1447,9 @@ namespace build2
{
if (!l->is_a<libs> ())
return;
+
+ if (l->path ().empty ()) // Binless.
+ return;
}
else
{
@@ -1428,12 +1510,16 @@ namespace build2
{
if (!for_install && !la)
{
- // Top-level shared library dependency. It is either matched or
- // imported so should be a cc library.
+ // Top-level shared library dependency.
//
- if (!cast_false<bool> (f->vars[c_system]))
- args.push_back (
- "-Wl,-rpath," + f->path ().directory ().string ());
+ if (!f->path ().empty ()) // Not binless.
+ {
+ // It is either matched or imported so should be a cc library.
+ //
+ if (!cast_false<bool> (f->vars[c_system]))
+ args.push_back (
+ "-Wl,-rpath," + f->path ().directory ().string ());
+ }
}
process_libraries (a, bs, li, sys_lib_dirs,
@@ -1478,15 +1564,41 @@ namespace build2
otype ot (lt.type);
linfo li (link_info (bs, ot));
+ bool binless (md.binless);
+ assert (ot != otype::e || !binless); // Sanity check.
+
// Update prerequisites. We determine if any relevant ones render us
// out-of-date manually below.
//
// Note that straight_execute_prerequisites() will blank out all the ad
// hoc prerequisites so we don't need to worry about them from now on.
//
+ target_state ts (straight_execute_prerequisites (a, t));
+
+ // (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.
+ //
+ if (for_install && lt.library () && !lt.utility)
+ pkgconfig_save (a, t, lt.static_library (), binless);
+
+ // If we have no binary to build then we are done.
+ //
+ if (binless)
+ {
+ t.mtime (timestamp_unreal);
+ return ts;
+ }
+
+ // Determine if we are out-of-date.
+ //
bool update (false);
timestamp mt (t.load_mtime ());
- target_state ts (straight_execute_prerequisites (a, t));
// Open the dependency database (do it before messing with Windows
// manifests to diagnose missing output directory).
@@ -1925,25 +2037,6 @@ namespace build2
dd.close ();
- // (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.
- //
- if (for_install)
- {
- bool la;
- const file* f;
-
- if ((la = (f = t.is_a<liba> ())) ||
- ( f = t.is_a<libs> ()))
- pkgconfig_save (a, *f, la);
- }
-
// If nothing changed, then we are done.
//
if (!update)
@@ -2438,33 +2531,42 @@ namespace build2
return clean_extra (
a, t, {".d", ".dlls/", ".manifest", "-.ilk"});
}
+ // For other platforms it's the defaults.
}
- else if (lt.shared_library ())
+ else
{
- if (tclass == "windows")
- {
- // Assuming it's VC or alike. Clean up .exp and .ilk.
- //
- // Note that .exp is based on the .lib, not .dll name. And with
- // versioning their bases may not be the same.
- //
- if (tsys != "mingw32")
- return clean_extra (a, t, {{".d", "-.ilk"}, {"-.exp"}});
- }
- else
+ const match_data& md (t.data<match_data> ());
+
+ if (md.binless)
+ return clean_extra (a, t, {nullptr}); // Clean prerequsites/members.
+
+ if (lt.shared_library ())
{
- // Here we can have a bunch of symlinks that we need to remove. If
- // the paths are empty, then they will be ignored.
- //
- const libs_paths& paths (t.data<match_data> ().libs_data);
+ if (tclass == "windows")
+ {
+ // Assuming it's VC or alike. Clean up .exp and .ilk.
+ //
+ // Note that .exp is based on the .lib, not .dll name. And with
+ // versioning their bases may not be the same.
+ //
+ if (tsys != "mingw32")
+ return clean_extra (a, t, {{".d", "-.ilk"}, {"-.exp"}});
+ }
+ else
+ {
+ // Here we can have a bunch of symlinks that we need to remove. If
+ // the paths are empty, then they will be ignored.
+ //
+ const libs_paths& paths (md.libs_data);
- return clean_extra (a, t, {".d",
- paths.link.string ().c_str (),
- paths.soname.string ().c_str (),
- paths.interm.string ().c_str ()});
+ return clean_extra (a, t, {".d",
+ paths.link.string ().c_str (),
+ paths.soname.string ().c_str (),
+ paths.interm.string ().c_str ()});
+ }
}
+ // For static library it's the defaults.
}
- // For static library it's just the defaults.
return clean_extra (a, t, {".d"});
}
diff --git a/build2/cc/link-rule.hxx b/build2/cc/link-rule.hxx
index ee4a2bd..47bfdf1 100644
--- a/build2/cc/link-rule.hxx
+++ b/build2/cc/link-rule.hxx
@@ -98,6 +98,8 @@ namespace build2
//
optional<bool> for_install;
+ bool binless; // Binary-less library.
+
libs_paths libs_data;
};
@@ -155,7 +157,7 @@ namespace build2
// pkg-config's .pc file generation (pkgconfig.cxx).
//
void
- pkgconfig_save (action, const file&, bool) const;
+ pkgconfig_save (action, const file&, bool, bool) const;
private:
const string rule_id;
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index 4a8d995..795e5d7 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -1114,7 +1114,7 @@ namespace build2
#endif
void link_rule::
- pkgconfig_save (action a, const file& l, bool la) const
+ pkgconfig_save (action a, const file& l, bool la, bool binless) const
{
tracer trace (x, "pkgconfig_save");
@@ -1132,7 +1132,9 @@ namespace build2
using install::resolve_dir;
dir_path idir (resolve_dir (l, cast<dir_path> (l["install.include"])));
- dir_path ldir (resolve_dir (l, cast<dir_path> (l["install.lib"])));
+ dir_path ldir (binless
+ ? dir_path ()
+ : resolve_dir (l, cast<dir_path> (l["install.lib"])));
if (verb >= 2)
text << "cat >" << p;
@@ -1256,75 +1258,78 @@ namespace build2
// the second position, not first. Gets even trickier with
// Libs.private split.
//
- os << "Libs:";
- os << " -L" << escape (ldir.string ());
-
- // Now process ourselves as if we were being linked to something (so
- // pretty similar to link_rule::append_libraries()).
- //
- bool priv (false);
- auto imp = [&priv] (const file&, bool la) {return priv && la;};
-
- auto lib = [&os, &save_library] (const file* const* c,
- const string& p,
- lflags,
- bool)
+ if (!binless)
{
- const file* l (c != nullptr ? *c : nullptr);
+ os << "Libs:";
+ os << " -L" << escape (ldir.string ());
+
+ // Now process ourselves as if we were being linked to something (so
+ // pretty similar to link_rule::append_libraries()).
+ //
+ bool priv (false);
+ auto imp = [&priv] (const file&, bool la) {return priv && la;};
- if (l != nullptr)
+ auto lib = [&os, &save_library] (const file* const* c,
+ const string& p,
+ lflags,
+ bool)
{
- if (l->is_a<libs> () || l->is_a<liba> ()) // See through libux.
- save_library (*l);
- }
- else
- os << ' ' << p; // Something "system'y", pass as is.
- };
+ const file* l (c != nullptr ? *c : nullptr);
- auto opt = [] (const file&,
- const string&,
- bool, bool)
- {
- //@@ TODO: should we filter -L similar to -I?
- //@@ TODO: how will the Libs/Libs.private work?
- //@@ TODO: remember to use escape()
+ 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.
+ };
- /*
- // If we need an interface value, then use the group (lib{}).
- //
- if (const target* g = exp && l.is_a<libs> () ? l.group : &l)
+ auto opt = [] (const file&,
+ const string&,
+ bool, bool)
{
- 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);
- }
- */
- };
+ //@@ TODO: should we filter -L similar to -I?
+ //@@ TODO: how will the Libs/Libs.private work?
+ //@@ TODO: remember to use escape()
- // Pretend we are linking an executable using what would be normal,
- // system-default link order.
- //
- linfo li {otype::e, la ? lorder::a_s : lorder::s_a};
-
- process_libraries (a, bs, li, sys_lib_dirs,
- l, la, 0, // Link flags.
- imp, lib, opt, true);
- os << endl;
+ /*
+ // 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);
+ }
+ */
+ };
- if (la)
- {
- os << "Libs.private:";
+ // Pretend we are linking an executable using what would be normal,
+ // system-default link order.
+ //
+ linfo li {otype::e, la ? lorder::a_s : lorder::s_a};
- priv = true;
process_libraries (a, bs, li, sys_lib_dirs,
l, la, 0, // Link flags.
- imp, lib, opt, false);
+ imp, lib, opt, true);
os << endl;
+
+ if (la)
+ {
+ os << "Libs.private:";
+
+ priv = true;
+ process_libraries (a, bs, li, sys_lib_dirs,
+ l, la, 0, // Link flags.
+ imp, lib, opt, false);
+ os << endl;
+ }
}
// If we have modules, list them in the modules variable. We also save
diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx
index 268bab2..507395c 100644
--- a/build2/cc/windows-rpath.cxx
+++ b/build2/cc/windows-rpath.cxx
@@ -77,7 +77,7 @@ namespace build2
{
// This can be an "undiscovered" DLL (see search_library()).
//
- if (!l->is_a<libs> () || l->path ().empty ())
+ if (!l->is_a<libs> () || l->path ().empty ()) // Covers binless.
return;
}
else
@@ -152,7 +152,7 @@ namespace build2
if (l != nullptr)
{
- if (l->is_a<libs> () && !l->path ().empty ())
+ if (l->is_a<libs> () && !l->path ().empty ()) // Covers binless.
{
// Get .pdb if there is one.
//
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
index 67f0e86..020551c 100644
--- a/build2/install/rule.cxx
+++ b/build2/install/rule.cxx
@@ -847,7 +847,11 @@ namespace build2
perform_install (action a, const target& xt) const
{
const file& t (xt.as<file> ());
- assert (!t.path ().empty ()); // Should have been assigned by update.
+ const path& tp (t.path ());
+
+ // Path should have been assigned by update unless it is unreal.
+ //
+ assert (!tp.empty () || t.mtime () == timestamp_unreal);
const scope& rs (t.root_scope ());
@@ -916,15 +920,22 @@ namespace build2
for (const target* m (t.member); m != nullptr; m = m->member)
{
if (const path* p = lookup_install<path> (*m, "install"))
- install_target (m->as<file> (), *p, false);
+ {
+ install_target (m->as<file> (), *p, tp.empty () /* verbose */);
+ r |= target_state::changed;
+ }
}
// Finally install the target itself (since we got here we know the
// install variable is there).
//
- install_target (t, cast<path> (t["install"]), true);
+ if (!tp.empty ())
+ {
+ install_target (t, cast<path> (t["install"]), true /* verbose */);
+ r |= target_state::changed;
+ }
- return (r |= target_state::changed);
+ return r;
}
// uninstall -d <dir>
@@ -1103,7 +1114,11 @@ namespace build2
perform_uninstall (action a, const target& xt) const
{
const file& t (xt.as<file> ());
- assert (!t.path ().empty ()); // Should have been assigned by update.
+ const path& tp (t.path ());
+
+ // Path should have been assigned by update unless it is unreal.
+ //
+ assert (!tp.empty () || t.mtime () == timestamp_unreal);
const scope& rs (t.root_scope ());
@@ -1154,7 +1169,10 @@ namespace build2
// Reverse order of installation: first the target itself (since we got
// here we know the install variable is there).
//
- target_state r (uninstall_target (t, cast<path> (t["install"]), true));
+ target_state r (target_state::unchanged);
+
+ if (!tp.empty ())
+ r |= uninstall_target (t, cast<path> (t["install"]), true);
// Then installable ad hoc group members, if any. To be anally precise
// we would have to do it in reverse, but that's not easy (it's a
@@ -1163,8 +1181,9 @@ namespace build2
for (const target* m (t.member); m != nullptr; m = m->member)
{
if (const path* p = lookup_install<path> (*m, "install"))
- r |= uninstall_target (
- m->as<file> (), *p, r != target_state::changed);
+ r |= uninstall_target (m->as<file> (),
+ *p,
+ tp.empty () || r != target_state::changed);
}
// Finally handle installable prerequisites.