use std::fs;
use ostool::build::config::LogLevel;
use super::{
features::{
c_compiler_features, c_config_features, c_defines, dynamic_pie_for_c_app,
map_c_app_features,
},
flags::{CFlagsInput, cflags, pthread_mutex_header_contents},
libc::{PIC_RUSTFLAG, append_pic_rustflag},
link::{find_final_linker_script, find_link_scripts, find_linker_search_dirs},
};
use crate::build::ARCEOS_LINKER_SCRIPT;
fn strings(items: &[&str]) -> Vec<String> {
items.iter().map(|item| item.to_string()).collect()
}
#[test]
fn c_config_features_skips_nested_cargo_only_features() {
let features = c_config_features(&strings(&[
"ax-libc/net",
"ax-feat/paging",
"ax-driver/plat-static",
"ax-driver/virtio-net",
"ax-hal/loongarch64-qemu-virt",
"some-crate/feature",
]));
assert_eq!(
features.into_iter().collect::<Vec<_>>(),
vec!["net".to_string(), "paging".to_string()]
);
}
#[test]
fn c_config_features_treats_dynamic_platform_as_smp() {
let features = c_config_features(&strings(&["plat-dyn", "multitask"]));
assert!(features.contains("smp"));
assert!(features.contains("multitask"));
}
#[test]
fn c_config_features_skips_case_define_features() {
let features = c_config_features(&strings(&["alloc", "c-define:ARCEOS_C_TEST_CASE_MEM"]));
assert_eq!(
features.into_iter().collect::<Vec<_>>(),
vec!["alloc".to_string()]
);
}
#[test]
fn c_defines_extracts_case_define_features() {
let defines = c_defines(&strings(&[
"alloc",
"c-define:ARCEOS_C_TEST_CASE_MEM",
"c-define:ARCEOS_C_TEST_CASE_NET_HTTP",
]));
assert_eq!(
defines.into_iter().collect::<Vec<_>>(),
vec![
"ARCEOS_C_TEST_CASE_MEM".to_string(),
"ARCEOS_C_TEST_CASE_NET_HTTP".to_string()
]
);
}
#[test]
fn c_compiler_features_keep_case_defines_for_cflags() {
let features = c_compiler_features(
&strings(&["alloc"]),
&strings(&["c-define:ARCEOS_C_TEST_CASE_MEM"]),
);
let flags = cflags(CFlagsInput {
workspace_root: std::path::Path::new("/workspace"),
arch: "x86_64",
mode: "release",
generated_include_dir: std::path::Path::new("/generated"),
include_dir: std::path::Path::new("/include"),
features: &features,
log: Some(LogLevel::Info),
dynamic_pie: false,
});
assert!(flags.contains(&"-DAX_CONFIG_ALLOC".to_string()));
assert!(flags.contains(&"-DARCEOS_C_TEST_CASE_MEM=1".to_string()));
}
#[test]
fn map_c_app_features_preserves_driver_features() {
let features = map_c_app_features(
&strings(&["net", "ax-driver/plat-static", "ax-driver/virtio-net"]),
&strings(&["ax-hal/loongarch64-qemu-virt"]),
);
assert!(features.contains(&"net".to_string()));
assert!(features.contains(&"fd".to_string()));
assert!(features.contains(&"ax-driver/plat-static".to_string()));
assert!(features.contains(&"ax-driver/virtio-net".to_string()));
assert!(features.contains(&"ax-hal/loongarch64-qemu-virt".to_string()));
}
#[test]
fn map_c_app_features_does_not_forward_case_define_features_to_cargo() {
let features = map_c_app_features(&strings(&["alloc", "c-define:ARCEOS_C_TEST_CASE_MEM"]), &[]);
assert_eq!(features, vec!["alloc".to_string()]);
}
#[test]
fn map_c_app_features_maps_dynamic_platform_for_axlibc() {
let features = map_c_app_features(&strings(&["alloc"]), &strings(&["plat-dyn"]));
assert!(features.contains(&"plat-dyn".to_string()));
assert!(features.contains(&"alloc".to_string()));
assert!(features.contains(&"smp".to_string()));
}
#[test]
fn dynamic_c_apps_use_pie_for_every_dynamic_platform() {
assert!(dynamic_pie_for_c_app(&strings(&["plat-dyn"])));
assert!(dynamic_pie_for_c_app(&strings(&["ax-std/plat-dyn"])));
assert!(!dynamic_pie_for_c_app(&strings(&["smp"])));
}
#[test]
fn pic_rustflag_is_appended_to_axlibc_cargo_env() {
let mut env = std::collections::HashMap::new();
append_pic_rustflag(&mut env);
assert_eq!(
env.get("CARGO_ENCODED_RUSTFLAGS"),
Some(&PIC_RUSTFLAG.to_string())
);
let mut env = std::collections::HashMap::from([(
"CARGO_ENCODED_RUSTFLAGS".to_string(),
"-Cforce-frame-pointers=yes".to_string(),
)]);
append_pic_rustflag(&mut env);
assert_eq!(
env.get("CARGO_ENCODED_RUSTFLAGS"),
Some(&format!("-Cforce-frame-pointers=yes\x1f{PIC_RUSTFLAG}"))
);
let mut env = std::collections::HashMap::from([(
"RUSTFLAGS".to_string(),
"-Cforce-frame-pointers=yes".to_string(),
)]);
append_pic_rustflag(&mut env);
assert_eq!(
env.get("RUSTFLAGS"),
Some(&format!("-Cforce-frame-pointers=yes {PIC_RUSTFLAG}"))
);
}
#[test]
fn map_c_app_features_forwards_multitask_to_runtime_features() {
let features = map_c_app_features(&strings(&["multitask"]), &[]);
assert!(features.contains(&"multitask".to_string()));
}
#[test]
fn pthread_mutex_header_matches_lockdep_smp_layout() {
let header = pthread_mutex_header_contents(&strings(&["multitask", "lockdep", "smp"]));
assert!(header.contains("long __l[10];"));
assert!(header.contains("{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0}"));
}
#[test]
fn pthread_mutex_header_matches_plain_smp_layout() {
let header = pthread_mutex_header_contents(&strings(&["multitask", "smp"]));
assert!(header.contains("long __l[6];"));
assert!(header.contains("{0, 0, 8, 0, 0, 0}"));
}
#[test]
fn pthread_mutex_header_matches_dynamic_platform_smp_layout() {
let header = pthread_mutex_header_contents(&strings(&["multitask", "plat-dyn"]));
assert!(header.contains("long __l[6];"));
assert!(header.contains("{0, 0, 8, 0, 0, 0}"));
}
#[test]
fn final_linker_script_comes_from_axruntime_build_out_dir() {
let root = tempfile::tempdir().unwrap();
let target_dir = root.path().join("target");
let target = "x86_64-unknown-none";
let mode = "release";
let stable_dir = target_dir.join(target).join(mode);
let out_dir = stable_dir.join("build/ax-runtime-abc/out");
fs::create_dir_all(&out_dir).unwrap();
fs::create_dir_all(&stable_dir).unwrap();
fs::write(stable_dir.join(ARCEOS_LINKER_SCRIPT), "stable").unwrap();
fs::write(out_dir.join(ARCEOS_LINKER_SCRIPT), "runtime").unwrap();
let linker = find_final_linker_script(&target_dir, target, mode).unwrap();
assert_eq!(linker, out_dir.join(ARCEOS_LINKER_SCRIPT));
}
#[test]
fn linker_search_dirs_use_current_platform_script_owner() {
let root = tempfile::tempdir().unwrap();
let target_dir = root.path().join("target");
let target = "x86_64-unknown-none";
let mode = "release";
let build_dir = target_dir.join(target).join(mode).join("build");
let axhal_out = build_dir.join("ax-hal-abc/out");
let loong_out = build_dir.join("ax-plat-loongarch64-qemu-virt-abc/out");
let stale_loong_out = build_dir.join("ax-plat-loongarch64-qemu-virt-old/out");
let runtime_out = build_dir.join("ax-runtime-def/out");
let unrelated_out = build_dir.join("unrelated-ghi/out");
fs::create_dir_all(&axhal_out).unwrap();
fs::create_dir_all(&loong_out).unwrap();
fs::create_dir_all(&stale_loong_out).unwrap();
fs::create_dir_all(&runtime_out).unwrap();
fs::create_dir_all(&unrelated_out).unwrap();
fs::write(axhal_out.join("axplat.x"), "").unwrap();
fs::write(loong_out.join("axplat.x"), "").unwrap();
fs::write(stale_loong_out.join("axplat.x"), "").unwrap();
fs::write(runtime_out.join(ARCEOS_LINKER_SCRIPT), "").unwrap();
fs::write(unrelated_out.join("note.txt"), "").unwrap();
let dirs = find_linker_search_dirs(
&target_dir,
target,
mode,
"loongarch64-qemu-virt",
&strings(&["ax-hal/loongarch64-qemu-virt"]),
)
.unwrap();
assert_eq!(dirs, vec![loong_out, runtime_out]);
}
#[test]
fn linker_search_dirs_use_axhal_for_generic_static_platforms() {
let root = tempfile::tempdir().unwrap();
let target_dir = root.path().join("target");
let target = "riscv64gc-unknown-none-elf";
let mode = "release";
let build_dir = target_dir.join(target).join(mode).join("build");
let axhal_out = build_dir.join("ax-hal-abc/out");
let runtime_out = build_dir.join("ax-runtime-def/out");
fs::create_dir_all(&axhal_out).unwrap();
fs::create_dir_all(&runtime_out).unwrap();
fs::write(axhal_out.join("axplat.x"), "").unwrap();
fs::write(runtime_out.join(ARCEOS_LINKER_SCRIPT), "").unwrap();
let dirs = find_linker_search_dirs(
&target_dir,
target,
mode,
"riscv64-sg2002",
&strings(&["ax-hal/riscv64-sg2002"]),
)
.unwrap();
assert_eq!(dirs, vec![axhal_out, runtime_out]);
}
#[test]
fn dynamic_link_scripts_use_runtime_script_as_entrypoint() {
let root = tempfile::tempdir().unwrap();
let target_dir = root.path().join("target");
let target = "aarch64-unknown-none-softfloat";
let mode = "release";
let build_dir = target_dir.join(target).join(mode).join("build");
let runtime_out = build_dir.join("ax-runtime-abc/out");
let axplat_out = build_dir.join("axplat-dyn-def/out");
let somehal_out = build_dir.join("somehal-ghi/out");
let someboot_out = build_dir.join("someboot-jkl/out");
fs::create_dir_all(&runtime_out).unwrap();
fs::create_dir_all(&axplat_out).unwrap();
fs::create_dir_all(&somehal_out).unwrap();
fs::create_dir_all(&someboot_out).unwrap();
fs::write(runtime_out.join(ARCEOS_LINKER_SCRIPT), "").unwrap();
fs::write(axplat_out.join("axplat.x"), "").unwrap();
fs::write(somehal_out.join("link.x"), "").unwrap();
fs::write(someboot_out.join("someboot.x"), "").unwrap();
let link_scripts = find_link_scripts(
&target_dir,
target,
mode,
"aarch64-generic",
&strings(&["plat-dyn"]),
)
.unwrap();
assert_eq!(link_scripts.script, runtime_out.join(ARCEOS_LINKER_SCRIPT));
assert!(link_scripts.pie);
assert!(link_scripts.search_dirs.contains(&runtime_out));
assert!(link_scripts.search_dirs.contains(&axplat_out));
assert!(link_scripts.search_dirs.contains(&somehal_out));
assert!(link_scripts.search_dirs.contains(&someboot_out));
}