]> git.lizzy.rs Git - rust.git/commitdiff
std: Add compatibility with android-9
authorAlex Crichton <alex@alexcrichton.com>
Mon, 25 Apr 2016 20:39:05 +0000 (13:39 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 27 Apr 2016 16:28:48 +0000 (09:28 -0700)
The Gecko folks currently use Android API level 9 for their builds, so they're
requesting that we move back our minimum supported API level from 18 to 9. Turns
out, ABI-wise at least, there's not that many changes we need to take care of.
The `ftruncate64` API appeared in android-12 and the `log2` and `log2f` APIs
appeared in android-18. We can have a simple shim for `ftruncate64` which falls
back on `ftruncate` and the `log2` function can be approximated with just
`ln(f) / ln(2)`.

This should at least get the standard library building on API level 9, although
the tests aren't quite happening there just yet. As we seem to be growing a
number of Android compatibility shims, they're now centralized in a common
`sys::android` module.

src/libstd/num/f32.rs
src/libstd/num/f64.rs
src/libstd/sys/unix/android.rs [new file with mode: 0644]
src/libstd/sys/unix/fs.rs
src/libstd/sys/unix/mod.rs

index 624748f352ee1ec9903947914c27cc7822b58c4c..94aa3d6b513ef4d24489f34bde91cf63091b5eee 100644 (file)
@@ -646,7 +646,10 @@ pub fn log(self, base: f32) -> f32 { self.ln() / base.ln() }
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn log2(self) -> f32 {
-        unsafe { intrinsics::log2f32(self) }
+        #[cfg(target_os = "android")]
+        return ::sys::android::log2f32(self);
+        #[cfg(not(target_os = "android"))]
+        return unsafe { intrinsics::log2f32(self) };
     }
 
     /// Returns the base 10 logarithm of the number.
index 6515301aefd14d0a05e009e17a3f5a2a54057af3..2beffb64d3dc44ecb0a75d6d1cd8a120c6aa0524 100644 (file)
@@ -546,7 +546,12 @@ pub fn log(self, base: f64) -> f64 { self.ln() / base.ln() }
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn log2(self) -> f64 {
-        self.log_wrapper(|n| { unsafe { intrinsics::log2f64(n) } })
+        self.log_wrapper(|n| {
+            #[cfg(target_os = "android")]
+            return ::sys::android::log2f64(n);
+            #[cfg(not(target_os = "android"))]
+            return unsafe { intrinsics::log2f64(n) };
+        })
     }
 
     /// Returns the base 10 logarithm of the number.
diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs
new file mode 100644 (file)
index 0000000..abbe3fc
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Android ABI-compatibility module
+//!
+//! The ABI of Android has changed quite a bit over time, and libstd attempts to
+//! be both forwards and backwards compatible as much as possible. We want to
+//! always work with the most recent version of Android, but we also want to
+//! work with older versions of Android for whenever projects need to.
+//!
+//! Our current minimum supported Android version is `android-9`, e.g. Android
+//! with API level 9. We then in theory want to work on that and all future
+//! versions of Android!
+//!
+//! Some of the detection here is done at runtime via `dlopen` and
+//! introspection. Other times no detection is performed at all and we just
+//! provide a fallback implementation as some versions of Android we support
+//! don't have the function.
+//!
+//! You'll find more details below about why each compatibility shim is needed.
+
+#![cfg(target_os = "android")]
+
+use libc::{c_int, sighandler_t};
+
+use io;
+use sys::cvt_r;
+
+// The `log2` and `log2f` functions apparently appeared in android-18, or at
+// least you can see they're not present in the android-17 header [1] and they
+// are present in android-18 [2].
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+//                                       /android-17/arch-arm/usr/include/math.h
+// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+//                                       /android-18/arch-arm/usr/include/math.h
+//
+// Note that these shims are likely less precise than directly calling `log2`,
+// but hopefully that should be enough for now...
+//
+// Note that mathematically, for any arbitrary `y`:
+//
+//      log_2(x) = log_y(x) / log_y(2)
+//               = log_y(x) / (1 / log_2(y))
+//               = log_y(x) * log_2(y)
+//
+// Hence because `ln` (log_e) is available on all Android we just choose `y = e`
+// and get:
+//
+//      log_2(x) = ln(x) * log_2(e)
+
+#[cfg(not(test))]
+pub fn log2f32(f: f32) -> f32 {
+    f.ln() * ::f32::consts::LOG2_E
+}
+
+#[cfg(not(test))]
+pub fn log2f64(f: f64) -> f64 {
+    f.ln() * ::f64::consts::LOG2_E
+}
+
+// Back in the day [1] the `signal` function was just an inline wrapper
+// around `bsd_signal`, but starting in API level android-20 the `signal`
+// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was
+// removed [3].
+//
+// Basically this means that if we want to be binary compatible with multiple
+// Android releases (oldest being 9 and newest being 21) then we need to check
+// for both symbols and not actually link against either.
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+//                                       /android-18/arch-arm/usr/include/signal.h
+// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
+//                                       /platforms/android-20/arch-arm
+//                                       /usr/include/signal.h
+// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
+//                                       /android-21/arch-arm/usr/include/signal.h
+pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
+    weak!(fn signal(c_int, sighandler_t) -> sighandler_t);
+    weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t);
+
+    let f = signal.get().or_else(|| bsd_signal.get());
+    let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
+    f(signum, handler)
+}
+
+// The `ftruncate64` symbol apparently appeared in android-12, so we do some
+// dynamic detection to see if we can figure out whether `ftruncate64` exists.
+//
+// If it doesn't we just fall back to `ftruncate`, generating an error for
+// too-large values.
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+    weak!(fn ftruncate64(c_int, i64) -> c_int);
+
+    extern {
+        fn ftruncate(fd: c_int, off: i32) -> c_int;
+    }
+
+    unsafe {
+        match ftruncate64.get() {
+            Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()),
+            None => {
+                if size > i32::max_value() as u64 {
+                    Err(io::Error::new(io::ErrorKind::InvalidInput,
+                                       "cannot truncate >2GB"))
+                } else {
+                    cvt_r(|| ftruncate(fd, size as i32)).map(|_| ())
+                }
+            }
+        }
+    }
+}
index d5d17e7ee1201ce382f914ea52e48f874e064e3e..0969a59ea433813e9cc0482bee4b87260089761a 100644 (file)
@@ -27,7 +27,7 @@
 #[cfg(any(target_os = "linux", target_os = "emscripten"))]
 use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
 #[cfg(target_os = "android")]
-use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off64_t, ftruncate64, lseek64,
+use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off64_t, lseek64,
            dirent as dirent64, open as open64};
 #[cfg(not(any(target_os = "linux",
               target_os = "emscripten",
@@ -475,10 +475,13 @@ unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
     }
 
     pub fn truncate(&self, size: u64) -> io::Result<()> {
-        cvt_r(|| unsafe {
+        #[cfg(target_os = "android")]
+        return ::sys::android::ftruncate64(self.0.raw(), size);
+
+        #[cfg(not(target_os = "android"))]
+        return cvt_r(|| unsafe {
             ftruncate64(self.0.raw(), size as off64_t)
-        })?;
-        Ok(())
+        }).map(|_| ());
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
index f8b2d4dd23240bdcaf5f32fcbffe81357c46758d..12a877f7478204feb105c8ee78b51c11b4499efb 100644 (file)
@@ -31,6 +31,7 @@
 #[macro_use]
 pub mod weak;
 
+pub mod android;
 pub mod backtrace;
 pub mod condvar;
 pub mod ext;
@@ -91,37 +92,8 @@ unsafe fn reset_sigpipe() {
     unsafe fn reset_sigpipe() {}
 }
 
-// Currently the minimum supported Android version of the standard library is
-// API level 18 (android-18). Back in those days [1] the `signal` function was
-// just an inline wrapper around `bsd_signal`, but starting in API level
-// android-20 the `signal` symbols was introduced [2]. Finally, in android-21
-// the API `bsd_signal` was removed [3].
-//
-// Basically this means that if we want to be binary compatible with multiple
-// Android releases (oldest being 18 and newest being 21) then we need to check
-// for both symbols and not actually link against either.
-//
-// Note that if we're not on android we just link against the `android` symbol
-// itself.
-//
-// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
-//                                       /android-18/arch-arm/usr/include/signal.h
-// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
-//                                       /platforms/android-20/arch-arm
-//                                       /usr/include/signal.h
-// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
-//                                       /android-21/arch-arm/usr/include/signal.h
 #[cfg(target_os = "android")]
-unsafe fn signal(signum: libc::c_int,
-                 handler: libc::sighandler_t) -> libc::sighandler_t {
-    weak!(fn signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t);
-    weak!(fn bsd_signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t);
-
-    let f = signal.get().or_else(|| bsd_signal.get());
-    let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
-    f(signum, handler)
-}
-
+pub use sys::android::signal;
 #[cfg(not(target_os = "android"))]
 pub use libc::signal;