aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/install/functions.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/install/functions.cxx')
-rw-r--r--libbuild2/install/functions.cxx116
1 files changed, 112 insertions, 4 deletions
diff --git a/libbuild2/install/functions.cxx b/libbuild2/install/functions.cxx
index 5668efe..1de4d3e 100644
--- a/libbuild2/install/functions.cxx
+++ b/libbuild2/install/functions.cxx
@@ -15,17 +15,125 @@ namespace build2
{
function_family f (m, "install");
- // Resolve potentially relative install.* value to an absolute directory
- // based on (other) install.* values visible from the calling scope.
+ // $install.resolve(<dir>[, <rel_base>])
+ //
+ // @@ TODO: add overload to call resolve_file().
+ //
+ // Resolve potentially relative install.* value to an absolute and
+ // normalized directory based on (other) install.* values visible from
+ // the calling scope.
+ //
+ // If rel_base is specified and is not empty, then make the resulting
+ // directory relative to it. If rel_base itself is relative, first
+ // resolve it to an absolute and normalized directory based on install.*
+ // values. Note that this argument is mandatory if this function is
+ // called during relocatable installation (install.relocatable is true).
+ // While you can pass empty directory to suppress this functionality,
+ // make sure this does not render the result non-relocatable.
+ //
+ // As an example, consider an executable that supports loading plugins
+ // and requires the plugin installation directory to be embedded into
+ // the executable during the build. The common way to support
+ // relocatable installations for such cases is to embed a path relative
+ // to the executable and complete it at runtime. If you would like to
+ // always use the relative path, regardless of whether the installation
+ // is relocatable of not, then you can simply always pass rel_base, for
+ // example:
+ //
+ // plugin_dir = $install.resolve($install.lib, $install.bin)
+ //
+ // Alternatively, if you would like to continue using absolute paths for
+ // non-relocatable installations, then you can use something like this:
+ //
+ // plugin_dir = $install.resolve($install.lib, ($install.relocatable ? $install.bin : [dir_path] ))
+ //
+ // Finally, if you are unable to support relocatable installations, the
+ // correct way to handle this is NOT to always pass an empty path for
+ // rel_base but rather assert in root.build that your project does not
+ // support relocatable installations, for example:
+ //
+ // assert (!$install.relocatable) 'relocatable installation not supported'
//
// Note that this function is not pure.
//
- f.insert (".resolve", false) += [] (const scope* s, dir_path d)
+ f.insert (".resolve", false) += [] (const scope* s,
+ dir_path dir,
+ optional<dir_path> rel_base)
{
if (s == nullptr)
fail << "install.resolve() called out of scope" << endf;
- return resolve_dir (*s, move (d));
+ if (!rel_base)
+ {
+ const scope& rs (*s->root_scope ());
+
+ if (cast_false<bool> (rs["install.relocatable"]))
+ {
+ fail << "relocatable installation requires relative base "
+ << "directory" <<
+ info << "pass empty relative base directory if this call does "
+ << "not affect installation relocatability" <<
+ info << "or add `assert (!$install.relocatable) 'relocatable "
+ << "installation not supported'` before the call";
+ }
+ }
+
+ return resolve_dir (*s,
+ move (dir),
+ rel_base ? move (*rel_base) : dir_path ());
+ };
+
+ // @@ TODO: add $install.chroot().
+
+ // $install.filter(<path>[, <type>])
+ //
+ // Apply filters from config.install.filter and return true if the
+ // specified filesystem entry should be installed/uninstalled. Note that
+ // the entry is specified as an absolute and normalized installation
+ // path (so not $path($>) but $install.resolve($>)).
+ //
+ // The type argument can be one of `regular`, `directory`, or `symlink`.
+ // If unspecified, either `directory` or `regular` is assumed, based on
+ // whether path is syntactially a directory (ends with a directory
+ // separator).
+ //
+ // Note that this function is not pure.
+ //
+ f.insert (".filter", false) += [] (const scope* s,
+ path p,
+ optional<names> ot)
+ {
+ if (s == nullptr)
+ fail << "install.filter() called out of scope" << endf;
+
+ entry_type t;
+ if (ot)
+ {
+ string v (convert<string> (move (*ot)));
+
+ if (v == "regular") t = entry_type::regular;
+ else if (v == "directory") t = entry_type::directory;
+ else if (v == "symlink") t = entry_type::symlink;
+ else throw invalid_argument ("unknown type '" + v + '\'');
+ }
+ else
+ t = p.to_directory () ? entry_type::directory : entry_type::regular;
+
+ // Split into directory and leaf.
+ //
+ dir_path d;
+ if (t == entry_type::directory)
+ {
+ d = path_cast<dir_path> (move (p));
+ p = path (); // No leaf.
+ }
+ else
+ {
+ d = p.directory ();
+ p.make_leaf ();
+ }
+
+ return filter_entry (*s->root_scope (), d, p, t);
};
}
}