Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide commands to support using multiple uniffi packages #1

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

crazytonyli
Copy link
Contributor

@crazytonyli crazytonyli commented Dec 19, 2024

This Rust library provides a command line interface to build Rust packages into one xcframework and generate a Package.swift that describes the dependencies among the Rust packages' Swift public modules (i.e. WordPressAPI, JetpackAPI) and internal ones (which include uniffi swift bindings).

I'd love to get feedback regarding the general solution. The code is messy, but you are welcome to review them in detail as usual.

Try it yourself

I have opened PR on Automattic/jetpack-rs#1 and Automattic/wordpress-rs#451 to integration this library. If you want to see this library in action, you can check out those PRs locally and execute this command to run their Swift tests on macOS:

# The same command works on both wordpress-rs and jetpack-rs.
make xcframework-only-macos generate-swift-package-manifest && swift test

There is a known issue: you'll need to manually install nightly keychain to build on tvOS and watchOS.

Example

Take jetpack-rs projects as an example, the Swift packages structure looks like the diagram below. Sorry about the huge diagram. I didn't find a way to make it more compact.

The structure on the wordpress-rs project looks very similar, except without the Jetpack packages.

block-beta
  columns 3
  
  JetpackAPI:1
  space
  WordPressAPI:1

  space:3

  JetpackAPIInternal
  space
  WordPressAPIInternal

  space:3

  block:group3[["libJetpackFFI.xcframework"]]:3
    columns 3
    jetpack_api space wp_api
    space:3
    block:deps
      uniffi serde ...
    end
  end

  JetpackAPI --> WordPressAPI
  JetpackAPI --> JetpackAPIInternal
  WordPressAPI --> WordPressAPIInternal
  JetpackAPIInternal --> WordPressAPIInternal
  JetpackAPIInternal --> jetpack_api
  WordPressAPIInternal --> wp_api
  jetpack_api --> wp_api
  jetpack_api --> deps
  wp_api --> deps

  style group3 fill:#F5CB4F
  style JetpackAPI fill:#D35F41,color:white
  style WordPressAPI fill:#D35F41,color:white
  style JetpackAPIInternal fill:#D35F41,color:white
  style WordPressAPIInternal fill:#D35F41,color:white
Loading

Commands

This library provides two commands:

  1. build --profile <release | dev> [--only-ios | --only-macos]
  2. generate-package --project-name <git-repo-name>

build

The build command is equivalent to the make xcframework in wordpress-rs. It cross-compiles Rust packages on Apple platforms and puts the static libraries into an xcframework. It also generates uniffi bindings files.

It handles building and "packaging" on Linux, too. So now we don't have separate commands to build on Apple platforms and Linux like wordpress-rs.

generate Package.swift

This generate-package command is a new thing. It generates Package.swift based on the uniffi packages dependencies. By "uniffi packages", I mean Rust packages that expose Swift API via uniffi-rs.

The code generation is based on one fact/assumption(*): no matter how many uniffi packages we are going to have, they will always be in a tree structure where there is a root node (top-level package) and a bunch of dependencies at the leaves.

(*) If it's not the case in the future, we can easily create a skeleton root-level package to make uniffi packages into a tree.

All these uniffi packages will be built into one static library. There will be multiple swift binding files though, one for each uniffi packages.

If we consider wordpress-rs and jetpack-rs as their own standalone library, each library has two targets in Package.swift: public (i.e., WordPressAPI) and internal one (i.e., WordPressAPIInternal). When we have multiple uniffi packages and they have dependencies among them, their binding files would also have the same dependencies. That means we'll need to map the Rust package dependencies onto the targets in Package.swift.

Having to sort out their dependencies on Package.swift is why I chose to generate the Package.swift file, instead of hand-wire them.

However, I really don't think we should be doing that, because auto-generating Package.swift puts lots of pressure on the code to be flexible enough to support customization (like adding other Swift Packages as dependencies). If we think the solution here is heading to the right direction, I think we should refactor this part to providing Swift Package targets, instead of generating the whole Package.swift file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant