1 //! A utility for cross compiling binaries using Cross
2
3 use std::path::{Path, PathBuf};
4 use std::process::{self, Command};
5 use std::{env, fs};
6
7 use clap::Parser;
8
9 #[derive(Parser, Debug)]
10 struct Options {
11 /// The path to an artifacts directory expecting to contain directories
12 /// named after platform tripes with binaries inside.
13 #[clap(long)]
14 pub(crate) output: PathBuf,
15
16 /// A url prefix where the artifacts can be found
17 #[clap(long)]
18 pub(crate) target: String,
19 }
20
21 /// This function is required until an upstream PR lands
22 /// https://github.com/rust-embedded/cross/pull/597
prepare_workspace(workspace_root: &Path)23 fn prepare_workspace(workspace_root: &Path) {
24 let src = PathBuf::from(env!("CROSS_CONFIG"));
25 let dest = workspace_root.join("Cross.toml");
26 println!("{src:?} -> {dest:?}");
27 fs::copy(src, dest).unwrap();
28
29 // Unfortunately, cross runs into issues when cross compiling incramentally.
30 // To avoid this, the workspace must be cleaned
31 let cargo = env::current_dir().unwrap().join(env!("CARGO"));
32 Command::new(cargo)
33 .current_dir(workspace_root)
34 .arg("clean")
35 .status()
36 .unwrap();
37 }
38
39 /// Execute a build for the provided platform
execute_cross(working_dir: &Path, target_triple: &str)40 fn execute_cross(working_dir: &Path, target_triple: &str) {
41 let cross = env::current_dir().unwrap().join(env!("CROSS_BIN"));
42 let status = Command::new(cross)
43 .current_dir(working_dir)
44 .arg("build")
45 .arg("--release")
46 .arg("--locked")
47 .arg("--bin")
48 .arg("cargo-bazel")
49 .arg(format!("--target={target_triple}"))
50 .status()
51 .unwrap();
52
53 if !status.success() {
54 process::exit(status.code().unwrap_or(1));
55 }
56 }
57
58 /// Install results to the output directory
install_outputs(working_dir: &Path, triple: &str, output_dir: &Path)59 fn install_outputs(working_dir: &Path, triple: &str, output_dir: &Path) {
60 let is_windows_target = triple.contains("windows");
61 let binary_name = if is_windows_target {
62 "cargo-bazel.exe"
63 } else {
64 "cargo-bazel"
65 };
66
67 // Since we always build from the workspace root, and the output
68 // is always expected to be `./target/{triple}`, we build a path
69 // to the expected output and write it.
70 let artifact = working_dir
71 .join("target")
72 .join(triple)
73 .join("release")
74 .join(binary_name);
75
76 let dest = output_dir.join(triple).join(binary_name);
77 fs::create_dir_all(dest.parent().unwrap()).unwrap();
78 fs::rename(artifact, &dest).unwrap();
79 println!("Installed: {}", dest.display());
80 }
81
main()82 fn main() {
83 let opt = Options::parse();
84
85 // Locate the workspace root
86 let workspace_root = PathBuf::from(
87 env::var("BUILD_WORKSPACE_DIRECTORY")
88 .expect("cross_installer is designed to run under Bazel"),
89 )
90 .join("crate_universe");
91
92 // Do some setup
93 prepare_workspace(&workspace_root);
94
95 // Build the binary
96 execute_cross(&workspace_root, &opt.target);
97
98 // Install the results
99 install_outputs(&workspace_root, &opt.target, &opt.output);
100 }
101