diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/.gitignore b/SwiftRustIntegrationTestRunner/test-release-fails/.gitignore new file mode 100644 index 00000000..f09c04da --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/.gitignore @@ -0,0 +1,5 @@ +generated/* +.build/* +target/* +Cargo.lock +TestReleaseFails/* \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/Cargo.toml b/SwiftRustIntegrationTestRunner/test-release-fails/Cargo.toml new file mode 100644 index 00000000..997c4f0d --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test-release-fails" +version = "0.1.0" +edition = "2021" +publish = [] + +build = "build.rs" + +[lib] +crate-type = ["staticlib"] + +[build-dependencies] +swift-bridge-build = { git = "https://github.com/chinedufn/swift-bridge.git", branch = "master" } + +[dependencies] +swift-bridge = { git = "https://github.com/chinedufn/swift-bridge.git", branch = "master", features = [ + "async", +] } + +[workspace] diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/Package.swift b/SwiftRustIntegrationTestRunner/test-release-fails/Package.swift new file mode 100644 index 00000000..f9235398 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version:5.5.0 +import PackageDescription +let package = Package( + name: "TestReleaseFails", + products: [ + .library( + name: "TestReleaseFails", + targets: ["TestReleaseFails"]), + .executable( + name: "TestReleaseFailsRunner", + targets: ["TestReleaseFailsRunner"]), + ], + dependencies: [], + targets: [ + .binaryTarget( + name: "RustXcframework", + path: "RustXcframework.xcframework" + ), + .target( + name: "TestReleaseFails", + dependencies: ["RustXcframework"]), + .executableTarget( + name: "TestReleaseFailsRunner", + dependencies: ["TestReleaseFails"]) + ] +) diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/README.md b/SwiftRustIntegrationTestRunner/test-release-fails/README.md new file mode 100644 index 00000000..cda4789a --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/README.md @@ -0,0 +1,7 @@ +# Test Release Builds Fail + +This test is a reproduction case for when building for release, +previously as `extern "Swift"` functions were `internal` +the Swift compiler would strip these, as it would infer +that they were dead code, even though these need to be accessible +from the Rust side of the FFI. \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/SwiftExterns.swift b/SwiftRustIntegrationTestRunner/test-release-fails/SwiftExterns.swift new file mode 100644 index 00000000..3e54517a --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/SwiftExterns.swift @@ -0,0 +1,5 @@ +import Foundation + +func add(lhs: Int32, rhs: Int32) -> Int32 { + lhs + rhs +} diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/build.rs b/SwiftRustIntegrationTestRunner/test-release-fails/build.rs new file mode 100644 index 00000000..284d926e --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/build.rs @@ -0,0 +1,13 @@ +use std::path::PathBuf; + +fn main() { + let out_dir = PathBuf::from("./generated"); + + let bridges = vec!["src/lib.rs"]; + for path in &bridges { + println!("cargo:rerun-if-changed={}", path); + } + + swift_bridge_build::parse_bridges(bridges) + .write_all_concatenated(out_dir, env!("CARGO_PKG_NAME")); +} diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/build.sh b/SwiftRustIntegrationTestRunner/test-release-fails/build.sh new file mode 100644 index 00000000..1de6a387 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +PACKAGE_NAME=test-release-fails +LIBRARY_NAME=test_release_fails +SWIFT_PACKAGE_NAME=TestReleaseFails +EXECUTABLE_TARGET_NAME="$SWIFT_PACKAGE_NAME"Runner + +set -e + +THISDIR=$(dirname $0) +cd $THISDIR + +echo "Building for macOS..." +rustup target add x86_64-apple-darwin aarch64-apple-darwin +cargo build --target x86_64-apple-darwin --release +cargo build --target aarch64-apple-darwin --release +mkdir -p ./target/universal-macos/release +lipo \ + ./target/aarch64-apple-darwin/release/lib$LIBRARY_NAME.a \ + ./target/x86_64-apple-darwin/release/lib$LIBRARY_NAME.a -create -output \ + ./target/universal-macos/release/lib$LIBRARY_NAME.a + +function create_package { + swift-bridge-cli create-package \ + --bridges-dir ./generated \ + --out-dir $SWIFT_PACKAGE_NAME \ + --ios ./target/universal-macos/release/lib$LIBRARY_NAME.a \ + --name $SWIFT_PACKAGE_NAME +} + +function patch_package { + cp Package.swift $SWIFT_PACKAGE_NAME/Package.swift + mkdir -p $SWIFT_PACKAGE_NAME/Sources/$EXECUTABLE_TARGET_NAME + cp main.swift $SWIFT_PACKAGE_NAME/Sources/$EXECUTABLE_TARGET_NAME/main.swift + cp SwiftExterns.swift $SWIFT_PACKAGE_NAME/Sources/$SWIFT_PACKAGE_NAME/SwiftExterns.swift +} + +echo "Creating Swift package..." +create_package +patch_package + +echo "Building for release..." +cd $SWIFT_PACKAGE_NAME +xcodebuild archive -scheme $EXECUTABLE_TARGET_NAME -archivePath ./build/$EXECUTABLE_TARGET_NAME.xcarchive -destination "platform=macOS" \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/main.swift b/SwiftRustIntegrationTestRunner/test-release-fails/main.swift new file mode 100644 index 00000000..08e75d50 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/main.swift @@ -0,0 +1,4 @@ +import Foundation +import TestReleaseFails + +call_swift_add() \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-fails/src/lib.rs b/SwiftRustIntegrationTestRunner/test-release-fails/src/lib.rs new file mode 100644 index 00000000..3c293b17 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-fails/src/lib.rs @@ -0,0 +1,14 @@ +#[swift_bridge::bridge] +mod ffi { + extern "Rust" { + fn call_swift_add(); + } + + extern "Swift" { + fn add(lhs: i32, rhs: i32) -> i32; + } +} + +fn call_swift_add() { + assert!(ffi::add(1, 1) == 2); +} diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/.gitignore b/SwiftRustIntegrationTestRunner/test-release-succeeds/.gitignore new file mode 100644 index 00000000..ff2c520e --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/.gitignore @@ -0,0 +1,5 @@ +generated/* +.build/* +target/* +Cargo.lock +TestReleaseSucceeds/* \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/Cargo.toml b/SwiftRustIntegrationTestRunner/test-release-succeeds/Cargo.toml new file mode 100644 index 00000000..8088b793 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test-release-succeeds" +version = "0.1.0" +edition = "2021" +publish = [] + +build = "build.rs" + +[lib] +crate-type = ["staticlib"] + +[build-dependencies] +swift-bridge-build = { path = "../../crates/swift-bridge-build" } + +[dependencies] +swift-bridge = { path = "../../", features = ["async"] } + +[workspace] diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/Package.swift b/SwiftRustIntegrationTestRunner/test-release-succeeds/Package.swift new file mode 100644 index 00000000..f0e56c55 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version:5.5.0 +import PackageDescription +let package = Package( + name: "TestReleaseSucceeds", + products: [ + .library( + name: "TestReleaseSucceeds", + targets: ["TestReleaseSucceeds"]), + .executable( + name: "TestReleaseSucceedsRunner", + targets: ["TestReleaseSucceedsRunner"]), + ], + dependencies: [], + targets: [ + .binaryTarget( + name: "RustXcframework", + path: "RustXcframework.xcframework" + ), + .target( + name: "TestReleaseSucceeds", + dependencies: ["RustXcframework"]), + .executableTarget( + name: "TestReleaseSucceedsRunner", + dependencies: ["TestReleaseSucceeds"]) + ] +) diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/README.md b/SwiftRustIntegrationTestRunner/test-release-succeeds/README.md new file mode 100644 index 00000000..81d9f10a --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/README.md @@ -0,0 +1,7 @@ +# Test Release + +This test makes sure that when building for release, +the Swift compiler doesn't strip functions that need to be accessible +from the Rust side of the FFI. +This test verifies that by making `extern "Swift"` functions `public` +release builds now succeed. \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/SwiftExterns.swift b/SwiftRustIntegrationTestRunner/test-release-succeeds/SwiftExterns.swift new file mode 100644 index 00000000..3e54517a --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/SwiftExterns.swift @@ -0,0 +1,5 @@ +import Foundation + +func add(lhs: Int32, rhs: Int32) -> Int32 { + lhs + rhs +} diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/build.rs b/SwiftRustIntegrationTestRunner/test-release-succeeds/build.rs new file mode 100644 index 00000000..284d926e --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/build.rs @@ -0,0 +1,13 @@ +use std::path::PathBuf; + +fn main() { + let out_dir = PathBuf::from("./generated"); + + let bridges = vec!["src/lib.rs"]; + for path in &bridges { + println!("cargo:rerun-if-changed={}", path); + } + + swift_bridge_build::parse_bridges(bridges) + .write_all_concatenated(out_dir, env!("CARGO_PKG_NAME")); +} diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/build.sh b/SwiftRustIntegrationTestRunner/test-release-succeeds/build.sh new file mode 100644 index 00000000..d2ccdace --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +PACKAGE_NAME=test-release-succeeds +LIBRARY_NAME=test_release_succeeds +SWIFT_PACKAGE_NAME=TestReleaseSucceeds +EXECUTABLE_TARGET_NAME="$SWIFT_PACKAGE_NAME"Runner + +set -e + +THISDIR=$(dirname $0) +cd $THISDIR + +echo "Building for macOS..." +rustup target add x86_64-apple-darwin aarch64-apple-darwin +cargo build --target x86_64-apple-darwin --release +cargo build --target aarch64-apple-darwin --release +mkdir -p ./target/universal-macos/release +lipo \ + ./target/aarch64-apple-darwin/release/lib$LIBRARY_NAME.a \ + ./target/x86_64-apple-darwin/release/lib$LIBRARY_NAME.a -create -output \ + ./target/universal-macos/release/lib$LIBRARY_NAME.a + +function create_package { + swift-bridge-cli create-package \ + --bridges-dir ./generated \ + --out-dir $SWIFT_PACKAGE_NAME \ + --ios ./target/universal-macos/release/lib$LIBRARY_NAME.a \ + --name $SWIFT_PACKAGE_NAME +} + +function patch_package { + cp Package.swift $SWIFT_PACKAGE_NAME/Package.swift + mkdir -p $SWIFT_PACKAGE_NAME/Sources/$EXECUTABLE_TARGET_NAME + cp main.swift $SWIFT_PACKAGE_NAME/Sources/$EXECUTABLE_TARGET_NAME/main.swift + cp SwiftExterns.swift $SWIFT_PACKAGE_NAME/Sources/$SWIFT_PACKAGE_NAME/SwiftExterns.swift +} + +echo "Creating Swift package..." +create_package +patch_package + +echo "Building for release..." +cd $SWIFT_PACKAGE_NAME +xcodebuild archive -scheme $EXECUTABLE_TARGET_NAME -archivePath ./build/$EXECUTABLE_TARGET_NAME.xcarchive -destination "platform=macOS" \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/main.swift b/SwiftRustIntegrationTestRunner/test-release-succeeds/main.swift new file mode 100644 index 00000000..0575419b --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/main.swift @@ -0,0 +1,4 @@ +import Foundation +import TestReleaseSucceeds + +call_swift_add() \ No newline at end of file diff --git a/SwiftRustIntegrationTestRunner/test-release-succeeds/src/lib.rs b/SwiftRustIntegrationTestRunner/test-release-succeeds/src/lib.rs new file mode 100644 index 00000000..3c293b17 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/test-release-succeeds/src/lib.rs @@ -0,0 +1,14 @@ +#[swift_bridge::bridge] +mod ffi { + extern "Rust" { + fn call_swift_add(); + } + + extern "Swift" { + fn add(lhs: i32, rhs: i32) -> i32; + } +} + +fn call_swift_add() { + assert!(ffi::add(1, 1) == 2); +} diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests.rs index 8e4e69e8..f9d6a9f5 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests.rs @@ -52,6 +52,7 @@ mod string_codegen_tests; mod transparent_enum_codegen_tests; mod transparent_struct_codegen_tests; mod vec_codegen_tests; +mod visibility_codegen_tests; struct CodegenTest { bridge_module: BridgeModule, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/boxed_fnonce_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/boxed_fnonce_codegen_tests.rs index 123d8387..556ae606 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/boxed_fnonce_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/boxed_fnonce_codegen_tests.rs @@ -55,7 +55,7 @@ mod test_swift_takes_no_args_no_return_callback { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallbackNoArgsNoRet(ptr: callback); let _ = some_function(callback: { cb0.call() }) }() } "#, @@ -149,7 +149,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { arg0 in cb0.call(arg0) }) }() } "#, @@ -248,7 +248,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { cb0.call() }) }() } "#, @@ -351,7 +351,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { arg0 in cb0.call(arg0) }) }() } "#, @@ -457,7 +457,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { cb0.call() }) }() } "#, @@ -566,7 +566,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { arg0 in cb0.call(arg0) }) }() } "#, @@ -690,7 +690,7 @@ class __private__RustFnOnceCallback$some_function$param1 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg0: UnsafeMutableRawPointer, _ arg1: UnsafeMutableRawPointer, _ arg2: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ arg0: UnsafeMutableRawPointer, _ arg1: UnsafeMutableRawPointer, _ arg2: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallbackNoArgsNoRet(ptr: arg0); let cb1 = __private__RustFnOnceCallback$some_function$param1(ptr: arg1); let cb2 = __private__RustFnOnceCallbackNoArgsNoRet(ptr: arg2); let _ = some_function(arg0: { cb0.call() }, arg1: { arg0 in cb1.call(arg0) }, arg2: { cb2.call() }) }() } "#, @@ -793,7 +793,7 @@ class __private__RustFnOnceCallback$some_function$param0 { "#, r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ callback: UnsafeMutableRawPointer) { { let cb0 = __private__RustFnOnceCallback$some_function$param0(ptr: callback); let _ = some_function(callback: { arg0, arg1 in cb0.call(arg0, arg1) }) }() } "#, @@ -865,7 +865,7 @@ mod test_swift_method_takes_no_args_no_return_callback { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$SomeType$some_method") -func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer, _ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer, _ callback: UnsafeMutableRawPointer) { { let cb1 = __private__RustFnOnceCallbackNoArgsNoRet(ptr: callback); let _ = Unmanaged.fromOpaque(this).takeUnretainedValue().some_method(callback: { cb1.call() }) }() } "#, @@ -967,7 +967,7 @@ class __private__RustFnOnceCallback$SomeType$some_method$param1 { "#, r#" @_cdecl("__swift_bridge__$SomeType$some_method") -func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer, _ callback: UnsafeMutableRawPointer) { +public func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer, _ callback: UnsafeMutableRawPointer) { { let cb1 = __private__RustFnOnceCallback$SomeType$some_method$param1(ptr: callback); let _ = Unmanaged.fromOpaque(this).takeUnretainedValue().some_method(callback: { arg0 in cb1.call(arg0) }) }() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs index 9a7e7dd4..7b004354 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs @@ -316,7 +316,7 @@ mod extern_swift_tuple_primitives { ExpectedSwiftCode::ContainsManyAfterTrim(vec![ r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$I32U8) -> __swift_bridge__$tuple$I32U8 { +public func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$I32U8) -> __swift_bridge__$tuple$I32U8 { { let val = some_function(arg: { let val = arg; return (val._0, val._1); }()); return __swift_bridge__$tuple$I32U8(_0: val.0, _1: val.1); }() } "#, @@ -389,7 +389,7 @@ mod extern_swift_tuple_opaque_and_string { ExpectedSwiftCode::ContainsManyAfterTrim(vec![ r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeTypeString) -> __swift_bridge__$tuple$SomeTypeString { +public func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeTypeString) -> __swift_bridge__$tuple$SomeTypeString { { let val = some_function(arg: { let val = arg; return (SomeType(ptr: val._0), RustString(ptr: val._1)); }()); return __swift_bridge__$tuple$SomeTypeString(_0: {val.0.isOwned = false; return val.0.ptr;}(), _1: { let rustString = val.1.intoRustString(); rustString.isOwned = false; return rustString.ptr }()); }() } "#, @@ -464,7 +464,7 @@ mod extern_swift_tuple_transparent_struct_and_transparent_enum { ExpectedSwiftCode::ContainsManyAfterTrim(vec![ r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeStructSomeEnum) -> __swift_bridge__$tuple$SomeStructSomeEnum { +public func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeStructSomeEnum) -> __swift_bridge__$tuple$SomeStructSomeEnum { { let val = some_function(arg: { let val = arg; return (val._0.intoSwiftRepr(), val._1.intoSwiftRepr()); }()); return __swift_bridge__$tuple$SomeStructSomeEnum(_0: val.0.intoFfiRepr(), _1: val.1.intoFfiRepr()); }() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs index 0a766ef0..76504e5f 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs @@ -37,7 +37,7 @@ mod test_extern_swift_freestanding_function_owned_opaque_swift_type_return { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> UnsafeMutableRawPointer { +public func __swift_bridge__some_function () -> UnsafeMutableRawPointer { Unmanaged.passRetained(some_function()).toOpaque() } "#, @@ -97,7 +97,7 @@ mod test_extern_swift_method_owned_opaque_swift_type_return { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$SomeType$some_method") -func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { +public func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { Unmanaged.passRetained(Unmanaged.fromOpaque(this).takeUnretainedValue().some_method()).toOpaque() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/function_attribute_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/function_attribute_codegen_tests.rs index eeac35d2..d76bd087 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/function_attribute_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/function_attribute_codegen_tests.rs @@ -445,7 +445,7 @@ public func callRustFromSwift() -> RustString { RustString(ptr: __swift_bridge__$call_rust_from_swift()) } @_cdecl("__swift_bridge__$call_swift_from_rust") -func __swift_bridge__call_swift_from_rust () -> UnsafeMutableRawPointer { +public func __swift_bridge__call_swift_from_rust () -> UnsafeMutableRawPointer { { let rustString = callSwiftFromRust().intoRustString(); rustString.isOwned = false; return rustString.ptr }() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs index fcb4420c..62cd2784 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs @@ -433,7 +433,7 @@ mod extern_swift_freestanding_fn_with_owned_opaque_rust_type_arg { const EXPECTED_SWIFT: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { some_function(arg: MyType(ptr: arg)) } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs index ffc97fd5..45469b96 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs @@ -46,7 +46,7 @@ mod extern_swift_freestanding_fn_with_owned_opaque_swift_type_arg { const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { some_function(arg: Unmanaged.fromOpaque(arg).takeRetainedValue()) } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs index e449ac62..951bc9f4 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs @@ -119,7 +119,7 @@ mod extern_swift_fn_option_primitive { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __private__OptionU8) -> __private__OptionF32 { +public func __swift_bridge__some_function (_ arg: __private__OptionU8) -> __private__OptionF32 { some_function(arg: arg.intoSwiftRepr()).intoFfiRepr() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/single_representation_type_elision_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/single_representation_type_elision_codegen_tests.rs index a0229f28..cbf96020 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/single_representation_type_elision_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/single_representation_type_elision_codegen_tests.rs @@ -54,7 +54,7 @@ public func rust_function(_ arg1: (), _ arg2: ()) -> () { "#, r#" @_cdecl("__swift_bridge__$swift_function") -func __swift_bridge__swift_function () { +public func __swift_bridge__swift_function () { swift_function(arg1: (), arg2: ()) } "#, @@ -140,7 +140,7 @@ public func rust_function(_ arg1: UnitStruct1, _ arg2: UnitStruct2, _ arg3: Unit "#, r#" @_cdecl("__swift_bridge__$swift_function") -func __swift_bridge__swift_function () { +public func __swift_bridge__swift_function () { { let _ = swift_function(arg1: UnitStruct1(), arg2: UnitStruct2(), arg3: UnitStruct3()); }() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs index 1578a313..06092b7a 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs @@ -243,7 +243,7 @@ mod extern_swift_func_returns_string { const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> UnsafeMutableRawPointer { +public func __swift_bridge__some_function () -> UnsafeMutableRawPointer { { let rustString = some_function().intoRustString(); rustString.isOwned = false; return rustString.ptr }() } "#, @@ -290,7 +290,7 @@ mod extern_swift_func_takes_and_returns_string { const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ value: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { +public func __swift_bridge__some_function (_ value: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { { let rustString = some_function(value: RustString(ptr: value)).intoRustString(); rustString.isOwned = false; return rustString.ptr }() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs index d34dbc87..1da65563 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs @@ -370,7 +370,7 @@ mod extern_swift_fn_arg_swift_repr_struct { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __swift_bridge__$SomeStruct) { +public func __swift_bridge__some_function (_ arg: __swift_bridge__$SomeStruct) { some_function(arg: arg.intoSwiftRepr()) } "#, @@ -487,7 +487,7 @@ mod extern_swift_return_swift_repr_struct { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> __swift_bridge__$SomeStruct { +public func __swift_bridge__some_function () -> __swift_bridge__$SomeStruct { some_function().intoFfiRepr() } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs index 1f715509..aff0f406 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs @@ -580,7 +580,7 @@ mod extern_swift_fn_return_vec_of_primitive_rust_type { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> UnsafeMutableRawPointer { +public func __swift_bridge__some_function () -> UnsafeMutableRawPointer { { let val = some_function(); val.isOwned = false; return val.ptr }() } "#, @@ -639,7 +639,7 @@ mod extern_swift_fn_arg_vec_of_primitive_rust_type { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { +public func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { some_function(arg: RustVec(ptr: arg)) } "#, diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/visibility_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/visibility_codegen_tests.rs new file mode 100644 index 00000000..c0fc9f93 --- /dev/null +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/visibility_codegen_tests.rs @@ -0,0 +1,49 @@ +use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode}; +use proc_macro2::TokenStream; +use quote::quote; + +/// Verify that extern "Swift" methods are declared `public` to prevent them from +/// being stripped by the Swift compiler when building in `Release` mode. +mod visibility_codegen_tests { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + fn some_function(); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::SkipTest + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$some_function") +public func __swift_bridge__some_function () { + some_function() +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::SkipTest + } + + #[test] + fn test_visibility_codegen_tests() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift.rs b/crates/swift-bridge-ir/src/codegen/generate_swift.rs index e0af4a47..a6b1832d 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift.rs @@ -317,7 +317,7 @@ class __private__RustFnOnceCallback{maybe_associated_ty}${fn_name}$param{idx} {{ let generated_func = format!( r#"@_cdecl("{link_name}") -func {prefixed_fn_name} ({params}){ret} {{ +public func {prefixed_fn_name} ({params}){ret} {{ {call_fn} }}{rust_fn_once_callback_classes} "#, @@ -429,7 +429,7 @@ public func foo() { let expected = r#" @_cdecl("__swift_bridge__$foo") -func __swift_bridge__foo () { +public func __swift_bridge__foo () { foo() } "#; @@ -452,7 +452,7 @@ func __swift_bridge__foo () { let expected = r#" @_cdecl("__swift_bridge__$foo") -func __swift_bridge__foo () -> __private__FfiSlice { +public func __swift_bridge__foo () -> __private__FfiSlice { foo().toFfiSlice() } "#; @@ -476,7 +476,7 @@ func __swift_bridge__foo () -> __private__FfiSlice { let expected = r#" @_cdecl("__swift_bridge__$MyType$foo") -func __swift_bridge__MyType_foo (_ this: UnsafeMutableRawPointer) -> __private__FfiSlice { +public func __swift_bridge__MyType_foo (_ this: UnsafeMutableRawPointer) -> __private__FfiSlice { Unmanaged.fromOpaque(this).takeUnretainedValue().foo().toFfiSlice() } "#; @@ -594,7 +594,7 @@ func __swift_bridge__Foo__free (ptr: UnsafeMutableRawPointer) { let expected = r#" @_cdecl("__swift_bridge__$Foo$new") -func __swift_bridge__Foo_new (_ a: UInt8) -> UnsafeMutableRawPointer { +public func __swift_bridge__Foo_new (_ a: UInt8) -> UnsafeMutableRawPointer { Unmanaged.passRetained(Foo(a: a)).toOpaque() } "#; @@ -701,12 +701,12 @@ extension Foo { let expected = r#" @_cdecl("__swift_bridge__$Foo$push") -func __swift_bridge__Foo_push (_ this: UnsafeMutableRawPointer, _ arg: UInt8) { +public func __swift_bridge__Foo_push (_ this: UnsafeMutableRawPointer, _ arg: UInt8) { Unmanaged.fromOpaque(this).takeUnretainedValue().push(arg: arg) } @_cdecl("__swift_bridge__$Foo$pop") -func __swift_bridge__Foo_pop (_ this: UnsafeMutableRawPointer) { +public func __swift_bridge__Foo_pop (_ this: UnsafeMutableRawPointer) { Unmanaged.fromOpaque(this).takeUnretainedValue().pop() } "#; @@ -833,7 +833,7 @@ extension FooRef { let expected = r#" @_cdecl("__swift_bridge__$Foo$bar") -func __swift_bridge__Foo_bar (_ arg: UInt8) { +public func __swift_bridge__Foo_bar (_ arg: UInt8) { Foo::bar(arg: arg) } "#; @@ -925,7 +925,7 @@ func void_pointer() -> UnsafeRawPointer { let expected = r#" @_cdecl("__swift_bridge__$void_pointer") -func __swift_bridge__void_pointer (_ arg: UnsafeRawPointer) { +public func __swift_bridge__void_pointer (_ arg: UnsafeRawPointer) { void_pointer(arg: arg) } "#; @@ -1004,7 +1004,7 @@ func some_function() -> Foo { let expected = r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () { +public func __swift_bridge__some_function () { someFunctionSwiftName() } "#; diff --git a/test-swift-rust-integration.sh b/test-swift-rust-integration.sh index bc6d7946..b64e7064 100755 --- a/test-swift-rust-integration.sh +++ b/test-swift-rust-integration.sh @@ -25,3 +25,12 @@ xcodebuild \ -project SwiftRustIntegrationTestRunner.xcodeproj \ -scheme SwiftRustIntegrationTestRunner \ clean test + +if ! sh ./test-release-fails/build.sh; then + echo "Build failed as expected" +else + echo "Error: Build succeeded but was expected to fail" + exit 1 +fi + +sh ./test-release-succeeds/build.sh \ No newline at end of file