mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
* Make native installs portable and smaller - Support glibc 2.34 with portable compiler binaries and static LLVM helpers. - Bundle the host runtime and discover optional cross-target packs from project dependencies. - Add application build benchmarks covering latency, executable size, and memory. * Keep bootstrap tools available in Sandbox validation - Isolate Zig for native compiler bootstrap and glibc entropy contracts. - Preserve the existing toolchain scope for cache and corpus tests. * Fix portable compiler bootstrap linking - Keep native support objects compatible with the destination host linker. - Resolve installed targets using the host declared by the toolchain manifest.
70 lines
4.4 KiB
JavaScript
70 lines
4.4 KiB
JavaScript
/** Exercise the packed npm command and its bundled host assets in isolation. */
|
|
import assert from "node:assert/strict";
|
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
import { cpSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { prepareNativeCommand } from "../packages/cli/scripts/prepare-native.mjs";
|
|
|
|
const repository = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const platform = resolve(process.argv[2]);
|
|
const identity = JSON.parse(readFileSync(join(platform, "package.json"), "utf8"));
|
|
const root = mkdtempSync(join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-npm-smoke-"));
|
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
const env = { ...process.env, npm_config_cache: join(root, "npm-cache"), npm_config_update_notifier: "false", NODE_PATH: "" };
|
|
function command(executable, args, cwd, environment = env) {
|
|
return execFileSync(executable, args, { cwd, env: environment, encoding: "utf8", shell: process.platform === "win32" && executable.endsWith(".cmd"), maxBuffer: 16 * 1024 * 1024 });
|
|
}
|
|
function pack(directory) {
|
|
const result = JSON.parse(command(npm, ["pack", "--ignore-scripts", "--json", "--pack-destination", root], directory));
|
|
return join(root, result[0].filename);
|
|
}
|
|
function bytes(directory) {
|
|
let size = 0;
|
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
if (entry.isSymbolicLink()) continue;
|
|
const path = join(directory, entry.name);
|
|
size += entry.isDirectory() ? bytes(path) : statSync(path).size;
|
|
}
|
|
return size;
|
|
}
|
|
try {
|
|
const platformTarball = pack(platform);
|
|
const wrapper = join(root, "wrapper");
|
|
mkdirSync(wrapper);
|
|
cpSync(join(repository, "packages/cli/scripts"), join(wrapper, "scripts"), { recursive: true });
|
|
const manifest = JSON.parse(readFileSync(join(repository, "packages/cli/package.json"), "utf8"));
|
|
assert.equal(manifest.version, identity.version);
|
|
assert.ok(Object.keys(manifest.optionalDependencies).every(name => name.startsWith("@scriptc/cli-")), "the default CLI must only depend on native host packages");
|
|
manifest.optionalDependencies = Object.fromEntries(Object.keys(manifest.optionalDependencies).map(name => [name, name === identity.name ? "file:" + platformTarball : identity.version]));
|
|
delete manifest.devDependencies;
|
|
delete manifest.scripts.prepack;
|
|
writeFileSync(join(wrapper, "package.json"), JSON.stringify(manifest));
|
|
prepareNativeCommand(wrapper);
|
|
const tarball = pack(wrapper);
|
|
const project = join(root, "project");
|
|
mkdirSync(project);
|
|
command(npm, ["install", "--offline", "--no-audit", "--no-fund", tarball], project);
|
|
const executable = join(project, "node_modules/scriptc/bin/scriptc.exe");
|
|
assert.equal(command(executable, ["--version"], project, { ...env, PATH: "" }).trim(), identity.version);
|
|
const entry = join(project, "hello.ts");
|
|
writeFileSync(entry, 'console.log("Hello, world!");\n');
|
|
const binary = join(project, process.platform === "win32" ? "hello.exe" : "hello");
|
|
const compileEnv = { ...env, SCRIPTC_CACHE_DIR: join(root, "build-cache") };
|
|
command(executable, ["build", entry, "-o", binary], project, compileEnv);
|
|
const result = spawnSync(binary, [], { cwd: project, env: { PATH: "" }, encoding: "utf8" });
|
|
assert.equal(result.status, 0, result.stderr);
|
|
assert.equal(result.stdout, "Hello, world!\n");
|
|
assert.equal(result.stderr, "");
|
|
const helloBytes = statSync(binary).size;
|
|
writeFileSync(entry, 'import { randomBytes, randomUUID } from "node:crypto";\nconst bytes = randomBytes(32);\nconsole.log(bytes.length === 32, randomUUID().length === 36, Math.random() >= 0);\n');
|
|
command(executable, ["build", entry, "-o", binary], project, compileEnv);
|
|
assert.equal(command(binary, [], project, { PATH: "" }), "true true true\n");
|
|
const installed = readdirSync(join(project, "node_modules/@scriptc"));
|
|
assert.deepEqual(installed, [identity.name.slice("@scriptc/".length)]);
|
|
console.log(JSON.stringify({ package: identity.name, version: identity.version, installed_packages: ["scriptc", ...installed], installed_file_bytes: bytes(join(project, "node_modules")), hello_executable_bytes: helloBytes, crypto_executable_bytes: statSync(binary).size, output: result.stdout }));
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|