Skip to main content

xtask_lib/tasks/
build_kobo.rs

1//! `cargo xtask build-kobo` — cross-compile Cadmus for Kobo devices.
2//!
3//! This task is a thin wrapper around `cargo build --release
4//! --target arm-unknown-linux-gnueabihf -p cadmus`. All dependency
5//! building (thirdparty libs, MuPDF, libwebp, mupdf_wrapper) is
6//! handled automatically by `build.rs` when cargo build runs.
7//!
8//! Pre-flight steps performed before invoking cargo:
9//!
10//! 1. Verify the Linaro ARM toolchain (`arm-linux-gnueabihf-gcc`)
11//!    is on `PATH`.
12//! 2. Build SQLite from source with UDL support for the ARM target
13//!    (placed in `target/cadmus-build-deps/arm-unknown-linux-gnueabihf/sqlite/`).
14//!
15//! Git submodules are not initialised up-front here: the Rust build
16//! script clones them lazily, only when the cached Kobo build
17//! artefacts in `libs/` and `target/cadmus-build-deps/...` are
18//! missing. This keeps warm-cache CI runs fast by avoiding the
19//! recursive submodule clone done by `actions/checkout`.
20//!
21//! The Kobo build is only available on Linux and macOS hosts.
22
23use anyhow::{Context, Result, bail};
24use clap::Args;
25
26use build_deps::build::sqlite;
27use build_deps::versions::CROSS_ENV;
28
29use super::util::{cmd, workspace};
30
31/// Arguments for `cargo xtask build-kobo`.
32#[derive(Debug, Args)]
33pub struct BuildKoboArgs {
34    /// Cargo feature flags to pass to the Cadmus build (e.g. `test`).
35    #[arg(long)]
36    pub features: Option<String>,
37}
38
39/// Cross-compiles Cadmus for Kobo ARM devices.
40///
41/// # Errors
42///
43/// Returns an error if:
44/// - The host platform is not Linux or macOS.
45/// - The Linaro ARM toolchain is not on `PATH`.
46/// - The underlying `cargo build` invocation fails.
47///
48/// Git submodules are initialised lazily by the Rust build script
49/// when the cached Kobo artefacts are missing; this task no longer
50/// triggers a recursive submodule clone unconditionally.
51pub fn run(args: BuildKoboArgs) -> Result<()> {
52    if !cfg!(any(target_os = "linux", target_os = "macos")) {
53        bail!(
54            "Kobo cross-compilation is only available on Linux and macOS.\n\
55             On other platforms, please use Docker or a Linux VM instead."
56        );
57    }
58
59    let root = workspace::root()?;
60
61    ensure_linaro_toolchain()?;
62
63    build_deps::ensure_submodules(&root).context("failed to initialise git submodules")?;
64    sqlite::ensure_sqlite(&root, sqlite::KOBO_TARGET).context("failed to build SQLite for Kobo")?;
65
66    cargo_build_kobo(&root, args.features.as_deref())?;
67
68    Ok(())
69}
70
71fn ensure_linaro_toolchain() -> Result<()> {
72    cmd::run(
73        "arm-linux-gnueabihf-gcc",
74        &["--version"],
75        std::path::Path::new("."),
76        &[],
77    )
78    .map_err(|_| {
79        anyhow::anyhow!(
80            "arm-linux-gnueabihf-gcc not found on PATH.\n\
81             Install the Linaro toolchain or run inside the devenv shell."
82        )
83    })
84}
85
86fn cargo_build_kobo(root: &std::path::Path, features: Option<&str>) -> Result<()> {
87    let mut cargo_args = vec![
88        "build",
89        "--release",
90        "--target",
91        "arm-unknown-linux-gnueabihf",
92        "-p",
93        "cadmus",
94    ];
95
96    if let Some(f) = features {
97        cargo_args.push("--features");
98        cargo_args.push(f);
99    }
100
101    cmd::run("cargo", &cargo_args, root, CROSS_ENV)
102}
103
104#[cfg(test)]
105mod tests {
106    #[test]
107    fn symlink_list_has_no_duplicates() {
108        let mut link_names: Vec<&str> = build_deps::versions::SONAMES.to_vec();
109        link_names.sort_unstable();
110        let original_len = link_names.len();
111        link_names.dedup();
112        assert_eq!(link_names.len(), original_len, "duplicate link names found");
113    }
114}