// file : libbuild2/install/init.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <libbuild2/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 <libbuild2/config/utility.hxx> #include <libbuild2/install/rule.hxx> #include <libbuild2/install/utility.hxx> #include <libbuild2/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 (r.ctx.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 (r.ctx.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) r.ctx.var_pool.rw (r).insert<bool> ( string ("install.") + n + ".subdirs"); } void functions (function_map&); // functions.cxx bool boot (scope& rs, const location&, unique_ptr<module_base>&) { tracer trace ("install::boot"); l5 ([&]{trace << "for " << rs;}); context& ctx (rs.ctx); // Register install function family if this is the first instance of the // install modules. // if (!function_family::defined (ctx.functions, "install")) functions (ctx.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 (rs.ctx.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<bool> ("for_install", variable_visibility::prereq); 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; } static const module_functions mod_functions[] = { {"install", &boot, &init}, {nullptr, nullptr, nullptr} }; const module_functions* build2_install_load () { return mod_functions; } } }