mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-04 08:04:37 +08:00
Record a linker job again when a path it names changes (#2428)
This commit is contained in:
@@ -212,7 +212,7 @@ test.skipIf(process.platform === "win32")(
|
||||
);
|
||||
|
||||
test.skipIf(process.platform === "win32")(
|
||||
"a changed startup object, library directory or linker sends the link to the driver",
|
||||
"a changed startup object, library directory or linker records the job again",
|
||||
() => {
|
||||
for (const change of ["crt", "lib", "ld"] as const) {
|
||||
const f = fixture();
|
||||
@@ -230,12 +230,44 @@ test.skipIf(process.platform === "win32")(
|
||||
}
|
||||
const second = f.stage();
|
||||
const count = f.calls().length;
|
||||
expect(open().run(second.args, second.paths, second.directory, true)).toBeNull();
|
||||
expect(f.calls().length).toBe(count);
|
||||
expect(open().run(second.args, second.paths, second.directory, true)).not.toBeNull();
|
||||
expect(f.calls().slice(count)).toEqual([
|
||||
`driver ${second.args.join(" ")} -v -###`,
|
||||
`ld.lld -pie ${f.crt} -L${f.lib} -pthread ${second.paths[0]} -lm -o ${second.output}`,
|
||||
]);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test.skipIf(process.platform === "win32")(
|
||||
"a traced link whose recorded job names a changed path records it again",
|
||||
() => {
|
||||
const f = fixture();
|
||||
const open = () => LinkerJobs.open(f.cache, f.toolchain(), ["-fuse-ld=lld"], false)!;
|
||||
const traced = (s: ReturnType<typeof f.stage>) => [...s.args, "-v", "-Wl,-t"];
|
||||
const linkThroughDriver = (s: ReturnType<typeof f.stage>) => {
|
||||
const link = open().link(s.paths, s.directory);
|
||||
expect(link.run(traced(s), false)).toBeNull();
|
||||
const linked = spawnSync(f.driver, traced(s), { encoding: "utf8" });
|
||||
expect(linked.status).toBe(0);
|
||||
link.record(traced(s), linked.stdout + "\n" + linked.stderr);
|
||||
};
|
||||
linkThroughDriver(f.stage());
|
||||
const before = statSync(f.crt);
|
||||
writeFileSync(f.crt, "new startup object");
|
||||
utimesSync(f.crt, before.atime, new Date(before.mtimeMs + 1000));
|
||||
// The recorded job names the old startup object: the link runs through the driver once,
|
||||
// and its job is recorded again.
|
||||
linkThroughDriver(f.stage());
|
||||
const third = f.stage();
|
||||
const count = f.calls().length;
|
||||
expect(open().link(third.paths, third.directory).run(traced(third), false)).not.toBeNull();
|
||||
expect(f.calls().slice(count)).toEqual([
|
||||
`ld.lld -pie ${f.crt} -L${f.lib} -pthread ${third.paths[0]} -lm -o ${third.output} -t`,
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.skipIf(process.platform === "win32")("a changed driver records the job again", () => {
|
||||
const f = fixture();
|
||||
const first = f.stage();
|
||||
|
||||
@@ -164,10 +164,11 @@ export class LinkerJobs {
|
||||
* Runs the link that the driver arguments `args` describe as its recorded
|
||||
* job. `paths` are the link's own inputs and output among `args`, which may
|
||||
* differ from build to build; `stage` is the directory they are in. Without
|
||||
* an entry, `list` records the job first with the driver's `-v -###`;
|
||||
* an entry, or with one whose job names a path that changed since it was
|
||||
* recorded, `list` records the job first with the driver's `-v -###`;
|
||||
* otherwise the caller links through the driver and passes its `-v` output
|
||||
* to record(). Null when the link has to run through the driver: no job is
|
||||
* recorded, a recorded path changed, or the job failed.
|
||||
* to record(), which records it again. Null when the link has to run
|
||||
* through the driver: no job is recorded, or the job failed.
|
||||
*/
|
||||
run(
|
||||
args: readonly string[],
|
||||
@@ -180,7 +181,7 @@ export class LinkerJobs {
|
||||
const roots = [resolve(stage), realpathSync(stage)];
|
||||
const key = this.key(args, paths, roots);
|
||||
if (key === null) return null;
|
||||
let entry = this.read(key, paths.length);
|
||||
let entry = this.fresh(this.read(key, paths.length));
|
||||
if (entry === null && list) {
|
||||
const result = spawnSync(this.driver, [...args, "-v", "-###"], {
|
||||
encoding: "utf8",
|
||||
@@ -190,7 +191,7 @@ export class LinkerJobs {
|
||||
return null;
|
||||
entry = this.store(key, result.stdout + "\n" + result.stderr, paths, roots);
|
||||
}
|
||||
if (entry === null || entry.job === null || !stillMatches(entry.inputs)) return null;
|
||||
if (entry === null || entry.job === null) return null;
|
||||
const job = entry.job.map((token) => (typeof token === "number" ? paths[token]! : token));
|
||||
const result = spawnSync(job[0]!, job.slice(1), {
|
||||
encoding: "utf8",
|
||||
@@ -206,6 +207,14 @@ export class LinkerJobs {
|
||||
}
|
||||
}
|
||||
|
||||
/** `entry`, or null when a path its job names changed since it was
|
||||
* recorded: the next link through the driver records the job again. An
|
||||
* entry without a job, a link the driver cannot hand to one ld.lld job,
|
||||
* stays. */
|
||||
private fresh(entry: LinkerJobEntry | null): LinkerJobEntry | null {
|
||||
return entry !== null && entry.job !== null && !stillMatches(entry.inputs) ? null : entry;
|
||||
}
|
||||
|
||||
private read(key: string, paths: number): LinkerJobEntry | null {
|
||||
const bytes = this.cache.read("linker-job", key, MAX_ENTRY_BYTES);
|
||||
if (bytes === null) return null;
|
||||
@@ -231,7 +240,7 @@ export class LinkerJobs {
|
||||
try {
|
||||
const roots = [resolve(stage), realpathSync(stage)];
|
||||
const key = this.key(args, paths, roots);
|
||||
if (key !== null && this.read(key, paths.length) === null)
|
||||
if (key !== null && this.fresh(this.read(key, paths.length)) === null)
|
||||
this.store(key, output, paths, roots, driverInputs);
|
||||
} catch {
|
||||
/* The next link records it. */
|
||||
|
||||
Reference in New Issue
Block a user