Skip to content

Commit 5037f91

Browse files
committed
Redesign spirv-builder's watch API
1 parent 29ba02d commit 5037f91

File tree

2 files changed

+66
-44
lines changed

2 files changed

+66
-44
lines changed

crates/spirv-builder/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub use rustc_codegen_spirv_types::Capability;
9292
pub use rustc_codegen_spirv_types::{CompileResult, ModuleResult};
9393

9494
#[cfg(feature = "watch")]
95-
pub use self::watch::Watch;
95+
pub use self::watch::{SpirvBuilderWatchError, Watch};
9696

9797
#[cfg(feature = "include-target-specs")]
9898
pub use rustc_codegen_spirv_target_specs::TARGET_SPEC_DIR_PATH;
@@ -125,14 +125,15 @@ pub enum SpirvBuilderError {
125125
BuildFailed,
126126
#[error("multi-module build cannot be used with print_metadata = MetadataPrintout::Full")]
127127
MultiModuleWithPrintMetadata,
128-
#[error("watching within build scripts will prevent build completion")]
129-
WatchWithPrintMetadata,
130128
#[error("multi-module metadata file missing")]
131129
MetadataFileMissing(#[from] std::io::Error),
132130
#[error("unable to parse multi-module metadata file")]
133131
MetadataFileMalformed(#[from] serde_json::Error),
134132
#[error("cargo metadata error")]
135133
CargoMetadata(#[from] cargo_metadata::Error),
134+
#[cfg(feature = "watch")]
135+
#[error(transparent)]
136+
WatchFailed(#[from] SpirvBuilderWatchError),
136137
}
137138

138139
const SPIRV_TARGET_PREFIX: &str = "spirv-unknown-";

crates/spirv-builder/src/watch.rs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,12 @@ impl SpirvBuilder {
2727
.as_ref()
2828
.ok_or(SpirvBuilderError::MissingCratePath)?;
2929
if !matches!(self.print_metadata, crate::MetadataPrintout::None) {
30-
return Err(SpirvBuilderError::WatchWithPrintMetadata);
30+
return Err(SpirvBuilderWatchError::WatchWithPrintMetadata.into());
3131
}
3232

33-
let metadata_result = crate::invoke_rustc(self);
34-
// Load the dependencies of the thing
35-
let metadata_file = if let Ok(path) = metadata_result {
36-
path
37-
} else {
38-
// Fall back to watching from the crate root if the initial compilation fails
39-
// This is likely to notice changes in the `target` dir, however, given that `cargo watch` doesn't seem to handle that,
40-
let mut watcher = Watcher::new();
41-
watcher
42-
.watcher
43-
.watch(path_to_crate, RecursiveMode::Recursive)
44-
.expect("Could watch crate root");
45-
loop {
46-
watcher.recv();
47-
let metadata_file = crate::invoke_rustc(self);
48-
if let Ok(f) = metadata_file {
49-
break f;
50-
}
51-
}
52-
};
33+
let metadata_file = metadata_file(path_to_crate, self)?;
5334
let metadata = self.parse_metadata_file(&metadata_file)?;
35+
5436
let mut first_compile = None;
5537
on_compilation_finishes(metadata, Some(AcceptFirstCompile(&mut first_compile)));
5638

@@ -65,7 +47,7 @@ impl SpirvBuilder {
6547
if let Ok(file) = metadata_result {
6648
let metadata = builder
6749
.parse_metadata_file(&file)
68-
.expect("Metadata file is correct");
50+
.expect("metadata file should be valid");
6951
watcher.watch_leaf_deps(&metadata_file);
7052
on_compilation_finishes(metadata, None);
7153
}
@@ -79,6 +61,52 @@ impl SpirvBuilder {
7961
}
8062
}
8163

64+
fn notify_channel() -> Result<(RecommendedWatcher, Receiver<()>), SpirvBuilderWatchError> {
65+
let (tx, rx) = sync_channel(0);
66+
let event_handler = move |result: notify::Result<Event>| match result {
67+
Ok(event) => match event.kind {
68+
notify::EventKind::Any
69+
| notify::EventKind::Create(_)
70+
| notify::EventKind::Modify(_)
71+
| notify::EventKind::Remove(_)
72+
| notify::EventKind::Other => {
73+
if let Err(err) = tx.try_send(()) {
74+
log::error!("send error: {err:?}");
75+
}
76+
}
77+
notify::EventKind::Access(_) => {}
78+
},
79+
Err(err) => log::error!("notify error: {err:?}"),
80+
};
81+
let watcher = notify::recommended_watcher(event_handler)?;
82+
Ok((watcher, rx))
83+
}
84+
85+
fn metadata_file<P>(
86+
crate_path: P,
87+
builder: &SpirvBuilder,
88+
) -> Result<PathBuf, SpirvBuilderWatchError>
89+
where
90+
P: AsRef<Path>,
91+
{
92+
match crate::invoke_rustc(builder) {
93+
Ok(path) => return Ok(path),
94+
Err(err) => log::error!("{err}"),
95+
}
96+
97+
// Fall back to watching from the crate root if the initial compilation fails
98+
// This is likely to notice changes in the `target` dir, however, given that `cargo watch` doesn't seem to handle that,
99+
let (mut watcher, rx) = notify_channel().expect("should be able to create notify channel");
100+
watcher.watch(crate_path.as_ref(), RecursiveMode::Recursive)?;
101+
loop {
102+
rx.recv().expect("watcher should be alive");
103+
match crate::invoke_rustc(builder) {
104+
Ok(path) => break Ok(path),
105+
Err(err) => log::error!("{err}"),
106+
}
107+
}
108+
}
109+
82110
pub struct AcceptFirstCompile<'a, T>(&'a mut Option<T>);
83111

84112
impl<'a, T> AcceptFirstCompile<'a, T> {
@@ -112,22 +140,7 @@ struct Watcher {
112140

113141
impl Watcher {
114142
fn new() -> Self {
115-
let (tx, rx) = sync_channel(0);
116-
let watcher =
117-
notify::recommended_watcher(move |event: notify::Result<Event>| match event {
118-
Ok(e) => match e.kind {
119-
notify::EventKind::Access(_) => (),
120-
notify::EventKind::Any
121-
| notify::EventKind::Create(_)
122-
| notify::EventKind::Modify(_)
123-
| notify::EventKind::Remove(_)
124-
| notify::EventKind::Other => {
125-
let _ = tx.try_send(());
126-
}
127-
},
128-
Err(e) => println!("notify error: {e:?}"),
129-
})
130-
.expect("Could create watcher");
143+
let (watcher, rx) = notify_channel().expect("should be able to create notify channel");
131144
Self {
132145
watcher,
133146
rx,
@@ -141,13 +154,21 @@ impl Watcher {
141154
if self.watched_paths.insert(path.to_owned()) {
142155
self.watcher
143156
.watch(it.to_path().unwrap(), RecursiveMode::NonRecursive)
144-
.expect("Cargo dependencies are valid files");
157+
.expect("files of cargo dependencies should be valid");
145158
}
146159
})
147-
.expect("Could read dependencies file");
160+
.expect("should be able to read dependencies file");
148161
}
149162

150163
fn recv(&self) {
151-
self.rx.recv().expect("Watcher still alive");
164+
self.rx.recv().expect("watcher should be alive");
152165
}
153166
}
167+
168+
#[derive(Debug, thiserror::Error)]
169+
pub enum SpirvBuilderWatchError {
170+
#[error("watching within build scripts will prevent build completion")]
171+
WatchWithPrintMetadata,
172+
#[error("could not notify for changes: {0}")]
173+
NotifyFailed(#[from] notify::Error),
174+
}

0 commit comments

Comments
 (0)