Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
> <span style="color:darkorange">**⚠️ WARNING**</span>
>
> 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

Expand Down Expand Up @@ -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"
Expand All @@ -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.
```

Expand Down
2 changes: 1 addition & 1 deletion examples/end-to-end/mymath-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 17 additions & 16 deletions examples/end-to-end/swift-exe/Package.swift
Original file line number Diff line number Diff line change
@@ -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"
),
]
)
1 change: 1 addition & 0 deletions src/cmd/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub fn build(conf: &Configuration) -> Result<()> {
let mut args: Vec<String> = vec![];

args.push("build".into());
args.push("--color=always".into());

if conf.target_dir != "target" {
args.push(format!("--target-dir={}", conf.target_dir));
Expand Down
23 changes: 20 additions & 3 deletions src/cmd/modulemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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!(
Expand All @@ -32,3 +41,11 @@ fn parse_module_name(content: &str) -> Result<String> {
}
Ok(module.to_string())
}

fn ls_modulemap_files(dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {
Ok(dir
.ls()
.files()
.filter(|path| path.extension().map_or(false, |ext| ext == "modulemap"))
.collect())
}
2 changes: 1 addition & 1 deletion src/conf/args.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use camino::Utf8PathBuf;
use camino_fs::Utf8PathBuf;

use super::LibType;

Expand Down
124 changes: 78 additions & 46 deletions src/conf/configuration.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -23,52 +23,23 @@ pub struct Configuration {
}

impl Configuration {
pub fn load(mut cli: CliArgs) -> Result<Self> {
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<Self> {
// 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
Expand All @@ -84,7 +55,6 @@ impl Configuration {
}
}
}

Ok(Self {
cargo_section: xc_conf,
cli,
Expand All @@ -96,6 +66,38 @@ impl Configuration {
})
}

pub fn load(cli: CliArgs) -> Result<Self> {
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<String> {
let name = self.module_name.borrow().clone();
if let Some(name) = name {
Expand All @@ -115,3 +117,33 @@ impl Configuration {
}
}
}

fn get_libtype(
package: &Package,
libtype: Option<LibType>,
) -> 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"),
})
}
Loading