49 lines
1.7 KiB
TypeScript
49 lines
1.7 KiB
TypeScript
/**
|
||
* Filesystem utilities used at configure time.
|
||
*
|
||
* Separate from shell.ts because "quote a shell argument" and "write a
|
||
* file idempotently" share nothing except being utility functions.
|
||
*/
|
||
|
||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||
|
||
/**
|
||
* Write `content` to `path` only if different (or file doesn't exist).
|
||
* Returns whether a write happened.
|
||
*
|
||
* Used throughout the build system for configure-time generated files
|
||
* (PCH wrapper, dep versions header, build.ninja itself). Preserving
|
||
* mtimes on unchanged content is what makes idempotent re-configure
|
||
* actually cheap: ninja sees no changes, does nothing. Without this,
|
||
* every configure touches everything and ninja at minimum re-stats.
|
||
*
|
||
* Synchronous because configure is single-threaded and the files are
|
||
* small. Async would add await noise for no concurrency benefit.
|
||
*/
|
||
export function writeIfChanged(path: string, content: string): boolean {
|
||
try {
|
||
if (readFileSync(path, "utf8") === content) return false;
|
||
} catch {
|
||
// File doesn't exist (or unreadable) — fall through to write.
|
||
}
|
||
writeFileSync(path, content);
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Create multiple directories (and their parents). Deduplicates so
|
||
* `["a/b/c", "a/b/d"]` only stats/creates `a/b` once.
|
||
*
|
||
* Used at configure time to pre-create all object-file parent dirs —
|
||
* ninja doesn't mkdir, and we don't want N×mkdir syscalls for the same
|
||
* directory when compiling N files that share a parent.
|
||
*/
|
||
export function mkdirAll(dirs: Iterable<string>): void {
|
||
const seen = new Set<string>();
|
||
for (const dir of dirs) {
|
||
if (seen.has(dir)) continue;
|
||
mkdirSync(dir, { recursive: true });
|
||
seen.add(dir);
|
||
}
|
||
}
|