|
| 1 | +//! This module deals with target specs, which are json metadata files that need to be passed to |
| 2 | +//! rustc to add foreign targets such as `spirv_unknown_vulkan1.2`. |
| 3 | +//! |
| 4 | +//! There are 4 version ranges of `rustc_codegen_spirv` and they all need different handling of |
| 5 | +//! their target specs: |
| 6 | +//! * "ancient" versions such as 0.9.0 or earlier do not need target specs, just passing the target |
| 7 | +//! string (`spirv-unknown-vulkan1.2`) directly is sufficient. We still prep target-specs for them |
| 8 | +//! like the "legacy" variant below, spirv-builder |
| 9 | +//! [will just ignore it](https://github.com/Rust-GPU/rust-gpu/blob/369122e1703c0c32d3d46f46fa11ccf12667af03/crates/spirv-builder/src/lib.rs#L987) |
| 10 | +//! * "legacy" versions require target specs to compile, which is a requirement introduced by some |
| 11 | +//! rustc version. Back then it was decided that cargo gpu would ship them, as they'd probably |
| 12 | +//! never change, right? So now we're stuck with having to ship these "legacy" target specs with |
| 13 | +//! cargo gpu *forever*. These are the symbol `legacy_target_specs::TARGET_SPECS`, with |
| 14 | +//! `legacy_target_specs` being a **fixed** version of `rustc_codegen_spirv-target-specs`, |
| 15 | +//! which must **never** update. |
| 16 | +//! * As of [PR 256](https://github.com/Rust-GPU/rust-gpu/pull/256), `rustc_codegen_spirv` now has |
| 17 | +//! a direct dependency on `rustc_codegen_spirv-target-specs`, allowing cargo gpu to pull the |
| 18 | +//! required target specs directly from that dependency. At this point, the target specs are |
| 19 | +//! still the same as the legacy target specs. |
| 20 | +//! * The [edition 2024 PR](https://github.com/Rust-GPU/rust-gpu/pull/249) must update the |
| 21 | +//! target specs to comply with newly added validation within rustc. This is why the new system |
| 22 | +//! was implemented, so we can support both old and new target specs without having to worry |
| 23 | +//! which version of cargo gpu you are using. It'll "just work". |
| 24 | +
|
| 25 | +use crate::cache_dir; |
| 26 | +use crate::spirv_source::{FindPackage as _, SpirvSource}; |
| 27 | +use anyhow::Context as _; |
| 28 | +use cargo_metadata::Metadata; |
| 29 | +use std::path::{Path, PathBuf}; |
| 30 | + |
| 31 | +/// Extract legacy target specs from our executable into some directory |
| 32 | +pub fn write_legacy_target_specs(target_spec_dir: &Path) -> anyhow::Result<()> { |
| 33 | + std::fs::create_dir_all(target_spec_dir)?; |
| 34 | + for (filename, contents) in legacy_target_specs::TARGET_SPECS { |
| 35 | + let path = target_spec_dir.join(filename); |
| 36 | + std::fs::write(&path, contents.as_bytes()) |
| 37 | + .with_context(|| format!("writing legacy target spec file at [{}]", path.display()))?; |
| 38 | + } |
| 39 | + Ok(()) |
| 40 | +} |
| 41 | + |
| 42 | +/// Copy spec files from one dir to another, assuming no subdirectories |
| 43 | +fn copy_spec_files(src: &Path, dst: &Path) -> anyhow::Result<()> { |
| 44 | + std::fs::create_dir_all(dst)?; |
| 45 | + let dir = std::fs::read_dir(src)?; |
| 46 | + for dir_entry in dir { |
| 47 | + let file = dir_entry?; |
| 48 | + let file_path = file.path(); |
| 49 | + if file_path.is_file() { |
| 50 | + std::fs::copy(file_path, dst.join(file.file_name()))?; |
| 51 | + } |
| 52 | + } |
| 53 | + Ok(()) |
| 54 | +} |
| 55 | + |
| 56 | +/// Computes the `target-specs` directory to use and updates the target spec files, if enabled. |
| 57 | +pub fn update_target_specs_files( |
| 58 | + source: &SpirvSource, |
| 59 | + dummy_metadata: &Metadata, |
| 60 | + update_files: bool, |
| 61 | +) -> anyhow::Result<PathBuf> { |
| 62 | + log::info!( |
| 63 | + "target-specs: Resolving target specs `{}`", |
| 64 | + if update_files { |
| 65 | + "and update them" |
| 66 | + } else { |
| 67 | + "without updating" |
| 68 | + } |
| 69 | + ); |
| 70 | + |
| 71 | + let mut target_specs_dst = source.install_dir()?.join("target-specs"); |
| 72 | + if let Ok(target_specs) = dummy_metadata.find_package("rustc_codegen_spirv-target-specs") { |
| 73 | + log::info!( |
| 74 | + "target-specs: found crate `rustc_codegen_spirv-target-specs` with manifest at `{}`", |
| 75 | + target_specs.manifest_path |
| 76 | + ); |
| 77 | + |
| 78 | + let target_specs_src = target_specs |
| 79 | + .manifest_path |
| 80 | + .as_std_path() |
| 81 | + .parent() |
| 82 | + .and_then(|root| { |
| 83 | + let src = root.join("target-specs"); |
| 84 | + src.is_dir().then_some(src) |
| 85 | + }) |
| 86 | + .context("Could not find `target-specs` directory within `rustc_codegen_spirv-target-specs` dependency")?; |
| 87 | + log::info!( |
| 88 | + "target-specs: found `rustc_codegen_spirv-target-specs` with `target-specs` directory `{}`", |
| 89 | + target_specs_dst.display() |
| 90 | + ); |
| 91 | + |
| 92 | + if source.is_path() { |
| 93 | + // skip copy |
| 94 | + log::info!( |
| 95 | + "target-specs resolution: source is local path, use target-specs directly from `{}`", |
| 96 | + target_specs_dst.display() |
| 97 | + ); |
| 98 | + target_specs_dst = target_specs_src; |
| 99 | + } else { |
| 100 | + // copy over the target-specs |
| 101 | + log::info!( |
| 102 | + "target-specs resolution: coping target-specs from `{}`{}", |
| 103 | + target_specs_dst.display(), |
| 104 | + if update_files { "" } else { " was skipped" } |
| 105 | + ); |
| 106 | + if update_files { |
| 107 | + copy_spec_files(&target_specs_src, &target_specs_dst) |
| 108 | + .context("copying target-specs json files")?; |
| 109 | + } |
| 110 | + } |
| 111 | + } else { |
| 112 | + // use legacy target specs bundled with cargo gpu |
| 113 | + if source.is_path() { |
| 114 | + // This is a stupid situation: |
| 115 | + // * We can't be certain that there are `target-specs` in the local checkout (there may be some in `spirv-builder`) |
| 116 | + // * We can't dump our legacy ones into the `install_dir`, as that would modify the local rust-gpu checkout |
| 117 | + // -> do what the old cargo gpu did, one global dir for all target specs |
| 118 | + // and hope parallel runs don't shred each other |
| 119 | + target_specs_dst = cache_dir()?.join("legacy-target-specs-for-local-checkout"); |
| 120 | + } |
| 121 | + log::info!( |
| 122 | + "target-specs resolution: legacy target specs in directory `{}`", |
| 123 | + target_specs_dst.display() |
| 124 | + ); |
| 125 | + if update_files { |
| 126 | + log::info!( |
| 127 | + "target-specs: Writing legacy target specs into `{}`", |
| 128 | + target_specs_dst.display() |
| 129 | + ); |
| 130 | + write_legacy_target_specs(&target_specs_dst)?; |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + Ok(target_specs_dst) |
| 135 | +} |
0 commit comments