224 lines
5.3 KiB
JavaScript
224 lines
5.3 KiB
JavaScript
import { execFile } from "node:child_process";
|
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { arch, platform } from "node:os";
|
|
import { join, relative, resolve } from "node:path";
|
|
import { promisify } from "node:util";
|
|
|
|
import {
|
|
generateLargeFixture,
|
|
LARGE_FIXTURE_COUNT,
|
|
sha256,
|
|
sourceDigest,
|
|
} from "./lib/conformance-fixtures.mjs";
|
|
|
|
const exec = promisify(execFile);
|
|
const root = process.cwd();
|
|
const sourceRoot = join(root, "tests/conformance");
|
|
const outputRoot = resolve(
|
|
process.env.REA_CONFORMANCE_BUILD ?? join(root, "build/conformance"),
|
|
);
|
|
if (platform() !== "darwin")
|
|
throw new Error(
|
|
"Mach-O conformance fixtures require macOS and Xcode command-line tools",
|
|
);
|
|
await rm(outputRoot, { recursive: true, force: true });
|
|
await mkdir(outputRoot, { recursive: true });
|
|
|
|
const clang = (await exec("xcrun", ["--find", "clang"])).stdout.trim();
|
|
const clangVersion = (await exec(clang, ["--version"])).stdout.split("\n")[0];
|
|
const sdkPath = (await exec("xcrun", ["--show-sdk-path"])).stdout.trim();
|
|
const architecture = arch();
|
|
const common = [
|
|
"-arch",
|
|
architecture,
|
|
"-isysroot",
|
|
sdkPath,
|
|
"-O0",
|
|
"-g",
|
|
"-fno-inline",
|
|
];
|
|
const fixtures = [];
|
|
|
|
await build(
|
|
"c",
|
|
["c/fixture.c"],
|
|
[...common, join(sourceRoot, "c/fixture.c"), "-o", join(outputRoot, "c")],
|
|
{
|
|
expectations: {
|
|
symbols: ["_rea_entry", "_rea_branch", "_rea_leaf", "_rea_c_global"],
|
|
strings: ["REA_C_ENTRY", "REA_C_LEAF"],
|
|
},
|
|
},
|
|
);
|
|
await build(
|
|
"version-v1",
|
|
["versions/v1.c"],
|
|
[
|
|
...common,
|
|
join(sourceRoot, "versions/v1.c"),
|
|
"-o",
|
|
join(outputRoot, "version-v1"),
|
|
],
|
|
{
|
|
expectations: {
|
|
symbols: ["_rea_version_entry", "_rea_version_leaf"],
|
|
strings: ["REA_VERSION_ONE"],
|
|
},
|
|
},
|
|
);
|
|
await build(
|
|
"version-v2",
|
|
["versions/v2.c"],
|
|
[
|
|
...common,
|
|
join(sourceRoot, "versions/v2.c"),
|
|
"-o",
|
|
join(outputRoot, "version-v2"),
|
|
],
|
|
{
|
|
expectations: {
|
|
symbols: ["_rea_version_entry", "_rea_version_leaf", "_rea_added"],
|
|
strings: ["REA_VERSION_TWO"],
|
|
},
|
|
},
|
|
);
|
|
|
|
const largeSource = generateLargeFixture();
|
|
const largePath = join(outputRoot, "large.c");
|
|
await writeFile(largePath, largeSource);
|
|
await build(
|
|
"large",
|
|
[{ path: "generated/large.c", content: largeSource }],
|
|
[...common, largePath, "-o", join(outputRoot, "large")],
|
|
{
|
|
expectations: {
|
|
symbolPrefix: "_rea_page_",
|
|
symbolCount: LARGE_FIXTURE_COUNT,
|
|
stringPrefix: "REA_PAGE_",
|
|
stringCount: LARGE_FIXTURE_COUNT,
|
|
},
|
|
},
|
|
);
|
|
|
|
await build(
|
|
"objc",
|
|
["objc/fixture.m"],
|
|
[
|
|
...common,
|
|
"-fobjc-arc",
|
|
join(sourceRoot, "objc/fixture.m"),
|
|
"-framework",
|
|
"Foundation",
|
|
"-o",
|
|
join(outputRoot, "objc"),
|
|
],
|
|
{
|
|
expectations: {
|
|
symbols: ["_OBJC_CLASS_$_REAWidget"],
|
|
strings: ["REAWidget", "REAWidgetDelegate", "performAction:error:"],
|
|
},
|
|
},
|
|
);
|
|
await build(
|
|
"napi",
|
|
["napi/fixture.c"],
|
|
[
|
|
...common,
|
|
"-bundle",
|
|
"-undefined",
|
|
"dynamic_lookup",
|
|
join(sourceRoot, "napi/fixture.c"),
|
|
"-o",
|
|
join(outputRoot, "fixture.node"),
|
|
],
|
|
{
|
|
expectations: {
|
|
symbols: ["_napi_register_module_v1"],
|
|
strings: ["reaProbeOne", "reaProbeTwo", "reaProbeThree"],
|
|
},
|
|
},
|
|
);
|
|
|
|
try {
|
|
const swiftc = (await exec("xcrun", ["--find", "swiftc"])).stdout.trim();
|
|
const swiftVersion = (await exec(swiftc, ["--version"])).stdout.split(
|
|
"\n",
|
|
)[0];
|
|
await build(
|
|
"swift",
|
|
["swift/fixture.swift"],
|
|
[
|
|
"-sdk",
|
|
sdkPath,
|
|
"-Onone",
|
|
"-g",
|
|
join(sourceRoot, "swift/fixture.swift"),
|
|
"-o",
|
|
join(outputRoot, "swift"),
|
|
],
|
|
{
|
|
expectations: {
|
|
strings: [
|
|
"REA_SWIFT_EXECUTE",
|
|
"REAService",
|
|
"REARecord",
|
|
"REAState",
|
|
"REAProtocol",
|
|
],
|
|
},
|
|
compiler: swiftc,
|
|
compilerVersion: swiftVersion,
|
|
},
|
|
);
|
|
} catch (cause) {
|
|
process.stderr.write(
|
|
`Skipping optional Swift fixture: ${cause instanceof Error ? cause.message : String(cause)}\n`,
|
|
);
|
|
}
|
|
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
platform: platform(),
|
|
architecture,
|
|
generatedAt: new Date(0).toISOString(),
|
|
fixtures,
|
|
};
|
|
await writeFile(
|
|
join(outputRoot, "manifest.json"),
|
|
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
);
|
|
process.stdout.write(
|
|
`${JSON.stringify({ outputRoot, fixtureCount: fixtures.length })}\n`,
|
|
);
|
|
|
|
async function build(name, sources, args, options) {
|
|
const compiler = options.compiler ?? clang;
|
|
const compilerVersion = options.compilerVersion ?? clangVersion;
|
|
const normalized = await Promise.all(
|
|
sources.map(async (source) => {
|
|
if (typeof source !== "string") return source;
|
|
return {
|
|
path: source,
|
|
content: await readFile(join(sourceRoot, source), "utf8"),
|
|
};
|
|
}),
|
|
);
|
|
await exec(compiler, args, { maxBuffer: 10 * 1024 * 1024 });
|
|
const artifactPath = args.at(-1);
|
|
if (typeof artifactPath !== "string")
|
|
throw new Error(`Missing output for ${name}`);
|
|
const artifact = await readFile(artifactPath);
|
|
fixtures.push({
|
|
name,
|
|
artifact: relative(outputRoot, artifactPath),
|
|
artifactSha256: sha256(artifact),
|
|
sourceSha256: sourceDigest(normalized),
|
|
sources: normalized.map(({ path, content }) => ({
|
|
path,
|
|
sha256: sha256(content),
|
|
})),
|
|
compiler: { path: compiler, version: compilerVersion, arguments: args },
|
|
expectations: options.expectations,
|
|
});
|
|
}
|