• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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