diff --git a/Cargo.toml b/Cargo.toml
index 2e94189..3daea86 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "xcframework"
-version = "0.1.0-alpha.6"
+version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/human-solutions/xcframework"
@@ -12,14 +12,16 @@ categories = ["development-tools::cargo-plugins", "development-tools::ffi"]
[dependencies]
anyhow = "1.0"
-cargo_metadata = { version = "0.18", features = ["builder"] }
-camino = "1.1"
+camino-fs = { git = "https://github.com/human-solutions/camino-fs", tag = "v0.1.3", features = [
+ "serde",
+] }
+# camino-fs = { path = "../camino-fs", features = ["serde"] }
+cargo_metadata = { version = "0.19", features = ["builder"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
yansi = "1.0"
fs-err = "3.0"
zip-extensions = { version = "0.8", default-features = false }
-fs_extra = "1.2"
xshell = "0.2.6"
glob = "0.3.1"
xflags = "0.3"
diff --git a/README.md b/README.md
index b3a06ff..ea603f4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
> **⚠️ WARNING**
>
-> This is work in progress and is not ready for use
+> This has not yet been thoroughly tested. Use at your own risk.
A Cargo plugin and library for building Apple XCFrameworks from Rust libraries
@@ -46,7 +46,7 @@ The built XCFramework is named after the top-level module name declared in the `
A typical such file looks like this:
```cpp
- The XCFramework will be named 'MyModuleName.xcframework'
+ // The XCFramework will be named 'MyModuleName.xcframework'
framework module MyModuleName {
// a header file in the same directory as the modulemap
header "mylib.h"
@@ -62,45 +62,55 @@ Cargo.toml parameters in section `[package.metadata.xcframework]`.
# Note that the modulemap needs to be present in the directory because the
# module name declared in it is used for the framework name.
include-dir = "my-bin-name"
+
# The library type. Can be staticlib or cdylib
#
# Optional. This is only necessary if both library types are configured in the
# [lib] sections `crate-type` parameter. Overridden by the command line parameter `--lib-type`.
lib-type = "staticlib"
+
# Whether to zip the resulting XCFramework
#
# Optional. Defaults to true.
-zip = true
+zip = false
+
# Enable Cargo to compile the standard library itself as part of a crate graph compilation.
# If enabled either run with `cargo +nightly xcframework`, set the default toolchain to nightly
# or set run `rustup override set nightly` in the project directory.
#
# Optional. Defaults to false. Requires nightly. Only useful for staticlib's, ignored for cdylibs.
build-std = false
+
# Whether to build for macOS
#
# Optional. Defaults to false.
macOS = false
+
# The macOS target triples
#
# Optional. Defaults to ["x86_64-apple-darwin", "aarch64-apple-darwin"].
macOS-targets = ["x86_64-apple-darwin", "aarch64-apple-darwin"]
+
# Whether to build the simulator targets. Not used when building for macOS.
#
# Optional. Defaults to false
simulators = false
+
# Whether to build for iOS
#
# Optional. Defaults to false.
iOS = false
+
# The iOS target triples
#
# Optional. Defaults to ["aarch64-apple-ios"].
iOS-targets = ["aarch64-apple-ios"]
+
# The iOS simulator target triples. Only used if `simulators` and `iOS` are true.
#
# Optional. Defaults to ["aarch64-apple-ios-sim", "x86_64-apple-ios"]
iOS-simulator-targets = ["aarch64-apple-ios-sim", "x86_64-apple-ios"]
+
# If there is interest, watchOS and tvOS can be added as well.
```
diff --git a/examples/end-to-end/mymath-lib/Cargo.toml b/examples/end-to-end/mymath-lib/Cargo.toml
index 3572ffc..70bd25d 100644
--- a/examples/end-to-end/mymath-lib/Cargo.toml
+++ b/examples/end-to-end/mymath-lib/Cargo.toml
@@ -8,7 +8,7 @@ name = "mymath"
crate-type = ["staticlib", "cdylib"]
[package.metadata.xcframework]
-lib-type = "staticlib"
+lib-type = "cdylib"
include-dir = "include"
iOS = true
macOS = true
diff --git a/examples/end-to-end/swift-exe/Package.swift b/examples/end-to-end/swift-exe/Package.swift
index 9e52a63..a18aca4 100644
--- a/examples/end-to-end/swift-exe/Package.swift
+++ b/examples/end-to-end/swift-exe/Package.swift
@@ -1,20 +1,21 @@
// swift-tools-version:5.7
import PackageDescription
+
let package = Package(
- name: "SwiftExe",
- products: [
- .library(
- name: "MyMath",
- targets: ["MyMath"]),
- ],
- dependencies: [],
- targets: [
- .executableTarget(
- name: "swift-cmd",
- dependencies: ["MyMath"]),
- .binaryTarget(
- name: "MyMath",
- path: "../mymath-lib/target/MyMath.xcframework.zip"
- ),
- ]
+ name: "SwiftExe",
+ products: [
+ .library(
+ name: "MyMath",
+ targets: ["MyMath"])
+ ],
+ dependencies: [],
+ targets: [
+ .executableTarget(
+ name: "swift-cmd",
+ dependencies: ["MyMath"]),
+ .binaryTarget(
+ name: "MyMath",
+ path: "../mymath-lib/target/MyMath.xcframework"
+ ),
+ ]
)
diff --git a/src/cmd/cargo.rs b/src/cmd/cargo.rs
index cadbd5f..1b8ef13 100644
--- a/src/cmd/cargo.rs
+++ b/src/cmd/cargo.rs
@@ -6,6 +6,7 @@ pub fn build(conf: &Configuration) -> Result<()> {
let mut args: Vec = vec![];
args.push("build".into());
+ args.push("--color=always".into());
if conf.target_dir != "target" {
args.push(format!("--target-dir={}", conf.target_dir));
diff --git a/src/cmd/modulemap.rs b/src/cmd/modulemap.rs
index 6bc160f..c5a5550 100644
--- a/src/cmd/modulemap.rs
+++ b/src/cmd/modulemap.rs
@@ -2,11 +2,20 @@ use std::io;
use crate::conf::Configuration;
use anyhow::{bail, Context, Result};
-use fs_err::File;
+use camino_fs::*;
+use std::fs::File;
pub fn get_module_name(conf: &Configuration) -> Result {
- let mm = conf.cargo_section.include_dir.join("module.modulemap");
- let file = File::open(&mm)?;
+ let mm_files = ls_modulemap_files(&conf.cargo_section.include_dir)?;
+ if mm_files.len() != 1 {
+ bail!(
+ "Expected one modulemap file in include directory, found {count}: {mm_files:?} in {dir}",
+ count = mm_files.len(),
+ dir = conf.cargo_section.include_dir
+ );
+ }
+ let mm = &mm_files[0];
+ let file = File::open(mm)?;
let content = io::read_to_string(&file)?;
parse_module_name(&content).context(format!(
@@ -32,3 +41,11 @@ fn parse_module_name(content: &str) -> Result {
}
Ok(module.to_string())
}
+
+fn ls_modulemap_files(dir: &Utf8Path) -> Result> {
+ Ok(dir
+ .ls()
+ .files()
+ .filter(|path| path.extension().map_or(false, |ext| ext == "modulemap"))
+ .collect())
+}
diff --git a/src/conf/args.rs b/src/conf/args.rs
index 31d38a0..c225aa3 100644
--- a/src/conf/args.rs
+++ b/src/conf/args.rs
@@ -1,4 +1,4 @@
-use camino::Utf8PathBuf;
+use camino_fs::Utf8PathBuf;
use super::LibType;
diff --git a/src/conf/configuration.rs b/src/conf/configuration.rs
index 2039dba..0b4be01 100644
--- a/src/conf/configuration.rs
+++ b/src/conf/configuration.rs
@@ -1,9 +1,9 @@
use std::cell::RefCell;
use crate::cmd::modulemap;
-use anyhow::{bail, Result};
-use camino::Utf8PathBuf;
-use cargo_metadata::MetadataCommand;
+use anyhow::{anyhow, bail, Context, Result};
+use camino_fs::Utf8PathBuf;
+use cargo_metadata::{Metadata, MetadataCommand, Package, TargetKind};
use super::{CliArgs, LibType, XCFrameworkConfiguration};
@@ -23,52 +23,23 @@ pub struct Configuration {
}
impl Configuration {
- pub fn load(mut cli: CliArgs) -> Result {
- let manifest_path = cli
- .manifest_path
- .clone()
- .unwrap_or_else(|| Utf8PathBuf::from("Cargo.toml"));
- let mut dir = manifest_path.clone();
- dir.pop();
-
- let target_dir = cli.target_dir.clone().unwrap_or_else(|| dir.join("target"));
+ pub fn new(
+ metadata: &Metadata,
+ package: &Package,
+ mut cli: CliArgs,
+ xc_conf: XCFrameworkConfiguration,
+ ) -> Result {
+ // Use the target directory from the CLI, or the one from the Cargo.toml
+ let target_dir = cli
+ .target_dir
+ .as_ref()
+ .unwrap_or(&metadata.target_directory)
+ .clone();
let build_dir = target_dir.join("xcframework");
-
- let metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?;
-
- let Some(package) = metadata.root_package() else {
- anyhow::bail!("Could not find root package in metadata");
- };
-
- let staticlib = package.targets.iter().find(|t| {
- t.kind.contains(&"staticlib".to_string()) || t.kind.contains(&"staticlib".to_string())
- });
- let dylib = package.targets.iter().find(|t| {
- t.kind.contains(&"cdylib".to_string()) || t.kind.contains(&"cdylib".to_string())
- });
-
- let xc_conf = XCFrameworkConfiguration::parse(&package.metadata, &dir)?;
-
let wanted_lib_type = cli.lib_type.clone().or_else(|| xc_conf.lib_type.clone());
- use LibType::*;
- let (lib_type, target) = match (staticlib, dylib, wanted_lib_type) {
- (Some(staticlib), None, None) => (StaticLib, staticlib),
- (Some(staticlib), _, Some(StaticLib)) => (StaticLib, staticlib),
- (Some(_staticlib), None, Some(CDyLib)) => {
- bail!("Please add 'cdylib' to '[lib] crate-type' in Cargo.toml")
- }
- (None, Some(dylib), None) => (CDyLib, dylib),
- (_, Some(dylib), Some(CDyLib)) => (CDyLib, dylib),
- (_, Some(_dylib), Some(StaticLib)) => {
- bail!("Please add 'staticlib' to '[lib] crate-type' in Cargo.toml")
- }
- (Some(_), Some(_), None) => {
- bail!("Please set '[package.metadata.xcframework] lib-type' in Cargo.toml")
- }
- (None, None, _) => bail!("Missing '[lib] crate-type' in Cargo.toml"),
- };
+ let (lib_type, target) = get_libtype(package, wanted_lib_type)?;
if xc_conf.build_std && lib_type == LibType::StaticLib {
let already_set = cli
@@ -84,7 +55,6 @@ impl Configuration {
}
}
}
-
Ok(Self {
cargo_section: xc_conf,
cli,
@@ -96,6 +66,38 @@ impl Configuration {
})
}
+ pub fn load(cli: CliArgs) -> Result {
+ let manifest_path = cli
+ .manifest_path
+ .clone()
+ .unwrap_or_else(|| Utf8PathBuf::from("Cargo.toml"));
+ let mut dir = manifest_path.clone();
+ dir.pop();
+
+ let metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?;
+
+ let package = if let Some(package) = &cli.package {
+ metadata
+ .workspace_packages()
+ .iter()
+ .find(|p| &p.name == package)
+ .ok_or(anyhow!("Could not find package '{package}'"))?
+ } else {
+ metadata
+ .root_package()
+ .ok_or(anyhow!("Could not find root package in metadata"))?
+ };
+
+ let Some(section) = package.metadata.get("xcframework") else {
+ bail!("Missing [package.metadata.xcframework] section in Cargo.toml");
+ };
+
+ let xc_conf = XCFrameworkConfiguration::parse(section, &dir, true)
+ .context("Error in Cargo.toml section [package.metadata.xcframework]")?;
+
+ Self::new(&metadata, package, cli, xc_conf)
+ }
+
pub fn module_name(&self) -> Result {
let name = self.module_name.borrow().clone();
if let Some(name) = name {
@@ -115,3 +117,33 @@ impl Configuration {
}
}
}
+
+fn get_libtype(
+ package: &Package,
+ libtype: Option,
+) -> Result<(LibType, &cargo_metadata::Target)> {
+ let staticlib = package.targets.iter().find(|t| {
+ t.kind.contains(&TargetKind::StaticLib) || t.kind.contains(&TargetKind::StaticLib)
+ });
+ let dylib = package
+ .targets
+ .iter()
+ .find(|t| t.kind.contains(&TargetKind::CDyLib) || t.kind.contains(&TargetKind::CDyLib));
+ use LibType::*;
+ Ok(match (staticlib, dylib, libtype) {
+ (Some(staticlib), None, None) => (StaticLib, staticlib),
+ (Some(staticlib), _, Some(StaticLib)) => (StaticLib, staticlib),
+ (Some(_staticlib), None, Some(CDyLib)) => {
+ bail!("Please add 'cdylib' to '[lib] crate-type' in Cargo.toml")
+ }
+ (None, Some(dylib), None) => (CDyLib, dylib),
+ (_, Some(dylib), Some(CDyLib)) => (CDyLib, dylib),
+ (_, Some(_dylib), Some(StaticLib)) => {
+ bail!("Please add 'staticlib' to '[lib] crate-type' in Cargo.toml")
+ }
+ (Some(_), Some(_), None) => {
+ bail!("Please set '[package.metadata.xcframework] lib-type' in Cargo.toml")
+ }
+ (None, None, _) => bail!("Missing '[lib] crate-type' in Cargo.toml"),
+ })
+}
diff --git a/src/conf/xcframework.rs b/src/conf/xcframework.rs
index 0637156..566b456 100644
--- a/src/conf/xcframework.rs
+++ b/src/conf/xcframework.rs
@@ -1,8 +1,8 @@
#![allow(non_snake_case)]
use super::Target;
-use anyhow::{bail, Context, Result};
-use camino::{Utf8Path, Utf8PathBuf};
+use anyhow::{bail, Result};
+use camino_fs::*;
use serde::Deserialize;
use std::str::FromStr;
@@ -38,6 +38,9 @@ impl LibType {
#[serde(rename_all = "kebab-case")]
pub struct XCFrameworkConfiguration {
/// The include directory containing the headers and the module.modulemap file
+ /// This is set to default because sometimes it needs to be set manually after
+ /// parsing the config.
+ #[serde(default)]
pub include_dir: Utf8PathBuf,
/// The library type (staticlib or cdylib)
@@ -72,7 +75,7 @@ pub struct XCFrameworkConfiguration {
}
pub fn zip_default() -> bool {
- true
+ false
}
impl XCFrameworkConfiguration {
@@ -92,22 +95,25 @@ impl XCFrameworkConfiguration {
/// Parses the [package.metadata.xcframework] section of the Cargo.toml
/// and updates the headers_directory to be relative to current working directory
- pub fn parse(metadata: &serde_json::Value, dir: &Utf8Path) -> Result {
- if let Some(xcfr) = metadata.get("xcframework") {
- Self::parse_xcframework(xcfr, dir)
- .context("Error in Cargo.toml section [package.metadata.xcframework]")
+ pub fn parse(
+ section: &serde_json::Value,
+ package_dir: &Utf8Path,
+ validate: bool,
+ ) -> Result {
+ let mut me = serde_json::from_value::(section.clone())?;
+ me.include_dir = package_dir.join(me.include_dir);
+ if validate {
+ me.validated()
} else {
- bail!("Missing [package.metadata.xcframework] section in Cargo.toml");
+ Ok(me)
}
}
- fn parse_xcframework(xcfr: &serde_json::Value, dir: &Utf8Path) -> Result {
- let mut me = serde_json::from_value::(xcfr.clone())?;
- me.include_dir = dir.join(me.include_dir);
- me.validated()
- }
-
fn validated(self) -> Result {
+ if self.include_dir.as_str().is_empty() {
+ bail!("The include-dir field is required");
+ }
+
if !self.include_dir.exists() {
bail!("The include-dir '{}' does not exist", self.include_dir);
}
diff --git a/src/core.rs b/src/core.rs
index b1e6718..f3eb9d4 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -1,8 +1,7 @@
use std::collections::HashMap;
use anyhow::{Context, Ok};
-use camino::Utf8PathBuf;
-use fs_err as fs;
+use camino_fs::*;
use platform::ApplePlatform;
use xshell::{cmd, Shell};
@@ -24,7 +23,7 @@ pub fn lipo_create_platform_libraries(
output_dir: &Utf8PathBuf,
) -> anyhow::Result> {
let sh = Shell::new()?;
- fs::create_dir_all(output_dir)?;
+ output_dir.mkdirs()?;
let mut libs = HashMap::new();
for (platform, paths) in platform_lib_paths.iter() {
@@ -34,7 +33,7 @@ pub fn lipo_create_platform_libraries(
continue;
}
let platform_dir = output_dir.join(format!("{:?}", platform));
- fs::create_dir_all(&platform_dir)?;
+ platform_dir.mkdirs()?;
let output_path = platform_dir.join(output_lib_name);
let mut cmd = cmd!(sh, "lipo -create");
@@ -44,7 +43,7 @@ pub fn lipo_create_platform_libraries(
cmd = cmd.arg("-output").arg(&output_path);
println!("🍭 Running lipo create for platform: {platform:?} ...");
cmd.run()?;
- println!("✅ Run lipo create success, platform: {platform:?}, output:\n{output_path:?}");
+ println!("✅ Run lipo create success, platform: {platform:?}, output:\n{output_path}");
libs.insert(platform.clone(), output_path);
}
Ok(libs)
@@ -60,7 +59,7 @@ pub fn wrap_as_framework(
crate_type: &CrateType,
lib_path: &Utf8PathBuf,
header_paths: Vec,
- module_paths: Vec,
+ module_path: Utf8PathBuf,
bundle_name: &str,
output_dir: &Utf8PathBuf,
) -> anyhow::Result {
@@ -73,7 +72,7 @@ pub fn wrap_as_framework(
let output_path = output_dir
.join(format!("{:?}", platform))
.join(format!("{}{}", bundle_name, SUFFIX));
- fs::create_dir_all(&output_path)?;
+ output_path.mkdirs()?;
let plist = plist::InfoPlistBuilder::new(bundle_name, platform);
let plist_path = output_path.join("Info.plist");
@@ -90,7 +89,7 @@ pub fn wrap_as_framework(
.run()?;
let to_binary = format!("{}/{}", &output_path, bundle_name);
- fs::copy(lib_path.as_path(), to_binary)?;
+ lib_path.cp(to_binary)?;
if let CrateType::Cdylib = crate_type {
sh.cmd("install_name_tool")
@@ -102,24 +101,16 @@ pub fn wrap_as_framework(
.output()?;
}
- fs::create_dir_all(format!("{}/Headers", &output_path))?;
- fs::create_dir_all(format!("{}/Modules", &output_path))?;
+ output_path.join("Headers").mkdirs()?;
+ output_path.join("Modules").mkdirs()?;
for header_path in header_paths.iter() {
let header_name = header_path.file_name().context("header path error")?;
- fs::copy(
- header_path,
- format!("{}/Headers/{}", output_path, header_name),
- )?;
+ header_path.cp(output_path.join("Headers").join(header_name))?;
}
- for module_path in module_paths.iter() {
- let module_name = module_path.file_name().context("module path error")?;
- fs::copy(
- module_path,
- format!("{}/Modules/{}", output_path, module_name),
- )?;
- }
+ let module_dest = output_path.join("Modules").join("module.modulemap");
+ module_path.cp(module_dest)?;
println!(
"✅ Wrapped artifacts as framework success, output:\n{}",
@@ -143,7 +134,7 @@ pub fn create_xcframework(
let xcframework_path = output_dir.join(format!("{}{}", bundle_name, SUFFIX));
if xcframework_path.exists() {
- fs::remove_dir_all(&xcframework_path)?;
+ xcframework_path.rm()?;
}
let mut cmd = sh.cmd("xcrun").args(["xcodebuild", "-create-xcframework"]);
diff --git a/src/ext/mod.rs b/src/ext/mod.rs
deleted file mode 100644
index d677c1d..0000000
--- a/src/ext/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-mod path;
-
-pub use path::PathBufExt;
diff --git a/src/ext/path.rs b/src/ext/path.rs
deleted file mode 100644
index 0f927f5..0000000
--- a/src/ext/path.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use std::path::Path;
-
-use anyhow::{Context, Result};
-use camino::Utf8PathBuf;
-use fs_extra::dir::CopyOptions;
-
-pub trait PathBufExt {
- fn resolve_home_dir(self) -> Result;
-
- fn create_dir_all_if_needed(&self) -> Result<()>;
-
- fn remove_dir_all_if_exists(&self) -> Result<()>;
-
- fn copy_dir>(&self, to: P) -> Result<()>;
-
- fn copy_dir_contents>(&self, to: P) -> Result<()>;
-}
-
-impl PathBufExt for Utf8PathBuf {
- fn create_dir_all_if_needed(&self) -> Result<()> {
- if !self.exists() {
- fs_err::create_dir_all(self)?;
- }
- Ok(())
- }
-
- fn resolve_home_dir(self) -> Result {
- if self.starts_with("~") {
- let home = std::env::var("HOME").context("Could not resolve $HOME")?;
- let home = Utf8PathBuf::from(home);
- Ok(home.join(self.strip_prefix("~").unwrap()))
- } else {
- Ok(self)
- }
- }
-
- fn remove_dir_all_if_exists(&self) -> Result<()> {
- if self.exists() {
- fs_err::remove_dir_all(self)?;
- }
- Ok(())
- }
-
- fn copy_dir>(&self, to: P) -> Result<()> {
- let to_path = to.as_ref();
- if !to_path.exists() {
- fs_err::create_dir_all(to_path)?;
- }
- fs_extra::dir::copy(self, to_path, &CopyOptions::new())?;
-
- Ok(())
- }
-
- fn copy_dir_contents>(&self, to: P) -> Result<()> {
- let to_path = to.as_ref();
- if !to_path.exists() {
- fs_err::create_dir_all(to_path)?;
- }
- let options = CopyOptions::new().content_only(true);
- fs_extra::dir::copy(self, to_path, &options)?;
-
- Ok(())
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index cca2a31..d7f892d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,20 +2,17 @@
mod cmd;
mod conf;
pub mod core;
-pub mod ext;
use core::platform::{ApplePlatform, Environment};
use std::collections::HashMap;
-use crate::conf::Configuration;
+pub use crate::conf::Configuration;
use anyhow::{Context, Result};
-use camino::Utf8PathBuf;
+use camino_fs::*;
use cmd::cargo;
pub use conf::CliArgs;
use conf::Target;
-pub use conf::XCFrameworkConfiguration;
-use ext::PathBufExt;
-use fs_err as fs;
+pub use conf::{LibType, XCFrameworkConfiguration};
#[derive(Debug, PartialEq, Eq)]
pub struct Produced {
@@ -24,19 +21,21 @@ pub struct Produced {
pub is_zipped: bool,
}
-pub fn build(cli: CliArgs) -> Result {
- let conf = Configuration::load(cli).context("loading configuration")?;
+pub fn build_from_cli(cli: CliArgs) -> Result {
+ let config = Configuration::load(cli).context("loading configuration")?;
- conf.build_dir
- .remove_dir_all_if_exists()
- .context("cleaning build dir")?;
+ crate::build(&config)
+}
+
+pub fn build(conf: &Configuration) -> Result {
+ conf.build_dir.rm().context("cleaning build dir")?;
- cargo::build(&conf).context("running cargo build")?;
+ cargo::build(conf).context("running cargo build")?;
let libs = {
let conf = &conf;
let libs_dir = conf.build_dir.join("libs");
- fs::create_dir_all(&libs_dir)?;
+ libs_dir.mkdirs()?;
let mut platform_lib_paths = HashMap::new();
if conf.cargo_section.iOS {
@@ -64,56 +63,56 @@ pub fn build(cli: CliArgs) -> Result {
}
.context("lipo: assembling libraries")?;
- let bundle_name = conf.module_name()?;
+ let bundle_name = conf.module_name().context("finding module name")?;
+
let crate_type = match conf.lib_type {
conf::LibType::StaticLib => &core::CrateType::Staticlib,
conf::LibType::CDyLib => &core::CrateType::Cdylib,
};
+
let framework_paths = libs
.into_iter()
.map(|(platform, lib_path)| {
let include_dir = &conf.cargo_section.include_dir;
let header_paths = get_header_paths(include_dir)?;
- let module_paths = get_module_paths(include_dir)?;
+ let module_path = get_module_path(include_dir)?;
let frameworks_dir = conf.target_dir.join("frameworks");
- std::fs::create_dir_all(&frameworks_dir)?;
+ frameworks_dir.mkdirs()?;
core::wrap_as_framework(
platform,
crate_type,
&lib_path,
header_paths,
- module_paths,
+ module_path,
&bundle_name,
&frameworks_dir,
)
})
- .collect::>>()?;
+ .collect::>>()
+ .context("collecting framework paths")?;
let xcframework_path =
- crate::core::create_xcframework(framework_paths, &conf.module_name()?, &conf.build_dir)?;
+ crate::core::create_xcframework(framework_paths, &conf.module_name()?, &conf.build_dir)
+ .context("creating xcframework")?;
+
let module_name = conf.module_name()?;
- let (path, is_zipped) = if conf.cargo_section.zip {
- (
- core::compress_xcframework(None, &xcframework_path, None, &conf.target_dir)?,
- true,
- )
+ let path = if conf.cargo_section.zip {
+ core::compress_xcframework(None, &xcframework_path, None, &conf.target_dir)?
} else {
let to = conf.target_dir.join(format!("{module_name}.xcframework"));
- if to.exists() {
- fs::remove_dir_all(&to)?;
- }
- fs::rename(xcframework_path, &to)?;
- (to, false)
+ to.rm()?;
+ xcframework_path.mv(&to)?;
+ to
};
- conf.build_dir.remove_dir_all_if_exists()?;
+ conf.build_dir.rm().context("cleaning build dir")?;
Ok(Produced {
module_name,
path,
- is_zipped,
+ is_zipped: conf.cargo_section.zip,
})
}
@@ -131,16 +130,15 @@ fn get_header_paths(include_dir: &Utf8PathBuf) -> Result> {
Ok(header_paths)
}
-fn get_module_paths(include_dir: &Utf8PathBuf) -> Result> {
- let mut module_paths = Vec::new();
- let pattern = format!("{}/**/*.modulemap", include_dir);
- for entry in glob::glob(&pattern)? {
- match entry {
- Ok(path) => module_paths.push(Utf8PathBuf::from_path_buf(path).unwrap()),
- Err(e) => println!("{:?}", e),
- }
+fn get_module_path(include_dir: &Utf8PathBuf) -> Result {
+ let pattern = format!("{include_dir}/**/*.modulemap");
+ let mut glob = glob::glob(&pattern)?;
+ let module_path = glob.next().context("modulemap not found")??;
+ if glob.next().is_some() {
+ anyhow::bail!("multiple modulemaps found");
}
- Ok(module_paths)
+
+ Ok(Utf8PathBuf::from_path_buf(module_path).unwrap())
}
fn lib_paths_for_targets(conf: &Configuration, targets: &[Target]) -> Result> {
diff --git a/src/main.rs b/src/main.rs
index 2fd6fc7..0135cc6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,9 @@
-use xcframework::{build, CliArgs};
+use xcframework::CliArgs;
fn main() {
let args = CliArgs::from_env_or_exit();
- if let Err(e) = crate::build(args) {
+ if let Err(e) = xcframework::build_from_cli(args) {
eprintln!("{:?}", e);
std::process::exit(1);
}
diff --git a/tests/test.rs b/tests/test.rs
index 8c28e5f..533be1a 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,9 +1,6 @@
use anyhow::Result;
-use camino::Utf8PathBuf;
-use fs_err as fs;
-use std::path::{Path, PathBuf};
+use camino_fs::*;
use std::process::Command;
-use xcframework::ext::PathBufExt;
use xcframework::CliArgs;
fn args(vec: &[&str]) -> CliArgs {
@@ -16,17 +13,15 @@ fn test_hello() {
let cli = args(&["--quiet", "--manifest-path", "tests/project/Cargo.toml"]);
- let produced = xcframework::build(cli).unwrap();
- assert!(produced.is_zipped);
+ let produced = xcframework::build_from_cli(cli).unwrap();
+ assert!(!produced.is_zipped);
assert_eq!(produced.module_name, "HelloTest");
}
-fn create_output_dir(subfolder: &str) -> PathBuf {
- let tmp_dir = PathBuf::from("tests").join("temp").join(subfolder);
- if tmp_dir.exists() {
- fs::remove_dir_all(&tmp_dir).unwrap();
- }
- fs::create_dir_all(&tmp_dir).unwrap();
+fn create_output_dir(subfolder: &str) -> Utf8PathBuf {
+ let tmp_dir = Utf8PathBuf::from("tests").join("temp").join(subfolder);
+ tmp_dir.rm().unwrap();
+ tmp_dir.mkdirs().unwrap();
tmp_dir
}
@@ -35,7 +30,7 @@ fn end_to_end_static() {
let out_dir = create_output_dir("static");
let target_dir = out_dir.join("mymath-lib/target");
- fs::create_dir_all(&target_dir).unwrap();
+ target_dir.mkdirs().unwrap();
let cli = args(&[
"--quiet",
@@ -44,11 +39,11 @@ fn end_to_end_static() {
"--lib-type",
"staticlib",
"--target-dir",
- target_dir.to_str().unwrap(),
+ target_dir.as_str(),
]);
- let produced = xcframework::build(cli).unwrap();
- assert!(produced.is_zipped);
+ let produced = xcframework::build_from_cli(cli).unwrap();
+ assert!(!produced.is_zipped);
assert_eq!(produced.module_name, "MyMath");
let swift_dir = cp_swift_exe(&out_dir).unwrap();
@@ -62,7 +57,7 @@ fn end_to_end_static() {
let stdout = String::from_utf8_lossy(&cmd.stdout);
let stderr = String::from_utf8_lossy(&cmd.stderr);
eprintln!("{stderr}");
- assert!(stderr.contains("Build complete!"));
+ assert!(cmd.status.success());
assert_eq!("MyMath.rust_add(4 + 2) = 6\n", stdout);
}
@@ -71,7 +66,7 @@ fn end_to_end_dynamic() {
let out_dir = create_output_dir("dynamic");
let target_dir = out_dir.join("mymath-lib/target");
- fs::create_dir_all(&target_dir).unwrap();
+ target_dir.mkdirs().unwrap();
let cli = args(&[
"--quiet",
@@ -80,11 +75,11 @@ fn end_to_end_dynamic() {
"--lib-type",
"cdylib",
"--target-dir",
- target_dir.to_str().unwrap(),
+ target_dir.as_str(),
]);
- let produced = xcframework::build(cli).unwrap();
- assert!(produced.is_zipped);
+ let produced = xcframework::build_from_cli(cli).unwrap();
+ assert!(!produced.is_zipped);
assert_eq!(produced.module_name, "MyMath");
let swift_dir = cp_swift_exe(out_dir.as_path()).unwrap();
@@ -105,16 +100,16 @@ fn end_to_end_dynamic() {
fn multi_platform_static() {
let out_dir = create_output_dir("multi-platform-static");
let target_dir = out_dir.join("mymath-lib/target");
- fs::create_dir_all(&target_dir).unwrap();
+ target_dir.mkdirs().unwrap();
let cli = args(&[
"--manifest-path",
"examples/multi-platform/mymath-lib/Cargo.toml",
"--lib-type",
"staticlib",
"--target-dir",
- target_dir.to_str().unwrap(),
+ target_dir.as_str(),
]);
- let produced = xcframework::build(cli).unwrap();
+ let produced = xcframework::build_from_cli(cli).unwrap();
assert_eq!(produced.module_name, "MyMath");
let tuist_workspace_dir = cp_tuist_workspace(out_dir.as_path()).unwrap();
let cmd = Command::new("tuist")
@@ -134,16 +129,17 @@ fn multi_platform_static() {
fn multi_platform_dynamic() {
let out_dir = create_output_dir("multi-platform-dynamic");
let target_dir = out_dir.join("mymath-lib/target");
- fs::create_dir_all(&target_dir).unwrap();
+ target_dir.mkdirs().unwrap();
+
let cli = args(&[
"--manifest-path",
"examples/multi-platform/mymath-lib/Cargo.toml",
"--lib-type",
"cdylib",
"--target-dir",
- target_dir.to_str().unwrap(),
+ target_dir.as_str(),
]);
- let produced = xcframework::build(cli).unwrap();
+ let produced = xcframework::build_from_cli(cli).unwrap();
assert_eq!(produced.module_name, "MyMath");
let tuist_workspace_dir = cp_tuist_workspace(out_dir.as_path()).unwrap();
let cmd = Command::new("tuist")
@@ -158,25 +154,22 @@ fn multi_platform_dynamic() {
}
}
-fn cp_swift_exe(dest: &Path) -> Result {
+fn cp_swift_exe(dest: &Utf8Path) -> Result {
+ println!("dest: {:?}", dest);
let from = Utf8PathBuf::from("examples/end-to-end/swift-exe");
- let dest = Utf8PathBuf::from_path_buf(dest.to_path_buf()).unwrap();
+ let dest = dest.join("swift-exe");
+ dest.mkdirs()?;
- dest.create_dir_all_if_needed()?;
-
- fs_extra::dir::copy(from, &dest, &fs_extra::dir::CopyOptions::new())?;
- let build_tmp = dest.join("swift-exe/.build");
- if build_tmp.exists() {
- fs::remove_dir_all(build_tmp)?;
- }
- Ok(dest.join("swift-exe"))
+ from.cp(&dest)?;
+ let build_tmp = dest.join(".build");
+ build_tmp.rm()?;
+ Ok(dest)
}
-fn cp_tuist_workspace(dest: &Path) -> Result {
+fn cp_tuist_workspace(dest: &Utf8Path) -> Result {
let from = Utf8PathBuf::from("examples/multi-platform/tuist-workspace");
- let dest = Utf8PathBuf::from_path_buf(dest.to_path_buf()).unwrap();
- dest.create_dir_all_if_needed()?;
- fs_extra::dir::copy(from, &dest, &fs_extra::dir::CopyOptions::new())?;
+ dest.mkdirs()?;
+ from.cp(dest)?;
Ok(dest.join("tuist-workspace"))
}