@h-rig/native-toolchain-plugin 0.0.0-e2e-live.20260630085347
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/src/git-binary.d.ts +2 -0
- package/dist/src/git-binary.js +221 -0
- package/dist/src/plugin.d.ts +4 -0
- package/dist/src/plugin.js +270 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @h-rig/native-toolchain-plugin
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/native-toolchain-plugin/src/git-binary.ts
|
|
3
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, renameSync, rmSync } from "fs";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { basename, dirname, extname, resolve } from "path";
|
|
6
|
+
var sharedNativeOutputDir = resolve(tmpdir(), "rig-native");
|
|
7
|
+
function isWindowsTarget(target) {
|
|
8
|
+
const platform = target?.platform ?? process.platform;
|
|
9
|
+
return platform === "win32" || platform === "windows" || target?.triple?.includes("windows") === true;
|
|
10
|
+
}
|
|
11
|
+
function targetPlatform(target) {
|
|
12
|
+
return target?.platform || process.platform;
|
|
13
|
+
}
|
|
14
|
+
function targetArch(target) {
|
|
15
|
+
return target?.arch || process.arch;
|
|
16
|
+
}
|
|
17
|
+
function nativeBuildManifestPath(outputPath) {
|
|
18
|
+
return `${outputPath}.build-manifest.json`;
|
|
19
|
+
}
|
|
20
|
+
function temporaryOutputPath(outputPath, name = basename(outputPath)) {
|
|
21
|
+
const suffix = extname(name);
|
|
22
|
+
const stem = suffix ? name.slice(0, -suffix.length) : name;
|
|
23
|
+
return resolve(dirname(outputPath), `.${stem}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix}`);
|
|
24
|
+
}
|
|
25
|
+
function publishNativeBinary(tempOutputPath, outputPath) {
|
|
26
|
+
try {
|
|
27
|
+
renameSync(tempOutputPath, outputPath);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (process.platform === "win32" && existsSync(outputPath)) {
|
|
30
|
+
rmSync(outputPath, { force: true });
|
|
31
|
+
renameSync(tempOutputPath, outputPath);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function sha256File(path) {
|
|
38
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
39
|
+
hasher.update(await Bun.file(path).arrayBuffer());
|
|
40
|
+
return hasher.digest("hex");
|
|
41
|
+
}
|
|
42
|
+
async function hasMatchingNativeBuildManifest(manifestPath, buildKey) {
|
|
43
|
+
if (!existsSync(manifestPath))
|
|
44
|
+
return false;
|
|
45
|
+
try {
|
|
46
|
+
const manifest = await Bun.file(manifestPath).json();
|
|
47
|
+
return manifest.version === 1 && manifest.buildKey === buildKey;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function artifact(name, path, kind, target) {
|
|
53
|
+
const base = { name, path, kind };
|
|
54
|
+
return target ? { ...base, target } : base;
|
|
55
|
+
}
|
|
56
|
+
function envKey(name) {
|
|
57
|
+
return name.replace(/\.exe$/i, "").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
58
|
+
}
|
|
59
|
+
function nativePackageBinaryCandidates(fromDir, fileName, target) {
|
|
60
|
+
const candidates = [];
|
|
61
|
+
let cursor = resolve(fromDir);
|
|
62
|
+
const tag = `${targetPlatform(target)}-${targetArch(target)}`;
|
|
63
|
+
for (let index = 0;index < 8; index += 1) {
|
|
64
|
+
candidates.push(resolve(cursor, "native", tag, fileName), resolve(cursor, "native", tag, "bin", fileName), resolve(cursor, "native", fileName), resolve(cursor, "native", "bin", fileName));
|
|
65
|
+
const parent = dirname(cursor);
|
|
66
|
+
if (parent === cursor)
|
|
67
|
+
break;
|
|
68
|
+
cursor = parent;
|
|
69
|
+
}
|
|
70
|
+
return candidates;
|
|
71
|
+
}
|
|
72
|
+
async function copyMaterializedBinary(input) {
|
|
73
|
+
mkdirSync(dirname(input.targetPath), { recursive: true });
|
|
74
|
+
const sourceDigest = await sha256File(input.sourcePath);
|
|
75
|
+
const buildKey = JSON.stringify({ version: 1, sourcePath: input.sourcePath, sourceDigest });
|
|
76
|
+
const manifestPath = nativeBuildManifestPath(input.targetPath);
|
|
77
|
+
const needsCopy = !existsSync(input.targetPath) || !await hasMatchingNativeBuildManifest(manifestPath, buildKey);
|
|
78
|
+
if (needsCopy) {
|
|
79
|
+
copyFileSync(input.sourcePath, input.targetPath);
|
|
80
|
+
if (input.kind === "executable")
|
|
81
|
+
chmodSync(input.targetPath, 493);
|
|
82
|
+
await Bun.write(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
|
|
83
|
+
`);
|
|
84
|
+
} else if (input.kind === "executable") {
|
|
85
|
+
chmodSync(input.targetPath, 493);
|
|
86
|
+
}
|
|
87
|
+
return artifact(input.name, input.targetPath, input.kind, input.target);
|
|
88
|
+
}
|
|
89
|
+
function nativeBinaryFileName(name, kind, target) {
|
|
90
|
+
if (kind === "executable" && isWindowsTarget(target) && !name.toLowerCase().endsWith(".exe")) {
|
|
91
|
+
return `${name}.exe`;
|
|
92
|
+
}
|
|
93
|
+
return name;
|
|
94
|
+
}
|
|
95
|
+
function inferBinaryKind(nameOrPath) {
|
|
96
|
+
const lower = nameOrPath.toLowerCase();
|
|
97
|
+
return lower.endsWith(".so") || lower.endsWith(".dylib") || lower.endsWith(".dll") ? "dynamic-library" : "executable";
|
|
98
|
+
}
|
|
99
|
+
function nativeBinaryCandidates(name, target) {
|
|
100
|
+
const key = envKey(name);
|
|
101
|
+
const kind = inferBinaryKind(name);
|
|
102
|
+
const fileName = nativeBinaryFileName(name, kind, target);
|
|
103
|
+
const execDir = process.execPath?.trim() ? dirname(process.execPath.trim()) : "";
|
|
104
|
+
return [...new Set([
|
|
105
|
+
process.env[`RIG_NATIVE_${key}_BIN`]?.trim() || "",
|
|
106
|
+
...nativePackageBinaryCandidates(import.meta.dir, fileName, target),
|
|
107
|
+
execDir ? resolve(execDir, fileName) : "",
|
|
108
|
+
execDir ? resolve(execDir, "..", fileName) : "",
|
|
109
|
+
execDir ? resolve(execDir, "..", "bin", fileName) : "",
|
|
110
|
+
resolve(sharedNativeOutputDir, `${name}-${targetPlatform(target)}-${targetArch(target)}${kind === "executable" && isWindowsTarget(target) ? ".exe" : ""}`)
|
|
111
|
+
].filter(Boolean))];
|
|
112
|
+
}
|
|
113
|
+
function resolveNativeBinaryPath(name, target) {
|
|
114
|
+
for (const candidate of nativeBinaryCandidates(name, target)) {
|
|
115
|
+
if (candidate && existsSync(candidate))
|
|
116
|
+
return candidate;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function resolveNativeSourcePath(name) {
|
|
121
|
+
const value = process.env[`RIG_NATIVE_${envKey(name)}_SOURCE`]?.trim() || "";
|
|
122
|
+
return value && existsSync(value) ? value : null;
|
|
123
|
+
}
|
|
124
|
+
function genericBuildCommand(input, tempOutputPath, zigBinary) {
|
|
125
|
+
const mode = input.kind === "dynamic-library" ? "build-lib" : "build-exe";
|
|
126
|
+
const defines = Object.entries(input.defines ?? {}).flatMap(([key, value]) => [`-D${key}=${value}`]);
|
|
127
|
+
return [
|
|
128
|
+
zigBinary,
|
|
129
|
+
mode,
|
|
130
|
+
input.sourcePath,
|
|
131
|
+
...input.kind === "dynamic-library" ? ["-dynamic"] : [],
|
|
132
|
+
"-O",
|
|
133
|
+
"ReleaseFast",
|
|
134
|
+
...input.target?.triple ? ["-target", input.target.triple] : [],
|
|
135
|
+
...input.buildArgs ?? [],
|
|
136
|
+
...defines,
|
|
137
|
+
`-femit-bin=${tempOutputPath}`
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
async function buildNativeBinary(input) {
|
|
141
|
+
if (!existsSync(input.sourcePath))
|
|
142
|
+
throw new Error(`Native source file not found: ${input.sourcePath}`);
|
|
143
|
+
const zigBinary = Bun.which("zig");
|
|
144
|
+
if (!zigBinary)
|
|
145
|
+
throw new Error("zig is required to build native binaries.");
|
|
146
|
+
mkdirSync(dirname(input.outputPath), { recursive: true });
|
|
147
|
+
const sourceDigest = await sha256File(input.sourcePath);
|
|
148
|
+
const buildKey = JSON.stringify({
|
|
149
|
+
version: 1,
|
|
150
|
+
zigBinary,
|
|
151
|
+
name: input.name,
|
|
152
|
+
kind: input.kind,
|
|
153
|
+
platform: targetPlatform(input.target),
|
|
154
|
+
arch: targetArch(input.target),
|
|
155
|
+
sourcePath: input.sourcePath,
|
|
156
|
+
sourceDigest,
|
|
157
|
+
buildArgs: input.buildArgs ?? [],
|
|
158
|
+
defines: input.defines ?? {},
|
|
159
|
+
triple: input.target?.triple ?? ""
|
|
160
|
+
});
|
|
161
|
+
const manifestPath = nativeBuildManifestPath(input.outputPath);
|
|
162
|
+
if (existsSync(input.outputPath) && await hasMatchingNativeBuildManifest(manifestPath, buildKey)) {
|
|
163
|
+
if (input.kind === "executable")
|
|
164
|
+
chmodSync(input.outputPath, 493);
|
|
165
|
+
return artifact(input.name, input.outputPath, input.kind, input.target);
|
|
166
|
+
}
|
|
167
|
+
const tempOutputPath = temporaryOutputPath(input.outputPath);
|
|
168
|
+
const build = Bun.spawn(genericBuildCommand(input, tempOutputPath, zigBinary), {
|
|
169
|
+
cwd: dirname(input.sourcePath),
|
|
170
|
+
stdout: "pipe",
|
|
171
|
+
stderr: "pipe"
|
|
172
|
+
});
|
|
173
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
174
|
+
build.exited,
|
|
175
|
+
new Response(build.stdout).text(),
|
|
176
|
+
new Response(build.stderr).text()
|
|
177
|
+
]);
|
|
178
|
+
if (exitCode !== 0 || !existsSync(tempOutputPath)) {
|
|
179
|
+
const details = [stderr.trim(), stdout.trim()].filter(Boolean).join(`
|
|
180
|
+
`);
|
|
181
|
+
throw new Error(`Failed to build native binary ${input.name}: ${details || `zig exited with code ${exitCode}`}`);
|
|
182
|
+
}
|
|
183
|
+
if (input.kind === "executable")
|
|
184
|
+
chmodSync(tempOutputPath, 493);
|
|
185
|
+
publishNativeBinary(tempOutputPath, input.outputPath);
|
|
186
|
+
await Bun.write(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
|
|
187
|
+
`);
|
|
188
|
+
return artifact(input.name, input.outputPath, input.kind, input.target);
|
|
189
|
+
}
|
|
190
|
+
var binaryBuildService = {
|
|
191
|
+
buildNativeBinary,
|
|
192
|
+
async materializeNativeBinary(input) {
|
|
193
|
+
const kind = inferBinaryKind(input.name);
|
|
194
|
+
const targetPath = resolve(input.targetDir, nativeBinaryFileName(input.name, kind, input.target));
|
|
195
|
+
const sourcePath = resolveNativeBinaryPath(input.name, input.target);
|
|
196
|
+
if (sourcePath) {
|
|
197
|
+
return copyMaterializedBinary({
|
|
198
|
+
name: input.name,
|
|
199
|
+
sourcePath,
|
|
200
|
+
targetPath,
|
|
201
|
+
kind,
|
|
202
|
+
...input.target ? { target: input.target } : {}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const source = resolveNativeSourcePath(input.name);
|
|
206
|
+
if (source) {
|
|
207
|
+
return buildNativeBinary({
|
|
208
|
+
projectRoot: input.projectRoot,
|
|
209
|
+
name: input.name,
|
|
210
|
+
sourcePath: source,
|
|
211
|
+
outputPath: targetPath,
|
|
212
|
+
kind,
|
|
213
|
+
...input.target ? { target: input.target } : {}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
throw new Error(`Native binary ${input.name} was not found; set RIG_NATIVE_${envKey(input.name)}_BIN or RIG_NATIVE_${envKey(input.name)}_SOURCE.`);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
export {
|
|
220
|
+
binaryBuildService
|
|
221
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const NATIVE_TOOLCHAIN_PLUGIN_NAME = "@rig/native-toolchain-plugin";
|
|
2
|
+
export declare const nativeToolchainPlugin: import("@rig/core/config").RigPlugin;
|
|
3
|
+
export declare function createNativeToolchainPlugin(): import("@rig/core/config").RigPlugin;
|
|
4
|
+
export default nativeToolchainPlugin;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// packages/native-toolchain-plugin/src/git-binary.ts
|
|
19
|
+
var exports_git_binary = {};
|
|
20
|
+
__export(exports_git_binary, {
|
|
21
|
+
binaryBuildService: () => binaryBuildService
|
|
22
|
+
});
|
|
23
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, renameSync, rmSync } from "fs";
|
|
24
|
+
import { tmpdir } from "os";
|
|
25
|
+
import { basename, dirname, extname, resolve } from "path";
|
|
26
|
+
function isWindowsTarget(target) {
|
|
27
|
+
const platform = target?.platform ?? process.platform;
|
|
28
|
+
return platform === "win32" || platform === "windows" || target?.triple?.includes("windows") === true;
|
|
29
|
+
}
|
|
30
|
+
function targetPlatform(target) {
|
|
31
|
+
return target?.platform || process.platform;
|
|
32
|
+
}
|
|
33
|
+
function targetArch(target) {
|
|
34
|
+
return target?.arch || process.arch;
|
|
35
|
+
}
|
|
36
|
+
function nativeBuildManifestPath(outputPath) {
|
|
37
|
+
return `${outputPath}.build-manifest.json`;
|
|
38
|
+
}
|
|
39
|
+
function temporaryOutputPath(outputPath, name = basename(outputPath)) {
|
|
40
|
+
const suffix = extname(name);
|
|
41
|
+
const stem = suffix ? name.slice(0, -suffix.length) : name;
|
|
42
|
+
return resolve(dirname(outputPath), `.${stem}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix}`);
|
|
43
|
+
}
|
|
44
|
+
function publishNativeBinary(tempOutputPath, outputPath) {
|
|
45
|
+
try {
|
|
46
|
+
renameSync(tempOutputPath, outputPath);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (process.platform === "win32" && existsSync(outputPath)) {
|
|
49
|
+
rmSync(outputPath, { force: true });
|
|
50
|
+
renameSync(tempOutputPath, outputPath);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function sha256File(path) {
|
|
57
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
58
|
+
hasher.update(await Bun.file(path).arrayBuffer());
|
|
59
|
+
return hasher.digest("hex");
|
|
60
|
+
}
|
|
61
|
+
async function hasMatchingNativeBuildManifest(manifestPath, buildKey) {
|
|
62
|
+
if (!existsSync(manifestPath))
|
|
63
|
+
return false;
|
|
64
|
+
try {
|
|
65
|
+
const manifest = await Bun.file(manifestPath).json();
|
|
66
|
+
return manifest.version === 1 && manifest.buildKey === buildKey;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function artifact(name, path, kind, target) {
|
|
72
|
+
const base = { name, path, kind };
|
|
73
|
+
return target ? { ...base, target } : base;
|
|
74
|
+
}
|
|
75
|
+
function envKey(name) {
|
|
76
|
+
return name.replace(/\.exe$/i, "").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
77
|
+
}
|
|
78
|
+
function nativePackageBinaryCandidates(fromDir, fileName, target) {
|
|
79
|
+
const candidates = [];
|
|
80
|
+
let cursor = resolve(fromDir);
|
|
81
|
+
const tag = `${targetPlatform(target)}-${targetArch(target)}`;
|
|
82
|
+
for (let index = 0;index < 8; index += 1) {
|
|
83
|
+
candidates.push(resolve(cursor, "native", tag, fileName), resolve(cursor, "native", tag, "bin", fileName), resolve(cursor, "native", fileName), resolve(cursor, "native", "bin", fileName));
|
|
84
|
+
const parent = dirname(cursor);
|
|
85
|
+
if (parent === cursor)
|
|
86
|
+
break;
|
|
87
|
+
cursor = parent;
|
|
88
|
+
}
|
|
89
|
+
return candidates;
|
|
90
|
+
}
|
|
91
|
+
async function copyMaterializedBinary(input) {
|
|
92
|
+
mkdirSync(dirname(input.targetPath), { recursive: true });
|
|
93
|
+
const sourceDigest = await sha256File(input.sourcePath);
|
|
94
|
+
const buildKey = JSON.stringify({ version: 1, sourcePath: input.sourcePath, sourceDigest });
|
|
95
|
+
const manifestPath = nativeBuildManifestPath(input.targetPath);
|
|
96
|
+
const needsCopy = !existsSync(input.targetPath) || !await hasMatchingNativeBuildManifest(manifestPath, buildKey);
|
|
97
|
+
if (needsCopy) {
|
|
98
|
+
copyFileSync(input.sourcePath, input.targetPath);
|
|
99
|
+
if (input.kind === "executable")
|
|
100
|
+
chmodSync(input.targetPath, 493);
|
|
101
|
+
await Bun.write(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
|
|
102
|
+
`);
|
|
103
|
+
} else if (input.kind === "executable") {
|
|
104
|
+
chmodSync(input.targetPath, 493);
|
|
105
|
+
}
|
|
106
|
+
return artifact(input.name, input.targetPath, input.kind, input.target);
|
|
107
|
+
}
|
|
108
|
+
function nativeBinaryFileName(name, kind, target) {
|
|
109
|
+
if (kind === "executable" && isWindowsTarget(target) && !name.toLowerCase().endsWith(".exe")) {
|
|
110
|
+
return `${name}.exe`;
|
|
111
|
+
}
|
|
112
|
+
return name;
|
|
113
|
+
}
|
|
114
|
+
function inferBinaryKind(nameOrPath) {
|
|
115
|
+
const lower = nameOrPath.toLowerCase();
|
|
116
|
+
return lower.endsWith(".so") || lower.endsWith(".dylib") || lower.endsWith(".dll") ? "dynamic-library" : "executable";
|
|
117
|
+
}
|
|
118
|
+
function nativeBinaryCandidates(name, target) {
|
|
119
|
+
const key = envKey(name);
|
|
120
|
+
const kind = inferBinaryKind(name);
|
|
121
|
+
const fileName = nativeBinaryFileName(name, kind, target);
|
|
122
|
+
const execDir = process.execPath?.trim() ? dirname(process.execPath.trim()) : "";
|
|
123
|
+
return [...new Set([
|
|
124
|
+
process.env[`RIG_NATIVE_${key}_BIN`]?.trim() || "",
|
|
125
|
+
...nativePackageBinaryCandidates(import.meta.dir, fileName, target),
|
|
126
|
+
execDir ? resolve(execDir, fileName) : "",
|
|
127
|
+
execDir ? resolve(execDir, "..", fileName) : "",
|
|
128
|
+
execDir ? resolve(execDir, "..", "bin", fileName) : "",
|
|
129
|
+
resolve(sharedNativeOutputDir, `${name}-${targetPlatform(target)}-${targetArch(target)}${kind === "executable" && isWindowsTarget(target) ? ".exe" : ""}`)
|
|
130
|
+
].filter(Boolean))];
|
|
131
|
+
}
|
|
132
|
+
function resolveNativeBinaryPath(name, target) {
|
|
133
|
+
for (const candidate of nativeBinaryCandidates(name, target)) {
|
|
134
|
+
if (candidate && existsSync(candidate))
|
|
135
|
+
return candidate;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
function resolveNativeSourcePath(name) {
|
|
140
|
+
const value = process.env[`RIG_NATIVE_${envKey(name)}_SOURCE`]?.trim() || "";
|
|
141
|
+
return value && existsSync(value) ? value : null;
|
|
142
|
+
}
|
|
143
|
+
function genericBuildCommand(input, tempOutputPath, zigBinary) {
|
|
144
|
+
const mode = input.kind === "dynamic-library" ? "build-lib" : "build-exe";
|
|
145
|
+
const defines = Object.entries(input.defines ?? {}).flatMap(([key, value]) => [`-D${key}=${value}`]);
|
|
146
|
+
return [
|
|
147
|
+
zigBinary,
|
|
148
|
+
mode,
|
|
149
|
+
input.sourcePath,
|
|
150
|
+
...input.kind === "dynamic-library" ? ["-dynamic"] : [],
|
|
151
|
+
"-O",
|
|
152
|
+
"ReleaseFast",
|
|
153
|
+
...input.target?.triple ? ["-target", input.target.triple] : [],
|
|
154
|
+
...input.buildArgs ?? [],
|
|
155
|
+
...defines,
|
|
156
|
+
`-femit-bin=${tempOutputPath}`
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
async function buildNativeBinary(input) {
|
|
160
|
+
if (!existsSync(input.sourcePath))
|
|
161
|
+
throw new Error(`Native source file not found: ${input.sourcePath}`);
|
|
162
|
+
const zigBinary = Bun.which("zig");
|
|
163
|
+
if (!zigBinary)
|
|
164
|
+
throw new Error("zig is required to build native binaries.");
|
|
165
|
+
mkdirSync(dirname(input.outputPath), { recursive: true });
|
|
166
|
+
const sourceDigest = await sha256File(input.sourcePath);
|
|
167
|
+
const buildKey = JSON.stringify({
|
|
168
|
+
version: 1,
|
|
169
|
+
zigBinary,
|
|
170
|
+
name: input.name,
|
|
171
|
+
kind: input.kind,
|
|
172
|
+
platform: targetPlatform(input.target),
|
|
173
|
+
arch: targetArch(input.target),
|
|
174
|
+
sourcePath: input.sourcePath,
|
|
175
|
+
sourceDigest,
|
|
176
|
+
buildArgs: input.buildArgs ?? [],
|
|
177
|
+
defines: input.defines ?? {},
|
|
178
|
+
triple: input.target?.triple ?? ""
|
|
179
|
+
});
|
|
180
|
+
const manifestPath = nativeBuildManifestPath(input.outputPath);
|
|
181
|
+
if (existsSync(input.outputPath) && await hasMatchingNativeBuildManifest(manifestPath, buildKey)) {
|
|
182
|
+
if (input.kind === "executable")
|
|
183
|
+
chmodSync(input.outputPath, 493);
|
|
184
|
+
return artifact(input.name, input.outputPath, input.kind, input.target);
|
|
185
|
+
}
|
|
186
|
+
const tempOutputPath = temporaryOutputPath(input.outputPath);
|
|
187
|
+
const build = Bun.spawn(genericBuildCommand(input, tempOutputPath, zigBinary), {
|
|
188
|
+
cwd: dirname(input.sourcePath),
|
|
189
|
+
stdout: "pipe",
|
|
190
|
+
stderr: "pipe"
|
|
191
|
+
});
|
|
192
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
193
|
+
build.exited,
|
|
194
|
+
new Response(build.stdout).text(),
|
|
195
|
+
new Response(build.stderr).text()
|
|
196
|
+
]);
|
|
197
|
+
if (exitCode !== 0 || !existsSync(tempOutputPath)) {
|
|
198
|
+
const details = [stderr.trim(), stdout.trim()].filter(Boolean).join(`
|
|
199
|
+
`);
|
|
200
|
+
throw new Error(`Failed to build native binary ${input.name}: ${details || `zig exited with code ${exitCode}`}`);
|
|
201
|
+
}
|
|
202
|
+
if (input.kind === "executable")
|
|
203
|
+
chmodSync(tempOutputPath, 493);
|
|
204
|
+
publishNativeBinary(tempOutputPath, input.outputPath);
|
|
205
|
+
await Bun.write(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
|
|
206
|
+
`);
|
|
207
|
+
return artifact(input.name, input.outputPath, input.kind, input.target);
|
|
208
|
+
}
|
|
209
|
+
var sharedNativeOutputDir, binaryBuildService;
|
|
210
|
+
var init_git_binary = __esm(() => {
|
|
211
|
+
sharedNativeOutputDir = resolve(tmpdir(), "rig-native");
|
|
212
|
+
binaryBuildService = {
|
|
213
|
+
buildNativeBinary,
|
|
214
|
+
async materializeNativeBinary(input) {
|
|
215
|
+
const kind = inferBinaryKind(input.name);
|
|
216
|
+
const targetPath = resolve(input.targetDir, nativeBinaryFileName(input.name, kind, input.target));
|
|
217
|
+
const sourcePath = resolveNativeBinaryPath(input.name, input.target);
|
|
218
|
+
if (sourcePath) {
|
|
219
|
+
return copyMaterializedBinary({
|
|
220
|
+
name: input.name,
|
|
221
|
+
sourcePath,
|
|
222
|
+
targetPath,
|
|
223
|
+
kind,
|
|
224
|
+
...input.target ? { target: input.target } : {}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const source = resolveNativeSourcePath(input.name);
|
|
228
|
+
if (source) {
|
|
229
|
+
return buildNativeBinary({
|
|
230
|
+
projectRoot: input.projectRoot,
|
|
231
|
+
name: input.name,
|
|
232
|
+
sourcePath: source,
|
|
233
|
+
outputPath: targetPath,
|
|
234
|
+
kind,
|
|
235
|
+
...input.target ? { target: input.target } : {}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
throw new Error(`Native binary ${input.name} was not found; set RIG_NATIVE_${envKey(input.name)}_BIN or RIG_NATIVE_${envKey(input.name)}_SOURCE.`);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// packages/native-toolchain-plugin/src/plugin.ts
|
|
244
|
+
import { defineCapability } from "@rig/core/capability";
|
|
245
|
+
import { definePlugin } from "@rig/core/config";
|
|
246
|
+
import { NATIVE_TOOLCHAIN_BINARY_BUILD } from "@rig/contracts";
|
|
247
|
+
var NATIVE_TOOLCHAIN_PLUGIN_NAME = "@rig/native-toolchain-plugin";
|
|
248
|
+
var NativeToolchainBinaryBuildCap = defineCapability(NATIVE_TOOLCHAIN_BINARY_BUILD);
|
|
249
|
+
var nativeToolchainPlugin = definePlugin({
|
|
250
|
+
name: NATIVE_TOOLCHAIN_PLUGIN_NAME,
|
|
251
|
+
version: "0.0.0-alpha.1",
|
|
252
|
+
contributes: {
|
|
253
|
+
capabilities: [
|
|
254
|
+
NativeToolchainBinaryBuildCap.provide(async () => (await Promise.resolve().then(() => (init_git_binary(), exports_git_binary))).binaryBuildService, {
|
|
255
|
+
title: "Native binary build/materialization",
|
|
256
|
+
description: "Build or materialize Rig-owned native binaries without repo/task semantics."
|
|
257
|
+
})
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
function createNativeToolchainPlugin() {
|
|
262
|
+
return nativeToolchainPlugin;
|
|
263
|
+
}
|
|
264
|
+
var plugin_default = nativeToolchainPlugin;
|
|
265
|
+
export {
|
|
266
|
+
nativeToolchainPlugin,
|
|
267
|
+
plugin_default as default,
|
|
268
|
+
createNativeToolchainPlugin,
|
|
269
|
+
NATIVE_TOOLCHAIN_PLUGIN_NAME
|
|
270
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h-rig/native-toolchain-plugin",
|
|
3
|
+
"version": "0.0.0-e2e-live.20260630085347",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "First-party mechanical native binary toolchain capability plugin for Rig.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/src/plugin.d.ts",
|
|
14
|
+
"import": "./dist/src/plugin.js"
|
|
15
|
+
},
|
|
16
|
+
"./plugin": {
|
|
17
|
+
"types": "./dist/src/plugin.d.ts",
|
|
18
|
+
"import": "./dist/src/plugin.js"
|
|
19
|
+
},
|
|
20
|
+
"./git-binary": {
|
|
21
|
+
"types": "./dist/src/git-binary.d.ts",
|
|
22
|
+
"import": "./dist/src/git-binary.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"bun": ">=1.3.11"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.0-e2e-live.20260630085347",
|
|
30
|
+
"@rig/core": "npm:@h-rig/core@0.0.0-e2e-live.20260630085347"
|
|
31
|
+
}
|
|
32
|
+
}
|