#![allow(clippy::needless_doctest_main)]
use std::env;
use std::path::PathBuf;
use std::process::Command;
#[derive(Default)]
pub struct Build {
pub link_args: Vec<String>,
pub used_env_vars: Vec<String>,
pub used_files: Vec<String>,
}
pub fn get_link_args(mut is_wave64_enabled: bool, target_cpu: &str) -> Build {
let mut build = Build::default();
let cur_dir = env!("CARGO_MANIFEST_DIR");
let mut device_libs = None;
if let Ok(v) = env::var("HIP_DEVICE_LIB_PATH") {
build.used_env_vars.push("HIP_DEVICE_LIB_PATH".into());
device_libs = Some(v);
} else {
let mut hipconfig = Command::new("hipconfig");
hipconfig.arg("-l");
let hipconfig_r = hipconfig.output();
if let Ok(r) = &hipconfig_r {
if !r.status.success() {
panic!(
"`hipconfig -l` exited unsuccessfully, either fix this or set $HIP_DEVICE_LIB_PATH"
);
}
let s =
String::from_utf8(r.stdout.clone()).expect("`hipconfig -l` returned invalid utf-8");
let p = PathBuf::from(s)
.canonicalize()
.expect("Failed to canonicalize device libs path")
.parent()
.expect("Device libs path must have parent")
.join("lib")
.join("clang");
let mut folders = Vec::new();
if let Ok(dir) = std::fs::read_dir(&p) {
for d in dir {
let d = d.expect("Failed to list lib/clang directory content");
if d.file_type().expect("Failed to get file type").is_dir() {
folders.push(d.path());
}
}
}
folders.sort();
if let Some(last) = folders.last() {
device_libs = Some(format!("{}/amdgcn/bitcode", last.display()));
}
}
if device_libs.is_none()
&& let Ok(v) = env::var("ROCM_DEVICE_LIB_PATH").or_else(|_| env::var("ROCM_PATH"))
{
build.used_env_vars.push("ROCM_PATH".into());
build.used_env_vars.push("ROCM_DEVICE_LIB_PATH".into());
device_libs = Some(format!("{}/amdgcn/bitcode", v));
}
}
let device_libs = device_libs.expect("Device libs not found, must set $HIP_DEVICE_LIB_PATH or provide a path through `hipconfig -l`");
let gfxip = target_cpu
.strip_prefix("gfx")
.unwrap_or_else(|| panic!("target-cpu '{target_cpu}' did not start with gfx"));
build.link_args.push(format!("{device_libs}/ockl.bc"));
build
.link_args
.push(format!("{device_libs}/oclc_isa_version_{gfxip}.bc"));
build
.link_args
.push(format!("{device_libs}/oclc_abi_version_600.bc"));
is_wave64_enabled |= gfxip.starts_with('9') && gfxip.len() == 3;
is_wave64_enabled |= gfxip.starts_with("9-") && gfxip.ends_with("-generic");
let wavesize = if is_wave64_enabled { 64 } else { 32 };
build.link_args.push(format!(
"{device_libs}/oclc_wavefrontsize64_{}.bc",
if is_wave64_enabled { "on" } else { "off" }
));
build
.used_files
.push(format!("{cur_dir}/util{wavesize}.bc"));
build.link_args.push(format!("{cur_dir}/util{wavesize}.bc"));
build.link_args.push("--undefined-version".into());
build.link_args.push("--no-gc-sections".into());
build
}
#[cfg(feature = "rustflags")]
pub fn build() {
use std::collections::HashSet;
use rustflags::Flag;
let mut target_features = env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or_default()
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect::<HashSet<_>>();
let mut target_cpu = None;
for flag in rustflags::from_env() {
if let Flag::Codegen { opt, value } = flag {
if opt == "target-cpu" {
target_cpu = value;
} else if opt == "target-feature"
&& let Some(feat) = value
{
if let Some(feat) = feat.strip_prefix('-') {
target_features.remove(feat);
} else {
let feat = feat.trim_start_matches('+');
target_features.insert(feat.into());
}
}
}
}
let target_cpu = target_cpu.expect("Did not find target-cpu in RUSTFLAGS");
let is_wave64_enabled = target_features.contains("wavefrontsize64");
let mut build = get_link_args(is_wave64_enabled, &target_cpu);
build.used_env_vars.push("CARGO_CFG_TARGET_FEATURE".into());
for v in &build.used_env_vars {
println!("cargo::rerun-if-env-changed={v}");
}
for f in &build.used_files {
println!("cargo::rerun-if-changed={f}");
}
for a in &build.link_args {
println!("cargo::rustc-link-arg={a}");
}
}