Skip to content
Source
Sign inSign up

build-browser-secret.mjs

64 lines2.6 KB

1import * as NodeChildProcess from "node:child_process";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeURL from "node:url";
5import * as NodeUtil from "node:util";
6​
7// oxlint-disable-next-line t3code/no-global-process-runtime -- The native compiler targets the actual host; this script has no Effect runtime.
8const hostArch = process.arch;
9// oxlint-disable-next-line t3code/no-global-process-runtime -- Native compilation only runs on the actual Linux host.
10const hostPlatform = process.platform;
11​
12const { values } = NodeUtil.parseArgs({
13 options: { output: { type: "string" }, arch: { type: "string", default: hostArch } },
14});
15​
16if (hostPlatform === "linux") {
17 const machine = { x64: 62, arm64: 183 }[values.arch];
18 if (machine === undefined) throw new Error(`Unsupported Linux architecture: ${values.arch}`);
19 const root = NodeURL.fileURLToPath(new URL("../../../native/browser-secret/", import.meta.url));
20 const source = NodePath.resolve(root, "main.c");
21 const output = values.output ?? NodePath.resolve(root, "build", values.arch, "t3-browser-secret");
22 const matchesArchitecture = (file) => {
23 const header = NodeFS.readFileSync(file).subarray(0, 20);
24 return header.toString("hex", 0, 6) === "7f454c460201" && header.readUInt16LE(18) === machine;
25 };
26 let current = false;
27 try {
28 current =
29 NodeFS.statSync(output).mtimeMs >=
30 Math.max(
31 NodeFS.statSync(source).mtimeMs,
32 NodeFS.statSync(NodeURL.fileURLToPath(import.meta.url)).mtimeMs,
33 ) && matchesArchitecture(output);
34 } catch {}
35 if (!current) {
36 let flags;
37 try {
38 flags = NodeChildProcess.execFileSync("pkg-config", ["--cflags", "--libs", "libsecret-1"], {
39 encoding: "utf8",
40 })
41 .trim()
42 .split(/\s+/);
43 } catch (cause) {
44 throw new Error(
45 "Building the Linux browser import helper requires pkg-config and libsecret development headers (Ubuntu/Debian: libsecret-1-dev).",
46 { cause },
47 );
48 }
49 NodeFS.mkdirSync(NodePath.dirname(output), { recursive: true });
50 const temporary = `${output}.${process.pid}.tmp`;
51 try {
52 NodeChildProcess.execFileSync(
53 process.env.CC || "cc",
54 ["-std=c11", "-O2", "-Wall", "-Wextra", "-Werror", source, "-o", temporary, ...flags],
55 { stdio: "inherit" },
56 );
57 if (!matchesArchitecture(temporary))
58 throw new Error(`C compiler did not produce a Linux ${values.arch} executable.`);
59 NodeFS.renameSync(temporary, output);
60 } finally {
61 NodeFS.rmSync(temporary, { force: true });
62 }
63 }