diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 0cc2ba50b..967e77b60 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -844,6 +844,61 @@ jobs: n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines) if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi + test_android: + name: Test Android builds + needs: [ min_version, deps ] + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + api-level: [28] + target: [default] + arch: [x86] # , arm64-v8a + env: + TERMUX: v0.118.0 + steps: + - uses: actions/checkout@v2 + - name: AVD cache + uses: actions/cache@v2 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/avd/*/snapshots/* + ~/.android/adb* + key: avd-${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }} + - name: Create and cache emulator image + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + ram-size: 2048M + disk-size: 5120M + force-avd-creation: true + emulator-options: -no-snapshot-load -noaudio -no-boot-anim -camera-back none + script: | + wget https://github.com/termux/termux-app/releases/download/${{ env.TERMUX }}/termux-app_${{ env.TERMUX }}+github-debug_${{ matrix.arch }}.apk + util/android-commands.sh snapshot termux-app_${{ env.TERMUX }}+github-debug_${{ matrix.arch }}.apk + adb -s emulator-5554 emu avd snapshot save ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }} + echo "Emulator image created." + pkill -9 qemu-system-x86_64 + - name: Build and Test on Android + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + ram-size: 2048M + disk-size: 5120M + force-avd-creation: false + emulator-options: -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -snapshot ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }} + script: | + util/android-commands.sh sync + util/android-commands.sh build + util/android-commands.sh tests + test_freebsd: name: Tests/FreeBSD test suite needs: [ min_version, deps ] diff --git a/Cargo.lock b/Cargo.lock index 09e0ca635..cf609cab4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,9 +791,9 @@ checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5" [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ "cfg-if 1.0.0", "libc", diff --git a/Cargo.toml b/Cargo.toml index 41532cd45..17f686008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ # coreutils (uutils) # * see the repository LICENSE, README, and CONTRIBUTING files for more information -# spell-checker:ignore (libs) libselinux +# spell-checker:ignore (libs) libselinux gethostid [package] name = "coreutils" @@ -120,6 +120,7 @@ feat_Tier1 = [ # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform feat_os_macos = [ "feat_os_unix", ## == a modern/usual *nix platform + "feat_require_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms feat_os_unix = [ @@ -140,12 +141,19 @@ feat_os_unix_gnueabihf = [ # "feat_require_unix", "feat_require_unix_utmpx", + "feat_require_hostid", ] # "feat_os_unix_musl" == set of utilities which can be built/run on targets binding to the "musl" library (ref: ) feat_os_unix_musl = [ "feat_Tier1", # "feat_require_unix", + "feat_require_hostid", +] +feat_os_unix_android = [ + "feat_Tier1", + # + "feat_require_unix", ] # "feat_selinux" == set of utilities providing support for SELinux Security Context if enabled with `--features feat_selinux`. # NOTE: @@ -172,7 +180,6 @@ feat_require_unix = [ "chown", "chroot", "groups", - "hostid", "id", "install", "kill", @@ -195,6 +202,10 @@ feat_require_unix_utmpx = [ "users", "who", ] +# "feat_require_hostid" == set of utilities requiring gethostid in libc (only some unixes provide) +feat_require_hostid = [ + "hostid", +] # "feat_require_selinux" == set of utilities depending on SELinux. feat_require_selinux = [ "chcon", @@ -386,7 +397,7 @@ walkdir = "2.2" atty = "0.2" hex-literal = "0.3.1" -[target.'cfg(target_os = "linux")'.dev-dependencies] +[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] rlimit = "0.8.3" [target.'cfg(unix)'.dev-dependencies] diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index 713336104..e54cc3f8f 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -208,7 +208,7 @@ fn set_groups(groups: &[libc::gid_t]) -> libc::c_int { unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn set_groups(groups: &[libc::gid_t]) -> libc::c_int { unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) } } diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index b60006df1..1aab9c37b 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -27,7 +27,7 @@ selinux = { version="0.2", optional=true } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs", "perms", "mode"] } walkdir = "2.2" -[target.'cfg(target_os = "linux")'.dependencies] +[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] ioctl-sys = "0.8" [target.'cfg(target_os = "windows")'.dependencies] diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index d69bb705b..df9cb0293 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -10,7 +10,7 @@ // spell-checker:ignore (ToDO) ficlone linkgs lstat nlink nlinks pathbuf reflink strs xattrs symlinked -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[macro_use] extern crate ioctl_sys; #[macro_use] @@ -49,7 +49,7 @@ use std::mem; use std::os::unix::ffi::OsStrExt; #[cfg(unix)] use std::os::unix::fs::{FileTypeExt, PermissionsExt}; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::os::unix::io::AsRawFd; #[cfg(windows)] use std::os::windows::ffi::OsStrExt; @@ -61,7 +61,7 @@ use uucore::error::{set_exit_code, ExitCode, UError, UResult}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use walkdir::WalkDir; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); quick_error! { @@ -686,11 +686,15 @@ impl Options { } } } else { - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] { ReflinkMode::Auto } - #[cfg(not(any(target_os = "linux", target_os = "macos")))] + #[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "macos" + )))] { ReflinkMode::Never } @@ -1467,14 +1471,14 @@ fn copy_helper( } else if source_is_symlink { copy_link(source, dest, symlinked_files)?; } else if options.reflink_mode != ReflinkMode::Never { - #[cfg(not(any(target_os = "linux", target_os = "macos")))] + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))] return Err("--reflink is only supported on linux and macOS" .to_string() .into()); #[cfg(target_os = "macos")] copy_on_write_macos(source, dest, options.reflink_mode, context)?; - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] copy_on_write_linux(source, dest, options.reflink_mode, context)?; } else { fs::copy(source, dest).context(context)?; @@ -1528,7 +1532,7 @@ fn copy_link( } /// Copies `source` to `dest` using copy-on-write if possible. -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn copy_on_write_linux( source: &Path, dest: &Path, diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index d311c9733..c805685ad 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -21,7 +21,7 @@ gcd = "2.0" libc = "0.2" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } -[target.'cfg(target_os = "linux")'.dependencies] +[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] signal-hook = "0.3.9" [[bin]] diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 3b9bb02d8..3f1a54b1c 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -26,7 +26,7 @@ use std::cmp; use std::env; use std::fs::{File, OpenOptions}; use std::io::{self, Read, Seek, Write}; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::os::unix::fs::OpenOptionsExt; use std::path::Path; use std::sync::mpsc; @@ -88,7 +88,7 @@ impl Input { } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn make_linux_iflags(iflags: &IFlags) -> Option { let mut flag = 0; @@ -140,7 +140,7 @@ impl Input { let mut opts = OpenOptions::new(); opts.read(true); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] if let Some(libc_flags) = make_linux_iflags(&iflags) { opts.custom_flags(libc_flags); } @@ -455,7 +455,7 @@ where } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn make_linux_oflags(oflags: &OFlags) -> Option { let mut flag = 0; @@ -504,7 +504,7 @@ impl OutputTrait for Output { .create_new(cflags.excl) .append(oflags.append); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] if let Some(libc_flags) = make_linux_oflags(oflags) { opts.custom_flags(libc_flags); } diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 4bc65bc1c..0a2fae99a 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -235,7 +235,7 @@ impl std::str::FromStr for Flag { "direct" => // Ok(Self::Direct), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::Direct) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -244,7 +244,7 @@ impl std::str::FromStr for Flag { "directory" => // Ok(Self::Directory), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::Directory) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -253,7 +253,7 @@ impl std::str::FromStr for Flag { "dsync" => // Ok(Self::Dsync), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::Dsync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -262,7 +262,7 @@ impl std::str::FromStr for Flag { "sync" => // Ok(Self::Sync), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::Sync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -276,7 +276,7 @@ impl std::str::FromStr for Flag { "nonblock" => // Ok(Self::NonBlock), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::NonBlock) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -285,7 +285,7 @@ impl std::str::FromStr for Flag { "noatime" => // Ok(Self::NoATime), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::NoATime) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -294,7 +294,7 @@ impl std::str::FromStr for Flag { "noctty" => // Ok(Self::NoCtty), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::NoCtty) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -303,7 +303,7 @@ impl std::str::FromStr for Flag { "nofollow" => // Ok(Self::NoFollow), { - if cfg!(target_os = "linux") { + if cfg!(any(target_os = "linux", target_os = "android")) { Ok(Self::NoFollow) } else { Err(ParseError::Unimplemented(s.to_string())) diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 95e783c58..9d8349873 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -4,7 +4,7 @@ use super::*; use crate::StatusLevel; -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] #[test] fn unimplemented_flags_should_error_non_linux() { let mut succeeded = Vec::new(); @@ -617,7 +617,7 @@ fn parse_oflag_tokens() { } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn parse_iflag_tokens_linux() { let exp = vec![ @@ -645,7 +645,7 @@ fn parse_iflag_tokens_linux() { } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn parse_oflag_tokens_linux() { let exp = vec![ diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 3bb9ce8c9..cc23ce19f 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -201,7 +201,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if state.cflag { if state.selinux_supported { // print SElinux context and exit - #[cfg(all(target_os = "linux", feature = "selinux"))] + #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "selinux"))] if let Ok(context) = selinux::SecurityContext::current(false) { let bytes = context.as_bytes(); print!("{}{}", String::from_utf8_lossy(bytes), line_ending); @@ -508,33 +508,39 @@ fn pline(possible_uid: Option) { println!( "{}:{}:{}:{}:{}:{}:{}:{}:{}:{}", pw.name, - pw.user_passwd, + pw.user_passwd.unwrap_or_default(), pw.uid, pw.gid, - pw.user_access_class, + pw.user_access_class.unwrap_or_default(), pw.passwd_change_time, pw.expiration, - pw.user_info, - pw.user_dir, - pw.user_shell + pw.user_info.unwrap_or_default(), + pw.user_dir.unwrap_or_default(), + pw.user_shell.unwrap_or_default() ); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn pline(possible_uid: Option) { let uid = possible_uid.unwrap_or_else(getuid); let pw = Passwd::locate(uid).unwrap(); println!( "{}:{}:{}:{}:{}:{}:{}", - pw.name, pw.user_passwd, pw.uid, pw.gid, pw.user_info, pw.user_dir, pw.user_shell + pw.name, + pw.user_passwd.unwrap_or_default(), + pw.uid, + pw.gid, + pw.user_info.unwrap_or_default(), + pw.user_dir.unwrap_or_default(), + pw.user_shell.unwrap_or_default() ); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn auditid() {} -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] fn auditid() { #[allow(deprecated)] let mut auditinfo: audit::c_auditinfo_addr_t = unsafe { std::mem::uninitialized() }; @@ -614,7 +620,7 @@ fn id_print(state: &mut State, groups: &[u32]) { .join(",") ); - #[cfg(all(target_os = "linux", feature = "selinux"))] + #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "selinux"))] if state.selinux_supported && !state.user_specified && std::env::var_os("POSIXLY_CORRECT").is_none() @@ -627,7 +633,7 @@ fn id_print(state: &mut State, groups: &[u32]) { } } -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] mod audit { use super::libc::{c_int, c_uint, dev_t, pid_t, uid_t}; diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index a55f29e23..17eec91ec 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2370,7 +2370,7 @@ fn display_len_or_rdev(metadata: &Metadata, config: &Config) -> SizeOrDeviceId { return SizeOrDeviceId::Device(major.to_string(), minor.to_string()); } } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let ft = metadata.file_type(); if ft.is_char_device() || ft.is_block_device() { diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index cfafb6b5b..0d67ad466 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -213,7 +213,7 @@ extern "C" { fn _vprocmgr_detach_from_console(flags: u32) -> *const libc::c_int; } -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] unsafe fn _vprocmgr_detach_from_console(_: u32) -> *const libc::c_int { std::ptr::null() } diff --git a/src/uu/nproc/src/nproc.rs b/src/uu/nproc/src/nproc.rs index 87fe9a4e7..cbabde292 100644 --- a/src/uu/nproc/src/nproc.rs +++ b/src/uu/nproc/src/nproc.rs @@ -13,7 +13,7 @@ use uucore::display::Quotable; use uucore::error::{UResult, USimpleError}; use uucore::format_usage; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] pub const _SC_NPROCESSORS_CONF: libc::c_int = 83; #[cfg(target_vendor = "apple")] pub const _SC_NPROCESSORS_CONF: libc::c_int = libc::_SC_NPROCESSORS_CONF; diff --git a/src/uu/pinky/src/pinky.rs b/src/uu/pinky/src/pinky.rs index 437c20cf5..5029caa24 100644 --- a/src/uu/pinky/src/pinky.rs +++ b/src/uu/pinky/src/pinky.rs @@ -245,12 +245,16 @@ fn time_string(ut: &Utmpx) -> String { time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C } -fn gecos_to_fullname(pw: &Passwd) -> String { - let mut gecos = pw.user_info.clone(); +fn gecos_to_fullname(pw: &Passwd) -> Option { + let mut gecos = if let Some(gecos) = &pw.user_info { + gecos.clone() + } else { + return None; + }; if let Some(n) = gecos.find(',') { gecos.truncate(n); } - gecos.replace('&', &pw.name.capitalize()) + Some(gecos.replace('&', &pw.name.capitalize())) } impl Pinky { @@ -278,8 +282,13 @@ impl Pinky { print!("{1:<8.0$}", utmpx::UT_NAMESIZE, ut.user()); if self.include_fullname { - if let Ok(pw) = Passwd::locate(ut.user().as_ref()) { - print!(" {:<19.19}", gecos_to_fullname(&pw)); + let fullname = if let Ok(pw) = Passwd::locate(ut.user().as_ref()) { + gecos_to_fullname(&pw) + } else { + None + }; + if let Some(fullname) = fullname { + print!(" {:<19.19}", fullname); } else { print!(" {:19}", " ???"); } @@ -341,13 +350,16 @@ impl Pinky { for u in &self.names { print!("Login name: {:<28}In real life: ", u); if let Ok(pw) = Passwd::locate(u.as_str()) { - println!(" {}", gecos_to_fullname(&pw)); + let fullname = gecos_to_fullname(&pw).unwrap_or_default(); + let user_dir = pw.user_dir.unwrap_or_default(); + let user_shell = pw.user_shell.unwrap_or_default(); + println!(" {}", fullname); if self.include_home_and_shell { - print!("Directory: {:<29}", pw.user_dir); - println!("Shell: {}", pw.user_shell); + print!("Directory: {:<29}", user_dir); + println!("Shell: {}", user_shell); } if self.include_project { - let mut p = PathBuf::from(&pw.user_dir); + let mut p = PathBuf::from(&user_dir); p.push(".project"); if let Ok(f) = File::open(p) { print!("Project: "); @@ -355,7 +367,7 @@ impl Pinky { } } if self.include_plan { - let mut p = PathBuf::from(&pw.user_dir); + let mut p = PathBuf::from(&user_dir); p.push(".plan"); if let Ok(f) = File::open(p) { println!("Plan:"); diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index 5e0e71789..816c86717 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -78,6 +78,7 @@ struct ProgramOptionsError(String); #[cfg(any( target_os = "linux", + target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "dragonflybsd" @@ -93,6 +94,7 @@ fn preload_strings() -> (&'static str, &'static str) { #[cfg(not(any( target_os = "linux", + target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "dragonflybsd", diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index f9c18d500..9baf9b182 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -27,17 +27,21 @@ static ARG_FILES: &str = "files"; #[cfg(unix)] mod platform { use super::libc; - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] use std::fs::File; - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] use std::os::unix::io::AsRawFd; pub unsafe fn do_sync() -> isize { + // see https://github.com/rust-lang/libc/pull/2161 + #[cfg(target_os = "android")] + libc::syscall(libc::SYS_sync); + #[cfg(not(target_os = "android"))] libc::sync(); 0 } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub unsafe fn do_syncfs(files: Vec) -> isize { for path in files { let f = File::open(&path).unwrap(); @@ -47,7 +51,7 @@ mod platform { 0 } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub unsafe fn do_fdatasync(files: Vec) -> isize { for path in files { let f = File::open(&path).unwrap(); @@ -179,10 +183,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[allow(clippy::if_same_then_else)] if matches.is_present(options::FILE_SYSTEM) { - #[cfg(any(target_os = "linux", target_os = "windows"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))] syncfs(files); } else if matches.is_present(options::DATA) { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] fdatasync(files); } else { sync(); @@ -221,12 +225,12 @@ fn sync() -> isize { unsafe { platform::do_sync() } } -#[cfg(any(target_os = "linux", target_os = "windows"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))] fn syncfs(files: Vec) -> isize { unsafe { platform::do_syncfs(files) } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn fdatasync(files: Vec) -> isize { unsafe { platform::do_fdatasync(files) } } diff --git a/src/uu/tail/src/platform/unix.rs b/src/uu/tail/src/platform/unix.rs index e01d5e444..7ddf6edd0 100644 --- a/src/uu/tail/src/platform/unix.rs +++ b/src/uu/tail/src/platform/unix.rs @@ -54,7 +54,7 @@ pub fn stdin_is_pipe_or_fifo() -> bool { fd >= 0 // GNU tail checks fd >= 0 && match fstat(fd) { Ok(stat) => { - let mode = stat.st_mode; + let mode = stat.st_mode as libc::mode_t; // NOTE: This is probably not the most correct way to check this (mode & S_IFIFO != 0) || (mode & S_IFSOCK != 0) } diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 864917574..ff08a1b59 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -320,10 +320,14 @@ fn parse_timestamp(s: &str) -> UResult { /// On Windows, uses GetFinalPathNameByHandleW to attempt to get the path /// from the stdout handle. fn pathbuf_from_stdout() -> UResult { - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "android")))] { Ok(PathBuf::from("/dev/stdout")) } + #[cfg(target_os = "android")] + { + Ok(PathBuf::from("/proc/self/fd/1")) + } #[cfg(windows)] { use std::os::windows::prelude::AsRawHandle; diff --git a/src/uu/uname/src/uname.rs b/src/uu/uname/src/uname.rs index bff033047..f7b578c27 100644 --- a/src/uu/uname/src/uname.rs +++ b/src/uu/uname/src/uname.rs @@ -36,6 +36,8 @@ pub mod options { const HOST_OS: &str = "GNU/Linux"; #[cfg(all(target_os = "linux", not(any(target_env = "gnu", target_env = ""))))] const HOST_OS: &str = "Linux"; +#[cfg(target_os = "android")] +const HOST_OS: &str = "Android"; #[cfg(target_os = "windows")] const HOST_OS: &str = "Windows NT"; #[cfg(target_os = "freebsd")] diff --git a/src/uu/wc/src/count_fast.rs b/src/uu/wc/src/count_fast.rs index 4515cd3d7..555225a06 100644 --- a/src/uu/wc/src/count_fast.rs +++ b/src/uu/wc/src/count_fast.rs @@ -36,8 +36,8 @@ fn count_bytes_using_splice(fd: &impl AsRawFd) -> Result { .map_err(|_| 0_usize)?; let null_rdev = stat::fstat(null_file.as_raw_fd()) .map_err(|_| 0_usize)? - .st_rdev; - if (stat::major(null_rdev), stat::minor(null_rdev)) != (1, 3) { + .st_rdev as libc::dev_t; + if unsafe { (libc::major(null_rdev), libc::minor(null_rdev)) } != (1, 3) { // This is not a proper /dev/null, writing to it is probably bad // Bit of an edge case, but it has been known to happen return Err(0); @@ -86,14 +86,14 @@ pub(crate) fn count_bytes_fast(handle: &mut T) -> (usize, Opti // The second case happens for files in pseudo-filesystems. For // example with /proc/version and /sys/kernel/profiling. So, // if it is 0 we don't report that and instead do a full read. - if (stat.st_mode & S_IFREG) != 0 && stat.st_size > 0 { + if (stat.st_mode as libc::mode_t & S_IFREG) != 0 && stat.st_size > 0 { return (stat.st_size as usize, None); } #[cfg(any(target_os = "linux", target_os = "android"))] { // Else, if we're on Linux and our file is a FIFO pipe // (or stdin), we use splice to count the number of bytes. - if (stat.st_mode & S_IFIFO) != 0 { + if (stat.st_mode as libc::mode_t & S_IFIFO) != 0 { match count_bytes_using_splice(handle) { Ok(n) => return (n, None), Err(n) => byte_count = n, diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 98ef06f47..6e21ac912 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -40,7 +40,7 @@ mod options { static ABOUT: &str = "Print information about users who are currently logged in."; const USAGE: &str = "{} [OPTION]... [ FILE | ARG1 ARG2 ]"; -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] static RUNLEVEL_HELP: &str = "print current runlevel"; #[cfg(not(target_os = "linux"))] static RUNLEVEL_HELP: &str = "print current runlevel (This is meaningless on non Linux)"; diff --git a/src/uu/yes/src/splice.rs b/src/uu/yes/src/splice.rs index f77a09ed6..a0d41e06f 100644 --- a/src/uu/yes/src/splice.rs +++ b/src/uu/yes/src/splice.rs @@ -23,7 +23,7 @@ use nix::{errno::Errno, libc::S_IFIFO, sys::stat::fstat}; use uucore::pipes::{pipe, splice_exact, vmsplice}; pub(crate) fn splice_data(bytes: &[u8], out: &impl AsRawFd) -> Result<()> { - let is_pipe = fstat(out.as_raw_fd())?.st_mode & S_IFIFO != 0; + let is_pipe = fstat(out.as_raw_fd())?.st_mode as nix::libc::mode_t & S_IFIFO != 0; if is_pipe { loop { diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 0f28d9acb..34fcdc103 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -51,7 +51,7 @@ default = [] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["libc", "nix", "winapi-util"] -fsext = ["libc", "time"] +fsext = ["libc", "nix", "time"] lines = [] memo = ["itertools"] mode = ["libc"] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index b1b87a613..2e5aea1e2 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -34,6 +34,7 @@ pub mod process; pub mod signals; #[cfg(all( unix, + not(target_os = "android"), not(target_os = "fuchsia"), not(target_os = "redox"), not(target_env = "musl"), diff --git a/src/uucore/src/lib/features/entries.rs b/src/uucore/src/lib/features/entries.rs index 0366355d4..561de888b 100644 --- a/src/uucore/src/lib/features/entries.rs +++ b/src/uucore/src/lib/features/entries.rs @@ -147,16 +147,16 @@ pub struct Passwd { /// AKA passwd.pw_gid pub gid: gid_t, /// AKA passwd.pw_gecos - pub user_info: String, + pub user_info: Option, /// AKA passwd.pw_shell - pub user_shell: String, + pub user_shell: Option, /// AKA passwd.pw_dir - pub user_dir: String, + pub user_dir: Option, /// AKA passwd.pw_passwd - pub user_passwd: String, + pub user_passwd: Option, /// AKA passwd.pw_class #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] - pub user_access_class: String, + pub user_access_class: Option, /// AKA passwd.pw_change #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] pub passwd_change_time: time_t, @@ -166,8 +166,13 @@ pub struct Passwd { } /// SAFETY: ptr must point to a valid C string. -unsafe fn cstr2string(ptr: *const c_char) -> String { - CStr::from_ptr(ptr).to_string_lossy().into_owned() +/// Returns None if ptr is null. +unsafe fn cstr2string(ptr: *const c_char) -> Option { + if !ptr.is_null() { + Some(CStr::from_ptr(ptr).to_string_lossy().into_owned()) + } else { + None + } } impl Passwd { @@ -175,10 +180,16 @@ impl Passwd { /// the function runs. That means PW_LOCK must be held. unsafe fn from_raw(raw: passwd) -> Self { Self { - name: cstr2string(raw.pw_name), + name: cstr2string(raw.pw_name).expect("passwd without name"), uid: raw.pw_uid, gid: raw.pw_gid, + #[cfg(not(all( + target_os = "android", + any(target_arch = "x86", target_arch = "arm") + )))] user_info: cstr2string(raw.pw_gecos), + #[cfg(all(target_os = "android", any(target_arch = "x86", target_arch = "arm")))] + user_info: None, user_shell: cstr2string(raw.pw_shell), user_dir: cstr2string(raw.pw_dir), user_passwd: cstr2string(raw.pw_passwd), @@ -243,7 +254,7 @@ impl Group { /// the function runs. That means PW_LOCK must be held. unsafe fn from_raw(raw: group) -> Self { Self { - name: cstr2string(raw.gr_name), + name: cstr2string(raw.gr_name).expect("group without name"), gid: raw.gr_gid, } } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 6845ca3ca..eeaf54061 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -15,9 +15,9 @@ extern crate time; pub use crate::*; // import macros from `../../macros.rs` -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] const LINUX_MTAB: &str = "/etc/mtab"; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] const LINUX_MOUNTINFO: &str = "/proc/self/mountinfo"; static MOUNT_OPT_BIND: &str = "bind"; #[cfg(windows)] @@ -75,7 +75,8 @@ use std::convert::{AsRef, From}; target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "linux" + target_os = "linux", + target_os = "android", ))] use std::ffi::CStr; #[cfg(not(windows))] @@ -88,8 +89,8 @@ use std::time::UNIX_EPOCH; #[cfg(any( target_os = "linux", - target_vendor = "apple", target_os = "android", + target_vendor = "apple", target_os = "freebsd", target_os = "openbsd" ))] @@ -106,8 +107,8 @@ pub use libc::statvfs as StatFs; #[cfg(any( target_os = "linux", - target_vendor = "apple", target_os = "android", + target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "redox" @@ -208,7 +209,7 @@ impl MountInfo { } } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] fn new(file_name: &str, raw: &[&str]) -> Option { match file_name { // spell-checker:ignore (word) noatime @@ -382,9 +383,9 @@ extern "C" { fn get_mount_info(mount_buffer_p: *mut *mut StatFs, flags: c_int) -> c_int; } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::fs::File; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::io::{BufRead, BufReader}; #[cfg(any( target_vendor = "apple", @@ -403,7 +404,7 @@ use std::ptr; use std::slice; /// Read file system list. pub fn read_fs_list() -> Vec { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let (file_name, f) = File::open(LINUX_MOUNTINFO) .map(|f| (LINUX_MOUNTINFO, f)) @@ -611,17 +612,27 @@ impl FsMeta for StatFs { fn free_file_nodes(&self) -> u64 { self.f_ffree as u64 } - #[cfg(any(target_os = "linux", target_vendor = "apple", target_os = "freebsd"))] + #[cfg(any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd" + ))] fn fs_type(&self) -> i64 { self.f_type as i64 } - #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "freebsd")))] + #[cfg(not(any( + target_os = "linux", + target_os = "android", + target_vendor = "apple", + target_os = "freebsd" + )))] fn fs_type(&self) -> i64 { // FIXME: statvfs doesn't have an equivalent, so we need to do something else unimplemented!() } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] fn io_size(&self) -> u64 { self.f_frsize as u64 } @@ -634,6 +645,7 @@ impl FsMeta for StatFs { target_vendor = "apple", target_os = "freebsd", target_os = "linux", + target_os = "android", target_os = "netbsd" )))] fn io_size(&self) -> u64 { @@ -650,24 +662,26 @@ impl FsMeta for StatFs { target_vendor = "apple", target_os = "freebsd", target_os = "linux", + target_os = "android", target_os = "openbsd" ))] fn fsid(&self) -> u64 { let f_fsid: &[u32; 2] = - unsafe { &*(&self.f_fsid as *const libc::fsid_t as *const [u32; 2]) }; + unsafe { &*(&self.f_fsid as *const nix::sys::statfs::fsid_t as *const [u32; 2]) }; (u64::from(f_fsid[0])) << 32 | u64::from(f_fsid[1]) } #[cfg(not(any( target_vendor = "apple", target_os = "freebsd", target_os = "linux", + target_os = "android", target_os = "openbsd" )))] fn fsid(&self) -> u64 { self.f_fsid as u64 } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] fn namelen(&self) -> u64 { self.f_namelen as u64 } @@ -684,6 +698,7 @@ impl FsMeta for StatFs { target_vendor = "apple", target_os = "freebsd", target_os = "linux", + target_os = "android", target_os = "netbsd", target_os = "openbsd" )))] @@ -903,7 +918,7 @@ mod tests { } #[test] - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] fn test_mountinfo() { // spell-checker:ignore (word) relatime let info = MountInfo::new( diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index e6d2e7763..e7b20e7d8 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -23,7 +23,7 @@ Linux Programmer's Manual */ -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] pub static ALL_SIGNALS: [&str; 32] = [ "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", diff --git a/src/uucore/src/lib/features/utmpx.rs b/src/uucore/src/lib/features/utmpx.rs index 2a0e2810b..302d03d71 100644 --- a/src/uucore/src/lib/features/utmpx.rs +++ b/src/uucore/src/lib/features/utmpx.rs @@ -198,14 +198,14 @@ impl Utmpx { /// A.K.A. ut.ut_exit /// /// Return (e_termination, e_exit) - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] pub fn exit_status(&self) -> (i16, i16) { (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit) } /// A.K.A. ut.ut_exit /// /// Return (0, 0) on Non-Linux platform - #[cfg(not(any(target_os = "linux", target_os = "android")))] + #[cfg(not(target_os = "linux"))] pub fn exit_status(&self) -> (i16, i16) { (0, 0) } diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 8f3d045eb..ad8f81259 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -62,6 +62,7 @@ pub use crate::features::process; pub use crate::features::signals; #[cfg(all( unix, + not(target_os = "android"), not(target_os = "fuchsia"), not(target_os = "redox"), not(target_env = "musl"), diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 96c77a40e..d4541d690 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -5,7 +5,7 @@ use std::fs::OpenOptions; #[cfg(unix)] use std::io::Read; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use rlimit::Resource; #[test] @@ -93,7 +93,7 @@ fn test_fifo_symlink() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_closes_file_descriptors() { // Each file creates a pipe, which has two file descriptors. // If they are not closed then five is certainly too many. @@ -396,10 +396,10 @@ fn test_squeeze_blank_before_numbering() { #[cfg(unix)] fn test_dev_random() { let mut buf = [0; 2048]; - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] const DEV_RANDOM: &str = "/dev/urandom"; - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] const DEV_RANDOM: &str = "/dev/random"; let mut proc = new_ucmd!().args(&[DEV_RANDOM]).run_no_wait(); diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 1d89caca7..33ec2c6c9 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -8,7 +8,7 @@ fn test_invalid_option() { new_ucmd!().arg("-w").arg("/").fails(); } -static DIR: &str = "/tmp"; +static DIR: &str = "/dev"; // we should always get both arguments, regardless of whether --reference was used #[test] @@ -49,11 +49,13 @@ fn test_invalid_group() { #[test] fn test_1() { if get_effective_gid() != 0 { - new_ucmd!() - .arg("bin") - .arg(DIR) - .fails() - .stderr_is("chgrp: changing group of '/tmp': Operation not permitted (os error 1)"); + new_ucmd!().arg("bin").arg(DIR).fails().stderr_contains( + // linux fails with "Operation not permitted (os error 1)" + // because of insufficient permissions, + // android fails with "Permission denied (os error 13)" + // because it can't resolve /proc (even though it can resolve /proc/self/) + "chgrp: changing group of '/dev': ", + ); } } @@ -76,7 +78,7 @@ fn test_preserve_root() { // It's weird that on OS X, `realpath /etc/..` returns '/private' for d in [ "/", - "/////tmp///../../../../", + "/////dev///../../../../", "../../../../../../../../../../../../../../", "./../../../../../../../../../../../../../../", ] { @@ -94,7 +96,7 @@ fn test_preserve_root_symlink() { let file = "test_chgrp_symlink2root"; for d in [ "/", - "////tmp//../../../../", + "////dev//../../../../", "..//../../..//../..//../../../../../../../../", ".//../../../../../../..//../../../../../../../", ] { @@ -108,7 +110,7 @@ fn test_preserve_root_symlink() { } let (at, mut ucmd) = at_and_ucmd!(); - at.symlink_file("///usr", file); + at.symlink_file("///dev", file); ucmd.arg("--preserve-root") .arg("-HR") .arg("bin").arg(format!(".//{}/..//..//../../", file)) @@ -116,15 +118,12 @@ fn test_preserve_root_symlink() { .stderr_is("chgrp: it is dangerous to operate recursively on '/'\nchgrp: use --no-preserve-root to override this failsafe"); let (at, mut ucmd) = at_and_ucmd!(); - at.symlink_file("/", "/tmp/__root__"); + at.symlink_file("/", "__root__"); ucmd.arg("--preserve-root") .arg("-R") - .arg("bin").arg("/tmp/__root__/.") + .arg("bin").arg("__root__/.") .fails() .stderr_is("chgrp: it is dangerous to operate recursively on '/'\nchgrp: use --no-preserve-root to override this failsafe"); - - use std::fs; - fs::remove_file("/tmp/__root__").unwrap(); } #[test] @@ -156,7 +155,7 @@ fn test_reference() { } #[test] -#[cfg(any(target_os = "linux", target_vendor = "apple"))] +#[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] fn test_reference_multi_no_equal() { new_ucmd!() .arg("-v") @@ -170,7 +169,7 @@ fn test_reference_multi_no_equal() { } #[test] -#[cfg(any(target_os = "linux", target_vendor = "apple"))] +#[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] fn test_reference_last() { new_ucmd!() .arg("-v") @@ -212,7 +211,7 @@ fn test_big_p() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_big_h() { if get_effective_gid() != 0 { assert!( diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 0857e5659..4470260f4 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -1,7 +1,7 @@ // spell-checker:ignore (words) agroupthatdoesntexist auserthatdoesntexist cuuser groupname notexisting passgrp use crate::common::util::*; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use rust_users::get_effective_uid; extern crate chown; @@ -617,7 +617,7 @@ fn test_root_preserve() { result.stderr_contains(&"chown: it is dangerous to operate recursively"); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_big_p() { if get_effective_uid() != 0 { @@ -627,7 +627,11 @@ fn test_big_p() { .arg("/proc/self/cwd") .fails() .stderr_contains( - "chown: changing ownership of '/proc/self/cwd': Operation not permitted (os error 1)", + // linux fails with "Operation not permitted (os error 1)" + // because of insufficient permissions, + // android fails with "Permission denied (os error 13)" + // because it can't resolve /proc (even though it can resolve /proc/self/) + "chown: changing ownership of '/proc/self/cwd': ", ); } } diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index 3e5c22679..6c9237ac3 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -14,6 +14,7 @@ fn test_missing_operand() { } #[test] +#[cfg(not(target_os = "android"))] fn test_enter_chroot_fails() { // NOTE: since #2689 this test also ensures that we don't regress #2687 let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 7bb11306d..079e966be 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -14,15 +14,15 @@ use std::os::unix::fs::PermissionsExt; #[cfg(windows)] use std::os::windows::fs::symlink_file; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use filetime::FileTime; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use rlimit::Resource; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::fs as std_fs; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::thread::sleep; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::time::Duration; static TEST_EXISTING_FILE: &str = "existing_file.txt"; @@ -38,11 +38,11 @@ static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/"; static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt"; static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new"; static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt"; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] static TEST_MOUNT_COPY_FROM_FOLDER: &str = "dir_with_mount"; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] static TEST_MOUNT_MOUNTPOINT: &str = "mount"; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt"; #[cfg(unix)] static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt"; @@ -1062,7 +1062,7 @@ fn test_cp_archive() { } #[test] -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "android")))] fn test_cp_archive_recursive() { let (at, mut ucmd) = at_and_ucmd!(); @@ -1132,7 +1132,7 @@ fn test_cp_archive_recursive() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_cp_preserve_timestamps() { let (at, mut ucmd) = at_and_ucmd!(); let ts = time::now().to_timespec(); @@ -1165,7 +1165,7 @@ fn test_cp_preserve_timestamps() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_cp_no_preserve_timestamps() { let (at, mut ucmd) = at_and_ucmd!(); let ts = time::now().to_timespec(); @@ -1206,7 +1206,7 @@ fn test_cp_no_preserve_timestamps() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_cp_target_file_dev_null() { let (at, mut ucmd) = at_and_ucmd!(); let file1 = "/dev/null"; @@ -1219,7 +1219,7 @@ fn test_cp_target_file_dev_null() { } #[test] -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] fn test_cp_one_file_system() { use crate::common::util::AtPath; use walkdir::WalkDir; @@ -1283,7 +1283,7 @@ fn test_cp_one_file_system() { } #[test] -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] fn test_cp_reflink_always() { let (at, mut ucmd) = at_and_ucmd!(); let result = ucmd @@ -1301,7 +1301,7 @@ fn test_cp_reflink_always() { } #[test] -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] fn test_cp_reflink_auto() { let (at, mut ucmd) = at_and_ucmd!(); ucmd.arg("--reflink=auto") @@ -1314,7 +1314,7 @@ fn test_cp_reflink_auto() { } #[test] -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] fn test_cp_reflink_never() { let (at, mut ucmd) = at_and_ucmd!(); ucmd.arg("--reflink=never") @@ -1327,7 +1327,7 @@ fn test_cp_reflink_never() { } #[test] -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] fn test_cp_reflink_bad() { let (_, mut ucmd) = at_and_ucmd!(); let _result = ucmd @@ -1339,7 +1339,7 @@ fn test_cp_reflink_bad() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_cp_reflink_insufficient_permission() { let (at, mut ucmd) = at_and_ucmd!(); @@ -1355,7 +1355,7 @@ fn test_cp_reflink_insufficient_permission() { .stderr_only("cp: 'unreadable' -> 'existing_file.txt': Permission denied (os error 13)"); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_closes_file_descriptors() { new_ucmd!() @@ -1520,6 +1520,7 @@ fn test_cp_archive_on_nonexistent_file() { } #[test] +#[cfg(not(target_os = "android"))] fn test_cp_link_backup() { let (at, mut ucmd) = at_and_ucmd!(); at.touch("file2"); @@ -1613,6 +1614,7 @@ fn test_cp_overriding_arguments() { ("--force", "--remove-destination"), ("--interactive", "--no-clobber"), ("--link", "--symbolic-link"), + #[cfg(not(target_os = "android"))] ("--symbolic-link", "--link"), ("--dereference", "--no-dereference"), ("--no-dereference", "--dereference"), diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index a04de9b59..81b176ce9 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -149,7 +149,7 @@ fn test_date_set_invalid() { } #[test] -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] fn test_date_set_permissions_error() { if !(get_effective_uid() == 0 || uucore::os::is_wsl_1()) { let result = new_ucmd!() diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 71049a2af..3cc5346b7 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -32,7 +32,7 @@ macro_rules! assert_fixture_exists { }}; } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] macro_rules! assert_fixture_not_exists { ($fname:expr) => {{ let fpath = PathBuf::from(format!("./fixtures/dd/{}", $fname)); @@ -261,7 +261,7 @@ fn test_final_stats_unspec() { new_ucmd!().run().stderr_only(&output).success(); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_excl_causes_failure_when_present() { let fname = "this-file-exists-excl.txt"; @@ -272,7 +272,7 @@ fn test_excl_causes_failure_when_present() { .fails(); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_noatime_does_not_update_infile_atime() { // NOTE: Not all environments support tracking access time. If this @@ -292,7 +292,7 @@ fn test_noatime_does_not_update_infile_atime() { assert_eq!(pre_atime, post_atime); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_noatime_does_not_update_ofile_atime() { // NOTE: Not all environments support tracking access time. If this @@ -312,7 +312,7 @@ fn test_noatime_does_not_update_ofile_atime() { assert_eq!(pre_atime, post_atime); } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_nocreat_causes_failure_when_outfile_not_present() { let fname = "this-file-does-not-exist.txt"; diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 1deddb77f..254e75166 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -47,7 +47,7 @@ fn test_du_basics_subdir() { let result = ts.ucmd().arg(SUB_DIR).succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &[SUB_DIR])); if result_reference.succeeded() { @@ -122,7 +122,7 @@ fn test_du_soft_link() { let result = ts.ucmd().arg(SUB_DIR_LINKS).succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &[SUB_DIR_LINKS])); if result_reference.succeeded() { @@ -160,6 +160,7 @@ fn _du_soft_link(s: &str) { } } +#[cfg(not(target_os = "android"))] #[test] fn test_du_hard_link() { let ts = TestScenario::new(util_name!()); @@ -213,7 +214,7 @@ fn test_du_d_flag() { let result = ts.ucmd().arg("-d1").succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["-d1"])); if result_reference.succeeded() { @@ -259,7 +260,7 @@ fn test_du_dereference() { let result = ts.ucmd().arg("-L").arg(SUB_DIR_LINKS).succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["-L", SUB_DIR_LINKS])); @@ -303,13 +304,13 @@ fn test_du_inodes_basic() { let ts = TestScenario::new(util_name!()); let result = ts.ucmd().arg("--inodes").succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["--inodes"])); assert_eq!(result.stdout_str(), result_reference.stdout_str()); } - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] _du_inodes_basic(result.stdout_str()); } @@ -357,7 +358,7 @@ fn test_du_inodes() { result.stdout_contains("3\t./subdir/links\n"); result.stdout_contains("3\t.\n"); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["--separate-dirs", "--inodes"])); @@ -438,7 +439,7 @@ fn test_du_no_permission() { "du: cannot read directory 'subdir/links': Permission denied (os error 13)", ); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &[SUB_DIR_LINKS])); if result_reference @@ -483,7 +484,7 @@ fn test_du_one_file_system() { let result = ts.ucmd().arg("-x").arg(SUB_DIR).succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["-x", SUB_DIR])); if result_reference.succeeded() { @@ -518,13 +519,13 @@ fn test_du_apparent_size() { let ts = TestScenario::new(util_name!()); let result = ts.ucmd().arg("--apparent-size").succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["--apparent-size"])); assert_eq!(result.stdout_str(), result_reference.stdout_str()); } - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] _du_apparent_size(result.stdout_str()); } @@ -586,7 +587,7 @@ fn test_du_bytes() { let ts = TestScenario::new(util_name!()); let result = ts.ucmd().arg("--bytes").succeeds(); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { let result_reference = unwrap_or_return!(expected_result(&ts, &["--bytes"])); assert_eq!(result.stdout_str(), result_reference.stdout_str()); @@ -602,7 +603,8 @@ fn test_du_bytes() { not(target_vendor = "apple"), not(target_os = "windows"), not(target_os = "freebsd"), - not(target_os = "linux") + not(target_os = "linux"), + not(target_os = "android"), ))] result.stdout_contains("21529\t./subdir\n"); } diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index 8606678e9..b791dbfd0 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -139,7 +139,7 @@ fn test_id_real() { } #[test] -#[cfg(all(unix, not(target_os = "linux")))] +#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))] fn test_id_pretty_print() { // `-p` is BSD only and not supported on GNU's `id` let username = whoami(); @@ -159,7 +159,7 @@ fn test_id_pretty_print() { } #[test] -#[cfg(all(unix, not(target_os = "linux")))] +#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))] fn test_id_password_style() { // `-P` is BSD only and not supported on GNU's `id` let username = whoami(); @@ -437,7 +437,10 @@ fn test_id_no_specified_user_posixly() { result.success(); } - #[cfg(all(target_os = "linux", feature = "feat_selinux"))] + #[cfg(all( + any(target_os = "linux", target_os = "android"), + feature = "feat_selinux" + ))] { use selinux::{self, KernelSupport}; if selinux::kernel_support() == KernelSupport::Unsupported { diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index a0e18c19a..dca04ac56 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -6,7 +6,7 @@ use rust_users::*; use std::os::unix::fs::PermissionsExt; #[cfg(not(any(windows, target_os = "freebsd")))] use std::process::Command; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use std::thread::sleep; #[test] @@ -98,7 +98,11 @@ fn test_install_ancestors_mode_directories() { let ancestor2 = "ancestor1/ancestor2"; let target_dir = "ancestor1/ancestor2/target_dir"; let directories_arg = "-d"; - let mode_arg = "--mode=700"; + let mode_arg = "--mode=200"; + let probe = "probe"; + + at.mkdir(probe); + let default_perms = at.metadata(probe).permissions().mode(); ucmd.args(&[mode_arg, directories_arg, target_dir]) .succeeds() @@ -108,11 +112,11 @@ fn test_install_ancestors_mode_directories() { assert!(at.dir_exists(ancestor2)); assert!(at.dir_exists(target_dir)); - assert_ne!(0o40_700_u32, at.metadata(ancestor1).permissions().mode()); - assert_ne!(0o40_700_u32, at.metadata(ancestor2).permissions().mode()); + assert_eq!(default_perms, at.metadata(ancestor1).permissions().mode()); + assert_eq!(default_perms, at.metadata(ancestor2).permissions().mode()); // Expected mode only on the target_dir. - assert_eq!(0o40_700_u32, at.metadata(target_dir).permissions().mode()); + assert_eq!(0o40_200_u32, at.metadata(target_dir).permissions().mode()); } #[test] @@ -386,7 +390,7 @@ fn test_install_copy_file() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_install_target_file_dev_null() { let (at, mut ucmd) = at_and_ucmd!(); @@ -487,7 +491,7 @@ fn test_install_copy_then_compare_file() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_install_copy_then_compare_file_with_extra_mode() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -549,6 +553,8 @@ const STRIP_SOURCE_FILE_SYMBOL: &str = "main"; fn strip_source_file() -> &'static str { if cfg!(target_os = "macos") { "helloworld_macos" + } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") { + "helloworld_android" } else { "helloworld_linux" } diff --git a/tests/by-util/test_link.rs b/tests/by-util/test_link.rs index 6e98f1d64..9f6a2ee5f 100644 --- a/tests/by-util/test_link.rs +++ b/tests/by-util/test_link.rs @@ -1,5 +1,6 @@ use crate::common::util::*; +#[cfg(not(target_os = "android"))] #[test] fn test_link_existing_file() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_ln.rs b/tests/by-util/test_ln.rs index 0dcde3b35..980225260 100644 --- a/tests/by-util/test_ln.rs +++ b/tests/by-util/test_ln.rs @@ -360,7 +360,7 @@ fn test_symlink_verbose() { scene .ucmd() - .args(&["-v", file_a, file_b]) + .args(&["-s", "-v", file_a, file_b]) .succeeds() .stdout_only(format!("'{}' -> '{}'\n", file_b, file_a)); @@ -368,7 +368,7 @@ fn test_symlink_verbose() { scene .ucmd() - .args(&["-v", "-b", file_a, file_b]) + .args(&["-s", "-v", "-b", file_a, file_b]) .succeeds() .stdout_only(format!( "'{}' -> '{}' (backup: '{}~')\n", @@ -639,7 +639,7 @@ fn test_backup_force() { assert!(at.file_exists("b~")); scene .ucmd() - .args(&["-f", "--b=simple", "a", "b"]) + .args(&["-s", "-f", "--b=simple", "a", "b"]) .succeeds() .no_stderr(); assert!(at.file_exists("a")); diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f979d1e14..72217a403 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -314,7 +314,7 @@ fn test_ls_devices() { .stdout_matches(&Regex::new("[^ ] 3, 2 [^ ]").unwrap()); } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { scene .ucmd() @@ -327,11 +327,15 @@ fn test_ls_devices() { // Tests display alignment against a file (stdout is a link to a tty) #[cfg(unix)] { + #[cfg(not(target_os = "android"))] + let stdout = "/dev/stdout"; + #[cfg(target_os = "android")] + let stdout = "/proc/self/fd/1"; let res = scene .ucmd() .arg("-alL") .arg("/dev/null") - .arg("/dev/stdout") + .arg(stdout) .succeeds(); let null_len = String::from_utf8(res.stdout().to_owned()) @@ -350,7 +354,7 @@ fn test_ls_devices() { .lines() .nth(1) .unwrap() - .strip_suffix("/dev/stdout") + .strip_suffix(stdout) .unwrap() .len(); @@ -1546,9 +1550,9 @@ fn test_ls_order_time() { at.open("test-4").metadata().unwrap().accessed().unwrap(); // It seems to be dependent on the platform whether the access time is actually set - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "android")))] result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n"); - #[cfg(windows)] + #[cfg(any(windows, target_os = "android"))] result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); } diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 314fd3a7f..c4ec03d95 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -783,7 +783,7 @@ fn test_mv_verbose() { } #[test] -#[cfg(target_os = "linux")] // mkdir does not support -m on windows. Freebsd doesn't return a permission error either. +#[cfg(any(target_os = "linux", target_os = "android"))] // mkdir does not support -m on windows. Freebsd doesn't return a permission error either. fn test_mv_permission_error() { let scene = TestScenario::new("mkdir"); let folder1 = "bar"; diff --git a/tests/by-util/test_nice.rs b/tests/by-util/test_nice.rs index 4a77ae24e..2b53ed437 100644 --- a/tests/by-util/test_nice.rs +++ b/tests/by-util/test_nice.rs @@ -1,6 +1,7 @@ use crate::common::util::*; #[test] +#[cfg(not(target_os = "android"))] fn test_get_current_niceness() { // NOTE: this assumes the test suite is being run with a default niceness // of 0, which may not necessarily be true @@ -8,6 +9,7 @@ fn test_get_current_niceness() { } #[test] +#[cfg(not(target_os = "android"))] fn test_negative_adjustment() { // This assumes the test suite is run as a normal (non-root) user, and as // such attempting to set a negative niceness value will be rejected by diff --git a/tests/by-util/test_nohup.rs b/tests/by-util/test_nohup.rs index b98ae007c..8d848131c 100644 --- a/tests/by-util/test_nohup.rs +++ b/tests/by-util/test_nohup.rs @@ -6,7 +6,12 @@ use std::thread::sleep; // All that can be tested is the side-effects. #[test] -#[cfg(any(target_os = "linux", target_os = "freebsd", target_vendor = "apple"))] +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_vendor = "apple" +))] fn test_nohup_multiple_args_and_flags() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_pinky.rs b/tests/by-util/test_pinky.rs index c036d449f..1da93ee42 100644 --- a/tests/by-util/test_pinky.rs +++ b/tests/by-util/test_pinky.rs @@ -24,11 +24,14 @@ fn test_capitalize() { fn test_long_format() { let login = "root"; let pw: Passwd = Passwd::locate(login).unwrap(); - let real_name = pw.user_info.replace('&', &pw.name.capitalize()); + let user_info = pw.user_info.unwrap_or_default(); + let user_dir = pw.user_dir.unwrap_or_default(); + let user_shell = pw.user_shell.unwrap_or_default(); + let real_name = user_info.replace('&', &pw.name.capitalize()); let ts = TestScenario::new(util_name!()); ts.ucmd().arg("-l").arg(login).succeeds().stdout_is(format!( "Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n", - login, real_name, pw.user_dir, pw.user_shell + login, real_name, user_dir, user_shell )); ts.ucmd() diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 4975ceff4..24846d207 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -872,7 +872,7 @@ fn sort_empty_chunk() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_compress() { new_ucmd!() .args(&[ @@ -888,7 +888,7 @@ fn test_compress() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_compress_merge() { new_ucmd!() .args(&[ diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index b8445543f..90ad2d12a 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -113,14 +113,14 @@ fn test_invalid_option() { #[cfg(unix)] const NORMAL_FORMAT_STR: &str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %x %X %y %Y %z %Z"; // avoid "%w %W" (birth/creation) due to `stat` limitations and linux kernel & rust version capability variations -#[cfg(any(target_os = "linux"))] +#[cfg(any(target_os = "linux", target_os = "android"))] const DEV_FORMAT_STR: &str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z"; #[cfg(target_os = "linux")] const FS_FORMAT_STR: &str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions #[test] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_terse_fs_format() { let args = ["-f", "-t", "/proc"]; let ts = TestScenario::new(util_name!()); @@ -238,6 +238,7 @@ fn test_symlinks() { // arbitrarily chosen symlinks with hope that the CI environment provides at least one of them for file in [ "/bin/sh", + "/data/data/com.termux/files/usr/bin/sh", // spell-checker:disable-line "/bin/sudoedit", "/usr/bin/ex", "/etc/localtime", @@ -259,7 +260,7 @@ fn test_symlinks() { } } -#[cfg(any(target_os = "linux", target_vendor = "apple"))] +#[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] #[test] fn test_char() { // TODO: "(%t) (%x) (%w)" deviate from GNU stat for `character special file` on macOS @@ -268,13 +269,13 @@ fn test_char() { // >"(f) (2021-05-20 23:08:03.455598000 +0200) (-)\n" let args = [ "-c", - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] DEV_FORMAT_STR, #[cfg(target_os = "linux")] "/dev/pts/ptmx", #[cfg(any(target_vendor = "apple"))] "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (/%T) %u %U %W %X %y %Y %z %Z", - #[cfg(any(target_vendor = "apple"))] + #[cfg(any(target_os = "android", target_vendor = "apple"))] "/dev/ptmx", ]; let ts = TestScenario::new(util_name!()); diff --git a/tests/by-util/test_uname.rs b/tests/by-util/test_uname.rs index adcaa1072..5e78ddc13 100644 --- a/tests/by-util/test_uname.rs +++ b/tests/by-util/test_uname.rs @@ -53,6 +53,11 @@ fn test_uname_kernel() { #[test] fn test_uname_operating_system() { + #[cfg(target_os = "android")] + new_ucmd!() + .arg("--operating-system") + .succeeds() + .stdout_is("Android\n"); #[cfg(target_vendor = "apple")] new_ucmd!() .arg("--operating-system") diff --git a/tests/by-util/test_wc.rs b/tests/by-util/test_wc.rs index 3537f902d..12ad3003b 100644 --- a/tests/by-util/test_wc.rs +++ b/tests/by-util/test_wc.rs @@ -262,10 +262,10 @@ fn test_read_from_nonexistent_file() { } #[test] -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(any(target_os = "linux", target_os = "android"))] fn test_files_from_pseudo_filesystem() { - let result = new_ucmd!().arg("-c").arg("/proc/version").succeeds(); - assert_ne!(result.stdout_str(), "0 /proc/version\n"); + let result = new_ucmd!().arg("-c").arg("/proc/cpuinfo").succeeds(); + assert_ne!(result.stdout_str(), "0 /proc/cpuinfo\n"); } #[test] diff --git a/tests/common/util.rs b/tests/common/util.rs index bf7143bb5..5a669fcd4 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -670,7 +670,7 @@ impl AtPath { let name = CString::new(self.plus_as_string(fifo)).unwrap(); let mut stat: libc::stat = std::mem::zeroed(); if libc::stat(name.as_ptr(), &mut stat) >= 0 { - libc::S_IFIFO & stat.st_mode != 0 + libc::S_IFIFO & stat.st_mode as libc::mode_t != 0 } else { false } @@ -892,7 +892,7 @@ pub struct UCommand { stdout: Option, stderr: Option, bytes_into_stdin: Option>, - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] limits: Vec<(rlimit::Resource, u64, u64)>, } @@ -913,19 +913,21 @@ impl UCommand { let mut cmd = Command::new(bin_path); cmd.current_dir(curdir.as_ref()); if env_clear { + cmd.env_clear(); if cfg!(windows) { // spell-checker:ignore (dll) rsaenh // %SYSTEMROOT% is required on Windows to initialize crypto provider // ... and crypto provider is required for std::rand // From `procmon`: RegQueryValue HKLM\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider\Image Path // SUCCESS Type: REG_SZ, Length: 66, Data: %SystemRoot%\system32\rsaenh.dll" - for (key, _) in env::vars_os() { - if key.as_os_str() != "SYSTEMROOT" { - cmd.env_remove(key); - } + if let Some(systemroot) = env::var_os("SYSTEMROOT") { + cmd.env("SYSTEMROOT", systemroot); } } else { - cmd.env_clear(); + // if someone is setting LD_PRELOAD, there's probably a good reason for it + if let Some(ld_preload) = env::var_os("LD_PRELOAD") { + cmd.env("LD_PRELOAD", ld_preload); + } } } cmd @@ -938,7 +940,7 @@ impl UCommand { stdin: None, stdout: None, stderr: None, - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] limits: vec![], }; @@ -1042,7 +1044,7 @@ impl UCommand { self } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn with_limit( &mut self, resource: rlimit::Resource, diff --git a/tests/fixtures/install/helloworld_android b/tests/fixtures/install/helloworld_android new file mode 100755 index 000000000..ffb3e6aeb Binary files /dev/null and b/tests/fixtures/install/helloworld_android differ diff --git a/tests/fixtures/install/helloworld_linux b/tests/fixtures/install/helloworld_linux index c1c6b9b37..4f0895df9 100755 Binary files a/tests/fixtures/install/helloworld_linux and b/tests/fixtures/install/helloworld_linux differ diff --git a/util/android-commands.sh b/util/android-commands.sh new file mode 100755 index 000000000..2a4fca416 --- /dev/null +++ b/util/android-commands.sh @@ -0,0 +1,152 @@ +# spell-checker:ignore termux keyevent sdcard binutils unmatch adb's dumpsys logcat pkill + +# There are three shells: the host's, adb, and termux. Only adb lets us run +# commands directly on the emulated device, only termux provides a GNU +# environment on the emulated device (to e.g. run cargo). So we use adb to +# launch termux, then to send keystrokes to it while it's running. +# This means that the commands sent to termux are first parsed as arguments in +# this shell, then as arguments in the adb shell, before finally being used as +# text inputs to the app. Hence, the "'wrapping'" on those commands. +# There's no way to get any feedback from termux, so every time we run a +# command on it, we make sure it ends by creating a unique *.probe file at the +# end of the command. The contents of the file are used as a return code: 0 on +# success, some other number for errors (an empty file is basically the same as +# 0). Note that the return codes are text, not raw bytes. + + +this_repo="$(dirname $(dirname -- "$(readlink -- "${0}")"))" + +help () { + echo \ +"Usage: $0 COMMAND [ARG] + +where COMMAND is one of: + snapshot APK install APK and dependencies on an emulator to prep a snapshot + (you can, but probably don't want to, run this for physical + devices -- just set up termux and the dependencies yourself) + sync [REPO] push the repo at REPO to the device, deleting and restoring all + symlinks (locally) in the process; by default, REPO is: + $this_repo + build run \`cargo build --features feat_os_unix_android\` on the + device, then pull the output as build.log + tests run \`cargo test --features feat_os_unix_android\` on the + device, then pull the output as tests.log + +If you have multiple devices, use the ANDROID_SERIAL environment variable to +specify which to connect to." +} + +hit_enter() { + adb shell input keyevent 66 +} + +launch_termux() { + echo "launching termux" + if ! adb shell 'am start -n com.termux/.HomeActivity' ; then + echo "failed to launch termux" + exit 1 + fi + # the emulator can sometimes be a little slow to launch the app + while ! adb shell 'ls /sdcard/launch.probe' 2>/dev/null; do + echo "waiting for launch.probe" + sleep 5 + adb shell input text 'touch\ /sdcard/launch.probe' && hit_enter + done + echo "found launch.probe" + adb shell 'rm /sdcard/launch.probe' && echo "removed launch.probe" +} + +run_termux_command() { + command="$1" # text of the escaped command, including creating the probe! + probe="$2" # unique file that indicates the command is complete + launch_termux + adb shell input text "$command" && hit_enter + while ! adb shell "ls $probe" 2>/dev/null; do echo "waiting for $probe"; sleep 30; done + return_code=$(adb shell "cat $probe") + adb shell "rm $probe" + echo "return code: $return_code" + return $return_code +} + +snapshot () { + apk="$1" + echo "running snapshot" + adb install -g "$apk" + probe='/sdcard/pkg.probe' + command="'yes | pkg install rust binutils openssl -y; touch $probe'" + run_termux_command "$command" "$probe" + echo "snapshot complete" + adb shell input text "exit" && hit_enter && hit_enter +} + +sync () { + repo="$1" + echo "running sync $1" + # android doesn't allow symlinks on shared dirs, and adb can't selectively push files + symlinks=$(find "$repo" -type l) + # dash doesn't support process substitution :( + echo $symlinks | sort >symlinks + git -C "$repo" diff --name-status | cut -f 2 >modified + modified_links=$(join symlinks modified) + if [ ! -z "$modified_links" ]; then + echo "You have modified symlinks. Either stash or commit them, then try again: $modified_links" + exit 1 + fi + if ! git ls-files --error-unmatch $symlinks >/dev/null; then + echo "You have untracked symlinks. Either remove or commit them, then try again." + exit 1 + fi + rm $symlinks + # adb's shell user only has access to shared dirs... + adb push "$repo" /sdcard/coreutils + git -C "$repo" checkout $symlinks + # ...but shared dirs can't build, so move it home as termux + probe='/sdcard/mv.probe' + command="'cp -r /sdcard/coreutils ~/; touch $probe'" + run_termux_command "$command" "$probe" +} + +build () { + probe='/sdcard/build.probe' + command="'cd ~/coreutils && cargo build --features feat_os_unix_android 2>/sdcard/build.log; echo \$? >$probe'" + echo "running build" + run_termux_command "$command" "$probe" + return_code=$? + adb pull /sdcard/build.log . + cat build.log + return $return_code +} + +tests () { + probe='/sdcard/tests.probe' + command="'cd ~/coreutils && cargo test --features feat_os_unix_android --no-fail-fast >/sdcard/tests.log 2>&1; echo \$? >$probe'" + run_termux_command "$command" "$probe" + return_code=$? + adb pull /sdcard/tests.log . + cat tests.log + return $return_code +} + +#adb logcat & +exit_code=0 + +if [ $# -eq 1 ]; then + case "$1" in + sync) sync "$this_repo"; exit_code=$?;; + build) build; exit_code=$?;; + tests) tests; exit_code=$?;; + *) help;; + esac +elif [ $# -eq 2 ]; then + case "$1" in + snapshot) snapshot "$2"; exit_code=$?;; + sync) sync "$2"; exit_code=$?;; + *) help; exit 1;; + esac +else + help + exit_code=1 +fi + +#pkill adb +exit $exit_code