diff --git a/CHANGELOG.md b/CHANGELOG.md index 432d8242d0..a8d3ec33df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ ## [[UnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) +### What's new? + +- Added the `uniffi-bindgen-swift` binary. It works like `uniffi-bindgen` but with additional + Swift-specific features. See + https://mozilla.github.io/uniffi-rs/latest/swift/uniffi-bindgen-swift.html for details. + +- Removed the [old and outdated diplomat comparison](https://github.com/mozilla/uniffi-rs/blob/69ecfbd7fdf587a4ab24d1234e2d6afb8a496581/docs/diplomat-and-macros.md) doc + +### What's fixed? + +- `uniffi.toml` of crates without a `lib` type where ignored in 0.28.1 +- Python: Fixed a bug when enum/error names were not proper camel case (HTMLError instead of HtmlError). + [All changes in [[UnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.1...HEAD). ## v0.28.1 (backend crates: v0.28.1) - (_2024-08-09_) diff --git a/Cargo.lock b/Cargo.lock index 18c01bc4a5..b3c8376b24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1969,7 +1969,6 @@ dependencies = [ "anyhow", "async-compat", "bytes", - "camino", "log", "once_cell", "paste", diff --git a/README.md b/README.md index 64ac3486a3..50348a976e 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,7 @@ FFI - [Abbreviation, Foreign Function Interface] Other tools we know of which try and solve a similarly shaped problem are: -* [Diplomat](https://github.com/rust-diplomat/diplomat/) - see our [writeup of - the different approach taken by that tool](docs/diplomat-and-macros.md) +* [Diplomat](https://github.com/rust-diplomat/diplomat/), which is focused more on C/C++ interop. * [Interoptopus](https://github.com/ralfbiedert/interoptopus/) (Please open a PR if you think other tools should be listed!) diff --git a/docs/diplomat-and-macros.md b/docs/diplomat-and-macros.md deleted file mode 100644 index 21ed30144b..0000000000 --- a/docs/diplomat-and-macros.md +++ /dev/null @@ -1,275 +0,0 @@ -# Comparing UniFFI with Diplomat - -[Diplomat](https://github.com/rust-diplomat/diplomat/) and [UniFFI](https://github.com/mozilla/uniffi-rs/) -are both tools which expose a rust implemented API over an FFI. -At face value, these tools are solving the exact same problem, but their approach -is significantly different. - -This document attempts to describe these different approaches and discuss the pros and cons of each. -It's not going to try and declare one better than the other, but instead just note how they differ. -If you are reading this hoping to find an answer to "what one should I use?", then that's easy - -each tool currently supports a unique set of foreign language bindings, so the tool you should -use is the one that supports the languages you care about! - -Disclaimer: This document was written by one of the UniFFI developers, who has never used -diplomat in anger. Please feel free to open PRs if anything here misrepresents diplomat. - -See also: This document was discussed in [this PR](https://github.com/mozilla/uniffi-rs/pull/1146), -which has some very interesting discussion - indeed, some of the content in this document has -been copy-pasted from that discussion, but there's further detail there which might be -of interest. - -# The type systems - -The key difference between these 2 tools is the "type system". While both are exposing Rust -code (which obviously comes with its own type system), the foreign bindings need to know -lots of details about all the types expressed by the tool. - -For the sake of this document, we will use the term "type universe" to define the set of -all types known by each of the tools. Both of these tools build their own "type universe" then -use that to generate both Rust code and foreign bindings. - -## UniFFI's type universe - -UniFFI's model is to parse an external ffi description from a `.udl` file which describes the -entire "type universe". This type universe is then used to generate both the Rust scaffolding -(on disk as a `.rs` file) and the foreign bindings. - -**What's good about this** is that the entire type system is known when generating both the rust code -and the foreign binding, and is known without parsing any Rust code. This is important because -things like field names and types in structs must be known on both sides of the FFI. - -**What's bad about this** is that the external UDL is very ugly and redundant in terms of the -implemented rust API. - -## Diplomat's type universe - -Diplomat defines its "type universe" (ie, the external ffi) using macros. - -**What's good about this** is that an "ffi module" (and there may be many) defines the canonical API -and it is defined in terms of Rust types - the redundant UDL is removed. -The Rust scaffolding can also be generated by the macros, meaning there are no generated `.rs` -files involved. Types can be shared among any of the ffi modules defined in the project - -for example, [this diplomat ffi module](https://github.com/unicode-org/icu4x/blob/7d9f89fcd7df4567e17ddd8c46810b0db287436a/ffi/diplomat/src/pluralrules.rs#L50-L51) -uses types from a [different ffi module](https://github.com/unicode-org/icu4x/blob/7d9f89fcd7df4567e17ddd8c46810b0db287436a/ffi/diplomat/src/locale.rs#L19). - -Restricting the definition of the FFI to a single module instead of allowing that definition -to appear in any Rust code in the crate also offers better control over the stability of the API, -because where the FFI is defined is constrained. This is -[an explicit design decision](https://github.com/rust-diplomat/diplomat/blob/main/docs/design_doc.md#requirements) of diplomat. - -While the process for defining the type universe is different, the actual in-memory -representation of that type universe isn't radically different from UniFFI - for example, -here's the [definition of a Rust struct](https://github.com/rust-diplomat/diplomat/blob/main/core/src/ast/structs.rs), -and while it is built from a `syn` struct, the final representation is independent of `syn` -and its ast representation. - -## UniFFI's experience with the macro approach. - -Ryan tried this same macro approach for UniFFI in [#416](https://github.com/mozilla/uniffi-rs/pull/416) - -but we struck a limitation in this approach for UniFFI's use-cases - the context in which the -macro runs doesn't know about types defined outside of that macro, which are what we need to -expose. - -### Example of this limitation - -Let's look at diplomat's simple example: - -```rust -#[diplomat::bridge] -mod ffi { - pub struct MyFfiType { - pub a: i32, - pub b: bool, - } - - impl MyFfiType { - pub fn create() -> MyFfiType { ... } - ... - } -} -``` - -This works fine, but starts to come unstuck if you want the types defined somewhere else. In this trivial example, something like: - -```Rust -pub struct MyFfiType { - pub a: i32, - pub b: bool, -} - -#[diplomat::bridge] -mod ffi { - impl MyFfiType { - pub fn create() -> MyFfiType { ... } - ... - } -} -``` - -fails - diplomat can't handle this scenario - in the same way and for the same reasons that Ryan's -[#416](https://github.com/mozilla/uniffi-rs/pull/416) can't - the contents of the struct aren't known. - -From the Rust side of the world, this is probably solvable by sprinkling more macros around - eg, something like: - -```Rust -#[uniffi::magic] -pub struct MyFfiType { - pub a: i32, - pub b: bool, -} -``` - -might be enough for the generation of the Rust scaffolding - in UniFFI's case, all we really need -is an implementation of `uniffi::RustBufferViaFfi` which is easy to derive, and UniFFI can -generate code which assumes that exists much like it does now. -However, the problems are in the foreign bindings, because those foreign bindings do not know -the names and types of the struct elements without re-parsing every bit of Rust code with those -annotations. As discussed below, re-parsing this code might be an option if we help Uniffi to -find it, but asking UniFFI to parse this and all dependent crates to auto-discover them -probably is not going to be viable. - -### Why is this considered a limitation for UniFFI but not diplomat? - -As mentioned above, diplomat considers the limitation described above as an intentional design -feature. By limiting where FFI types can be described, there's no risk of changes made "far away" -from the FFI to change the FFI. This was born of experience in tools like `cbindgen`. - -For Uniffi, all use-cases needed by Mozilla don't share this design goal, primarily because the -FFI is the primary consumer of the crate. The Rust API exists purely to service the FFI. It's not -really possible to accidentally change the API, because every API change made will be in service -of exposing that change over the FFI. The test suites written in the foreign languages are -considered canonical. - -## How the type universe is constructed for the macro approach. - -In both diplomat and [#416](https://github.com/mozilla/uniffi-rs/pull/416), the approach taken -is very similar - it takes a path to a the Rust source file/tree, and uses `syn` to locate the special modules (ie, ones annotated with `#[diplomat:bridge]` in the case of diplomat.) - -While some details differ, this is just a matter of implementation - #416 isn't quite as aggressive -about consuming the entire crate to find multiple FFI modules (and even then, diplomat doesn't -actually *process* the entire crate, just modules tagged as a bridge), but could easily be -extended to do so. - -But in both cases, for our problematic example above, this process never sees the layout of the -`MyFfiType` struct because it's not inside the processed module, so that layout can't be -communicated to the foreign bindings. -As noted above, this is considered a feature for diplomat, but a limitation for UniFFI. - -This is the problem which caused us to decide to stop working on -[#416](https://github.com/mozilla/uniffi-rs/pull/416) - the current world where the type universe -is described externally doesn't have this limitation - only the UDL file needs to be parsed when -generating the foreign bindings. The application-services team has -concluded that none of our non-trivial use-cases for UniFFI could be described using macros, -so supporting both mechanisms is pain for no gain. - -As noted in #416, `wasm-bindgen` has a similarly shaped problem, and solves it by having -the Rust macro arrange for the resulting library to have an extra data section with the -serialized "type universe" - foreign binding generation would then read this information from the -already built binary. This sounds more complex than the UniFFI team has appetite for at -the current time. - -# Looking forward - -## Adapting the diplomat/[#416](https://github.com/mozilla/uniffi-rs/pull/416) model to process the entire crate? - -We noted that diplomat intentionally restricts where the ffi is generated, -whereas UniFFI considers that a limitation - but what if we can teach UniFFI to process -more of the Rust crate? - -It might be reasonable for the foreign bindings to know that Rust "paths" to modules which should -be processed, and inside those modules find structs "marked up" as being used by the FFI. - -In other words, borrowing the example above: - -```Rust -#[uniffi::magic] -pub struct MyFfiType { - pub a: i32, - pub b: bool, -} -``` - -maybe can be made to work, so long as we are happy to help UniFFI discover where such annotations -may exist. - -A complication here is that currently UniFFI allows types defined in external crates, -but that might still be workable - eg, -[diplomat has an issue open to support exactly this](https://github.com/rust-diplomat/diplomat/issues/34) - -## Duplicating structs inside Rust - -When reviewing the draft of this document, @rfk noted that we are already duplicating Rust structs -in UDL and in Rust. So instead of having: - -``` -// In a UDL file: -dictionary MyFfiType { - i32 a; - bool b; -}; -// Then in Rust: -pub struct MyFfiType { - pub a: i32, - pub b: bool, -} -``` - -we could have: -```rust -// In the Rust implementation, in some other module. -pub struct MyFfiType { - pub a: i32, - pub b: bool, -} - -// And to expose it over the FFI: -#[ffi::something] -mod ffi { - - #[ffi::magic_external_type_declaration] - pub struct MyFfiType { - pub a: i32, - pub b: bool, - } - - impl MyFfiType { - pub fn create() -> MyFfiType { ... } - ... - } -} -``` - -So while we haven't exactly reduced the duplication, we have removed the UDL. -We probably also haven't helped with documentation, because the natural location for -the documentation of `MyFfiType` is probably at the *actual* implementation. - -While it might not solve all our problems, it is worthy of serious consideration - fewer problems -is still a worthwhile goal, and needing a UDL file and parser seems like one worth removing. - -## Try and share some definitions with diplomat - -We note above that the type universe described by diplomat is somewhat "leaner" than that -described by UniFFI, but in general they are very similar. Thus, there might be a future where -merging or otherwise creating some interoperability between these type universes might make -sense. - -It seems likely that this would start to add unwelcome constraints - eg, diplomat would not want -its ability to refactor type representations limited by what UniFFI needs. - -However, what you could see happening in the future is UniFFI becoming a kind of higher-level -wrapper around Diplomat. You can imagine a Diplomat backend for UniFFI that converts a .udl file -into a bridge module and then uses the Diplomat toolchain to generate bindings from it, -keeping some of the additional affordances/conveniences UniFFI built for its specific use-cases. - -# Next steps for UniFFI - -As much as some of the UniFFI team dislike the external UDL file, there's no clear path to -moving away from it. We could experiment with some of the options above and see if they are -both viable and worth the investment for the UniFFI use-cases. That sounds like a long-term -goal. - -In the short term, the best we can probably do is to enumerate the perceived problems -with the UDL file and try to make them more ergonomic - for example, avoiding repetition of -`[Throws=SomeError]` would remove alot of noise, and some strategy for generating -documentation might go a long way. diff --git a/docs/manual/src/swift/configuration.md b/docs/manual/src/swift/configuration.md index cc2fd92d41..3965c5a7fa 100644 --- a/docs/manual/src/swift/configuration.md +++ b/docs/manual/src/swift/configuration.md @@ -13,7 +13,7 @@ more likely to change than other configurations. | `module_name` | `{namespace}`[^1] | The name of the Swift module containing the high-level foreign-language bindings. | | `ffi_module_name` | `{module_name}FFI` | The name of the lower-level C module containing the FFI declarations. | | `ffi_module_filename` | `{ffi_module_name}` | The filename stem for the lower-level C module containing the FFI declarations. | -| `generate_module_map` | `true` | Whether to generate a `.modulemap` file for the lower-level C module with FFI declarations. | +| `generate_module_map` | `true` | Whether to generate a `.modulemap` file for the lower-level C module with FFI declarations. (ignored by `uniffi-bindgen-swift`) | | `omit_argument_labels` | `false` | Whether to omit argument labels in Swift function definitions. | | `generate_immutable_records` | `false` | Whether to generate records with immutable fields (`let` instead of `var`). | | `experimental_sendable_value_types` | `false` | Whether to mark value types as `Sendable'. | diff --git a/docs/manual/src/swift/overview.md b/docs/manual/src/swift/overview.md index 374cd0e1c5..af19c401a1 100644 --- a/docs/manual/src/swift/overview.md +++ b/docs/manual/src/swift/overview.md @@ -25,20 +25,10 @@ Concepts from the UDL file map into Swift as follows: * If this happens inside a non-throwing Swift function, it will be converted into a fatal Swift error that cannot be caught. -Conceptually, the generated bindings are split into two Swift modules, one for the low-level -C FFI layer and one for the higher-level Swift bindings. For a UniFFI component named "example" -we generate: +## Generated files -* A C header file `exampleFFI.h` declaring the low-level structs and functions for calling - into Rust, along with a corresponding `exampleFFI.modulemap` to expose them to Swift. -* A Swift source file `example.swift` that imports the `exampleFFI` module and wraps it - to provide the higher-level Swift API. +UniFFI generates several kinds of files for Swift bindings: -Splitting up the bindings in this way gives you flexibility over how both the Rust code -and the Swift code are distributed to consumers. For example, you may choose to compile -and distribute the Rust code for several UniFFI components as a single shared library -in order to reduce the compiled code size, while distributing their Swift wrappers as -individual modules. - -For more technical details on how the bindings work internally, please see the -[module documentation](https://docs.rs/uniffi_bindgen/latest/uniffi_bindgen/bindings/swift/index.html) +* C header files declaring the FFI structs/functions used by the Rust scaffolding code +* A modulemap, which defines a Swift module for the C FFI definitions in the header file. +* A Swift source file that defines the Swift API used by consumers. This imports the FFI module. diff --git a/docs/manual/src/swift/uniffi-bindgen-swift.md b/docs/manual/src/swift/uniffi-bindgen-swift.md new file mode 100644 index 0000000000..ab293657b3 --- /dev/null +++ b/docs/manual/src/swift/uniffi-bindgen-swift.md @@ -0,0 +1,48 @@ +# uniffi-bindgen-swift + +Swift bindings can be generated like other languages using `uniffi-bindgen -l swift`. However, you +can also use the `uniffi-bindgen-swift` binary which gives greater control over Swift-specific +features: + +* Select which kind of files to generate: headers, modulemaps, and/or Swift sources. +* Generate a single modulemap for a library. +* Generate XCFramework-compatible modulemaps. +* Customize the modulemap module name. +* Customize the modulemap filename. + +`uniffi-bindgen-swift` can be added to your project using the same general steps as `uniffi-bindgen`. +See https://mozilla.github.io/uniffi-rs/latest/tutorial/foreign_language_bindings.html#creating-the-bindgen-binary. +The Rust source for the binary should be: + +``` +fn main() { + uniffi::uniffi_bindgen_swift() +} +``` + +`uniffi-bindgen-swift` always inputs a library path and runs in "library mode". This means +proc-macro-based bindings generation is always supported. + +## Examples: + + +Generate .swift source files for a library +``` +cargo run -p uniffi-bindgen-swift -- target/release/mylibrary.a build/swift --swift-sources +``` + +Generate .h files for a library +``` +cargo run -p uniffi-bindgen-swift -- target/release/mylibrary.a build/swift/Headers --headers +``` + + +Generate a modulemap +``` +cargo run -p uniffi-bindgen-swift -- target/release/mylibrary.a build/swift/Modules --modulemap --modulemap-filename mymodule.modulemap +``` + +Generate a Xcframework-compatible modulemap +``` +cargo run -p uniffi-bindgen-swift -- target/release/mylibrary.a build/swift/Modules --xcframework --modulemap --modulemap-filename mymodule.modulemap +``` diff --git a/fixtures/callbacks/tests/bindings/test_callbacks.kts b/fixtures/callbacks/tests/bindings/test_callbacks.kts index ecd5284d10..321c80af59 100644 --- a/fixtures/callbacks/tests/bindings/test_callbacks.kts +++ b/fixtures/callbacks/tests/bindings/test_callbacks.kts @@ -141,3 +141,11 @@ listOf(1, 2).forEach { v -> assert(expected == observed) { "callback is sent on construction: $expected != $observed" } } rustStringifier.destroy() + +// `stringifier` must remain valid after `rustStringifier2` drops the reference +val stringifier = StoredKotlinStringifier() +val rustStringifier1 = RustStringifier(stringifier) +val rustStringifier2 = RustStringifier(stringifier) +assert("kotlin: 123" == rustStringifier2.fromSimpleType(123)) +rustStringifier2.destroy() +assert("kotlin: 123" == rustStringifier1.fromSimpleType(123)) diff --git a/fixtures/callbacks/tests/bindings/test_callbacks.py b/fixtures/callbacks/tests/bindings/test_callbacks.py index d7ed2627e3..37c7c507e9 100644 --- a/fixtures/callbacks/tests/bindings/test_callbacks.py +++ b/fixtures/callbacks/tests/bindings/test_callbacks.py @@ -130,4 +130,14 @@ def test_complex_errors(self): rust_getters.get_option(callback, "unexpected-error", True) self.assertEqual(cm.exception.reason, repr(ValueError("unexpected value"))) +class TestCallbackLifetime(unittest.TestCase): + def test_callback_reference_does_not_invalidate_other_references(self): + # `stringifier` must remain valid after `rust_stringifier_2` drops the reference + stringifier = StoredPythonStringifier() + rust_stringifier_1 = RustStringifier(stringifier) + rust_stringifier_2 = RustStringifier(stringifier) + assert("python: 123" == rust_stringifier_2.from_simple_type(123)) + del rust_stringifier_2 + assert("python: 123" == rust_stringifier_1.from_simple_type(123)) + unittest.main() diff --git a/fixtures/callbacks/tests/bindings/test_callbacks.swift b/fixtures/callbacks/tests/bindings/test_callbacks.swift index 7c4d6e55d2..78adf31424 100644 --- a/fixtures/callbacks/tests/bindings/test_callbacks.swift +++ b/fixtures/callbacks/tests/bindings/test_callbacks.swift @@ -103,6 +103,16 @@ do { assert(expected == observed, "callback is sent on construction: \(expected) != \(observed)") } + do { + // `stringifier` must remain valid after `rustStringifier2` drops the reference + let stringifier = StoredSwiftStringifier() + let rustStringifier1 = RustStringifier(callback: stringifier) + do { + let rustStringifier2 = RustStringifier(callback: stringifier) + assert("swift: 123" == rustStringifier2.fromSimpleType(value: 123)) + } + assert("swift: 123" == rustStringifier1.fromSimpleType(value: 123)) + } // 3. Error handling do { diff --git a/fixtures/coverall/src/coverall.udl b/fixtures/coverall/src/coverall.udl index ce8d9fdb79..4b4f4e5b1c 100644 --- a/fixtures/coverall/src/coverall.udl +++ b/fixtures/coverall/src/coverall.udl @@ -36,6 +36,9 @@ namespace coverall { Getters test_round_trip_through_rust(Getters getters); void test_round_trip_through_foreign(Getters getters); + // Always throws an InvalidHTML error + [Throws=HTMLError] + void validate_html(string source); }; dictionary SimpleDict { @@ -295,3 +298,10 @@ interface ISecond { }; dictionary EmptyStruct {}; + +// Error class with a controversial camel case. This can cause errors if we don't consistently +// convert the name. +[Error] +enum HTMLError { + "InvalidHTML" +}; diff --git a/fixtures/coverall/src/lib.rs b/fixtures/coverall/src/lib.rs index 76d4639fbd..d4f5f70061 100644 --- a/fixtures/coverall/src/lib.rs +++ b/fixtures/coverall/src/lib.rs @@ -606,4 +606,14 @@ impl ISecond { pub struct EmptyStruct; +#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)] +pub enum HTMLError { + #[error("InvalidHTML")] + InvalidHTML, +} + +pub fn validate_html(_source: String) -> Result<(), HTMLError> { + Err(HTMLError::InvalidHTML) +} + uniffi::include_scaffolding!("coverall"); diff --git a/fixtures/coverall/tests/bindings/test_coverall.kts b/fixtures/coverall/tests/bindings/test_coverall.kts index ba7a0538f3..83a47d12b1 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.kts +++ b/fixtures/coverall/tests/bindings/test_coverall.kts @@ -541,6 +541,17 @@ Coveralls("using_fakes_with_real_objects_crashes").use { coveralls -> assert(exception != null) } +Coveralls("HtmlError").use { coveralls -> + var exception: Throwable? = null + try { + validateHtml("test") + } catch (e: HtmlException.InvalidHtml) { + exception = e + } + assert(exception != null) +} + + // This is from an earlier GC test; ealier, we made 1000 new objects. // By now, the GC has had time to clean up, and now we should see 0 alive. // (hah! Wishful-thinking there ;) diff --git a/fixtures/coverall/tests/bindings/test_coverall.py b/fixtures/coverall/tests/bindings/test_coverall.py index 0c7697f3f7..06ea12ab85 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.py +++ b/fixtures/coverall/tests/bindings/test_coverall.py @@ -454,5 +454,9 @@ def test_rust_only_traits(self): self.assertEqual(traits[0].concat("cow", "boy"), "cowboy") self.assertEqual(traits[1].concat("cow", "boy"), "cowboy") + def test_html_error(self): + with self.assertRaises(HtmlError): + validate_html("test") + if __name__=='__main__': unittest.main() diff --git a/fixtures/coverall/tests/bindings/test_coverall.rb b/fixtures/coverall/tests/bindings/test_coverall.rb index 4fe1140419..6160650ea5 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.rb +++ b/fixtures/coverall/tests/bindings/test_coverall.rb @@ -259,4 +259,10 @@ def test_bytes assert_equal coveralls.reverse("123").encoding, Encoding::BINARY end + def test_html_error + assert_raise Coverall::HtmlError::InvalidHtml do + Coverall.validate_html("test") + end + end + end diff --git a/fixtures/coverall/tests/bindings/test_coverall.swift b/fixtures/coverall/tests/bindings/test_coverall.swift index 91e95342cd..084d4a5897 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.swift +++ b/fixtures/coverall/tests/bindings/test_coverall.swift @@ -479,3 +479,13 @@ do { assert(stringUtils[0].concat(a: "cow", b: "boy") == "cowboy") assert(stringUtils[1].concat(a: "cow", b: "boy") == "cowboy") } + +// Test HTMLError +do { + try validateHtml(source: "test") + fatalError("should have thrown") +} catch HtmlError.InvalidHtml { + // Expected +} catch { + fatalError("Unexpected error: \(error)") +} diff --git a/mkdocs.yml b/mkdocs.yml index 9a9de54a9c..789adf479c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -69,6 +69,7 @@ nav: - 'Swift': - ./swift/overview.md + - ./swift/uniffi-bindgen-swift.md - ./swift/configuration.md - ./swift/module.md - ./swift/xcode.md diff --git a/uniffi/src/cli/mod.rs b/uniffi/src/cli/mod.rs new file mode 100644 index 0000000000..ab4bb0d356 --- /dev/null +++ b/uniffi/src/cli/mod.rs @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +mod swift; +mod uniffi_bindgen; + +pub fn uniffi_bindgen_main() { + if let Err(e) = uniffi_bindgen::run_main() { + eprintln!("{e}"); + std::process::exit(1); + } +} + +pub fn uniffi_bindgen_swift() { + if let Err(e) = swift::run_main() { + eprintln!("{e}"); + std::process::exit(1); + } +} diff --git a/uniffi/src/cli/swift.rs b/uniffi/src/cli/swift.rs new file mode 100644 index 0000000000..a74cd9a7ef --- /dev/null +++ b/uniffi/src/cli/swift.rs @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use anyhow::Result; +use camino::Utf8PathBuf; +use clap::{Args, Parser}; + +use uniffi_bindgen::bindings::{generate_swift_bindings, SwiftBindingsOptions}; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(flatten)] + kinds: Kinds, + /// Library path to generate bindings for + library_path: Utf8PathBuf, + /// Directory to generate files in + out_dir: Utf8PathBuf, + /// Generate a XCFramework-compatible modulemap + #[arg(long)] + xcframework: bool, + /// module name for the generated modulemap + #[arg(long)] + module_name: Option, + /// filename for the generate modulemap + #[arg(long)] + modulemap_filename: Option, + /// Whether we should exclude dependencies when running "cargo metadata". + /// This will mean external types may not be resolved if they are implemented in crates + /// outside of this workspace. + /// This can be used in environments when all types are in the namespace and fetching + /// all sub-dependencies causes obscure platform specific problems. + #[clap(long)] + metadata_no_deps: bool, +} + +#[derive(Debug, Args)] +#[group(required = true, multiple = true)] +struct Kinds { + /// Generate swift files + #[arg(long)] + swift_sources: bool, + + /// Generate header files + #[arg(long)] + headers: bool, + + /// Generate modulemap + #[arg(long)] + modulemap: bool, +} + +pub fn run_main() -> Result<()> { + let cli = Cli::parse(); + generate_swift_bindings(cli.into()) +} + +impl From for SwiftBindingsOptions { + fn from(cli: Cli) -> Self { + Self { + generate_swift_sources: cli.kinds.swift_sources, + generate_headers: cli.kinds.headers, + generate_modulemap: cli.kinds.modulemap, + library_path: cli.library_path, + out_dir: cli.out_dir, + xcframework: cli.xcframework, + module_name: cli.module_name, + modulemap_filename: cli.modulemap_filename, + metadata_no_deps: cli.metadata_no_deps, + } + } +} diff --git a/uniffi/src/cli.rs b/uniffi/src/cli/uniffi_bindgen.rs similarity index 100% rename from uniffi/src/cli.rs rename to uniffi/src/cli/uniffi_bindgen.rs diff --git a/uniffi/src/lib.rs b/uniffi/src/lib.rs index d49e89e584..42b94c0438 100644 --- a/uniffi/src/lib.rs +++ b/uniffi/src/lib.rs @@ -10,6 +10,10 @@ mod cli; #[cfg(feature = "bindgen-tests")] pub use uniffi_bindgen::bindings::{kotlin_test, python_test, ruby_test, swift_test}; +#[cfg(all(feature = "cargo-metadata", feature = "bindgen"))] +pub use uniffi_bindgen::cargo_metadata::CrateConfigSupplier as CargoMetadataConfigSupplier; +#[cfg(feature = "bindgen")] +pub use uniffi_bindgen::library_mode::generate_bindings as generate_bindings_library_mode; #[cfg(feature = "bindgen")] pub use uniffi_bindgen::{ bindings::{ @@ -24,9 +28,7 @@ pub use uniffi_build::{generate_scaffolding, generate_scaffolding_for_crate}; pub use uniffi_macros::build_foreign_language_testcases; #[cfg(feature = "cli")] -pub fn uniffi_bindgen_main() { - cli::run_main().unwrap(); -} +pub use cli::*; #[cfg(test)] mod test { diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt index c6b266066d..21546a1624 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterBoolean: FfiConverter { override fun lift(value: Byte): Boolean { return value.toInt() != 0 diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt index c9449069e2..3f1a34a337 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterByteArray: FfiConverterRustBuffer { override fun read(buf: ByteBuffer): ByteArray { val len = buf.getInt() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt b/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt index d58a651e24..7f30bc02fd 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt @@ -6,6 +6,9 @@ internal const val UNIFFI_CALLBACK_SUCCESS = 0 internal const val UNIFFI_CALLBACK_ERROR = 1 internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2 +/** + * @suppress + */ public abstract class FfiConverterCallbackInterface: FfiConverter { internal val handleMap = UniffiHandleMap() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt index d2cdee4f33..6f9b98aa7c 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt @@ -9,5 +9,9 @@ {% include "Interface.kt" %} {% include "CallbackInterfaceImpl.kt" %} -// The ffiConverter which transforms the Callbacks in to handles to pass to Rust. +/** + * The ffiConverter which transforms the Callbacks in to handles to pass to Rust. + * + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverterCallbackInterface<{{ interface_name }}>() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt index 123535e5c8..9b7d3ac72e 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt @@ -33,6 +33,9 @@ public typealias {{ type_name }} = {{ concrete_type_name }} {%- else %} {%- endmatch %} +/** + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverter<{{ type_name }}, {{ ffi_type_name }}> { override fun lift(value: {{ ffi_type_name }}): {{ type_name }} { val builtinValue = {{ builtin|lift_fn }}(value) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt index 62e02607f3..522d925b50 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterDuration: FfiConverterRustBuffer { override fun read(buf: ByteBuffer): java.time.Duration { // Type mismatch (should be u64) but we check for overflow/underflow below diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index 1c17799633..4e4153fa3f 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -27,6 +27,9 @@ enum class {{ type_name }}(val value: {{ variant_discr_type|type_name(ci) }}) { } {% endmatch %} +/** + * @suppress + */ public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> { override fun read(buf: ByteBuffer) = try { {% if config.use_enum_entries() %} @@ -84,6 +87,9 @@ sealed class {{ type_name }}{% if contains_object_references %}: Disposable {% e companion object } +/** + * @suppress + */ public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }}>{ override fun read(buf: ByteBuffer): {{ type_name }} { return when(buf.getInt()) { diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt index 8c08e765fe..8f517ffeb6 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt @@ -54,6 +54,9 @@ sealed class {{ type_name }}: kotlin.Exception(){% if contains_object_references } {%- endif %} +/** + * @suppress + */ public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }}> { override fun read(buf: ByteBuffer): {{ type_name }} { {% if e.is_flat() %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt index 0de90b9c4b..e1c6696de1 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt @@ -1,7 +1,11 @@ -// The FfiConverter interface handles converter types to and from the FFI -// -// All implementing objects should be public to support external types. When a -// type is external we need to import it's FfiConverter. +/** + * The FfiConverter interface handles converter types to and from the FFI + * + * All implementing objects should be public to support external types. When a + * type is external we need to import it's FfiConverter. + * + * @suppress + */ public interface FfiConverter { // Convert an FFI type to a Kotlin type fun lift(value: FfiType): KotlinType @@ -64,7 +68,11 @@ public interface FfiConverter { } } -// FfiConverter that uses `RustBuffer` as the FfiType +/** + * FfiConverter that uses `RustBuffer` as the FfiType + * + * @suppress + */ public interface FfiConverterRustBuffer: FfiConverter { override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt index be91ac8fcb..45f1fb9198 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterFloat: FfiConverter { override fun lift(value: Float): Float { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt index 5eb465f0df..81b736ac73 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterDouble: FfiConverter { override fun lift(value: Double): Double { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt index ca9d98189e..245cc164de 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt @@ -36,7 +36,11 @@ internal open class UniffiRustCallStatus : Structure() { class InternalException(message: String) : kotlin.Exception(message) -// Each top-level error class has a companion object that can lift the error from the call status's rust buffer +/** + * Each top-level error class has a companion object that can lift the error from the call status's rust buffer + * + * @suppress + */ interface UniffiRustCallStatusErrorHandler { fun lift(error_buf: RustBuffer.ByValue): E; } @@ -73,7 +77,11 @@ private fun uniffiCheckCallStatus(errorHandler: UniffiRustC } } -// UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR +/** + * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + * + * @suppress + */ object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler { override fun lift(error_buf: RustBuffer.ByValue): InternalException { RustBuffer.free(error_buf) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt index de8296fff6..728b8ac65b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterShort: FfiConverter { override fun lift(value: Short): Short { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt index 171809a9c4..c6e1d6f74b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterInt: FfiConverter { override fun lift(value: Int): Int { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt index 35cf8f3169..1d1f65e88e 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterLong: FfiConverter { override fun lift(value: Long): Long { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt index 27c98a6659..85d1cd97f7 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterByte: FfiConverter { override fun lift(value: Byte): Byte { return value diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt index a80418eb00..459c932aab 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt @@ -1,5 +1,9 @@ {%- let key_type_name = key_type|type_name(ci) %} {%- let value_type_name = value_type|type_name(ci) %} + +/** + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverterRustBuffer> { override fun read(buf: ByteBuffer): Map<{{ key_type_name }}, {{ value_type_name }}> { val len = buf.getInt() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt index e3e85544d7..75986163e8 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt @@ -1,10 +1,14 @@ -// The cleaner interface for Object finalization code to run. -// This is the entry point to any implementation that we're using. -// -// The cleaner registers objects and returns cleanables, so now we are -// defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the -// different implmentations available at compile time. +/** + * The cleaner interface for Object finalization code to run. + * This is the entry point to any implementation that we're using. + * + * The cleaner registers objects and returns cleanables, so now we are + * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the + * different implmentations available at compile time. + * + * @suppress + */ interface UniffiCleaner { interface Cleanable { fun clean() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index 72fdbe5648..837b7251c3 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -262,6 +262,9 @@ open class {{ impl_class_name }}: Disposable, AutoCloseable, {{ interface_name } {% include "CallbackInterfaceImpl.kt" %} {%- endif %} +/** + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverter<{{ type_name }}, Pointer> { {%- if obj.has_callback_interface() %} internal val handleMap = UniffiHandleMap<{{ type_name }}>() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt index 98451e1451..aa46ab3088 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt @@ -1,5 +1,8 @@ {%- let inner_type_name = inner_type|type_name(ci) %} +/** + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverterRustBuffer<{{ inner_type_name }}?> { override fun read(buf: ByteBuffer): {{ inner_type_name }}? { if (buf.get().toInt() == 0) { diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index bc3028c736..223e62c144 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -36,6 +36,9 @@ class {{ type_name }} { } {%- endif %} +/** + * @suppress + */ public object {{ rec|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> { override fun read(buf: ByteBuffer): {{ type_name }} { {%- if rec.has_fields() %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt index b28f25bfc3..c12c386d7c 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt @@ -2,6 +2,9 @@ // A rust-owned buffer is represented by its capacity, its current length, and a // pointer to the underlying data. +/** + * @suppress + */ @Structure.FieldOrder("capacity", "len", "data") open class RustBuffer : Structure() { // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. @@ -54,6 +57,8 @@ open class RustBuffer : Structure() { * Required for callbacks taking in an out pointer. * * Size is the sum of all values in the struct. + * + * @suppress */ class RustBufferByReference : ByReference(16) { /** @@ -88,7 +93,7 @@ class RustBufferByReference : ByReference(16) { // completeness. @Structure.FieldOrder("len", "data") -open class ForeignBytes : Structure() { +internal open class ForeignBytes : Structure() { @JvmField var len: Int = 0 @JvmField var data: Pointer? = null diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt index 61f911cb0c..dab8f22d88 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt @@ -1,5 +1,8 @@ {%- let inner_type_name = inner_type|type_name(ci) %} +/** + * @suppress + */ public object {{ ffi_converter_name }}: FfiConverterRustBuffer> { override fun read(buf: ByteBuffer): List<{{ inner_type_name }}> { val len = buf.getInt() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt index b67435bd1a..ab532421a0 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterString: FfiConverter { // Note: we don't inherit from FfiConverterRustBuffer, because we use a // special encoding when lowering/lifting. We can use `RustBuffer.len` to diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt index 10a450a4bd..217abac566 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterTimestamp: FfiConverterRustBuffer { override fun read(buf: ByteBuffer): java.time.Instant { val seconds = buf.getLong() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt index c27121b701..3555c5f8eb 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt @@ -18,6 +18,9 @@ interface Disposable { } } +/** + * @suppress + */ inline fun T.use(block: (T) -> R) = try { block(this) @@ -30,7 +33,11 @@ inline fun T.use(block: (T) -> R) = } } -/** Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. */ +/** + * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. + * + * @suppress + * */ object NoPointer {%- for type_ in ci.iter_types() %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt index b179145b62..b16a66506e 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterUShort: FfiConverter { override fun lift(value: Short): UShort { return value.toUShort() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt index 202d5bcd5b..c1c6eb62cd 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterUInt: FfiConverter { override fun lift(value: Int): UInt { return value.toUInt() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt index 9be2a5a69d..18e09a4c85 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterULong: FfiConverter { override fun lift(value: Long): ULong { return value.toULong() diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt index ee360673e0..7b3eae80f8 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt @@ -1,3 +1,6 @@ +/** + * @suppress + */ public object FfiConverterUByte: FfiConverter { override fun lift(value: Byte): UByte { return value.toUByte() diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index 2d6cdc07fb..f76dea50b6 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -14,7 +14,7 @@ pub use python::PythonBindingGenerator; mod ruby; pub use ruby::RubyBindingGenerator; mod swift; -pub use swift::SwiftBindingGenerator; +pub use swift::{generate_swift_bindings, SwiftBindingGenerator, SwiftBindingsOptions}; #[cfg(feature = "bindgen-tests")] pub use self::{ diff --git a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs index 9c93965e35..4bc09522fd 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs @@ -22,7 +22,7 @@ impl CodeType for CallbackInterfaceCodeType { } fn canonical_name(&self) -> String { - format!("CallbackInterface{}", self.id) + format!("Type{}", self.type_label()) } fn literal(&self, _literal: &Literal) -> String { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs index 0818e7198b..8782339170 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs @@ -21,6 +21,6 @@ impl CodeType for CustomCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.name) + format!("Type{}", self.type_label()) } } diff --git a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs index 83ce177e07..a6ceaee1f1 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs @@ -22,7 +22,7 @@ impl CodeType for EnumCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.id) + format!("Type{}", self.type_label()) } fn literal(&self, literal: &Literal) -> String { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/error.rs b/uniffi_bindgen/src/bindings/python/gen_python/error.rs index aa1c0db75e..19f6fb0781 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/error.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/error.rs @@ -22,7 +22,7 @@ impl CodeType for ErrorCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.id) + format!("Type{}", self.type_label()) } fn literal(&self, _literal: &Literal) -> String { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/uniffi_bindgen/src/bindings/python/gen_python/external.rs index 0a46251d6d..c98738eba4 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/external.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/external.rs @@ -21,6 +21,6 @@ impl CodeType for ExternalCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.name) + format!("Type{}", self.type_label()) } } diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index b831274963..0064fae6e2 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -519,6 +519,10 @@ impl VisitMut for PythonCodeOracle { //TODO: Renaming the function name in wrapper.py is not currently tested function.rename(self.fn_name(function.name())); } + + fn visit_error_name(&self, name: &mut String) { + *name = self.class_name(name); + } } trait AsCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/object.rs b/uniffi_bindgen/src/bindings/python/gen_python/object.rs index 1165bb0e54..f137de3287 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/object.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/object.rs @@ -22,7 +22,7 @@ impl CodeType for ObjectCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.id) + format!("Type{}", self.type_label()) } fn literal(&self, _literal: &Literal) -> String { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/record.rs b/uniffi_bindgen/src/bindings/python/gen_python/record.rs index df00f98e8b..6fe3e7a2f8 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/record.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/record.rs @@ -22,7 +22,7 @@ impl CodeType for RecordCodeType { } fn canonical_name(&self) -> String { - format!("Type{}", self.id) + format!("Type{}", self.type_label()) } fn literal(&self, _literal: &Literal) -> String { diff --git a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs index c81495a8c6..4d1440262b 100644 --- a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs @@ -267,7 +267,6 @@ impl Config { } /// Generate UniFFI component bindings for Swift, as strings in memory. -/// pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result { let header = BridgingHeader::new(config, ci) .render() @@ -277,7 +276,7 @@ pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result Result Result { + BridgingHeader::new(config, ci) + .render() + .context("failed to render Swift bridging header") +} + +/// Generate the swift source for a component +pub fn generate_swift(config: &Config, ci: &ComponentInterface) -> Result { + SwiftWrapper::new(config.clone(), ci) + .render() + .context("failed to render Swift library") +} + +/// Generate the modulemap for a set of components +pub fn generate_modulemap( + module_name: String, + header_filenames: Vec, + xcframework: bool, +) -> Result { + ModuleMap { + module_name, + header_filenames, + xcframework, + } + .render() + .context("failed to render Swift library") +} + /// Renders Swift helper code for all types /// /// This template is a bit different than others in that it stores internal state from the render @@ -369,14 +397,19 @@ impl<'config, 'ci> BridgingHeader<'config, 'ci> { /// so that it can be imported by the higher-level code in from [`SwiftWrapper`]. #[derive(Template)] #[template(syntax = "c", escape = "none", path = "ModuleMapTemplate.modulemap")] -pub struct ModuleMap<'config, 'ci> { - config: &'config Config, - _ci: &'ci ComponentInterface, +pub struct ModuleMap { + module_name: String, + header_filenames: Vec, + xcframework: bool, } -impl<'config, 'ci> ModuleMap<'config, 'ci> { - pub fn new(config: &'config Config, _ci: &'ci ComponentInterface) -> Self { - Self { config, _ci } +impl ModuleMap { + pub fn new_for_single_component(config: &Config, _ci: &ComponentInterface) -> Self { + Self { + module_name: config.ffi_module_name(), + header_filenames: vec![config.header_filename()], + xcframework: false, + } } } diff --git a/uniffi_bindgen/src/bindings/swift/mod.rs b/uniffi_bindgen/src/bindings/swift/mod.rs index 27d2bcf0a3..9f69ea68d9 100644 --- a/uniffi_bindgen/src/bindings/swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/mod.rs @@ -30,12 +30,13 @@ //! use crate::{BindingGenerator, Component, GenerationSettings}; -use anyhow::Result; +use anyhow::{Context, Result}; +use camino::Utf8PathBuf; use fs_err as fs; use std::process::Command; mod gen_swift; -use gen_swift::{generate_bindings, Config}; +use gen_swift::{generate_bindings, generate_header, generate_modulemap, generate_swift, Config}; #[cfg(feature = "bindgen-tests")] pub mod test; @@ -121,3 +122,98 @@ impl BindingGenerator for SwiftBindingGenerator { Ok(()) } } + +/// Generate Swift bindings +/// +/// This is used by the uniffi-bindgen-swift command, which supports Swift-specific options. +/// +/// In the future, we may want to replace the generalized `uniffi-bindgen` with a set of +/// specialized `uniffi-bindgen-[language]` commands. +pub fn generate_swift_bindings(options: SwiftBindingsOptions) -> Result<()> { + #[cfg(feature = "cargo-metadata")] + let config_supplier = { + use crate::cargo_metadata::CrateConfigSupplier; + let mut cmd = cargo_metadata::MetadataCommand::new(); + if options.metadata_no_deps { + cmd.no_deps(); + } + let metadata = cmd.exec().context("error running cargo metadata")?; + CrateConfigSupplier::from(metadata) + }; + #[cfg(not(feature = "cargo-metadata"))] + let config_supplier = crate::EmptyCrateConfigSupplier; + + fs::create_dir_all(&options.out_dir)?; + + let mut components = + crate::library_mode::find_components(&options.library_path, &config_supplier)? + // map the TOML configs into a our Config struct + .into_iter() + .map(|Component { ci, config }| { + let config = SwiftBindingGenerator.new_config(&config.into())?; + Ok(Component { ci, config }) + }) + .collect::>>()?; + SwiftBindingGenerator + .update_component_configs(&GenerationSettings::default(), &mut components)?; + + for Component { ci, config } in &components { + if options.generate_swift_sources { + let source_file = options + .out_dir + .join(format!("{}.swift", config.module_name())); + fs::write(&source_file, generate_swift(config, ci)?)?; + } + + if options.generate_headers { + let header_file = options.out_dir.join(config.header_filename()); + fs::write(header_file, generate_header(config, ci)?)?; + } + } + + // find the library name by stripping the extension and leading `lib` from the library path + let library_name = { + let stem = options + .library_path + .file_stem() + .with_context(|| format!("Invalid library path {}", options.library_path))?; + match stem.strip_prefix("lib") { + Some(name) => name, + None => stem, + } + }; + + let module_name = options + .module_name + .unwrap_or_else(|| library_name.to_string()); + let modulemap_filename = options + .modulemap_filename + .unwrap_or_else(|| format!("{library_name}.modulemap")); + + if options.generate_modulemap { + let mut header_filenames: Vec<_> = components + .iter() + .map(|Component { config, .. }| config.header_filename()) + .collect(); + header_filenames.sort(); + let modulemap_source = + generate_modulemap(module_name, header_filenames, options.xcframework)?; + let modulemap_path = options.out_dir.join(modulemap_filename); + fs::write(modulemap_path, modulemap_source)?; + } + + Ok(()) +} + +#[derive(Debug)] +pub struct SwiftBindingsOptions { + pub generate_swift_sources: bool, + pub generate_headers: bool, + pub generate_modulemap: bool, + pub library_path: Utf8PathBuf, + pub out_dir: Utf8PathBuf, + pub xcframework: bool, + pub module_name: Option, + pub modulemap_filename: Option, + pub metadata_no_deps: bool, +} diff --git a/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift b/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift index 465e519628..c5a67e71c9 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterBool : FfiConverter { typealias FfiType = Int8 typealias SwiftType = Bool diff --git a/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift index 7aa1cca9b2..2ea0d35a5f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift @@ -12,27 +12,45 @@ {% include "CallbackInterfaceImpl.swift" %} // FfiConverter protocol for callback interfaces +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct {{ ffi_converter_name }} { fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>() } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif extension {{ ffi_converter_name }} : FfiConverter { typealias SwiftType = {{ type_name }} typealias FfiType = UInt64 +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lift(_ handle: UInt64) throws -> SwiftType { try handleMap.get(handle: handle) } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { let handle: UInt64 = try readInt(&buf) return try lift(handle) } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lower(_ v: SwiftType) -> UInt64 { return handleMap.insert(obj: v) } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func write(_ v: SwiftType, into buf: inout [UInt8]) { writeInt(&buf, lower(v)) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift b/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift index 2137ceff9c..d82ece70ac 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift @@ -7,6 +7,10 @@ * is needed because the UDL type name is used in function/method signatures. */ public typealias {{ type_name }} = {{ builtin|type_name }} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct FfiConverterType{{ name }}: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} { return try {{ builtin|read_fn }}(from: &buf) @@ -46,6 +50,9 @@ public typealias {{ type_name }} = {{ concrete_type_name }} {%- else %} {%- endmatch %} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct FfiConverterType{{ name }}: FfiConverter { {#- Custom type config supplied, use it to convert the builtin type #} @@ -76,10 +83,16 @@ public struct FfiConverterType{{ name }}: FfiConverter { We always write these public functions just incase the type is used as an external type by another crate. #} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func FfiConverterType{{ name }}_lift(_ value: {{ ffi_type_name }}) throws -> {{ type_name }} { return try FfiConverterType{{ name }}.lift(value) } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func FfiConverterType{{ name }}_lower(_ value: {{ type_name }}) -> {{ ffi_type_name }} { return FfiConverterType{{ name }}.lower(value) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift b/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift index 7db240bf9c..b017c19faf 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterData: FfiConverterRustBuffer { typealias SwiftType = Data diff --git a/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift b/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift index c2aa49e9d1..d729bf0844 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterDuration: FfiConverterRustBuffer { typealias SwiftType = TimeInterval diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index 1d8b3cf500..94da4efecd 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -22,6 +22,9 @@ public enum {{ type_name }} : {{ variant_discr_type|type_name }} { } {% endmatch %} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct {{ ffi_converter_name }}: FfiConverterRustBuffer { typealias SwiftType = {{ type_name }} @@ -66,10 +69,16 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer { We always write these public functions just in case the enum is used as an external type by another crate. #} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lift(_ buf: RustBuffer) throws -> {{ type_name }} { return try {{ ffi_converter_name }}.lift(buf) } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuffer { return {{ ffi_converter_name }}.lower(value) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift index cd4953d8e3..f9a283a078 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift @@ -19,6 +19,9 @@ public enum {{ type_name }} { } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct {{ ffi_converter_name }}: FfiConverterRustBuffer { typealias SwiftType = {{ type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift index fb986beab6..b48664c182 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterFloat: FfiConverterPrimitive { typealias FfiType = Float typealias SwiftType = Float diff --git a/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift index 74421c045c..ca9cd42cca 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterDouble: FfiConverterPrimitive { typealias FfiType = Double typealias SwiftType = Double diff --git a/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift index ac57fc5e58..e39a0ddb37 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterInt16: FfiConverterPrimitive { typealias FfiType = Int16 typealias SwiftType = Int16 diff --git a/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift index 0ccfc13e4e..299a36e7a6 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterInt32: FfiConverterPrimitive { typealias FfiType = Int32 typealias SwiftType = Int32 diff --git a/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift index d7d4082933..47f3d8690a 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterInt64: FfiConverterPrimitive { typealias FfiType = Int64 typealias SwiftType = Int64 diff --git a/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift index f2387e4340..a3bb799fa0 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterInt8: FfiConverterPrimitive { typealias FfiType = Int8 typealias SwiftType = Int8 diff --git a/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift index 05713aca26..b9d359cae7 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer { public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) { let len = Int32(value.count) diff --git a/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap b/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap index f5f73ceb1b..3b39f42f82 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap +++ b/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap @@ -1,6 +1,6 @@ -// This file was autogenerated by some hot garbage in the `uniffi` crate. -// Trust me, you don't want to mess with it! -module {{ config.ffi_module_name() }} { - header "{{ config.header_filename() }}" +{% if xcframework %}framework {%endif %}module {{ module_name }} { + {%- for filename in header_filenames %} + header "{{ filename }}" + {%- endfor %} export * } diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index 7a41d49499..aca78cf2c1 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -29,6 +29,9 @@ open class {{ impl_class_name }}: fileprivate let pointer: UnsafeMutableRawPointer! /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public struct NoPointer { public init() {} } @@ -40,15 +43,21 @@ open class {{ impl_class_name }}: self.pointer = pointer } - /// This constructor can be used to instantiate a fake object. - /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. - /// - /// - Warning: - /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. + // This constructor can be used to instantiate a fake object. + // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public init(noPointer: NoPointer) { self.pointer = nil } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public func uniffiClonePointer() -> UnsafeMutableRawPointer { return try! rustCall { {{ obj.ffi_object_clone().name() }}(self.pointer, $0) } } @@ -118,6 +127,9 @@ open class {{ impl_class_name }}: {% include "CallbackInterfaceImpl.swift" %} {%- endif %} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct {{ ffi_converter_name }}: FfiConverter { {%- if obj.has_callback_interface() %} fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>() @@ -169,6 +181,9 @@ extension {{ type_name }}: Foundation.LocalizedError { } {# Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer holding a pointer #} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer { public static func lift(_ buf: RustBuffer) throws -> {{ type_name }} { var reader = createReader(data: Data(rustBuffer: buf)) @@ -193,10 +208,16 @@ public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer { We always write these public functions just in case the enum is used as an external type by another crate. #} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} { return try {{ ffi_converter_name }}.lift(pointer) } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer { return {{ ffi_converter_name }}.lower(value) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift index 1dac65be63..b4434172ba 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer { typealias SwiftType = {{ type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index c262a7a216..4d89194238 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -35,6 +35,9 @@ extension {{ type_name }}: Equatable, Hashable { } {% endif %} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public struct {{ ffi_converter_name }}: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} { return {%- if rec.has_fields() %} @@ -60,10 +63,16 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer { We always write these public functions just in case the struct is used as an external type by another crate. #} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lift(_ buf: RustBuffer) throws -> {{ type_name }} { return try {{ ffi_converter_name }}.lift(buf) } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuffer { return {{ ffi_converter_name }}.lower(value) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift index cf8eec4019..025b7bfdfb 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift @@ -157,10 +157,16 @@ fileprivate protocol FfiConverter { fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { } extension FfiConverterPrimitive { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lift(_ value: FfiType) throws -> SwiftType { return value } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lower(_ value: SwiftType) -> FfiType { return value } @@ -171,6 +177,9 @@ extension FfiConverterPrimitive { fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} extension FfiConverterRustBuffer { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lift(_ buf: RustBuffer) throws -> SwiftType { var reader = createReader(data: Data(rustBuffer: buf)) let value = try read(from: &reader) @@ -181,6 +190,9 @@ extension FfiConverterRustBuffer { return value } +#if swift(>=5.8) + @_documentation(visibility: private) +#endif public static func lower(_ value: SwiftType) -> RustBuffer { var writer = createWriter() write(value, into: &writer) diff --git a/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift index bf664f6411..5dfc7bdb39 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer { typealias SwiftType = {{ type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift b/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift index b7d3466bdd..c74c54d71a 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterString: FfiConverter { typealias SwiftType = String typealias FfiType = RustBuffer diff --git a/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift b/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift index 3cd472fa0e..cdd0113792 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer { typealias SwiftType = Date diff --git a/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift index b7fc0942a5..4833a36232 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { typealias FfiType = UInt16 typealias SwiftType = UInt16 diff --git a/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift index e7a64aab93..2dc6b2b46c 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterUInt32: FfiConverterPrimitive { typealias FfiType = UInt32 typealias SwiftType = UInt32 diff --git a/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift index eb674a2c53..b09d8d6cc7 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterUInt64: FfiConverterPrimitive { typealias FfiType = UInt64 typealias SwiftType = UInt64 diff --git a/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift b/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift index 4baf613494..0d53705e39 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift @@ -1,3 +1,6 @@ +#if swift(>=5.8) +@_documentation(visibility: private) +#endif fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { typealias FfiType = UInt8 typealias SwiftType = UInt8 diff --git a/uniffi_bindgen/src/cargo_metadata.rs b/uniffi_bindgen/src/cargo_metadata.rs index e304025f8f..ce426c30ca 100644 --- a/uniffi_bindgen/src/cargo_metadata.rs +++ b/uniffi_bindgen/src/cargo_metadata.rs @@ -44,11 +44,20 @@ impl From for CrateConfigSupplier { .packages .iter() .flat_map(|p| { - p.targets.iter().filter(|t| t.is_lib()).filter_map(|t| { - p.manifest_path - .parent() - .map(|p| (t.name.replace('-', "_"), p.to_owned())) - }) + p.targets + .iter() + .filter(|t| { + !t.is_bin() + && !t.is_example() + && !t.is_test() + && !t.is_bench() + && !t.is_custom_build() + }) + .filter_map(|t| { + p.manifest_path + .parent() + .map(|p| (t.name.replace('-', "_"), p.to_owned())) + }) }) .collect(); Self { paths } diff --git a/uniffi_bindgen/src/interface/visit_mut.rs b/uniffi_bindgen/src/interface/visit_mut.rs index 31d03ae0fc..1f157cdcf2 100644 --- a/uniffi_bindgen/src/interface/visit_mut.rs +++ b/uniffi_bindgen/src/interface/visit_mut.rs @@ -131,6 +131,15 @@ impl ComponentInterface { } } } + + self.errors = self + .errors + .drain() + .map(|mut name| { + visitor.visit_error_name(&mut name); + name + }) + .collect() } fn fix_record_keys_after_rename(&mut self) { diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 6d32bd4784..3b756d4b8e 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -210,6 +210,11 @@ pub trait VisitMut { /// naming conventions. fn visit_type(&self, type_: &mut Type); + /// Go through each error name in the interface and adjust it to language specific naming + /// conventions. The new name must match the name of the Enum/Object definition after it's + /// visited. + fn visit_error_name(&self, name: &mut String); + /// Go through each `Method` of an `Object` and /// adjust it to language specific naming conventions. fn visit_method(&self, method: &mut Method); diff --git a/uniffi_bindgen/src/macro_metadata/extract.rs b/uniffi_bindgen/src/macro_metadata/extract.rs index 6d440919f1..29ebbc4f1f 100644 --- a/uniffi_bindgen/src/macro_metadata/extract.rs +++ b/uniffi_bindgen/src/macro_metadata/extract.rs @@ -99,8 +99,13 @@ pub fn extract_from_macho(macho: MachO<'_>, file_data: &[u8]) -> anyhow::Result< // - Has type=N_SECT (it's regular data as opposed to something like // "undefined" or "indirect") // - Has a metadata symbol name - if nlist.is_global() && nlist.get_type() == symbols::N_SECT && is_metadata_symbol(name) { - let section = §ions[nlist.n_sect]; + if nlist.is_global() + && nlist.get_type() == symbols::N_SECT + && is_metadata_symbol(name) + && nlist.n_sect != goblin::mach::symbols::NO_SECT as usize + { + let section = §ions[nlist.n_sect - 1]; + // `nlist.n_value` is an address, so we can calculating the offset inside the section // using the difference between that and `section.addr` let offset = section.offset as usize + nlist.n_value as usize - section.addr as usize; diff --git a/uniffi_core/Cargo.toml b/uniffi_core/Cargo.toml index ce67b7f754..37b28ac66a 100644 --- a/uniffi_core/Cargo.toml +++ b/uniffi_core/Cargo.toml @@ -16,7 +16,6 @@ readme = "../README.md" anyhow = "1" async-compat = { version = "0.2.1", optional = true } bytes = "1.3" -camino = "1.0.8" log = "0.4" once_cell = "1.10.0" # Regular dependencies diff --git a/uniffi_core/src/ffi/rustcalls.rs b/uniffi_core/src/ffi/rustcalls.rs index c4cd1ac03a..a5ec05acfe 100644 --- a/uniffi_core/src/ffi/rustcalls.rs +++ b/uniffi_core/src/ffi/rustcalls.rs @@ -174,10 +174,7 @@ pub(crate) fn rust_call_with_out_status( where F: panic::UnwindSafe + FnOnce() -> Result, { - let result = panic::catch_unwind(|| { - crate::panichook::ensure_setup(); - callback() - }); + let result = panic::catch_unwind(callback); match result { // Happy path. Note: no need to update out_status in this case because the calling code // initializes it to [RustCallStatusCode::Success] diff --git a/uniffi_core/src/lib.rs b/uniffi_core/src/lib.rs index 2d55006ba7..e44f5433c7 100644 --- a/uniffi_core/src/lib.rs +++ b/uniffi_core/src/lib.rs @@ -64,13 +64,11 @@ pub mod deps { pub use static_assertions; } -mod panichook; - const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); // For the significance of this magic number 10 here, and the reason that // it can't be a named constant, see the `check_compatible_version` function. -static_assertions::const_assert!(PACKAGE_VERSION.as_bytes().len() < 10); +static_assertions::const_assert!(PACKAGE_VERSION.len() < 10); /// Check whether the uniffi runtime version is compatible a given uniffi_bindgen version. /// diff --git a/uniffi_core/src/panichook.rs b/uniffi_core/src/panichook.rs deleted file mode 100644 index ef0ab86f1f..0000000000 --- a/uniffi_core/src/panichook.rs +++ /dev/null @@ -1,34 +0,0 @@ -/// Initialize our panic handling hook to optionally log panics -#[cfg(feature = "log_panics")] -pub fn ensure_setup() { - use std::sync::Once; - static INIT_BACKTRACES: Once = Once::new(); - INIT_BACKTRACES.call_once(move || { - #[cfg(all(feature = "log_backtraces", not(target_os = "android")))] - { - std::env::set_var("RUST_BACKTRACE", "1"); - } - // Turn on a panic hook which logs both backtraces and the panic - // "Location" (file/line). We do both in case we've been stripped, - // ). - std::panic::set_hook(Box::new(move |panic_info| { - let (file, line) = if let Some(loc) = panic_info.location() { - (loc.file(), loc.line()) - } else { - // Apparently this won't happen but rust has reserved the - // ability to start returning None from location in some cases - // in the future. - ("", 0) - }; - log::error!("### Rust `panic!` hit at file '{file}', line {line}"); - #[cfg(all(feature = "log_backtraces", not(target_os = "android")))] - { - log::error!(" Complete stack trace:\n{:?}", backtrace::Backtrace::new()); - } - })); - }); -} - -/// Initialize our panic handling hook to optionally log panics -#[cfg(not(feature = "log_panics"))] -pub fn ensure_setup() {} diff --git a/uniffi_macros/src/util.rs b/uniffi_macros/src/util.rs index 6f6214208b..07354628ad 100644 --- a/uniffi_macros/src/util.rs +++ b/uniffi_macros/src/util.rs @@ -117,7 +117,11 @@ pub fn create_metadata_items( #[doc(hidden)] #[no_mangle] pub extern "C" fn #ident() -> u16 { - #const_ident.checksum() + // Force constant evaluation to ensure: + // 1. The checksum is computed at compile time; and + // 2. The metadata buffer is not embedded into the binary. + const CHECKSUM: u16 = #const_ident.checksum(); + CHECKSUM } } });