Skip to content

Commit

Permalink
Generate Swift Package (#36)
Browse files Browse the repository at this point in the history
* Initial commit

* Added platform enum

* Added test

* Added XCFramework generation

* Added Swift Package generation

* Added import PackageDescription to generated Package.swift

* update gitignore and Cargo.toml

* Removed generated files from git

* Minor fixes in Package generation

* fixed gen_package test

* Finished gen_package test

* Code cleanup

* rename of test file

* minor edit

* removed note in book

It turns out the library tags order is not important after all, so I have removed it.

* added build script to test

Fixed test failing with error "Couldn't copy SwiftBirdgeCore header file". This was due to the build.sh not being executed.

* added create generated folder

* updated relevant book chapter

* removed simulator target from test

* Added post-build to book chapter

* removed println output of xcodebuild

* rename test projects

* update workspace members to reflect new name

* debug for actions

* removed iOS targets from test

This is likely the reason the tests are failing, because these targets are not present in GitHub Actions

* added target to test.yml

* transform to temp dir

* made init func public

This is needed when accessing the init when depending on the Swift package. Otherwise the init can't be used.

* rename test-swift-packages

* update book chapter

* rename generate to create

* rename ApplePlatform variants to use Rust naming conventions

* test modifications

- Added deletion of previously generated files
- Added publish = false to Cargo.toml of test package
- Added THISDIR trick to build script

* Hello Rust! > Hello, From Rust

* using tempfile crate for tempdir

* cargo fmt

* remove post-build from build instruction

* moved Swift Package test to integration-tests

* code cleanup

* rm echo
  • Loading branch information
Jomy10 authored Mar 10, 2022
1 parent 87475ec commit 4f2a9d7
Show file tree
Hide file tree
Showing 26 changed files with 516 additions and 156 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ members = [
"crates/swift-bridge-cli",
"crates/swift-bridge-ir",
"crates/swift-bridge-macro",

"crates/swift-integration-tests",
"SwiftRustIntegrationTestRunner/integration-test-create-swift-package",
"SwiftRustIntegrationTestRunner/swift-package-rust-library-fixture",

"examples/async-functions",
"examples/codegen-visualizer",
"examples/ios-rust-analyzer",
]
]
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
dependencies = (
);
name = SwiftRustIntegrationTestRunner;
packageProductDependencies = (
);
productName = SwiftRustIntegrationTestRunner;
productReference = 228FE5D12740DB6A00805D9E /* SwiftRustIntegrationTestRunner.app */;
productType = "com.apple.product-type.application";
Expand Down
4 changes: 2 additions & 2 deletions SwiftRustIntegrationTestRunner/build-rust.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

set -e
set -e

if [[ -z "$PROJECT_DIR" ]]; then
echo "Missing PROJECT_DIR environment variable." 1>&2
Expand All @@ -11,7 +11,7 @@ export PATH="$HOME/.cargo/bin:$PATH"

export SWIFT_BRIDGE_OUT_DIR="${PROJECT_DIR}/Generated"

# Without this we can't comppile on MacOS Big Sur
# Without this we can't compile on MacOS Big Sur
# https://github.com/TimNN/cargo-lipo/issues/41#issuecomment-774793892
if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then
export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "integration-test-create-swift-package"
version = "0.1.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
swift-bridge-build = { path = "../../crates/swift-bridge-build" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::collections::HashMap;
use std::path::Path;

use swift_bridge_build::ApplePlatform as Platform;
use swift_bridge_build::{create_package, CreatePackageConfig};

// TODO: paths
fn main() {
// Generate package
create_package(CreatePackageConfig {
bridge_dir: &Path::new("swift-package-rust-library-fixture/generated"),
paths: HashMap::from([
(Platform::MacOS, &Path::new("swift-package-rust-library-fixture/target/x86_64-apple-darwin/debug/libtest_swift_packages.a") as _),
]),
out_dir: &Path::new("swift-package-rust-library-fixture/MySwiftPackage"),
package_name: "MySwiftPackage"
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target/
package/
generated/
MySwiftPackage
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "test-swift-packages"
version = "0.1.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["staticlib"]

[build-dependencies]
swift-bridge-build = { path = "../../crates/swift-bridge-build" }

[dependencies]
swift-bridge = { path = "../.." }
Original file line number Diff line number Diff line change
@@ -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"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

THIS_DIR=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
ROOT_DIR="$THIS_DIR"
cd $ROOT_DIR

export SWIFT_BRIDGE_OUT_DIR="$(pwd)/generated"
cargo build --target x86_64-apple-darwin --target-dir "$(pwd)/target"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
fn hello_rust() -> String;
}
}

fn hello_rust() -> String {
String::from("Hello, From Rust!")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// swift-tools-version:5.5

import PackageDescription

let package = Package(
name: "swift-package-test-package",
products: [
.library(
name: "swift-package-test-package",
targets: ["swift-package-test-package"]),
],
dependencies: [
.package(path: "../swift-package-rust-library-fixture/MySwiftPackage")
],
targets: [
.target(
name: "swift-package-test-package",
dependencies: []),
.testTarget(
name: "swift-package-test-packageTests",
dependencies: ["swift-package-test-package", "MySwiftPackage"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import XCTest
import MySwiftPackage
@testable import swift_package_test_package

final class swift_package_test_packageTests: XCTestCase {
func testPackageRun() throws {
XCTAssertEqual("Hello, From Rust!", hello_rust().toString())
}
}
153 changes: 25 additions & 128 deletions book/src/building/swift-packages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,23 @@ Add a new `build.rs` file (`touch build.rs`):
// my-rust-lib/build.rs

use std::path::PathBuf;
use swift_bridge_build::{GeneratePackageConfig, ApplePlatform};

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"));
}
```

Create a new folder called *generated* `mkdir generated`.

Build the project for the desired platforms:

```bash
Expand All @@ -76,140 +79,34 @@ cargo build --target aarch64-apple-ios
cargo build --target x86_64-apple-ios
```

## Creating the XCFramework

Go back to the root of the project and make a new directory `cd ..`.

```bash
mkdir MyFramework && cd $_
```

Copy the generated libraries and the headers to this folder:
```bash
mkdir include
touch include/module.modulemap
cp ../my-rust-lib/generated/SwiftBridgeCore.h ./include
cp ../my-rust-lib/generated/my_rust_lib/my_rust_lib.h ./includ
mkdir ios
cp ../my-rust-lib/target/aarch64-apple-ios/debug/libmy_rust_lib.a ./ios
mkdir macos
cp ../my-rust-lib/target/x86_64-apple-darwin/debug/libmy_rust_lib.a ./macos
mkdir simulator
cp ../my-rust-lib/target/x86_64-apple-ios/debug/libmy_rust_lib.a ./simulator
```

This should result in the follwing folder structure:
```
MyFramework
├── include
│ ├── SwiftBridgeCore.h
│ ├── module.modulemap
│ └── my_rust_lib.h
├── ios
│ └── libmy_rust_lib.a
├── macos
│ └── libmy_rust_lib.a
└── simulator
└── libmy_rust_lib.a
```
We can now take our generated files and turn them into a Swift Package. This can be achieved using the `API` or the `CLI` to package the bridging code and the Rust libraries into a Swift Package.

Edit `include/module.modulemap`:
#### API

```modulemap
module MyRustLib {
header "my_rust_lib.h"
header "SwiftBridgeCore.h"
export *
```rust
use std::path::Path;
use std::collections::HashMap;
use swift_bridge_build::{CreatePackageConfig, ApplePlatform};
fn main() {
swift_bridge_build::create_package(GeneratePackageConfig {
bridge_dir: &Path::new("./generated"),
paths: HashMap::from([
(ApplePlatform::IOS, &"target/x86_64-apple-ios/debug/libmy_rust_lib.a" as _),
(ApplePlatform::Simulator, &"target/aarch64-apple-ios/debug/libmy_rust_lib.a" as _),
(ApplePlatform::MacOS, &"target/x86_64-apple-darwin/debug/libmy_rust_lib.a" as _),
]),
out_dir: &Path::new("MySwiftPackage"),
package_name: "MySwiftPackage"
});
}
```

Now it is time to build the xcframework:

```bash
xcodebuild -create-xcframework \
-library simulator/libmy_rust_lib.a \
-headers include \
-library ios/libmy_rust_lib.a \
-headers include \
-library macos/libmy_rust_lib.a \
-headers include \
-output MyRustLib.xcframework
```

*The order of the `library` tags is important, but we don't currently know why*

## Creating the Swift package

Go back to the root of the project (`cd ..`) and create a new Swift package:

```bash
mkdir MySwiftPackage && cd MySwiftPackage
```

Now either do `mkdir -r Sources/MySwiftPackage` and `touch Package.swift`, or use `swift package init --type library`.

Copy the xcframework and generated swift files to this folder:
```bash
cp -r ../MyFramework/MyRustLib.xcframework ./
cp ../my-rust-lib/generated/SwiftBridgeCore.swift Sources/MySwiftPackage
cp ../my-rust-lib/generated/my_rust_lib/my_rust_lib.swift Sources/MySwiftPackage
```

The folder structure should be:
```
MySwiftPackage
├── Sources
│ └── MySwiftPackage
│ └── SwiftBridgeCore.swift
│ └── my_rust_lib.swift
├── MyRustLib.b.xcframework
└── Package.swift
```

Add the framework as a binary target to `Package.swift`:
```swift
// MySwiftPackage/Package.swift

// swift-tools-version:5.5.0
import PackageDescription
let package = Package(
name: "MySwiftPackage",
products: [
.library(
name: "MySwiftPackage",
targets: ["MySwiftPackage"]),
],
dependencies: [

],
targets: [
.binaryTarget(
name: "MyRustLib",
path: "MyRustLib.xcframework"
),
.target(
name: "MySwiftPackge",
dependencies: ["MyRustLib"]),
]
)
```

<!--TODO: better way of dealing with this instead of editing the generated files-->
We will need to import our rust library in `my_rust_lib.swift` and `SwiftBridgeCore.swift`:

```swift
// MySwiftPackage/Sources/MySwiftPackage/SwiftBridgeCore.swift
import MyRustLib
```

```swift
// MySwiftPackage/Sources/MySwiftPackage/my_rust_lib.swift
import MyRustLib
```
#### CLI
*Not yet implemented*

## Using the Swift Package

We now have a Swift Package which we can include in other projects using the Swift Package Manager.
We now have a Swift Package (in the `MySwiftPackage` directory) which we can include in other projects using the Swift Package Manager.

### Example: MacOS executable
Here is an example of an executable project located in `rust-swift-project/testPackage`.
Expand Down
1 change: 1 addition & 0 deletions crates/swift-bridge-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ license = "Apache-2.0/MIT"
proc-macro2 = "1"
swift-bridge-ir = {version = "0.1.28", path = "../swift-bridge-ir"}
syn = {version = "1"}
tempfile = "3.3"
2 changes: 2 additions & 0 deletions crates/swift-bridge-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#![deny(missing_docs)]

mod package;
pub use package::*;
use std::path::Path;
use swift_bridge_ir::{CodegenConfig, SwiftBridgeModule};
use syn::__private::ToTokens;
Expand Down
Loading

0 comments on commit 4f2a9d7

Please sign in to comment.