aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/install
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/install')
-rw-r--r--libbuild2/install/functions.cxx56
-rw-r--r--libbuild2/install/rule.cxx46
-rw-r--r--libbuild2/install/utility.hxx13
3 files changed, 102 insertions, 13 deletions
diff --git a/libbuild2/install/functions.cxx b/libbuild2/install/functions.cxx
index c36a46e..8d1a1f3 100644
--- a/libbuild2/install/functions.cxx
+++ b/libbuild2/install/functions.cxx
@@ -15,18 +15,70 @@ namespace build2
{
function_family f (m, "install");
+ // $install.resolve(<dir>[, <rel_base>])
+ //
// 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 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 ());
};
}
}
diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx
index 9f7eaac..5a8242b 100644
--- a/libbuild2/install/rule.cxx
+++ b/libbuild2/install/rule.cxx
@@ -668,24 +668,52 @@ namespace build2
return rs;
}
- static inline install_dirs
- resolve (const target& t, dir_path d, bool fail_unknown = true)
+ static dir_path
+ resolve_dir (const scope& s, const target* t,
+ dir_path d, dir_path rb,
+ bool fail_unknown)
{
- return resolve (t.base_scope (), &t, move (d), fail_unknown);
+ install_dirs rs (resolve (s, t, move (d), fail_unknown));
+
+ if (rs.empty ())
+ return dir_path ();
+
+ dir_path r (move (rs.back ().dir));
+
+ if (!rb.empty ())
+ {
+ dir_path b (resolve (s, t, move (rb), false).back ().dir);
+
+ try
+ {
+ r = r.relative (b);
+ }
+ catch (const invalid_path&)
+ {
+ fail << "unable to make installation directory " << r
+ << " relative to " << b;
+ }
+ }
+
+ return r;
}
dir_path
- resolve_dir (const target& t, dir_path d, bool fail_unknown)
+ resolve_dir (const target& t, dir_path d, dir_path rb, bool fail_unknown)
{
- install_dirs r (resolve (t, move (d), fail_unknown));
- return r.empty () ? dir_path () : move (r.back ().dir);
+ return resolve_dir (t.base_scope (), &t, move (d), move (rb), fail_unknown);
}
dir_path
- resolve_dir (const scope& s, dir_path d, bool fail_unknown)
+ resolve_dir (const scope& s, dir_path d, dir_path rb, bool fail_unknown)
+ {
+ return resolve_dir (s, nullptr, move (d), move (rb), fail_unknown);
+ }
+
+ static inline install_dirs
+ resolve (const target& t, dir_path d, bool fail_unknown = true)
{
- install_dirs r (resolve (s, nullptr, move (d), fail_unknown));
- return r.empty () ? dir_path () : move (r.back ().dir);
+ return resolve (t.base_scope (), &t, move (d), fail_unknown);
}
path
diff --git a/libbuild2/install/utility.hxx b/libbuild2/install/utility.hxx
index 7ab8114..2ba7b18 100644
--- a/libbuild2/install/utility.hxx
+++ b/libbuild2/install/utility.hxx
@@ -74,13 +74,22 @@ namespace build2
// and fail unless fail_unknown is false, in which case return empty
// directory.
//
+ // For rel_base semantics, see the $install.resolve() documentation. Note
+ // that fail_unknown does not apply to the rel_base resolution.
+ //
// Note: implemented in rule.cxx.
//
LIBBUILD2_SYMEXPORT dir_path
- resolve_dir (const target&, dir_path, bool fail_unknown = true);
+ resolve_dir (const target&,
+ dir_path,
+ dir_path rel_base = {},
+ bool fail_unknown = true);
LIBBUILD2_SYMEXPORT dir_path
- resolve_dir (const scope&, dir_path, bool fail_unknown = true);
+ resolve_dir (const scope&,
+ dir_path,
+ dir_path rel_base = {},
+ bool fail_unknown = true);
// Resolve file installation path returning empty path if not installable.
//