Skip to content

Commit 44ef402

Browse files
committed
Patch manylinux R to use /usr/bin/tar
Instead of the hardcoded /usr/bin/gtar.
1 parent 0c81b98 commit 44ef402

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

src/linux.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,10 @@ fn safe_user_install(
623623
std::fs::rename(&content, &dest)?;
624624

625625
patch_r_home_dir(&dest)?;
626+
if let Err(e) = patch_renviron_tar(&dest) {
627+
OUTPUT.warn(&format!("Could not set TAR in Renviron: {}", e));
628+
warn!("Could not set TAR in Renviron: {}", e);
629+
}
626630
if let Err(e) = shim_bundled_xcursor(&dest) {
627631
OUTPUT.warn(&format!("Could not shim libXcursor: {}", e));
628632
warn!("Could not shim libXcursor: {}", e);
@@ -682,6 +686,59 @@ fn patch_r_home_dir_script(content: &str, home: &str) -> Option<String> {
682686
Some(out)
683687
}
684688

689+
// The portable builds are configured on a machine where GNU tar is installed as
690+
// `gtar`, so their `etc/Renviron` has `TAR=${TAR-'/usr/bin/gtar'}`, a path that
691+
// does not exist on most Linux systems. Point `TAR` at `/usr/bin/tar` instead,
692+
// which is GNU tar on Linux.
693+
fn patch_renviron_tar(dest: &Path) -> Result<(), Box<dyn Error>> {
694+
let renviron = dest.join("lib").join("R").join("etc").join("Renviron");
695+
if !renviron.exists() {
696+
debug!("No {}, not patching TAR", renviron.display());
697+
return Ok(());
698+
}
699+
let tar = preferred_tar();
700+
let content = std::fs::read_to_string(&renviron)?;
701+
match patch_renviron_tar_content(&content, tar) {
702+
Some(patched) => {
703+
debug!("Patching TAR in {} to {}", renviron.display(), tar);
704+
// Writing to the existing file keeps its permissions.
705+
std::fs::write(&renviron, patched)?;
706+
}
707+
None => {
708+
debug!("No TAR default to patch in {}", renviron.display());
709+
}
710+
}
711+
Ok(())
712+
}
713+
714+
// The tar `Renviron` should default to: `/usr/bin/tar`, unless it is missing and
715+
// the `/usr/bin/gtar` the build was configured with is actually there.
716+
fn preferred_tar() -> &'static str {
717+
if !Path::new("/usr/bin/tar").exists() && Path::new("/usr/bin/gtar").exists() {
718+
"/usr/bin/gtar"
719+
} else {
720+
"/usr/bin/tar"
721+
}
722+
}
723+
724+
// Set the `TAR` default in the contents of `etc/Renviron` to `tar`, keeping the
725+
// `${TAR-...}` fallback form, so a `TAR` from the environment still wins.
726+
// Returns `None` if there is no `TAR=` line, or it already has this default.
727+
fn patch_renviron_tar_content(content: &str, tar: &str) -> Option<String> {
728+
let want = format!("TAR=${{TAR-'{}'}}", tar);
729+
let mut lines: Vec<String> = content.lines().map(|l| l.to_string()).collect();
730+
let idx = lines.iter().position(|l| l.starts_with("TAR="))?;
731+
if lines[idx] == want {
732+
return None;
733+
}
734+
lines[idx] = want;
735+
let mut out = lines.join("\n");
736+
if content.ends_with('\n') {
737+
out.push('\n');
738+
}
739+
Some(out)
740+
}
741+
685742
// The name the bundled libX11 passes to `dlopen()`, and the name of the shim we
686743
// put next to it. See `shim_bundled_xcursor()`.
687744
const XCURSOR_SONAME: &str = "libXcursor.so.1";
@@ -2507,6 +2564,39 @@ Usage: /lib/ld-musl-x86_64.so.1 [options] [--] pathname\n";
25072564
assert!(patch_r_home_dir_script("echo \"${R_HOME_DIR}\"\n", "/home/u/r/lib/R").is_none());
25082565
}
25092566

2567+
// The relevant part of `etc/Renviron` of a portable build.
2568+
const RENVIRON: &str = "R_GZIPCMD=${R_GZIPCMD-'/usr/bin/gzip'}\n\
2569+
TAR=${TAR-'/usr/bin/gtar'}\n\
2570+
R_UNZIPCMD=${R_UNZIPCMD-'/usr/bin/unzip'}\n";
2571+
2572+
#[test]
2573+
fn patch_renviron_tar_content_replaces_gtar_default() {
2574+
let out = patch_renviron_tar_content(RENVIRON, "/usr/bin/tar")
2575+
.expect("Renviron has a TAR default");
2576+
assert_eq!(
2577+
out,
2578+
"R_GZIPCMD=${R_GZIPCMD-'/usr/bin/gzip'}\n\
2579+
TAR=${TAR-'/usr/bin/tar'}\n\
2580+
R_UNZIPCMD=${R_UNZIPCMD-'/usr/bin/unzip'}\n"
2581+
);
2582+
}
2583+
2584+
#[test]
2585+
fn patch_renviron_tar_content_leaves_matching_default() {
2586+
// Already the wanted default, or `gtar` on a system that only has that.
2587+
let tar = RENVIRON.replace("gtar", "tar");
2588+
assert!(patch_renviron_tar_content(&tar, "/usr/bin/tar").is_none());
2589+
assert!(patch_renviron_tar_content(RENVIRON, "/usr/bin/gtar").is_none());
2590+
}
2591+
2592+
#[test]
2593+
fn patch_renviron_tar_content_without_tar_line() {
2594+
assert!(patch_renviron_tar_content("R_GZIPCMD=/usr/bin/gzip\n", "/usr/bin/tar").is_none());
2595+
// `R_TAR` and a reference to `${TAR}` are not `TAR` assignments.
2596+
assert!(patch_renviron_tar_content("R_TAR=/usr/bin/gtar\n", "/usr/bin/tar").is_none());
2597+
assert!(patch_renviron_tar_content("echo ${TAR}\n", "/usr/bin/tar").is_none());
2598+
}
2599+
25102600
#[test]
25112601
fn link_points_into_admin_root_matches_opt_r() {
25122602
assert!(link_points_into_admin_root(Path::new("/opt/R/4.6.0/bin/R")));

0 commit comments

Comments
 (0)