diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37c099c10..0aeecf9bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -207,6 +207,7 @@ jobs: - { target: thumbv7em-none-eabi, os: ubuntu-latest, std: 1 } - { target: thumbv7em-none-eabihf, os: ubuntu-latest, std: 1 } - { target: thumbv7m-none-eabi, os: ubuntu-latest, std: 1 } + - { target: zig, os: ubuntu-latest } build: name: target (${{ matrix.pretty }},${{ matrix.os }}) @@ -283,7 +284,7 @@ jobs: IMAGE: ${{ steps.build-docker-image.outputs.image }} shell: bash - name: Test Image - if: steps.prepare-meta.outputs.has-image + if: steps.prepare-meta.outputs.has-image && matrix.target != 'zig' run: ./ci/test.sh env: TARGET: ${{ matrix.target }} @@ -294,6 +295,12 @@ jobs: RUN: ${{ matrix.run }} RUNNERS: ${{ matrix.runners }} shell: bash + + - name: Test Zig Image + if: steps.prepare-meta.outputs.has-image && matrix.target == 'zig' + run: ./ci/test-zig.sh + shell: bash + - uses: ./.github/actions/cargo-install-upload-artifacts if: matrix.deploy with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b46ba777..80a7aa1f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +### Added + +- #880 - added a zig-based image, allowing multiple targets to be built from the same image. + ### Changed - #869 - ensure cargo configuration environment variable flags are passed to the docker container. diff --git a/ci/shared.sh b/ci/shared.sh new file mode 100644 index 000000000..457861dd7 --- /dev/null +++ b/ci/shared.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +function retry { + local tries="${TRIES-5}" + local timeout="${TIMEOUT-1}" + local try=0 + local exit_code=0 + + while (( try < tries )); do + if "${@}"; then + return 0 + else + exit_code=$? + fi + + sleep "${timeout}" + echo "Retrying ..." 1>&2 + try=$(( try + 1 )) + timeout=$(( timeout * 2 )) + done + + return ${exit_code} +} diff --git a/ci/test-zig.sh b/ci/test-zig.sh new file mode 100755 index 000000000..1b5b0394c --- /dev/null +++ b/ci/test-zig.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2086,SC1091,SC1090 + +set -x +set -euo pipefail + +# NOTE: "${@}" is an unbound variable for bash 3.2, which is the +# installed version on macOS. likewise, "${var[@]}" is an unbound +# error if var is an empty array. + +ci_dir=$(dirname "${BASH_SOURCE[0]}") +ci_dir=$(realpath "${ci_dir}") +project_home=$(dirname "${ci_dir}") +. "${ci_dir}"/shared.sh + +# zig cc is very slow: only use a few targets. +TARGETS=( + "aarch64-unknown-linux-gnu" + "aarch64-unknown-linux-musl" + "i586-unknown-linux-gnu" + "i586-unknown-linux-musl" +) + +main() { + export CROSS_BUILD_ZIG=1 + + local td= + local target= + + retry cargo fetch + cargo build + export CROSS="${project_home}/target/debug/cross" + + td="$(mktemp -d)" + git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word "${td}" + pushd "${td}" + + for target in "${TARGETS[@]}"; do + "${CROSS}" build --target "${target}" + # note: ensure #724 doesn't replicate during CI. + cargo clean + done + + popd + rm -rf "${td}" +} + +main "${@}" diff --git a/ci/test.sh b/ci/test.sh index b920429b9..a8f89ab4c 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# shellcheck disable=SC2086 +# shellcheck disable=SC2086,SC1091,SC1090 set -x set -euo pipefail @@ -8,27 +8,9 @@ set -euo pipefail # installed version on macOS. likewise, "${var[@]}" is an unbound # error if var is an empty array. -function retry { - local tries="${TRIES-5}" - local timeout="${TIMEOUT-1}" - local try=0 - local exit_code=0 - - while (( try < tries )); do - if "${@}"; then - return 0 - else - exit_code=$? - fi - - sleep "${timeout}" - echo "Retrying ..." 1>&2 - try=$(( try + 1 )) - timeout=$(( timeout * 2 )) - done - - return ${exit_code} -} +ci_dir=$(dirname "${BASH_SOURCE[0]}") +ci_dir=$(realpath "${ci_dir}") +. "${ci_dir}"/shared.sh workspace_test() { "${CROSS[@]}" build --target "${TARGET}" --workspace "$@" ${CROSS_FLAGS} diff --git a/docker/Dockerfile.zig b/docker/Dockerfile.zig new file mode 100644 index 000000000..d17a1415c --- /dev/null +++ b/docker/Dockerfile.zig @@ -0,0 +1,30 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +COPY zig.sh / +RUN /zig.sh + +# needed for the zig cache, which tries to install a cache dir +# in the current users directory, relative to $HOME. if the user +# doesn't exist, it will try to install in the root directory, +# getting permission errors +RUN adduser --uid 1000 --disabled-password --gecos "" cross + +RUN apt-get update && apt-get install --assume-yes --no-install-recommends \ + qemu-system \ + qemu-user + +# we don't export `BINDGEN_EXTRA_CLANG_ARGS`, `QEMU_LD_PREFIX`, or +# `PKG_CONFIG_PATH` since zig doesn't have a traditional sysroot structure, +# and we're not using standard, shared packages. none of the packages +# have runners either, since they do not ship with the required +# dynamic linker (`ld-linux-${arch}.so`). diff --git a/docker/zig.sh b/docker/zig.sh new file mode 100755 index 000000000..24ac9f8e1 --- /dev/null +++ b/docker/zig.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +set -x +set -euo pipefail + +# shellcheck disable=SC1091 +. lib.sh + +main() { + install_packages ca-certificates curl xz-utils + + install_zig + install_zigbuild + + purge_packages + rm "${0}" +} + +install_zig() { + local version="0.9.1" + local filename="zig-linux-x86_64-${version}.tar.xz" + local dst="/opt/zig" + + local td + td="$(mktemp -d)" + + pushd "${td}" + + curl --retry 3 -sSfL "https://ziglang.org/download/${version}/${filename}" -O + mkdir -p "${dst}" + tar --strip-components=1 -xJf "${filename}" --directory "${dst}" + + popd + + rm -rf "${td}" +} + +install_zigbuild() { + local version=0.10.3 + + local td + td="$(mktemp -d)" + + pushd "${td}" + + export RUSTUP_HOME="${td}/rustup" + export CARGO_HOME="${td}/cargo" + + curl --retry 3 -sSfL https://sh.rustup.rs -o rustup-init.sh + sh rustup-init.sh -y --no-modify-path --profile minimal + + PATH="${CARGO_HOME}/bin:${PATH}" \ + cargo install cargo-zigbuild \ + --version "${version}" \ + --root /usr/local + + popd + + rm -rf "${td}" +} + +main "${@}" diff --git a/docs/cross_toml.md b/docs/cross_toml.md index 51f878302..6e7c0b256 100644 --- a/docs/cross_toml.md +++ b/docs/cross_toml.md @@ -10,6 +10,7 @@ The `build` key allows you to set global variables, e.g.: [build] xargo = true build-std = true +zig = false default-target = "x86_64-unknown-linux-gnu" ``` @@ -33,6 +34,7 @@ The `target` key allows you to specify parameters for specific compilation targe [target.aarch64-unknown-linux-gnu] xargo = false build-std = false +zig = true image = "test-image" pre-build = ["apt-get update"] runner = "custom-runner" diff --git a/src/config.rs b/src/config.rs index 4c2ae734d..149f95ec0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -65,6 +65,10 @@ impl Environment { self.get_values_for("BUILD_STD", target, bool_from_envvar) } + fn zig(&self, target: &Target) -> (Option, Option) { + self.get_values_for("ZIG", target, bool_from_envvar) + } + fn image(&self, target: &Target) -> Option { self.get_target_var(target, "IMAGE") } @@ -244,6 +248,10 @@ impl Config { self.bool_from_config(target, Environment::build_std, CrossToml::build_std) } + pub fn zig(&self, target: &Target) -> Option { + self.bool_from_config(target, Environment::zig, CrossToml::zig) + } + pub fn image(&self, target: &Target) -> Result> { self.string_from_config(target, Environment::image, CrossToml::image) } @@ -382,16 +390,19 @@ mod tests { let env = Environment::new(Some(map)); assert_eq!(env.xargo(&target()), (Some(true), None)); assert_eq!(env.build_std(&target()), (Some(false), None)); + assert_eq!(env.zig(&target()), (None, None)); } #[test] pub fn build_and_target_set_returns_tuple() { let mut map = std::collections::HashMap::new(); map.insert("CROSS_BUILD_XARGO", "true"); + map.insert("CROSS_BUILD_ZIG", "false"); map.insert("CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_XARGO", "false"); let env = Environment::new(Some(map)); assert_eq!(env.xargo(&target()), (Some(true), Some(false))); + assert_eq!(env.zig(&target()), (Some(false), None)); } #[test] @@ -527,7 +538,6 @@ mod tests { config.env_volumes(&target())?, Some(vec![s!("VOLUME3"), s!("VOLUME4")]) ); - // TODO(ahuszagh) Need volumes Ok(()) } diff --git a/src/cross_toml.rs b/src/cross_toml.rs index 0eed16216..230868036 100644 --- a/src/cross_toml.rs +++ b/src/cross_toml.rs @@ -23,6 +23,7 @@ pub struct CrossBuildConfig { env: CrossEnvConfig, xargo: Option, build_std: Option, + zig: Option, default_target: Option, pre_build: Option>, #[serde(default, deserialize_with = "opt_string_or_struct")] @@ -35,6 +36,7 @@ pub struct CrossBuildConfig { pub struct CrossTargetConfig { xargo: Option, build_std: Option, + zig: Option, image: Option, #[serde(default, deserialize_with = "opt_string_or_struct")] dockerfile: Option, @@ -277,6 +279,11 @@ impl CrossToml { self.get_bool(target, |b| b.build_std, |t| t.build_std) } + /// Returns the `build.zig` or the `target.{}.zig` part of `Cross.toml` + pub fn zig(&self, target: &Target) -> (Option, Option) { + self.get_bool(target, |b| b.zig, |t| t.zig) + } + /// Returns the list of environment variables to pass through for `build` and `target` pub fn env_passthrough(&self, target: &Target) -> (Option<&[String]>, Option<&[String]>) { self.get_vec( @@ -441,6 +448,7 @@ mod tests { }, xargo: Some(true), build_std: None, + zig: None, default_target: None, pre_build: Some(vec!["echo 'Hello World!'".to_string()]), dockerfile: None, @@ -470,6 +478,7 @@ mod tests { target_map.insert( Target::BuiltIn { triple: "aarch64-unknown-linux-gnu".to_string(), + libc: None, }, CrossTargetConfig { env: CrossEnvConfig { @@ -478,6 +487,7 @@ mod tests { }, xargo: Some(false), build_std: Some(true), + zig: None, image: Some("test-image".to_string()), runner: None, dockerfile: None, @@ -514,10 +524,12 @@ mod tests { target_map.insert( Target::BuiltIn { triple: "aarch64-unknown-linux-gnu".to_string(), + libc: None, }, CrossTargetConfig { xargo: Some(false), build_std: None, + zig: None, image: None, dockerfile: Some(CrossTargetDockerfileConfig { file: "Dockerfile.test".to_string(), @@ -542,6 +554,7 @@ mod tests { }, xargo: Some(true), build_std: None, + zig: Some(false), default_target: None, pre_build: Some(vec![]), dockerfile: None, @@ -551,6 +564,7 @@ mod tests { let test_str = r#" [build] xargo = true + zig = false pre-build = [] [build.env] @@ -600,6 +614,7 @@ mod tests { }, build_std: None, xargo: Some(true), + zig: None, default_target: None, pre_build: None, dockerfile: None, @@ -746,7 +761,7 @@ mod tests { assert_eq!(build.env.volumes, Some(vec![])); let targets = &cfg_expected.targets; - let aarch64 = &targets[&Target::new_built_in("aarch64-unknown-linux-gnu")]; + let aarch64 = &targets[&Target::new_built_in("aarch64-unknown-linux-gnu", None)]; assert_eq!(aarch64.build_std, Some(true)); assert_eq!(aarch64.xargo, Some(false)); assert_eq!(aarch64.image, Some(s!("test-image1"))); diff --git a/src/docker/custom.rs b/src/docker/custom.rs index 5079de5ea..cf378320d 100644 --- a/src/docker/custom.rs +++ b/src/docker/custom.rs @@ -33,6 +33,7 @@ impl<'a> Dockerfile<'a> { build_args: impl IntoIterator, impl AsRef)>, target_triple: &Target, msg_info: MessageInfo, + uses_zig: bool, ) -> Result { let mut docker_build = docker::subcommand(engine, "build"); docker_build.current_dir(host_root); @@ -81,7 +82,7 @@ impl<'a> Dockerfile<'a> { }; if matches!(self, Dockerfile::File { .. }) { - if let Ok(cross_base_image) = self::image_name(config, target_triple) { + if let Ok(cross_base_image) = self::image_name(config, target_triple, uses_zig) { docker_build.args([ "--build-arg", &format!("CROSS_BASE_IMAGE={cross_base_image}"), diff --git a/src/docker/local.rs b/src/docker/local.rs index 7282a3232..9b7a00785 100644 --- a/src/docker/local.rs +++ b/src/docker/local.rs @@ -9,7 +9,7 @@ use crate::errors::Result; use crate::extensions::CommandExt; use crate::file::{PathExt, ToUtf8}; use crate::shell::{MessageInfo, Stream}; -use crate::{Config, Target}; +use crate::{CargoCommand, Config, Target}; use eyre::Context; #[allow(clippy::too_many_arguments)] // TODO: refactor @@ -19,7 +19,7 @@ pub(crate) fn run( args: &[String], metadata: &CargoMetadata, config: &Config, - uses_xargo: bool, + cargo_command: CargoCommand, sysroot: &Path, msg_info: MessageInfo, docker_in_docker: bool, @@ -27,7 +27,7 @@ pub(crate) fn run( ) -> Result { let dirs = Directories::create(engine, metadata, cwd, sysroot, docker_in_docker)?; - let mut cmd = cargo_safe_command(uses_xargo); + let mut cmd = cargo_safe_command(cargo_command); cmd.args(args); let mut docker = subcommand(engine, "run"); @@ -82,10 +82,18 @@ pub(crate) fn run( docker.arg("-t"); } } - let mut image = image_name(config, target)?; + let mut image = image_name(config, target, cargo_command.uses_zig())?; if needs_custom_image(target, config) { - image = custom_image_build(target, config, metadata, dirs, engine, msg_info) - .wrap_err("when building custom image")? + image = custom_image_build( + target, + config, + metadata, + dirs, + engine, + msg_info, + cargo_command.uses_zig(), + ) + .wrap_err("when building custom image")? } docker diff --git a/src/docker/mod.rs b/src/docker/mod.rs index b341736f4..3dd96b8cc 100644 --- a/src/docker/mod.rs +++ b/src/docker/mod.rs @@ -13,7 +13,7 @@ use std::process::ExitStatus; use crate::cargo::CargoMetadata; use crate::errors::*; use crate::shell::MessageInfo; -use crate::{Config, Target}; +use crate::{CargoCommand, Config, Target}; #[allow(clippy::too_many_arguments)] // TODO: refactor pub fn run( @@ -22,7 +22,7 @@ pub fn run( args: &[String], metadata: &CargoMetadata, config: &Config, - uses_xargo: bool, + cargo_command: CargoCommand, sysroot: &Path, msg_info: MessageInfo, docker_in_docker: bool, @@ -35,7 +35,7 @@ pub fn run( args, metadata, config, - uses_xargo, + cargo_command, sysroot, msg_info, docker_in_docker, @@ -48,7 +48,7 @@ pub fn run( args, metadata, config, - uses_xargo, + cargo_command, sysroot, msg_info, docker_in_docker, diff --git a/src/docker/remote.rs b/src/docker/remote.rs index 2167ed513..dcb30ef16 100644 --- a/src/docker/remote.rs +++ b/src/docker/remote.rs @@ -15,7 +15,7 @@ use crate::rustc::{self, VersionMetaExt}; use crate::rustup; use crate::shell::{self, MessageInfo, Stream}; use crate::temp; -use crate::{Host, Target}; +use crate::{CargoCommand, Host, Target}; // the mount directory for the data volume. pub const MOUNT_PREFIX: &str = "/cross"; @@ -704,7 +704,7 @@ pub(crate) fn run( args: &[String], metadata: &CargoMetadata, config: &Config, - uses_xargo: bool, + cargo_command: CargoCommand, sysroot: &Path, msg_info: MessageInfo, docker_in_docker: bool, @@ -795,7 +795,7 @@ pub(crate) fn run( } docker - .arg(&image_name(config, target)?) + .arg(&image_name(config, target, cargo_command.uses_zig())?) // ensure the process never exits until we stop it .args(&["sh", "-c", "sleep infinity"]) .run_and_get_status(msg_info, true)?; @@ -938,7 +938,7 @@ pub(crate) fn run( final_args.push("--target-dir".to_string()); final_args.push(target_dir_string); } - let mut cmd = cargo_safe_command(uses_xargo); + let mut cmd = cargo_safe_command(cargo_command); cmd.args(final_args); // 5. create symlinks for copied data diff --git a/src/docker/shared.rs b/src/docker/shared.rs index 8288f3e30..6cba35cab 100644 --- a/src/docker/shared.rs +++ b/src/docker/shared.rs @@ -13,7 +13,7 @@ use crate::file::{self, write_file, PathExt, ToUtf8}; use crate::id; use crate::rustc::{self, VersionMetaExt}; use crate::shell::{self, MessageInfo, Verbosity}; -use crate::Target; +use crate::{CargoCommand, Target}; pub use super::custom::CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX; @@ -212,12 +212,8 @@ pub fn parse_docker_opts(value: &str) -> Result> { shell_words::split(value).wrap_err_with(|| format!("could not parse docker opts of {}", value)) } -pub(crate) fn cargo_safe_command(uses_xargo: bool) -> SafeCommand { - if uses_xargo { - SafeCommand::new("xargo") - } else { - SafeCommand::new("cargo") - } +pub(crate) fn cargo_safe_command(cargo_command: CargoCommand) -> SafeCommand { + SafeCommand::new(cargo_command.to_str()) } fn add_cargo_configuration_envvars(docker: &mut Command) { @@ -458,8 +454,9 @@ pub(crate) fn custom_image_build( Directories { host_root, .. }: Directories, engine: &Engine, msg_info: MessageInfo, + uses_zig: bool, ) -> Result { - let mut image = image_name(config, target)?; + let mut image = image_name(config, target, uses_zig)?; if let Some(path) = config.dockerfile(target)? { let context = config.dockerfile_context(target)?; @@ -480,6 +477,7 @@ pub(crate) fn custom_image_build( config.dockerfile_build_args(target)?.unwrap_or_default(), target, msg_info, + uses_zig, ) .wrap_err("when building dockerfile")?; } @@ -505,6 +503,7 @@ pub(crate) fn custom_image_build( Some(("CROSS_CMD", pre_build.join("\n"))), target, msg_info, + uses_zig, ) .wrap_err("when pre-building") .with_note(|| format!("CROSS_CMD={}", pre_build.join("\n")))?; @@ -514,7 +513,7 @@ pub(crate) fn custom_image_build( Ok(image) } -pub(crate) fn image_name(config: &Config, target: &Target) -> Result { +pub(crate) fn image_name(config: &Config, target: &Target, uses_zig: bool) -> Result { if let Some(image) = config.image(target)? { return Ok(image); } @@ -532,7 +531,11 @@ pub(crate) fn image_name(config: &Config, target: &Target) -> Result { "main" }; - Ok(format!("{CROSS_IMAGE}/{target}:{version}")) + if uses_zig { + Ok(format!("{CROSS_IMAGE}/zig:{version}")) + } else { + Ok(format!("{CROSS_IMAGE}/{target}:{version}")) + } } fn docker_read_mount_paths(engine: &Engine) -> Result> { diff --git a/src/lib.rs b/src/lib.rs index c3db3954d..85ef536f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,14 +150,20 @@ impl<'a> From<&'a str> for Host { #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)] #[serde(from = "String")] pub enum Target { - BuiltIn { triple: String }, - Custom { triple: String }, + BuiltIn { + triple: String, + libc: Option, + }, + Custom { + triple: String, + }, } impl Target { - fn new_built_in(triple: &str) -> Self { + fn new_built_in(triple: &str, libc: Option<&str>) -> Self { Target::BuiltIn { triple: triple.to_owned(), + libc: libc.map(|s| s.to_owned()), } } @@ -169,8 +175,22 @@ impl Target { fn triple(&self) -> &str { match *self { - Target::BuiltIn { ref triple } => triple, - Target::Custom { ref triple } => triple, + Target::BuiltIn { ref triple, .. } => triple, + Target::Custom { ref triple, .. } => triple, + } + } + + fn libc(&self) -> Option<&str> { + match *self { + Target::BuiltIn { ref libc, .. } => libc.as_deref(), + Target::Custom { .. } => None, + } + } + + fn needs_zig(&self) -> bool { + match *self { + Target::BuiltIn { ref libc, .. } => libc.is_some(), + Target::Custom { .. } => false, } } @@ -312,16 +332,38 @@ impl Target { impl std::fmt::Display for Target { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.triple()) + f.write_str(self.triple())?; + if let Some(libc) = self.libc() { + f.write_str(".")?; + f.write_str(libc)?; + } + Ok(()) } } impl Target { - pub fn from(triple: &str, target_list: &TargetList) -> Target { + pub fn from(target: &str, target_list: &TargetList) -> Target { + // we need to extract the libc component if we have a zig + // target here, like `aarch64-unknown-linux-gnu.2.17` + // however, some targets have valid `.` characters, like + // `thumbv8m.base-none-eabi`, so we have to split only the libc. + + // with some custom toolchains, this might not actually be a libc, + // but since we format it identically, it doesn't matter. + let (triple, libc) = match target.rsplit_once('-') { + Some((_, suffix)) => match suffix.split_once('.') { + Some((_, libc)) => { + let triple = &target[..target.len() - libc.len() - 1]; + (triple, Some(libc)) + } + None => (target, None), + }, + None => (target, None), + }; if target_list.contains(triple) { - Target::new_built_in(triple) + Target::new_built_in(triple, libc) } else { - Target::new_custom(triple) + Target::new_custom(target) } } } @@ -329,13 +371,15 @@ impl Target { impl From for Target { fn from(host: Host) -> Target { match host { - Host::X86_64UnknownLinuxGnu => Target::new_built_in("x86_64-unknown-linux-gnu"), - Host::X86_64UnknownLinuxMusl => Target::new_built_in("x86_64-unknown-linux-musl"), - Host::X86_64AppleDarwin => Target::new_built_in("x86_64-apple-darwin"), - Host::X86_64PcWindowsMsvc => Target::new_built_in("x86_64-pc-windows-msvc"), - Host::Aarch64AppleDarwin => Target::new_built_in("aarch64-apple-darwin"), - Host::Aarch64UnknownLinuxGnu => Target::new_built_in("aarch64-unknown-linux-gnu"), - Host::Aarch64UnknownLinuxMusl => Target::new_built_in("aarch64-unknown-linux-musl"), + Host::X86_64UnknownLinuxGnu => Target::new_built_in("x86_64-unknown-linux-gnu", None), + Host::X86_64UnknownLinuxMusl => Target::new_built_in("x86_64-unknown-linux-musl", None), + Host::X86_64AppleDarwin => Target::new_built_in("x86_64-apple-darwin", None), + Host::X86_64PcWindowsMsvc => Target::new_built_in("x86_64-pc-windows-msvc", None), + Host::Aarch64AppleDarwin => Target::new_built_in("aarch64-apple-darwin", None), + Host::Aarch64UnknownLinuxGnu => Target::new_built_in("aarch64-unknown-linux-gnu", None), + Host::Aarch64UnknownLinuxMusl => { + Target::new_built_in("aarch64-unknown-linux-musl", None) + } Host::Other(s) => Target::from( s.as_str(), &rustc::target_list(Verbosity::Quiet.into()).unwrap(), @@ -353,11 +397,42 @@ impl From for Target { impl Serialize for Target { fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&self.to_string()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CargoCommand { + Cargo, + Xargo, + Zig, +} + +impl CargoCommand { + pub fn create(uses_zig: bool, uses_xargo: bool) -> Result { + match (uses_zig, uses_xargo) { + (true, true) => eyre::bail!("cannot use both zig and xargo"), + (true, false) => Ok(CargoCommand::Zig), + (false, true) => Ok(CargoCommand::Xargo), + (false, false) => Ok(CargoCommand::Cargo), + } + } + + pub fn to_str(self) -> &'static str { match self { - Target::BuiltIn { triple } => serializer.serialize_str(triple), - Target::Custom { triple } => serializer.serialize_str(triple), + CargoCommand::Cargo => "cargo", + CargoCommand::Xargo => "xargo", + CargoCommand::Zig => "cargo-zigbuild", } } + + pub fn uses_xargo(self) -> bool { + self == CargoCommand::Xargo + } + + pub fn uses_zig(self) -> bool { + self == CargoCommand::Zig + } } pub fn run() -> Result { @@ -387,7 +462,14 @@ pub fn run() -> Result { .unwrap_or_else(|| Target::from(host.triple(), &target_list)); config.confusable_target(&target, args.msg_info)?; - let image_exists = match docker::image_name(&config, &target) { + let uses_zig = config.zig(&target).unwrap_or(false); + if target.needs_zig() && !uses_zig { + shell::warn( + format!("target \"{target}\" needs zig image, but zig is not being used."), + args.msg_info, + )?; + } + let image_exists = match docker::image_name(&config, &target, uses_zig) { Ok(_) => true, Err(err) => { shell::warn(err, args.msg_info)?; @@ -417,9 +499,11 @@ pub fn run() -> Result { is_nightly = channel == Channel::Nightly; } + // If we don't use zig but have libc, have a fatal warning. let uses_build_std = config.build_std(&target).unwrap_or(false); let uses_xargo = !uses_build_std && config.xargo(&target).unwrap_or(!target.is_builtin()); + let cargo_command = CargoCommand::create(uses_zig, uses_xargo)?; if std::env::var("CROSS_CUSTOM_TOOLCHAIN").is_err() { // build-std overrides xargo, but only use it if it's a built-in // tool but not an available target or doesn't have rust-std. @@ -513,7 +597,7 @@ pub fn run() -> Result { &filtered_args, &metadata, &config, - uses_xargo, + cargo_command, &sysroot, args.msg_info, args.docker_in_docker,