diff options
Diffstat (limited to 'libbuild2/cc/install-rule.cxx')
-rw-r--r-- | libbuild2/cc/install-rule.cxx | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx new file mode 100644 index 0000000..670757e --- /dev/null +++ b/libbuild2/cc/install-rule.cxx @@ -0,0 +1,355 @@ +// file : libbuild2/cc/install-rule.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbuild2/cc/install-rule.hxx> + +#include <libbuild2/algorithm.hxx> + +#include <libbuild2/bin/target.hxx> + +#include <libbuild2/cc/utility.hxx> +#include <libbuild2/cc/link-rule.hxx> // match() + +using namespace std; + +namespace build2 +{ + namespace cc + { + using namespace bin; + + // 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 + { + // 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. + // + // Less obvious: we also want to install a static library prerequisite + // of a library (since it could be referenced from its .pc file, etc). + // + // Note: for now we assume these prerequisites never come from see- + // through groups. + // + // Note: we install ad hoc prerequisites by default. + // + otype ot (link_type (t).type); + + 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. + + if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || + (at && (p.is_a<libx> () || 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)); + + // Note: not redundant since we are returning a member. + // + if ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ())) + return pt->in (t.weak_scope ()) ? pt : nullptr; + + // See through to libu*{} members. Note that we are always in the same + // project (and thus amalgamation). + // + if (pt->is_a<libux> ()) + return pt; + } + + // The rest of the tests only succeed if the base filter() succeeds. + // + const target* pt (file_rule::filter (a, t, p)); + if (pt == nullptr) + return pt; + + // Don't install executable'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. + // + if (t.is_a<exe> ()) + { + if (x_header (p)) + pt = nullptr; + else if (p.type.see_through) + { + for (i.enter_group (); i.group (); ) + { + if (x_header (*++i)) + pt = nullptr; + } + } + + if (pt == nullptr) + return pt; + } + + // Here is a problem: if the user spells the obj*/bmi*{} targets + // explicitly, then the source files, including headers/modules may be + // specified as preprequisites of those targets and not of this target. + // While this can be worked around for headers by also listing them as + // prerequisites of this target, this won't work for modules (since they + // are compiled). So what we are going to do here is detect bmi*{} and + // translate them to their mxx{} (this doesn't quite work for headers + // since there would normally be many of them). + // + // Note: for now we assume bmi*{} never come from see-through groups. + // + bool g (false); + if (p.is_a<bmi> () || (g = p.is_a (compile_types (ot).bmi))) + { + if (g) + resolve_group (a, *pt); + + for (prerequisite_member pm: + group_prerequisite_members (a, *pt, members_mode::maybe)) + { + // This is tricky: we need to "look" inside groups for mxx{} but if + // found, remap to the group, not member. + // + if (pm.is_a (*x_mod)) + { + pt = t.is_a<exe> () + ? nullptr + : file_rule::filter (a, *pt, pm.prerequisite); + break; + } + } + + if (pt == nullptr) + return pt; + } + + return pt; + } + + bool install_rule:: + match (action a, target& t, const string& hint) 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, ""); + } + + recipe install_rule:: + apply (action a, target& t) const + { + recipe r (file_rule::apply (a, t)); + + 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> ()); + + if (md.for_install) + { + if (!*md.for_install) + fail << "target " << t << " already updated but 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> ()) + { + 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)); + } + } + } + + return r; + } + + bool install_rule:: + install_extra (const file& t, const install_dir& id) const + { + bool r (false); + + 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<link_rule::libs_paths> ()); + + auto ln = [&rs, &id] (const path& f, const path& l) + { + install_l (rs, id, f.leaf (), l.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); + + 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; } + } + + return r; + } + + bool install_rule:: + uninstall_extra (const file& t, const install_dir& id) const + { + bool r (false); + + 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<link_rule::libs_paths> ()); + + auto rm = [&rs, &id] (const path& l) + { + return uninstall_f (rs, id, nullptr, l.leaf (), 2 /* verbosity */); + }; + + const path& lk (lp.link); + const path& ld (lp.load); + 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; + } + + return r; + } + + // libux_install_rule + // + libux_install_rule:: + 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 + { + const prerequisite& p (i->prerequisite); + + // 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); + + 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. + + if ((st && (p.is_a<libx> () || p.is_a<libs> ())) || + (at && (p.is_a<libx> () || 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 ((st && pt->is_a<libs> ()) || (at && pt->is_a<liba> ())) + return pt->in (t.weak_scope ()) ? pt : nullptr; + + if (pt->is_a<libux> ()) + return pt; + } + + const target* pt (install::file_rule::instance.filter (a, t, p)); + if (pt == nullptr) + return pt; + + if (t.is_a<libue> ()) + { + if (x_header (p)) + pt = nullptr; + else if (p.type.see_through) + { + for (i.enter_group (); i.group (); ) + { + if (x_header (*++i)) + pt = nullptr; + } + } + + if (pt == nullptr) + return pt; + } + + bool g (false); + if (p.is_a<bmi> () || (g = p.is_a (compile_types (ot).bmi))) + { + if (g) + resolve_group (a, *pt); + + for (prerequisite_member pm: + group_prerequisite_members (a, *pt, members_mode::maybe)) + { + if (pm.is_a (*x_mod)) + { + pt = t.is_a<libue> () + ? nullptr + : install::file_rule::instance.filter (a, *pt, pm.prerequisite); + break; + } + } + + if (pt == nullptr) + return pt; + } + + return pt; + } + + bool libux_install_rule:: + match (action a, target& t, const string& hint) 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, ""); + } + } +} |