Skip to content

Commit ddb039b

Browse files
committed
spirv-builder: fall back to hashed codegen dylibs
spirv-builder only looks for the exact librustc_codegen_spirv.{so,dylib,dll} filename in the dylib search path. That works in the rust-gpu workspace, but it misses external build.rs consumers where Cargo only exposes hashed deps artifacts such as librustc_codegen_spirv-<hash>.so. This showed up in a downstream workspace update to current rust-gpu main where the consumer path only had hashed backend artifacts available to default SpirvBuilder discovery. Keep preferring the exact filename when present, then fall back to the newest hashed rustc_codegen_spirv dylib in the same search paths. Add tests for exact matches, hashed fallback, and exact-over-hashed precedence.
1 parent be4c9b7 commit ddb039b

File tree

2 files changed

+148
-11
lines changed

2 files changed

+148
-11
lines changed

crates/spirv-builder/src/lib.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ use serde::Deserialize;
8383
use std::borrow::Borrow;
8484
use std::collections::HashMap;
8585
use std::env;
86+
use std::ffi::OsStr;
8687
use std::fs::File;
8788
use std::io::BufReader;
8889
use std::path::{Path, PathBuf};
8990
use std::process::Stdio;
91+
use std::time::SystemTime;
9092
use thiserror::Error;
9193

9294
#[cfg(feature = "watch")]
@@ -805,20 +807,71 @@ fn dylib_path() -> Vec<PathBuf> {
805807
dylibs
806808
}
807809

810+
fn rustc_codegen_spirv_dylib_name() -> String {
811+
format!(
812+
"{}rustc_codegen_spirv{}",
813+
env::consts::DLL_PREFIX,
814+
env::consts::DLL_SUFFIX
815+
)
816+
}
817+
818+
fn find_latest_hashed_rustc_codegen_spirv_in_dir(dir: &Path) -> Option<PathBuf> {
819+
let prefix = format!("{}rustc_codegen_spirv-", env::consts::DLL_PREFIX);
820+
let suffix = env::consts::DLL_SUFFIX;
821+
let mut best_match: Option<(SystemTime, PathBuf)> = None;
822+
823+
for entry in std::fs::read_dir(dir).ok()?.flatten() {
824+
let path = entry.path();
825+
if !path.is_file() {
826+
continue;
827+
}
828+
829+
let Some(name) = path.file_name().and_then(OsStr::to_str) else {
830+
continue;
831+
};
832+
if !name.starts_with(&prefix) || !name.ends_with(suffix) {
833+
continue;
834+
}
835+
836+
let modified = entry
837+
.metadata()
838+
.ok()
839+
.and_then(|metadata| metadata.modified().ok())
840+
.unwrap_or(SystemTime::UNIX_EPOCH);
841+
match &mut best_match {
842+
Some((best_modified, best_path)) if modified < *best_modified => {}
843+
Some((best_modified, best_path)) => {
844+
*best_modified = modified;
845+
*best_path = path;
846+
}
847+
None => best_match = Some((modified, path)),
848+
}
849+
}
850+
851+
best_match.map(|(_, path)| path)
852+
}
853+
854+
fn find_rustc_codegen_spirv_in_paths(dylib_paths: Vec<PathBuf>) -> Option<PathBuf> {
855+
let exact_name = rustc_codegen_spirv_dylib_name();
856+
857+
for dir in &dylib_paths {
858+
let path = dir.join(&exact_name);
859+
if path.is_file() {
860+
return Some(path);
861+
}
862+
}
863+
864+
dylib_paths
865+
.into_iter()
866+
.find_map(|dir| find_latest_hashed_rustc_codegen_spirv_in_dir(&dir))
867+
}
868+
808869
fn find_rustc_codegen_spirv() -> Result<PathBuf, SpirvBuilderError> {
809870
if cfg!(feature = "rustc_codegen_spirv") {
810-
let filename = format!(
811-
"{}rustc_codegen_spirv{}",
812-
env::consts::DLL_PREFIX,
813-
env::consts::DLL_SUFFIX
814-
);
815-
let dylib_paths = dylib_path();
816-
for mut path in dylib_paths {
817-
path.push(&filename);
818-
if path.is_file() {
819-
return Ok(path);
820-
}
871+
if let Some(path) = find_rustc_codegen_spirv_in_paths(dylib_path()) {
872+
return Ok(path);
821873
}
874+
let filename = rustc_codegen_spirv_dylib_name();
822875
panic!("Could not find {filename} in library path");
823876
} else {
824877
Err(SpirvBuilderError::MissingRustcCodegenSpirvDylib)

crates/spirv-builder/src/tests.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,87 @@ mod clap {
1818
};
1919
}
2020
}
21+
22+
mod dylib_lookup {
23+
use crate::{find_rustc_codegen_spirv_in_paths, rustc_codegen_spirv_dylib_name};
24+
use std::fs;
25+
use std::path::{Path, PathBuf};
26+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
27+
28+
struct TempDir(PathBuf);
29+
30+
impl TempDir {
31+
fn new(test_name: &str) -> Self {
32+
let unique = SystemTime::now()
33+
.duration_since(UNIX_EPOCH)
34+
.unwrap_or(Duration::ZERO)
35+
.as_nanos();
36+
let path = std::env::temp_dir().join(format!(
37+
"spirv-builder-{test_name}-{}-{unique}",
38+
std::process::id()
39+
));
40+
fs::create_dir_all(&path).unwrap();
41+
Self(path)
42+
}
43+
44+
fn path(&self) -> &Path {
45+
&self.0
46+
}
47+
}
48+
49+
impl Drop for TempDir {
50+
fn drop(&mut self) {
51+
let _ = fs::remove_dir_all(&self.0);
52+
}
53+
}
54+
55+
#[test]
56+
fn finds_exact_backend_dylib() {
57+
let dir = TempDir::new("exact");
58+
let exact = dir.path().join(rustc_codegen_spirv_dylib_name());
59+
fs::File::create(&exact).expect("failed to create exact backend dylib file");
60+
61+
assert_eq!(
62+
find_rustc_codegen_spirv_in_paths(vec![dir.path().to_path_buf()]),
63+
Some(exact)
64+
);
65+
}
66+
67+
#[test]
68+
fn falls_back_to_hashed_backend_dylib() {
69+
let dir = TempDir::new("hashed");
70+
let hashed = dir.path().join(format!(
71+
"{}rustc_codegen_spirv-deadbeef{}",
72+
std::env::consts::DLL_PREFIX,
73+
std::env::consts::DLL_SUFFIX
74+
));
75+
fs::File::create(&hashed).expect("failed to create hashed backend dylib file");
76+
77+
assert_eq!(
78+
find_rustc_codegen_spirv_in_paths(vec![dir.path().to_path_buf()]),
79+
Some(hashed)
80+
);
81+
}
82+
83+
#[test]
84+
fn prefers_exact_match_over_hashed_match_in_earlier_dir() {
85+
let hashed_dir = TempDir::new("hashed-first");
86+
let exact_dir = TempDir::new("exact-second");
87+
let hashed = hashed_dir.path().join(format!(
88+
"{}rustc_codegen_spirv-deadbeef{}",
89+
std::env::consts::DLL_PREFIX,
90+
std::env::consts::DLL_SUFFIX
91+
));
92+
let exact = exact_dir.path().join(rustc_codegen_spirv_dylib_name());
93+
fs::File::create(&hashed).expect("failed to create hashed backend dylib file");
94+
fs::File::create(&exact).expect("failed to create exact backend dylib file");
95+
96+
assert_eq!(
97+
find_rustc_codegen_spirv_in_paths(vec![
98+
hashed_dir.path().to_path_buf(),
99+
exact_dir.path().to_path_buf()
100+
]),
101+
Some(exact)
102+
);
103+
}
104+
}

0 commit comments

Comments
 (0)