44//! version. Then with that we `git checkout` the `rust-gpu` repo that corresponds to that version.
55//! From there we can look at the source code to get the required Rust toolchain.
66
7- use anyhow:: Context as _;
7+ use anyhow:: { anyhow, Context as _} ;
8+ use cargo_metadata:: semver:: Version ;
9+ use cargo_metadata:: { MetadataCommand , Package } ;
810use std:: path:: { Path , PathBuf } ;
911
1012/// The canonical `rust-gpu` URI
@@ -17,9 +19,7 @@ const RUST_GPU_REPO: &str = "https://github.com/Rust-GPU/rust-gpu";
1719pub enum SpirvSource {
1820 /// If the shader specifies a simple version like `spirv-std = "0.9.0"` then the source of
1921 /// `rust-gpu` is the conventional crates.io version.
20- ///
21- /// `String` is the simple version like, "0.9.0"
22- CratesIO ( String ) ,
22+ CratesIO ( Version ) ,
2323 /// If the shader specifies a version like:
2424 /// `spirv-std = { git = "https://github.com..." ... }`
2525 /// then the source of `rust-gpu` is `Git`.
@@ -32,7 +32,7 @@ pub enum SpirvSource {
3232 /// If the shader specifies a version like:
3333 /// `spirv-std = { path = "/path/to/rust-gpu" ... }`
3434 /// then the source of `rust-gpu` is `Path`.
35- Path ( ( String , String ) ) ,
35+ Path ( ( String , Version ) ) ,
3636}
3737
3838impl core:: fmt:: Display for SpirvSource {
@@ -42,7 +42,7 @@ impl core::fmt::Display for SpirvSource {
4242 ) ]
4343 fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
4444 match self {
45- Self :: CratesIO ( version) => f . write_str ( version ) ,
45+ Self :: CratesIO ( version) => version . fmt ( f ) ,
4646 Self :: Git { url, rev } => f. write_str ( & format ! ( "{url}+{rev}" ) ) ,
4747 Self :: Path ( ( a, b) ) => f. write_str ( & format ! ( "{a}+{b}" ) ) ,
4848 }
@@ -198,123 +198,72 @@ impl SpirvSource {
198198 let canonical_shader_path = Self :: shader_crate_path_canonical ( shader_crate_path) ?;
199199
200200 log:: debug!(
201- "Running `cargo tree ` on {}" ,
201+ "Running `cargo metadata ` on {}" ,
202202 canonical_shader_path. display( )
203203 ) ;
204- let output_cargo_tree = std:: process:: Command :: new ( "cargo" )
205- . current_dir ( canonical_shader_path. clone ( ) )
206- . args ( [ "tree" , "--workspace" , "--prefix" , "none" ] )
207- . output ( ) ?;
208- anyhow:: ensure!(
209- output_cargo_tree. status. success( ) ,
210- format!(
211- "could not query shader's `Cargo.toml` for `spirv-std` dependency: {}" ,
212- String :: from_utf8( output_cargo_tree. stderr) ?
213- )
214- ) ;
215- let cargo_tree_string = String :: from_utf8_lossy ( & output_cargo_tree. stdout ) ;
216-
217- let maybe_spirv_std_def = cargo_tree_string
218- . lines ( )
219- . find ( |line| line. contains ( "spirv-std" ) ) ;
220- log:: trace!( " found {maybe_spirv_std_def:?}" ) ;
221-
222- let Some ( spirv_std_def) = maybe_spirv_std_def else {
223- anyhow:: bail!( "`spirv-std` not found in shader's `Cargo.toml` at {canonical_shader_path:?}:\n {cargo_tree_string}" ) ;
204+ let metadata = MetadataCommand :: new ( )
205+ . current_dir ( & canonical_shader_path)
206+ . exec ( ) ?;
207+
208+ let Some ( spirv_std_package) = metadata
209+ . packages
210+ . iter ( )
211+ . find ( |package| package. name . eq ( "spirv-std" ) )
212+ else {
213+ anyhow:: bail!(
214+ "`spirv-std` not found in shader's `Cargo.toml` at {canonical_shader_path:?}"
215+ ) ;
224216 } ;
217+ log:: trace!( " found {spirv_std_package:?}" ) ;
225218
226- Self :: parse_spirv_std_source_and_version ( spirv_std_def )
219+ Ok ( Self :: parse_spirv_std_source_and_version ( spirv_std_package ) ? )
227220 }
228221
229222 /// Parse a string like:
230223 /// `spirv-std v0.9.0 (https://github.com/Rust-GPU/rust-gpu?rev=54f6978c#54f6978c) (*)`
231224 /// Which would return:
232225 /// `SpirvSource::Git("https://github.com/Rust-GPU/rust-gpu", "54f6978c")`
233- fn parse_spirv_std_source_and_version ( spirv_std_def : & str ) -> anyhow:: Result < Self > {
234- log:: trace!( "parsing spirv-std source and version from def: '{spirv_std_def}'" ) ;
235- let parts: Vec < String > = spirv_std_def. split_whitespace ( ) . map ( String :: from) . collect ( ) ;
236- let version = parts
237- . get ( 1 )
238- . context ( "Couldn't find `spirv_std` version in shader crate" ) ?
239- . to_owned ( ) ;
240- let mut source = Self :: CratesIO ( version. clone ( ) ) ;
241-
242- if parts. len ( ) > 2 {
243- let mut source_string = parts
244- . get ( 2 )
245- . context ( "Couldn't get Uri from dependency string" ) ?
246- . to_owned ( ) ;
247- source_string = source_string. replace ( [ '(' , ')' ] , "" ) ;
248-
249- // Unfortunately Uri ignores the fragment/hash portion of the Uri.
250- //
251- // There's been a ticket open for years:
252- // <https://github.com/hyperium/http/issues/127>
253- //
254- // So here we'll parse the fragment out of the source string by hand
255- let uri = source_string. parse :: < http:: Uri > ( ) ?;
256- let maybe_hash = if source_string. contains ( '#' ) {
257- let mut splits = source_string. split ( '#' ) ;
258- splits. next_back ( ) . map ( std:: borrow:: ToOwned :: to_owned)
259- } else {
260- None
261- } ;
262- if uri. scheme ( ) . is_some ( ) {
263- source = Self :: parse_git_source ( version, & uri, maybe_hash) ?;
264- } else {
265- source = Self :: Path ( ( source_string, version) ) ;
266- }
267- }
268-
269- log:: debug!( "Parsed `rust-gpu` source and version: {source:?}" ) ;
270-
271- Ok ( source)
272- }
273-
274- /// Parse a Git source like: `https://github.com/Rust-GPU/rust-gpu?rev=54f6978c#54f6978c`
275- fn parse_git_source (
276- version : String ,
277- uri : & http:: Uri ,
278- fragment : Option < String > ,
279- ) -> anyhow:: Result < Self > {
226+ fn parse_spirv_std_source_and_version ( spirv_std_package : & Package ) -> anyhow:: Result < Self > {
280227 log:: trace!(
281- "parsing git source from version: '{version}' and uri : '{uri}' and fragment: {} " ,
282- fragment . as_deref ( ) . unwrap_or ( "?" )
228+ "parsing spirv-std source and version from package : '{:?}' " ,
229+ spirv_std_package
283230 ) ;
284- let repo = format ! (
285- "{}://{}{}" ,
286- uri. scheme( ) . context( "Couldn't parse scheme from Uri" ) ?,
287- uri. host( ) . context( "Couldn't parse host from Uri" ) ?,
288- uri. path( )
289- ) ;
290-
291- let rev = Self :: parse_git_revision ( uri. query ( ) , fragment, version) ;
292231
293- Ok ( Self :: Git { url : repo, rev } )
294- }
295-
296- /// Decide the Git revision to use.
297- fn parse_git_revision (
298- maybe_query : Option < & str > ,
299- maybe_fragment : Option < String > ,
300- version : String ,
301- ) -> String {
302- let marker = "rev=" ;
303- let maybe_sane_query = maybe_query. and_then ( |query| {
304- // TODO: This might seem a little crude, but it saves adding a whole query parsing dependency.
305- let sanity_check = query. contains ( marker) && query. split ( '=' ) . count ( ) == 2 ;
306- sanity_check. then_some ( query)
307- } ) ;
308-
309- if let Some ( query) = maybe_sane_query {
310- return query. replace ( marker, "" ) ;
311- }
232+ let result = match & spirv_std_package. source {
233+ Some ( source) => {
234+ let is_git = source. repr . starts_with ( "git+" ) ;
235+ let is_crates_io = source. is_crates_io ( ) ;
236+
237+ match ( is_git, is_crates_io) {
238+ ( true , true ) => unreachable ! ( ) ,
239+ ( true , false ) => {
240+ let link = & source. repr [ 4 ..] ;
241+ let sharp_index = link. find ( '#' ) . ok_or ( anyhow ! (
242+ "Git url of spirv-std package does not contain revision!"
243+ ) ) ?;
244+ let question_mark_index = link. find ( '?' ) . ok_or ( anyhow ! (
245+ "Git url of spirv-std package does not contain revision!"
246+ ) ) ?;
247+ let url = link[ ..question_mark_index] . to_string ( ) ;
248+ let rev = link[ sharp_index + 1 ..] . to_string ( ) ;
249+ Self :: Git { url, rev }
250+ }
251+ ( false , true ) => Self :: CratesIO ( spirv_std_package. version . clone ( ) ) ,
252+ ( false , false ) => {
253+ anyhow:: bail!( "Metadata of spirv-std package uses unknown url format!" )
254+ }
255+ }
256+ }
257+ None => {
258+ let path = & spirv_std_package. manifest_path ;
259+ let version = & spirv_std_package. version ;
260+ Self :: Path ( ( path. to_string ( ) , version. clone ( ) ) )
261+ }
262+ } ;
312263
313- if let Some ( fragment) = maybe_fragment {
314- return fragment;
315- }
264+ log:: debug!( "Parsed `rust-gpu` source and version: {result:?}" ) ;
316265
317- version
266+ Ok ( result )
318267 }
319268
320269 /// `git clone` the `rust-gpu` repo. We use it to get the required Rust toolchain to compile
@@ -370,34 +319,7 @@ mod test {
370319 source,
371320 SpirvSource :: Git {
372321 url: "https://github.com/Rust-GPU/rust-gpu" . to_owned( ) ,
373- rev: "82a0f69" . to_owned( )
374- }
375- ) ;
376- }
377-
378- #[ test_log:: test]
379- fn parsing_spirv_std_dep_for_git_source ( ) {
380- let definition =
381- "spirv-std v9.9.9 (https://github.com/Rust-GPU/rust-gpu?rev=82a0f69#82a0f69) (*)" ;
382- let source = SpirvSource :: parse_spirv_std_source_and_version ( definition) . unwrap ( ) ;
383- assert_eq ! (
384- source,
385- SpirvSource :: Git {
386- url: "https://github.com/Rust-GPU/rust-gpu" . to_owned( ) ,
387- rev: "82a0f69" . to_owned( )
388- }
389- ) ;
390- }
391-
392- #[ test_log:: test]
393- fn parsing_spirv_std_dep_for_git_source_hash ( ) {
394- let definition = "spirv-std v9.9.9 (https://github.com/Rust-GPU/rust-gpu#82a0f69) (*)" ;
395- let source = SpirvSource :: parse_spirv_std_source_and_version ( definition) . unwrap ( ) ;
396- assert_eq ! (
397- source,
398- SpirvSource :: Git {
399- url: "https://github.com/Rust-GPU/rust-gpu" . to_owned( ) ,
400- rev: "82a0f69" . to_owned( )
322+ rev: "82a0f69008414f51d59184763146caa6850ac588" . to_owned( )
401323 }
402324 ) ;
403325 }
0 commit comments