diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index a4f1ca6e6..e902862a8 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -23,9 +23,11 @@ use std::fs; use std::fs::File; use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; +use std::process::Command; use std::result::Result; const DEFAULT_MODE: u32 = 0o755; +const DEFAULT_STRIP_PROGRAM: &str = "strip"; #[allow(dead_code)] pub struct Behavior { @@ -37,6 +39,8 @@ pub struct Behavior { verbose: bool, preserve_timestamps: bool, compare: bool, + strip: bool, + strip_program: String, } #[derive(Clone, Eq, PartialEq)] @@ -164,17 +168,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("apply access/modification times of SOURCE files to corresponding destination files") ) .arg( - // TODO implement flag Arg::with_name(OPT_STRIP) .short("s") .long(OPT_STRIP) - .help("(unimplemented) strip symbol tables") + .help("strip symbol tables (no action Windows)") ) .arg( - // TODO implement flag Arg::with_name(OPT_STRIP_PROGRAM) .long(OPT_STRIP_PROGRAM) - .help("(unimplemented) program used to strip binaries") + .help("program used to strip binaries (no action Windows)") .value_name("PROGRAM") ) .arg( @@ -266,10 +268,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> { Err("-b") } else if matches.is_present(OPT_CREATED) { Err("-D") - } else if matches.is_present(OPT_STRIP) { - Err("--strip, -s") - } else if matches.is_present(OPT_STRIP_PROGRAM) { - Err("--strip-program") } else if matches.is_present(OPT_SUFFIX) { Err("--suffix, -S") } else if matches.is_present(OPT_TARGET_DIRECTORY) { @@ -339,6 +337,12 @@ fn behavior(matches: &ArgMatches) -> Result { verbose: matches.is_present(OPT_VERBOSE), preserve_timestamps: matches.is_present(OPT_PRESERVE_TIMESTAMPS), compare: matches.is_present(OPT_COMPARE), + strip: matches.is_present(OPT_STRIP), + strip_program: String::from( + matches + .value_of(OPT_STRIP_PROGRAM) + .unwrap_or(DEFAULT_STRIP_PROGRAM), + ), }) } @@ -521,6 +525,21 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { return Err(()); } + if b.strip && cfg!(not(windows)) { + match Command::new(&b.strip_program).arg(to).output() { + Ok(o) => { + if !o.status.success() { + crash!( + 1, + "strip program failed: {}", + String::from_utf8(o.stderr).unwrap_or_default() + ); + } + } + Err(e) => crash!(1, "strip program execution failed: {}", e), + } + } + if mode::chmod(&to, b.mode()).is_err() { return Err(()); } diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 8ac6396fd..840b2f6c7 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -2,6 +2,8 @@ use crate::common::util::*; use filetime::FileTime; use rust_users::*; use std::os::unix::fs::PermissionsExt; +#[cfg(not(windows))] +use std::process::Command; #[cfg(target_os = "linux")] use std::thread::sleep; @@ -566,3 +568,97 @@ fn test_install_copy_then_compare_file_with_extra_mode() { assert!(after_install_sticky != after_install_sticky_again); } + +const STRIP_TARGET_FILE: &str = "helloworld_installed"; +const SYMBOL_DUMP_PROGRAM: &str = "objdump"; +const STRIP_SOURCE_FILE_SYMBOL: &str = "main"; + +fn strip_source_file() -> &'static str { + if cfg!(target_os = "macos") { + "helloworld_macos" + } else { + "helloworld_linux" + } +} + +#[test] +#[cfg(not(windows))] +fn test_install_and_strip() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + scene + .ucmd() + .arg("-s") + .arg(strip_source_file()) + .arg(STRIP_TARGET_FILE) + .succeeds() + .no_stderr(); + + let output = Command::new(SYMBOL_DUMP_PROGRAM) + .arg("-t") + .arg(at.plus(STRIP_TARGET_FILE)) + .output() + .unwrap(); + + let stdout = String::from_utf8(output.stdout).unwrap(); + assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL)); +} + +#[test] +#[cfg(not(windows))] +fn test_install_and_strip_with_program() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + scene + .ucmd() + .arg("-s") + .arg("--strip-program") + .arg("/usr/bin/strip") + .arg(strip_source_file()) + .arg(STRIP_TARGET_FILE) + .succeeds() + .no_stderr(); + + let output = Command::new(SYMBOL_DUMP_PROGRAM) + .arg("-t") + .arg(at.plus(STRIP_TARGET_FILE)) + .output() + .unwrap(); + + let stdout = String::from_utf8(output.stdout).unwrap(); + assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL)); +} + +#[test] +#[cfg(not(windows))] +fn test_install_and_strip_with_invalid_program() { + let scene = TestScenario::new(util_name!()); + + let stderr = scene + .ucmd() + .arg("-s") + .arg("--strip-program") + .arg("/bin/date") + .arg(strip_source_file()) + .arg(STRIP_TARGET_FILE) + .fails() + .stderr; + assert!(stderr.contains("strip program failed")); +} + +#[test] +#[cfg(not(windows))] +fn test_install_and_strip_with_non_existent_program() { + let scene = TestScenario::new(util_name!()); + + let stderr = scene + .ucmd() + .arg("-s") + .arg("--strip-program") + .arg("/usr/bin/non_existent_program") + .arg(strip_source_file()) + .arg(STRIP_TARGET_FILE) + .fails() + .stderr; + assert!(stderr.contains("No such file or directory")); +} diff --git a/tests/fixtures/install/helloworld.rs b/tests/fixtures/install/helloworld.rs new file mode 100644 index 000000000..47ad8c634 --- /dev/null +++ b/tests/fixtures/install/helloworld.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello World!"); +} diff --git a/tests/fixtures/install/helloworld_linux b/tests/fixtures/install/helloworld_linux new file mode 100755 index 000000000..c1c6b9b37 Binary files /dev/null and b/tests/fixtures/install/helloworld_linux differ diff --git a/tests/fixtures/install/helloworld_macos b/tests/fixtures/install/helloworld_macos new file mode 100755 index 000000000..40e6ee515 Binary files /dev/null and b/tests/fixtures/install/helloworld_macos differ