aboutsummaryrefslogtreecommitdiff
path: root/build2/install
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-07-04 19:12:15 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-07-05 14:24:43 +0300
commit57b10c06925d0bdf6ffb38488ee908f085109e95 (patch)
treef2103684d319650c3302aef9d7a70dd64ff2a347 /build2/install
parent30b4eda196e090aa820d312e6a9435a4ae84c303 (diff)
Move config, dist, test, and install modules into library
Diffstat (limited to 'build2/install')
-rw-r--r--build2/install/functions.cxx33
-rw-r--r--build2/install/init.cxx303
-rw-r--r--build2/install/init.hxx31
-rw-r--r--build2/install/operation.cxx84
-rw-r--r--build2/install/operation.hxx23
-rw-r--r--build2/install/rule.cxx1222
-rw-r--r--build2/install/rule.hxx195
-rw-r--r--build2/install/utility.hxx76
8 files changed, 0 insertions, 1967 deletions
diff --git a/build2/install/functions.cxx b/build2/install/functions.cxx
deleted file mode 100644
index 5780fd8..0000000
--- a/build2/install/functions.cxx
+++ /dev/null
@@ -1,33 +0,0 @@
-// file : build2/install/functions.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <libbuild2/function.hxx>
-#include <libbuild2/variable.hxx>
-
-#include <build2/install/utility.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace install
- {
- void
- functions ()
- {
- function_family f ("install");
-
- // Resolve potentially relative install.* value to an absolute directory
- // based on (other) install.* values visible from the calling scope.
- //
- f[".resolve"] = [] (const scope* s, dir_path d)
- {
- if (s == nullptr)
- fail << "install.resolve() called out of scope" << endf;
-
- return resolve_dir (*s, move (d));
- };
- }
- }
-}
diff --git a/build2/install/init.cxx b/build2/install/init.cxx
deleted file mode 100644
index 055b8b1..0000000
--- a/build2/install/init.cxx
+++ /dev/null
@@ -1,303 +0,0 @@
-// file : build2/install/init.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/install/init.hxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/rule.hxx>
-#include <libbuild2/function.hxx>
-#include <libbuild2/operation.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <build2/config/utility.hxx>
-
-#include <build2/install/rule.hxx>
-#include <build2/install/utility.hxx>
-#include <build2/install/operation.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace install
- {
- // Set install.<name>.* values based on config.install.<name>.* ones
- // or the defaults. If none of config.install.* values were specified,
- // then we do omitted/delayed configuration. Note that we still need
- // to set all the install.* values to defaults, as if we had the
- // default configuration.
- //
- // If override is true, then override values that came from outer
- // configurations. We have to do this for paths that contain the
- // package name.
- //
- // For global values we only set config.install.* variables. Non-global
- // values with NULL defaults are omitted.
- //
- template <typename T, typename CT>
- static void
- set_var (bool spec,
- scope& r,
- const char* name,
- const char* var,
- const CT* dv,
- bool override = false)
- {
- string vn;
- lookup l;
-
- bool global (*name == '\0');
-
- if (spec)
- {
- // Note: overridable.
- //
- vn = "config.install";
- if (!global)
- {
- vn += '.';
- vn += name;
- }
- vn += var;
- const variable& vr (var_pool.rw (r).insert<CT> (move (vn), true));
-
- l = dv != nullptr
- ? config::required (r, vr, *dv, override).first
- : (global
- ? config::optional (r, vr)
- : config::omitted (r, vr).first);
- }
-
- if (global)
- return;
-
- // Note: not overridable.
- //
- vn = "install.";
- vn += name;
- vn += var;
- const variable& vr (var_pool.rw (r).insert<T> (move (vn)));
-
- value& v (r.assign (vr));
-
- if (spec)
- {
- if (l)
- v = cast<T> (l); // Strip CT to T.
- }
- else
- {
- if (dv != nullptr)
- v = *dv;
- }
- }
-
- template <typename T>
- static void
- set_dir (bool s, // specified
- scope& r, // root scope
- const char* n, // var name
- const T& p, // path
- bool o = false, // override
- const string& fm = string (), // file mode
- const string& dm = string (), // dir mode
- const build2::path& c = build2::path ()) // command
- {
- using build2::path;
-
- bool global (*n == '\0');
-
- if (!global)
- set_var<dir_path> (s, r, n, "", p.empty () ? nullptr : &p, o);
-
- set_var<path> (s, r, n, ".cmd", c.empty () ? nullptr : &c);
- set_var<strings> (s, r, n, ".options", (strings*) (nullptr));
- set_var<string> (s, r, n, ".mode", fm.empty () ? nullptr : &fm);
- set_var<string> (s, r, n, ".dir_mode", dm.empty () ? nullptr : &dm);
- set_var<string> (s, r, n, ".sudo", (string*) (nullptr));
-
- // This one doesn't have config.* value (only set in a buildfile).
- //
- if (!global)
- var_pool.rw (r).insert<bool> (string ("install.") + n + ".subdirs");
- }
-
- void
- functions (); // functions.cxx
-
- bool
- boot (scope& rs, const location&, unique_ptr<module_base>&)
- {
- tracer trace ("install::boot");
- l5 ([&]{trace << "for " << rs;});
-
- // Register install function family if this is the first instance of the
- // install modules.
- //
- if (!function_family::defined ("install"))
- functions ();
-
- // Register our operations.
- //
- rs.insert_operation (install_id, op_install);
- rs.insert_operation (uninstall_id, op_uninstall);
- rs.insert_operation (update_for_install_id, op_update_for_install);
-
- return false;
- }
-
- static const path cmd ("install");
-
- static const dir_path dir_root ("root");
-
- static const dir_path dir_sbin (dir_path ("exec_root") /= "sbin");
- static const dir_path dir_bin (dir_path ("exec_root") /= "bin");
- static const dir_path dir_lib (dir_path ("exec_root") /= "lib");
- static const dir_path dir_libexec (dir_path ("exec_root") /= "libexec");
- static const dir_path dir_pkgconfig (dir_path ("lib") /= "pkgconfig");
-
- static const dir_path dir_data (dir_path ("data_root") /= "share");
- static const dir_path dir_include (dir_path ("data_root") /= "include");
-
- static const dir_path dir_doc (dir_path (dir_data) /= "doc");
- static const dir_path dir_man (dir_path (dir_data) /= "man");
- static const dir_path dir_man1 (dir_path ("man") /= "man1");
-
- static const group_rule group_rule_ (true /* see_through_only */);
-
- bool
- init (scope& rs,
- scope& bs,
- const location& l,
- unique_ptr<module_base>&,
- bool first,
- bool,
- const variable_map& config_hints)
- {
- tracer trace ("install::init");
-
- if (!first)
- {
- warn (l) << "multiple install module initializations";
- return true;
- }
-
- const dir_path& out_root (rs.out_path ());
- l5 ([&]{trace << "for " << out_root;});
-
- assert (config_hints.empty ()); // We don't known any hints.
-
- // Enter module variables.
- //
- auto& vp (var_pool.rw (rs));
-
- // Note that the set_dir() calls below enter some more.
- //
- {
- // Note: not overridable.
- //
- // The install variable is a path, not dir_path, since it can be used
- // to both specify the target directory (to install with the same file
- // name) or target file (to install with a different name). And the
- // way we distinguish between the two is via the presence/absence of
- // the trailing directory separator.
- //
- vp.insert<path> ("install", variable_visibility::target);
- vp.insert<string> ("install.mode", variable_visibility::project);
- vp.insert<bool> ("install.subdirs", variable_visibility::project);
- }
-
- // Register our rules.
- //
- {
- auto& r (bs.rules);
-
- const auto& ar (alias_rule::instance);
- const auto& dr (fsdir_rule::instance);
- const auto& fr (file_rule::instance);
- const auto& gr (group_rule_);
-
- r.insert<alias> (perform_install_id, "install.alias", ar);
- r.insert<alias> (perform_uninstall_id, "uninstall.alias", ar);
-
- r.insert<fsdir> (perform_install_id, "install.fsdir", dr);
- r.insert<fsdir> (perform_uninstall_id, "install.fsdir", dr);
-
- r.insert<file> (perform_install_id, "install.file", fr);
- r.insert<file> (perform_uninstall_id, "uninstall.file", fr);
-
- r.insert<target> (perform_install_id, "install.file", gr);
- r.insert<target> (perform_uninstall_id, "uninstall.file", gr);
- }
-
- // Configuration.
- //
- // 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.
- //
- {
- using build2::path;
-
- bool s (config::specified (rs, "install"));
-
- // Adjust module priority so that the (numerous) config.install.*
- // values are saved at the end of config.build.
- //
- if (s)
- config::save_module (rs, "install", INT32_MAX);
-
- const string& n (project (rs).string ());
-
- // Global config.install.* values.
- //
- set_dir (s, rs, "", abs_dir_path (), false, "644", "755", cmd);
-
- set_dir (s, rs, "root", abs_dir_path ());
-
- set_dir (s, rs, "data_root", dir_root);
- set_dir (s, rs, "exec_root", dir_root, false, "755");
-
- set_dir (s, rs, "sbin", dir_sbin);
- set_dir (s, rs, "bin", dir_bin);
- set_dir (s, rs, "lib", dir_lib);
- set_dir (s, rs, "libexec", dir_path (dir_libexec) /= n, true);
- set_dir (s, rs, "pkgconfig", dir_pkgconfig, false, "644");
-
- set_dir (s, rs, "data", dir_path (dir_data) /= n, true);
- set_dir (s, rs, "include", dir_include);
-
- set_dir (s, rs, "doc", dir_path (dir_doc) /= n, true);
- set_dir (s, rs, "man", dir_man);
- set_dir (s, rs, "man1", dir_man1);
-
- // Support for chroot'ed install (aka DESTDIR).
- //
- {
- auto& var (vp.insert<dir_path> ( "install.chroot"));
- auto& cvar (vp.insert<abs_dir_path> ("config.install.chroot", true));
-
- value& v (rs.assign (var));
-
- if (s)
- {
- if (lookup l = config::optional (rs, cvar))
- v = cast<dir_path> (l); // Strip abs_dir_path.
- }
- }
- }
-
- // Configure "installability" for built-in target types.
- //
- install_path<exe> (bs, dir_path ("bin")); // Install into install.bin.
- install_path<doc> (bs, dir_path ("doc")); // Install into install.doc.
- install_path<man> (bs, dir_path ("man")); // Install into install.man.
- install_path<man1> (bs, dir_path ("man1")); // Install into install.man1.
-
- return true;
- }
- }
-}
diff --git a/build2/install/init.hxx b/build2/install/init.hxx
deleted file mode 100644
index 579c03e..0000000
--- a/build2/install/init.hxx
+++ /dev/null
@@ -1,31 +0,0 @@
-// file : build2/install/init.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_INSTALL_INIT_HXX
-#define BUILD2_INSTALL_INIT_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-
-namespace build2
-{
- namespace install
- {
- bool
- boot (scope&, const location&, unique_ptr<module_base>&);
-
- bool
- init (scope&,
- scope&,
- const location&,
- unique_ptr<module_base>&,
- bool,
- bool,
- const variable_map&);
- }
-}
-
-#endif // BUILD2_INSTALL_INIT_HXX
diff --git a/build2/install/operation.cxx b/build2/install/operation.cxx
deleted file mode 100644
index 6ad1899..0000000
--- a/build2/install/operation.cxx
+++ /dev/null
@@ -1,84 +0,0 @@
-// file : build2/install/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/install/operation.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace install
- {
- static operation_id
- install_pre (const values& params, meta_operation_id mo, const location& l)
- {
- if (!params.empty ())
- fail (l) << "unexpected parameters for operation install";
-
- // Run update as a pre-operation, unless we are disfiguring.
- //
- return mo != disfigure_id ? update_id : 0;
- }
-
- // Note that we run both install and uninstall serially. The reason for
- // this is all the fuzzy things we are trying to do like removing empty
- // outer directories if they are empty. If we do this in parallel, then
- // those things get racy. Also, since all we do here is creating/removing
- // files, there is not going to be much speedup from doing it in parallel.
-
- const operation_info op_install {
- install_id,
- 0,
- "install",
- "install",
- "installing",
- "installed",
- "has nothing to install", // We cannot "be installed".
- execution_mode::first,
- 0,
- &install_pre,
- nullptr
- };
-
- // Note that we run update as a pre-operation, just like install. Which
- // may seem bizarre at first. We do it to obtain the exact same dependency
- // graph as install so that we uninstall exactly the same set of files as
- // install would install. Note that just matching the rules without
- // executing them may not be enough: for example, a presence of an ad hoc
- // group member may only be discovered after executing the rule (e.g., VC
- // link.exe only creates a DLL's import library if there are any exported
- // symbols).
- //
- const operation_info op_uninstall {
- uninstall_id,
- 0,
- "uninstall",
- "uninstall",
- "uninstalling",
- "uninstalled",
- "is not installed",
- execution_mode::last,
- 0,
- &install_pre,
- nullptr
- };
-
- // Also the explicit update-for-install operation alias.
- //
- const operation_info op_update_for_install {
- update_id, // Note: not update_for_install_id.
- install_id,
- op_update.name,
- op_update.name_do,
- op_update.name_doing,
- op_update.name_did,
- op_update.name_done,
- op_update.mode,
- op_update.concurrency,
- op_update.pre,
- op_update.post
- };
- }
-}
diff --git a/build2/install/operation.hxx b/build2/install/operation.hxx
deleted file mode 100644
index 7de0225..0000000
--- a/build2/install/operation.hxx
+++ /dev/null
@@ -1,23 +0,0 @@
-// file : build2/install/operation.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_INSTALL_OPERATION_HXX
-#define BUILD2_INSTALL_OPERATION_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/operation.hxx>
-
-namespace build2
-{
- namespace install
- {
- extern const operation_info op_install;
- extern const operation_info op_uninstall;
- extern const operation_info op_update_for_install;
- }
-}
-
-#endif // BUILD2_INSTALL_OPERATION_HXX
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
deleted file mode 100644
index faa7c3f..0000000
--- a/build2/install/rule.cxx
+++ /dev/null
@@ -1,1222 +0,0 @@
-// file : build2/install/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/install/rule.hxx>
-
-#include <libbutl/filesystem.mxx> // dir_exists(), file_exists()
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/algorithm.hxx>
-#include <libbuild2/filesystem.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace install
- {
- // Lookup the install or install.* variable. Return NULL if not found or
- // if the value is the special 'false' name (which means do not install;
- // so the result can be used as bool). T is either scope or target.
- //
- template <typename P, typename T>
- static const P*
- lookup_install (T& t, const string& var)
- {
- auto l (t[var]);
-
- if (!l)
- return nullptr;
-
- const P& r (cast<P> (l));
- return r.simple () && r.string () == "false" ? nullptr : &r;
- }
-
- // alias_rule
- //
- const alias_rule alias_rule::instance;
-
- bool alias_rule::
- match (action, target&, const string&) 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;
- }
-
- const target* alias_rule::
- filter (action a, const target& t, prerequisite_iterator& i) const
- {
- assert (i->member == nullptr);
- return filter (a, t, i->prerequisite);
- }
-
- const target* alias_rule::
- filter (action, const target& t, const prerequisite& p) const
- {
- const target& pt (search (t, p));
- return pt.in (t.weak_scope ()) ? &pt : nullptr;
- }
-
- recipe alias_rule::
- apply (action a, target& t) const
- {
- tracer trace ("install::alias_rule::apply");
-
- // Pass-through to our installable prerequisites.
- //
- // @@ Shouldn't we do match in parallel (here and below)?
- //
- 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)
- {
- const prerequisite& p (i->prerequisite);
-
- // Ignore excluded.
- //
- include_type pi (include (a, t, p));
-
- if (!pi)
- continue;
-
- // Ignore unresolved targets that are imported from other projects.
- // We are definitely not installing those.
- //
- if (p.proj)
- continue;
-
- // Let a customized rule have its say.
- //
- // 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 (pt == nullptr)
- {
- l5 ([&]{trace << "ignoring " << p << " (filtered out)";});
- continue;
- }
-
- // Check if this prerequisite is explicitly "not installable", that
- // is, there is the 'install' variable and its value is false.
- //
- // At first, this might seem redundand since we could have let the
- // file_rule below take care of it. The nuance is this: this
- // prerequsite can be in a different subproject that hasn't loaded the
- // install module (and therefore has no file_rule registered). The
- // typical example would be the 'tests' subproject.
- //
- // Note: not the same as lookup_install() above.
- //
- auto l ((*pt)["install"]);
- if (l && cast<path> (l).string () == "false")
- {
- l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
- continue;
- }
-
- // 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> ())
- build2::match (a, *pt);
- else if (!try_match (a, *pt).first)
- {
- l5 ([&]{trace << "ignoring " << *pt << " (no rule)";});
- pt = nullptr;
- }
-
- if (pt != nullptr)
- pts.push_back (prerequisite_target (pt, pi));
- }
-
- return default_recipe;
- }
-
- // fsdir_rule
- //
- const fsdir_rule fsdir_rule::instance;
-
- bool fsdir_rule::
- match (action, target&, const string&) 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 &execute_inner;
- }
- else
- return noop_recipe;
- }
-
- // group_rule
- //
- const group_rule group_rule::instance (false /* see_through_only */);
-
- bool group_rule::
- match (action a, target& t, const string& h) const
- {
- return (!see_through || t.type ().see_through) &&
- alias_rule::match (a, t, h);
- }
-
- const target* group_rule::
- filter (action, const target&, const target& m) const
- {
- return &m;
- }
-
- recipe group_rule::
- apply (action a, target& t) const
- {
- tracer trace ("install::group_rule::apply");
-
- // Resolve group members.
- //
- // Remember that we are called twice: first during update for install
- // (pre-operation) and then during install. During the former, we rely
- // on the normall update rule to resolve the group members. During the
- // latter, there will be no rule to do this but the group will already
- // have been resolved by the pre-operation.
- //
- // If the rule could not resolve the group, then we ignore it.
- //
- group_view gv (a.outer ()
- ? resolve_members (a, t)
- : t.group_members (a));
-
- 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]);
-
- if (m == nullptr)
- continue;
-
- // Let a customized rule have its say.
- //
- const target* mt (filter (a, t, *m));
- if (mt == nullptr)
- {
- l5 ([&]{trace << "ignoring " << *m << " (filtered out)";});
- continue;
- }
-
- // See if we were explicitly instructed not to touch this target
- // (the same semantics as in the prerequisites match).
- //
- // Note: not the same as lookup_install() above.
- //
- auto l ((*mt)["install"]);
- if (l && cast<path> (l).string () == "false")
- {
- l5 ([&]{trace << "ignoring " << *mt << " (not installable)";});
- continue;
- }
-
- build2::match (a, *mt);
- pts.push_back (mt); // Never ad hoc.
- }
- }
-
- // Delegate to the base rule.
- //
- return alias_rule::apply (a, t);
- }
-
-
- // file_rule
- //
- const file_rule file_rule::instance;
-
- bool file_rule::
- match (action, target&, const string&) const
- {
- // We always match, even if this target is not installable (so that we
- // can ignore it; see apply()).
- //
- return true;
- }
-
- const target* file_rule::
- filter (action a, const target& t, prerequisite_iterator& i) const
- {
- assert (i->member == nullptr);
- return filter (a, t, i->prerequisite);
- }
-
- const target* file_rule::
- filter (action, const target& t, const prerequisite& p) const
- {
- const target& pt (search (t, p));
- return pt.in (t.root_scope ()) ? &pt : nullptr;
- }
-
- recipe file_rule::
- apply (action a, target& t) const
- {
- tracer trace ("install::file_rule::apply");
-
- // 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.
- //
- // In both cases we first determine if the target is installable and
- // return noop if it's not. Otherwise, in the first case (update-for-
- // un/install) we delegate to the normal update and in the second
- // (un/install) -- perform the test.
- //
- if (!lookup_install<path> (t, "install"))
- return noop_recipe;
-
- // In both cases, the next step is to search, match, and collect all the
- // installable prerequisites.
- //
- // But first, in case of the update pre-operation, match the inner rule
- // (actual update). We used to do this after matching the prerequisites
- // but the inner rule may provide some rule-specific information (like
- // the target extension for exe{}) that may be required during the
- // prerequisite search (like the base name for in{}).
- //
- optional<bool> unchanged;
- if (a.operation () == update_id)
- unchanged = match_inner (a, t, unmatch::unchanged);
-
- 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)
- {
- const prerequisite& p (i->prerequisite);
-
- // Ignore excluded.
- //
- include_type pi (include (a, t, p));
-
- if (!pi)
- continue;
-
- // Ignore unresolved targets that are imported from other projects.
- // We are definitely not installing those.
- //
- if (p.proj)
- continue;
-
- // Let a customized rule have its say.
- //
- // 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 (pt == nullptr)
- {
- l5 ([&]{trace << "ignoring " << p << " (filtered out)";});
- continue;
- }
-
- // See if we were explicitly instructed not to touch this target (the
- // same semantics as in alias_rule).
- //
- // Note: not the same as lookup_install() above.
- //
- auto l ((*pt)["install"]);
- if (l && cast<path> (l).string () == "false")
- {
- l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
- continue;
- }
-
- if (pt->is_a<file> ())
- {
- // If the matched rule returned noop_recipe, then the target state
- // is set to unchanged as an optimization. Use this knowledge to
- // optimize things on our side as well since this will help a lot
- // when updating static installable content (headers, documentation,
- // etc).
- //
- if (build2::match (a, *pt, unmatch::unchanged))
- pt = nullptr;
- }
- else if (!try_match (a, *pt).first)
- {
- l5 ([&]{trace << "ignoring " << *pt << " (no rule)";});
- pt = nullptr;
- }
-
- if (pt != nullptr)
- pts.push_back (prerequisite_target (pt, pi));
- }
-
- if (a.operation () == update_id)
- {
- return *unchanged
- ? (pts.empty () ? noop_recipe : default_recipe)
- : &perform_update;
- }
- else
- {
- return [this] (action a, const target& t)
- {
- return a.operation () == install_id
- ? perform_install (a, t)
- : perform_uninstall (a, t);
- };
- }
- }
-
- target_state file_rule::
- perform_update (action a, const target& t)
- {
- // First execute the inner recipe then prerequisites.
- //
- target_state ts (execute_inner (a, t));
-
- if (t.prerequisite_targets[a].size () != 0)
- ts |= straight_execute_prerequisites (a, t);
-
- return ts;
- }
-
- bool file_rule::
- install_extra (const file&, const install_dir&) const
- {
- return false;
- }
-
- bool file_rule::
- uninstall_extra (const file&, const install_dir&) const
- {
- return false;
- }
-
- auto_rmfile file_rule::
- install_pre (const file& t, const install_dir&) const
- {
- return auto_rmfile (t.path (), false /* active */);
- }
-
- bool file_rule::
- install_post (const file& t, const install_dir& id, auto_rmfile&&) const
- {
- return install_extra (t, id);
- }
-
- struct install_dir
- {
- dir_path dir;
-
- // If not NULL, then point to the corresponding install.* value.
- //
- const string* sudo = nullptr;
- const path* cmd = nullptr;
- const strings* options = nullptr;
- const string* mode = nullptr;
- const string* dir_mode = nullptr;
-
- explicit
- install_dir (dir_path d = dir_path ()): dir (move (d)) {}
-
- install_dir (dir_path d, const install_dir& b)
- : dir (move (d)),
- sudo (b.sudo),
- cmd (b.cmd),
- options (b.options),
- mode (b.mode),
- dir_mode (b.dir_mode) {}
- };
-
- using install_dirs = vector<install_dir>;
-
- // Calculate a subdirectory based on l's location (*.subdirs) and if not
- // empty add it to install_dirs. Return the new last element.
- //
- static install_dir&
- resolve_subdir (install_dirs& rs,
- const target& t,
- const scope& s,
- const lookup& l)
- {
- // Find the scope from which this value came and use as a base
- // to calculate the subdirectory.
- //
- for (const scope* p (&s); p != nullptr; p = p->parent_scope ())
- {
- if (l.belongs (*p, true)) // Include target type/pattern-specific.
- {
- // The target can be in out or src.
- //
- const dir_path& d (t.out_dir ().leaf (p->out_path ()));
-
- // Add it as another leading directory rather than modifying
- // the last one directly; somehow, it feels right.
- //
- if (!d.empty ())
- rs.emplace_back (rs.back ().dir / d, rs.back ());
- break;
- }
- }
-
- return rs.back ();
- }
-
- // Resolve installation directory name to absolute directory path. Return
- // all the super-directories leading up to the destination (last).
- //
- // If target is not NULL, then also handle the subdirs logic.
- //
- static install_dirs
- resolve (const scope& s,
- const target* t,
- dir_path d,
- bool fail_unknown = true,
- const string* var = nullptr)
- {
- install_dirs rs;
-
- if (d.absolute ())
- rs.emplace_back (move (d.normalize ()));
- else
- {
- // If it is relative, then the first component is treated as the
- // installation directory name, e.g., bin, sbin, lib, etc. Look it
- // up and recurse.
- //
- if (d.empty ())
- fail << "empty installation directory name";
-
- const string& sn (*d.begin ());
- const string var ("install." + sn);
- if (const dir_path* dn = lookup_install<dir_path> (s, var))
- {
- if (dn->empty ())
- fail << "empty installation directory for name " << sn <<
- info << "did you specified empty config." << var << "?";
-
- rs = resolve (s, t, *dn, fail_unknown, &var);
-
- if (rs.empty ())
- {
- assert (!fail_unknown);
- return rs; // Empty.
- }
-
- d = rs.back ().dir / dir_path (++d.begin (), d.end ());
- rs.emplace_back (move (d.normalize ()), rs.back ());
- }
- else
- {
- if (fail_unknown)
- fail << "unknown installation directory name '" << sn << "'" <<
- info << "did you forget to specify config." << var << "?";
-
- return rs; // Empty.
- }
- }
-
- install_dir* r (&rs.back ());
-
- // Override components in install_dir if we have our own.
- //
- if (var != nullptr)
- {
- if (auto l = s[*var + ".sudo"]) r->sudo = &cast<string> (l);
- if (auto l = s[*var + ".cmd"]) r->cmd = &cast<path> (l);
- if (auto l = s[*var + ".mode"]) r->mode = &cast<string> (l);
- if (auto l = s[*var + ".dir_mode"]) r->dir_mode = &cast<string> (l);
- if (auto l = s[*var + ".options"]) r->options = &cast<strings> (l);
-
- if (t != nullptr)
- {
- if (auto l = s[*var + ".subdirs"])
- {
- if (cast<bool> (l))
- r = &resolve_subdir (rs, *t, s, l);
- }
- }
- }
-
- // Set globals for unspecified components.
- //
- if (r->sudo == nullptr)
- r->sudo = cast_null<string> (s["config.install.sudo"]);
-
- if (r->cmd == nullptr)
- r->cmd = &cast<path> (s["config.install.cmd"]);
-
- if (r->options == nullptr)
- r->options = cast_null<strings> (s["config.install.options"]);
-
- if (r->mode == nullptr)
- r->mode = &cast<string> (s["config.install.mode"]);
-
- if (r->dir_mode == nullptr)
- r->dir_mode = &cast<string> (s["config.install.dir_mode"]);
-
- return rs;
- }
-
- static inline install_dirs
- resolve (const target& t, dir_path d, bool fail_unknown = true)
- {
- return resolve (t.base_scope (), &t, d, fail_unknown);
- }
-
- dir_path
- resolve_dir (const target& t, dir_path d, bool fail_unknown)
- {
- install_dirs r (resolve (t, move (d), fail_unknown));
- return r.empty () ? dir_path () : move (r.back ().dir);
- }
-
- dir_path
- resolve_dir (const scope& s, dir_path d, bool fail_unknown)
- {
- install_dirs r (resolve (s, nullptr, move (d), fail_unknown));
- return r.empty () ? dir_path () : move (r.back ().dir);
- }
-
- path
- resolve_file (const file& f)
- {
- // Note: similar logic to perform_install().
- //
- const path* p (lookup_install<path> (f, "install"));
-
- if (p == nullptr) // Not installable.
- return path ();
-
- bool n (!p->to_directory ());
- dir_path d (n ? p->directory () : path_cast<dir_path> (*p));
-
- install_dirs ids (resolve (f, d));
-
- if (!n)
- {
- if (auto l = f["install.subdirs"])
- {
- if (cast<bool> (l))
- resolve_subdir (ids, f, f.base_scope (), l);
- }
- }
-
- return ids.back ().dir / (n ? p->leaf () : f.path ().leaf ());
- }
-
- // On Windows we use MSYS2 install.exe and MSYS2 by default ignores
- // filesystem permissions (noacl mount option). And this means, for
- // example, that .exe that we install won't be runnable by Windows (MSYS2
- // itself will still run them since it recognizes the file extension).
- //
- // NOTE: this is no longer the case and we now use noacl (and acl causes
- // other problems; see baseutils fstab for details).
- //
- // The way we work around this (at least in our distribution of the MSYS2
- // tools) is by changing the mount option for cygdrives (/c, /d, etc) to
- // acl. But that's not all: we also have to install via a path that "hits"
- // one of those mount points, c:\foo won't work, we have to use /c/foo.
- // So this function translates an absolute Windows path to its MSYS
- // representation.
- //
- // Note that we return the result as a string, not dir_path since path
- // starting with / are illegal on Windows. Also note that the result
- // doesn't have the trailing slash.
- //
- static string
- msys_path (const dir_path& d)
- {
- assert (d.absolute ());
- string s (d.representation ());
-
- // First replace ':' with the drive letter (so the path is no longer
- // absolute) but postpone setting the first character to / until we are
- // a string.
- //
- s[1] = lcase (s[0]);
- s = dir_path (move (s)).posix_string ();
- s[0] = '/';
-
- return s;
- }
-
- // Given an abolute path return its chroot'ed version, if any, accoring to
- // install.chroot.
- //
- template <typename P>
- static inline P
- chroot_path (const scope& rs, const P& p)
- {
- if (const dir_path* d = cast_null<dir_path> (rs["install.chroot"]))
- {
- dir_path r (p.root_directory ());
- assert (!r.empty ()); // Must be absolute.
-
- return *d / p.leaf (r);
- }
-
- return p;
- }
-
- // install -d <dir>
- //
- static void
- install_d (const scope& rs,
- const install_dir& base,
- const dir_path& d,
- bool verbose = true)
- {
- // Here is the problem: if this is a dry-run, then we will keep showing
- // the same directory creation commands over and over again (because we
- // don't actually create them). There are two alternative ways to solve
- // this: actually create the directories or simply don't show anything.
- // While we use the former approach during update (see mkdir() in
- // filesystem), here it feels like we really shouldn't be touching the
- // destination filesystem. Plus, not showing anything will be symmetric
- // with uninstall since the directories won't be empty (because we don't
- // actually uninstall any files).
- //
- if (dry_run)
- return;
-
- dir_path chd (chroot_path (rs, d));
-
- try
- {
- if (dir_exists (chd)) // May throw (e.g., EACCES).
- return;
- }
- catch (const system_error& e)
- {
- fail << "invalid installation directory " << chd << ": " << e;
- }
-
- // While install -d will create all the intermediate components between
- // base and dir, we do it explicitly, one at a time. This way the output
- // is symmetrical to uninstall() below.
- //
- // Note that if the chroot directory does not exist, then install -d
- // will create it and we don't bother removing it.
- //
- if (d != base.dir)
- {
- dir_path pd (d.directory ());
-
- if (pd != base.dir)
- install_d (rs, base, pd, verbose);
- }
-
- cstrings args;
-
- string reld (
- cast<string> ((*global_scope)["build.host.class"]) == "windows"
- ? msys_path (chd)
- : relative (chd).string ());
-
- if (base.sudo != nullptr)
- args.push_back (base.sudo->c_str ());
-
- args.push_back (base.cmd->string ().c_str ());
- args.push_back ("-d");
-
- if (base.options != nullptr)
- append_options (args, *base.options);
-
- args.push_back ("-m");
- args.push_back (base.dir_mode->c_str ());
- args.push_back (reld.c_str ());
- args.push_back (nullptr);
-
- process_path pp (run_search (args[0]));
-
- if (verb >= 2)
- print_process (args);
- else if (verb && verbose)
- text << "install " << chd;
-
- run (pp, args);
- }
-
- // install <file> <dir>/
- // install <file> <file>
- //
- static void
- install_f (const scope& rs,
- const install_dir& base,
- const path& name,
- const file& t,
- const path& f,
- bool verbose)
- {
- path relf (relative (f));
-
- dir_path chd (chroot_path (rs, base.dir));
-
- string reld (
- cast<string> ((*global_scope)["build.host.class"]) == "windows"
- ? msys_path (chd)
- : relative (chd).string ());
-
- if (!name.empty ())
- {
- reld += path::traits_type::directory_separator;
- reld += name.string ();
- }
-
- cstrings args;
-
- if (base.sudo != nullptr)
- args.push_back (base.sudo->c_str ());
-
- args.push_back (base.cmd->string ().c_str ());
-
- if (base.options != nullptr)
- append_options (args, *base.options);
-
- args.push_back ("-m");
- args.push_back (base.mode->c_str ());
- args.push_back (relf.string ().c_str ());
- args.push_back (reld.c_str ());
- args.push_back (nullptr);
-
- process_path pp (run_search (args[0]));
-
- if (verb >= 2)
- print_process (args);
- else if (verb && verbose)
- text << "install " << t;
-
- if (!dry_run)
- run (pp, args);
- }
-
- void file_rule::
- install_l (const scope& rs,
- const install_dir& base,
- const path& target,
- const path& link,
- bool verbose)
- {
- path rell (relative (chroot_path (rs, base.dir)));
- rell /= link;
-
- // We can create a symlink directly without calling ln. This, however,
- // won't work if we have sudo. Also, we would have to deal with existing
- // destinations (ln's -f takes care of that). So we are just going to
- // always use ln.
- //
- const char* args_a[] = {
- base.sudo != nullptr ? base.sudo->c_str () : nullptr,
- "ln",
- "-sf",
- target.string ().c_str (),
- rell.string ().c_str (),
- nullptr};
-
- const char** args (&args_a[base.sudo == nullptr ? 1 : 0]);
-
- process_path pp (run_search (args[0]));
-
- if (verb >= 2)
- print_process (args);
- else if (verb && verbose)
- text << "install " << rell << " -> " << target;
-
- if (!dry_run)
- run (pp, args);
- }
-
- target_state file_rule::
- perform_install (action a, const target& xt) const
- {
- const file& t (xt.as<file> ());
- 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 ());
-
- auto install_target = [&rs, this] (const file& t,
- const path& p,
- bool verbose)
- {
- // Note: similar logic to resolve_file().
- //
- bool n (!p.to_directory ());
- dir_path d (n ? p.directory () : path_cast<dir_path> (p));
-
- // Resolve target directory.
- //
- install_dirs ids (resolve (t, d));
-
- // Handle install.subdirs if one was specified. Unless the target path
- // includes the file name in which case we assume it's a "final" path.
- //
- if (!n)
- {
- if (auto l = t["install.subdirs"])
- {
- if (cast<bool> (l))
- resolve_subdir (ids, t, t.base_scope (), l);
- }
- }
-
- // Create leading directories. Note that we are using the leading
- // directory (if there is one) for the creation information (mode,
- // sudo, etc).
- //
- for (auto i (ids.begin ()), j (i); i != ids.end (); j = i++)
- install_d (rs, *j, i->dir, verbose); // install -d
-
- install_dir& id (ids.back ());
-
- // Override mode if one was specified.
- //
- if (auto l = t["install.mode"])
- id.mode = &cast<string> (l);
-
- // Install the target.
- //
- auto_rmfile f (install_pre (t, id));
-
- // If install_pre() returned a different file name, make sure we
- // install it as the original.
- //
- const path& tp (t.path ());
- const path& fp (f.path);
-
- install_f (
- rs,
- id,
- n ? p.leaf () : fp.leaf () != tp.leaf () ? tp.leaf () : path (),
- t,
- f.path,
- verbose);
-
- install_post (t, id, move (f));
- };
-
- // First handle installable prerequisites.
- //
- target_state r (straight_execute_prerequisites (a, t));
-
- // Then installable ad hoc group members, if any.
- //
- 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, tp.empty () /* verbose */);
- r |= target_state::changed;
- }
- }
-
- // Finally install the target itself (since we got here we know the
- // install variable is there).
- //
- if (!tp.empty ())
- {
- install_target (t, cast<path> (t["install"]), true /* verbose */);
- r |= target_state::changed;
- }
-
- return r;
- }
-
- // uninstall -d <dir>
- //
- // We try to remove all the directories between base and dir but not base
- // itself unless base == dir. Return false if nothing has been removed
- // (i.e., the directories do not exist or are not empty).
- //
- static bool
- uninstall_d (const scope& rs,
- const install_dir& base,
- const dir_path& d,
- bool verbose)
- {
- // See install_d() for the rationale.
- //
- if (dry_run)
- return false;
-
- dir_path chd (chroot_path (rs, d));
-
- // Figure out if we should try to remove this directory. Note that if
- // it doesn't exist, then we may still need to remove outer ones.
- //
- bool r (false);
- try
- {
- if ((r = dir_exists (chd))) // May throw (e.g., EACCES).
- {
- if (!dir_empty (chd)) // May also throw.
- return false; // Won't be able to remove any outer directories.
- }
- }
- catch (const system_error& e)
- {
- fail << "invalid installation directory " << chd << ": " << e;
- }
-
- if (r)
- {
- dir_path reld (relative (chd));
-
- // Normally when we need to remove a file or directory we do it
- // directly without calling rm/rmdir. This however, won't work if we
- // have sudo. So we are going to do it both ways.
- //
- // While there is no sudo on Windows, deleting things that are being
- // used can get complicated. So we will always use rm/rmdir there.
- //
-#ifndef _WIN32
- if (base.sudo == nullptr)
- {
- if (verb >= 2)
- text << "rmdir " << reld;
- else if (verb && verbose)
- text << "uninstall " << reld;
-
- try
- {
- try_rmdir (chd);
- }
- catch (const system_error& e)
- {
- fail << "unable to remove directory " << chd << ": " << e;
- }
- }
- else
-#endif
- {
- const char* args_a[] = {
- base.sudo != nullptr ? base.sudo->c_str () : nullptr,
- "rmdir",
- reld.string ().c_str (),
- nullptr};
-
- const char** args (&args_a[base.sudo == nullptr ? 1 : 0]);
-
- process_path pp (run_search (args[0]));
-
- if (verb >= 2)
- print_process (args);
- else if (verb && verbose)
- text << "uninstall " << reld;
-
- run (pp, args);
- }
- }
-
- // If we have more empty directories between base and dir, then try
- // to clean them up as well.
- //
- if (d != base.dir)
- {
- dir_path pd (d.directory ());
-
- if (pd != base.dir)
- r = uninstall_d (rs, base, pd, verbose) || r;
- }
-
- return r;
- }
-
- bool file_rule::
- uninstall_f (const scope& rs,
- const install_dir& base,
- const file* t,
- const path& name,
- bool verbose)
- {
- assert (t != nullptr || !name.empty ());
- path f (chroot_path (rs, base.dir) /
- (name.empty () ? t->path ().leaf () : name));
-
- try
- {
- // Note: don't follow symlinks so if the target is a dangling symlinks
- // we will proceed to removing it.
- //
- if (!file_exists (f, false)) // May throw (e.g., EACCES).
- return false;
- }
- catch (const system_error& e)
- {
- fail << "invalid installation path " << f << ": " << e;
- }
-
- path relf (relative (f));
-
- if (verb == 1 && verbose)
- {
- if (t != nullptr)
- text << "uninstall " << *t;
- else
- text << "uninstall " << relf;
- }
-
- // The same story as with uninstall -d.
- //
-#ifndef _WIN32
- if (base.sudo == nullptr)
- {
- if (verb >= 2)
- text << "rm " << relf;
-
- if (!dry_run)
- {
- try
- {
- try_rmfile (f);
- }
- catch (const system_error& e)
- {
- fail << "unable to remove file " << f << ": " << e;
- }
- }
- }
- else
-#endif
- {
- const char* args_a[] = {
- base.sudo != nullptr ? base.sudo->c_str () : nullptr,
- "rm",
- "-f",
- relf.string ().c_str (),
- nullptr};
-
- const char** args (&args_a[base.sudo == nullptr ? 1 : 0]);
-
- process_path pp (run_search (args[0]));
-
- if (verb >= 2)
- print_process (args);
-
- if (!dry_run)
- run (pp, args);
- }
-
- return true;
- }
-
- target_state file_rule::
- perform_uninstall (action a, const target& xt) const
- {
- const file& t (xt.as<file> ());
- 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 ());
-
- auto uninstall_target = [&rs, this] (const file& t,
- const path& p,
- bool verbose) -> target_state
- {
- bool n (!p.to_directory ());
- dir_path d (n ? p.directory () : path_cast<dir_path> (p));
-
- // Resolve target directory.
- //
- install_dirs ids (resolve (t, d));
-
- // Handle install.subdirs if one was specified.
- //
- if (!n)
- {
- if (auto l = t["install.subdirs"])
- {
- if (cast<bool> (l))
- resolve_subdir (ids, t, t.base_scope (), l);
- }
- }
-
- // Remove extras and the target itself.
- //
- const install_dir& id (ids.back ());
-
- target_state r (uninstall_extra (t, id)
- ? target_state::changed
- : target_state::unchanged);
-
- if (uninstall_f (rs, id, &t, n ? p.leaf () : path (), verbose))
- r |= target_state::changed;
-
- // Clean up empty leading directories (in reverse).
- //
- // Note that we are using the leading directory (if there is one)
- // for the clean up information (sudo, etc).
- //
- for (auto i (ids.rbegin ()), j (i), e (ids.rend ()); i != e; j = ++i)
- {
- if (install::uninstall_d (rs, ++j != e ? *j : *i, i->dir, verbose))
- r |= target_state::changed;
- }
-
- return r;
- };
-
- // Reverse order of installation: first the target itself (since we got
- // here we know the install variable is there).
- //
- 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
- // single-linked list).
- //
- 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,
- tp.empty () || r != target_state::changed);
- }
-
- // Finally handle installable prerequisites.
- //
- r |= reverse_execute_prerequisites (a, t);
-
- return r;
- }
- }
-}
diff --git a/build2/install/rule.hxx b/build2/install/rule.hxx
deleted file mode 100644
index 09dd1b5..0000000
--- a/build2/install/rule.hxx
+++ /dev/null
@@ -1,195 +0,0 @@
-// file : build2/install/rule.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_INSTALL_RULE_HXX
-#define BUILD2_INSTALL_RULE_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/rule.hxx>
-#include <libbuild2/action.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/filesystem.hxx>
-
-namespace build2
-{
- namespace install
- {
- class alias_rule: public rule
- {
- public:
- virtual bool
- match (action, target&, const string&) const override;
-
- // Return NULL if this prerequisite should be ignored and pointer to its
- // target otherwise. The default implementation accepts all prerequsites
- // from the target's (weak) amalgamation.
- //
- // 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*
- filter (action, const target&, prerequisite_iterator&) const;
-
- virtual const target*
- filter (action, const target&, const prerequisite&) const;
-
- virtual recipe
- apply (action, target&) const override;
-
- alias_rule () {}
- static const alias_rule instance;
- };
-
- class fsdir_rule: public rule
- {
- public:
- virtual bool
- match (action, target&, const string&) const override;
-
- virtual recipe
- apply (action, target&) const override;
-
- fsdir_rule () {}
- static const fsdir_rule instance;
- };
-
- // In addition to the alias rule's semantics, this rule sees through to
- // the group's members.
- //
- // The default group_rule::instance matches any target for which it was
- // registered. It is to be used for non-see-through groups that should
- // exhibit the see-through behavior for install (see lib{} in the bin
- // module for an example).
- //
- // We also register (for all targets) another instance of this rule that
- // only matches see-through groups.
- //
- class group_rule: public alias_rule
- {
- public:
- virtual bool
- match (action, target&, const string&) const override;
-
- // Return NULL if this group member should be ignored and pointer to its
- // target otherwise. The default implementation accepts all members.
- //
- virtual const target*
- filter (action, const target&, const target& group_member) const;
-
- using alias_rule::filter; // "Unhide" to make Clang happy.
-
- virtual recipe
- apply (action, target&) const override;
-
- group_rule (bool see_through_only): see_through (see_through_only) {}
- static const group_rule instance;
-
- bool see_through;
- };
-
- struct install_dir;
-
- class file_rule: public rule
- {
- public:
- virtual bool
- match (action, target&, const string&) const override;
-
- // 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...
- //
- // 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*
- filter (action, const target&, prerequisite_iterator&) const;
-
- virtual const target*
- filter (action, const target&, const prerequisite&) const;
-
- virtual recipe
- apply (action, target&) const override;
-
- static target_state
- perform_update (action, const target&);
-
- // Extra un/installation hooks. Return true if anything was actually
- // un/installed.
- //
- using install_dir = install::install_dir; // For derived rules.
-
- virtual bool
- install_extra (const file&, const install_dir&) const;
-
- virtual bool
- uninstall_extra (const file&, const install_dir&) const;
-
- // Lower-level pre/post installation hooks that can be used to override
- // the source file path being installed (for example, to implement
- // post-processing, etc).
- //
- // Note that one cannot generally perform post-processing in-place
- // because of permissions.
- //
- virtual auto_rmfile
- install_pre (const file&, const install_dir&) const;
-
- virtual bool
- install_post (const file&, const install_dir&, auto_rmfile&&) const;
-
- // Installation/uninstallation "commands".
- //
- // If verbose is false, then only print the command at verbosity level 2
- // or higher. Note that these functions respect the dry_run flag.
-
- // Install a symlink: base/link -> target.
- //
- static void
- install_l (const scope& rs,
- const install_dir& base,
- const path& target,
- const path& link,
- bool verbose);
-
- // Uninstall a file or symlink:
- //
- // uninstall <target> <base>/ rm <base>/<target>.leaf (); name empty
- // uninstall <target> <name> rm <base>/<name>; target can be NULL
- //
- // Return false if nothing has been removed (i.e., the file does not
- // exist).
- //
- static bool
- uninstall_f (const scope& rs,
- const install_dir& base,
- const file* target,
- const path& name,
- bool verbose);
-
- target_state
- perform_install (action, const target&) const;
-
- target_state
- perform_uninstall (action, const target&) const;
-
- static const file_rule instance;
- file_rule () {}
- };
- }
-}
-
-#endif // BUILD2_INSTALL_RULE_HXX
diff --git a/build2/install/utility.hxx b/build2/install/utility.hxx
deleted file mode 100644
index 29c6db0..0000000
--- a/build2/install/utility.hxx
+++ /dev/null
@@ -1,76 +0,0 @@
-// file : build2/install/utility.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_INSTALL_UTILITY_HXX
-#define BUILD2_INSTALL_UTILITY_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-
-namespace build2
-{
- namespace install
- {
- // Set install path, mode for a target type.
- //
- inline void
- install_path (scope& s, const target_type& tt, dir_path d)
- {
- auto r (
- s.target_vars[tt]["*"].insert (
- var_pool.rw (s).insert ("install")));
-
- if (r.second) // Already set by the user?
- r.first.get () = path_cast<path> (move (d));
- }
-
- template <typename T>
- inline void
- install_path (scope& s, dir_path d)
- {
- return install_path (s, T::static_type, move (d));
- }
-
- inline void
- install_mode (scope& s, const target_type& tt, string m)
- {
- auto r (
- s.target_vars[tt]["*"].insert (
- var_pool.rw (s).insert ("install.mode")));
-
- if (r.second) // Already set by the user?
- r.first.get () = move (m);
- }
-
- template <typename T>
- inline void
- install_mode (scope& s, string m)
- {
- return install_mode (s, T::static_type, move (m));
- }
-
- // 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
- // unless fail_unknown is false, in which case return empty directory.
- //
- // Note: implemented in rule.cxx.
- //
- dir_path
- resolve_dir (const target&, dir_path, bool fail_unknown = true);
-
- dir_path
- resolve_dir (const scope&, dir_path, bool fail_unknown = true);
-
- // Resolve file installation path returning empty path if not installable.
- //
- path
- resolve_file (const file&); // rule.cxx
- }
-}
-
-#endif // BUILD2_INSTALL_UTILITY_HXX