objc_sys/lib.rs
1//! # Raw bindings to Objective-C runtimes
2//!
3//! These bindings contain almost no documentation, so it is highly
4//! recommended to read Apple's [documentation about the Objective-C
5//! runtime][runtime-guide], Apple's [runtime reference][apple], or to use
6//! `objc2::runtime` which provides a higher-level API.
7//!
8//! [runtime-guide]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
9//! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
10//!
11//!
12//! ## Runtime Support
13//!
14//! Objective-C has a runtime, different implementations of said runtime
15//! exist, and they act in slightly different ways. By default, Apple
16//! platforms link to Apple's runtime, but if you're using another runtime you
17//! must tell it to this library using feature flags (you might have to
18//! disable the default `apple` feature first).
19//!
20//! One could ask, why even bother supporting other runtimes? For me, the
21//! primary reasoning iss _robustness_. By testing with these alternative
22//! runtimes in CI, we become by extension much more confident that our
23//! implementation doesn't rely on brittle unspecified behaviour, and works
24//! across different macOS and iOS versions.
25//!
26//!
27//! ### Apple's [`objc4`](https://github.com/apple-oss-distributions/objc4)
28//!
29//! - Feature flag: `apple`.
30//!
31//! This is used by default, and has the highest support priority (all of
32//! `objc2` will work with this runtime).
33//!
34//! The supported runtime version (higher versions lets the compiler enable
35//! newer optimizations, at the cost of not supporting older operating
36//! systems) can be chosen using the standard `X_DEPLOYMENT_TARGET`
37//! environment variables:
38//!
39//! - macOS: `MACOSX_DEPLOYMENT_TARGET`, default `10.12`, `11.0` on Aarch64.
40//! - iOS / iPadOS: `IPHONEOS_DEPLOYMENT_TARGET`, default `10.0`.
41//! - tvOS: `TVOS_DEPLOYMENT_TARGET`, default `10.0`.
42//! - watchOS: `WATCHOS_DEPLOYMENT_TARGET`, default `5.0`.
43//!
44//! The default (and minimum) versions are the [same as those Rust itself
45//! has][rust-apple-spec].
46//!
47//! [rust-apple-spec]: https://github.com/rust-lang/rust/blob/1.74.0/compiler/rustc_target/src/spec/apple_base.rs
48//!
49//!
50//! ### GNUStep's [`libobjc2`](https://github.com/gnustep/libobjc2)
51//!
52//! - Feature flag: `gnustep-1-7`, `gnustep-1-8`, `gnustep-1-9`, `gnustep-2-0`
53//! and `gnustep-2-1` depending on the version you're using.
54//!
55//!
56//! ### Microsoft's [`WinObjC`](https://github.com/microsoft/WinObjC)
57//!
58//! - Feature flag: `unstable-winobjc`.
59//!
60//! **Unstable: Hasn't been tested on Windows yet!**
61//!
62//! [A fork](https://github.com/microsoft/libobjc2) based on GNUStep's
63//! `libobjc2` version 1.8, with very few user-facing changes.
64//!
65//!
66//! ### [`ObjFW`](https://github.com/ObjFW/ObjFW)
67//!
68//! - Feature flag: `unstable-objfw`.
69//!
70//! **Unstable: Doesn't work yet!**
71//!
72//! TODO.
73//!
74//!
75//! ### Other runtimes
76//!
77//! This library will probably only ever support ["Modern"][modern]
78//! Objective-C runtimes, since support for reference-counting primitives like
79//! `objc_retain` and `objc_autoreleasePoolPop` is a vital requirement for
80//! most applications.
81//!
82//! This rules out the GCC [`libobjc`][gcc-libobjc] runtime (see
83//! [this][gcc-objc-support]), the [`mulle-objc`] runtime and [cocotron]. (But
84//! support for [`darling`] may be added). More information on different
85//! runtimes can be found in GNUStep's [Objective-C Compiler and Runtime
86//! FAQ][gnustep-faq].
87//!
88//! [modern]: https://en.wikipedia.org/wiki/Objective-C#Modern_Objective-C
89//! [gcc-libobjc]: https://github.com/gcc-mirror/gcc/tree/master/libobjc
90//! [gcc-objc-support]: https://gcc.gnu.org/onlinedocs/gcc/Standards.html#Objective-C-and-Objective-C_002b_002b-Languages
91//! [`mulle-objc`]: https://github.com/mulle-objc/mulle-objc-runtime
92//! [cocotron]: https://cocotron.org/
93//! [`darling`]: https://github.com/darlinghq/darling-objc4
94//! [gnustep-faq]: http://wiki.gnustep.org/index.php/Objective-C_Compiler_and_Runtime_FAQ
95//!
96//!
97//! ## Advanced linking configuration
98//!
99//! This crate defines the `links` key in `Cargo.toml` so it's possible to
100//! change the linking to `libobjc`, see [the relevant cargo
101//! docs][overriding].
102//!
103//! In the future, this crate may vendor the required source code to
104//! automatically build and link to the runtimes. Choosing static vs. dynamic
105//! linking here may also become an option.
106//!
107//! [overriding]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts
108//!
109//!
110//! ## Objective-C Compiler configuration
111//!
112//! Objective-C compilers like `clang` and `gcc` requires configuring the
113//! calling ABI to the runtime you're using:
114//! - `clang` uses the [`-fobjc-runtime`] flag, of which there are a few
115//! different [options][clang-objc-kinds].
116//! - `gcc` uses the [`-fgnu-runtime` or `-fnext-runtime`][gcc-flags] options.
117//! Note that Modern Objective-C features are ill supported.
118//!
119//! This is relevant if you're building and linking to custom Objective-C
120//! sources in a build script. To assist in compiling Objective-C sources,
121//! this crate's build script expose the `DEP_OBJC_0_3_CC_ARGS` environment
122//! variable to downstream build scripts.
123//!
124//! Example usage in your `build.rs` (using the `cc` crate) would be as
125//! follows:
126//!
127//! ```ignore
128//! fn main() {
129//! let mut builder = cc::Build::new();
130//! builder.compiler("clang");
131//! builder.file("my_objective_c_script.m");
132//!
133//! for flag in std::env::var("DEP_OBJC_0_3_CC_ARGS").unwrap().split(' ') {
134//! builder.flag(flag);
135//! }
136//!
137//! builder.compile("libmy_objective_c_script.a");
138//! }
139//! ```
140//!
141//! [`-fobjc-runtime`]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fobjc-runtime
142//! [clang-objc-kinds]: https://clang.llvm.org/doxygen/classclang_1_1ObjCRuntime.html#af19fe070a7073df4ecc666b44137c4e5
143//! [gcc-flags]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html
144//!
145//!
146//! ## Design choices
147//!
148//! It is recognized that the most primary consumer of this library will be
149//! macOS and secondly iOS applications. Therefore it was chosen not to use
150//! `bindgen`[^1] in our build script to not add compilation cost to those
151//! targets.
152//!
153//! Deprecated functions are also not included for future compability, since
154//! they could be removed in any macOS release, and then our code would break.
155//! If you have a need for these, please open an issue and we can discuss it!
156//!
157//! Some items (in particular the `objc_msgSend_X` family) have `cfg`s that
158//! prevent their usage on different platforms; these are **semver-stable** in
159//! the sense that they will only get less restrictive, never more.
160//!
161//! [^1]: That said, most of this is created with the help of `bindgen`'s
162//! commandline interface, so huge thanks to them!
163
164#![no_std]
165#![warn(clippy::missing_errors_doc)]
166#![warn(clippy::missing_panics_doc)]
167#![allow(clippy::upper_case_acronyms)]
168#![allow(non_camel_case_types)]
169#![allow(non_upper_case_globals)]
170#![allow(non_snake_case)]
171#![allow(missing_debug_implementations)]
172#![doc(html_root_url = "https://docs.rs/objc-sys/0.3.5")]
173#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
174#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide))]
175#![cfg_attr(docsrs, doc(cfg_hide(doc)))]
176
177// TODO: Remove this and add "no-std" category to Cargo.toml
178// Requires a better solution for C-types in `no_std` crates.
179// See https://github.com/japaric/cty/issues/14.
180extern crate std;
181
182#[cfg(not(feature = "std"))]
183compile_error!("The `std` feature currently must be enabled.");
184
185use core::cell::UnsafeCell;
186use core::marker::{PhantomData, PhantomPinned};
187
188macro_rules! generate_linking_tests {
189 {
190 extern $abi:literal {$(
191 $(#[$m:meta])*
192 $v:vis fn $name:ident(
193 $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
194 ) $(-> $r:ty)?;
195 )+}
196 mod $test_name:ident;
197 } => {
198 extern $abi {$(
199 $(#[$m])*
200 $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
201 )+}
202
203 #[allow(deprecated)]
204 #[cfg(test)]
205 mod $test_name {
206 #[allow(unused)]
207 use super::*;
208
209 $(
210 $(#[$m])*
211 #[test]
212 fn $name() {
213 // Get function pointer to make the linker require the
214 // symbol to be available.
215 let f: unsafe extern $abi fn($($(#[$a_m])* $t),*) $(-> $r)? = crate::$name;
216 // Workaround for https://github.com/rust-lang/rust/pull/92964
217 #[cfg(feature = "unstable-c-unwind")]
218 #[allow(clippy::useless_transmute)]
219 let f: unsafe extern "C" fn() = unsafe { core::mem::transmute(f) };
220 // Execute side-effect to ensure it is not optimized away.
221 std::println!("{:p}", f);
222 }
223 )+
224 }
225 };
226}
227
228macro_rules! extern_c {
229 {
230 $(
231 $(#[$m:meta])*
232 $v:vis fn $name:ident(
233 $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
234 ) $(-> $r:ty)?;
235 )+
236 } => {
237 generate_linking_tests! {
238 extern "C" {$(
239 $(#[$m])*
240 $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
241 )+}
242 mod test_linkable;
243 }
244 };
245}
246
247// A lot of places may call `+initialize`, but the runtime guards those calls
248// with `@try/@catch` blocks already, so we don't need to mark every function
249// "C-unwind", only certain ones!
250macro_rules! extern_c_unwind {
251 {
252 $(
253 $(#[$m:meta])*
254 $v:vis fn $name:ident(
255 $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
256 ) $(-> $r:ty)?;
257 )+
258 } => {
259 #[cfg(not(feature = "unstable-c-unwind"))]
260 generate_linking_tests! {
261 extern "C" {$(
262 $(#[$m])*
263 $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
264 )+}
265 mod test_linkable_unwind;
266 }
267
268 #[cfg(feature = "unstable-c-unwind")]
269 generate_linking_tests! {
270 extern "C-unwind" {$(
271 $(#[$m])*
272 $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
273 )+}
274 mod test_linkable_unwind;
275 }
276 };
277}
278
279mod class;
280mod constants;
281mod exception;
282mod image_info;
283mod libc;
284mod message;
285mod method;
286mod object;
287mod property;
288mod protocol;
289mod rc;
290mod selector;
291mod types;
292mod various;
293
294pub use self::class::*;
295pub use self::constants::*;
296pub use self::exception::*;
297pub use self::image_info::*;
298pub use self::libc::*;
299pub use self::message::*;
300pub use self::method::*;
301pub use self::object::*;
302pub use self::property::*;
303pub use self::protocol::*;
304pub use self::rc::*;
305pub use self::selector::*;
306pub use self::types::*;
307pub use self::various::*;
308
309/// We don't know much about the actual structs, so better mark them `!Send`,
310/// `!Sync`, `!UnwindSafe`, `!RefUnwindSafe`, `!Unpin` and as mutable behind
311/// shared references.
312///
313/// Downstream libraries can always manually opt in to these types afterwards.
314/// (It's also less of a breaking change on our part if we re-add these).
315///
316/// TODO: Replace this with `extern type` to also mark it as `!Sized`.
317type OpaqueData = UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>;
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use std::ffi::CStr;
323
324 #[test]
325 fn smoke() {
326 // Verify that this library links and works fine by itself
327 let name = CStr::from_bytes_with_nul(b"abc:def:\0").unwrap();
328 let sel = unsafe { sel_registerName(name.as_ptr()) };
329 let rtn = unsafe { CStr::from_ptr(sel_getName(sel)) };
330 assert_eq!(name, rtn);
331 }
332}