libcnb-package 0.11.4

Library for cross-compiling and packaging buildpacks written with libcnb.rs
Documentation
use std::ffi::OsString;
use which::which;

/// Provides assistance for cross-compiling from the user's host platform to the desired target platform.
///
/// This function will not install required toolchains, linkers or compilers automatically. It will
/// look for the required tools and returns a human-readable help text if they cannot be found or
/// any other issue has been detected.
pub fn cross_compile_assistance(target_triple: impl AsRef<str>) -> CrossCompileAssistance {
    // Background: https://omarkhawaja.com/cross-compiling-rust-from-macos-to-linux/
    if target_triple.as_ref() == X86_64_UNKNOWN_LINUX_MUSL && cfg!(target_os = "macos") {
        // There is more than just one binary name here since we also support the binary name for
        // an older version cross_compile_assistance which suggested installing a different
        // toolchain.
        let possible_gcc_binary_names =
            vec!["x86_64-unknown-linux-musl-gcc", "x86_64-linux-musl-gcc"];

        possible_gcc_binary_names
            .iter()
            .find_map(|binary_name| which(binary_name).ok())
            .map_or_else(|| CrossCompileAssistance::HelpText(String::from(
                r#"For cross-compilation from macOS to x86_64-unknown-linux-musl, a C compiler and
linker for the target platform must be installed on your computer.

The easiest way to install the required cross-compilation toolchain is to run:
brew install messense/macos-cross-toolchains/x86_64-unknown-linux-musl

For more information, see:
https://github.com/messense/homebrew-macos-cross-toolchains"#,
            )), |gcc_binary_path| {
                CrossCompileAssistance::Configuration {
                    cargo_env: vec![
                        (
                            // Required until Cargo can auto-detect the musl-cross gcc/linker itself,
                            // since otherwise it checks for a binary named 'musl-gcc':
                            // https://github.com/FiloSottile/homebrew-musl-cross/issues/16
                            // https://github.com/rust-lang/cargo/issues/4133
                            OsString::from("CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER"),
                            OsString::from(&gcc_binary_path),
                        ),
                        (
                            // Required so that any crates that call out to gcc are also cross-compiled:
                            // https://github.com/alexcrichton/cc-rs/issues/82
                            OsString::from("CC_x86_64_unknown_linux_musl"),
                            OsString::from(&gcc_binary_path),
                        ),
                    ],
                }
            })
    } else if target_triple.as_ref() == X86_64_UNKNOWN_LINUX_MUSL && cfg!(target_os = "linux") {
        match which("musl-gcc") {
            Ok(_) => CrossCompileAssistance::Configuration { cargo_env: vec![] },
            Err(_) => CrossCompileAssistance::HelpText(String::from(
                r#"For cross-compilation from Linux to x86_64-unknown-linux-musl, a C compiler and
linker for the target platform must be installed on your computer.

The easiest way to install 'musl-gcc' is to install the 'musl-tools' package:
- https://packages.ubuntu.com/focal/musl-tools
- https://packages.debian.org/bullseye/musl-tools"#,
            )),
        }
    } else {
        CrossCompileAssistance::NoAssistance
    }
}

pub enum CrossCompileAssistance {
    /// No specific assistance available for the current host and target platform combination.
    NoAssistance,
    /// A human-readable help text with instructions on how to setup the
    /// host machine for cross-compilation.
    HelpText(String),
    /// Required configuration to cross-compile to the target platform.
    Configuration {
        cargo_env: Vec<(OsString, OsString)>,
    },
}

const X86_64_UNKNOWN_LINUX_MUSL: &str = "x86_64-unknown-linux-musl";