aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-10-26 11:15:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-10-26 13:29:25 +0200
commita7c48347e8437d3b699691bc1eea9e76e93604b6 (patch)
tree3fa67c6f23dd29cd47e5d31e0045e763e788d7d3
parent37cccafddbaeb3661998125ef41775ba974ed149 (diff)
WIP: install
-rw-r--r--libbuild2/bash/rule.cxx4
-rw-r--r--libbuild2/bash/rule.hxx2
-rw-r--r--libbuild2/bin/target.hxx15
-rw-r--r--libbuild2/cc/install-rule.cxx298
-rw-r--r--libbuild2/cc/install-rule.hxx29
-rw-r--r--libbuild2/cc/module.cxx3
-rw-r--r--libbuild2/install/rule.cxx164
-rw-r--r--libbuild2/install/rule.hxx96
8 files changed, 448 insertions, 163 deletions
diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx
index 502a206..6e96b34 100644
--- a/libbuild2/bash/rule.cxx
+++ b/libbuild2/bash/rule.cxx
@@ -455,9 +455,9 @@ namespace build2
}
recipe install_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
{
- recipe r (file_rule::apply_impl (a, t));
+ recipe r (file_rule::apply_impl (a, t, me));
if (r == nullptr)
return noop_recipe;
diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx
index 444d176..3f9618f 100644
--- a/libbuild2/bash/rule.hxx
+++ b/libbuild2/bash/rule.hxx
@@ -74,7 +74,7 @@ namespace build2
match (action, target&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
protected:
const in_rule& in_;
diff --git a/libbuild2/bin/target.hxx b/libbuild2/bin/target.hxx
index 9685e39..8f2a92e 100644
--- a/libbuild2/bin/target.hxx
+++ b/libbuild2/bin/target.hxx
@@ -412,6 +412,21 @@ namespace build2
virtual group_view
group_members (action) const override;
+ // Match options for the install operation on the liba{}/libs{} and
+ // libua{}/libus{} target types (note: not lib{}/libul{} nor libue{}).
+ //
+ // If only install_runtime option is specified, then only install the
+ // runtime files omitting everything buildtime (headers, pkg-config
+ // files, shared library version-related symlinks, etc).
+ //
+ // Note that it's either runtime-only or runtime and buildtime (i.e.,
+ // everything), so match with install_all instead of install_buildtime
+ // (the latter is only useful in the rule implementations).
+ //
+ static constexpr uint64_t option_install_runtime = 0x01;
+ static constexpr uint64_t option_install_buildtime = 0x02;
+ static constexpr uint64_t option_install_all = match_extra::all_options;
+
public:
static const target_type static_type;
};
diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx
index 3eaaa94..4ed674a 100644
--- a/libbuild2/cc/install-rule.cxx
+++ b/libbuild2/cc/install-rule.cxx
@@ -24,14 +24,17 @@ namespace build2
install_rule (data&& d, const link_rule& l)
: common (move (d)), link_ (l) {}
- const target* install_rule::
+ pair<const target*, uint64_t> install_rule::
filter (const scope* is,
- action a, const target& t, prerequisite_iterator& i) const
+ action a, const target& t, prerequisite_iterator& i,
+ match_extra& me) const
{
// NOTE: see libux_install_rule::filter() if changing anything here.
const prerequisite& p (i->prerequisite);
+ uint64_t options (match_extra::all_options);
+
// If this is a shared library prerequisite, install it as long as it is
// in the installation scope.
//
@@ -64,23 +67,58 @@ namespace build2
if (const libx* l = pt->is_a<libx> ())
pt = link_member (*l, a, link_info (t.base_scope (), ot));
- // Note: not redundant since we are returning a member.
+
+ // Note: not redundant since we could be returning a member.
//
if ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ()))
- return is == nullptr || pt->in (*is) ? pt : nullptr;
+ {
+ // Adjust match options.
+ //
+ if (a.operation () != update_id)
+ {
+ if (t.is_a<exe> ())
+ options = lib::option_install_runtime;
+ else
+ {
+ // This is a library prerequisite of a library target and
+ // runtime-only begets runtime-only.
+ //
+ if (me.cur_options == lib::option_install_runtime)
+ options = lib::option_install_runtime;
+ }
+ }
+
+ return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr,
+ options);
+ }
// See through to libu*{} members. Note that we are always in the same
// project (and thus amalgamation).
//
if (pt->is_a<libux> ())
- return pt;
+ {
+ // Adjust match options (similar to above).
+ //
+ if (a.operation () != update_id && !pt->is_a<libue> ())
+ {
+ if (t.is_a<exe> ())
+ options = lib::option_install_runtime;
+ else
+ {
+ if (me.cur_options == lib::option_install_runtime)
+ options = lib::option_install_runtime;
+ }
+ }
+
+ return make_pair (pt, options);
+ }
}
// The rest of the tests only succeed if the base filter() succeeds.
//
- const target* pt (file_rule::filter (is, a, t, p));
+ const target* pt (file_rule::filter (is, a, t, p, me).first);
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
// Don't install executable's prerequisite headers and module
// interfaces.
@@ -115,7 +153,7 @@ namespace build2
}
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
}
// Here is a problem: if the user spells the obj*/bmi*{} targets
@@ -145,16 +183,16 @@ namespace build2
{
pt = t.is_a<exe> ()
? nullptr
- : file_rule::filter (is, a, *pt, pm.prerequisite);
+ : file_rule::filter (is, a, *pt, pm.prerequisite, me).first;
break;
}
}
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
}
- return pt;
+ return make_pair (pt, options);
}
bool install_rule::
@@ -172,6 +210,7 @@ namespace build2
struct install_match_data
{
build2::recipe recipe;
+ uint64_t options; // Match options.
link_rule::libs_paths libs_paths;
target_state
@@ -182,12 +221,31 @@ namespace build2
};
recipe install_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
{
- recipe r (file_rule::apply_impl (a, t));
+ // Handle match options.
+ //
+ // Do it before calling apply_impl() since we need this information
+ // in the filter() callbacks.
+ //
+ if (a.operation () != update_id)
+ {
+ if (!t.is_a<exe> ())
+ {
+ if (me.new_options == 0)
+ me.new_options = lib::option_install_runtime; // Minimum we can do.
+
+ me.cur_options = me.new_options;
+ }
+ }
+
+ recipe r (file_rule::apply_impl (a, t, me));
if (r == nullptr)
+ {
+ me.cur_options = match_extra::all_options; // Noop for all options.
return noop_recipe;
+ }
if (a.operation () == update_id)
{
@@ -217,11 +275,17 @@ namespace build2
{
if (!f->path ().empty ()) // Not binless.
{
+ // Note: we could omit deriving the paths if cur_options doesn't
+ // have the buildtime option. But then we would have to duplicate
+ // this code in reapply() below (where this bit could be added).
+ // So let's keep it simple if a bit inefficient for now.
+ //
const string* p (cast_null<string> (t["bin.lib.prefix"]));
const string* s (cast_null<string> (t["bin.lib.suffix"]));
return install_match_data {
move (r),
+ me.cur_options,
link_.derive_libs_paths (*f,
p != nullptr ? p->c_str (): nullptr,
s != nullptr ? s->c_str (): nullptr)};
@@ -232,6 +296,41 @@ namespace build2
return r;
}
+ void install_rule::
+ reapply (action a, target& t, match_extra& me) const
+ {
+ assert (a.operation () != update_id && !t.is_a<exe> ());
+
+ text << "REMATCH " << t
+ << ' ' << me.cur_options
+ << ' ' << me.new_options; // @@ TMP
+
+ // If we are rematched with the buildtime option, propagate it to our
+ // prerequisite libraries.
+ //
+ if ((me.new_options & lib::option_install_buildtime) != 0)
+ {
+ for (const target* pt: t.prerequisite_targets[a])
+ {
+ if (pt != nullptr && (pt->is_a<liba> () || pt->is_a<libs> () ||
+ pt->is_a<libua> () || pt->is_a<libus> ()))
+ rematch_sync (a, *pt, lib::option_install_buildtime);
+ }
+ }
+
+ // @@ TODO: match additional prerequisites if required.
+
+ me.cur_options |= me.new_options;
+
+ // Update options in install_match_data.
+ //
+ if (file* f = t.is_a<libs> ())
+ {
+ if (!f->path ().empty ()) // Not binless.
+ t.data<install_match_data> (a).options = me.cur_options;
+ }
+ }
+
bool install_rule::
install_extra (const file& t, const install_dir& id) const
{
@@ -239,28 +338,33 @@ namespace build2
if (t.is_a<libs> ())
{
- // Here we may have a bunch of symlinks that we need to install.
- //
- const scope& rs (t.root_scope ());
- auto& lp (t.data<install_match_data> (perform_install_id).libs_paths);
+ const auto& md (t.data<install_match_data> (perform_install_id));
- auto ln = [&t, &rs, &id] (const path& f, const path& l)
+ if ((md.options & lib::option_install_buildtime) != 0)
{
- install_l (rs, id, l.leaf (), t, f.leaf (), 2 /* verbosity */);
- return true;
- };
+ // Here we may have a bunch of symlinks that we need to install.
+ //
+ const scope& rs (t.root_scope ());
+ const link_rule::libs_paths& lp (md.libs_paths);
- const path& lk (lp.link);
- const path& ld (lp.load);
- const path& so (lp.soname);
- const path& in (lp.interm);
+ auto ln = [&t, &rs, &id] (const path& f, const path& l)
+ {
+ install_l (rs, id, l.leaf (), t, f.leaf (), 2 /* verbosity */);
+ return true;
+ };
+
+ const path& lk (lp.link);
+ const path& ld (lp.load);
+ const path& so (lp.soname);
+ const path& in (lp.interm);
- const path* f (lp.real);
+ const path* f (lp.real);
- if (!in.empty ()) {r = ln (*f, in) || r; f = &in;}
- if (!so.empty ()) {r = ln (*f, so) || r; f = &so;}
- if (!ld.empty ()) {r = ln (*f, ld) || r; f = &ld;}
- if (!lk.empty ()) {r = ln (*f, lk) || r; }
+ if (!in.empty ()) {r = ln (*f, in) || r; f = &in;}
+ if (!so.empty ()) {r = ln (*f, so) || r; f = &so;}
+ if (!ld.empty ()) {r = ln (*f, ld) || r; f = &ld;}
+ if (!lk.empty ()) {r = ln (*f, lk) || r; }
+ }
}
return r;
@@ -273,27 +377,32 @@ namespace build2
if (t.is_a<libs> ())
{
- // Here we may have a bunch of symlinks that we need to uninstall.
- //
- const scope& rs (t.root_scope ());
- auto& lp (t.data<install_match_data> (perform_uninstall_id).libs_paths);
+ const auto& md (t.data<install_match_data> (perform_uninstall_id));
- auto rm = [&rs, &id] (const path& f, const path& l)
+ if ((md.options & lib::option_install_buildtime) != 0)
{
- return uninstall_l (rs, id, l.leaf (), f.leaf (), 2 /* verbosity */);
- };
+ // Here we may have a bunch of symlinks that we need to uninstall.
+ //
+ const scope& rs (t.root_scope ());
+ const link_rule::libs_paths& lp (md.libs_paths);
- const path& lk (lp.link);
- const path& ld (lp.load);
- const path& so (lp.soname);
- const path& in (lp.interm);
+ auto rm = [&rs, &id] (const path& f, const path& l)
+ {
+ return uninstall_l (rs, id, l.leaf (), f.leaf (), 2 /* verbosity */);
+ };
- const path* f (lp.real);
+ const path& lk (lp.link);
+ const path& ld (lp.load);
+ const path& so (lp.soname);
+ const path& in (lp.interm);
- if (!in.empty ()) {r = rm (*f, in) || r; f = &in;}
- if (!so.empty ()) {r = rm (*f, so) || r; f = &so;}
- if (!ld.empty ()) {r = rm (*f, ld) || r; f = &ld;}
- if (!lk.empty ()) {r = rm (*f, lk) || r; }
+ const path* f (lp.real);
+
+ if (!in.empty ()) {r = rm (*f, in) || r; f = &in;}
+ if (!so.empty ()) {r = rm (*f, so) || r; f = &so;}
+ if (!ld.empty ()) {r = rm (*f, ld) || r; f = &ld;}
+ if (!lk.empty ()) {r = rm (*f, lk) || r; }
+ }
}
return r;
@@ -305,14 +414,17 @@ namespace build2
libux_install_rule (data&& d, const link_rule& l)
: common (move (d)), link_ (l) {}
- const target* libux_install_rule::
+ pair<const target*, uint64_t> libux_install_rule::
filter (const scope* is,
- action a, const target& t, prerequisite_iterator& i) const
+ action a, const target& t, prerequisite_iterator& i,
+ match_extra& me) const
{
using file_rule = install::file_rule;
const prerequisite& p (i->prerequisite);
+ uint64_t options (match_extra::all_options);
+
// The "see through" semantics that should be parallel to install_rule
// above. In particular, here we use libue/libua/libus{} as proxies for
// exe/liba/libs{} there.
@@ -332,15 +444,42 @@ namespace build2
pt = link_member (*l, a, link_info (t.base_scope (), ot));
if ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ()))
- return is == nullptr || pt->in (*is) ? pt : nullptr;
+ {
+ if (a.operation () != update_id)
+ {
+ if (t.is_a<libue> ())
+ options = lib::option_install_runtime;
+ else
+ {
+ if (me.cur_options == lib::option_install_runtime)
+ options = lib::option_install_runtime;
+ }
+ }
+
+ return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr,
+ options);
+ }
if (pt->is_a<libux> ())
- return pt;
+ {
+ if (a.operation () != update_id && !pt->is_a<libue> ())
+ {
+ if (t.is_a<libue> ())
+ options = lib::option_install_runtime;
+ else
+ {
+ if (me.cur_options == lib::option_install_runtime)
+ options = lib::option_install_runtime;
+ }
+ }
+
+ return make_pair (pt, options);
+ }
}
- const target* pt (file_rule::instance.filter (is, a, t, p));
+ const target* pt (file_rule::instance.filter (is, a, t, p, me).first);
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
auto header_source = [this] (const auto& p)
{
@@ -366,7 +505,7 @@ namespace build2
}
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
}
bool g (false);
@@ -382,16 +521,17 @@ namespace build2
{
pt = t.is_a<libue> ()
? nullptr
- : file_rule::instance.filter (is, a, *pt, pm.prerequisite);
+ : file_rule::instance.filter (
+ is, a, *pt, pm.prerequisite, me).first;
break;
}
}
if (pt == nullptr)
- return pt;
+ return make_pair (pt, options);
}
- return pt;
+ return make_pair (pt, options);
}
bool libux_install_rule::
@@ -403,5 +543,51 @@ namespace build2
return link_.sub_match (x_link, update_id, a, t, me) &&
alias_rule::match (a, t);
}
+
+ recipe libux_install_rule::
+ apply (action a, target& t, match_extra& me) const
+ {
+ if (a.operation () != update_id)
+ {
+ if (!t.is_a<libue> ())
+ {
+ if (me.new_options == 0)
+ me.new_options = lib::option_install_runtime;
+
+ me.cur_options = me.new_options;
+ }
+ }
+
+ return alias_rule::apply (a, t, me);
+ }
+
+ void libux_install_rule::
+ reapply (action a, target& t, match_extra& me) const
+ {
+ assert (a.operation () != update_id && !t.is_a<libue> ());
+
+ text << "REMATCH " << t
+ << ' ' << me.cur_options
+ << ' ' << me.new_options; // @@ TMP
+
+ // If we are rematched with the buildtime option, propagate it to our
+ // prerequisite libraries.
+ //
+ // @@ Also libux?
+ //
+ if ((me.new_options & lib::option_install_buildtime) != 0)
+ {
+ for (const target* pt: t.prerequisite_targets[a])
+ {
+ if (pt != nullptr && (pt->is_a<liba> () || pt->is_a<libs> () ||
+ pt->is_a<libua> () || pt->is_a<libus> ()))
+ rematch_sync (a, *pt, lib::option_install_buildtime);
+ }
+ }
+
+ // @@ TODO: match additional prerequisites if required.
+
+ me.cur_options |= me.new_options;
+ }
}
}
diff --git a/libbuild2/cc/install-rule.hxx b/libbuild2/cc/install-rule.hxx
index 6998d63..2427fda 100644
--- a/libbuild2/cc/install-rule.hxx
+++ b/libbuild2/cc/install-rule.hxx
@@ -20,7 +20,7 @@ namespace build2
{
class link_rule;
- // Installation rule for exe{} and lib*{}. Here we do:
+ // Installation rule for exe{} and lib[as]{}. Here we do:
//
// 1. Signal to the link rule that this is update for install.
//
@@ -28,17 +28,20 @@ namespace build2
//
// 3. Extra un/installation (e.g., libs{} symlinks).
//
+ // 4. Handling runtime/buildtime match options for lib[as]{}.
+ //
class LIBBUILD2_CC_SYMEXPORT install_rule: public install::file_rule,
virtual common
{
public:
install_rule (data&&, const link_rule&);
- virtual const target*
+ virtual pair<const target*, uint64_t>
filter (const scope*,
- action, const target&, prerequisite_iterator&) const override;
+ action, const target&, prerequisite_iterator&,
+ match_extra&) const override;
- // Note: rule::match() override.
+ // Note: rule::match() override (with hint and match_extra).
//
virtual bool
match (action, target&, const string&, match_extra&) const override;
@@ -46,7 +49,10 @@ namespace build2
using file_rule::match; // Make Clang happy.
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
+
+ virtual void
+ reapply (action, target&, match_extra&) const override;
virtual bool
install_extra (const file&, const install_dir&) const override;
@@ -58,7 +64,7 @@ namespace build2
const link_rule& link_;
};
- // Installation rule for libu*{}.
+ // Installation rule for libu[eas]{}.
//
// While libu*{} members themselves are not installable, we need to see
// through them in case they depend on stuff that we need to install
@@ -71,9 +77,10 @@ namespace build2
public:
libux_install_rule (data&&, const link_rule&);
- virtual const target*
+ virtual pair<const target*, uint64_t>
filter (const scope*,
- action, const target&, prerequisite_iterator&) const override;
+ action, const target&, prerequisite_iterator&,
+ match_extra&) const override;
// Note: rule::match() override.
//
@@ -82,6 +89,12 @@ namespace build2
using alias_rule::match; // Make Clang happy.
+ virtual recipe
+ apply (action, target&, match_extra&) const override;
+
+ virtual void
+ reapply (action, target&, match_extra&) const override;
+
private:
const link_rule& link_;
};
diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx
index 40857d7..eed7db1 100644
--- a/libbuild2/cc/module.cxx
+++ b/libbuild2/cc/module.cxx
@@ -1125,6 +1125,9 @@ namespace build2
//
if (install_loaded)
{
+ // Note: we rely quite heavily in these rule implementations that
+ // these are the only target types they are registered for.
+
const install_rule& ir (*this);
r.insert<exe> (perform_install_id, x_install, ir);
diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx
index 4dd11e8..7e086a8 100644
--- a/libbuild2/install/rule.cxx
+++ b/libbuild2/install/rule.cxx
@@ -71,24 +71,27 @@ namespace build2
return true;
}
- const target* alias_rule::
+ pair<const target*, uint64_t> alias_rule::
filter (const scope* is,
- action a, const target& t, prerequisite_iterator& i) const
+ action a, const target& t, prerequisite_iterator& i,
+ match_extra& me) const
{
assert (i->member == nullptr);
- return filter (is, a, t, i->prerequisite);
+ return filter (is, a, t, i->prerequisite, me);
}
- const target* alias_rule::
+ pair<const target*, uint64_t> alias_rule::
filter (const scope* is,
- action, const target& t, const prerequisite& p) const
+ action, const target& t, const prerequisite& p,
+ match_extra&) const
{
const target& pt (search (t, p));
- return is == nullptr || pt.in (*is) ? &pt : nullptr;
+ return make_pair (is == nullptr || pt.in (*is) ? &pt : nullptr,
+ match_extra::all_options);
}
recipe alias_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
{
tracer trace ("install::alias_rule::apply");
@@ -125,7 +128,9 @@ namespace build2
if (!is)
is = a.operation () != update_id ? install_scope (t) : nullptr;
- const target* pt (filter (*is, a, t, i));
+ pair<const target*, uint64_t> fr (filter (*is, a, t, i, me));
+
+ const target* pt (fr.first);
if (pt == nullptr)
{
l5 ([&]{trace << "ignoring " << p << " (filtered out)";});
@@ -150,12 +155,14 @@ namespace build2
continue;
}
+ uint64_t options (fr.second);
+
// If this is not a file-based target (e.g., a target group such as
// libu{}) then ignore it if there is no rule to install.
//
if (pt->is_a<file> ())
- match_sync (a, *pt);
- else if (!try_match_sync (a, *pt).first)
+ match_sync (a, *pt, options);
+ else if (!try_match_sync (a, *pt, options).first)
{
l5 ([&]{trace << "ignoring " << *pt << " (no rule)";});
pt = nullptr;
@@ -168,39 +175,11 @@ namespace build2
return default_recipe;
}
- // fsdir_rule
- //
- const fsdir_rule fsdir_rule::instance;
-
- bool fsdir_rule::
- match (action, target&) const
+ recipe alias_rule::
+ apply (action, target&) const
{
- // We always match.
- //
- // Note that we are called both as the outer part during the update-for-
- // un/install pre-operation and as the inner part during the un/install
- // operation itself.
- //
- return true;
- }
-
- recipe fsdir_rule::
- apply (action a, target& t) const
- {
- // If this is outer part of the update-for-un/install, delegate to the
- // default fsdir rule. Otherwise, this is a noop (we don't install
- // fsdir{}).
- //
- // For now we also assume we don't need to do anything for prerequisites
- // (the only sensible prerequisite of fsdir{} is another fsdir{}).
- //
- if (a.operation () == update_id)
- {
- match_inner (a, t);
- return inner_recipe;
- }
- else
- return noop_recipe;
+ assert (false); // Never called.
+ return nullptr;
}
// group_rule
@@ -220,10 +199,13 @@ namespace build2
return &m;
}
- const target* group_rule::
+ pair<const target*, uint64_t> group_rule::
filter (const scope* is,
- action, const target& t, const prerequisite& p) const
+ action, const target& t, const prerequisite& p,
+ match_extra&) const
{
+ pair<const target*, uint64_t> r (nullptr, match_extra::all_options);
+
// The same logic as in file_rule::filter() below.
//
if (p.is_a<exe> ())
@@ -232,15 +214,18 @@ namespace build2
if (p.vars.empty () ||
cast_empty<path> (p.vars[var_install (rs)]).string () != "true")
- return nullptr;
+ return r;
}
const target& pt (search (t, p));
- return is == nullptr || pt.in (*is) ? &pt : nullptr;
+ if (is == nullptr || pt.in (*is))
+ r.first = &pt;
+
+ return r;
}
recipe group_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
{
tracer trace ("install::group_rule::apply");
@@ -298,7 +283,7 @@ namespace build2
// Delegate to the base rule.
//
- return alias_rule::apply (a, t);
+ return alias_rule::apply (a, t, me);
}
@@ -315,18 +300,22 @@ namespace build2
return true;
}
- const target* file_rule::
+ pair<const target*, uint64_t> file_rule::
filter (const scope* is,
- action a, const target& t, prerequisite_iterator& i) const
+ action a, const target& t, prerequisite_iterator& i,
+ match_extra& me) const
{
assert (i->member == nullptr);
- return filter (is, a, t, i->prerequisite);
+ return filter (is, a, t, i->prerequisite, me);
}
- const target* file_rule::
+ pair<const target*, uint64_t> file_rule::
filter (const scope* is,
- action, const target& t, const prerequisite& p) const
+ action, const target& t, const prerequisite& p,
+ match_extra&) const
{
+ pair<const target*, uint64_t> r (nullptr, match_extra::all_options);
+
// See also group_rule::filter() with identical semantics.
//
if (p.is_a<exe> ())
@@ -340,22 +329,32 @@ namespace build2
//
if (p.vars.empty () ||
cast_empty<path> (p.vars[var_install (rs)]).string () != "true")
- return nullptr;
+ return r;
}
const target& pt (search (t, p));
- return is == nullptr || pt.in (*is) ? &pt : nullptr;
+ if (is == nullptr || pt.in (*is))
+ r.first = &pt;
+
+ return r;
}
recipe file_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
{
- recipe r (apply_impl (a, t));
+ recipe r (apply_impl (a, t, me));
return r != nullptr ? move (r) : noop_recipe;
}
recipe file_rule::
- apply_impl (action a, target& t) const
+ apply (action, target&) const
+ {
+ assert (false); // Never called.
+ return nullptr;
+ }
+
+ recipe file_rule::
+ apply_impl (action a, target& t, match_extra& me) const
{
tracer trace ("install::file_rule::apply");
@@ -438,8 +437,9 @@ namespace build2
if (!is)
is = a.operation () != update_id ? install_scope (t) : nullptr;
- const target* pt (filter (*is, a, t, i));
+ pair<const target*, uint64_t> fr (filter (*is, a, t, i, me));
+ const target* pt (fr.first);
if (pt == nullptr)
{
l5 ([&]{trace << "ignoring " << p << " (filtered out)";});
@@ -458,6 +458,8 @@ namespace build2
continue;
}
+ uint64_t options (fr.second);
+
if (pt->is_a<file> ())
{
// If the matched rule returned noop_recipe, then the target state
@@ -466,10 +468,15 @@ namespace build2
// when updating static installable content (headers, documentation,
// etc).
//
- if (match_sync (a, *pt, unmatch::unchanged).first)
+ // Regarding options, the expectation here is that they are not used
+ // for the update operation. And for install/uninstall, if they are
+ // used, then they don't effect whether the target is unchanged. All
+ // feels reasonable.
+ //
+ if (match_sync (a, *pt, unmatch::unchanged, options).first)
pt = nullptr;
}
- else if (!try_match_sync (a, *pt).first)
+ else if (!try_match_sync (a, *pt, options).first)
{
l5 ([&]{trace << "ignoring " << *pt << " (no rule)";});
pt = nullptr;
@@ -1566,5 +1573,40 @@ namespace build2
return r;
}
+
+ // fsdir_rule
+ //
+ const fsdir_rule fsdir_rule::instance;
+
+ bool fsdir_rule::
+ match (action, target&) const
+ {
+ // We always match.
+ //
+ // Note that we are called both as the outer part during the update-for-
+ // un/install pre-operation and as the inner part during the un/install
+ // operation itself.
+ //
+ return true;
+ }
+
+ recipe fsdir_rule::
+ apply (action a, target& t) const
+ {
+ // If this is outer part of the update-for-un/install, delegate to the
+ // default fsdir rule. Otherwise, this is a noop (we don't install
+ // fsdir{}).
+ //
+ // For now we also assume we don't need to do anything for prerequisites
+ // (the only sensible prerequisite of fsdir{} is another fsdir{}).
+ //
+ if (a.operation () == update_id)
+ {
+ match_inner (a, t);
+ return inner_recipe;
+ }
+ else
+ return noop_recipe;
+ }
}
}
diff --git a/libbuild2/install/rule.hxx b/libbuild2/install/rule.hxx
index b319071..3f30757 100644
--- a/libbuild2/install/rule.hxx
+++ b/libbuild2/install/rule.hxx
@@ -25,42 +25,43 @@ namespace build2
match (action, target&) const override;
// Return NULL if this prerequisite should be ignored and pointer to its
- // target otherwise.
+ // target otherwise. In the latter case, return the match options that
+ // should be used for this prerequisite (use match_extra::all_options
+ // and not 0 if no match options are needed).
//
// The default implementation ignores prerequsites that are outside of
// the installation scope (see install_scope() for details).
//
+ // The default implementation always returns match_extra::all_options.
+ // The match_extra argument is not used by the default implementation.
+ //
// The prerequisite is passed as an iterator allowing the filter to
// "see" inside groups.
//
using prerequisite_iterator =
prerequisite_members_range<group_prerequisites>::iterator;
- virtual const target*
+ virtual pair<const target*, uint64_t>
filter (const scope*,
- action, const target&, prerequisite_iterator&) const;
+ action, const target&, prerequisite_iterator&,
+ match_extra&) const;
- virtual const target*
- filter (const scope*, action, const target&, const prerequisite&) const;
+ virtual pair<const target*, uint64_t>
+ filter (const scope*,
+ action, const target&, const prerequisite&,
+ match_extra&) const;
+ // Note: rule::apply() override (with match_extra).
+ //
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
alias_rule () {}
static const alias_rule instance;
- };
-
- class fsdir_rule: public simple_rule
- {
- public:
- virtual bool
- match (action, target&) const override;
+ private:
virtual recipe
- apply (action, target&) const override;
-
- fsdir_rule () {}
- static const fsdir_rule instance;
+ apply (action, target&) const override; // Dummy simple_rule override.
};
// In addition to the alias rule's semantics, this rule sees through to
@@ -89,19 +90,17 @@ namespace build2
filter (action, const target&, const target& group_member) const;
// Return NULL if this prerequisite should be ignored and pointer to its
- // target otherwise.
- //
- // The same semantics as in file_rule below.
+ // target otherwise. The same semantics as in file_rule below.
//
- using alias_rule::filter; // "Unhide" to make Clang happy.
-
- virtual const target*
+ virtual pair<const target*, uint64_t>
filter (const scope*,
- action, const target&,
- const prerequisite&) const override;
+ action, const target&, const prerequisite&,
+ match_extra&) const override;
+
+ using alias_rule::filter; // "Unhide" to make Clang happy.
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
group_rule (bool sto): see_through_only (sto) {}
static const group_rule instance;
@@ -118,7 +117,9 @@ namespace build2
match (action, target&) const override;
// Return NULL if this prerequisite should be ignored and pointer to its
- // target otherwise.
+ // target otherwise. In the latter case, return the match options that
+ // should be used for this prerequisite (use match_extra::all_options
+ // and not 0 if no match options are needed).
//
// The default implementation ignores prerequsites that are outside of
// the installation scope (see install_scope() for details). It also
@@ -130,27 +131,35 @@ namespace build2
//
// exe{foo}: exe{bar}: install = true # foo runs bar
//
+ // The default implementation always returns match_extra::all_options.
+ // The match_extra argument is not used by the default implementation.
+ //
// The prerequisite is passed as an iterator allowing the filter to
// "see" inside groups.
//
using prerequisite_iterator =
prerequisite_members_range<group_prerequisites>::iterator;
- virtual const target*
+ virtual pair<const target*, uint64_t>
filter (const scope*,
- action, const target&, prerequisite_iterator&) const;
+ action, const target&, prerequisite_iterator&,
+ match_extra&) const;
- virtual const target*
- filter (const scope*, action, const target&, const prerequisite&) const;
+ virtual pair<const target*, uint64_t>
+ filter (const scope*,
+ action, const target&, const prerequisite&,
+ match_extra&) const;
+ // Note: rule::apply() override (with match_extra).
+ //
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
- // Implementation of apply() that returns empty_recipe if the target is
- // not installable.
+ // Implementation of apply() that returns empty_recipe (i.e., NULL) if
+ // the target is not installable.
//
recipe
- apply_impl (action, target&) const;
+ apply_impl (action, target&, match_extra&) const;
static target_state
perform_update (action, const target&);
@@ -288,6 +297,23 @@ namespace build2
static const file_rule instance;
file_rule () {}
+
+ private:
+ virtual recipe
+ apply (action, target&) const override; // Dummy simple_rule override.
+ };
+
+ class fsdir_rule: public simple_rule
+ {
+ public:
+ virtual bool
+ match (action, target&) const override;
+
+ virtual recipe
+ apply (action, target&) const override;
+
+ fsdir_rule () {}
+ static const fsdir_rule instance;
};
}
}