|
1 | | -use std::{collections::HashSet, sync::mpsc::sync_channel}; |
2 | | - |
3 | | -use notify::{Event, RecursiveMode, Watcher}; |
4 | | -use rustc_codegen_spirv_types::CompileResult; |
5 | | - |
6 | 1 | use crate::{SpirvBuilder, SpirvBuilderError, leaf_deps}; |
| 2 | +use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _}; |
| 3 | +use rustc_codegen_spirv_types::CompileResult; |
| 4 | +use std::path::{Path, PathBuf}; |
| 5 | +use std::sync::mpsc::Receiver; |
| 6 | +use std::{collections::HashSet, sync::mpsc::sync_channel}; |
7 | 7 |
|
8 | 8 | impl SpirvBuilder { |
9 | 9 | /// Watches the module for changes using [`notify`](https://crates.io/crates/notify), |
10 | | - /// and rebuild it upon changes |
11 | | - /// |
12 | | - /// Returns the result of the first successful compilation, then calls |
13 | | - /// `on_compilation_finishes` for each subsequent compilation. |
| 10 | + /// and rebuild it upon changes. Calls `on_compilation_finishes` after each |
| 11 | + /// successful compilation. |
14 | 12 | pub fn watch( |
15 | | - self, |
| 13 | + &self, |
16 | 14 | mut on_compilation_finishes: impl FnMut(CompileResult) + Send + 'static, |
17 | | - ) -> Result<CompileResult, SpirvBuilderError> { |
| 15 | + ) -> Result<(), SpirvBuilderError> { |
18 | 16 | let path_to_crate = self |
19 | 17 | .path_to_crate |
20 | 18 | .as_ref() |
21 | 19 | .ok_or(SpirvBuilderError::MissingCratePath)?; |
22 | 20 | if !matches!(self.print_metadata, crate::MetadataPrintout::None) { |
23 | 21 | return Err(SpirvBuilderError::WatchWithPrintMetadata); |
24 | 22 | } |
25 | | - let metadata_result = crate::invoke_rustc(&self); |
| 23 | + |
| 24 | + let metadata_result = crate::invoke_rustc(self); |
26 | 25 | // Load the dependencies of the thing |
27 | 26 | let metadata_file = if let Ok(path) = metadata_result { |
28 | 27 | path |
29 | 28 | } else { |
30 | | - let (tx, rx) = sync_channel(0); |
31 | 29 | // Fall back to watching from the crate root if the initial compilation fails |
32 | | - let mut watcher = |
33 | | - notify::recommended_watcher(move |event: notify::Result<Event>| match event { |
34 | | - Ok(e) => match e.kind { |
35 | | - notify::EventKind::Access(_) => (), |
36 | | - notify::EventKind::Any |
37 | | - | notify::EventKind::Create(_) |
38 | | - | notify::EventKind::Modify(_) |
39 | | - | notify::EventKind::Remove(_) |
40 | | - | notify::EventKind::Other => { |
41 | | - let _ = tx.try_send(()); |
42 | | - } |
43 | | - }, |
44 | | - Err(e) => println!("notify error: {e:?}"), |
45 | | - }) |
46 | | - .expect("Could create watcher"); |
47 | 30 | // This is likely to notice changes in the `target` dir, however, given that `cargo watch` doesn't seem to handle that, |
| 31 | + let mut watcher = Watcher::new(); |
48 | 32 | watcher |
| 33 | + .watcher |
49 | 34 | .watch(path_to_crate, RecursiveMode::Recursive) |
50 | 35 | .expect("Could watch crate root"); |
51 | 36 | loop { |
52 | | - rx.recv().expect("Watcher still alive"); |
53 | | - let metadata_file = crate::invoke_rustc(&self); |
| 37 | + watcher.recv(); |
| 38 | + let metadata_file = crate::invoke_rustc(self); |
54 | 39 | if let Ok(f) = metadata_file { |
55 | 40 | break f; |
56 | 41 | } |
57 | 42 | } |
58 | 43 | }; |
59 | 44 | let metadata = self.parse_metadata_file(&metadata_file)?; |
60 | | - let first_result = metadata; |
| 45 | + on_compilation_finishes(metadata); |
61 | 46 |
|
| 47 | + let builder = self.clone(); |
62 | 48 | let thread = std::thread::spawn(move || { |
63 | | - let mut watched_paths = HashSet::new(); |
64 | | - let (tx, rx) = sync_channel(0); |
65 | | - let mut watcher = |
66 | | - notify::recommended_watcher(move |event: notify::Result<Event>| match event { |
67 | | - Ok(e) => match e.kind { |
68 | | - notify::EventKind::Access(_) => (), |
69 | | - notify::EventKind::Any |
70 | | - | notify::EventKind::Create(_) |
71 | | - | notify::EventKind::Modify(_) |
72 | | - | notify::EventKind::Remove(_) |
73 | | - | notify::EventKind::Other => { |
74 | | - let _ = tx.try_send(()); |
75 | | - } |
76 | | - }, |
77 | | - Err(e) => println!("notify error: {e:?}"), |
78 | | - }) |
79 | | - .expect("Could create watcher"); |
80 | | - leaf_deps(&metadata_file, |it| { |
81 | | - let path = it.to_path().unwrap(); |
82 | | - if watched_paths.insert(path.to_owned()) { |
83 | | - watcher |
84 | | - .watch(it.to_path().unwrap(), RecursiveMode::NonRecursive) |
85 | | - .expect("Cargo dependencies are valid files"); |
86 | | - } |
87 | | - }) |
88 | | - .expect("Could read dependencies file"); |
| 49 | + let mut watcher = Watcher::new(); |
| 50 | + watcher.watch_leaf_deps(&metadata_file); |
| 51 | + |
89 | 52 | loop { |
90 | | - rx.recv().expect("Watcher still alive"); |
91 | | - let metadata_result = crate::invoke_rustc(&self); |
| 53 | + watcher.recv(); |
| 54 | + let metadata_result = crate::invoke_rustc(&builder); |
92 | 55 | if let Ok(file) = metadata_result { |
93 | | - let metadata = self |
| 56 | + let metadata = builder |
94 | 57 | .parse_metadata_file(&file) |
95 | 58 | .expect("Metadata file is correct"); |
96 | | - |
97 | | - leaf_deps(&file, |it| { |
98 | | - let path = it.to_path().unwrap(); |
99 | | - if watched_paths.insert(path.to_owned()) { |
100 | | - watcher |
101 | | - .watch(it.to_path().unwrap(), RecursiveMode::NonRecursive) |
102 | | - .expect("Cargo dependencies are valid files"); |
103 | | - } |
104 | | - }) |
105 | | - .expect("Could read dependencies file"); |
106 | | - |
| 59 | + watcher.watch_leaf_deps(&metadata_file); |
107 | 60 | on_compilation_finishes(metadata); |
108 | 61 | } |
109 | 62 | } |
110 | 63 | }); |
111 | | - std::mem::drop(thread); |
112 | | - Ok(first_result) |
| 64 | + drop(thread); |
| 65 | + Ok(()) |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +struct Watcher { |
| 70 | + watcher: RecommendedWatcher, |
| 71 | + rx: Receiver<()>, |
| 72 | + watched_paths: HashSet<PathBuf>, |
| 73 | +} |
| 74 | + |
| 75 | +impl Watcher { |
| 76 | + fn new() -> Self { |
| 77 | + let (tx, rx) = sync_channel(0); |
| 78 | + let watcher = |
| 79 | + notify::recommended_watcher(move |event: notify::Result<Event>| match event { |
| 80 | + Ok(e) => match e.kind { |
| 81 | + notify::EventKind::Access(_) => (), |
| 82 | + notify::EventKind::Any |
| 83 | + | notify::EventKind::Create(_) |
| 84 | + | notify::EventKind::Modify(_) |
| 85 | + | notify::EventKind::Remove(_) |
| 86 | + | notify::EventKind::Other => { |
| 87 | + let _ = tx.try_send(()); |
| 88 | + } |
| 89 | + }, |
| 90 | + Err(e) => println!("notify error: {e:?}"), |
| 91 | + }) |
| 92 | + .expect("Could create watcher"); |
| 93 | + Self { |
| 94 | + watcher, |
| 95 | + rx, |
| 96 | + watched_paths: HashSet::new(), |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + fn watch_leaf_deps(&mut self, metadata_file: &Path) { |
| 101 | + leaf_deps(metadata_file, |it| { |
| 102 | + let path = it.to_path().unwrap(); |
| 103 | + if self.watched_paths.insert(path.to_owned()) { |
| 104 | + self.watcher |
| 105 | + .watch(it.to_path().unwrap(), RecursiveMode::NonRecursive) |
| 106 | + .expect("Cargo dependencies are valid files"); |
| 107 | + } |
| 108 | + }) |
| 109 | + .expect("Could read dependencies file"); |
| 110 | + } |
| 111 | + |
| 112 | + fn recv(&self) { |
| 113 | + self.rx.recv().expect("Watcher still alive"); |
113 | 114 | } |
114 | 115 | } |
0 commit comments