xtask_lib/tasks/setup.rs
1//! `cargo xtask setup` — build thirdparty dependencies that must be
2//! ready before `cargo build` runs.
3//!
4//! Currently this covers SQLite only: `libsqlite3-sys`'s own build
5//! script runs before `cadmus-core`'s build.rs, so the custom SQLite
6//! library (built with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`) must
7//! already be on disk and pointed to by `SQLITE3_LIB_DIR` /
8//! `SQLITE3_INCLUDE_DIR`.
9//!
10//! ## Usage
11//!
12//! ```text
13//! cargo xtask setup # build for all known targets (host + Kobo)
14//! cargo xtask setup --host # build for the native host only
15//! cargo xtask setup --kobo # build for Kobo (ARM) only
16//! cargo xtask setup --all # explicitly build for all known targets
17//! cargo xtask setup --target <triple> # build for an arbitrary target
18//! ```
19//!
20//! After running, set the printed environment variables before
21//! `cargo build` or `cargo xtask build-kobo`.
22
23use anyhow::{Context, Result};
24use clap::Args;
25
26use build_deps::build::sqlite;
27
28use super::util::workspace;
29
30/// Arguments for `cargo xtask setup`.
31#[derive(Debug, Args)]
32pub struct SetupArgs {
33 /// Build for the native host target only.
34 #[arg(long)]
35 pub host: bool,
36
37 /// Build for the Kobo ARM target only.
38 #[arg(long)]
39 pub kobo: bool,
40
41 /// Build for all known targets (host + Kobo). Implied when no
42 /// flags are passed.
43 #[arg(long)]
44 pub all: bool,
45
46 /// Build for an arbitrary target triple (advanced).
47 #[arg(long)]
48 pub target: Option<String>,
49}
50
51/// Build thirdparty dependencies that must exist before `cargo build`.
52///
53/// When no target flags are supplied the default is `--all`, which
54/// builds for every known target (native host + Kobo ARM).
55///
56/// # Errors
57///
58/// Returns an error if:
59/// - Git submodules cannot be initialised.
60/// - TCL is not installed (required for SQLite amalgamation generation).
61/// - The SQLite build fails.
62pub fn run(args: SetupArgs) -> Result<()> {
63 let root = workspace::root()?;
64
65 build_deps::ensure_submodules(&root).context("failed to initialise git submodules")?;
66
67 let targets = resolve_targets(&args);
68
69 for target in &targets {
70 let artifacts = sqlite::ensure_sqlite(&root, target).context("failed to build sqlite")?;
71
72 println!();
73 println!("SQLite artifacts ready for {target}:");
74 println!(" export SQLITE3_LIB_DIR={}", artifacts.lib_dir.display());
75 println!(
76 " export SQLITE3_INCLUDE_DIR={}",
77 artifacts.include_dir.display()
78 );
79 println!(" export SQLITE3_STATIC=1");
80 }
81
82 Ok(())
83}
84
85/// Determine which target triples to build based on CLI flags.
86///
87/// `--target` takes precedence; otherwise `--host` / `--kobo` select
88/// individual targets. When none are given `--all` is implied.
89fn resolve_targets(args: &SetupArgs) -> Vec<String> {
90 if let Some(ref t) = args.target {
91 return vec![t.clone()];
92 }
93
94 let build_all = args.all || (!args.host && !args.kobo);
95
96 let mut targets = Vec::new();
97 if build_all || args.host {
98 targets.push(guess_host_triple());
99 }
100 if build_all || args.kobo {
101 targets.push(sqlite::KOBO_TARGET.to_string());
102 }
103 targets
104}
105
106/// Best-effort detection of the host target triple.
107#[must_use]
108fn guess_host_triple() -> String {
109 std::env::var("TARGET").unwrap_or_else(|_| {
110 if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
111 "x86_64-unknown-linux-gnu".to_string()
112 } else if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") {
113 "aarch64-unknown-linux-gnu".to_string()
114 } else if cfg!(target_arch = "x86_64") && cfg!(target_os = "macos") {
115 "x86_64-apple-darwin".to_string()
116 } else if cfg!(target_arch = "aarch64") && cfg!(target_os = "macos") {
117 "aarch64-apple-darwin".to_string()
118 } else {
119 "x86_64-unknown-linux-gnu".to_string()
120 }
121 })
122}