Skip to content

Commit 1eeb9de

Browse files
committed
Adding BindgenLoader
This is a new way for binding generators to load the CIs, Configs, metadata, etc. I'm hoping that this can replace the `generate_bindings` and `generate_external_bindings` functions, as well some other functions like `library_mode::find_components`. The goal for the new design is to let bindings generators drive the process instead of a function like `generate_external_bindings`. The advantage of this is that it's easier to customize and we don't need to keep adding new hook methods. It also feels simpler overall to me. See mozilla#2640 for a discussion of this. If we adopt this new system, then I think we can use it to remove a bunch of duplicate code. We can keep around the old functions for backwards-compatibility, but I think they can just be wrappers around `BindgenLoader`. Also, we should figure out a nice way to hook this up to the pipeline code. Made `uniffi-bindgen-swift` use the new system. This was mostly to test the code and to serve as an example, but a nice side-benefit is that `uniffi-bindgen-swift` now can handle UDL files. I also added a `uniffi-bindgen-swift` binary file to go with the `uniffi-bindgen` binary.
1 parent 2d1912a commit 1eeb9de

File tree

10 files changed

+279
-36
lines changed

10 files changed

+279
-36
lines changed

uniffi/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,9 @@ name = "uniffi-bindgen"
6868
path = "uniffi-bindgen.rs"
6969
required-features = ["cli"]
7070
doc = false
71+
72+
[[bin]]
73+
name = "uniffi-bindgen-swift"
74+
path = "uniffi-bindgen-swift.rs"
75+
required-features = ["cli"]
76+
doc = false

uniffi/src/cli/swift.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ use uniffi_bindgen::bindings::{generate_swift_bindings, SwiftBindingsOptions};
1313
struct Cli {
1414
#[command(flatten)]
1515
kinds: Kinds,
16-
/// Library path to generate bindings for
17-
library_path: Utf8PathBuf,
16+
#[clap(name = "PATH_TO_LIBRARY_OR_UDL")]
17+
/// UDL File / path to generate bindings for
18+
source: Utf8PathBuf,
1819
/// Directory to generate files in
1920
out_dir: Utf8PathBuf,
2021
/// Generate a XCFramework-compatible modulemap
@@ -65,7 +66,7 @@ impl From<Cli> for SwiftBindingsOptions {
6566
generate_swift_sources: cli.kinds.swift_sources,
6667
generate_headers: cli.kinds.headers,
6768
generate_modulemap: cli.kinds.modulemap,
68-
library_path: cli.library_path,
69+
source: cli.source,
6970
out_dir: cli.out_dir,
7071
xcframework: cli.xcframework,
7172
module_name: cli.module_name,

uniffi/uniffi-bindgen-swift.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use uniffi::uniffi_bindgen_swift;
6+
7+
fn main() {
8+
uniffi_bindgen_swift();
9+
}

uniffi_bindgen/src/bindings/swift/mod.rs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
//! * How to read from and write into a byte buffer.
3030
//!
3131
32-
use crate::{BindingGenerator, Component, GenerationSettings};
33-
use anyhow::{Context, Result};
32+
use crate::{BindgenLoader, BindingGenerator, Component, ComponentInterface, GenerationSettings};
33+
use anyhow::Result;
3434
use camino::Utf8PathBuf;
3535
use fs_err as fs;
3636
use std::process::Command;
@@ -147,6 +147,7 @@ pub fn generate_swift_bindings(options: SwiftBindingsOptions) -> Result<()> {
147147
#[cfg(feature = "cargo-metadata")]
148148
let config_supplier = {
149149
use crate::cargo_metadata::CrateConfigSupplier;
150+
use anyhow::Context;
150151
let mut cmd = cargo_metadata::MetadataCommand::new();
151152
if options.metadata_no_deps {
152153
cmd.no_deps();
@@ -159,17 +160,10 @@ pub fn generate_swift_bindings(options: SwiftBindingsOptions) -> Result<()> {
159160

160161
fs::create_dir_all(&options.out_dir)?;
161162

162-
let mut components =
163-
crate::library_mode::find_components(&options.library_path, &config_supplier)?
164-
// map the TOML configs into a our Config struct
165-
.into_iter()
166-
.map(|Component { ci, config }| {
167-
let config = SwiftBindingGenerator.new_config(&config.into())?;
168-
Ok(Component { ci, config })
169-
})
170-
.collect::<Result<Vec<_>>>()?;
171-
SwiftBindingGenerator
172-
.update_component_configs(&GenerationSettings::default(), &mut components)?;
163+
let loader = BindgenLoader::new(&config_supplier);
164+
let metadata = loader.load_metadata(&options.source)?;
165+
let cis = loader.load_cis(metadata)?;
166+
let components = loader.load_components(cis, parse_config)?;
173167

174168
for Component { ci, config } in &components {
175169
if options.generate_swift_sources {
@@ -185,24 +179,15 @@ pub fn generate_swift_bindings(options: SwiftBindingsOptions) -> Result<()> {
185179
}
186180
}
187181

188-
// find the library name by stripping the extension and leading `lib` from the library path
189-
let library_name = {
190-
let stem = options
191-
.library_path
192-
.file_stem()
193-
.with_context(|| format!("Invalid library path {}", options.library_path))?;
194-
match stem.strip_prefix("lib") {
195-
Some(name) => name,
196-
None => stem,
197-
}
198-
};
182+
// Derive the default module_name/modulemap_filename from the source filename.
183+
let source_basename = loader.source_basename(&options.source);
199184

200185
let module_name = options
201186
.module_name
202-
.unwrap_or_else(|| library_name.to_string());
187+
.unwrap_or_else(|| source_basename.to_string());
203188
let modulemap_filename = options
204189
.modulemap_filename
205-
.unwrap_or_else(|| format!("{library_name}.modulemap"));
190+
.unwrap_or_else(|| format!("{source_basename}.modulemap"));
206191

207192
if options.generate_modulemap {
208193
let mut header_filenames: Vec<_> = components
@@ -223,12 +208,23 @@ pub fn generate_swift_bindings(options: SwiftBindingsOptions) -> Result<()> {
223208
Ok(())
224209
}
225210

211+
fn parse_config(ci: &ComponentInterface, root_toml: toml::Value) -> Result<Config> {
212+
let mut config: Config = match root_toml.get("bindings").and_then(|b| b.get("swift")) {
213+
Some(v) => v.clone().try_into()?,
214+
None => Default::default(),
215+
};
216+
config
217+
.module_name
218+
.get_or_insert_with(|| ci.namespace().into());
219+
Ok(config)
220+
}
221+
226222
#[derive(Debug, Default)]
227223
pub struct SwiftBindingsOptions {
228224
pub generate_swift_sources: bool,
229225
pub generate_headers: bool,
230226
pub generate_modulemap: bool,
231-
pub library_path: Utf8PathBuf,
227+
pub source: Utf8PathBuf,
232228
pub out_dir: Utf8PathBuf,
233229
pub xcframework: bool,
234230
pub module_name: Option<String>,

uniffi_bindgen/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ use std::process::Command;
104104
pub mod bindings;
105105
pub mod interface;
106106
pub mod library_mode;
107+
mod loader;
107108
pub mod macro_metadata;
108109
pub mod pipeline;
109110
pub mod scaffolding;
@@ -120,6 +121,8 @@ pub use library_mode::find_components;
120121
use scaffolding::RustScaffolding;
121122
use uniffi_meta::Type;
122123

124+
pub use loader::BindgenLoader;
125+
123126
/// The options used when creating bindings. Named such
124127
/// it doesn't cause confusion that it's settings specific to
125128
/// the generator itself.

uniffi_bindgen/src/library_mode.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> {
107107
/// calls.
108108
///
109109
/// `config_supplier` is used to find UDL files on disk and load config data.
110-
pub fn find_components(
110+
pub fn find_cis(
111111
library_path: &Utf8Path,
112112
config_supplier: &dyn BindgenCrateConfigSupplier,
113-
) -> Result<Vec<Component<TomlTable>>> {
113+
) -> Result<Vec<ComponentInterface>> {
114114
let items = macro_metadata::extract_from_library(library_path)?;
115115
let mut metadata_groups = create_metadata_groups(&items);
116116
group_metadata(&mut metadata_groups, items)?;
@@ -137,10 +137,29 @@ pub fn find_components(
137137
let crate_name = &group.namespace.crate_name;
138138
let mut ci = ComponentInterface::new(crate_name);
139139
ci.add_metadata(group)?;
140+
ci.set_crate_to_namespace_map(crate_to_namespace_map.clone());
141+
Ok(ci)
142+
})
143+
.collect()
144+
}
145+
146+
/// Find UniFFI components from a shared library file
147+
///
148+
/// This method inspects the library file and creates [ComponentInterface] instances for each
149+
/// component used to build it. It parses the UDL files from `uniffi::include_scaffolding!` macro
150+
/// calls.
151+
///
152+
/// `config_supplier` is used to find UDL files on disk and load config data.
153+
pub fn find_components(
154+
library_path: &Utf8Path,
155+
config_supplier: &dyn BindgenCrateConfigSupplier,
156+
) -> Result<Vec<Component<TomlTable>>> {
157+
find_cis(library_path, config_supplier)?
158+
.into_iter()
159+
.map(|ci| {
140160
let config = config_supplier
141161
.get_toml(ci.crate_name())?
142162
.unwrap_or_default();
143-
ci.set_crate_to_namespace_map(crate_to_namespace_map.clone());
144163
Ok(Component { ci, config })
145164
})
146165
.collect()

0 commit comments

Comments
 (0)