diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..4dce6d46f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,125 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + # Faster compilation and error on warnings + RUSTFLAGS: "-C debuginfo=0 -D warnings" + +jobs: + fmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Check formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check + + test: + name: Test + strategy: + matrix: + platform: + - { os: ubuntu-latest } # TODO: 32bit and gcc-multilib + - { os: macos-10.15 } + - { os: macos-11 } + # - { target: x86_64-apple-ios, os: macos-latest, } + # - { target: aarch64-apple-ios, os: macos-latest, } + + runs-on: ${{ matrix.platform.os }} + + steps: + - uses: actions/checkout@v2 + + - name: Cache Rust + uses: actions/cache@v2 + with: + path: | + ~/.cargo/ + target/ + key: ${{ matrix.platform.os }}-cargo-${{ hashFiles('**/Cargo.toml') }} + restore-keys: | + ${{ matrix.platform.os }}-cargo- + + - name: Install Packages + if: contains(matrix.platform.os, 'ubuntu') + run: sudo apt-get install gobjc clang make + + - name: Install different Rust toolchain + # A default toolchain is already installed + if: matrix.platform.target + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.platform.target }} + profile: minimal + override: true + + - name: Check documentation + uses: actions-rs/cargo@v1 + with: + # TODO: Disallow warnings here + command: doc + args: --no-deps --document-private-items + + - # Run before we install GNUStep as a "fail fast" mechanism + name: Run checks + uses: actions-rs/cargo@v1 + with: + command: check + args: --verbose + + - name: Install GNUStep libobjc2 + if: contains(matrix.platform.os, 'ubuntu') + run: | + wget https://github.com/gnustep/libobjc2/archive/refs/tags/v1.9.tar.gz + tar -xzf v1.9.tar.gz + mkdir libobjc2-1.9/build + cd libobjc2-1.9/build + export CC="clang" + export CXX="clang++" + cmake ../ + sudo make install + + - name: Install GNUStep make + if: contains(matrix.platform.os, 'ubuntu') + run: | + wget https://github.com/gnustep/tools-make/archive/refs/tags/make-2_9_0.tar.gz + tar -xzf make-2_9_0.tar.gz + cd tools-make-make-2_9_0 + ./configure --with-library-combo=ng-gnu-gnu + sudo make install + + - name: Install GNUStep-Base + if: contains(matrix.platform.os, 'ubuntu') + run: | + wget https://github.com/gnustep/libs-base/archive/refs/tags/base-1_28_0.tar.gz + tar -xzf base-1_28_0.tar.gz + cd libs-base-base-1_28_0 + ./configure --disable-tls + sudo make install + + - name: Setup environment + if: contains(matrix.platform.os, 'ubuntu') + run: | + ls -al /usr/local/lib + ls -al /usr/local/include + echo "LIBRARY_PATH=/usr/local/lib:$LIBRARY_PATH" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV + echo "CPATH=/usr/local/include:$CPATH" >> $GITHUB_ENV + + - name: Build and run tests + uses: actions-rs/cargo@v1 + with: + command: test + # TODO: `objc/exception` feature is broken in objc_foundation + # TODO: `objc_foundation/block` feature doesn't work on GNUStep + args: --verbose --no-fail-fast --no-default-features diff --git a/.travis-disabled.yml b/.travis-disabled.yml new file mode 100644 index 000000000..08d0abd4f --- /dev/null +++ b/.travis-disabled.yml @@ -0,0 +1,45 @@ +sudo: false +language: rust +cache: cargo +os: osx + +# Run on master and PRs +if: branch = master + +script: +- cargo test --workspace --verbose +- # TODO: cargo test --workspace --verbose --all-features + +jobs: + include: + - name: MacOS 10.11 + osx_image: xcode7.3 + + - name: MacOS 10.13 (with 32bit) + osx_image: xcode9.4 + rust: nightly + # 32-bit targets only have tier 3 support + install: rustup component add rust-src + script: + - cargo test --workspace --verbose + - # TODO: cargo test --workspace --verbose --all-features + - # objc_exception doesn't work on 32bit? + cargo test --workspace --exclude objc_exception --verbose -Z build-std --target i686-apple-darwin + - # TODO: cargo test --workspace --verbose --all-features -Z build-std --target i686-apple-darwin + + - name: MacOS 11.3 + osx_image: xcode12.5 + + - name: iOS nightly + osx_image: xcode7.3 + rust: nightly + before_install: rustup component add rust-src + # Install rust-test-ios + install: curl -LO https://github.com/SSheldon/rust-test-ios/releases/download/0.1.1/rust-test-ios && chmod +x rust-test-ios + before_script: + # Enable -Z build-std, 32-bit targets only have tier 3 support + - printf '[unstable]\nbuild-std = ["std"]\n' > $HOME/.cargo/config.toml + # Remove workspace since `rust-test-ios` is not made for that + - rm Cargo.toml + # TODO: env: FEATURES="exception" + script: cd objc && ../rust-test-ios diff --git a/Cargo.toml b/Cargo.toml index aa57a694a..47b27aed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ members = [ "objc_foundation", "objc_id", ] +exclude = ["objc/tests-ios"] diff --git a/objc/.travis.yml b/objc/.travis.yml deleted file mode 100644 index 20118f8a4..000000000 --- a/objc/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: rust -rust: - - stable - - nightly -os: - - osx - - linux -env: - - FEATURES="" IOS_ARCHS="" -matrix: - include: - - os: osx - rust: stable - env: FEATURES="exception verify_message" IOS_ARCHS="" - - os: osx - osx_image: xcode7.3 - rust: 1.41.0 - env: FEATURES="exception" IOS_ARCHS="i386 x86_64 armv7 armv7s aarch64" -sudo: false -install: ./travis_install.sh -before_script: > - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - export LIBRARY_PATH=$HOME/libobjc2_staging/lib:$LIBRARY_PATH; - export LD_LIBRARY_PATH=$HOME/libobjc2_staging/lib:$LD_LIBRARY_PATH; - fi -script: ./travis_test.sh -addons: - apt: - packages: - - clang - - cmake diff --git a/objc/Cargo.toml b/objc/Cargo.toml index 9c8523dd6..83eaa46e5 100644 --- a/objc/Cargo.toml +++ b/objc/Cargo.toml @@ -15,8 +15,6 @@ exclude = [ ".gitignore", ".travis.yml", "doc.sh", - "travis_install.sh", - "travis_test.sh", "tests-ios/**", ] diff --git a/objc/tests-ios/prelude.rs b/objc/tests-ios/prelude.rs index 8ec4c55ad..2eabd4c83 100644 --- a/objc/tests-ios/prelude.rs +++ b/objc/tests-ios/prelude.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate objc; + use objc::rc::*; use objc::runtime::*; pub use objc::*; diff --git a/objc/travis_install.sh b/objc/travis_install.sh deleted file mode 100755 index 13b0fc330..000000000 --- a/objc/travis_install.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /usr/bin/env sh - -set -eu - -gnustep_install() { - git clone -b 1.9 https://github.com/gnustep/libobjc2.git - mkdir libobjc2/build - cd libobjc2/build - export CC="clang" - export CXX="clang++" - cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/libobjc2_staging ../ - make install -} - -for arch in $IOS_ARCHS; do - rustup target add "${arch}-apple-ios" -done - -if [ -n "$IOS_ARCHS" ]; then - curl -LO https://github.com/SSheldon/rust-test-ios/releases/download/0.1.1/rust-test-ios - chmod +x rust-test-ios -fi - -if [ "$TRAVIS_OS_NAME" = "linux" ]; then - gnustep_install -fi diff --git a/objc/travis_test.sh b/objc/travis_test.sh deleted file mode 100755 index 1e0999389..000000000 --- a/objc/travis_test.sh +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env sh - -set -eu - -if [ -z "$IOS_ARCHS" ]; then - cargo build --verbose --features "$FEATURES" - cargo test --verbose --features "$FEATURES" -else - ./rust-test-ios -fi diff --git a/objc_exception/build.rs b/objc_exception/build.rs index a17b0cc09..e53b0b8c6 100644 --- a/objc_exception/build.rs +++ b/objc_exception/build.rs @@ -1,5 +1,6 @@ fn main() { cc::Build::new() .file("extern/exception.m") + .flag("-fobjc-exceptions") .compile("libexception.a"); } diff --git a/objc_exception/extern/exception.m b/objc_exception/extern/exception.m index 700439ecf..be3ea58e3 100644 --- a/objc_exception/extern/exception.m +++ b/objc_exception/extern/exception.m @@ -1,20 +1,17 @@ -#include -#include - -void RustObjCExceptionThrow(id exception) { - @throw exception; -} +// Always available in Objective-C +// See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-retain +id objc_retain(id value); int RustObjCExceptionTryCatch(void (*try)(void *), void *context, id *error) { @try { try(context); if (error) { - *error = nil; + *error = (id)0; // nil } return 0; } @catch (id exception) { if (error) { - *error = [exception retain]; + *error = objc_retain(exception); } return 1; } diff --git a/objc_exception/src/lib.rs b/objc_exception/src/lib.rs index 5b591257e..80098a24f 100644 --- a/objc_exception/src/lib.rs +++ b/objc_exception/src/lib.rs @@ -5,10 +5,17 @@ use std::os::raw::{c_int, c_void}; use std::ptr; #[link(name = "objc", kind = "dylib")] -extern "C" {} +// TODO: "C-unwind" +extern "C" { + /// See [`objc-exception.h`][objc-exception]. + /// + /// [objc-exception]: https://opensource.apple.com/source/objc4/objc4-818.2/runtime/objc-exception.h.auto.html + // Header marks this with _Nonnull, but LLVM output shows otherwise + fn objc_exception_throw(exception: *mut c_void) -> !; + // fn objc_exception_rethrow(); +} extern "C" { - fn RustObjCExceptionThrow(exception: *mut c_void); fn RustObjCExceptionTryCatch( r#try: extern "C" fn(*mut c_void), context: *mut c_void, @@ -26,9 +33,14 @@ pub enum Exception {} /// /// This unwinds from Objective-C, and the exception must be caught using an /// Objective-C exception handler. +/// +/// This also invokes undefined behaviour until `C-unwind` is stabilized, see +/// [RFC-2945]. +/// +/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html +#[inline] pub unsafe fn throw(exception: *mut Exception) -> ! { - RustObjCExceptionThrow(exception as *mut _); - unreachable!(); + objc_exception_throw(exception as *mut _) } unsafe fn try_no_ret(closure: F) -> Result<(), *mut Exception> diff --git a/objc_foundation/Cargo.toml b/objc_foundation/Cargo.toml index dd28da395..faa8dcf7b 100644 --- a/objc_foundation/Cargo.toml +++ b/objc_foundation/Cargo.toml @@ -12,7 +12,10 @@ license = "MIT" exclude = [".gitignore"] +[features] +default = ["block"] + [dependencies] -block = "0.1" +block = { optional = true, version = "0.1" } objc = { path = "../objc", version = "0.2.7" } objc_id = { path = "../objc_id", version = "0.1" } diff --git a/objc_foundation/src/data.rs b/objc_foundation/src/data.rs index c73d5fdec..8d377082a 100644 --- a/objc_foundation/src/data.rs +++ b/objc_foundation/src/data.rs @@ -1,9 +1,9 @@ -use std::mem; use std::ops::Range; use std::os::raw::c_void; use std::slice; use super::{INSCopying, INSMutableCopying, INSObject, NSRange}; +#[cfg(feature = "block")] use block::{Block, ConcreteBlock}; use objc::msg_send; use objc_id::Id; @@ -39,6 +39,7 @@ pub trait INSData: INSObject { } } + #[cfg(feature = "block")] fn from_vec(bytes: Vec) -> Id { let capacity = bytes.capacity(); let dealloc = ConcreteBlock::new(move |bytes: *mut c_void, len: usize| unsafe { @@ -56,7 +57,7 @@ pub trait INSData: INSObject { let obj: *mut Self = msg_send![obj, initWithBytesNoCopy:bytes_ptr length:bytes.len() deallocator:dealloc]; - mem::forget(bytes); + std::mem::forget(bytes); Id::from_retained_ptr(obj) } } @@ -192,6 +193,7 @@ mod tests { assert!(data.bytes() == [8, 17]); } + #[cfg(feature = "block")] #[test] fn test_from_vec() { let bytes = vec![3, 7, 16]; diff --git a/objc_foundation/src/lib.rs b/objc_foundation/src/lib.rs index 564c6de0f..da610293e 100644 --- a/objc_foundation/src/lib.rs +++ b/objc_foundation/src/lib.rs @@ -11,9 +11,30 @@ pub use self::object::{INSObject, NSObject}; pub use self::string::{INSCopying, INSMutableCopying, INSString, NSString}; pub use self::value::{INSValue, NSValue}; +#[cfg(any(target_os = "macos", target_os = "ios"))] #[link(name = "Foundation", kind = "framework")] extern "C" {} +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +use objc::runtime::Class; + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[link(name = "gnustep-base", kind = "dylib")] +extern "C" {} + +// Split up to illustrate that the linking doesn't have to be annotated on the +// correct `extern` block. +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +extern "C" { + static _OBJC_CLASS_NSObject: Class; +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[allow(dead_code)] +unsafe fn get_class_to_force_linkage() -> &'static Class { + &_OBJC_CLASS_NSObject +} + #[macro_use] mod macros; diff --git a/objc_foundation/src/string.rs b/objc_foundation/src/string.rs index 3c9d55ade..38bb80b47 100644 --- a/objc_foundation/src/string.rs +++ b/objc_foundation/src/string.rs @@ -84,6 +84,12 @@ impl fmt::Display for NSString { mod tests { use super::{INSCopying, INSString, NSString}; + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + #[test] + fn ensure_linkage() { + unsafe { crate::get_class_to_force_linkage() }; + } + #[test] fn test_utf8() { let expected = "ประเทศไทย中华Việt Nam"; diff --git a/objc_id/src/id.rs b/objc_id/src/id.rs index b5ed2829e..a7bf2e730 100644 --- a/objc_id/src/id.rs +++ b/objc_id/src/id.rs @@ -206,6 +206,12 @@ mod tests { use objc::runtime::Object; use objc::{class, msg_send}; + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + #[test] + fn ensure_linkage() { + unsafe { crate::get_class_to_force_linkage() }; + } + fn retain_count(obj: &Object) -> usize { unsafe { msg_send![obj, retainCount] } } diff --git a/objc_id/src/lib.rs b/objc_id/src/lib.rs index 2a82acf9a..b291bd086 100644 --- a/objc_id/src/lib.rs +++ b/objc_id/src/lib.rs @@ -11,7 +11,7 @@ which can be cloned to allow multiple references. Weak references may be created using the [`WeakId`](struct.WeakId.html) struct. -``` +```no_run # use objc::msg_send; use objc::runtime::{Class, Object}; use objc_id::{Id, WeakId}; @@ -41,3 +41,23 @@ assert!(weak.load().is_none()); pub use id::{Id, Owned, Ownership, ShareId, Shared, WeakId}; mod id; + +// TODO: Remove the need for this hack + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +use objc::runtime::Class; + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[link(name = "gnustep-base", kind = "dylib")] +extern "C" {} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +extern "C" { + static _OBJC_CLASS_NSObject: Class; +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[allow(dead_code)] +unsafe fn get_class_to_force_linkage() -> &'static Class { + &_OBJC_CLASS_NSObject +}