xcode_build_rs/lib.rs
1// //! # Warning: This library is unstable, only the executable's behaviour is subject to semver
2
3// use std::env;
4
5// use camino::{Utf8Path, Utf8PathBuf};
6// use clap::{Args, Parser, Subcommand};
7// use color_eyre::{
8// eyre::{eyre, Context as _, Report},
9// Section as _,
10// };
11// use serde::Deserialize;
12// use tracing::info;
13
14// #[derive(Parser, Debug)]
15// #[command(version, about)]
16// #[command(name = "cargo", bin_name = "cargo")]
17// pub enum TopLevel {
18// #[clap(name = "xcode-build-rs")]
19// XCodeBuild(XcodeBuild),
20// }
21
22// impl TopLevel {
23// fn inner(&self) -> &XcodeBuild {
24// match self {
25// TopLevel::XCodeBuild(inner) => inner,
26// }
27// }
28// }
29
30// #[derive(clap::Args, Debug)]
31// #[command(version, about)]
32// pub struct XcodeBuild {
33// #[clap(subcommand)]
34// mode: Mode,
35
36// #[clap(flatten)]
37// options: Options,
38// }
39
40// impl TopLevel {
41// pub fn options(&self) -> &Options {
42// &self.inner().options
43// }
44
45// pub fn mode(&self) -> &Mode {
46// &self.inner().mode
47// }
48// }
49
50// #[derive(Subcommand, Clone, Debug)]
51// pub enum Mode {
52// /// Run in XCode
53// Xcode,
54// /// Run a test build for an iOS simulator
55// Test,
56// }
57
58// #[derive(Args, Debug)]
59// pub struct Options {
60// /// By default, doesn't display colour because this can be annoying in the XCode terminal
61// #[arg(long, alias = "colour")]
62// pub colour: bool,
63
64// /// The --manifest-path option to pass to `cargo rustc builds`.
65// /// Often you can pass `.`
66// #[arg(long, alias = "manifest-dir")]
67// manifest_dir: Utf8PathBuf,
68// }
69
70// impl Options {
71// /// Specifically to the *file* `Cargo.toml`, *not directory*
72// pub fn manifest_path(&self) -> Utf8PathBuf {
73// self.manifest_dir.to_owned().join("Cargo.toml")
74// }
75// }
76
77// #[derive(Deserialize, Debug, Default)]
78// pub struct Config {
79// /// What features to enable for iOS builds
80// #[serde(default)]
81// ios: Flags,
82// }
83
84// #[derive(Debug, Deserialize, Clone)]
85// pub struct Flags {
86// #[serde(default)]
87// features: Vec<String>,
88// /// Whether or not to pass the flag `--no-default-features` to `cargo rustc`
89// /// See https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options
90// #[serde(default = "Flags::default_default_features")]
91// default_features: bool,
92// }
93
94// impl Flags {
95// /// Default for [Self::default_features]
96// fn default_default_features() -> bool {
97// true
98// }
99// }
100
101// impl Default for Flags {
102// fn default() -> Self {
103// Flags {
104// default_features: Flags::default_default_features(),
105// features: vec![],
106// }
107// }
108// }
109
110// impl Config {
111// pub fn retrieve_from_toml_config(manifest_path: &Utf8Path) -> Result<Config, Report> {
112// match std::fs::read_to_string(manifest_path) {
113// Err(err) => {
114// info!(
115// message = "Cannot find `Cargo.toml` file in manifest_dir, using default config",
116// ?err,
117// ?manifest_path
118// );
119// Ok(Config::default())
120// }
121// Ok(config) => {
122// let raw_config: toml::Value = toml::from_str(&config)
123// .wrap_err_with(|| format!("Cannot parse Cargo.toml file: {:?}", manifest_path))?;
124// let config = raw_config
125// .get("package")
126// .and_then(|package| package.get("metadata"))
127// .and_then(|metadata| metadata.get("xcode-build-rs"));
128// match config {
129// None => {
130// info!("Using default config since `package.metadata.xcode_build_rs` section is missing from Cargo.toml");
131// Ok(Config::default())
132// }
133// Some(config) => {
134// let config: Config = config
135// .clone()
136// .try_into()
137// .wrap_err("Cannot deserialize `xcode-build-rs` section of Cargo.toml")?;
138// info!(message = "Deserialized Config from Cargo.toml", ?config);
139// Ok(config)
140// }
141// }
142// }
143// }
144// }
145
146// pub fn ios_feature_flags(&self) -> Flags {
147// self.ios.clone()
148// }
149// }
150
151// impl Flags {
152// pub fn into_args(&self) -> Vec<String> {
153// let mut args = vec![];
154// if !self.default_features {
155// args.push("--no-default-features".into());
156// }
157// for feature in self.features.iter() {
158// args.push("--features".into());
159// args.push(feature.clone());
160// }
161// args
162// }
163// }
164
165// pub fn release_profile() -> Result<bool, Report> {
166// let mut is_release_build = true;
167// const CONFIGURATION: &str = "CONFIGURATION";
168// let configuration_env_var = env::var(CONFIGURATION);
169// if configuration_env_var == Ok("Release".into()) {
170// is_release_build = true;
171// info!(
172// message =
173// "Assuming a --release profile since the CONFIGURATION env flag was set to 'Release'",
174// ?configuration_env_var
175// );
176// } else if configuration_env_var == Ok("Debug".into()) {
177// is_release_build = false;
178// info!(
179// message =
180// "Assuming not a release profile since the CONFIGURATION env flag was set to 'Debug'",
181// ?configuration_env_var
182// );
183// } else {
184// info!(
185// message = "No known release profile was provided in the CONFIGURATION env var",
186// ?configuration_env_var,
187// ?is_release_build
188// );
189// }
190// Ok(is_release_build)
191// }
192
193// pub fn is_simulator() -> Result<bool, Report> {
194// let mut is_simulator = false;
195// if let Some(llvm_target_triple_suffix) = env::var_os("LLVM_TARGET_TRIPLE_SUFFIX") {
196// if llvm_target_triple_suffix == *"-simulator" {
197// info!(
198// message = "Assuming building for a simulator",
199// ?llvm_target_triple_suffix
200// );
201// is_simulator = true;
202// }
203// }
204// Ok(is_simulator)
205// }
206
207// pub enum Archs {
208// X86_64,
209// Arm64,
210// }
211
212// pub fn parse_archs() -> color_eyre::Result<Archs> {
213// impl Archs {
214// fn try_from_str(str: &str) -> Result<Self, Report> {
215// Ok(match str {
216// "x86_64" => Archs::X86_64,
217// "arm64" => Archs::Arm64,
218// _ => {
219// return Err(eyre!("Cannot parse ARCHS env variable").note(format!("ARCHS: {:?}", str)))
220// }
221// })
222// }
223// }
224
225// match env::var("ARCHS") {
226// Err(e) => Err(e).wrap_err("No ARCHS env var present")?,
227// Ok(archs) => Archs::try_from_str(&archs),
228// }
229// }
230
231// pub fn rustc(
232// target: &'static str,
233// release: bool,
234// flags: Flags,
235// manifest_dir: &Utf8Path,
236// ) -> Result<(), Report> {
237// let rustc_path = which::which("cargo").wrap_err("Cannot find cargo executable path")?;
238// // don't change this to pure. just don't.
239// let mut rustc = bossy::Command::impure(&rustc_path).with_args([
240// "rustc",
241// "--crate-type",
242// "staticlib",
243// "--lib",
244// "--target",
245// target,
246// "--manifest-path",
247// manifest_dir.as_str(),
248// ]);
249// for flag in flags.into_args() {
250// rustc.add_arg(flag);
251// }
252// if release {
253// rustc.add_arg("--release");
254// }
255// info!(message = "About to run rustc", cwd = ?cwd(), ?rustc_path, ?flags, ?manifest_dir);
256// rustc
257// .run_and_wait()
258// .wrap_err("rustc invocation failed, likely a Rust-side build error")?;
259// Ok(())
260// }
261
262// pub fn cwd() -> Result<Utf8PathBuf, Report> {
263// Utf8PathBuf::try_from(env::current_dir().wrap_err("Cannot find cwd")?).wrap_err("CWD is not UTF8")
264// }
265
266// pub fn debug_confirm_on_path(
267// paths: &[std::path::PathBuf],
268// bin: &'static str,
269// ) -> Result<bool, Report> {
270// let bin_path = which::which(bin).wrap_err("Couldn't find binary")?;
271// let bin_dir = {
272// let mut bin_path = bin_path.clone();
273// bin_path.pop();
274// bin_path
275// };
276// let bin_contained = paths.contains(&bin_dir);
277// info!(
278// ?bin_contained,
279// ?bin_dir,
280// ?bin_path,
281// "Debug searching for `{}` bin path, was successful: {}",
282// bin,
283// bin_contained
284// );
285// Ok(bin_contained)
286// }