Add stderr and termcolor features to Naga (#7482)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
Co-authored-by: Erich Gubler <erichdongubler@gmail.com>
This commit is contained in:
Zachary Harrold 2025-04-19 07:06:22 +10:00 committed by GitHub
parent 6666d528b2
commit bf8cc43a15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 144 additions and 42 deletions

View File

@ -351,6 +351,7 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h
- Refactored `use` statements to simplify future `no_std` support. By @bushrat011899 in [#7256](https://github.com/gfx-rs/wgpu/pull/7256)
- Naga's WGSL frontend no longer allows using the `&` operator to take the address of a component of a vector,
which is not permitted by the WGSL specification. By @andyleiserson in [#7284](https://github.com/gfx-rs/wgpu/pull/7284)
- Naga's use of `termcolor` and `stderr` are now optional behind features of the same names. By @bushrat011899 in [#7482](https://github.com/gfx-rs/wgpu/pull/7482)
#### Vulkan

1
Cargo.lock generated
View File

@ -2407,6 +2407,7 @@ dependencies = [
"arrayvec",
"bit-set 0.8.0",
"bitflags 2.9.0",
"cfg-if",
"cfg_aliases 0.2.1",
"codespan-reporting",
"diff",

View File

@ -37,6 +37,8 @@ naga = { workspace = true, features = [
"dot-out",
"serialize",
"deserialize",
"termcolor",
"stderr",
] }
bincode.workspace = true

View File

@ -80,15 +80,19 @@ hlsl-out-if-target-windows = []
compact = []
## Enables colored output through codespan-reporting and termcolor.
termcolor = ["codespan-reporting/termcolor"]
## Enables writing output to stderr.
stderr = ["codespan-reporting/std"]
[dependencies]
arbitrary = { version = "1.4", features = ["derive"], optional = true }
arrayvec.workspace = true
bitflags.workspace = true
bit-set.workspace = true
codespan-reporting = { workspace = true, default-features = false, features = [
"std",
"termcolor",
] }
cfg-if.workspace = true
codespan-reporting = { workspace = true, default-features = false }
hashbrown.workspace = true
half = { workspace = true, default-features = false, features = ["num-traits"] }
rustc-hash.workspace = true

View File

@ -1,4 +1,4 @@
use alloc::{boxed::Box, string::String, vec::Vec};
use alloc::{boxed::Box, string::String};
use core::{error::Error, fmt};
#[derive(Clone, Debug)]
@ -43,14 +43,88 @@ impl fmt::Display for ShaderError<crate::WithSpan<crate::valid::ValidationError>
let label = self.label.as_deref().unwrap_or_default();
let files = SimpleFile::new(label, &self.source);
let config = term::Config::default();
let mut writer = term::termcolor::NoColor::new(Vec::new());
term::emit(&mut writer, &config, &files, &self.inner.diagnostic())
let writer = {
let mut writer = DiagnosticBuffer::new();
term::emit(
writer.inner_mut(),
&config,
&files,
&self.inner.diagnostic(),
)
.expect("cannot write error");
write!(
f,
"\nShader validation {}",
String::from_utf8_lossy(&writer.into_inner())
writer.into_string()
};
write!(f, "\nShader validation {}", writer)
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "termcolor")] {
type DiagnosticBufferInner = codespan_reporting::term::termcolor::NoColor<alloc::vec::Vec<u8>>;
pub(crate) use codespan_reporting::term::termcolor::WriteColor as _ErrorWrite;
} else if #[cfg(feature = "stderr")] {
type DiagnosticBufferInner = alloc::vec::Vec<u8>;
pub(crate) use std::io::Write as _ErrorWrite;
} else {
type DiagnosticBufferInner = String;
pub(crate) use core::fmt::Write as _ErrorWrite;
}
}
// Using this indirect export to avoid duplicating the expect(...) for all three cases above.
#[cfg_attr(
not(any(feature = "spv-in", feature = "glsl-in")),
expect(
unused_imports,
reason = "only need `ErrorWrite` with an appropriate front-end."
)
)]
pub(crate) use _ErrorWrite as ErrorWrite;
pub(crate) struct DiagnosticBuffer {
inner: DiagnosticBufferInner,
}
impl DiagnosticBuffer {
#[cfg_attr(
not(feature = "termcolor"),
expect(
clippy::missing_const_for_fn,
reason = "`NoColor::new` isn't `const`, but other `inner`s are."
)
)]
pub fn new() -> Self {
cfg_if::cfg_if! {
if #[cfg(feature = "termcolor")] {
let inner = codespan_reporting::term::termcolor::NoColor::new(alloc::vec::Vec::new());
} else if #[cfg(feature = "stderr")] {
let inner = alloc::vec::Vec::new();
} else {
let inner = String::new();
}
};
Self { inner }
}
pub fn inner_mut(&mut self) -> &mut DiagnosticBufferInner {
&mut self.inner
}
pub fn into_string(self) -> String {
let Self { inner } = self;
cfg_if::cfg_if! {
if #[cfg(feature = "termcolor")] {
String::from_utf8(inner.into_inner()).unwrap()
} else if #[cfg(feature = "stderr")] {
String::from_utf8(inner).unwrap()
} else {
inner
}
}
}
}

View File

@ -8,13 +8,12 @@ use alloc::{
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::SimpleFile;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{NoColor, WriteColor};
use pp_rs::token::PreprocessorError;
use thiserror::Error;
use super::token::TokenValue;
use crate::SourceLocation;
use crate::{proc::ConstantEvaluatorError, Span};
use crate::{error::ErrorWrite, proc::ConstantEvaluatorError, Span};
fn join_with_comma(list: &[ExpectedToken]) -> String {
let mut string = "".to_string();
@ -166,11 +165,11 @@ pub struct ParseErrors {
}
impl ParseErrors {
pub fn emit_to_writer(&self, writer: &mut impl WriteColor, source: &str) {
pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {
self.emit_to_writer_with_path(writer, source, "glsl");
}
pub fn emit_to_writer_with_path(&self, writer: &mut impl WriteColor, source: &str, path: &str) {
pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {
let path = path.to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
@ -187,9 +186,9 @@ impl ParseErrors {
}
pub fn emit_to_string(&self, source: &str) -> String {
let mut writer = NoColor::new(Vec::new());
self.emit_to_writer(&mut writer, source);
String::from_utf8(writer.into_inner()).unwrap()
let mut writer = crate::error::DiagnosticBuffer::new();
self.emit_to_writer(writer.inner_mut(), source);
writer.into_string()
}
}

View File

@ -1,16 +1,14 @@
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::files::SimpleFile;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{NoColor, WriteColor};
use super::ModuleState;
use crate::{arena::Handle, front::atomic_upgrade};
use crate::{arena::Handle, error::ErrorWrite, front::atomic_upgrade};
#[derive(Clone, Debug, thiserror::Error)]
pub enum Error {
@ -153,11 +151,11 @@ pub enum Error {
}
impl Error {
pub fn emit_to_writer(&self, writer: &mut impl WriteColor, source: &str) {
pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {
self.emit_to_writer_with_path(writer, source, "glsl");
}
pub fn emit_to_writer_with_path(&self, writer: &mut impl WriteColor, source: &str, path: &str) {
pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {
let path = path.to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
@ -167,9 +165,9 @@ impl Error {
}
pub fn emit_to_string(&self, source: &str) -> String {
let mut writer = NoColor::new(Vec::new());
self.emit_to_writer(&mut writer, source);
String::from_utf8(writer.into_inner()).unwrap()
let mut writer = crate::error::DiagnosticBuffer::new();
self.emit_to_writer(writer.inner_mut(), source);
writer.into_string()
}
}

View File

@ -14,7 +14,6 @@ use super::parse::lexer::Token;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::SimpleFile;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, NoColor, StandardStream};
use thiserror::Error;
use alloc::{
@ -68,11 +67,13 @@ impl ParseError {
}
/// Emits a summary of the error to standard error stream.
#[cfg(feature = "stderr")]
pub fn emit_to_stderr(&self, source: &str) {
self.emit_to_stderr_with_path(source, "wgsl")
}
/// Emits a summary of the error to standard error stream.
#[cfg(feature = "stderr")]
pub fn emit_to_stderr_with_path<P>(&self, source: &str, path: P)
where
P: AsRef<std::path::Path>,
@ -80,7 +81,15 @@ impl ParseError {
let path = path.as_ref().display().to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
let writer = StandardStream::stderr(ColorChoice::Auto);
cfg_if::cfg_if! {
if #[cfg(feature = "termcolor")] {
let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);
} else {
let writer = std::io::stderr();
}
}
term::emit(&mut writer.lock(), &config, &files, &self.diagnostic())
.expect("cannot write error");
}
@ -98,9 +107,11 @@ impl ParseError {
let path = path.as_ref().display().to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
let mut writer = NoColor::new(Vec::new());
term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error");
String::from_utf8(writer.into_inner()).unwrap()
let mut writer = crate::error::DiagnosticBuffer::new();
term::emit(writer.inner_mut(), &config, &files, &self.diagnostic())
.expect("cannot write error");
writer.into_string()
}
/// Returns a [`SourceLocation`] for the first label in the error message.

View File

@ -110,7 +110,9 @@ void main() {
wgsl_out,
feature = "spv-in",
feature = "wgsl-in"
feature = "wgsl-in",
feature = "stderr",
))]
extern crate std;

View File

@ -273,6 +273,7 @@ impl<E> WithSpan<E> {
}
/// Emits a summary of the error to standard error stream.
#[cfg(feature = "stderr")]
pub fn emit_to_stderr(&self, source: &str)
where
E: Error,
@ -281,16 +282,24 @@ impl<E> WithSpan<E> {
}
/// Emits a summary of the error to standard error stream.
#[cfg(feature = "stderr")]
pub fn emit_to_stderr_with_path(&self, source: &str, path: &str)
where
E: Error,
{
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use codespan_reporting::{files, term};
let files = files::SimpleFile::new(path, source);
let config = term::Config::default();
let writer = StandardStream::stderr(ColorChoice::Auto);
cfg_if::cfg_if! {
if #[cfg(feature = "termcolor")] {
let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);
} else {
let writer = std::io::stderr();
}
}
term::emit(&mut writer.lock(), &config, &files, &self.diagnostic())
.expect("cannot write error");
}
@ -308,14 +317,15 @@ impl<E> WithSpan<E> {
where
E: Error,
{
use codespan_reporting::term::termcolor::NoColor;
use codespan_reporting::{files, term};
let files = files::SimpleFile::new(path, source);
let config = term::Config::default();
let mut writer = NoColor::new(Vec::new());
term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error");
String::from_utf8(writer.into_inner()).unwrap()
let mut writer = crate::error::DiagnosticBuffer::new();
term::emit(writer.inner_mut(), &config, &files, &self.diagnostic())
.expect("cannot write error");
writer.into_string()
}
}

View File

@ -324,9 +324,9 @@ cfg_aliases.workspace = true
[dev-dependencies]
cfg-if.workspace = true
env_logger.workspace = true
glam.workspace = true # for ray-traced-triangle example
naga = { workspace = true, features = ["wgsl-in"] }
winit.workspace = true # for "halmark" example
glam.workspace = true # for ray-traced-triangle example
naga = { workspace = true, features = ["wgsl-in", "termcolor"] }
winit.workspace = true # for "halmark" example
### Platform: Windows + MacOS + Linux for "raw-gles" example ###
[target.'cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "visionos")))'.dev-dependencies]

View File

@ -153,7 +153,7 @@ fragile-send-sync-non-atomic-wasm = [
#########################
[dependencies]
naga = { workspace = true, optional = true }
naga = { workspace = true, optional = true, features = ["termcolor"] }
wgpu-core = { workspace = true, optional = true }
wgpu-types.workspace = true