Skip to content

Commit

Permalink
Updating docs and uniffi-bindgen generate
Browse files Browse the repository at this point in the history
Documented the separate bindings changes.

Added some backwards compatibility to `uniffi-bindgen generate`.  It now
tries to run the binary from the external bindings crate using the same
flags passed to it.
  • Loading branch information
bendk committed Apr 5, 2022
1 parent 22fad3e commit 1c5cc55
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 49 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@

### ⚠️ Breaking Changes ⚠️

- `uniffi-bindgen` has been split into separate crates for each language.
`uniffi-bindgen` will continue to generate the scaffolding code (for now at
least), but you will need to install a separate crate for your foreign
language bindings (`uniffi-bindgen-(kotlin|swift|python|ruby)`).
- For backwards compatability `uniffi-bindgen generate` will still work by running
the tool specific to your language, however this is deprecated and you should
update your build system by replacing calls to
`uniffi-bindgen generate --langauge [lang]` with `uniffi-bindgen-[lang]`.
- Removed support for `uniffi-bindgen test`, which was only used for internal testing.
- When custom types are used in function/method signatures UniFFI will now use
the UDL name for that type and create a typealias to the concrete type. In the
[URL example](https://mozilla.github.io/uniffi-rs/udl/custom_types.html#custom-types-in-the-bindings-code),
Expand Down
9 changes: 5 additions & 4 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,16 @@ subdirectory of each example to see how its generated API can be used from diffe

Other directories of interest include:

- **[`./uniffi_bindgen`](../uniffi_bindgen):** This is the source for the `uniffi-bindgen` executable and is where
- **[`./uniffi_bindgen`](../uniffi_bindgen) and friends:** This is the source for the `uniffi-bindgen` executable and is where
most of the logic for the UniFFI tool lives. Its contents include:
- **[`./uniffi_bindgen/src/interface/`](../uniffi_bindgen/src/interface):** The logic for parsing `.udl` files
into an in-memory representation called `ComponentInterface`, from which we can generate code for different languages.
- **[`./uniffi_bindgen/src/scaffolding`](../uniffi_bindgen/src/scaffolding):** This module turns a `ComponentInterface`
into *Rust scaffolding*, the code that wraps the user-provided Rust code and exposes it via a C-compatible FFI layer.
- **[`./uniffi_bindgen/src/bindings/`](../uniffi_bindgen/src/bindings):** This module turns a `ComponentInterface` into
*foreign-language bindings*, the code that can load the FFI layer exposed by the scaffolding and expose it as a
higher-level API in a target language. There is a sub-module for each supported language.
- **[`./uniffi_bindgen-kotlin/`](../uniffi_bindgen_kotlin/src/):** Turns a `ComponentInterface` into Kotlin bindings.
- **[`./uniffi_bindgen-swift/`](../uniffi_bindgen_swift/src/):** Turns a `ComponentInterface` into Swift bindings.
- **[`./uniffi_bindgen-python/`](../uniffi_bindgen_python/src/):** Turns a `ComponentInterface` into Python bindings.
- **[`./uniffi_bindgen-ruby/`](../uniffi_bindgen_ruby/src/):** Turns a `ComponentInterface` into ruby bindings.
- **[`./uniffi`](../uniffi):** This is a run-time support crate that is used by the generated Rust scaffolding. It
controls how values of various types are passed back-and-forth over the FFI layer, by means of the `FfiConverter` trait.
- **[`./uniffi_build`](../uniffi_build):** This is a small hook to run `uniffi-bindgen` from the `build.rs` script
Expand Down
4 changes: 2 additions & 2 deletions docs/manual/src/Motivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Using UniFFI, you can:
* Run `uniffi-bindgen scaffolding ./src/lib.udl` to generate a bunch of boilerplate rust code that exposes this API as a C-compatible FFI layer,
and include it as part of your crate.
* `cargo build` your crate as normal to produce a shared library.
* Run `uniffi-bindgen generate ./src/lib.udl -l kotlin` to generate a Kotlin library that can load your shared library
* Run `uniffi-bindgen-kotlin ./src/lib.udl` to generate a Kotlin library that can load your shared library
and expose it to Kotlin code using your nice high-level component API!
* Or `-l swift` or `-l python` to produce bindings for other languages.

Expand Down Expand Up @@ -60,4 +60,4 @@ here are some tradeoffs you should keep in mind:
(but it might be useful as a building-block for such a tool!).

We also have a list of [design principles](./internals/design_principles.md) that might help
you decide whether this project is a good fit for your needs.
you decide whether this project is a good fit for your needs.
4 changes: 2 additions & 2 deletions docs/manual/src/internals/crates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

The code for UniFFI is organized into the following crates:

- **[`./uniffi_bindgen`](./api/uniffi_bindgen/index.html):** This is the source for the `uniffi-bindgen` executable and is where
most of the logic for the UniFFI tool lives. Its contents include:
- **[`./uniffi_bindgen`](./api/uniffi_bindgen/index.html):** This contains shared code for bindings generation, as well as the
scaffolding generation code. Its contents include:
- **[`./uniffi_bindgen/src/interface/`](./api/uniffi_bindgen/interface/index.html):** The logic for parsing `.udl` files
into an in-memory representation called [`ComponentInterface`](./api/uniffi_bindgen/interface/struct.ComponentInterface.html),
from which we can generate code for different languages.
Expand Down
4 changes: 2 additions & 2 deletions docs/manual/src/kotlin/gradle.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ It is possible to generate Kotlin bindings at compile time for Kotlin Android pr
android.libraryVariants.all { variant ->
def t = tasks.register("generate${variant.name.capitalize()}UniFFIBindings", Exec) {
workingDir "${project.projectDir}"
// Runs the bindings generation, note that you must have uniffi-bindgen installed and in your PATH environment variable
commandLine 'uniffi-bindgen', 'generate', '<PATH TO .udl FILE>', '--language', 'kotlin', '--out-dir', "${buildDir}/generated/source/uniffi/${variant.name}/java"
// Runs the bindings generation, note that you must have uniffi-bindgen-kotlin installed and in your PATH environment variable
commandLine 'uniffi-bindgen_kotlin', '<PATH TO .udl FILE>', '--out-dir', "${buildDir}/generated/source/uniffi/${variant.name}/java"
}
variant.javaCompileProvider.get().dependsOn(t)
def sourceSet = variant.sourceSets.find { it.name == variant.name }
Expand Down
6 changes: 3 additions & 3 deletions docs/manual/src/swift/xcode.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Broadly, you will need to:

1. Add a build phase to compile the Rust crate into a static lib and link it
into your framework.
2. Add a build phase to run `uniffi-bindgen ` and generate the Swift bindings.
2. Add a build phase to run `uniffi-bindgen-swift` and generate the Swift bindings.
3. Include the generated bridging header into your overall bridging header.

There is also an example app in the UniFFI project repo that may be helpful.
Expand All @@ -26,14 +26,14 @@ static library of Rust code.
## Generating the bindings

In the "Build Rules" section of your config, add a rule to process `.udl` files
using `uniffi-bindgen`. We recommend having it generate the output files
using `uniffi-bindgen-swift`. We recommend having it generate the output files
somewhere in your source tree, rather than in XCode's default `$DERIVED_FILE_DIR`;
this both helps with debugging the build output, and makes it easier to configure
how the generated files are used.

* Add a build rule processing files with names matching `*.udl`.
* Use something like the following as the custom script:
* `$HOME/.cargo/bin/uniffi-bindgen generate $INPUT_FILE_PATH --language swift --out-dir $INPUT_FILE_DIR/Generated`
* `$HOME/.cargo/bin/uniffi-bindgen-swift $INPUT_FILE_PATH --out-dir $INPUT_FILE_DIR/Generated`
* Add both the `.swift` file and the generated bridging header as output files:
* `$(INPUT_FILE_DIR)/Generated/$(INPUT_FILE_BASE).swift`
* `$(INPUT_FILE_DIR)/Generated/$(INPUT_FILE_BASE)FFI.h`
Expand Down
25 changes: 8 additions & 17 deletions docs/manual/src/tutorial/Prerequisites.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
# Prerequisites

## The uniffi-bindgen cli tool
## Bindgen CLI tools

Install the `uniffi-bindgen` binary on your system using:

`cargo install uniffi_bindgen`

You can see what it can do with `uniffi-bindgen --help`, but let's leave it aside for now.
Install one or more binaries for the foreign languages you need bindings for.
- `cargo install uniffi_bindgen_kotlin` for `uniffi-bindgen-kotlin`
- `cargo install uniffi_bindgen_swift` for `uniffi-bindgen-swift`
- `cargo install uniffi_bindgen_python` for `uniffi-bindgen-python`
- `cargo install uniffi_bindgen_ruby` for `uniffi-bindgen-ruby`
- Search crates.io for other bindings generators.

### Running from a source checkout

It's also possible to run `uniffi-bindgen` from a source checkout of uniffi - this might
be useful if you are experimenting with changes to uniffi and want to test them out.

In this case, just use `cargo run` in the `uniffi_bindgen` crate directory.
For example, from the root of the `uniffi-rs` repo, execute:

```shell
% cd uniffi_bindgen/src
% cargo run -- --help
```

and you will see the help output from running `uniffi-bindgen` locally. Refer to
the docs for `cargo run` for more information and options.
You can see what these can do with `uniffi-bindgen --help`, but let's leave it aside for now.

## Build your crate as a cdylib

Expand Down
5 changes: 4 additions & 1 deletion docs/manual/src/tutorial/Rust_scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ First, add `uniffi` to your crate dependencies; this is the runtime support code
uniffi = "0.8"
```

Important note: the `uniffi` version must be the same as the `uniffi-bindgen` command-line tool installed on your system.
Important note: the versions of all 3 of these tools must match:
- The `uniffi` dependency of your Rust crate
- The `uniffi-bindgen` used to generate your Rust scaffolding code
- The `uniffi-bindgen-[language]` used to generate the foreign language bindings code

Then let's add `uniffi_build` to your build dependencies: it generates the Rust scaffolding code that exposes our Rust functions as a C-compatible FFI layer.

Expand Down
42 changes: 26 additions & 16 deletions docs/manual/src/tutorial/foreign_language_bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@

As stated in the [Overview](../Overview.md), this library and tutorial does not cover *how* to ship a Rust library on mobile, but how to generate bindings for it, so this section will only cover that.

First, make sure you have installed all the [prerequisites](./Prerequisites.md) - particularly,
installing `uniffi-bindgen` (or alternatively, understanding how to run it from the source tree)
First, make sure you have installed all the [prerequisites](./Prerequisites.md) - in particular:
- `uniffi-bindgen` to generate the Rust scaffolding (or alternatively, understanding how to run it from the source tree)
- `uniffi-bindgen-[language]` for each language you want to generate bindings for.

## Kotlin
## Examples:
- **Kotlin**: Running `uniffi-bindgen-kotlin src/math.udl` will generate `math.kt` at `src/uniffi/math/`
- **Swift**: Running `uniffi-bindgen-swift src/math.udl` will generate `match.swift`, `mathFFI.h`, and `mathFFI.modulemap` at
`src/uniffi/math`.
- **Python**: Running `uniffi-bindgen-python src/math.udl` will generate `math.py` at `src/uniffi/math/`
- **Ruby**: Running `uniffi-bindgen-python src/math.udl` will generate `math.rb` at `src/uniffi/math/`

Run
```
uniffi-bindgen generate src/math.udl --language kotlin
```
then have a look at `src/uniffi/math/math.kt`
## Standard vs external bindings generators

## Swift
The 4 languages listed above represent the standard UniFFI bindings generators. They are developed inside the `uniffi-rs` repo and are
supported by the UniFFI team. However, you can also create external crates to generate bindings for other languages. These crates
generall depend on `uniffi-bindgen` to handle some common tasks, and extend to with bindings generation code and a user-facing CLI.

Run
```
uniffi-bindgen generate src/math.udl --language swift
```
then check out `src/math.swift`
## Standard CLI arguments

Note that these commands could be integrated as part of your gradle/XCode build process.
All of the standard bindgen tools support the same arguments:

This is it, you have an MVP integration of UniFFI in your project.
- `--help`: Print help text
- `--out-dir=[path]`: Path to the directory to generate code in
- `--config=[path]`: Path to the `uniffi.toml` config file
- `--no-format`: Disable code formatting
- `[udl_file]` (positional arg): path to the UDL file

`uniffi-bindgen-kotlin`, `uniffi-bindgen-python`, and `uniffi-bindgen-ruby` also support `--stdout` which causes the output to be
printed to `STDOUT` rather than written to a file.

External bindings generations should support the same arguments, if it makes sense for their language. However, they are free to
change the arguments they accept as they see fit.
33 changes: 31 additions & 2 deletions uniffi_bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ use std::io::prelude::*;
use std::{
env,
fs::File,
io::ErrorKind,
path::{Path, PathBuf},
process::Command,
str::FromStr,
Expand Down Expand Up @@ -233,7 +234,6 @@ pub trait BindingGenerator: Sized {
}

/// Generate bindings for an external binding generator
/// Ideally, this should replace the [`generate_bindings`] function below
///
/// Implements an entry point for external binding generators.
/// The function does the following:
Expand Down Expand Up @@ -298,7 +298,36 @@ pub fn generate_bindings<P: AsRef<Path>>(
out_dir_override: Option<P>,
try_format_code: bool,
) -> Result<()> {
// TODO: rework this
for language in target_languages {
let command_name = format!("uniffi-bindgen-{}", language);
let mut cmd = Command::new(&command_name);
cmd.arg(udl_file.as_ref());
if let Some(ref path) = config_file_override {
cmd.arg("--config-path").arg(path.as_ref());
}
if let Some(ref path) = out_dir_override {
cmd.arg("--out-dir").arg(path.as_ref());
}
if !try_format_code {
cmd.arg("--no-format");
}

let status = match cmd.spawn() {
Ok(mut child) => child.wait()?,
Err(e) => match e.kind() {
ErrorKind::NotFound => bail!(
"{} not found, do you need to run `cargo install {}`?",
&command_name,
command_name.replace("-", "_")
),
_ => bail!("Error runnning {:?}", cmd),
},
};

if !status.success() {
bail!("Error runnning {:?}", cmd);
}
}
Ok(())
}

Expand Down

0 comments on commit 1c5cc55

Please sign in to comment.