Skip to content

Commit 8336cd0

Browse files
committed
target-spec: generate target specs on the fly in spirv-builder
1 parent 78ca2e7 commit 8336cd0

File tree

6 files changed

+95
-43
lines changed

6 files changed

+95
-43
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rustc_codegen_spirv-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ spirv = { version = "0.3.0", features = ["serialize", "deserialize"] }
1212
rspirv = "0.12"
1313
serde = { version = "1.0", features = ["derive"] }
1414
serde_json = "1.0"
15+
semver = { version = "1.0.24", features = ["serde"] }

crates/rustc_codegen_spirv-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
pub use rspirv::spirv::Capability;
44

55
mod compile_result;
6+
mod target_spec;
67
pub use compile_result::*;
8+
pub use target_spec::*;
79

810
// HACK(eddyb) allows downstream crates to access the correct version directly.
911
pub use serde;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use semver::Version;
2+
use std::borrow::Cow;
3+
use std::ffi::OsStr;
4+
use std::path::Path;
5+
6+
/// Enum for different versions of target specs, to allow changing the target spec for different rust versions.
7+
/// The version listed in the enum is always the minimum version to require said target spec, with the newest version
8+
/// always at the top.
9+
#[allow(non_camel_case_types)]
10+
#[derive(Copy, Clone, Debug)]
11+
pub enum TargetSpecVersion {
12+
/// rustc 1.76 has been tested to correctly parse modern target_spec jsons.
13+
/// Some later version requires them.
14+
/// Some earlier version fails with them (notably our 0.9.0 release).
15+
Rustc_1_76_0,
16+
}
17+
18+
impl TargetSpecVersion {
19+
pub fn target_arg<'a>(
20+
rustc_version: Version,
21+
target_env: &'a str,
22+
target_spec_folder: &Path,
23+
) -> std::io::Result<Cow<'a, OsStr>> {
24+
if let Some(target_spec) = Self::from_rustc_version(rustc_version) {
25+
std::fs::create_dir_all(&target_spec_folder)?;
26+
let spec_file = target_spec_folder.join(format!("spirv-unknown-{target_env}.json"));
27+
std::fs::write(&spec_file, target_spec.format_spec(target_env))?;
28+
Ok(Cow::Owned(spec_file.into_os_string()))
29+
} else {
30+
Ok(Cow::Borrowed(OsStr::new(target_env)))
31+
}
32+
}
33+
34+
/// Returns the version of the target spec required for a certain rustc version. May return `None` if the version
35+
/// is old enough to not need target specs.
36+
pub fn from_rustc_version(rustc_version: Version) -> Option<Self> {
37+
if rustc_version >= Version::new(1, 76, 0) {
38+
Some(Self::Rustc_1_76_0)
39+
} else {
40+
None
41+
}
42+
}
43+
44+
pub fn format_spec(&self, target_env: &str) -> String {
45+
match self {
46+
TargetSpecVersion::Rustc_1_76_0 => {
47+
format!(
48+
r#"{{
49+
"allows-weak-linkage": false,
50+
"arch": "spirv",
51+
"crt-objects-fallback": "false",
52+
"crt-static-allows-dylibs": true,
53+
"crt-static-respected": true,
54+
"data-layout": "e-m:e-p:32:32:32-i64:64-n8:16:32:64",
55+
"dll-prefix": "",
56+
"dll-suffix": ".spv.json",
57+
"dynamic-linking": true,
58+
"emit-debug-gdb-scripts": false,
59+
"env": "{target_env}",
60+
"linker-flavor": "unix",
61+
"linker-is-gnu": false,
62+
"llvm-target": "spirv-unknown-{target_env}",
63+
"main-needs-argc-argv": false,
64+
"metadata": {{
65+
"description": null,
66+
"host_tools": null,
67+
"std": null,
68+
"tier": null
69+
}},
70+
"panic-strategy": "abort",
71+
"simd-types-indirect": false,
72+
"target-pointer-width": "32"
73+
}}"#
74+
)
75+
}
76+
}
77+
}
78+
}

crates/spirv-builder/src/lib.rs

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,10 @@ use std::path::{Path, PathBuf};
8888
use std::process::{Command, Stdio};
8989
use thiserror::Error;
9090

91-
pub use rustc_codegen_spirv_types::Capability;
92-
pub use rustc_codegen_spirv_types::{CompileResult, ModuleResult};
93-
9491
#[cfg(feature = "watch")]
9592
pub use self::watch::{SpirvWatcher, SpirvWatcherError};
93+
pub use rustc_codegen_spirv_types::Capability;
94+
pub use rustc_codegen_spirv_types::{CompileResult, ModuleResult, TargetSpecVersion};
9695

9796
#[derive(Debug, Error)]
9897
#[non_exhaustive]
@@ -113,11 +112,6 @@ pub enum SpirvBuilderError {
113112
MissingRustcCodegenSpirvDylib,
114113
#[error("`rustc_codegen_spirv_location` path '{0}' is not a file")]
115114
RustcCodegenSpirvDylibDoesNotExist(PathBuf),
116-
#[error(
117-
"Without feature `include-target-specs`, instead of setting a `target`, \
118-
you need to set the path of the target spec file of your particular target with `path_to_target_spec`"
119-
)]
120-
MissingTargetSpec,
121115
#[error("build failed")]
122116
BuildFailed,
123117
#[error("multi-module build cannot be used with print_metadata = MetadataPrintout::Full")]
@@ -458,12 +452,6 @@ pub struct SpirvBuilder {
458452
#[cfg_attr(feature = "clap", clap(skip))]
459453
pub toolchain_rustc_version: Option<Version>,
460454

461-
/// The path of the "target specification" file.
462-
///
463-
/// For more info on "target specification" see
464-
/// [this RFC](https://rust-lang.github.io/rfcs/0131-target-specification.html).
465-
#[cfg_attr(feature = "clap", clap(skip))]
466-
pub path_to_target_spec: Option<PathBuf>,
467455
/// Set the target dir path to use for building shaders. Relative paths will be resolved
468456
/// relative to the `target` dir of the shader crate, absolute paths are used as is.
469457
/// Defaults to `spirv-builder`, resulting in the path `./target/spirv-builder`.
@@ -514,7 +502,6 @@ impl Default for SpirvBuilder {
514502
extensions: Vec::new(),
515503
extra_args: Vec::new(),
516504
rustc_codegen_spirv_location: None,
517-
path_to_target_spec: None,
518505
target_dir_path: None,
519506
toolchain_overwrite: None,
520507
toolchain_rustc_version: None,
@@ -535,16 +522,6 @@ impl SpirvBuilder {
535522
}
536523
}
537524

538-
/// Sets the path of the "target specification" file.
539-
///
540-
/// For more info on "target specification" see
541-
/// [this RFC](https://rust-lang.github.io/rfcs/0131-target-specification.html).
542-
#[must_use]
543-
pub fn target_spec(mut self, p: impl AsRef<Path>) -> Self {
544-
self.path_to_target_spec = Some(p.as_ref().to_path_buf());
545-
self
546-
}
547-
548525
/// Whether to print build.rs cargo metadata (e.g. cargo:rustc-env=var=val). Defaults to [`MetadataPrintout::Full`].
549526
#[must_use]
550527
pub fn print_metadata(mut self, v: MetadataPrintout) -> Self {
@@ -802,16 +779,17 @@ fn join_checking_for_separators(strings: Vec<impl Borrow<str>>, sep: &str) -> St
802779

803780
// Returns path to the metadata json.
804781
fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
805-
let target = builder
806-
.target
807-
.as_ref()
808-
.ok_or(SpirvBuilderError::MissingTarget)?;
809782
let path_to_crate = builder
810783
.path_to_crate
811784
.as_ref()
812785
.ok_or(SpirvBuilderError::MissingCratePath)?;
786+
let target_env;
813787
{
814-
let target_env = target.strip_prefix(SPIRV_TARGET_PREFIX).ok_or_else(|| {
788+
let target = builder
789+
.target
790+
.as_ref()
791+
.ok_or(SpirvBuilderError::MissingTarget)?;
792+
target_env = target.strip_prefix(SPIRV_TARGET_PREFIX).ok_or_else(|| {
815793
SpirvBuilderError::NonSpirvTarget {
816794
target: target.clone(),
817795
}
@@ -1032,19 +1010,10 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
10321010
cargo.args(extra_cargoflags.split_whitespace());
10331011
}
10341012

1035-
// FIXME(eddyb) consider moving `target-specs` into `rustc_codegen_spirv_types`.
1036-
// FIXME(eddyb) consider the `RUST_TARGET_PATH` env var alternative.
1037-
1038-
// NOTE(firestar99) rustc 1.76 has been tested to correctly parse modern
1039-
// target_spec jsons, some later version requires them, some earlier
1040-
// version fails with them (notably our 0.9.0 release)
1041-
if toolchain_rustc_version >= Version::new(1, 76, 0) {
1042-
let path_opt = builder.path_to_target_spec.clone();
1043-
let path = path_opt.ok_or(SpirvBuilderError::MissingTargetSpec)?;
1044-
cargo.arg("--target").arg(path);
1045-
} else {
1046-
cargo.arg("--target").arg(target);
1047-
}
1013+
let target_spec_dir = target_dir.join("target-specs");
1014+
let target =
1015+
TargetSpecVersion::target_arg(toolchain_rustc_version, target_env, &target_spec_dir)?;
1016+
cargo.arg("--target").arg(target);
10481017

10491018
if !builder.shader_crate_features.default_features {
10501019
cargo.arg("--no-default-features");

tests/difftests/tests/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)