diff options
Diffstat (limited to 'libbuild2/cc/install-rule.cxx')
-rw-r--r-- | libbuild2/cc/install-rule.cxx | 560 |
1 files changed, 488 insertions, 72 deletions
diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index dc10543..6758e03 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -18,21 +18,69 @@ namespace build2 { using namespace bin; + using posthoc_prerequisite_target = + context::posthoc_target::prerequisite_target; + // install_rule // install_rule:: install_rule (data&& d, const link_rule& l) : common (move (d)), link_ (l) {} - const target* install_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + // Wrap the file_rule's recipe into a data-carrying recipe. + // + struct install_match_data + { + build2::recipe recipe; + uint64_t options; // Match options. + link_rule::libs_paths libs_paths; + + target_state + operator() (action a, const target& t) + { + return recipe (a, t); + } + }; + + bool install_rule:: + filter (action a, const target& t, const target& m) const + { + if (!t.is_a<exe> ()) + { + // If runtime-only, filter out all known buildtime target types. + // + const auto& md (t.data<install_match_data> (a)); + + if ((md.options & lib::option_install_buildtime) == 0) + { + if (m.is_a<liba> () || // Staic library. + m.is_a<pc> () || // pkg-config file. + m.is_a<libi> ()) // Import library. + return false; + } + } + + return true; + } + + pair<const target*, uint64_t> install_rule:: + filter (const scope* is, + 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); - // If this is a shared library prerequisite, install it as long as it - // is in the same amalgamation as we are. + uint64_t options (match_extra::all_options); + + otype ot (link_type (t).type); + + // @@ TMP: drop eventually. + // +#if 0 + // 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). @@ -42,10 +90,14 @@ namespace build2 // // Note: we install ad hoc prerequisites by default. // - otype ot (link_type (t).type); + // Note: at least one must be true since we only register this rule for + // exe{}, and lib[as]{} (this makes sure the following if-condition will + // always be true for libx{}). + // bool st (t.is_a<exe> () || t.is_a<libs> ()); // Target needs shared. bool at (t.is_a<liba> () || t.is_a<libs> ()); // Target needs static. + assert (st || at); if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || (at && (p.is_a<libx> () || p.is_a<liba> ()))) @@ -58,26 +110,115 @@ 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 pt->in (t.weak_scope ()) ? 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); + } + } +#else + // Note that at first it may seem like we don't need to install static + // library prerequisites of executables. But such libraries may still + // have prerequisites that are needed at runtime (say, some data files). + // So we install all libraries as long as they are in the installation + // scope and deal with runtime vs buildtime distiction using match + // options. + // + // Note: for now we assume these prerequisites never come from see- + // through groups. + // + // Note: we install ad hoc prerequisites by default. + // + if (p.is_a<libx> () || p.is_a<libs> () || p.is_a<liba> ()) + { + const target* pt (&search (t, p)); + + // If this is the lib{}/libu*{} group, pick a member which we would + // link. For libu*{} we want the "see through" logic. + // + if (const libx* l = pt->is_a<libx> ()) + pt = link_member (*l, a, link_info (t.base_scope (), ot)); + + // 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; + } + } + + // Note: not redundant since we could be returning a member. + // + if (pt->is_a<libs> () || pt->is_a<liba> ()) + { + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } + else // libua{} or libus{} + { + // See through to libu*{} members. Note that we are always in the + // same project (and thus amalgamation). + // + return make_pair (pt, options); + } } +#endif // 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, me).first); if (pt == nullptr) - return pt; + return make_pair (pt, options); - // Don't install executable's prerequisite headers and module - // interfaces. + // Don't install executable's or runtime-only library's prerequisite + // headers and module interfaces. // // Note that if they come from a group, then we assume the entire // group is not to be installed. @@ -87,16 +228,22 @@ namespace build2 // auto header_source = [this] (const auto& p) { - return (x_header (p) || - p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + return (x_header (p) || + p.is_a (x_src) || + p.is_a (c::static_type) || + p.is_a (S::static_type) || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || + p.is_a (m::static_type)))); }; - if (t.is_a<exe> ()) + if (t.is_a<exe> () || + (a.operation () != update_id && + me.cur_options == lib::option_install_runtime)) { if (header_source (p)) pt = nullptr; - else if (p.type.see_through) + else if (p.type.see_through ()) { for (i.enter_group (); i.group (); ) { @@ -107,7 +254,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 @@ -137,76 +284,180 @@ namespace build2 { pt = t.is_a<exe> () ? nullptr - : file_rule::filter (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:: - match (action a, target& t, const string& hint) const + match (action a, target& t, const string&, match_extra& me) const { - // @@ How do we split the hint between the two? - // - // We only want to handle installation if we are also the ones building // this target. So first run link's match(). // - return link_.match (a, t, hint) && file_rule::match (a, t, ""); + return link_.sub_match (x_link, update_id, a, t, me) && + file_rule::match (a, t); } recipe install_rule:: - apply (action a, target& t) const + apply (action a, target& t, match_extra& me) const { - recipe r (file_rule::apply (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, + me.cur_options != match_extra::all_options /* reapply */)); + + if (r == nullptr) + { + me.cur_options = match_extra::all_options; // Noop for all options. + return noop_recipe; + } if (a.operation () == update_id) { // Signal to the link rule that this is update for install. And if the // update has already been executed, verify it was done for install. // - auto& md (t.data<link_rule::match_data> ()); + auto& md (t.data<link_rule::match_data> (a.inner_action ())); if (md.for_install) { + // Note: see also append_libraries() for the other half. + // if (!*md.for_install) - fail << "target " << t << " already updated but not for install"; + fail << "incompatible " << t << " build" << + info << "target already built not for install"; } else md.for_install = true; } else // install or uninstall { - // Derive shared library paths and cache them in the target's aux - // storage if we are un/installing (used in the *_extra() functions - // below). - // - static_assert (sizeof (link_rule::libs_paths) <= target::data_size, - "insufficient space"); - - if (file* f = t.is_a<libs> ()) + file* ls; + if ((ls = t.is_a<libs> ()) || t.is_a<liba> ()) { - if (!f->path ().empty ()) // Not binless. + // Derive shared library paths and cache them in the target's aux + // storage if we are un/installing (used in the *_extra() functions + // below). + // + link_rule::libs_paths lsp; + if (ls != nullptr && !ls->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)); + + lsp = link_.derive_libs_paths (*ls, + p != nullptr ? p->c_str (): nullptr, + s != nullptr ? s->c_str (): nullptr); } + + return install_match_data {move (r), me.cur_options, move (lsp)}; } } return r; } + void install_rule:: + apply_posthoc (action a, target& t, match_extra& me) const + { + // Similar semantics to filter() above for shared libraries specified as + // post hoc prerequisites (e.g., plugins). + // + if (a.operation () != update_id) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + if (t.is_a<exe> ()) + p.match_options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + p.match_options = lib::option_install_runtime; + } + } + } + } + } + + void install_rule:: + reapply (action a, target& t, match_extra& me) const + { + tracer trace ("cc::install_rule::reapply"); + + assert (a.operation () != update_id && !t.is_a<exe> ()); + + l6 ([&]{trace << "rematching " << t + << ", current options " << me.cur_options + << ", new options " << me.new_options;}); + + me.cur_options |= me.new_options; + + // We also need to update options in install_match_data. + // + t.data<install_match_data> (a).options = me.cur_options; + + if ((me.new_options & lib::option_install_buildtime) != 0) + { + // If we are rematched with the buildtime option, propagate it to our + // prerequisite libraries. + // + 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> ())) + { + // Go for all options instead of just install_buildtime to avoid + // any further relocking/reapply (we only support runtime-only or + // everything). + // + rematch_sync (a, *pt, match_extra::all_options); + } + } + + // Also to post hoc. + // + if (me.posthoc_prerequisite_targets != nullptr) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + p.match_options = match_extra::all_options; + } + } + } + + // Also match any additional prerequisites (e.g., headers). + // + file_rule::reapply_impl (a, t, me); + } + } + bool install_rule:: install_extra (const file& t, const install_dir& id) const { @@ -214,14 +465,19 @@ namespace build2 if (t.is_a<libs> ()) { + const auto& md (t.data<install_match_data> (perform_install_id)); + // Here we may have a bunch of symlinks that we need to install. // + // Note that for runtime-only install we only omit the name that is + // used for linking (e.g., libfoo.so). + // const scope& rs (t.root_scope ()); - auto& lp (t.data<link_rule::libs_paths> ()); + const link_rule::libs_paths& lp (md.libs_paths); - auto ln = [&rs, &id] (const path& f, const path& l) + auto ln = [&t, &rs, &id] (const path& f, const path& l) { - install_l (rs, id, f.leaf (), l.leaf (), 2 /* verbosity */); + install_l (rs, id, l.leaf (), t, f.leaf (), 2 /* verbosity */); return true; }; @@ -235,7 +491,10 @@ namespace build2 if (!in.empty ()) {r = ln (*f, in) || r; f = ∈} 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 ((md.options & lib::option_install_buildtime) != 0) + { + if (!lk.empty ()) {r = ln (*f, lk) || r;} + } } return r; @@ -248,14 +507,16 @@ namespace build2 if (t.is_a<libs> ()) { + const auto& md (t.data<install_match_data> (perform_uninstall_id)); + // Here we may have a bunch of symlinks that we need to uninstall. // const scope& rs (t.root_scope ()); - auto& lp (t.data<link_rule::libs_paths> ()); + const link_rule::libs_paths& lp (md.libs_paths); - auto rm = [&rs, &id] (const path& l) + auto rm = [&rs, &id] (const path& f, const path& l) { - return uninstall_f (rs, id, nullptr, l.leaf (), 2 /* verbosity */); + return uninstall_l (rs, id, l.leaf (), f.leaf (), 2 /* verbosity */); }; const path& lk (lp.link); @@ -263,10 +524,15 @@ namespace build2 const path& so (lp.soname); const path& in (lp.interm); - if (!lk.empty ()) r = rm (lk) || r; - if (!ld.empty ()) r = rm (ld) || r; - if (!so.empty ()) r = rm (so) || r; - if (!in.empty ()) r = rm (in) || r; + const path* f (lp.real); + + if (!in.empty ()) {r = rm (*f, in) || r; f = ∈} + if (!so.empty ()) {r = rm (*f, so) || r; f = &so;} + if (!ld.empty ()) {r = rm (*f, ld) || r; f = &ld;} + if ((md.options & lib::option_install_buildtime) != 0) + { + if (!lk.empty ()) {r = rm (*f, lk) || r;} + } } return r; @@ -278,19 +544,30 @@ namespace build2 libux_install_rule (data&& d, const link_rule& l) : common (move (d)), link_ (l) {} - const target* libux_install_rule:: - filter (action a, const target& t, prerequisite_iterator& i) const + pair<const target*, uint64_t> libux_install_rule:: + filter (const scope* is, + 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); + + otype ot (link_type (t).type); + // 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. // - otype ot (link_type (t).type); + // @@ TMP: drop eventually. + // +#if 0 bool st (t.is_a<libue> () || t.is_a<libus> ()); // Target needs shared. bool at (t.is_a<libua> () || t.is_a<libus> ()); // Target needs static. + assert (st || at); if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || (at && (p.is_a<libx> () || p.is_a<liba> ()))) @@ -301,28 +578,89 @@ 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 pt->in (t.weak_scope ()) ? 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); + } + } +#else + if (p.is_a<libx> () || p.is_a<libs> () || p.is_a<liba> ()) + { + const target* pt (&search (t, p)); + + if (const libx* l = pt->is_a<libx> ()) + pt = link_member (*l, a, link_info (t.base_scope (), ot)); + + 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; + } + } + + if (pt->is_a<libs> () || pt->is_a<liba> ()) + { + return make_pair (is == nullptr || pt->in (*is) ? pt : nullptr, + options); + } + else + return make_pair (pt, options); } +#endif - const target* pt (install::file_rule::instance.filter (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) { - return (x_header (p) || - p.is_a (x_src) || - (x_mod != nullptr && p.is_a (*x_mod))); + return (x_header (p) || + p.is_a (x_src) || + p.is_a (c::static_type) || + p.is_a (S::static_type) || + (x_mod != nullptr && p.is_a (*x_mod)) || + (x_obj != nullptr && (p.is_a (*x_obj) || + p.is_a (m::static_type)))); }; - if (t.is_a<libue> ()) + if (t.is_a<libue> () || + (a.operation () != update_id && + me.cur_options == lib::option_install_runtime)) { if (header_source (p)) pt = nullptr; - else if (p.type.see_through) + else if (p.type.see_through ()) { for (i.enter_group (); i.group (); ) { @@ -333,7 +671,7 @@ namespace build2 } if (pt == nullptr) - return pt; + return make_pair (pt, options); } bool g (false); @@ -349,25 +687,103 @@ namespace build2 { pt = t.is_a<libue> () ? nullptr - : install::file_rule::instance.filter (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:: - match (action a, target& t, const string& hint) const + match (action a, target& t, const string&, match_extra& me) const { // We only want to handle installation if we are also the ones building // this target. So first run link's match(). // - return link_.match (a, t, hint) && alias_rule::match (a, t, ""); + 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_impl ( + a, t, me, me.cur_options != match_extra::all_options /* reapply */); + } + + void libux_install_rule:: + apply_posthoc (action a, target& t, match_extra& me) const + { + if (a.operation () != update_id) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + if (t.is_a<libue> ()) + p.match_options = lib::option_install_runtime; + else + { + if (me.cur_options == lib::option_install_runtime) + p.match_options = lib::option_install_runtime; + } + } + } + } + } + + void libux_install_rule:: + reapply (action a, target& t, match_extra& me) const + { + tracer trace ("cc::linux_install_rule::reapply"); + + assert (a.operation () != update_id && !t.is_a<libue> ()); + + l6 ([&]{trace << "rematching " << t + << ", current options " << me.cur_options + << ", new options " << me.new_options;}); + + me.cur_options |= me.new_options; + + 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, match_extra::all_options); + } + + if (me.posthoc_prerequisite_targets != nullptr) + { + for (posthoc_prerequisite_target& p: *me.posthoc_prerequisite_targets) + { + if (p.target != nullptr && p.target->is_a<libs> ()) + { + p.match_options = match_extra::all_options; + } + } + } + + alias_rule::reapply_impl (a, t, me); + } } } } |