From d552de5d028e1dfb108f343810604d0dfd74c2e6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 13 Sep 2021 17:00:08 +0200 Subject: Consistently install prerequisites from any scope by default It is also now possible to adjust this behavior with global config.install.scope override. Valid values for this variable are: project -- only from project strong -- from strong amalgamation weak -- from weak amalgamation global -- from all projects (default) --- libbuild2/bash/rule.cxx | 15 --------------- libbuild2/bash/rule.hxx | 11 ++--------- libbuild2/cc/install-rule.cxx | 24 ++++++++++++++---------- libbuild2/cc/install-rule.hxx | 6 ++++-- libbuild2/install/init.cxx | 31 +++++++++++++++++++++++++++++++ libbuild2/install/rule.cxx | 43 +++++++++++++++++++++++++++++-------------- libbuild2/install/rule.hxx | 21 +++++++++++---------- libbuild2/install/utility.cxx | 32 ++++++++++++++++++++++++++++++++ libbuild2/install/utility.hxx | 8 ++++++++ 9 files changed, 131 insertions(+), 60 deletions(-) create mode 100644 libbuild2/install/utility.cxx (limited to 'libbuild2') diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index d378227..82c06c7 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -420,20 +420,5 @@ namespace build2 return r; } - - const target* install_rule:: - filter (action a, const target& t, const prerequisite& p) const - { - // If this is a module prerequisite, install it as long as it is in the - // same amalgamation as we are. - // - if (p.is_a ()) - { - const target& pt (search (t, p)); - return pt.in (t.weak_scope ()) ? &pt : nullptr; - } - else - return file_rule::filter (a, t, p); - } } } diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx index 81c4604..c54b07c 100644 --- a/libbuild2/bash/rule.hxx +++ b/libbuild2/bash/rule.hxx @@ -61,12 +61,8 @@ namespace build2 const string&) const; }; - // Installation rule for bash scripts (exe{}) and modules (bash{}). Here - // we do: - // - // 1. Signal to in_rule that this is update for install. - // - // 2. Custom filtering of prerequisites. + // Installation rule for bash scripts (exe{}) and modules (bash{}) that + // signals to in_rule that this is update for install. // class LIBBUILD2_BASH_SYMEXPORT install_rule: public install::file_rule { @@ -79,9 +75,6 @@ namespace build2 virtual recipe apply (action, target&) const override; - virtual const target* - filter (action, const target&, const prerequisite&) const override; - protected: const in_rule& in_; }; diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index dc10543..3e62d59 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -25,14 +25,15 @@ namespace build2 : common (move (d)), link_ (l) {} const target* install_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + filter (const scope* is, + action a, const target& t, prerequisite_iterator& i) const { // NOTE: see libux_install_rule::filter() if changing anything here. const prerequisite& p (i->prerequisite); - // If this is a shared library prerequisite, install it as long as it - // is in the same amalgamation as we are. + // If this is a shared library prerequisite, install it as long as it is + // in the installation scope. // // Less obvious: we also want to install a static library prerequisite // of a library (since it could be referenced from its .pc file, etc). @@ -61,7 +62,7 @@ namespace build2 // Note: not redundant since we are returning a member. // if ((st && pt->is_a ()) || (at && pt->is_a ())) - return pt->in (t.weak_scope ()) ? pt : nullptr; + return is == nullptr || pt->in (*is) ? pt : nullptr; // See through to libu*{} members. Note that we are always in the same // project (and thus amalgamation). @@ -72,7 +73,7 @@ namespace build2 // The rest of the tests only succeed if the base filter() succeeds. // - const target* pt (file_rule::filter (a, t, p)); + const target* pt (file_rule::filter (is, a, t, p)); if (pt == nullptr) return pt; @@ -137,7 +138,7 @@ namespace build2 { pt = t.is_a () ? nullptr - : file_rule::filter (a, *pt, pm.prerequisite); + : file_rule::filter (is, a, *pt, pm.prerequisite); break; } } @@ -279,8 +280,11 @@ namespace build2 : common (move (d)), link_ (l) {} const target* libux_install_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + filter (const scope* is, + action a, const target& t, prerequisite_iterator& i) const { + using file_rule = install::file_rule; + const prerequisite& p (i->prerequisite); // The "see through" semantics that should be parallel to install_rule @@ -301,13 +305,13 @@ namespace build2 pt = link_member (*l, a, link_info (t.base_scope (), ot)); if ((st && pt->is_a ()) || (at && pt->is_a ())) - return pt->in (t.weak_scope ()) ? pt : nullptr; + return is == nullptr || pt->in (*is) ? pt : nullptr; if (pt->is_a ()) return pt; } - const target* pt (install::file_rule::instance.filter (a, t, p)); + const target* pt (file_rule::instance.filter (is, a, t, p)); if (pt == nullptr) return pt; @@ -349,7 +353,7 @@ namespace build2 { pt = t.is_a () ? nullptr - : install::file_rule::instance.filter (a, *pt, pm.prerequisite); + : file_rule::instance.filter (is, a, *pt, pm.prerequisite); break; } } diff --git a/libbuild2/cc/install-rule.hxx b/libbuild2/cc/install-rule.hxx index 5856352..acd1bd8 100644 --- a/libbuild2/cc/install-rule.hxx +++ b/libbuild2/cc/install-rule.hxx @@ -35,7 +35,8 @@ namespace build2 install_rule (data&&, const link_rule&); virtual const target* - filter (action, const target&, prerequisite_iterator&) const override; + filter (const scope*, + action, const target&, prerequisite_iterator&) const override; virtual bool match (action, target&, const string&) const override; @@ -67,7 +68,8 @@ namespace build2 libux_install_rule (data&&, const link_rule&); virtual const target* - filter (action, const target&, prerequisite_iterator&) const override; + filter (const scope*, + action, const target&, prerequisite_iterator&) const override; virtual bool match (action, target&, const string&) const override; diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 677ee07..5b5d1e2 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -392,6 +392,37 @@ namespace build2 // Configuration. // + + // config.install.scope + // + // We do not install prerequisites (for example, shared libraries) of + // targets (for example, executables) that belong to projects outside of + // this scope. Valid values are: + // + // project -- project scope + // strong -- strong amalgamation + // weak -- weak amalgamation + // global -- all projects (default) + // + // Note: can only be specified as a global override. + // + { + auto& v (vp.insert ("config.install.scope")); + + // If specified, verify it is a global override. + // + if (lookup l = rs[v]) + { + if (!l.belongs (rs.global_scope ())) + fail << "config.install.scope must be a global override" << + info << "specify !config.install.scope=..."; + } + + config::unsave_variable (rs, v); + } + + // Installation directories. + // // Note that we don't use any defaults for root -- the location // must be explicitly specified or the installer will complain // if and when we try to install. diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index 388400a..5092d42 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -54,16 +54,19 @@ namespace build2 } const target* alias_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + filter (const scope* is, + action a, const target& t, prerequisite_iterator& i) const { assert (i->member == nullptr); - return filter (a, t, i->prerequisite); + return filter (is, a, t, i->prerequisite); } const target* alias_rule:: - filter (action, const target& t, const prerequisite& p) const + filter (const scope* is, + action, const target& t, const prerequisite& p) const { - return &search (t, p); + const target& pt (search (t, p)); + return is == nullptr || pt.in (*is) ? &pt : nullptr; } recipe alias_rule:: @@ -75,8 +78,9 @@ namespace build2 // // @@ Shouldn't we do match in parallel (here and below)? // - auto& pts (t.prerequisite_targets[a]); + optional is; // Installation scope (resolve lazily). + auto& pts (t.prerequisite_targets[a]); auto pms (group_prerequisite_members (a, t, members_mode::never)); for (auto i (pms.begin ()), e (pms.end ()); i != e; ++i) { @@ -100,7 +104,10 @@ namespace build2 // Note: we assume that if the filter enters the group, then it // iterates over all its members. // - const target* pt (filter (a, t, i)); + if (!is) + is = install_scope (t); + + const target* pt (filter (*is, a, t, i)); if (pt == nullptr) { l5 ([&]{trace << "ignoring " << p << " (filtered out)";}); @@ -217,7 +224,6 @@ namespace build2 if (gv.members != nullptr) { auto& pts (t.prerequisite_targets[a]); - for (size_t i (0); i != gv.count; ++i) { const target* m (gv.members[i]); @@ -271,17 +277,19 @@ namespace build2 } const target* file_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + filter (const scope* is, + action a, const target& t, prerequisite_iterator& i) const { assert (i->member == nullptr); - return filter (a, t, i->prerequisite); + return filter (is, a, t, i->prerequisite); } const target* file_rule:: - filter (action, const target& t, const prerequisite& p) const + filter (const scope* is, + action, const target& t, const prerequisite& p) const { const target& pt (search (t, p)); - return pt.in (t.root_scope ()) ? &pt : nullptr; + return is == nullptr || pt.in (*is) ? &pt : nullptr; } recipe file_rule:: @@ -314,8 +322,9 @@ namespace build2 if (a.operation () == update_id) unchanged = match_inner (a, t, unmatch::unchanged).first; - auto& pts (t.prerequisite_targets[a]); + optional is; // Installation scope (resolve lazily). + auto& pts (t.prerequisite_targets[a]); auto pms (group_prerequisite_members (a, t, members_mode::never)); for (auto i (pms.begin ()), e (pms.end ()); i != e; ++i) { @@ -339,7 +348,11 @@ namespace build2 // Note: we assume that if the filter enters the group, then it // iterates over all its members. // - const target* pt (filter (a, t, i)); + if (!is) + is = install_scope (t); + + const target* pt (filter (*is, a, t, i)); + if (pt == nullptr) { l5 ([&]{trace << "ignoring " << p << " (filtered out)";}); @@ -541,7 +554,9 @@ namespace build2 { if (fail_unknown) fail << "unknown installation directory name '" << sn << "'" << - info << "did you forget to specify config." << var << "?"; + info << "did you forget to specify config." << var << "?" << + info << "specify !config." << var << "=... if installing " + << "from multiple projects"; return rs; // Empty. } diff --git a/libbuild2/install/rule.hxx b/libbuild2/install/rule.hxx index 73f2486..ce60bb9 100644 --- a/libbuild2/install/rule.hxx +++ b/libbuild2/install/rule.hxx @@ -25,7 +25,9 @@ namespace build2 match (action, target&, const string&) const override; // Return NULL if this prerequisite should be ignored and pointer to its - // target otherwise. The default implementation allows all prerequsites. + // target otherwise. The default implementation ignores prerequsites + // that are outside of the installation scope (see install_scope() for + // details). // // The prerequisite is passed as an iterator allowing the filter to // "see" inside groups. @@ -34,10 +36,11 @@ namespace build2 prerequisite_members_range::iterator; virtual const target* - filter (action, const target&, prerequisite_iterator&) const; + filter (const scope*, + action, const target&, prerequisite_iterator&) const; virtual const target* - filter (action, const target&, const prerequisite&) const; + filter (const scope*, action, const target&, const prerequisite&) const; virtual recipe apply (action, target&) const override; @@ -103,11 +106,8 @@ namespace build2 // Return NULL if this prerequisite should be ignored and pointer to its // target otherwise. The default implementation ignores prerequsites - // that are outside of this target's project. - // - // @@ I wonder why we do weak amalgamation for alias but project for - // file? And then override this for prerequisite libraries/modules - // in cc::install_rule and bash::install_rule... + // that are outside of the installation scope (see install_scope() for + // details). // // The prerequisite is passed as an iterator allowing the filter to // "see" inside groups. @@ -116,10 +116,11 @@ namespace build2 prerequisite_members_range::iterator; virtual const target* - filter (action, const target&, prerequisite_iterator&) const; + filter (const scope*, + action, const target&, prerequisite_iterator&) const; virtual const target* - filter (action, const target&, const prerequisite&) const; + filter (const scope*, action, const target&, const prerequisite&) const; virtual recipe apply (action, target&) const override; diff --git a/libbuild2/install/utility.cxx b/libbuild2/install/utility.cxx new file mode 100644 index 0000000..12215f8 --- /dev/null +++ b/libbuild2/install/utility.cxx @@ -0,0 +1,32 @@ +// file : libbuild2/install/utility.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +namespace build2 +{ + namespace install + { + const scope* + install_scope (const target& t) + { + context& ctx (t.ctx); + + const variable& var (*ctx.var_pool.find ("config.install.scope")); + + if (const string* s = cast_null (ctx.global_scope[var])) + { + if (*s == "project") + return &t.root_scope (); + else if (*s == "strong") + return &t.strong_scope (); + else if (*s == "weak") + return &t.weak_scope (); + else if (*s != "global") + fail << "invalid " << var << " value '" << *s << "'"; + } + + return nullptr; + } + } +} diff --git a/libbuild2/install/utility.hxx b/libbuild2/install/utility.hxx index 2c0ca56..cc5cd53 100644 --- a/libbuild2/install/utility.hxx +++ b/libbuild2/install/utility.hxx @@ -56,6 +56,14 @@ namespace build2 return install_mode (s, T::static_type, move (m)); } + // Return the "installation scope". We do not install prerequisites (for + // example, shared libraries) of targets (for example, executables) that + // belong to projects outside of this scope. If it's NULL, install + // prerequisites from all projects. See also config.install.scope. + // + LIBBUILD2_SYMEXPORT const scope* + install_scope (const target&); + // Resolve relative installation directory path (e.g., include/libfoo) to // its absolute directory path (e.g., /usr/include/libfoo). If the // resolution encountered an unknown directory, issue diagnostics and fail -- cgit v1.1