-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bootstrap.py: patch RPATH on NixOS to handle the new zlib dependency. #74441
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -349,6 +349,7 @@ def __init__(self): | |
self.use_vendored_sources = '' | ||
self.verbose = False | ||
self.git_version = None | ||
self.nix_deps_dir = None | ||
|
||
def download_stage0(self): | ||
"""Fetch the build system for Rust, written in Rust | ||
|
@@ -388,8 +389,12 @@ def support_xz(): | |
filename = "rustc-{}-{}{}".format(rustc_channel, self.build, | ||
tarball_suffix) | ||
self._download_stage0_helper(filename, "rustc", tarball_suffix) | ||
self.fix_executable("{}/bin/rustc".format(self.bin_root())) | ||
self.fix_executable("{}/bin/rustdoc".format(self.bin_root())) | ||
self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root())) | ||
self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root())) | ||
lib_dir = "{}/lib".format(self.bin_root()) | ||
for lib in os.listdir(lib_dir): | ||
if lib.endswith(".so"): | ||
self.fix_bin_or_dylib("{}/{}".format(lib_dir, lib)) | ||
with output(self.rustc_stamp()) as rust_stamp: | ||
rust_stamp.write(self.date) | ||
|
||
|
@@ -408,7 +413,7 @@ def support_xz(): | |
filename = "cargo-{}-{}{}".format(cargo_channel, self.build, | ||
tarball_suffix) | ||
self._download_stage0_helper(filename, "cargo", tarball_suffix) | ||
self.fix_executable("{}/bin/cargo".format(self.bin_root())) | ||
self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) | ||
with output(self.cargo_stamp()) as cargo_stamp: | ||
cargo_stamp.write(self.date) | ||
|
||
|
@@ -421,8 +426,8 @@ def support_xz(): | |
[channel, date] = rustfmt_channel.split('-', 1) | ||
filename = "rustfmt-{}-{}{}".format(channel, self.build, tarball_suffix) | ||
self._download_stage0_helper(filename, "rustfmt-preview", tarball_suffix, date) | ||
self.fix_executable("{}/bin/rustfmt".format(self.bin_root())) | ||
self.fix_executable("{}/bin/cargo-fmt".format(self.bin_root())) | ||
self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) | ||
self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) | ||
with output(self.rustfmt_stamp()) as rustfmt_stamp: | ||
rustfmt_stamp.write(self.date + self.rustfmt_channel) | ||
|
||
|
@@ -440,12 +445,12 @@ def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): | |
get("{}/{}".format(url, filename), tarball, verbose=self.verbose) | ||
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) | ||
|
||
@staticmethod | ||
def fix_executable(fname): | ||
"""Modifies the interpreter section of 'fname' to fix the dynamic linker | ||
def fix_bin_or_dylib(self, fname): | ||
"""Modifies the interpreter section of 'fname' to fix the dynamic linker, | ||
or the RPATH section, to fix the dynamic library search path | ||
|
||
This method is only required on NixOS and uses the PatchELF utility to | ||
change the dynamic linker of ELF executables. | ||
change the interpreter/RPATH of ELF executables. | ||
|
||
Please see https://nixos.org/patchelf.html for more information | ||
""" | ||
|
@@ -472,38 +477,61 @@ def fix_executable(fname): | |
nix_os_msg = "info: you seem to be running NixOS. Attempting to patch" | ||
print(nix_os_msg, fname) | ||
|
||
try: | ||
interpreter = subprocess.check_output( | ||
["patchelf", "--print-interpreter", fname]) | ||
interpreter = interpreter.strip().decode(default_encoding) | ||
except subprocess.CalledProcessError as reason: | ||
print("warning: failed to call patchelf:", reason) | ||
return | ||
|
||
loader = interpreter.split("/")[-1] | ||
|
||
try: | ||
ldd_output = subprocess.check_output( | ||
['ldd', '/run/current-system/sw/bin/sh']) | ||
ldd_output = ldd_output.strip().decode(default_encoding) | ||
except subprocess.CalledProcessError as reason: | ||
print("warning: unable to call ldd:", reason) | ||
return | ||
|
||
for line in ldd_output.splitlines(): | ||
libname = line.split()[0] | ||
if libname.endswith(loader): | ||
loader_path = libname[:len(libname) - len(loader)] | ||
break | ||
# Only build `stage0/.nix-deps` once. | ||
nix_deps_dir = self.nix_deps_dir | ||
if not nix_deps_dir: | ||
nix_deps_dir = "{}/.nix-deps".format(self.bin_root()) | ||
if not os.path.exists(nix_deps_dir): | ||
os.makedirs(nix_deps_dir) | ||
|
||
nix_deps = [ | ||
# Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). | ||
"stdenv.cc.bintools", | ||
|
||
# Needed as a system dependency of `libLLVM-*.so`. | ||
"zlib", | ||
|
||
# Needed for patching ELF binaries (see doc comment above). | ||
"patchelf", | ||
] | ||
|
||
# Run `nix-build` to "build" each dependency (which will likely reuse | ||
# the existing `/nix/store` copy, or at most download a pre-built copy). | ||
# Importantly, we don't rely on `nix-build` printing the `/nix/store` | ||
# path on stdout, but use `-o` to symlink it into `stage0/.nix-deps/$dep`, | ||
# ensuring garbage collection will never remove the `/nix/store` path | ||
# (which would break our patched binaries that hardcode those paths). | ||
for dep in nix_deps: | ||
try: | ||
subprocess.check_output([ | ||
"nix-build", "<nixpkgs>", | ||
"-A", dep, | ||
"-o", "{}/{}".format(nix_deps_dir, dep), | ||
]) | ||
except subprocess.CalledProcessError as reason: | ||
print("warning: failed to call nix-build:", reason) | ||
return | ||
|
||
self.nix_deps_dir = nix_deps_dir | ||
|
||
patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) | ||
|
||
if fname.endswith(".so"): | ||
# Dynamic library, patch RPATH to point to system dependencies. | ||
dylib_deps = ["zlib"] | ||
rpath_entries = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm… shouldn't we just append the rpath? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would require a command to read it, which... I can do it if you want, it's just uglier (and I've gotten used to hardcoding the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, it is just not clear to me that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the record, NixOS |
||
# Relative default, all binary and dynamic libraries we ship | ||
# appear to have this (even when `../lib` is redundant). | ||
"$ORIGIN/../lib", | ||
] + ["{}/{}/lib".format(nix_deps_dir, dep) for dep in dylib_deps] | ||
patchelf_args = ["--set-rpath", ":".join(rpath_entries)] | ||
else: | ||
print("warning: unable to find the path to the dynamic linker") | ||
return | ||
|
||
correct_interpreter = loader_path + loader | ||
bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) | ||
with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: | ||
patchelf_args = ["--set-interpreter", dynamic_linker.read().rstrip()] | ||
|
||
try: | ||
subprocess.check_output( | ||
["patchelf", "--set-interpreter", correct_interpreter, fname]) | ||
subprocess.check_output([patchelf] + patchelf_args + [fname]) | ||
except subprocess.CalledProcessError as reason: | ||
print("warning: failed to call patchelf:", reason) | ||
return | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to try and build all of the deps with a single command?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how to do that and keep the symlinks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The symlinks won’t be named as well as they are right now, but: