aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/install/operation.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-03-23 06:18:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-03-23 06:18:26 +0200
commit5a763a5552184090602f0a9303b9266f5412f020 (patch)
tree0cf1fb0189157c4d02976156007fe4ee6ad7f1d1 /libbuild2/install/operation.cxx
parentf62b8809d827a0474284e75ad5724cf201ed83b1 (diff)
Add support for relocatable installation manifest
Diffstat (limited to 'libbuild2/install/operation.cxx')
-rw-r--r--libbuild2/install/operation.cxx99
1 files changed, 80 insertions, 19 deletions
diff --git a/libbuild2/install/operation.cxx b/libbuild2/install/operation.cxx
index 32da60f..95b4381 100644
--- a/libbuild2/install/operation.cxx
+++ b/libbuild2/install/operation.cxx
@@ -10,6 +10,8 @@
#include <libbuild2/context.hxx>
#include <libbuild2/variable.hxx>
+#include <libbuild2/install/utility.hxx>
+
using namespace std;
using namespace butl;
@@ -20,13 +22,67 @@ namespace build2
#ifndef BUILD2_BOOTSTRAP
install_context_data::
install_context_data (const path* mf)
- : manifest_file (mf),
+ : manifest_name (mf),
manifest_os (mf != nullptr
- ? open_file_or_stdout (manifest_file, manifest_ofs)
+ ? open_file_or_stdout (manifest_name, manifest_ofs)
: manifest_ofs),
manifest_autorm (manifest_ofs.is_open () ? *mf : path ()),
manifest_json (manifest_os, 0 /* indentation */)
{
+ if (manifest_ofs.is_open ())
+ {
+ manifest_file = *mf;
+ manifest_file.complete ();
+ manifest_file.normalize ();
+ }
+ }
+
+ static path
+ relocatable_path (install_context_data& d, const target& t, path p)
+ {
+ // This is both inefficient (re-detecting relocatable manifest for every
+ // path) and a bit dirty (if multiple projects are being installed with
+ // different install.{relocatable,root} values, we may end up producing
+ // some paths relative and some absolute). But doing either of these
+ // properly is probably not worth the extra complexity.
+ //
+ if (!d.manifest_file.empty ()) // Not stdout.
+ {
+ const scope& rs (t.root_scope ());
+
+ if (cast_false<bool> (rs["install.relocatable"]))
+ {
+ // Note: install.root is abs_dir_path so absolute and normalized.
+ //
+ const dir_path* root (cast_null<dir_path> (rs["install.root"]));
+ if (root == nullptr)
+ fail << "unknown installation root directory in " << rs <<
+ info << "did you forget to specify config.install.root?";
+
+ // The manifest path would include chroot so if used, we need to add
+ // it to root and the file path (we could also strip it, but then
+ // making it absolute gets tricky on Windows).
+ //
+ dir_path md (d.manifest_file.directory ());
+
+ if (md.sub (chroot_path (rs, *root))) // Inside installation root.
+ {
+ p = chroot_path (rs, p);
+ try
+ {
+ p = p.relative (md);
+ }
+ catch (const invalid_path&)
+ {
+ fail << "unable to make filesystem entry path " << p
+ << " relative to " << md <<
+ info << "required for relocatable installation manifest";
+ }
+ }
+ }
+ }
+
+ return p;
}
// Serialize current target and, if tgt is not NULL, start the new target.
@@ -69,19 +125,21 @@ namespace build2
for (const auto& e: d.manifest_target_entries)
{
+ path p (relocatable_path (d, *d.manifest_target, move (e.path)));
+
s.begin_object ();
if (e.target.empty ())
{
s.member ("type", "file");
- s.member ("path", e.path);
+ s.member ("path", p.string ());
s.member ("mode", e.mode);
}
else
{
s.member ("type", "symlink");
- s.member ("path", e.path);
- s.member ("target", e.target);
+ s.member ("path", p.string ());
+ s.member ("target", e.target.string ());
}
s.end_object ();
@@ -92,11 +150,11 @@ namespace build2
}
catch (const json::invalid_json_output& e)
{
- fail << "invalid " << d.manifest_file << " json output: " << e;
+ fail << "invalid " << d.manifest_name << " json output: " << e;
}
catch (const io_error& e)
{
- fail << "unable to write to " << d.manifest_file << ": " << e;
+ fail << "unable to write to " << d.manifest_name << ": " << e;
}
d.manifest_target_entries.clear ();
@@ -114,7 +172,7 @@ namespace build2
auto& d (
*static_cast<install_context_data*> (ctx.current_inner_odata.get ()));
- if (d.manifest_file.path != nullptr)
+ if (d.manifest_name.path != nullptr)
{
try
{
@@ -127,17 +185,17 @@ namespace build2
s.begin_object ();
s.member ("type", "directory");
- s.member ("path", dir.string ());
+ s.member ("path", relocatable_path (d, tgt, dir).string ());
s.member ("mode", mode);
s.end_object ();
}
catch (const json::invalid_json_output& e)
{
- fail << "invalid " << d.manifest_file << " json output: " << e;
+ fail << "invalid " << d.manifest_name << " json output: " << e;
}
catch (const io_error& e)
{
- fail << "unable to write to " << d.manifest_file << ": " << e;
+ fail << "unable to write to " << d.manifest_name << ": " << e;
}
}
}
@@ -152,13 +210,13 @@ namespace build2
auto& d (
*static_cast<install_context_data*> (ctx.current_inner_odata.get ()));
- if (d.manifest_file.path != nullptr)
+ if (d.manifest_name.path != nullptr)
{
if (d.manifest_target != &tgt)
manifest_flush_target (d, &tgt);
d.manifest_target_entries.push_back (
- manifest_target_entry {(dir / name).string (), mode, ""});
+ manifest_target_entry {dir / name, mode, path ()});
}
}
@@ -172,14 +230,13 @@ namespace build2
auto& d (
*static_cast<install_context_data*> (ctx.current_inner_odata.get ()));
- if (d.manifest_file.path != nullptr)
+ if (d.manifest_name.path != nullptr)
{
if (d.manifest_target != &tgt)
manifest_flush_target (d, &tgt);
d.manifest_target_entries.push_back (
- manifest_target_entry {
- (dir / link).string (), "", link_target.string ()});
+ manifest_target_entry {dir / link, "", link_target});
}
}
@@ -189,7 +246,7 @@ namespace build2
auto& d (
*static_cast<install_context_data*> (ctx.current_inner_odata.get ()));
- if (d.manifest_file.path != nullptr)
+ if (d.manifest_name.path != nullptr)
{
try
{
@@ -205,11 +262,11 @@ namespace build2
}
catch (const json::invalid_json_output& e)
{
- fail << "invalid " << d.manifest_file << " json output: " << e;
+ fail << "invalid " << d.manifest_name << " json output: " << e;
}
catch (const io_error& e)
{
- fail << "unable to write to " << d.manifest_file << ": " << e;
+ fail << "unable to write to " << d.manifest_name << ": " << e;
}
}
}
@@ -291,6 +348,10 @@ namespace build2
const variable& var (*ctx.var_pool.find ("config.install.manifest"));
const path* mf (cast_null<path> (ctx.global_scope[var]));
+ // Note that we cannot calculate whether the manifest should use
+ // relocatable (relative) paths once here since we don't know the
+ // value of config.install.root.
+
ctx.current_inner_odata = context::current_data_ptr (
new install_context_data (mf),
[] (void* p) {delete static_cast<install_context_data*> (p);});