From 57b10c06925d0bdf6ffb38488ee908f085109e95 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 4 Jul 2019 19:12:15 +0300 Subject: Move config, dist, test, and install modules into library --- build2/install/functions.cxx | 33 -- build2/install/init.cxx | 303 ----------- build2/install/init.hxx | 31 -- build2/install/operation.cxx | 84 --- build2/install/operation.hxx | 23 - build2/install/rule.cxx | 1222 ------------------------------------------ build2/install/rule.hxx | 195 ------- build2/install/utility.hxx | 76 --- 8 files changed, 1967 deletions(-) delete mode 100644 build2/install/functions.cxx delete mode 100644 build2/install/init.cxx delete mode 100644 build2/install/init.hxx delete mode 100644 build2/install/operation.cxx delete mode 100644 build2/install/operation.hxx delete mode 100644 build2/install/rule.cxx delete mode 100644 build2/install/rule.hxx delete mode 100644 build2/install/utility.hxx (limited to 'build2/install') 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 -#include - -#include - -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 - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -using namespace std; -using namespace butl; - -namespace build2 -{ - namespace install - { - // Set install..* values based on config.install..* 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 - 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 (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 (move (vn))); - - value& v (r.assign (vr)); - - if (spec) - { - if (l) - v = cast (l); // Strip CT to T. - } - else - { - if (dv != nullptr) - v = *dv; - } - } - - template - 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 (s, r, n, "", p.empty () ? nullptr : &p, o); - - set_var (s, r, n, ".cmd", c.empty () ? nullptr : &c); - set_var (s, r, n, ".options", (strings*) (nullptr)); - set_var (s, r, n, ".mode", fm.empty () ? nullptr : &fm); - set_var (s, r, n, ".dir_mode", dm.empty () ? nullptr : &dm); - set_var (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 (string ("install.") + n + ".subdirs"); - } - - void - functions (); // functions.cxx - - bool - boot (scope& rs, const location&, unique_ptr&) - { - 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&, - 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 ("install", variable_visibility::target); - vp.insert ("install.mode", variable_visibility::project); - vp.insert ("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 (perform_install_id, "install.alias", ar); - r.insert (perform_uninstall_id, "uninstall.alias", ar); - - r.insert (perform_install_id, "install.fsdir", dr); - r.insert (perform_uninstall_id, "install.fsdir", dr); - - r.insert (perform_install_id, "install.file", fr); - r.insert (perform_uninstall_id, "uninstall.file", fr); - - r.insert (perform_install_id, "install.file", gr); - r.insert (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 ( "install.chroot")); - auto& cvar (vp.insert ("config.install.chroot", true)); - - value& v (rs.assign (var)); - - if (s) - { - if (lookup l = config::optional (rs, cvar)) - v = cast (l); // Strip abs_dir_path. - } - } - } - - // Configure "installability" for built-in target types. - // - install_path (bs, dir_path ("bin")); // Install into install.bin. - install_path (bs, dir_path ("doc")); // Install into install.doc. - install_path (bs, dir_path ("man")); // Install into install.man. - install_path (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 -#include - -#include - -namespace build2 -{ - namespace install - { - bool - boot (scope&, const location&, unique_ptr&); - - bool - init (scope&, - scope&, - const location&, - unique_ptr&, - 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 - -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 -#include - -#include - -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 - -#include // dir_exists(), file_exists() - -#include -#include -#include -#include -#include - -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 - static const P* - lookup_install (T& t, const string& var) - { - auto l (t[var]); - - if (!l) - return nullptr; - - const P& r (cast

(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 (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 ()) - 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 (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 (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 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 (l).string () == "false") - { - l5 ([&]{trace << "ignoring " << *pt << " (not installable)";}); - continue; - } - - if (pt->is_a ()) - { - // 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; - - // 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 (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 (l); - if (auto l = s[*var + ".cmd"]) r->cmd = &cast (l); - if (auto l = s[*var + ".mode"]) r->mode = &cast (l); - if (auto l = s[*var + ".dir_mode"]) r->dir_mode = &cast (l); - if (auto l = s[*var + ".options"]) r->options = &cast (l); - - if (t != nullptr) - { - if (auto l = s[*var + ".subdirs"]) - { - if (cast (l)) - r = &resolve_subdir (rs, *t, s, l); - } - } - } - - // Set globals for unspecified components. - // - if (r->sudo == nullptr) - r->sudo = cast_null (s["config.install.sudo"]); - - if (r->cmd == nullptr) - r->cmd = &cast (s["config.install.cmd"]); - - if (r->options == nullptr) - r->options = cast_null (s["config.install.options"]); - - if (r->mode == nullptr) - r->mode = &cast (s["config.install.mode"]); - - if (r->dir_mode == nullptr) - r->dir_mode = &cast (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 (f, "install")); - - if (p == nullptr) // Not installable. - return path (); - - bool n (!p->to_directory ()); - dir_path d (n ? p->directory () : path_cast (*p)); - - install_dirs ids (resolve (f, d)); - - if (!n) - { - if (auto l = f["install.subdirs"]) - { - if (cast (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 - static inline P - chroot_path (const scope& rs, const P& p) - { - if (const dir_path* d = cast_null (rs["install.chroot"])) - { - dir_path r (p.root_directory ()); - assert (!r.empty ()); // Must be absolute. - - return *d / p.leaf (r); - } - - return p; - } - - // install -d

- // - 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 ((*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 / - // install - // - 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 ((*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 ()); - 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 (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 (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 (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 (*m, "install")) - { - install_target (m->as (), *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 (t["install"]), true /* verbose */); - r |= target_state::changed; - } - - return r; - } - - // uninstall -d - // - // 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 ()); - 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 (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 (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 (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 (*m, "install")) - r |= uninstall_target (m->as (), - *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 -#include - -#include -#include -#include -#include - -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::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::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 / rm /.leaf (); name empty - // uninstall rm /; 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 -#include - -#include -#include - -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 (move (d)); - } - - template - 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 - 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 -- cgit v1.1