mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-22 05:59:31 +08:00
scr_runtime.h now includes 48 module headers (scr_core.h, scr_rc.h, scr_string.h, scr_dyn.h, scr_http.h, ...) in dependency order and holds no declarations. Each module header holds whole sections of the former file, includes the headers whose names it uses, and compiles on its own. Declarations, macros, struct layouts and inline bodies are unchanged. Five blocks move to join their module: the class-hierarchy vtable header (into scr_error.h, after the string core), the node:timers/promises pair (into scr_async.h), the JSON.parse entry points and the JSON output buffer (into scr_json.h, after the dyn declarations) and the EventTarget block (after scr_json.h). No moved block uses or declares a name that a block it moves past uses or declares. The string methods keep their position after the class objects in scr_string_methods.h, because their struct ScrDyn parameters rely on the file-scope struct ScrDyn declaration there. The runtime ABI scan reads scr_runtime.h with its module headers expanded in include order. A runtime test compiles every module header on its own. scripts/runtime-header-hunks.mjs retargets a diff of the single-file header onto the module headers.
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { expect, test } from "vitest";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const srcDir = join(import.meta.dirname, "../src");
|
|
|
|
/** The module headers scr_runtime.h includes, in include order. */
|
|
const MODULE_HEADERS = [
|
|
...readFileSync(join(srcDir, "scr_runtime.h"), "utf8").matchAll(/^#include "(scr_\w+\.h)"/gm),
|
|
].map((m) => m[1]!);
|
|
|
|
const DEFINE_SETS = [[], ["-DSCR_LIB"], ["-DSCR_LIB", "-DSCR_THREAD_INSTANCES"], ["-DSCR_WORKERS"]];
|
|
|
|
// Each module header includes what it uses, so a runtime unit can include
|
|
// only the headers it needs. A header that relies on an earlier one it does
|
|
// not include fails here; -Werror=visibility catches a `struct T *`
|
|
// parameter whose tag is not declared at file scope yet.
|
|
test("every module header compiles on its own", async () => {
|
|
expect(MODULE_HEADERS.length).toBeGreaterThan(40);
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-runtime-headers-"));
|
|
try {
|
|
const failures: string[] = [];
|
|
await Promise.all(
|
|
MODULE_HEADERS.map(async (header) => {
|
|
const source = join(dir, `${header}.c`);
|
|
await writeFile(source, `#include "${header}"\n`);
|
|
for (const defines of DEFINE_SETS) {
|
|
try {
|
|
await execFileAsync("clang", [
|
|
"-std=c11",
|
|
"-fsyntax-only",
|
|
"-Werror=visibility",
|
|
"-Werror=implicit-function-declaration",
|
|
...(process.platform === "linux" ? ["-D_GNU_SOURCE"] : []),
|
|
...defines,
|
|
"-I",
|
|
srcDir,
|
|
source,
|
|
]);
|
|
} catch (error) {
|
|
failures.push(
|
|
`${header} ${defines.join(" ")}: ${(error as { stderr?: string }).stderr}`,
|
|
);
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
expect(failures).toEqual([]);
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("scr_runtime.h holds no declarations of its own", () => {
|
|
const text = readFileSync(join(srcDir, "scr_runtime.h"), "utf8")
|
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.filter((line) => line !== "");
|
|
expect(text.filter((line) => !/^#(include "scr_\w+\.h"|ifndef|define|endif)/.test(line))).toEqual(
|
|
[],
|
|
);
|
|
});
|