Skip to content

Commit 2323373

Browse files
committed
Fix rebuild after installing stubs
1 parent c3e8e0e commit 2323373

1 file changed

Lines changed: 46 additions & 19 deletions

File tree

build.rs

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@
88
//! indices into the embedded file array.
99
//!
1010
//! The generated file is consumed by `src/stubs.rs` at compile time.
11+
//!
12+
//! ## Re-run strategy
13+
//!
14+
//! The `stubs/` directory is gitignored, so Cargo's default "re-run when
15+
//! any package file changes" behaviour does not notice when
16+
//! `composer install` creates it. Explicit `rerun-if-changed` on paths
17+
//! inside `stubs/` also fails when the directory doesn't exist yet.
18+
//!
19+
//! Instead we watch the project root directory (`.`). Its mtime changes
20+
//! whenever a direct child like `stubs/` is created or removed. We also
21+
//! watch `build.rs` and `composer.lock` for targeted rebuilds.
22+
//!
23+
//! To avoid unnecessary recompilation of the main crate we compare the
24+
//! newly generated content against the existing output file and only write
25+
//! when something actually changed. This way `rustc` sees a stable mtime
26+
//! on `stub_map_generated.rs` and skips recompilation when the stubs
27+
//! haven't changed.
1128
1229
use std::collections::{BTreeMap, BTreeSet};
1330
use std::env;
@@ -22,12 +39,14 @@ const MAP_FILE: &str = "stubs/jetbrains/phpstorm-stubs/PhpStormStubsMap.php";
2239
const STUBS_DIR: &str = "stubs/jetbrains/phpstorm-stubs";
2340

2441
fn main() {
25-
// Tell Cargo to re-run this script when dependencies change.
26-
// We watch composer.lock (rather than PhpStormStubsMap.php directly)
27-
// because that's the file that changes when `composer update` pulls
28-
// a new version of phpstorm-stubs — more natural for PHP developers.
29-
println!("cargo:rerun-if-changed=composer.lock");
42+
// Watch the project root directory so that creating/removing `stubs/`
43+
// (which is gitignored) is detected via the directory mtime change.
44+
// Without this, Cargo's default "any package file" check ignores
45+
// gitignored paths, and explicit watches on non-existent paths don't
46+
// reliably trigger when they first appear.
47+
println!("cargo:rerun-if-changed=.");
3048
println!("cargo:rerun-if-changed=build.rs");
49+
println!("cargo:rerun-if-changed=composer.lock");
3150

3251
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
3352
let map_path = Path::new(&manifest_dir).join(MAP_FILE);
@@ -41,7 +60,13 @@ fn main() {
4160
"cargo:warning=Could not read PhpStormStubsMap.php ({}); generating empty stub index",
4261
e
4362
);
44-
write_empty_generated_file();
63+
let content = concat!(
64+
"pub(crate) static STUB_FILES: [&str; 0] = [];\n",
65+
"pub(crate) static STUB_CLASS_MAP: [(&str, usize); 0] = [];\n",
66+
"pub(crate) static STUB_FUNCTION_MAP: [(&str, usize); 0] = [];\n",
67+
"pub(crate) static STUB_CONSTANT_MAP: [(&str, usize); 0] = [];\n",
68+
);
69+
write_if_changed(content);
4570
return;
4671
}
4772
};
@@ -82,9 +107,6 @@ fn main() {
82107

83108
// ── Generate Rust source ────────────────────────────────────────────
84109

85-
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
86-
let dest_path = Path::new(&out_dir).join("stub_map_generated.rs");
87-
88110
let mut out = String::with_capacity(512 * 1024);
89111

90112
// 1. The embedded file array.
@@ -170,7 +192,7 @@ fn main() {
170192
}
171193
out.push_str("];\n");
172194

173-
fs::write(&dest_path, &out).expect("Failed to write generated stub map");
195+
write_if_changed(&out);
174196
}
175197

176198
/// Parse one of the `const CLASSES = array(...)`, `const FUNCTIONS = array(...)`,
@@ -223,15 +245,20 @@ fn escape(s: &str) -> String {
223245
s.replace('\\', "\\\\").replace('"', "\\\"")
224246
}
225247

226-
/// Write an empty generated file when stubs are not available.
227-
fn write_empty_generated_file() {
248+
/// Write the generated file only if its content has actually changed.
249+
///
250+
/// This avoids bumping the mtime on `stub_map_generated.rs` when nothing
251+
/// changed, which in turn prevents `rustc` from unnecessarily recompiling
252+
/// the main crate.
253+
fn write_if_changed(content: &str) {
228254
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
229255
let dest_path = Path::new(&out_dir).join("stub_map_generated.rs");
230-
let content = concat!(
231-
"pub(crate) static STUB_FILES: [&str; 0] = [];\n",
232-
"pub(crate) static STUB_CLASS_MAP: [(&str, usize); 0] = [];\n",
233-
"pub(crate) static STUB_FUNCTION_MAP: [(&str, usize); 0] = [];\n",
234-
"pub(crate) static STUB_CONSTANT_MAP: [(&str, usize); 0] = [];\n",
235-
);
236-
fs::write(&dest_path, content).expect("Failed to write empty generated stub map");
256+
257+
if let Ok(existing) = fs::read_to_string(&dest_path)
258+
&& existing == content
259+
{
260+
return;
261+
}
262+
263+
fs::write(&dest_path, content).expect("Failed to write generated stub map");
237264
}

0 commit comments

Comments
 (0)