xcframework/conf/
configuration.rs

1use crate::cmd::modulemap;
2use anyhow::{Context, Result, anyhow, bail};
3use camino_fs::Utf8PathBuf;
4use cargo_metadata::{Metadata, MetadataCommand, Package, TargetKind};
5
6use super::{CliArgs, LibType, XCFrameworkConfiguration};
7
8#[derive(Debug)]
9pub struct Configuration {
10    pub cargo_section: XCFrameworkConfiguration,
11    pub cli: CliArgs,
12    pub lib_type: LibType,
13    // Name of the library (used for the compiled artifacts)
14    pub lib_name: String,
15    /// Directory for all generated artifacts
16    pub target_dir: Utf8PathBuf,
17    /// Directory where the xcframework will be built
18    pub build_dir: Utf8PathBuf,
19}
20
21impl Configuration {
22    pub fn new(
23        metadata: &Metadata,
24        package: &Package,
25        mut cli: CliArgs,
26        xc_conf: XCFrameworkConfiguration,
27    ) -> Result<Self> {
28        // Use the target directory from the CLI, or the one from the Cargo.toml
29        let target_dir = cli
30            .target_dir
31            .as_ref()
32            .unwrap_or(&metadata.target_directory)
33            .clone();
34
35        let build_dir = target_dir.join("xcframework");
36        let wanted_lib_type = cli.lib_type.clone().or_else(|| xc_conf.lib_type.clone());
37
38        let (lib_type, target) = get_libtype(package, wanted_lib_type)?;
39
40        if xc_conf.build_std && lib_type == LibType::StaticLib {
41            let already_set = cli
42                .unstable_flags
43                .as_ref()
44                .map(|f| f.contains("build-std=std"))
45                .unwrap_or(false);
46            if !already_set {
47                if let Some(flag) = cli.unstable_flags.to_owned() {
48                    cli.unstable_flags = Some(format!("{flag},build-std=std"));
49                } else {
50                    cli.unstable_flags = Some("build-std=std".to_string());
51                }
52            }
53        }
54        Ok(Self {
55            cargo_section: xc_conf,
56            cli,
57            lib_type,
58            lib_name: target.name.clone(),
59            target_dir,
60            build_dir,
61        })
62    }
63
64    pub fn load(cli: CliArgs) -> Result<Self> {
65        let manifest_path = cli
66            .manifest_path
67            .clone()
68            .unwrap_or_else(|| Utf8PathBuf::from("Cargo.toml"));
69        let mut dir = manifest_path.clone();
70        dir.pop();
71
72        let metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?;
73
74        let workspace_packages = metadata.workspace_packages();
75        let package = if let Some(package) = &cli.package {
76            workspace_packages
77                .iter()
78                .find(|p| p.name.as_str() == package)
79                .ok_or(anyhow!("Could not find package '{package}'"))?
80        } else {
81            metadata
82                .root_package()
83                .ok_or(anyhow!("Could not find root package in metadata"))?
84        };
85
86        let Some(section) = package.metadata.get("xcframework") else {
87            bail!("Missing [package.metadata.xcframework] section in Cargo.toml");
88        };
89
90        let xc_conf = XCFrameworkConfiguration::parse(section, &dir, true)
91            .context("Error in Cargo.toml section [package.metadata.xcframework]")?;
92
93        Self::new(&metadata, package, cli, xc_conf)
94    }
95
96    pub fn module_name(&self) -> Result<String> {
97        modulemap::get_module_name(self)
98    }
99
100    pub fn profile(&self) -> &str {
101        if self.cli.release {
102            "release"
103        } else {
104            self.cli.profile.as_deref().unwrap_or("debug")
105        }
106    }
107}
108
109fn get_libtype(
110    package: &Package,
111    libtype: Option<LibType>,
112) -> Result<(LibType, &cargo_metadata::Target)> {
113    let staticlib = package.targets.iter().find(|t| {
114        t.kind.contains(&TargetKind::StaticLib) || t.kind.contains(&TargetKind::StaticLib)
115    });
116    let dylib = package
117        .targets
118        .iter()
119        .find(|t| t.kind.contains(&TargetKind::CDyLib) || t.kind.contains(&TargetKind::CDyLib));
120    use LibType::*;
121    Ok(match (staticlib, dylib, libtype) {
122        (Some(staticlib), None, None) => (StaticLib, staticlib),
123        (Some(staticlib), _, Some(StaticLib)) => (StaticLib, staticlib),
124        (Some(_staticlib), None, Some(CDyLib)) => {
125            bail!("Please add 'cdylib' to '[lib] crate-type' in Cargo.toml")
126        }
127        (None, Some(dylib), None) => (CDyLib, dylib),
128        (_, Some(dylib), Some(CDyLib)) => (CDyLib, dylib),
129        (_, Some(_dylib), Some(StaticLib)) => {
130            bail!("Please add 'staticlib' to '[lib] crate-type' in Cargo.toml")
131        }
132        (Some(_), Some(_), None) => {
133            bail!("Please set '[package.metadata.xcframework] lib-type' in Cargo.toml")
134        }
135        (None, None, _) => bail!("Missing '[lib] crate-type' in Cargo.toml"),
136    })
137}