Skip to content

Commit 9c256cb

Browse files
committed
Add initial sys crate for blocks, improving GNUStep support
1 parent 4dd5852 commit 9c256cb

11 files changed

Lines changed: 185 additions & 35 deletions

File tree

.github/workflows/gnustep.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,17 @@ jobs:
101101
uses: actions-rs/cargo@v1
102102
with:
103103
command: check
104-
args: --verbose --no-default-features
104+
args: --verbose --no-default-features --features gnustep-1-9
105105

106-
- name: Test GNUStep
106+
- name: Test without features
107107
uses: actions-rs/cargo@v1
108108
with:
109109
command: test
110-
# Temporary fix
111-
args: --verbose --no-fail-fast --no-default-features --package objc2_sys --package objc2 --package objc2_encode --package objc2_exception --package objc2_foundation
110+
args: --verbose --no-fail-fast --no-default-features --features gnustep-1-9
112111

113-
- name: Test GNUStep with features
112+
- name: Test with features
114113
uses: actions-rs/cargo@v1
115114
with:
116115
command: test
117-
# Temporary fix
118-
args: --verbose --no-fail-fast --no-default-features --features exception,verify_message --package objc2_sys --package objc2 --package objc2_encode --package objc2_exception --package objc2_foundation
116+
# Not using --all-features because some features are nightly-only
117+
args: --verbose --no-fail-fast --features gnustep-1-9,block,exception,verify_message

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
members = [
33
"objc2",
44
"objc2_block",
5+
"objc2_block_sys",
56
"objc2_encode",
67
"objc2_exception",
78
"objc2_foundation",

objc2_block/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ exclude = [
2323

2424
[dependencies]
2525
objc2_encode = { path = "../objc2_encode" }
26+
objc2_block_sys = { path = "../objc2_block_sys" }
2627

2728
[dev-dependencies]
2829
objc2_test_utils = { path = "../objc2_test_utils" }

objc2_block/src/lib.rs

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,9 @@ use core::ops::{Deref, DerefMut};
6363
use core::ptr;
6464
use std::os::raw::{c_int, c_ulong};
6565

66+
pub use objc2_block_sys as ffi;
6667
use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
6768

68-
// TODO: Replace with `objc2::Class`
69-
#[repr(C)]
70-
struct ClassInternal {
71-
_priv: [u8; 0],
72-
}
73-
74-
#[cfg_attr(target_vendor = "apple", link(name = "System", kind = "dylib"))]
75-
#[cfg_attr(
76-
not(target_vendor = "apple"),
77-
link(name = "BlocksRuntime", kind = "dylib")
78-
)]
79-
extern "C" {
80-
static _NSConcreteStackBlock: ClassInternal;
81-
82-
fn _Block_copy(block: *const c_void) -> *mut c_void;
83-
fn _Block_release(block: *const c_void);
84-
}
85-
8669
/// Types that may be used as the arguments to an Objective-C block.
8770
pub trait BlockArguments: Sized {
8871
/// Calls the given `Block` with self as the arguments.
@@ -151,7 +134,7 @@ block_args_impl!(
151134

152135
#[repr(C)]
153136
struct BlockBase<A, R> {
154-
isa: *const ClassInternal,
137+
isa: *const ffi::Class,
155138
flags: c_int,
156139
_reserved: c_int,
157140
invoke: unsafe extern "C" fn(*mut Block<A, R>, ...) -> R,
@@ -207,7 +190,7 @@ impl<A, R> RcBlock<A, R> {
207190
///
208191
/// The given pointer must point to a valid `Block`.
209192
pub unsafe fn copy(ptr: *mut Block<A, R>) -> Self {
210-
let ptr = _Block_copy(ptr as *const c_void) as *mut Block<A, R>;
193+
let ptr = ffi::_Block_copy(ptr as *const c_void) as *mut Block<A, R>;
211194
RcBlock { ptr }
212195
}
213196
}
@@ -229,7 +212,7 @@ impl<A, R> Deref for RcBlock<A, R> {
229212
impl<A, R> Drop for RcBlock<A, R> {
230213
fn drop(&mut self) {
231214
unsafe {
232-
_Block_release(self.ptr as *const c_void);
215+
ffi::_Block_release(self.ptr as *const c_void);
233216
}
234217
}
235218
}
@@ -397,7 +380,7 @@ impl<A, R, F> ConcreteBlock<A, R, F> {
397380
unsafe fn with_invoke(invoke: unsafe extern "C" fn(*mut Self, ...) -> R, closure: F) -> Self {
398381
ConcreteBlock {
399382
base: BlockBase {
400-
isa: &_NSConcreteStackBlock,
383+
isa: &ffi::_NSConcreteStackBlock,
401384
// 1 << 25 = BLOCK_HAS_COPY_DISPOSE
402385
flags: 1 << 25,
403386
_reserved: 0,

objc2_block_sys/Cargo.toml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[package]
2+
name = "objc2_block_sys"
3+
version = "0.0.0" # Remember to update html_root_url in lib.rs
4+
authors = ["Mads Marquart <mads@marquart.dk>"]
5+
edition = "2018"
6+
7+
description = "Raw bindings to Apple's C language extension of blocks"
8+
keywords = ["objective-c", "macos", "ios", "blocks", "sys"]
9+
categories = [
10+
"external-ffi-bindings",
11+
# "no_std", # TODO
12+
"os::macos-apis",
13+
]
14+
repository = "https://github.com/madsmtm/objc2"
15+
documentation = "https://docs.rs/objc2_block_sys/"
16+
license = "MIT"
17+
18+
# readme = "README.md"
19+
20+
# Downstream users can customize the linking!
21+
# See https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts
22+
links = "block"
23+
build = "build.rs"
24+
25+
[features]
26+
# Link to Apple's libclosure (exists in libSystem)
27+
# See https://opensource.apple.com/source/libclosure/
28+
# Accessible mirror https://github.com/madsmtm/libclosure
29+
#
30+
# This is the default on Apple platforms
31+
apple = []
32+
33+
# Link to libBlocksRuntime from compiler-rt
34+
# See https://github.com/llvm/llvm-project/tree/release/13.x/compiler-rt/lib/BlocksRuntime
35+
#
36+
# This is the default on non-Apple platforms
37+
compiler-rt = []
38+
39+
# Link to GNUStep's libobjc2 (which contains the block implementation)
40+
gnustep-1-7 = ["objc2_sys"]
41+
gnustep-1-8 = ["gnustep-1-7"]
42+
gnustep-1-9 = ["gnustep-1-8"]
43+
gnustep-2-0 = ["gnustep-1-9"]
44+
gnustep-2-1 = ["gnustep-2-0"]
45+
46+
# Link to Microsoft's libobjc2
47+
winobjc = ["gnustep-1-8"]
48+
49+
# TODO
50+
objfw = []
51+
52+
[dependencies]
53+
objc2_sys = { path = "../objc2_sys", optional = true }

objc2_block_sys/build.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use std::env;
2+
use std::path::Path;
3+
4+
fn main() {
5+
// Only rerun if this file changes; the script doesn't depend on our code
6+
println!("cargo:rerun-if-changed=build.rs");
7+
8+
let mut apple = env::var_os("CARGO_FEATURE_APPLE").is_some();
9+
let mut compiler_rt = env::var_os("CARGO_FEATURE_COMPILER_RT").is_some();
10+
let gnustep = env::var_os("CARGO_FEATURE_GNUSTEP_1_7").is_some();
11+
let objfw = env::var_os("CARGO_FEATURE_OBJFW").is_some();
12+
13+
if let (false, false, false, false) = (apple, compiler_rt, gnustep, objfw) {
14+
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
15+
if let "macos" | "ios" | "tvos" | "watchos" = &*target_os {
16+
apple = true;
17+
// Add cheaty #[cfg(feature = "apple")] directive
18+
println!("cargo:rustc-cfg=feature=\"apple\"");
19+
} else {
20+
compiler_rt = true;
21+
// Add cheaty #[cfg(feature = "compiler-rt")] directive
22+
println!("cargo:rustc-cfg=feature=\"compiler-rt\"");
23+
}
24+
}
25+
26+
let mut cc_args = "-fblocks".to_owned();
27+
28+
match (apple, compiler_rt, gnustep, objfw) {
29+
(true, false, false, false) => {
30+
// Link to libclosure (internally called libsystem_blocks), which is
31+
// exported by libSystem.dylib.
32+
//
33+
// Note that System.framework is just a deprecated wrapper over the
34+
// dynamic library.
35+
println!("cargo:rustc-link-lib=dylib=System");
36+
// Alternative: Only link to libsystem_blocks.dylib
37+
// println!("cargo:rustc-link-search=native=/usr/lib/system");
38+
// println!("cargo:rustc-link-lib=dylib=system_blocks");
39+
}
40+
(false, true, false, false) => {
41+
println!("cargo:rustc-link-lib=dylib=BlocksRuntime");
42+
}
43+
(false, false, true, false) => {
44+
// Don't link to anything; objc2_sys already does that for us!
45+
46+
// Add GNUStep compability headers to make `#include <Block.h>`
47+
// work (on newer GNUStep versions these headers are present)
48+
if !env::var_os("CARGO_FEATURE_GNUSTEP_2_0").is_some() {
49+
let compat_headers =
50+
Path::new(env!("CARGO_MANIFEST_DIR")).join("gnustep-compat-headers");
51+
cc_args.push_str("-I");
52+
cc_args.push_str(compat_headers.to_str().unwrap());
53+
}
54+
}
55+
(false, false, false, true) => unimplemented!(),
56+
// Checked in if-let above
57+
(false, false, false, false) => unreachable!(),
58+
(_, _, _, _) => panic!("Invalid feature combination; only one runtime may be selected!"),
59+
}
60+
61+
// Add DEP_BLOCK_CC_ARGS
62+
println!("cargo:cc_args={}", cc_args);
63+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <objc/blocks_runtime.h>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <objc/blocks_private.h>

objc2_block_sys/src/lib.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//!
2+
//! See:
3+
//! - https://github.com/apple/swift-corelibs-libdispatch/tree/main/src/BlocksRuntime
4+
//! - https://clang.llvm.org/docs/BlockLanguageSpec.html
5+
//! - https://clang.llvm.org/docs/Block-ABI-Apple.html
6+
7+
// Update in Cargo.toml as well.
8+
#![doc(html_root_url = "https://docs.rs/objc2_block_sys/0.0.0")]
9+
10+
use core::ffi::c_void;
11+
12+
#[repr(C)]
13+
pub struct Class {
14+
#[cfg(any(feature = "apple", feature = "compiler-rt"))]
15+
_priv: [*mut c_void; 32],
16+
#[cfg(any(feature = "gnustep-1-7", feature = "objfw"))]
17+
// The size of this is unknown
18+
_priv: [u8; 0],
19+
}
20+
21+
extern "C" {
22+
// the raw data space for runtime classes for blocks
23+
// class+meta used for stack, malloc, and collectable based blocks
24+
25+
pub static _NSConcreteGlobalBlock: Class;
26+
pub static _NSConcreteStackBlock: Class;
27+
pub static _NSConcreteMallocBlock: Class;
28+
29+
pub fn _Block_copy(block: *const c_void) -> *mut c_void;
30+
pub fn _Block_release(block: *const c_void);
31+
32+
/// Runtime entry point called by compiler when assigning objects inside
33+
/// copy helper routines
34+
pub fn _Block_object_assign(dest_addr: *mut c_void, object: *const c_void, flags: i32);
35+
36+
/// runtime entry point called by the compiler when disposing of objects
37+
/// inside dispose helper routine
38+
pub fn _Block_object_dispose(object: *const c_void, flags: i32);
39+
}

objc2_test_utils/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ license = "MIT"
1818

1919
build = "build.rs"
2020

21+
[dependencies]
22+
objc2_block_sys = { path = "../objc2_block_sys" }
23+
2124
[build-dependencies]
2225
cc = "1.0"

0 commit comments

Comments
 (0)