forked from Rust-GPU/rust-gpu
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwatch.rs
More file actions
147 lines (133 loc) 路 5.13 KB
/
Copy pathwatch.rs
File metadata and controls
147 lines (133 loc) 路 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::convert::Infallible;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
use std::thread::JoinHandle;
use std::{collections::HashSet, sync::mpsc::sync_channel};
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _};
use rustc_codegen_spirv_types::CompileResult;
use crate::{SpirvBuilder, SpirvBuilderError, leaf_deps};
impl SpirvBuilder {
/// Watches the module for changes using [`notify`], rebuilding it upon changes.
///
/// Calls `on_compilation_finishes` after each successful compilation.
pub fn watch<T, F>(&self, mut on_compilation_finishes: F) -> Result<Watch<T>, SpirvBuilderError>
where
F: FnMut(CompileResult, Option<AcceptFirstCompile<'_, T>>) + Send + 'static,
{
let path_to_crate = self
.path_to_crate
.as_ref()
.ok_or(SpirvBuilderError::MissingCratePath)?;
if !matches!(self.print_metadata, crate::MetadataPrintout::None) {
return Err(SpirvBuilderError::WatchWithPrintMetadata);
}
let metadata_result = crate::invoke_rustc(self);
// Load the dependencies of the thing
let metadata_file = if let Ok(path) = metadata_result {
path
} else {
// Fall back to watching from the crate root if the initial compilation fails
// This is likely to notice changes in the `target` dir, however, given that `cargo watch` doesn't seem to handle that,
let mut watcher = Watcher::new();
watcher
.watcher
.watch(path_to_crate, RecursiveMode::Recursive)
.expect("Could watch crate root");
loop {
watcher.recv();
let metadata_file = crate::invoke_rustc(self);
if let Ok(f) = metadata_file {
break f;
}
}
};
let metadata = self.parse_metadata_file(&metadata_file)?;
let mut first_compile = None;
on_compilation_finishes(metadata, Some(AcceptFirstCompile(&mut first_compile)));
let builder = self.clone();
let watch_thread = std::thread::spawn(move || {
let mut watcher = Watcher::new();
watcher.watch_leaf_deps(&metadata_file);
loop {
watcher.recv();
let metadata_result = crate::invoke_rustc(&builder);
if let Ok(file) = metadata_result {
let metadata = builder
.parse_metadata_file(&file)
.expect("Metadata file is correct");
watcher.watch_leaf_deps(&metadata_file);
on_compilation_finishes(metadata, None);
}
}
});
Ok(Watch {
first_compile,
watch_thread,
})
}
}
/// The second parameter of the callback passed to [`SpirvBuilder::watch()`]
/// which allows it to return some value of `T` on the first compile.
pub struct AcceptFirstCompile<'a, T>(&'a mut Option<T>);
impl<'a, T> AcceptFirstCompile<'a, T> {
/// Accepts some value of `T` to be later returned by [`SpirvBuilder::watch()`].
pub fn submit(self, value: T) {
*self.0 = Some(value);
}
}
/// Result of [watching](SpirvBuilder::watch) a module for changes.
#[must_use]
#[non_exhaustive]
pub struct Watch<T> {
/// Result of the first compile, if any.
pub first_compile: Option<T>,
/// Join handle to the watching thread.
///
/// You can drop it to detach the watching thread,
/// or [`join()`](JoinHandle::join) it to block the current thread until shutdown of the program.
pub watch_thread: JoinHandle<Infallible>,
}
struct Watcher {
watcher: RecommendedWatcher,
rx: Receiver<()>,
watched_paths: HashSet<PathBuf>,
}
impl Watcher {
fn new() -> Self {
let (tx, rx) = sync_channel(0);
let watcher =
notify::recommended_watcher(move |event: notify::Result<Event>| match event {
Ok(e) => match e.kind {
notify::EventKind::Access(_) => (),
notify::EventKind::Any
| notify::EventKind::Create(_)
| notify::EventKind::Modify(_)
| notify::EventKind::Remove(_)
| notify::EventKind::Other => {
let _ = tx.try_send(());
}
},
Err(e) => println!("notify error: {e:?}"),
})
.expect("Could create watcher");
Self {
watcher,
rx,
watched_paths: HashSet::new(),
}
}
fn watch_leaf_deps(&mut self, metadata_file: &Path) {
leaf_deps(metadata_file, |it| {
let path = it.to_path().unwrap();
if self.watched_paths.insert(path.to_owned()) {
self.watcher
.watch(it.to_path().unwrap(), RecursiveMode::NonRecursive)
.expect("Cargo dependencies are valid files");
}
})
.expect("Could read dependencies file");
}
fn recv(&self) {
self.rx.recv().expect("Watcher still alive");
}
}