swift_package/conf/
configuration.rs1use crate::SWIFT_PACKAGE_UNIFFY_VERSION;
2
3use super::{CliArgs, SwiftPackageConfiguration};
4use anyhow::{anyhow, bail, Context, Result};
5use camino_fs::Utf8PathBuf;
6use cargo_metadata::{Metadata, MetadataCommand, Package, TargetKind};
7use xcframework::{Configuration as XCMainConfig, XCFrameworkConfiguration};
8
9#[derive(Debug)]
10pub struct Configuration {
11 pub target_name: String,
12 pub cargo_section: SwiftPackageConfiguration,
13 pub xcframework: XCMainConfig,
14 pub cli: CliArgs,
15
16 pub manifest_dir: Utf8PathBuf,
19 pub target_dir: Utf8PathBuf,
20 pub framework_build_dir: Utf8PathBuf,
21 pub bindings_build_dir: Utf8PathBuf,
22}
23
24impl Configuration {
25 pub fn load(cli: CliArgs) -> Result<Self> {
26 let manifest_path = cli
27 .manifest_path
28 .clone()
29 .unwrap_or_else(|| Utf8PathBuf::from("Cargo.toml"));
30 let mut manifest_dir = manifest_path.clone();
31 manifest_dir.pop();
32
33 let metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?;
34
35 let target_dir = cli
36 .target_dir
37 .clone()
38 .unwrap_or_else(|| metadata.target_directory.clone());
39
40 let package = if let Some(package) = &cli.package {
41 metadata
42 .workspace_packages()
43 .iter()
44 .find(|p| &p.name == package)
45 .ok_or(anyhow!("Could not find package '{package}'"))?
46 } else {
47 metadata
48 .root_package()
49 .ok_or(anyhow!("Could not find root package in metadata"))?
50 };
51 let package_dir = package.manifest_path.parent().unwrap();
52
53 uniffi_version_check(&package, &metadata)?;
54 let Some(section) = package.metadata.get("swift-package") else {
55 bail!("Missing '[package.metadata.swift-package]' section in Cargo.toml")
56 };
57 let sp_conf = SwiftPackageConfiguration::parse(§ion, &package_dir).context(
58 "Error when creating swift package configuration by parsing \
59 Cargo.toml section [package.metadata.swift-package]",
60 )?;
61 let target_name = find_target_name(&package)?;
62 let build_dir = target_dir.join(format!("lib{target_name}"));
63 let framework_build_dir = build_dir.join(format!("{}.package", sp_conf.package_name));
64 let bindings_build_dir = build_dir.join("bindings");
65
66 let mut xc_conf = XCFrameworkConfiguration::parse(§ion, &package_dir, false).context(
67 "Error when creating xcframework configuration by parsing \
68 Cargo.toml section [package.metadata.swift-package]",
69 )?;
70 xc_conf.include_dir = bindings_build_dir.clone();
71 let xc_cli = cli.to_xc_cli();
72 let xcframework = XCMainConfig::new(&metadata, package, xc_cli, xc_conf)?;
73
74 Ok(Self {
75 cargo_section: sp_conf,
76 xcframework,
77 cli,
78 manifest_dir,
79 framework_build_dir,
80 bindings_build_dir,
81 target_name,
82 target_dir,
83 })
84 }
85
86 pub fn dylib_file(&self) -> Utf8PathBuf {
87 let profile = if self.cli.release { "release" } else { "debug" };
88 self.target_dir
89 .join(profile)
90 .join(format!("lib{}.dylib", self.target_name))
91 }
92}
93
94fn find_target_name(package: &Package) -> Result<String> {
95 let target = package
96 .targets
97 .iter()
98 .find(|t| t.kind.iter().any(|k| *k == TargetKind::CDyLib))
99 .ok_or(anyhow!(
100 "Could not find a cdylib target in package {}",
101 package.name
102 ))?;
103 Ok(target.name.clone())
104}
105
106fn uniffi_version_check(package: &Package, metadata: &Metadata) -> Result<()> {
107 package
108 .dependencies
109 .iter()
110 .find(|dep| dep.name == "uniffi")
111 .ok_or(anyhow!(
112 "The package {} should have a uniffi dependency",
113 package.name
114 ))?;
115 let uniffi_bindgen_version = metadata
116 .packages
117 .iter()
118 .find(|pack| pack.name == "uniffi")
119 .unwrap()
120 .version
121 .to_string();
122
123 let expected = major_and_minor(SWIFT_PACKAGE_UNIFFY_VERSION);
124 let found = major_and_minor(&uniffi_bindgen_version);
125 if expected != found {
126 bail!(
127 "uniffi_bindgen version mismatch: \
128 swift-package is build with {SWIFT_PACKAGE_UNIFFY_VERSION} \
129 but the project uses {uniffi_bindgen_version}"
130 );
131 }
132 Ok(())
133}
134
135fn major_and_minor(semver: &str) -> String {
136 let mut parts = semver.split('.');
137 let major = parts.next().unwrap();
138 let minor = parts.next().unwrap();
139 format!("{}.{}", major, minor)
140}