diff --git a/crates/cargo-lambda-build/src/lib.rs b/crates/cargo-lambda-build/src/lib.rs index 319b88a7..7451a044 100644 --- a/crates/cargo-lambda-build/src/lib.rs +++ b/crates/cargo-lambda-build/src/lib.rs @@ -8,8 +8,10 @@ use std::{ fs::{create_dir_all, read, File}, io::Write, path::{Path, PathBuf}, + str::FromStr, }; use strum_macros::EnumString; +use target_arch::TargetArch; use zip::{write::FileOptions, ZipWriter}; mod toolchain; @@ -40,8 +42,10 @@ pub struct Build { pub use cargo_zigbuild::Zig; -const TARGET_ARM: &str = "aarch64-unknown-linux-gnu"; -const TARGET_X86_64: &str = "x86_64-unknown-linux-gnu"; +pub const TARGET_ARM: &str = "aarch64-unknown-linux-gnu"; +pub const TARGET_X86_64: &str = "x86_64-unknown-linux-gnu"; + +mod target_arch; #[derive(Clone, Debug, strum_macros::Display, EnumString)] #[strum(ascii_case_insensitive)] @@ -62,34 +66,23 @@ impl Build { )); } - if self.arm64 { - self.build.target = vec![TARGET_ARM.into()]; - } - - let build_target = self.build.target.get(0); - match build_target { - Some(target) => { - // Validate that the build target is supported in AWS Lambda - check_build_target(target)?; - } - // No explicit target, but build host same as target host - None if host_target == TARGET_ARM || host_target == TARGET_X86_64 => { - // Set the target explicitly, so it's easier to find the binaries later - self.build.target = vec![host_target.into()]; - } - // No explicit target, and build host not compatible with Lambda hosts - None => { - self.build.target = vec![TARGET_X86_64.into()]; + let target_arch = if self.arm64 { + TargetArch::arm64() + } else { + let build_target = self.build.target.get(0); + match build_target { + Some(target) => TargetArch::from_str(target)?, + // No explicit target, but build host same as target host + None if host_target == TARGET_ARM || host_target == TARGET_X86_64 => { + // Set the target explicitly, so it's easier to find the binaries later + TargetArch::from_str(host_target)? + } + // No explicit target, and build host not compatible with Lambda hosts + None => TargetArch::x86_64(), } }; - - let final_target = self - .build - .target - .get(0) - .map(|x| x.split_once('.').map(|(t, _)| t).unwrap_or(x.as_str())) - .unwrap_or(TARGET_X86_64); - + self.build.target = vec![target_arch.full_zig_string()]; + let rustc_target_without_glibc_version = target_arch.rustc_target_without_glibc_version(); let profile = match self.build.profile.as_deref() { Some("dev" | "test") => "debug", Some("release" | "bench") => "release", @@ -101,7 +94,7 @@ impl Build { // confirm that target component is included in host toolchain, or add // it with `rustup` otherwise. toolchain::check_target_component_with_rustc_meta( - final_target, + &rustc_target_without_glibc_version, host_target, release_channel, ) @@ -134,7 +127,11 @@ impl Build { .build_command("build") .map_err(|e| miette::miette!("{}", e))?; if self.build.release { - cmd.env("RUSTFLAGS", "-C strip=symbols"); + let target_cpu = target_arch.target_cpu(); + cmd.env( + "RUSTFLAGS", + format!("-C strip=symbols -C target-cpu={target_cpu}"), + ); } let mut child = cmd @@ -156,7 +153,9 @@ impl Build { target_dir.join("lambda") }; - let base = target_dir.join(final_target).join(profile); + let base = target_dir + .join(rustc_target_without_glibc_version) + .join(profile); for name in &binaries { let binary = base.join(name); @@ -194,25 +193,6 @@ impl Build { } } -/// Validate that the build target is supported in AWS Lambda -/// -/// Here we use *starts with* instead of an exact match because: -/// - the target could also also be a *musl* variant: `x86_64-unknown-linux-musl` -/// - the target could also [specify a glibc version], which `cargo-zigbuild` supports -/// -/// [specify a glibc version]: https://github.com/messense/cargo-zigbuild#specify-glibc-version -fn check_build_target(target: &str) -> Result<()> { - if !target.starts_with("aarch64-unknown-linux") && !target.starts_with("x86_64-unknown-linux") { - // Unsupported target for an AWS Lambda environment - return Err(miette::miette!( - "Invalid or unsupported target for AWS Lambda: {}", - target - )); - } - - Ok(()) -} - pub struct BinaryArchive { pub architecture: String, pub sha256: String, diff --git a/crates/cargo-lambda-build/src/target_arch.rs b/crates/cargo-lambda-build/src/target_arch.rs new file mode 100644 index 00000000..2696bb27 --- /dev/null +++ b/crates/cargo-lambda-build/src/target_arch.rs @@ -0,0 +1,91 @@ +use miette::Result; +use std::str::FromStr; + +use crate::{TARGET_ARM, TARGET_X86_64}; + +enum Arch { + ARM64, + X86_64, +} + +pub struct TargetArch { + rustc_target_without_glibc_version: String, + glibc_version: Option, + arch: Arch, +} + +impl TargetArch { + pub fn arm64() -> Self { + Self { + glibc_version: None, + rustc_target_without_glibc_version: TARGET_ARM.into(), + arch: Arch::ARM64, + } + } + pub fn x86_64() -> Self { + Self { + glibc_version: None, + rustc_target_without_glibc_version: TARGET_X86_64.into(), + arch: Arch::X86_64, + } + } + pub fn full_zig_string(&self) -> String { + format!( + "{}.{}", + self.rustc_target_without_glibc_version, + self.glibc_version.as_ref().unwrap_or(&"".to_string()) + ) + } + + pub fn target_cpu(&self) -> String { + match self.arch { + Arch::ARM64 => "neoverse-n1".to_string(), + Arch::X86_64 => "haswell".to_string(), + } + } + + pub fn rustc_target_without_glibc_version(&self) -> String { + self.rustc_target_without_glibc_version.to_string() + } +} +impl FromStr for TargetArch { + type Err = miette::Report; + + fn from_str(s: &str) -> Result { + // Validate that the build target is supported in AWS Lambda + let arch = check_build_target(s)?; + match s.split_once('.') { + Some((rustc_target_without_glibc_version, glibc_version)) => Ok(Self { + rustc_target_without_glibc_version: rustc_target_without_glibc_version.into(), + glibc_version: Some(glibc_version.into()), + arch, + }), + None => Ok(Self { + rustc_target_without_glibc_version: s.into(), + glibc_version: None, + arch, + }), + } + } +} + +/// Validate that the build target is supported in AWS Lambda +/// +/// Here we use *starts with* instead of an exact match because: +/// - the target could also also be a *musl* variant: `x86_64-unknown-linux-musl` +/// - the target could also [specify a glibc version], which `cargo-zigbuild` supports +/// +/// [specify a glibc version]: https://github.com/messense/cargo-zigbuild#specify-glibc-version +fn check_build_target(target: &str) -> Result { + if target.starts_with("aarch64-unknown-linux") { + Ok(Arch::ARM64) + } else if target.starts_with("x86_64-unknown-linux") { + Ok(Arch::X86_64) + } else { + // Unsupported target for an AWS Lambda environment + Err(miette::miette!( + "Invalid or unsupported target for AWS Lambda: {}", + target + )) + } +}