913 lines
32 KiB
Rust
913 lines
32 KiB
Rust
use super::*;
|
|
use crate::package::{Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec};
|
|
use crate::test_support::TestEnv;
|
|
use std::collections::HashMap;
|
|
use std::ffi::OsStr;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
fn mk_spec(cflags: Vec<&str>, ldflags: Vec<&str>) -> PackageSpec {
|
|
let flags = BuildFlags {
|
|
cflags: cflags.into_iter().map(String::from).collect(),
|
|
ldflags: ldflags.into_iter().map(String::from).collect(),
|
|
..BuildFlags::default()
|
|
};
|
|
PackageSpec {
|
|
package: PackageInfo {
|
|
name: "env-test".into(),
|
|
real_name: None,
|
|
version: "1.0".into(),
|
|
revision: 1,
|
|
description: "d".into(),
|
|
homepage: "h".into(),
|
|
abi_breaking: false,
|
|
built_against: Vec::new(),
|
|
license: vec!["MIT".into()],
|
|
},
|
|
packages: Vec::new(),
|
|
alternatives: Default::default(),
|
|
manual_sources: Vec::new(),
|
|
source: vec![crate::package::Source {
|
|
url: "https://example.test/src.tar.gz".into(),
|
|
sha256: "abc".into(),
|
|
extract_dir: "src".into(),
|
|
patches: Vec::new(),
|
|
post_extract: Vec::new(),
|
|
cherry_pick: Vec::new(),
|
|
}],
|
|
build: Build {
|
|
build_type: BuildType::Custom,
|
|
flags,
|
|
},
|
|
dependencies: Dependencies::default(),
|
|
package_alternatives: Default::default(),
|
|
package_dependencies: Default::default(),
|
|
spec_dir: PathBuf::from("."),
|
|
}
|
|
}
|
|
|
|
fn env_value<'a>(env: &'a EnvVars, key: &str) -> Option<&'a str> {
|
|
env.iter()
|
|
.find(|(candidate, _)| candidate == key)
|
|
.map(|(_, value)| value.as_str())
|
|
}
|
|
|
|
#[test]
|
|
fn test_prepare_command() {
|
|
let mut cmd = Command::new("ls");
|
|
// Set an env var that should be cleared
|
|
cmd.env("FORBIDDEN", "value");
|
|
// Set PATH manually in the current process to ensure it's picked up if it exists
|
|
let mut env = TestEnv::new();
|
|
env.set_var("PATH", "/usr/bin");
|
|
env.set_var("HOME", "/home/test");
|
|
env.set_var("SHELL", "/bin/zsh");
|
|
env.set_var("DEPOT_ROOTFS", "/my/rootfs");
|
|
env.set_var("TERM", "xterm-256color");
|
|
env.set_var("CLICOLOR_FORCE", "1");
|
|
|
|
prepare_command(&mut cmd, &vec![("MYVAR".to_string(), "myval".to_string())]);
|
|
|
|
let envs: HashMap<_, _> = cmd.get_envs().collect();
|
|
assert!(envs.contains_key(OsStr::new("PATH")));
|
|
assert!(envs.contains_key(OsStr::new("HOME")));
|
|
assert!(!envs.contains_key(OsStr::new("FORBIDDEN")));
|
|
assert_eq!(
|
|
envs.get(OsStr::new("SHELL")),
|
|
Some(&Some(std::ffi::OsString::from("/bin/sh").as_os_str()))
|
|
);
|
|
assert_eq!(
|
|
envs.get(OsStr::new("MYVAR")),
|
|
Some(&Some(std::ffi::OsString::from("myval").as_os_str()))
|
|
);
|
|
// DEPOT_ROOTFS should be preserved from the parent environment
|
|
assert_eq!(
|
|
envs.get(OsStr::new("DEPOT_ROOTFS")),
|
|
Some(&Some(std::ffi::OsString::from("/my/rootfs").as_os_str()))
|
|
);
|
|
assert_eq!(
|
|
envs.get(OsStr::new("TERM")),
|
|
Some(&Some(
|
|
std::ffi::OsString::from("xterm-256color").as_os_str()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
envs.get(OsStr::new("CLICOLOR_FORCE")),
|
|
Some(&Some(std::ffi::OsString::from("1").as_os_str()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_prepare_command_preserves_destdir() {
|
|
let mut cmd = std::process::Command::new("ls");
|
|
let mut env = TestEnv::new();
|
|
env.set_var("DESTDIR", "/tmp/dest");
|
|
prepare_command(&mut cmd, &Vec::new());
|
|
let envs: HashMap<_, _> = cmd.get_envs().collect();
|
|
assert_eq!(
|
|
envs.get(OsStr::new("DESTDIR")),
|
|
Some(&Some(std::ffi::OsString::from("/tmp/dest").as_os_str()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_prepare_command_preserves_rust_toolchain_homes() {
|
|
let mut cmd = std::process::Command::new("ls");
|
|
let mut env = TestEnv::new();
|
|
env.set_var("CARGO_HOME", "/var/cache/cargo-home");
|
|
env.set_var("RUSTUP_HOME", "/var/cache/rustup-home");
|
|
prepare_command(&mut cmd, &Vec::new());
|
|
let envs: HashMap<_, _> = cmd.get_envs().collect();
|
|
assert_eq!(
|
|
envs.get(OsStr::new("CARGO_HOME")),
|
|
Some(&Some(
|
|
std::ffi::OsString::from("/var/cache/cargo-home").as_os_str()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
envs.get(OsStr::new("RUSTUP_HOME")),
|
|
Some(&Some(
|
|
std::ffi::OsString::from("/var/cache/rustup-home").as_os_str()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_tool_package_option_maps_supported_builders() {
|
|
assert_eq!(
|
|
build_tool_package_option(BuildType::Meson),
|
|
Some("DEPOT_MESON_PACKAGE")
|
|
);
|
|
assert_eq!(
|
|
build_tool_package_option(BuildType::CMake),
|
|
Some("DEPOT_CMAKE_PACKAGE")
|
|
);
|
|
assert_eq!(build_tool_package_option(BuildType::Bin), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_static_build_args_skip_disable_static_when_no_delete_static_enabled() {
|
|
let args = static_build_args_for_request(BuildType::Autotools, Some(false), true);
|
|
assert!(args.is_empty());
|
|
|
|
let args = static_build_args_for_request(BuildType::CMake, Some(false), true);
|
|
assert!(args.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_static_build_args_keep_other_requested_modes() {
|
|
assert_eq!(
|
|
static_build_args_for_request(BuildType::Autotools, Some(false), false),
|
|
vec![
|
|
"--enable-shared".to_string(),
|
|
"--disable-static".to_string()
|
|
]
|
|
);
|
|
assert_eq!(
|
|
static_build_args_for_request(BuildType::CMake, Some(false), false),
|
|
vec!["-DBUILD_SHARED_LIBS=ON".to_string()]
|
|
);
|
|
assert_eq!(
|
|
static_build_args_for_request(BuildType::Meson, Some(false), false),
|
|
vec!["-Ddefault_library=shared".to_string()]
|
|
);
|
|
assert_eq!(
|
|
static_build_args_for_request(BuildType::Perl, Some(false), false),
|
|
vec!["LINKTYPE=dynamic".to_string()]
|
|
);
|
|
assert_eq!(
|
|
static_build_args_for_request(BuildType::Meson, Some(true), true),
|
|
vec!["-Ddefault_library=static".to_string()]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_native_linker_and_cpp() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.ranlib = "llvm-ranlib".to_string();
|
|
spec.build.flags.strip = "llvm-strip".to_string();
|
|
spec.build.flags.ld = "ld.lld".to_string();
|
|
spec.build.flags.nm = "llvm-nm".to_string();
|
|
spec.build.flags.objcopy = "llvm-objcopy".to_string();
|
|
spec.build.flags.objdump = "llvm-objdump".to_string();
|
|
spec.build.flags.readelf = "llvm-readelf".to_string();
|
|
spec.build.flags.cpp = "clang-cpp".to_string();
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(env.iter().any(|(k, v)| k == "RANLIB" && v == "llvm-ranlib"));
|
|
assert!(env.iter().any(|(k, v)| k == "STRIP" && v == "llvm-strip"));
|
|
assert!(env.iter().any(|(k, v)| k == "LD" && v == "ld.lld"));
|
|
assert!(env.iter().any(|(k, v)| k == "NM" && v == "llvm-nm"));
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "OBJCOPY" && v == "llvm-objcopy")
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "OBJDUMP" && v == "llvm-objdump")
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "READELF" && v == "llvm-readelf")
|
|
);
|
|
assert!(env.iter().any(|(k, v)| k == "CPP" && v == "clang-cpp"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_tool_dir_and_expands_tool_commands() {
|
|
let mut spec = mk_spec(
|
|
vec!["--gcc-toolchain=$TOOL_DIR"],
|
|
vec!["-B$TOOL_DIR", "-Wl,--as-needed"],
|
|
);
|
|
spec.build.flags.tool_dir = "/opt/depot-tools/bin".to_string();
|
|
spec.build.flags.cc = "$TOOL_DIR/clang".to_string();
|
|
spec.build.flags.cxx = "$TOOL_DIR/clang++".to_string();
|
|
spec.build.flags.ar = "$TOOL_DIR/llvm-ar".to_string();
|
|
spec.build.flags.ranlib = "$TOOL_DIR/llvm-ranlib".to_string();
|
|
spec.build.flags.ld = "$TOOL_DIR/ld.lld".to_string();
|
|
spec.build.flags.env_vars = vec!["LLVM_CONFIG=$TOOL_DIR/llvm-config".to_string()];
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
|
|
assert_eq!(env_value(&env, "TOOL_DIR"), Some("/opt/depot-tools/bin"));
|
|
assert_eq!(env_value(&env, "CC"), Some("/opt/depot-tools/bin/clang"));
|
|
assert_eq!(env_value(&env, "CXX"), Some("/opt/depot-tools/bin/clang++"));
|
|
assert_eq!(env_value(&env, "AR"), Some("/opt/depot-tools/bin/llvm-ar"));
|
|
assert_eq!(
|
|
env_value(&env, "RANLIB"),
|
|
Some("/opt/depot-tools/bin/llvm-ranlib")
|
|
);
|
|
assert_eq!(env_value(&env, "LD"), Some("/opt/depot-tools/bin/ld.lld"));
|
|
assert_eq!(
|
|
env_value(&env, "CFLAGS"),
|
|
Some("--gcc-toolchain=/opt/depot-tools/bin")
|
|
);
|
|
assert_eq!(
|
|
env_value(&env, "LDFLAGS"),
|
|
Some("-B/opt/depot-tools/bin -Wl,--as-needed")
|
|
);
|
|
assert_eq!(
|
|
env_value(&env, "LLVM_CONFIG"),
|
|
Some("/opt/depot-tools/bin/llvm-config")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_cross_uses_package_tool_overrides() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.cc = "/tools/bin/clang".to_string();
|
|
spec.build.flags.cxx = "/tools/bin/clang++".to_string();
|
|
spec.build.flags.ar = "/tools/bin/llvm-ar".to_string();
|
|
spec.build.flags.ranlib = "/tools/bin/llvm-ranlib".to_string();
|
|
spec.build.flags.strip = "/tools/bin/llvm-strip".to_string();
|
|
spec.build.flags.ld = "/tools/bin/ld.lld".to_string();
|
|
spec.build.flags.nm = "/tools/bin/llvm-nm".to_string();
|
|
spec.build.flags.objcopy = "/tools/bin/llvm-objcopy".to_string();
|
|
spec.build.flags.objdump = "/tools/bin/llvm-objdump".to_string();
|
|
spec.build.flags.readelf = "/tools/bin/llvm-readelf".to_string();
|
|
spec.build.flags.cpp = "/tools/bin/clang-cpp".to_string();
|
|
let cross = CrossConfig {
|
|
prefix: "x86_64-test-linux-gnu".into(),
|
|
cc: "x86_64-test-linux-gnu-gcc".into(),
|
|
cxx: "x86_64-test-linux-gnu-g++".into(),
|
|
ar: "x86_64-test-linux-gnu-ar".into(),
|
|
ranlib: "x86_64-test-linux-gnu-ranlib".into(),
|
|
strip: "x86_64-test-linux-gnu-strip".into(),
|
|
ld: "x86_64-test-linux-gnu-ld".into(),
|
|
nm: "x86_64-test-linux-gnu-nm".into(),
|
|
objcopy: "x86_64-test-linux-gnu-objcopy".into(),
|
|
objdump: "x86_64-test-linux-gnu-objdump".into(),
|
|
readelf: "x86_64-test-linux-gnu-readelf".into(),
|
|
};
|
|
|
|
let env = standard_build_env(&spec, Some(&cross), true, true);
|
|
|
|
assert_eq!(env_value(&env, "CC"), Some("/tools/bin/clang"));
|
|
assert_eq!(env_value(&env, "CXX"), Some("/tools/bin/clang++"));
|
|
assert_eq!(env_value(&env, "AR"), Some("/tools/bin/llvm-ar"));
|
|
assert_eq!(env_value(&env, "RANLIB"), Some("/tools/bin/llvm-ranlib"));
|
|
assert_eq!(env_value(&env, "STRIP"), Some("/tools/bin/llvm-strip"));
|
|
assert_eq!(env_value(&env, "LD"), Some("/tools/bin/ld.lld"));
|
|
assert_eq!(env_value(&env, "NM"), Some("/tools/bin/llvm-nm"));
|
|
assert_eq!(env_value(&env, "OBJCOPY"), Some("/tools/bin/llvm-objcopy"));
|
|
assert_eq!(env_value(&env, "OBJDUMP"), Some("/tools/bin/llvm-objdump"));
|
|
assert_eq!(env_value(&env, "READELF"), Some("/tools/bin/llvm-readelf"));
|
|
assert_eq!(env_value(&env, "CPP"), Some("/tools/bin/clang-cpp"));
|
|
assert_eq!(
|
|
env_value(&env, "CROSS_PREFIX"),
|
|
Some("x86_64-test-linux-gnu")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_effective_carch_for_cross_and_lib32() {
|
|
let spec = mk_spec(Vec::new(), Vec::new());
|
|
let cross = CrossConfig {
|
|
prefix: "aarch64-linux-gnu".into(),
|
|
cc: "aarch64-linux-gnu-gcc".into(),
|
|
cxx: "aarch64-linux-gnu-g++".into(),
|
|
ar: "aarch64-linux-gnu-ar".into(),
|
|
ranlib: "aarch64-linux-gnu-ranlib".into(),
|
|
strip: "aarch64-linux-gnu-strip".into(),
|
|
ld: "aarch64-linux-gnu-ld".into(),
|
|
nm: "aarch64-linux-gnu-nm".into(),
|
|
objcopy: "aarch64-linux-gnu-objcopy".into(),
|
|
objdump: "aarch64-linux-gnu-objdump".into(),
|
|
readelf: "aarch64-linux-gnu-readelf".into(),
|
|
};
|
|
|
|
let cross_env = standard_build_env(&spec, Some(&cross), true, true);
|
|
assert!(
|
|
cross_env
|
|
.iter()
|
|
.any(|(k, v)| k == "CARCH" && v == "aarch64"),
|
|
"expected cross builds to export target CARCH"
|
|
);
|
|
assert!(
|
|
cross_env
|
|
.iter()
|
|
.any(|(k, v)| k == "OBJCOPY" && v == "aarch64-linux-gnu-objcopy"),
|
|
"expected cross builds to export OBJCOPY"
|
|
);
|
|
assert!(
|
|
cross_env
|
|
.iter()
|
|
.any(|(k, v)| k == "OBJDUMP" && v == "aarch64-linux-gnu-objdump"),
|
|
"expected cross builds to export OBJDUMP"
|
|
);
|
|
assert!(
|
|
cross_env
|
|
.iter()
|
|
.any(|(k, v)| k == "READELF" && v == "aarch64-linux-gnu-readelf"),
|
|
"expected cross builds to export READELF"
|
|
);
|
|
|
|
let mut lib32_spec = spec.clone();
|
|
lib32_spec.build.flags.lib32_variant = true;
|
|
lib32_spec.build.flags.carch = "x86_64".into();
|
|
let lib32_env = standard_build_env(&lib32_spec, None, true, true);
|
|
assert!(
|
|
lib32_env.iter().any(|(k, v)| k == "CARCH" && v == "i686"),
|
|
"expected lib32 builds to export i686 CARCH"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_spec_with_target_configure_appends_matching_arch_args() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.configure = vec!["--base".to_string()];
|
|
spec.build
|
|
.flags
|
|
.configure_arch
|
|
.insert("aarch64".to_string(), vec!["--for-aarch64".to_string()]);
|
|
spec.build
|
|
.flags
|
|
.configure_arch
|
|
.insert("x86_64".to_string(), vec!["--for-x86".to_string()]);
|
|
let cross = CrossConfig {
|
|
prefix: "aarch64-linux-gnu".into(),
|
|
cc: "aarch64-linux-gnu-gcc".into(),
|
|
cxx: "aarch64-linux-gnu-g++".into(),
|
|
ar: "aarch64-linux-gnu-ar".into(),
|
|
ranlib: "aarch64-linux-gnu-ranlib".into(),
|
|
strip: "aarch64-linux-gnu-strip".into(),
|
|
ld: "aarch64-linux-gnu-ld".into(),
|
|
nm: "aarch64-linux-gnu-nm".into(),
|
|
objcopy: "aarch64-linux-gnu-objcopy".into(),
|
|
objdump: "aarch64-linux-gnu-objdump".into(),
|
|
readelf: "aarch64-linux-gnu-readelf".into(),
|
|
};
|
|
|
|
let adjusted = spec_with_target_configure(&spec, Some(&cross), TargetBuildKind::Primary)
|
|
.expect("expected arch-specific configure args");
|
|
|
|
assert_eq!(
|
|
adjusted.build.flags.configure,
|
|
vec!["--base".to_string(), "--for-aarch64".to_string()]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_spec_with_target_configure_uses_lib32_arch() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.lib32_variant = true;
|
|
spec.build.flags.carch = "x86_64".to_string();
|
|
spec.build
|
|
.flags
|
|
.configure_arch
|
|
.insert("i686".to_string(), vec!["--for-lib32".to_string()]);
|
|
|
|
let adjusted = spec_with_target_configure(&spec, None, TargetBuildKind::Lib32)
|
|
.expect("expected lib32 configure args");
|
|
|
|
assert_eq!(
|
|
adjusted.build.flags.configure,
|
|
vec!["--for-lib32".to_string()]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_respects_export_compiler_flags_toggle() {
|
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
|
spec.build.flags.cxxflags = vec!["-O2".into(), "-fno-exceptions".into()];
|
|
|
|
let enabled = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
enabled.iter().any(|(k, v)| k == "CFLAGS" && v == "-O2"),
|
|
"expected CFLAGS to be exported when enabled"
|
|
);
|
|
assert!(
|
|
enabled
|
|
.iter()
|
|
.any(|(k, v)| k == "CXXFLAGS" && v == "-O2 -fno-exceptions"),
|
|
"expected CXXFLAGS to be exported when enabled"
|
|
);
|
|
assert!(
|
|
enabled
|
|
.iter()
|
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,--as-needed"),
|
|
"expected LDFLAGS to be exported when enabled"
|
|
);
|
|
|
|
let disabled = standard_build_env(&spec, None, true, false);
|
|
assert!(
|
|
!disabled.iter().any(|(k, _)| k == "CFLAGS"),
|
|
"expected CFLAGS to be omitted when disabled"
|
|
);
|
|
assert!(
|
|
!disabled.iter().any(|(k, _)| k == "CXXFLAGS"),
|
|
"expected CXXFLAGS to be omitted when disabled"
|
|
);
|
|
assert!(
|
|
!disabled.iter().any(|(k, _)| k == "LDFLAGS"),
|
|
"expected LDFLAGS to be omitted when disabled"
|
|
);
|
|
|
|
let mut disabled_by_spec = spec.clone();
|
|
disabled_by_spec.build.flags.no_flags = true;
|
|
let disabled_env = standard_build_env(&disabled_by_spec, None, true, true);
|
|
assert!(
|
|
!disabled_env.iter().any(|(k, _)| k == "CFLAGS"),
|
|
"expected CFLAGS to be omitted when no_flags is set in spec"
|
|
);
|
|
assert!(
|
|
!disabled_env.iter().any(|(k, _)| k == "CXXFLAGS"),
|
|
"expected CXXFLAGS to be omitted when no_flags is set in spec"
|
|
);
|
|
assert!(
|
|
!disabled_env.iter().any(|(k, _)| k == "LDFLAGS"),
|
|
"expected LDFLAGS to be omitted when no_flags is set in spec"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_injects_ltoflags_into_compiler_and_linker_flags() {
|
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
|
spec.build.flags.cxxflags = vec!["-O2".into()];
|
|
spec.build.flags.ltoflags = vec!["-flto=auto".into(), "-fuse-linker-plugin".into()];
|
|
spec.build.flags.use_lto = true;
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| { k == "CFLAGS" && v == "-O2 -flto=auto -fuse-linker-plugin" }),
|
|
"expected LTOFLAGS to be appended to CFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "CXXFLAGS" && v == "-O2 -flto=auto -fuse-linker-plugin"),
|
|
"expected LTOFLAGS to be appended to CXXFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter().any(|(k, v)| {
|
|
k == "LDFLAGS" && v == "-Wl,--as-needed -flto=auto -fuse-linker-plugin"
|
|
}),
|
|
"expected LTOFLAGS to be appended to LDFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=auto -fuse-linker-plugin"),
|
|
"expected LTOFLAGS variable to be exported"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_injects_fuse_ld_into_ldflags() {
|
|
let mut spec = mk_spec(Vec::new(), vec!["-Wl,--as-needed"]);
|
|
spec.build.flags.fuse_ld = "/usr/bin/ld.lld".into();
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
|
|
assert_eq!(
|
|
env_value(&env, "LDFLAGS"),
|
|
Some("-fuse-ld=/usr/bin/ld.lld -Wl,--as-needed")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_normalizes_fuse_ld_tool_names() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.fuse_ld = "ld.lld".into();
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
|
|
assert_eq!(env_value(&env, "LDFLAGS"), Some("-fuse-ld=lld"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_applies_replace_flag_rules() {
|
|
let mut spec = mk_spec(vec!["-D_FORTIFY_SOURCE=3", "-O2"], vec!["-Wl,-O3"]);
|
|
spec.build.flags.cxxflags = vec!["-O2".into(), "-stdlib=libc++".into()];
|
|
spec.build.flags.replace_cflags = vec!["_FORTIFY_SOURCE=3=_FORTIFY_SOURCE=2".into()];
|
|
spec.build.flags.replace_cxxflags = vec!["-stdlib=libc++=>-stdlib=libstdc++".into()];
|
|
spec.build.flags.replace_ldflags = vec!["-O3=>-O2".into()];
|
|
spec.build.flags.ltoflags = vec!["-flto=auto".into()];
|
|
spec.build.flags.replace_ltoflags = vec!["auto=>thin".into()];
|
|
spec.build.flags.use_lto = true;
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "CFLAGS" && v == "-D_FORTIFY_SOURCE=2 -O2 -flto=thin"),
|
|
"expected replace_cflags and replace_ltoflags to be applied"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| { k == "CXXFLAGS" && v == "-O2 -stdlib=libstdc++ -flto=thin" }),
|
|
"expected replace_cxxflags to be applied"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,-O2 -flto=thin"),
|
|
"expected replace_ldflags to be applied"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=thin"),
|
|
"expected replace_ltoflags to affect exported LTOFLAGS"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_drops_bare_dash_flags() {
|
|
let mut spec = mk_spec(vec!["-O2", "-", ""], vec!["-Wl,--as-needed", " "]);
|
|
spec.build.flags.cxxflags = vec!["-O2".into(), "-".into(), "-fno-exceptions".into()];
|
|
spec.build.flags.ltoflags = vec!["-".into(), "-flto=thin".into()];
|
|
spec.build.flags.use_lto = true;
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "CFLAGS" && v == "-O2 -flto=thin"),
|
|
"expected bare dash entries to be removed from CFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "CXXFLAGS" && v == "-O2 -fno-exceptions -flto=thin"),
|
|
"expected bare dash entries to be removed from CXXFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,--as-needed -flto=thin"),
|
|
"expected blank and bare dash entries to be removed from LDFLAGS"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=thin"),
|
|
"expected bare dash entries to be removed from LTOFLAGS"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_skips_lto_injection_when_disabled() {
|
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
|
spec.build.flags.cxxflags = vec!["-O2".into()];
|
|
spec.build.flags.ltoflags = vec!["-flto=auto".into()];
|
|
spec.build.flags.rustltoflags = vec!["-Clinker-plugin-lto".into()];
|
|
spec.build.flags.use_lto = false;
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter().any(|(k, v)| k == "CFLAGS" && v == "-O2"),
|
|
"expected CFLAGS to remain unchanged when use_lto is false"
|
|
);
|
|
assert!(
|
|
env.iter().any(|(k, v)| k == "CXXFLAGS" && v == "-O2"),
|
|
"expected CXXFLAGS to remain unchanged when use_lto is false"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,--as-needed"),
|
|
"expected LDFLAGS to remain unchanged when use_lto is false"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=auto"),
|
|
"expected LTOFLAGS variable to be exported even when use_lto is false"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "RUSTLTOFLAGS" && v == "-Clinker-plugin-lto"),
|
|
"expected RUSTLTOFLAGS variable to be exported even when use_lto is false"
|
|
);
|
|
assert_eq!(effective_rustflags(&spec.build.flags), Vec::<String>::new());
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_makeflags() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.makeflags = "-j12 --output-sync=target".to_string();
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "MAKEFLAGS" && v == "-j12 --output-sync=target"),
|
|
"expected MAKEFLAGS to be exported from build flags"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_install_dir_vars() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.prefix = "/opt/vertex".into();
|
|
spec.build.flags.bindir = "/opt/vertex/bin".into();
|
|
spec.build.flags.sbindir = "/opt/vertex/sbin".into();
|
|
spec.build.flags.libdir = "/opt/vertex/lib".into();
|
|
spec.build.flags.libexecdir = "/opt/vertex/libexec".into();
|
|
spec.build.flags.sysconfdir = "/etc/vertex".into();
|
|
spec.build.flags.localstatedir = "/var".into();
|
|
spec.build.flags.sharedstatedir = "/var/lib".into();
|
|
spec.build.flags.includedir = "/opt/vertex/include".into();
|
|
spec.build.flags.datarootdir = "/opt/vertex/share".into();
|
|
spec.build.flags.datadir = "/opt/vertex/share/data".into();
|
|
spec.build.flags.mandir = "/opt/vertex/share/man".into();
|
|
spec.build.flags.infodir = "/opt/vertex/share/info".into();
|
|
|
|
let env = standard_build_env(&spec, None, false, true);
|
|
|
|
assert_eq!(env_value(&env, "PREFIX"), Some("/opt/vertex"));
|
|
assert_eq!(env_value(&env, "BINDIR"), Some("/opt/vertex/bin"));
|
|
assert_eq!(env_value(&env, "SBINDIR"), Some("/opt/vertex/sbin"));
|
|
assert_eq!(env_value(&env, "LIBDIR"), Some("/opt/vertex/lib"));
|
|
assert_eq!(env_value(&env, "LIBEXECDIR"), Some("/opt/vertex/libexec"));
|
|
assert_eq!(env_value(&env, "SYSCONFDIR"), Some("/etc/vertex"));
|
|
assert_eq!(env_value(&env, "LOCALSTATEDIR"), Some("/var"));
|
|
assert_eq!(env_value(&env, "SHAREDSTATEDIR"), Some("/var/lib"));
|
|
assert_eq!(env_value(&env, "INCLUDEDIR"), Some("/opt/vertex/include"));
|
|
assert_eq!(env_value(&env, "DATAROOTDIR"), Some("/opt/vertex/share"));
|
|
assert_eq!(env_value(&env, "DATADIR"), Some("/opt/vertex/share/data"));
|
|
assert_eq!(env_value(&env, "MANDIR"), Some("/opt/vertex/share/man"));
|
|
assert_eq!(env_value(&env, "INFODIR"), Some("/opt/vertex/share/info"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_install_dir_vars_use_effective_defaults() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.lib32_variant = true;
|
|
|
|
let env = standard_build_env(&spec, None, false, true);
|
|
|
|
assert_eq!(env_value(&env, "LIBDIR"), Some("/usr/lib32"));
|
|
assert_eq!(env_value(&env, "LIBEXECDIR"), Some("/usr/lib32"));
|
|
assert_eq!(env_value(&env, "DATAROOTDIR"), Some("/usr/share"));
|
|
assert_eq!(env_value(&env, "DATADIR"), Some("/usr/share"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_passthrough_env() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.passthrough_env = vec!["RUSTFLAGS".into()];
|
|
|
|
let mut env = TestEnv::new();
|
|
env.set_var("RUSTFLAGS", "-C target-cpu=native");
|
|
|
|
let env = standard_build_env(&spec, None, false, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "RUSTFLAGS" && v == "-C target-cpu=native"),
|
|
"expected RUSTFLAGS to be copied from parent environment"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_exports_declared_env_vars() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.package.version = "2.4.1".to_string();
|
|
spec.spec_dir = PathBuf::from("/tmp/specs/demo");
|
|
spec.build.flags.env_vars = vec![
|
|
"SETUPTOOLS_SCM_PRETEND_VERSION=$version".into(),
|
|
"PYO3_CONFIG_FILE=$specdir/pyo3.toml".into(),
|
|
"PKG_CONFIG_PATH=$LIBDIR/pkgconfig".into(),
|
|
];
|
|
|
|
let env = standard_build_env(&spec, None, false, true);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "SETUPTOOLS_SCM_PRETEND_VERSION" && v == "2.4.1"),
|
|
"expected env_vars values to expand package variables"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "PYO3_CONFIG_FILE" && v == "/tmp/specs/demo/pyo3.toml"),
|
|
"expected env_vars values to expand specdir variables"
|
|
);
|
|
assert!(
|
|
env.iter()
|
|
.any(|(k, v)| k == "PKG_CONFIG_PATH" && v == "/usr/lib/pkgconfig"),
|
|
"expected env_vars values to expand install directory variables"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_declared_env_vars_override_defaults_and_passthrough() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.cc = "spec-cc".to_string();
|
|
spec.build.flags.passthrough_env = vec!["CC".into()];
|
|
spec.build.flags.env_vars = vec!["CC=custom-cc".into()];
|
|
|
|
let mut env = TestEnv::new();
|
|
env.set_var("CC", "host-cc");
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter().any(|(k, v)| k == "CC" && v == "custom-cc"),
|
|
"expected explicit env_vars assignments to override default and passthrough values"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_effective_rustflags_applies_replace_rules() {
|
|
let flags = BuildFlags {
|
|
rustflags: vec!["-C".into(), "debuginfo=2".into()],
|
|
replace_rustflags: vec!["debuginfo=2=>opt-level=2".into()],
|
|
..BuildFlags::default()
|
|
};
|
|
|
|
assert_eq!(effective_rustflags(&flags), vec!["-C", "opt-level=2"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_effective_rustflags_appends_rustltoflags_when_enabled() {
|
|
let flags = BuildFlags {
|
|
rustflags: vec!["-C".into(), "opt-level=3".into()],
|
|
rustltoflags: vec!["-Clinker-plugin-lto".into(), "-Cembed-bitcode=yes".into()],
|
|
use_lto: true,
|
|
..BuildFlags::default()
|
|
};
|
|
|
|
assert_eq!(
|
|
effective_rustflags(&flags),
|
|
vec![
|
|
"-C",
|
|
"opt-level=3",
|
|
"-Clinker-plugin-lto",
|
|
"-Cembed-bitcode=yes"
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_standard_build_env_passthrough_does_not_override_default_vars() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.cc = "spec-cc".to_string();
|
|
spec.build.flags.passthrough_env = vec!["CC".into()];
|
|
|
|
let mut env = TestEnv::new();
|
|
env.set_var("CC", "host-cc");
|
|
|
|
let env = standard_build_env(&spec, None, true, true);
|
|
assert!(
|
|
env.iter().any(|(k, v)| k == "CC" && v == "spec-cc"),
|
|
"expected default CC to take precedence over passthrough CC"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_install_dirs_use_defaults_and_lib32_fallbacks() {
|
|
let default_dirs = install_dirs(&BuildFlags::default());
|
|
assert_eq!(default_dirs.bindir, "/usr/bin");
|
|
assert_eq!(default_dirs.sbindir, "/usr/bin");
|
|
assert_eq!(default_dirs.libdir, "/usr/lib");
|
|
assert_eq!(default_dirs.libexecdir, "/usr/lib");
|
|
assert_eq!(default_dirs.datarootdir, "/usr/share");
|
|
assert_eq!(default_dirs.datadir, "/usr/share");
|
|
|
|
let lib32_dirs = install_dirs(&BuildFlags {
|
|
lib32_variant: true,
|
|
..BuildFlags::default()
|
|
});
|
|
assert_eq!(lib32_dirs.libdir, "/usr/lib32");
|
|
assert_eq!(lib32_dirs.libexecdir, "/usr/lib32");
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_helper_context_restores_runtime_build_flags() {
|
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
|
spec.build.flags.lib32_variant = true;
|
|
spec.build.flags.host_build_dir = Some("/tmp/build-host".into());
|
|
|
|
let restored = BuildHelperContext::from_spec(&spec).build_flags();
|
|
assert!(restored.lib32_variant);
|
|
assert_eq!(restored.host_build_dir.as_deref(), Some("/tmp/build-host"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_install_dirs_respect_explicit_overrides_and_derived_defaults() {
|
|
let dirs = install_dirs(&BuildFlags {
|
|
bindir: "/opt/bin".into(),
|
|
libdir: "/opt/lib64".into(),
|
|
datarootdir: "/opt/share-root".into(),
|
|
..BuildFlags::default()
|
|
});
|
|
|
|
assert_eq!(dirs.bindir, "/opt/bin");
|
|
assert_eq!(dirs.libdir, "/opt/lib64");
|
|
assert_eq!(dirs.libexecdir, "/opt/lib64");
|
|
assert_eq!(dirs.datarootdir, "/opt/share-root");
|
|
assert_eq!(dirs.datadir, "/opt/share-root");
|
|
}
|
|
|
|
#[test]
|
|
fn test_install_destdir_path_uses_build_dir_for_lib32() {
|
|
let build_dir = Path::new("/tmp/build");
|
|
let destdir = Path::new("/tmp/pkg");
|
|
assert_eq!(install_destdir_path(build_dir, destdir, false), destdir);
|
|
assert_eq!(
|
|
install_destdir_path(build_dir, destdir, true),
|
|
build_dir.join("destdir")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stage_lib32_install_tree_uses_usr_lib32_when_present() -> Result<()> {
|
|
let temp = tempfile::tempdir()?;
|
|
let staging = temp.path().join("staging");
|
|
let dest = temp.path().join("dest");
|
|
fs::create_dir_all(staging.join("usr/lib32"))?;
|
|
fs::create_dir_all(staging.join("usr/bin"))?;
|
|
fs::write(staging.join("usr/lib32/libfoo.so.1"), "lib32")?;
|
|
fs::write(staging.join("usr/bin/foo"), "bin")?;
|
|
|
|
stage_lib32_install_tree(&staging, &dest)?;
|
|
|
|
assert_eq!(
|
|
fs::read_to_string(dest.join("usr/lib32/libfoo.so.1"))?,
|
|
"lib32"
|
|
);
|
|
assert!(!dest.join("usr/bin/foo").exists());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn test_stage_lib32_install_tree_relocates_usr_lib_when_needed() -> Result<()> {
|
|
use std::os::unix::fs as unix_fs;
|
|
|
|
let temp = tempfile::tempdir()?;
|
|
let staging = temp.path().join("staging");
|
|
let dest = temp.path().join("dest");
|
|
fs::create_dir_all(staging.join("usr/lib"))?;
|
|
fs::create_dir_all(staging.join("usr/share/man/man1"))?;
|
|
fs::write(staging.join("usr/lib/libfoo.so.1"), "relocated")?;
|
|
fs::write(staging.join("usr/share/man/man1/foo.1"), "manpage")?;
|
|
unix_fs::symlink("libfoo.so.1", staging.join("usr/lib/libfoo.so"))?;
|
|
|
|
stage_lib32_install_tree(&staging, &dest)?;
|
|
|
|
assert_eq!(
|
|
fs::read_to_string(dest.join("usr/lib32/libfoo.so.1"))?,
|
|
"relocated"
|
|
);
|
|
assert_eq!(
|
|
fs::read_link(dest.join("usr/lib32/libfoo.so"))?,
|
|
PathBuf::from("libfoo.so.1")
|
|
);
|
|
assert!(!dest.join("usr/share/man/man1/foo.1").exists());
|
|
assert!(!dest.join("usr/lib").exists());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_stage_lib32_install_tree_preserves_hardlinks() -> Result<()> {
|
|
use std::os::unix::fs::MetadataExt;
|
|
|
|
let temp = tempfile::tempdir()?;
|
|
let staging = temp.path().join("staging");
|
|
let dest = temp.path().join("dest");
|
|
fs::create_dir_all(staging.join("usr/lib32"))?;
|
|
fs::write(staging.join("usr/lib32/libfoo.so.1"), "lib32")?;
|
|
fs::hard_link(
|
|
staging.join("usr/lib32/libfoo.so.1"),
|
|
staging.join("usr/lib32/libfoo-current.so"),
|
|
)?;
|
|
|
|
stage_lib32_install_tree(&staging, &dest)?;
|
|
|
|
let first = dest.join("usr/lib32/libfoo.so.1").metadata()?;
|
|
let second = dest.join("usr/lib32/libfoo-current.so").metadata()?;
|
|
assert_eq!(first.ino(), second.ino());
|
|
assert_eq!(first.nlink(), 2);
|
|
assert_eq!(second.nlink(), 2);
|
|
Ok(())
|
|
}
|