libcnb_package/
cross_compile.rs

1use indoc::{formatdoc, indoc};
2use std::env::consts;
3use std::ffi::OsString;
4use which::which;
5
6/// Provides assistance for cross-compiling from the user's host platform to the desired target platform.
7///
8/// This function will not install required toolchains, linkers or compilers automatically. It will
9/// look for the required tools and returns a human-readable help text if they can't be found or
10/// any other issue has been detected.
11pub fn cross_compile_assistance(target_triple: impl AsRef<str>) -> CrossCompileAssistance {
12    let target_triple = target_triple.as_ref();
13    let (gcc_binary_name, help_text) = match (target_triple, consts::OS, consts::ARCH) {
14        (AARCH64_UNKNOWN_LINUX_MUSL, OS_LINUX, ARCH_X86_64) => (
15            "aarch64-linux-gnu-gcc",
16            indoc! {"
17                To install an aarch64 cross-compiler on Ubuntu:
18                sudo apt-get install g++-aarch64-linux-gnu libc6-dev-arm64-cross musl-tools
19            "},
20        ),
21        (AARCH64_UNKNOWN_LINUX_MUSL, OS_MACOS, ARCH_X86_64 | ARCH_AARCH64) => (
22            "aarch64-unknown-linux-musl-gcc",
23            indoc! {"
24                To install an aarch64 cross-compiler on macOS:
25                brew install messense/macos-cross-toolchains/aarch64-unknown-linux-musl
26            "},
27        ),
28        (AARCH64_UNKNOWN_LINUX_MUSL, OS_LINUX, ARCH_AARCH64)
29        | (X86_64_UNKNOWN_LINUX_MUSL, OS_LINUX, ARCH_X86_64) => (
30            "musl-gcc",
31            indoc! {"
32                To install musl-tools on Ubuntu:
33                sudo apt-get install musl-tools
34            "},
35        ),
36        (X86_64_UNKNOWN_LINUX_MUSL, OS_LINUX, ARCH_AARCH64) => (
37            "x86_64-linux-gnu-gcc",
38            indoc! {"
39                To install an x86_64 cross-compiler on Ubuntu:
40                sudo apt-get install g++-x86-64-linux-gnu libc6-dev-amd64-cross musl-tools
41            "},
42        ),
43        (X86_64_UNKNOWN_LINUX_MUSL, OS_MACOS, ARCH_X86_64 | ARCH_AARCH64) => (
44            "x86_64-unknown-linux-musl-gcc",
45            indoc! {"
46                To install an x86_64 cross-compiler on macOS:
47                brew install messense/macos-cross-toolchains/x86_64-unknown-linux-musl
48            "},
49        ),
50        _ => return CrossCompileAssistance::NoAssistance,
51    };
52
53    match which(gcc_binary_name) {
54        Ok(_) => {
55            // When the gcc binary name is `musl-gcc`, Cargo will automatically select the appropriate default linker,
56            // and set the required environment variables.
57            if gcc_binary_name == "musl-gcc" {
58                CrossCompileAssistance::Configuration {
59                    cargo_env: Vec::new(),
60                }
61            } else {
62                CrossCompileAssistance::Configuration {
63                    cargo_env: vec![
64                        (
65                            // Required until Cargo can auto-detect the musl-cross gcc/linker itself,
66                            // since otherwise it checks for a binary named 'musl-gcc' (which is handled above):
67                            // https://github.com/rust-lang/cargo/issues/4133
68                            OsString::from(format!(
69                                "CARGO_TARGET_{}_LINKER",
70                                target_triple.to_uppercase().replace('-', "_")
71                            )),
72                            OsString::from(gcc_binary_name),
73                        ),
74                        (
75                            // Required so that any crates that call out to gcc are also cross-compiled:
76                            // https://github.com/alexcrichton/cc-rs/issues/82
77                            OsString::from(format!("CC_{}", target_triple.replace('-', "_"))),
78                            OsString::from(gcc_binary_name),
79                        ),
80                    ],
81                }
82            }
83        }
84        Err(_) => CrossCompileAssistance::HelpText(formatdoc! {"
85            For cross-compilation from {0} {1} to {target_triple},
86            a C compiler and linker for the target platform must be installed:
87
88            {help_text}
89            You will also need to install the Rust target:
90            rustup target add {target_triple}
91            ",
92            consts::ARCH,
93            consts::OS
94        }),
95    }
96}
97
98pub enum CrossCompileAssistance {
99    /// No specific assistance available for the current host and target platform combination.
100    NoAssistance,
101    /// A human-readable help text with instructions on how to setup the
102    /// host machine for cross-compilation.
103    HelpText(String),
104    /// Required configuration to cross-compile to the target platform.
105    Configuration {
106        cargo_env: Vec<(OsString, OsString)>,
107    },
108}
109
110// Constants for supported target triples
111pub const AARCH64_UNKNOWN_LINUX_MUSL: &str = "aarch64-unknown-linux-musl";
112pub const X86_64_UNKNOWN_LINUX_MUSL: &str = "x86_64-unknown-linux-musl";
113
114// Constants for `std::env::consts::OS` and `std::env::consts::ARCH`
115const OS_LINUX: &str = "linux";
116const OS_MACOS: &str = "macos";
117const ARCH_X86_64: &str = "x86_64";
118const ARCH_AARCH64: &str = "aarch64";