From cade14de0b1ded65eb3a3f5e1ab96cb19aa12e91 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Sat, 17 Aug 2024 15:00:58 +0100 Subject: [PATCH 1/4] Add --no-xcodebuild to build ios --- crates/ubrn_cli/src/ios.rs | 60 ++++++++++++++++++---------- crates/ubrn_common/src/rust_crate.rs | 7 ++++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/crates/ubrn_cli/src/ios.rs b/crates/ubrn_cli/src/ios.rs index d98d766b..849083b4 100644 --- a/crates/ubrn_cli/src/ios.rs +++ b/crates/ubrn_cli/src/ios.rs @@ -97,6 +97,13 @@ pub(crate) struct IOsArgs { #[clap(long, conflicts_with_all = ["sim_only"], default_value = "false")] no_sim: bool, + /// Does not perform the xcodebuild step to generate the xcframework + /// + /// The xcframework will need to be generated externally from this tool. + /// This is useful when adding extra bindings (e.g. Swift) to the project. + #[clap(long, alias = "no-xcframework")] + no_xcodebuild: bool, + #[clap(flatten)] pub(crate) common_args: CommonBuildArgs, } @@ -104,7 +111,6 @@ pub(crate) struct IOsArgs { impl IOsArgs { pub(crate) fn build(&self) -> Result> { let config = self.project_config()?; - let project_root = config.project_root(); let crate_ = &config.crate_; let metadata = crate_.metadata()?; let rust_dir = crate_.directory()?; @@ -112,18 +118,19 @@ impl IOsArgs { let manifest_path = crate_.manifest_path()?; let ios = &config.ios; - let ios_dir = ios.directory(project_root); - let mut library_args = Vec::new(); let mut target_files = Vec::new(); - for target in &ios.targets { - if self.no_sim && target.contains("sim") { - continue; - } - if self.sim_only && !target.contains("sim") { - continue; + let targets = ios.targets.iter().filter(|target| { + let is_sim = target.contains("sim"); + if self.no_sim { + !is_sim + } else if self.sim_only { + is_sim + } else { + true } - + }); + for target in targets { let mut cmd = Command::new("cargo"); cmd.arg("build") .arg("--manifest-path") @@ -137,33 +144,44 @@ impl IOsArgs { cmd.args(ios.cargo_extras.clone()); run_cmd(cmd.current_dir(&rust_dir))?; - // Now we need to get the path to the lib.a file, - // to feed to xcodebuild. + // Now we need to get the path to the lib.a file, to feed to xcodebuild. let library = metadata.library_path(Some(target), profile)?; - if !library.exists() { - anyhow::bail!("Calculated library doesn't exist. This may be because `staticlib` is not in the `crate_type` list in the [[lib]] entry of Cargo.toml."); - } + metadata.library_path_exists(&library)?; + target_files.push(library); + } + + if !self.no_xcodebuild { + self.create_xcframework(&config, &target_files)?; + } + Ok(target_files) + } + + fn create_xcframework( + &self, + config: &ProjectConfig, + target_files: &Vec, + ) -> Result<(), anyhow::Error> { + let ios = &config.ios; + let project_root = config.project_root(); + let ios_dir = ios.directory(project_root); + let mut library_args = Vec::new(); + for library in target_files { // :eyes: single dash arg. library_args.push("-library".to_string()); library_args.push(library.to_string()); - target_files.push(library); } - let framework_path = ios.framework_path(project_root); if framework_path.exists() { rm_dir(&framework_path)?; } let mut cmd = Command::new("xcodebuild"); - // :eyes: single dash arg. cmd.arg("-create-xcframework") .args(library_args) .arg("-output") .arg(&framework_path) .args(ios.xcodebuild_extras.clone()); - run_cmd(cmd.current_dir(ios_dir))?; - - Ok(target_files) + Ok(()) } pub(crate) fn project_config(&self) -> Result { diff --git a/crates/ubrn_common/src/rust_crate.rs b/crates/ubrn_common/src/rust_crate.rs index dc242040..6ee981ec 100644 --- a/crates/ubrn_common/src/rust_crate.rs +++ b/crates/ubrn_common/src/rust_crate.rs @@ -36,6 +36,13 @@ impl CrateMetadata { }) } + pub fn library_path_exists(&self, path: &Utf8Path) -> Result<()> { + if !path.exists() { + anyhow::bail!("Library doesn't exist. This may be because `staticlib` is not in the `crate-type` list in the [lib] entry of Cargo.toml: {}", self.manifest_path()); + } + Ok(()) + } + pub fn library_file(&self, target: Option<&str>) -> String { let ext = so_extension(target); format!("lib{}.{ext}", &self.library_name) From 9fcc4959ead64d3a74e3a5d0b9810b1b6ece3a22 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Sat, 17 Aug 2024 16:26:22 +0100 Subject: [PATCH 2/4] Add --no-cargo to build ios and build android --- crates/ubrn_cli/src/android.rs | 133 +++++++++++++++++++-------- crates/ubrn_cli/src/building.rs | 8 ++ crates/ubrn_cli/src/ios.rs | 111 +++++++++++++++------- crates/ubrn_common/src/rust_crate.rs | 6 +- xtask/src/run/mod.rs | 2 +- xtask/src/run/rust_crate.rs | 2 +- 6 files changed, 189 insertions(+), 73 deletions(-) diff --git a/crates/ubrn_cli/src/android.rs b/crates/ubrn_cli/src/android.rs index 1c576cd1..c82eae55 100644 --- a/crates/ubrn_cli/src/android.rs +++ b/crates/ubrn_cli/src/android.rs @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ use serde::Deserialize; -use std::{fmt::Display, fs, process::Command, str::FromStr}; +use std::{collections::HashMap, fmt::Display, fs, process::Command, str::FromStr}; use clap::Args; @@ -15,6 +15,7 @@ use ubrn_common::{mk_dir, rm_dir, run_cmd, CrateMetadata}; use crate::{ building::{CommonBuildArgs, ExtraArgs}, config::ProjectConfig, + rust::CrateConfig, workspace, }; @@ -122,61 +123,119 @@ impl AndroidArgs { let config: ProjectConfig = self.project_config()?; let project_root = config.project_root(); let crate_ = &config.crate_; - let rust_dir = crate_.directory()?; - let manifest_path = crate_.manifest_path()?; let android = &config.android; - let jni_libs = android.jni_libs(project_root); - rm_dir(&jni_libs)?; - - let mut target_files: Vec<_> = Vec::new(); - for target in &android.targets { - let target = target.parse::()?; - let mut cmd = Command::new("cargo"); - cmd.arg("ndk") - .arg("--manifest-path") - .arg(&manifest_path) - .arg("--target") - .arg(target.to_string()) - .arg("--platform") - .arg(format!("{}", android.api_level)); - - if !self.common_args.release { - cmd.arg("--no-strip"); + let cargo_extras = &android.cargo_extras; + let api_level = android.api_level; + let target_files = if self.common_args.no_cargo { + let files = self.find_existing(&crate_.metadata()?, &android.targets); + if !files.is_empty() { + files + } else { + self.cargo_build_all(crate_, &android.targets, cargo_extras, api_level)? } + } else { + self.cargo_build_all(crate_, &android.targets, cargo_extras, api_level)? + }; - cmd.arg("--").arg("build"); - if self.common_args.release { - cmd.arg("--release"); - } - - cmd.args(android.cargo_extras.clone()); - - run_cmd(cmd.current_dir(&rust_dir))?; - let metadata = crate_.metadata()?; - let src_lib = metadata.library_path( - Some(target.triple()), - CrateMetadata::profile(self.common_args.release), - )?; + let metadata = crate_.metadata()?; + let jni_libs = android.jni_libs(project_root); + rm_dir(&jni_libs)?; + for (target, library) in &target_files { let dst_dir = jni_libs.join(target.to_string()); mk_dir(&dst_dir)?; let dst_lib = dst_dir.join(metadata.library_file(Some(target.triple()))); - fs::copy(&src_lib, &dst_lib)?; - - target_files.push(src_lib); + fs::copy(library, &dst_lib)?; } + Ok(target_files.into_values().collect()) + } + + fn cargo_build_all( + &self, + crate_: &CrateConfig, + targets: &[String], + cargo_extras: &ExtraArgs, + api_level: usize, + ) -> Result> { + let rust_dir = crate_.directory()?; + let manifest_path = crate_.manifest_path()?; + let metadata = crate_.metadata()?; + let mut target_files = HashMap::new(); + let profile = self.common_args.profile(); + for target in targets { + let target = + self.cargo_build(target, &manifest_path, cargo_extras, api_level, &rust_dir)?; + let library = metadata.library_path(Some(target.triple()), profile); + metadata.library_path_exists(&library)?; + target_files.insert(target, library); + } Ok(target_files) } + fn cargo_build( + &self, + target: &str, + manifest_path: &Utf8PathBuf, + cargo_extras: &ExtraArgs, + api_level: usize, + rust_dir: &Utf8PathBuf, + ) -> Result { + let target = target.parse::()?; + let mut cmd = Command::new("cargo"); + cmd.arg("ndk") + .arg("--manifest-path") + .arg(manifest_path) + .arg("--target") + .arg(target.to_string()) + .arg("--platform") + .arg(format!("{}", api_level)); + if !self.common_args.release { + cmd.arg("--no-strip"); + } + cmd.arg("--").arg("build"); + if self.common_args.release { + cmd.arg("--release"); + } + cmd.args(cargo_extras.clone()); + run_cmd(cmd.current_dir(rust_dir))?; + Ok(target) + } + + fn find_existing( + &self, + metadata: &CrateMetadata, + targets: &[String], + ) -> HashMap { + let profile = self.common_args.profile(); + targets + .iter() + .filter_map(|target| { + let target = target.parse::(); + match target { + Ok(target) => Some(target), + Err(_) => None, + } + }) + .filter_map(|target| { + let library = metadata.library_path(Some(target.triple()), profile); + if library.exists() { + Some((target, library)) + } else { + None + } + }) + .collect() + } + pub(crate) fn project_config(&self) -> Result { self.config.clone().try_into() } } -#[derive(Debug, Deserialize, Default, Clone)] +#[derive(Debug, Deserialize, Default, Clone, Hash, PartialEq, Eq)] pub enum Target { #[serde(rename = "armeabi-v7a")] ArmeabiV7a, diff --git a/crates/ubrn_cli/src/building.rs b/crates/ubrn_cli/src/building.rs index 858175b0..eb06d8c0 100644 --- a/crates/ubrn_cli/src/building.rs +++ b/crates/ubrn_cli/src/building.rs @@ -109,6 +109,14 @@ pub(crate) struct CommonBuildArgs { #[clap(long, short, default_value = "false")] pub(crate) release: bool, + /// If the Rust library has been built for at least one target, then + /// don't re-run cargo build. + /// + /// This may be useful if you are using a pre-built library or are + /// managing the build process yourself. + #[clap(long)] + pub(crate) no_cargo: bool, + /// Optionally generate the bindings and turbo-module code for the crate #[clap(long = "and-generate", short = 'g')] pub(crate) and_generate: bool, diff --git a/crates/ubrn_cli/src/ios.rs b/crates/ubrn_cli/src/ios.rs index 849083b4..c6c23d08 100644 --- a/crates/ubrn_cli/src/ios.rs +++ b/crates/ubrn_cli/src/ios.rs @@ -11,11 +11,12 @@ use camino::{Utf8Path, Utf8PathBuf}; use clap::Args; use heck::ToUpperCamelCase; use serde::Deserialize; -use ubrn_common::{rm_dir, run_cmd}; +use ubrn_common::{rm_dir, run_cmd, CrateMetadata}; use crate::{ building::{CommonBuildArgs, ExtraArgs}, config::ProjectConfig, + rust::CrateConfig, workspace, }; @@ -112,48 +113,81 @@ impl IOsArgs { pub(crate) fn build(&self) -> Result> { let config = self.project_config()?; let crate_ = &config.crate_; - let metadata = crate_.metadata()?; - let rust_dir = crate_.directory()?; - let profile = self.common_args.profile(); - let manifest_path = crate_.manifest_path()?; - let ios = &config.ios; - let mut target_files = Vec::new(); - let targets = ios.targets.iter().filter(|target| { - let is_sim = target.contains("sim"); - if self.no_sim { - !is_sim - } else if self.sim_only { - is_sim + let targets = ios + .targets + .iter() + .filter(|target| { + let is_sim = target.contains("sim"); + if self.no_sim { + !is_sim + } else if self.sim_only { + is_sim + } else { + true + } + }) + .map(String::clone) + .collect::>(); + + let target_files = if self.common_args.no_cargo { + let files = self.find_existing(&crate_.metadata()?, &targets); + if !files.is_empty() { + files } else { - true - } - }); - for target in targets { - let mut cmd = Command::new("cargo"); - cmd.arg("build") - .arg("--manifest-path") - .arg(&manifest_path) - .arg("--target") - .arg(target); - if self.common_args.release { - cmd.arg("--release"); + self.cargo_build_all(crate_, &targets, &ios.cargo_extras)? } + } else { + self.cargo_build_all(crate_, &targets, &ios.cargo_extras)? + }; - cmd.args(ios.cargo_extras.clone()); - run_cmd(cmd.current_dir(&rust_dir))?; + if !self.no_xcodebuild { + self.create_xcframework(&config, &target_files)?; + } + Ok(target_files) + } + + fn cargo_build_all( + &self, + crate_: &CrateConfig, + targets: &[String], + cargo_extras: &ExtraArgs, + ) -> Result> { + let mut target_files = Vec::new(); + let metadata = crate_.metadata()?; + let rust_dir = crate_.directory()?; + let manifest_path = crate_.manifest_path()?; + for target in targets { + self.cargo_build(&manifest_path, target, cargo_extras, &rust_dir)?; // Now we need to get the path to the lib.a file, to feed to xcodebuild. - let library = metadata.library_path(Some(target), profile)?; + let library = metadata.library_path(Some(target), self.common_args.profile()); metadata.library_path_exists(&library)?; target_files.push(library); } + Ok(target_files) + } - if !self.no_xcodebuild { - self.create_xcframework(&config, &target_files)?; + fn cargo_build( + &self, + manifest_path: &Utf8PathBuf, + target: &String, + cargo_extras: &ExtraArgs, + rust_dir: &Utf8PathBuf, + ) -> Result<()> { + let mut cmd = Command::new("cargo"); + cmd.arg("build") + .arg("--manifest-path") + .arg(manifest_path) + .arg("--target") + .arg(target); + if self.common_args.release { + cmd.arg("--release"); } - Ok(target_files) + cmd.args(cargo_extras.clone()); + run_cmd(cmd.current_dir(rust_dir))?; + Ok(()) } fn create_xcframework( @@ -184,6 +218,21 @@ impl IOsArgs { Ok(()) } + fn find_existing(&self, metadata: &CrateMetadata, targets: &[String]) -> Vec { + let profile = self.common_args.profile(); + targets + .iter() + .filter_map(|target| { + let library = metadata.library_path(Some(target), profile); + if library.exists() { + Some(library) + } else { + None + } + }) + .collect::>() + } + pub(crate) fn project_config(&self) -> Result { self.config.clone().try_into() } diff --git a/crates/ubrn_common/src/rust_crate.rs b/crates/ubrn_common/src/rust_crate.rs index 6ee981ec..66e89b6e 100644 --- a/crates/ubrn_common/src/rust_crate.rs +++ b/crates/ubrn_common/src/rust_crate.rs @@ -28,12 +28,12 @@ impl CrateMetadata { } } - pub fn library_path(&self, target: Option<&str>, profile: &str) -> Result { + pub fn library_path(&self, target: Option<&str>, profile: &str) -> Utf8PathBuf { let library_name = self.library_file(target); - Ok(match target { + match target { Some(t) => self.target_dir.join(t).join(profile).join(library_name), None => self.target_dir.join(profile).join(library_name), - }) + } } pub fn library_path_exists(&self, path: &Utf8Path) -> Result<()> { diff --git a/xtask/src/run/mod.rs b/xtask/src/run/mod.rs index c918add1..e7585881 100644 --- a/xtask/src/run/mod.rs +++ b/xtask/src/run/mod.rs @@ -74,7 +74,7 @@ impl RunCmd { Ok(Some(so_file)) } (Some(crate_), None, Some(bindings)) => { - let crate_lib = crate_.library_path(None, release)?; + let crate_lib = crate_.library_path(None, release); let target_dir = crate_.target_dir(); let lib_name = crate_.library_name(); let cpp_files = bindings.generate(&crate_lib)?; diff --git a/xtask/src/run/rust_crate.rs b/xtask/src/run/rust_crate.rs index 6ecee916..2ca9a3e6 100644 --- a/xtask/src/run/rust_crate.rs +++ b/xtask/src/run/rust_crate.rs @@ -32,7 +32,7 @@ impl CrateArg { pub(crate) fn cargo_build(&self, clean: bool) -> Result { let metadata = CrateMetadata::try_from(self.crate_dir.clone().expect("crate has no path"))?; let profile = CrateMetadata::profile(self.release); - let lib_path = metadata.library_path(None, profile)?; + let lib_path = metadata.library_path(None, profile); if lib_path.exists() && clean { metadata.cargo_clean()?; } From 5ca5b75b4ec09259224fae5dc1417767d5e481db Mon Sep 17 00:00:00 2001 From: James Hugman Date: Sat, 17 Aug 2024 16:53:27 +0100 Subject: [PATCH 3/4] Add --no-jniLibs to build android --- crates/ubrn_cli/src/android.rs | 59 +++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/crates/ubrn_cli/src/android.rs b/crates/ubrn_cli/src/android.rs index c82eae55..c2187d3f 100644 --- a/crates/ubrn_cli/src/android.rs +++ b/crates/ubrn_cli/src/android.rs @@ -116,38 +116,47 @@ pub(crate) struct AndroidArgs { #[clap(flatten)] pub(crate) common_args: CommonBuildArgs, + + /// Suppress the copying of the Rust library into the JNI library directories. + #[clap(long = "no-jniLibs")] + no_jni_libs: bool, } impl AndroidArgs { pub(crate) fn build(&self) -> Result> { let config: ProjectConfig = self.project_config()?; - let project_root = config.project_root(); + let crate_ = &config.crate_; let android = &config.android; - - let cargo_extras = &android.cargo_extras; - let api_level = android.api_level; let target_files = if self.common_args.no_cargo { let files = self.find_existing(&crate_.metadata()?, &android.targets); if !files.is_empty() { files } else { - self.cargo_build_all(crate_, &android.targets, cargo_extras, api_level)? + self.cargo_build_all( + crate_, + &android.targets, + &android.cargo_extras, + android.api_level, + )? } } else { - self.cargo_build_all(crate_, &android.targets, cargo_extras, api_level)? + self.cargo_build_all( + crate_, + &android.targets, + &android.cargo_extras, + android.api_level, + )? }; - let metadata = crate_.metadata()?; - let jni_libs = android.jni_libs(project_root); - rm_dir(&jni_libs)?; - for (target, library) in &target_files { - let dst_dir = jni_libs.join(target.to_string()); - mk_dir(&dst_dir)?; - - let dst_lib = dst_dir.join(metadata.library_file(Some(target.triple()))); - fs::copy(library, &dst_lib)?; + if !self.no_jni_libs { + let project_root = config.project_root(); + self.copy_into_jni_libs( + &crate_.metadata()?, + &android.jni_libs(project_root), + &target_files, + )?; } Ok(target_files.into_values().collect()) @@ -230,6 +239,26 @@ impl AndroidArgs { .collect() } + fn copy_into_jni_libs( + &self, + metadata: &CrateMetadata, + jni_libs: &Utf8Path, + target_files: &HashMap, + ) -> Result<()> { + println!("-- Copying into jniLibs directory"); + println!("rm -Rf {jni_libs}"); + rm_dir(jni_libs)?; + for (target, library) in target_files { + let dst_dir = jni_libs.join(target.to_string()); + mk_dir(&dst_dir)?; + + let dst_lib = dst_dir.join(metadata.library_file(Some(target.triple()))); + println!("cp {library} {dst_lib}"); + fs::copy(library, &dst_lib)?; + } + Ok(()) + } + pub(crate) fn project_config(&self) -> Result { self.config.clone().try_into() } From 6a36625b8e147475234b281d0b5a17e98587b27f Mon Sep 17 00:00:00 2001 From: James Hugman Date: Sat, 17 Aug 2024 17:28:06 +0100 Subject: [PATCH 4/4] Add generate all convenience method --- crates/ubrn_cli/src/android.rs | 4 +++ crates/ubrn_cli/src/building.rs | 42 ++++------------------ crates/ubrn_cli/src/generate.rs | 63 +++++++++++++++++++++++++++++++-- crates/ubrn_cli/src/ios.rs | 4 +++ 4 files changed, 75 insertions(+), 38 deletions(-) diff --git a/crates/ubrn_cli/src/android.rs b/crates/ubrn_cli/src/android.rs index c2187d3f..d5da4465 100644 --- a/crates/ubrn_cli/src/android.rs +++ b/crates/ubrn_cli/src/android.rs @@ -262,6 +262,10 @@ impl AndroidArgs { pub(crate) fn project_config(&self) -> Result { self.config.clone().try_into() } + + pub(crate) fn config(&self) -> Utf8PathBuf { + self.config.clone() + } } #[derive(Debug, Deserialize, Default, Clone, Hash, PartialEq, Eq)] diff --git a/crates/ubrn_cli/src/building.rs b/crates/ubrn_cli/src/building.rs index eb06d8c0..7eae50e4 100644 --- a/crates/ubrn_cli/src/building.rs +++ b/crates/ubrn_cli/src/building.rs @@ -8,10 +8,9 @@ use anyhow::{anyhow, Result}; use camino::Utf8PathBuf; use clap::{Args, Subcommand}; use serde::Deserialize; -use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs}; use ubrn_common::CrateMetadata; -use crate::{android::AndroidArgs, config::ProjectConfig, ios::IOsArgs}; +use crate::{android::AndroidArgs, generate::GenerateAllArgs, ios::IOsArgs}; #[derive(Args, Debug)] pub(crate) struct BuildArgs { @@ -30,7 +29,7 @@ pub(crate) enum BuildCmd { impl BuildArgs { pub(crate) fn build(&self) -> Result<()> { let lib_file = self.cmd.build()?; - if self.and_generate() { + if self.cmd.and_generate() { self.generate(lib_file)?; } @@ -38,36 +37,7 @@ impl BuildArgs { } fn generate(&self, lib_file: Utf8PathBuf) -> Result<()> { - let project = self.cmd.project_config()?; - let root = project.project_root(); - let pwd = ubrn_common::pwd()?; - let modules = { - let dir = project.crate_.directory()?; - ubrn_common::cd(&dir)?; - let ts_dir = project.bindings.ts_path(root); - let cpp_dir = project.bindings.cpp_path(root); - let config = project.bindings.uniffi_toml_path(root); - if let Some(ref file) = config { - if !file.exists() { - anyhow::bail!("uniffi.toml file {:?} does not exist. Either delete the uniffiToml property or supply a file", file) - } - } - let bindings = BindingsArgs::new( - SourceArgs::library(&lib_file).with_config(config), - OutputArgs::new(&ts_dir, &cpp_dir, false), - ); - - bindings.run()? - }; - ubrn_common::cd(&pwd)?; - - let rust_crate = project.crate_.metadata()?; - crate::codegen::render_files(project, rust_crate, modules)?; - Ok(()) - } - - fn and_generate(&self) -> bool { - self.cmd.and_generate() + GenerateAllArgs::new(lib_file, self.cmd.config()).run() } } @@ -84,10 +54,10 @@ impl BuildCmd { .ok_or_else(|| anyhow!("No targets were specified")) } - pub(crate) fn project_config(&self) -> Result { + fn config(&self) -> Utf8PathBuf { match self { - Self::Android(a) => a.project_config(), - Self::Ios(a) => a.project_config(), + Self::Android(a) => a.config(), + Self::Ios(a) => a.config(), } } diff --git a/crates/ubrn_cli/src/generate.rs b/crates/ubrn_cli/src/generate.rs index 886f6edb..e1344a0f 100644 --- a/crates/ubrn_cli/src/generate.rs +++ b/crates/ubrn_cli/src/generate.rs @@ -4,10 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ use anyhow::Result; +use camino::Utf8PathBuf; use clap::{Args, Subcommand}; -use ubrn_bindgen::BindingsArgs; +use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs}; -use crate::codegen::TurboModuleArgs; +use crate::{codegen::TurboModuleArgs, config::ProjectConfig}; #[derive(Args, Debug)] pub(crate) struct GenerateArgs { @@ -27,6 +28,11 @@ pub(crate) enum GenerateCmd { Bindings(BindingsArgs), /// Generate the TurboModule code to plug the bindings into the app TurboModule(TurboModuleArgs), + /// Generate the Bindings and TurboModule code from a library + /// file and a YAML config file. + /// + /// This is the second step of the `--and-generate` option of the build command. + All(GenerateAllArgs), } impl GenerateCmd { @@ -40,6 +46,59 @@ impl GenerateCmd { t.run()?; Ok(()) } + Self::All(t) => { + t.run()?; + Ok(()) + } } } } + +#[derive(Args, Debug)] +pub(crate) struct GenerateAllArgs { + /// The configuration file for this project + #[clap(long)] + config: Utf8PathBuf, + + /// A path to staticlib file. + lib_file: Utf8PathBuf, +} + +impl GenerateAllArgs { + pub(crate) fn new(lib_file: Utf8PathBuf, config: Utf8PathBuf) -> Self { + Self { lib_file, config } + } + + pub(crate) fn run(&self) -> Result<()> { + let project = self.project_config()?; + let root = project.project_root(); + let pwd = ubrn_common::pwd()?; + let lib_file = pwd.join(&self.lib_file); + let modules = { + let dir = project.crate_.directory()?; + ubrn_common::cd(&dir)?; + let ts_dir = project.bindings.ts_path(root); + let cpp_dir = project.bindings.cpp_path(root); + let config = project.bindings.uniffi_toml_path(root); + if let Some(ref file) = config { + if !file.exists() { + anyhow::bail!("uniffi.toml file {:?} does not exist. Either delete the uniffiToml property or supply a file", file) + } + } + let bindings = BindingsArgs::new( + SourceArgs::library(&lib_file).with_config(config), + OutputArgs::new(&ts_dir, &cpp_dir, false), + ); + + bindings.run()? + }; + ubrn_common::cd(&pwd)?; + let rust_crate = project.crate_.metadata()?; + crate::codegen::render_files(project, rust_crate, modules)?; + Ok(()) + } + + fn project_config(&self) -> Result { + self.config.clone().try_into() + } +} diff --git a/crates/ubrn_cli/src/ios.rs b/crates/ubrn_cli/src/ios.rs index c6c23d08..8b14d968 100644 --- a/crates/ubrn_cli/src/ios.rs +++ b/crates/ubrn_cli/src/ios.rs @@ -236,4 +236,8 @@ impl IOsArgs { pub(crate) fn project_config(&self) -> Result { self.config.clone().try_into() } + + pub(crate) fn config(&self) -> Utf8PathBuf { + self.config.clone() + } }