From f62b8809d827a0474284e75ad5724cf201ed83b1 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 22 Mar 2023 10:45:51 +0200 Subject: Initial work on relocatable install: config.install.relocatable and rpath --- libbuild2/bin/init.cxx | 3 ++ libbuild2/cc/link-rule.cxx | 68 ++++++++++++++++++++++++++++++++++++++++++++-- libbuild2/install/init.cxx | 25 +++++++++++++++-- 3 files changed, 92 insertions(+), 4 deletions(-) diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx index c5b58bb..1bdf6a6 100644 --- a/libbuild2/bin/init.cxx +++ b/libbuild2/bin/init.cxx @@ -79,6 +79,9 @@ namespace build2 // example, addition of rpaths for prerequisite libraries (see the cc // module for an example). Default is true. // + // Note also that a rule may need to make rpath relative if + // install.relocatable is true. + // vp.insert ("config.bin.rpath"); vp.insert ("config.bin.rpath.auto"); diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 99aa421..f860fdc 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -20,6 +20,8 @@ #include #include +#include + #include // c, pc* #include @@ -3264,10 +3266,72 @@ namespace build2 rpath_libraries (sargs, bs, a, t, li, for_install /* link */); lookup l; - if ((l = t["bin.rpath"]) && !l->empty ()) + { + // See if we need to make the specified paths relative using the + // $ORIGIN (Linux, BSD) or @loader_path (Mac OS) mechanisms. + // + optional origin; + if (for_install && cast_false (rs["install.relocatable"])) + { + // Note that both $ORIGIN and @loader_path will be expanded to + // the path of the binary that we are building (executable or + // shared library) as opposed to top-level executable. + // + path p (install::resolve_file (t)); + + // If the file is not installable then the install.relocatable + // semantics does not apply, naturally. + // + if (!p.empty ()) + origin = p.directory (); + } + + bool origin_used (false); for (const dir_path& p: cast (l)) - sargs.push_back ("-Wl,-rpath," + p.string ()); + { + string o ("-Wl,-rpath,"); + + // Note that we only rewrite absolute paths so if the user + // specified $ORIGIN or @loader_path manually, we will pass it + // through as is. + // + if (origin && p.absolute ()) + { + dir_path l; + try + { + l = p.relative (*origin); + } + catch (const invalid_path&) + { + fail << "unable to make rpath " << p << " relative to " + << *origin << + info << "required for relocatable installation"; + } + + o += (tclass == "macos" ? "@loader_path" : "$ORIGIN"); + + if (!l.empty ()) + { + o += path_traits::directory_separator; + o += l.string (); + } + + origin_used = true; + } + else + o += p.string (); + + sargs.push_back (move (o)); + } + + // According to the Internet, `-Wl,-z,origin` is not needed except + // potentially for older BSDs. + // + if (origin_used && tclass == "bsd") + sargs.push_back ("-Wl,-z,origin"); + } if ((l = t["bin.rpath_link"]) && !l->empty ()) { diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 92c2790..a3155d1 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -421,9 +421,9 @@ namespace build2 using config::lookup_config; using config::specified_config; - // Note: ignore config.install.scope (see below). + // Note: ignore config.install.{scope,manifest} (see below). // - bool s (specified_config (rs, "install", {"scope"})); + bool s (specified_config (rs, "install", {"scope", "manifest"})); // Adjust module priority so that the (numerous) config.install.* // values are saved at the end of config.build. @@ -582,6 +582,27 @@ namespace build2 } } + // Support for relocatable install. + // + // Note that it is false by default since supporting relocatable + // installation may require extra effort and not all projects may + // support it. A project that is known not to support it should assert + // this fact in its root.build, for example: + // + // assert (!$install.relocatable) 'relocatable installation not supported' + // + { + auto& var (vp.insert ( "install.relocatable")); + auto& cvar (vp.insert ("config.install.relocatable")); + + value& v (rs.assign (var)); + + // Note: unlike other variables, for ease of assertion set it to + // false if no config.install.* is specified. + // + v = s && cast_false (lookup_config (rs, cvar, false)); + } + // Global config.install.* values. // set_dir (s, p, rs, "", abs_dir_path (), false, "644", "755", cmd); -- cgit v1.1