Skip to content

Commit

Permalink
Use UniFFI library mode to generate bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniusnaumann committed Oct 3, 2023
1 parent d4c33e9 commit 1871f98
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 71 deletions.
28 changes: 25 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "cargo-swift"
description = "A cargo plugin to easily build Swift packages from Rust code for use in iOS and macOS applications"
version = "0.5.0-alpha01"
version = "0.5.0-alpha02"
edition = "2021"
authors = ["Antonius Naumann <git@antonius.dev>"]
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -29,3 +29,5 @@ uniffi_bindgen = "0.24.3"
# Error Handling
anyhow = "1.0.75"
thiserror = "1.0.49"
nonempty = "0.8.1"
cargo_metadata = "0.18.0"
64 changes: 11 additions & 53 deletions src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use std::fs::{self, create_dir};

use crate::Result;
use camino::Utf8Path;
use regex::Regex;
use uniffi_bindgen::bindings::TargetLanguage;

use crate::recreate_dir;

pub fn generate_bindings(lib_path: Option<&Utf8Path>) -> Result<String> {
let udl_file = Utf8Path::new("./src/lib.udl");
/// Generates UniFFI bindings for crate and returns the .udl namespace
pub fn generate_bindings(lib_path: &Utf8Path, crate_name: &str) -> Result<()> {
let out_dir = Utf8Path::new("./generated");
let headers = out_dir.join("headers");
let sources = out_dir.join("sources");
Expand All @@ -17,66 +16,25 @@ pub fn generate_bindings(lib_path: Option<&Utf8Path>) -> Result<String> {
create_dir(&headers)?;
create_dir(&sources)?;

uniffi_bindgen::generate_bindings(
udl_file,
None,
vec![TargetLanguage::Swift],
Some(out_dir),
uniffi_bindgen::library_mode::generate_bindings(
lib_path,
Some(crate_name.to_owned()),
&[TargetLanguage::Swift],
out_dir,
false,
)?;

let namespace = detect_namespace(udl_file)?;

fs::copy(
out_dir.join(format!("{namespace}.swift")),
sources.join(format!("{namespace}.swift")),
out_dir.join(format!("{crate_name}.swift")),
sources.join(format!("{crate_name}.swift")),
)?;

let header = format!("{namespace}FFI.h");
let header = format!("{crate_name}FFI.h");
fs::copy(out_dir.join(&header), headers.join(&header))?;
fs::copy(
out_dir.join(format!("{namespace}FFI.modulemap")),
out_dir.join(format!("{crate_name}FFI.modulemap")),
headers.join("module.modulemap"),
)?;

Ok(namespace)
}

fn detect_namespace(udl_file: &Utf8Path) -> Result<String> {
let content = fs::read_to_string(udl_file)?;

extract_namespace(&content)
}

fn extract_namespace(content: &str) -> Result<String> {
let regex = Regex::new(r"namespace\s*([^\{\s]*)\s*\{").unwrap();

let namespace = regex
.captures(content)
.and_then(|c| c.get(1))
.map(|m| m.as_str().to_string())
.ok_or("lib.udl does not contain a namespace!")?;

Ok(namespace)
}

#[cfg(test)]
mod tests {
use super::extract_namespace;

#[test]
fn test_extract_namespace_ok() {
let content = "namespace math {
u64 add(u64 a, u64 b);
};";

assert_eq!(extract_namespace(content).unwrap(), "math");

let content = "namespace Example {};";
assert_eq!(extract_namespace(content).unwrap(), "Example");

let content = "namespace whitespace {}";
assert_eq!(extract_namespace(content).unwrap(), "whitespace");
}
Ok(())
}
17 changes: 10 additions & 7 deletions src/commands/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ pub fn run(
build_with_output(target, &lib_name, mode, lib_type, &config)?;
}

let namespace = generate_bindings_with_output(&targets, &lib_name, mode, lib_type, &config)?;
generate_bindings_with_output(&targets, &lib_name, mode, lib_type, &config)?;

recreate_output_dir(&package_name).expect("Could not create package output directory!");
create_xcframework_with_output(&targets, &lib_name, &package_name, mode, lib_type, &config)?;
create_package_with_output(&package_name, &namespace, &config)?;
create_package_with_output(&package_name, &lib_name, &config)?;

Ok(())
}
Expand Down Expand Up @@ -279,15 +279,18 @@ fn generate_bindings_with_output(
mode: Mode,
lib_type: LibType,
config: &Config,
) -> Result<String> {
) -> Result<()> {
run_step(config, "Generating Swift bindings...", || {
let lib_file = library_file_name(lib_name, lib_type);
let target = target_dir();
let lib_path: Option<Utf8PathBuf> = targets
let archs = targets
.first()
.and_then(|t| t.architectures().first().cloned())
.map(|arch| format!("{target}/{arch}/{mode}/{lib_file}").into());
generate_bindings(lib_path.as_deref())
.ok_or("Could not generate UniFFI bindings: No target platform selected!")?
.architectures();
let arch = archs.first();
let lib_path: Utf8PathBuf = format!("{target}/{arch}/{mode}/{lib_file}").into();

generate_bindings(&lib_path, &lib_name)
.map_err(|e| format!("Could not generate UniFFI bindings for udl files due to the following error: \n {e}").into())
})
}
Expand Down
13 changes: 7 additions & 6 deletions src/targets.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{fmt::Display, process::Command};

use execute::command;
use nonempty::{nonempty, NonEmpty};

use crate::lib_type::LibType;

Expand All @@ -17,7 +18,7 @@ pub enum Target {
},
Universal {
universal_name: &'static str,
architectures: Vec<&'static str>,
architectures: NonEmpty<&'static str>,
display_name: &'static str,
platform: ApplePlatform,
},
Expand Down Expand Up @@ -112,9 +113,9 @@ impl Target {
///
/// If this target is a single target, the returned vector will always contain exactly one element.
/// The names returned here exactly match the identifiers of the respective official Rust targets.
pub fn architectures(&self) -> Vec<&'static str> {
pub fn architectures(&self) -> NonEmpty<&'static str> {
match self {
Target::Single { architecture, .. } => vec![architecture],
Target::Single { architecture, .. } => nonempty![architecture],
Target::Universal { architectures, .. } => architectures.to_owned(),
}
}
Expand Down Expand Up @@ -188,13 +189,13 @@ impl TargetInfo for ApplePlatform {
},
IOSSimulator => Target::Universal {
universal_name: "universal-ios",
architectures: vec!["x86_64-apple-ios", "aarch64-apple-ios-sim"],
architectures: nonempty!["x86_64-apple-ios", "aarch64-apple-ios-sim"],
display_name: "iOS Simulator",
platform: *self,
},
MacOS => Target::Universal {
universal_name: "universal-macos",
architectures: vec!["x86_64-apple-darwin", "aarch64-apple-darwin"],
architectures: nonempty!["x86_64-apple-darwin", "aarch64-apple-darwin"],
display_name: "macOS",
platform: *self,
},
Expand All @@ -203,7 +204,7 @@ impl TargetInfo for ApplePlatform {
}
TvOS => Target::Universal {
universal_name: "universal-tvos",
architectures: vec!["aarch64-apple-tvos", "x86_64-apple-tvos"],
architectures: nonempty!["aarch64-apple-tvos", "x86_64-apple-tvos"],
display_name: "tvOS",
platform: *self,
},
Expand Down
2 changes: 1 addition & 1 deletion template/template.Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["<LIB_TYPE>"]
crate-type = ["<LIB_TYPE>", "lib"]
name = "<NAMESPACE>"

[dependencies]
Expand Down

0 comments on commit 1871f98

Please sign in to comment.