@gjsify/gtk-runtime-darwin-arm64 0.26.0 → 0.27.0
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
CHANGED
|
@@ -8,8 +8,16 @@ installed on the host — Phase 2 of cross-platform node-gi.
|
|
|
8
8
|
Platform-gated (`os: ["darwin"]`, `cpu: ["arm64"]`), tier 3 (experimental). On any
|
|
9
9
|
other platform npm skips it and `@gjsify/node-gi` falls back to a system/Homebrew
|
|
10
10
|
GTK. The heavy `gtk/` payload is **not committed** — it is built on a macOS CI
|
|
11
|
-
runner (`scripts/build-gtk-runtime.mjs
|
|
12
|
-
staged into node-gi's
|
|
11
|
+
runner ([`../scripts/build-gtk-runtime-darwin.mjs`](../scripts/build-gtk-runtime-darwin.mjs))
|
|
12
|
+
and shipped via the package tarball / staged into node-gi's
|
|
13
|
+
`prebuilds/darwin-arm64/gtk/`.
|
|
14
|
+
|
|
15
|
+
The Intel sibling [`@gjsify/gtk-runtime-darwin-x64`](../gtk-runtime-darwin-x64)
|
|
16
|
+
shares that ONE builder: nothing in the relocation pass is arch-dependent, so the
|
|
17
|
+
target is derived from the running Node (`darwin-${process.arch}`) and `--out` is
|
|
18
|
+
cross-checked against the destination package's own `os`/`cpu`. Neither package
|
|
19
|
+
ships a copy of the script; `gtk/manifest.json` records its repo path under
|
|
20
|
+
`builder` so a consumer holding only the tarball can find the recipe.
|
|
13
21
|
|
|
14
22
|
## Layout
|
|
15
23
|
|
|
@@ -22,8 +30,8 @@ gtk/
|
|
|
22
30
|
|
|
23
31
|
## How it is built (the relocation, the crux)
|
|
24
32
|
|
|
25
|
-
`node scripts/build-gtk-runtime.mjs` runs on a macOS runner **with** a
|
|
26
|
-
Homebrew GTK stack (the closure *source*, not shipped):
|
|
33
|
+
`node ../scripts/build-gtk-runtime-darwin.mjs` runs on a macOS runner **with** a
|
|
34
|
+
build-time Homebrew GTK stack (the closure *source*, not shipped):
|
|
27
35
|
|
|
28
36
|
1. **Collect** — walk the dylib graph with `otool -L`, recursively, from the
|
|
29
37
|
typelib-backing libraries the display-free conformance loads
|
|
@@ -34,13 +42,20 @@ Homebrew GTK stack (the closure *source*, not shipped):
|
|
|
34
42
|
sibling reference to `@loader_path/<leaf>` with `install_name_tool`, then
|
|
35
43
|
**ad-hoc re-sign** it (`codesign --force --sign -`). Re-signing is mandatory
|
|
36
44
|
on Apple silicon: `install_name_tool` invalidates the code signature and dyld
|
|
37
|
-
refuses to load a mis-signed dylib.
|
|
38
|
-
|
|
39
|
-
3. **
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
refuses to load a mis-signed dylib. On Intel the signature is not enforced, but
|
|
46
|
+
re-signing keeps ONE code path.
|
|
47
|
+
3. **Verify** — re-read every relocated image and fail on any absolute reference to
|
|
48
|
+
a library the bundle *does* carry. The predicate is `$(brew --prefix)`-**derived**,
|
|
49
|
+
not the literal `/opt/homebrew`: that literal is vacuously absent on an Intel
|
|
50
|
+
runner (prefix `/usr/local`), so a hardcoded grep would have passed while proving
|
|
51
|
+
nothing. OS-provided libraries the bundle deliberately leaves alone are reported,
|
|
52
|
+
never failed.
|
|
53
|
+
4. **Typelibs** — copy the typelib set into `gtk/girepository-1.0`.
|
|
54
|
+
5. **Addon (optional, `--addon`)** — relocate a *copy* of the node-gi addon
|
|
55
|
+
(`node_gi.node`): rewrite its Homebrew-prefix refs to `@rpath/<leaf>` and add
|
|
56
|
+
`@loader_path/gtk/lib` as an rpath, so the addon loads the **bundled**
|
|
57
|
+
`libgirepository` with no Homebrew. This is the env-free path, and it gets the
|
|
58
|
+
same verification as the dylibs.
|
|
44
59
|
|
|
45
60
|
## How node-gi finds it
|
|
46
61
|
|
|
@@ -73,18 +88,20 @@ batteries-included install, and nothing elsewhere.
|
|
|
73
88
|
|
|
74
89
|
## Release flow — how the `gtk/` bundle reaches npm
|
|
75
90
|
|
|
76
|
-
The `gtk/` payload is gitignored and built ONLY on macOS
|
|
77
|
-
|
|
91
|
+
The `gtk/` payload is gitignored and built ONLY on macOS, so it cannot be produced
|
|
92
|
+
by the main (ubuntu) release publish. A dedicated macOS job in
|
|
78
93
|
[`.github/workflows/release.yml`](../../../.github/workflows/release.yml) —
|
|
79
|
-
`publish-gtk-runtime-darwin
|
|
94
|
+
`publish-gtk-runtime-darwin`, a two-leg matrix over `arm64` (`macos-latest`) and
|
|
95
|
+
`x64` (`macos-15-intel`) — owns both darwin packages' publish end to end:
|
|
80
96
|
|
|
81
97
|
1. On `release: published` it checks out the tag (the version `@release-it/bumper`
|
|
82
98
|
already bumped in this package's `package.json`, in lockstep with the train).
|
|
83
99
|
2. `brew install`s the GTK/GI stack as the build-time closure **source**.
|
|
84
|
-
3. Runs
|
|
85
|
-
dylibs + typelibs + `manifest.json`)
|
|
86
|
-
|
|
87
|
-
[`index.js`](./index.js)'s `isPresent`
|
|
100
|
+
3. Runs `../scripts/build-gtk-runtime-darwin.mjs --out gtk` to populate `gtk/`
|
|
101
|
+
(relocated dylibs + typelibs + `manifest.json`) — the script itself fails on a
|
|
102
|
+
surviving Homebrew-prefix reference — then asserts that `gtk/lib` +
|
|
103
|
+
`gtk/girepository-1.0` exist (the two dirs [`index.js`](./index.js)'s `isPresent`
|
|
104
|
+
gate checks).
|
|
88
105
|
4. OIDC-publishes **only this package** (Trusted Publisher configured for
|
|
89
106
|
`release.yml`), so `files: ["gtk"]` ships the whole bundle recursively (the
|
|
90
107
|
gjsify packer expands a plain-directory `files` entry). Consumers then see
|
|
@@ -98,9 +115,12 @@ flags of the build script are a *separate* node-gi concern (see
|
|
|
98
115
|
[`node-gi.yml`](../../../.github/workflows/node-gi.yml) → `macos-gtk-runtime`) and
|
|
99
116
|
are **not** part of this tarball.
|
|
100
117
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
This was the PATTERN the two siblings now follow:
|
|
119
|
+
[`@gjsify/gtk-runtime-win32-x64`](../gtk-runtime-win32-x64) has its own
|
|
120
|
+
`windows-latest` publish job (gvsbuild closure source, no relocation step — Windows
|
|
121
|
+
resolves DLLs by search path), and
|
|
122
|
+
[`@gjsify/gtk-runtime-darwin-x64`](../gtk-runtime-darwin-x64) is a second leg of
|
|
123
|
+
*this* job, since it shares this package's builder outright.
|
|
104
124
|
|
|
105
125
|
## Scope & what a full windowing bundle still needs
|
|
106
126
|
|
|
Binary file
|
|
Binary file
|
package/gtk/manifest.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"platform": "darwin-arm64",
|
|
3
3
|
"windowing": false,
|
|
4
|
+
"builder": "packages/node-gi/scripts/build-gtk-runtime-darwin.mjs",
|
|
4
5
|
"generatedFrom": "/opt/homebrew",
|
|
5
6
|
"dylibs": 45,
|
|
6
7
|
"typelibs": 38,
|
|
7
8
|
"libBytes": 29101712,
|
|
8
|
-
"typelibBytes":
|
|
9
|
+
"typelibBytes": 2344980,
|
|
9
10
|
"dataBytes": 0,
|
|
10
|
-
"totalBytes":
|
|
11
|
+
"totalBytes": 31446692,
|
|
11
12
|
"dylibList": [
|
|
12
13
|
"libX11.6.dylib",
|
|
13
14
|
"libXau.6.dylib",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/gtk-runtime-darwin-arm64",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Batteries-included, relocated GTK/GObject-Introspection runtime bundle for macOS arm64 — lets @gjsify/node-gi load gi:// namespaces (GLib/GObject/Gio/cairo/Pango/Graphene/Gdk) with NO Homebrew GTK. Platform-gated (darwin/arm64 only).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"index.js",
|
|
25
25
|
"index.d.ts",
|
|
26
|
-
"scripts",
|
|
27
26
|
"gtk"
|
|
28
27
|
],
|
|
29
28
|
"gjsify": {
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"tier": 3
|
|
37
36
|
},
|
|
38
37
|
"scripts": {
|
|
39
|
-
"build:bundle": "node scripts/build-gtk-runtime.mjs"
|
|
38
|
+
"build:bundle": "node ../scripts/build-gtk-runtime-darwin.mjs"
|
|
40
39
|
},
|
|
41
40
|
"repository": {
|
|
42
41
|
"type": "git",
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// Collect + relocate a standalone, batteries-included GTK/GObject-Introspection
|
|
3
|
-
// runtime bundle for macOS arm64 — so @gjsify/node-gi's display-free conformance
|
|
4
|
-
// runs with NO Homebrew GTK on the host (Phase 2 of cross-platform node-gi).
|
|
5
|
-
//
|
|
6
|
-
// node scripts/build-gtk-runtime.mjs [--out <dir>] [--addon <node_gi.node>] [--stage <dir>]
|
|
7
|
-
//
|
|
8
|
-
// Runs ONLY on darwin/arm64 with a build-time Homebrew GTK stack installed (the
|
|
9
|
-
// closure SOURCE — not shipped). It:
|
|
10
|
-
// 1. Walks the dylib graph (`otool -L`, recursively) from the typelib-backing
|
|
11
|
-
// libraries the conformance loads (glib/gobject/gio + girepository + cairo +
|
|
12
|
-
// pango + graphene + gdk-pixbuf + gtk4), collecting every Homebrew dylib.
|
|
13
|
-
// 2. Copies them flat into <out>/lib and RELOCATES each: rewrites its own id +
|
|
14
|
-
// every sibling reference to `@loader_path/<leaf>` (via install_name_tool),
|
|
15
|
-
// leaving /usr/lib + /System refs untouched, then AD-HOC RE-SIGNS it
|
|
16
|
-
// (`codesign -s -`) — mandatory on Apple silicon: install_name_tool
|
|
17
|
-
// invalidates the code signature and dyld refuses an unsigned/mis-signed
|
|
18
|
-
// dylib.
|
|
19
|
-
// 3. Copies the typelib set into <out>/girepository-1.0.
|
|
20
|
-
// 4. (optional) Relocates a COPY of the node-gi addon (--addon) so it loads the
|
|
21
|
-
// BUNDLED libgirepository via `@rpath` (add_rpath @loader_path/gtk/lib) with
|
|
22
|
-
// NO Homebrew — the env-free path the core conformance leg exercises.
|
|
23
|
-
//
|
|
24
|
-
// The result is portable: none of the bundled libraries reference /opt/homebrew.
|
|
25
|
-
// Reference: GJS ships no relocation; the technique mirrors macOS app-bundle
|
|
26
|
-
// dylib fix-up (install_name_tool + @loader_path + ad-hoc codesign).
|
|
27
|
-
import { execFileSync } from 'node:child_process';
|
|
28
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
29
|
-
import { basename, dirname, join } from 'node:path';
|
|
30
|
-
import { fileURLToPath } from 'node:url';
|
|
31
|
-
|
|
32
|
-
const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
33
|
-
|
|
34
|
-
// --- args ------------------------------------------------------------------
|
|
35
|
-
function argValue(flag) {
|
|
36
|
-
const i = process.argv.indexOf(flag);
|
|
37
|
-
return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : undefined;
|
|
38
|
-
}
|
|
39
|
-
const OUT = argValue('--out') ?? join(pkgRoot, 'gtk');
|
|
40
|
-
const ADDON = argValue('--addon'); // optional: a node_gi.node to relocate a copy of
|
|
41
|
-
const STAGE = argValue('--stage'); // optional: sibling layout <stage>/{node_gi.node,gtk/}
|
|
42
|
-
|
|
43
|
-
if (process.platform !== 'darwin' || process.arch !== 'arm64') {
|
|
44
|
-
console.error(`build-gtk-runtime: only supported on darwin/arm64, not ${process.platform}/${process.arch}`);
|
|
45
|
-
process.exit(2);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const sh = (bin, args) => execFileSync(bin, args, { encoding: 'utf8' });
|
|
49
|
-
|
|
50
|
-
const brewPrefix = sh('brew', ['--prefix']).trim();
|
|
51
|
-
const brewLib = join(brewPrefix, 'lib');
|
|
52
|
-
const brewTypelibs = join(brewLib, 'girepository-1.0');
|
|
53
|
-
if (!existsSync(brewLib)) {
|
|
54
|
-
console.error(`build-gtk-runtime: ${brewLib} not found — install the GTK stack first (brew install gtk4 …)`);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// The typelib-backing libraries the DISPLAY-FREE conformance closure loads.
|
|
59
|
-
// Version-agnostic base patterns matched against $(brew --prefix)/lib. The
|
|
60
|
-
// recursive otool walk pulls every transitive Homebrew dep (harfbuzz, fribidi,
|
|
61
|
-
// fontconfig, freetype, pixman, png, intl, pcre2, ffi, epoxy, …).
|
|
62
|
-
const SEED_PATTERNS = [
|
|
63
|
-
/^libgirepository-2\.0\..*\.dylib$/, // GIRepository (merged into glib on modern brew)
|
|
64
|
-
/^libglib-2\.0\..*\.dylib$/,
|
|
65
|
-
/^libgobject-2\.0\..*\.dylib$/,
|
|
66
|
-
/^libgio-2\.0\..*\.dylib$/,
|
|
67
|
-
/^libgmodule-2\.0\..*\.dylib$/,
|
|
68
|
-
/^libcairo(-gobject|-script-interpreter)?\.[\d.]*dylib$/,
|
|
69
|
-
/^libpango-1\.0\..*\.dylib$/,
|
|
70
|
-
/^libpangocairo-1\.0\..*\.dylib$/,
|
|
71
|
-
/^libpangoft2-1\.0\..*\.dylib$/,
|
|
72
|
-
/^libgraphene-1\.0\..*\.dylib$/,
|
|
73
|
-
/^libgdk_pixbuf-2\.0\..*\.dylib$/,
|
|
74
|
-
/^libgtk-4\..*\.dylib$/, // provides the Gdk typelib's backing library
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
// WINDOWING superset (opt-in via --windowing): also bundle libadwaita, whose dylib
|
|
78
|
-
// backs the Adw-1 typelib, AND libgtksourceview-5, whose dylib backs the GtkSource-5
|
|
79
|
-
// typelib. The display-free conformance closure never touches Adwaita OR GtkSource,
|
|
80
|
-
// so they are NOT base seeds — but the batteries-included bundle ships their TYPELIBS,
|
|
81
|
-
// so without the dylibs a real `new Adw.Application()` / `new GtkSource.View()` fails
|
|
82
|
-
// with "Failed to load shared library '…'" (→ the type is not a constructible
|
|
83
|
-
// GObject). libgtksourceview backs the Learn6502 editor (a GtkSource.View subclass) —
|
|
84
|
-
// the app-gnome node-gi port. This is exactly what the macOS GTK-GUI proof
|
|
85
|
-
// (macos-gtk-windowing) needs; the recursive otool walk pulls each seed's transitive
|
|
86
|
-
// deps + relocates them like any other seed. Mirrors the win32 --windowing superset
|
|
87
|
-
// (WINDOWING_SEED_PATTERNS there). NB: GtkSource's runtime DATA (language-specs /
|
|
88
|
-
// styles under share/gtksourceview-5) is a separate concern; the darwin bundle has no
|
|
89
|
-
// share/ DATA step yet, so GtkSource CONSTRUCTS here but syntax-highlighting language
|
|
90
|
-
// files are not bundled (tracked follow-up; the win32 bundle DOES ship them).
|
|
91
|
-
const WINDOWING = process.argv.includes('--windowing');
|
|
92
|
-
const WINDOWING_SEED_PATTERNS = [/^libadwaita-1\..*\.dylib$/, /^libgtksourceview-5\..*\.dylib$/];
|
|
93
|
-
|
|
94
|
-
function isSystemPath(p) {
|
|
95
|
-
return p.startsWith('/usr/lib/') || p.startsWith('/System/');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Parse an `otool -L <lib>` output into the list of dependency install paths
|
|
99
|
-
// (skipping the first line — the library's own id — and system libraries).
|
|
100
|
-
function otoolDeps(libPath) {
|
|
101
|
-
let out;
|
|
102
|
-
try {
|
|
103
|
-
out = sh('otool', ['-L', libPath]);
|
|
104
|
-
} catch {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
const lines = out.split('\n').slice(1); // line 0 is the id, not a dep
|
|
108
|
-
const deps = [];
|
|
109
|
-
for (const line of lines) {
|
|
110
|
-
const trimmed = line.trim();
|
|
111
|
-
if (!trimmed) continue;
|
|
112
|
-
const path = trimmed.split(' (')[0];
|
|
113
|
-
if (!path || isSystemPath(path)) continue;
|
|
114
|
-
deps.push(path);
|
|
115
|
-
}
|
|
116
|
-
return deps;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Resolve a dependency's leaf to the real Homebrew file (following symlinks),
|
|
120
|
-
// or null when it is not a Homebrew library we bundle.
|
|
121
|
-
function resolveInBrew(leaf) {
|
|
122
|
-
const candidate = join(brewLib, leaf);
|
|
123
|
-
if (!existsSync(candidate)) return null;
|
|
124
|
-
try {
|
|
125
|
-
return realpathSync(candidate);
|
|
126
|
-
} catch {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// --- 1. discover the closure ----------------------------------------------
|
|
132
|
-
console.log(`build-gtk-runtime: brew prefix ${brewPrefix}${WINDOWING ? ' (windowing superset — + libadwaita + libgtksourceview)' : ' (display-free)'}`);
|
|
133
|
-
const seedPatterns = WINDOWING ? [...SEED_PATTERNS, ...WINDOWING_SEED_PATTERNS] : SEED_PATTERNS;
|
|
134
|
-
const seeds = readdirSync(brewLib).filter((f) => seedPatterns.some((re) => re.test(f)));
|
|
135
|
-
if (seeds.length === 0) {
|
|
136
|
-
console.error('build-gtk-runtime: no seed libraries found — is the GTK stack installed?');
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
console.log(`build-gtk-runtime: ${seeds.length} seed libraries: ${seeds.join(', ')}`);
|
|
140
|
-
|
|
141
|
-
const bundled = new Map(); // leaf -> real source path
|
|
142
|
-
const queue = [...seeds];
|
|
143
|
-
while (queue.length) {
|
|
144
|
-
const leaf = queue.shift();
|
|
145
|
-
if (bundled.has(leaf)) continue;
|
|
146
|
-
const real = resolveInBrew(leaf);
|
|
147
|
-
if (!real) continue; // system / non-brew dep — leave as-is
|
|
148
|
-
bundled.set(leaf, real);
|
|
149
|
-
for (const dep of otoolDeps(real)) {
|
|
150
|
-
const depLeaf = basename(dep);
|
|
151
|
-
if (!bundled.has(depLeaf)) queue.push(depLeaf);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
console.log(`build-gtk-runtime: closure = ${bundled.size} dylibs`);
|
|
155
|
-
|
|
156
|
-
// --- 2. copy + relocate + re-sign -----------------------------------------
|
|
157
|
-
const libOut = join(OUT, 'lib');
|
|
158
|
-
rmSync(OUT, { recursive: true, force: true });
|
|
159
|
-
mkdirSync(libOut, { recursive: true });
|
|
160
|
-
|
|
161
|
-
for (const [leaf, src] of bundled) {
|
|
162
|
-
copyFileSync(src, join(libOut, leaf));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const bundledLeaves = new Set(bundled.keys());
|
|
166
|
-
function relocate(libPath, { id } = {}) {
|
|
167
|
-
// Own id (dylibs only).
|
|
168
|
-
if (id) execFileSync('install_name_tool', ['-id', `@loader_path/${basename(libPath)}`, libPath]);
|
|
169
|
-
// Rewrite every dependency that points at a library WE bundle → @loader_path.
|
|
170
|
-
for (const dep of otoolDeps(libPath)) {
|
|
171
|
-
if (bundledLeaves.has(basename(dep))) {
|
|
172
|
-
execFileSync('install_name_tool', ['-change', dep, `@loader_path/${basename(dep)}`, libPath]);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
// Ad-hoc re-sign — install_name_tool invalidated the signature (arm64 hard req).
|
|
176
|
-
execFileSync('codesign', ['--force', '--sign', '-', libPath]);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const leaf of bundledLeaves) {
|
|
180
|
-
relocate(join(libOut, leaf), { id: true });
|
|
181
|
-
}
|
|
182
|
-
console.log(`build-gtk-runtime: relocated + re-signed ${bundledLeaves.size} dylibs → @loader_path`);
|
|
183
|
-
|
|
184
|
-
// --- 3. typelibs -----------------------------------------------------------
|
|
185
|
-
const typelibOut = join(OUT, 'girepository-1.0');
|
|
186
|
-
mkdirSync(typelibOut, { recursive: true });
|
|
187
|
-
let typelibCount = 0;
|
|
188
|
-
if (existsSync(brewTypelibs)) {
|
|
189
|
-
for (const f of readdirSync(brewTypelibs)) {
|
|
190
|
-
if (f.endsWith('.typelib')) {
|
|
191
|
-
copyFileSync(join(brewTypelibs, f), join(typelibOut, f));
|
|
192
|
-
typelibCount++;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
console.log(`build-gtk-runtime: copied ${typelibCount} typelibs`);
|
|
197
|
-
|
|
198
|
-
// --- 3b. WINDOWING data (schemas / icons / gtksource) ---------------------
|
|
199
|
-
// The runtime DATA a REAL app needs beyond the dylibs+typelibs: compiled GSettings
|
|
200
|
-
// schemas (Gio.Settings — a HARD startup blocker without them), the Adwaita/hicolor
|
|
201
|
-
// icon themes, and GtkSource's language-specs/styles. These are plain files (no dylib
|
|
202
|
-
// relocation), located at runtime via GSETTINGS_SCHEMA_DIR / XDG_DATA_DIRS (node-gi's
|
|
203
|
-
// gtk-runtime.js maybeWireGtkWindowingEnv keys on the gschemas.compiled marker). Gated
|
|
204
|
-
// on --windowing; the display-free bundle is byte-unchanged. NB the gdk-pixbuf image
|
|
205
|
-
// LOADERS (needed to render SVG symbolic icons) are NOT bundled yet — they are dylibs
|
|
206
|
-
// that need @loader_path relocation from a NESTED dir (unlike win32's plain DLL copy);
|
|
207
|
-
// tracked follow-up, so symbolic icons may be blank until then.
|
|
208
|
-
const windowing = { schemas: false, iconThemes: [], gtksource: false };
|
|
209
|
-
if (WINDOWING) {
|
|
210
|
-
const brewShare = join(brewPrefix, 'share');
|
|
211
|
-
const findTool = (leaf) => {
|
|
212
|
-
const inBrew = join(brewPrefix, 'bin', leaf);
|
|
213
|
-
return existsSync(inBrew) ? inBrew : leaf; // fall back to PATH
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
// 3b-a. Compiled GSettings schemas + (re)compile gschemas.compiled — also the
|
|
217
|
-
// windowing-data marker node-gi's loader detects.
|
|
218
|
-
const schemasSrc = join(brewShare, 'glib-2.0', 'schemas');
|
|
219
|
-
if (existsSync(schemasSrc)) {
|
|
220
|
-
const schemasOut = join(OUT, 'share', 'glib-2.0', 'schemas');
|
|
221
|
-
mkdirSync(schemasOut, { recursive: true });
|
|
222
|
-
for (const f of readdirSync(schemasSrc)) {
|
|
223
|
-
if (f.endsWith('.xml') || f.endsWith('.gschema.override') || f === 'gschemas.compiled') {
|
|
224
|
-
copyFileSync(join(schemasSrc, f), join(schemasOut, f));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
try {
|
|
228
|
-
execFileSync(findTool('glib-compile-schemas'), [schemasOut]);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
console.warn(`build-gtk-runtime: WARNING — glib-compile-schemas failed: ${err?.message ?? err}`);
|
|
231
|
-
}
|
|
232
|
-
windowing.schemas = existsSync(join(schemasOut, 'gschemas.compiled'));
|
|
233
|
-
console.log(`build-gtk-runtime: GSettings schemas ${windowing.schemas ? 'compiled' : 'copied (no gschemas.compiled!)'}`);
|
|
234
|
-
} else {
|
|
235
|
-
console.warn(`build-gtk-runtime: WARNING — ${schemasSrc} missing; GSettings schemas NOT bundled (Gio.Settings will fail)`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 3b-b. Icon themes (Adwaita symbolic + hicolor) + caches, loaded from
|
|
239
|
-
// XDG_DATA_DIRS/icons/<theme>/.
|
|
240
|
-
const updateIconCache = findTool('gtk4-update-icon-cache');
|
|
241
|
-
for (const theme of ['Adwaita', 'hicolor']) {
|
|
242
|
-
const themeSrc = join(brewShare, 'icons', theme);
|
|
243
|
-
if (!existsSync(themeSrc)) continue;
|
|
244
|
-
cpSync(themeSrc, join(OUT, 'share', 'icons', theme), { recursive: true });
|
|
245
|
-
try {
|
|
246
|
-
execFileSync(updateIconCache, ['-q', '-t', '-f', join(OUT, 'share', 'icons', theme)]);
|
|
247
|
-
} catch {
|
|
248
|
-
// an existing icon-theme.cache from the copy is a usable fallback
|
|
249
|
-
}
|
|
250
|
-
windowing.iconThemes.push(theme);
|
|
251
|
-
}
|
|
252
|
-
console.log(
|
|
253
|
-
windowing.iconThemes.length
|
|
254
|
-
? `build-gtk-runtime: icon themes ${windowing.iconThemes.join(', ')}`
|
|
255
|
-
: 'build-gtk-runtime: WARNING — no Adwaita/hicolor icon theme under share/icons',
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// 3b-c. GtkSource-5 language-specs + styles (the editor's syntax highlighting).
|
|
259
|
-
const gtksourceSrc = join(brewShare, 'gtksourceview-5');
|
|
260
|
-
if (existsSync(gtksourceSrc)) {
|
|
261
|
-
let copied = 0;
|
|
262
|
-
for (const sub of ['language-specs', 'styles']) {
|
|
263
|
-
const subSrc = join(gtksourceSrc, sub);
|
|
264
|
-
if (existsSync(subSrc)) {
|
|
265
|
-
cpSync(subSrc, join(OUT, 'share', 'gtksourceview-5', sub), { recursive: true });
|
|
266
|
-
copied += readdirSync(subSrc).length;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
windowing.gtksource = copied > 0;
|
|
270
|
-
console.log(`build-gtk-runtime: GtkSource-5 data ${windowing.gtksource ? `bundled (${copied} files)` : 'empty'}`);
|
|
271
|
-
} else {
|
|
272
|
-
console.warn(`build-gtk-runtime: WARNING — ${gtksourceSrc} missing; GtkSource-5 data NOT bundled`);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// --- 4. optional: relocate a copy of the node-gi addon --------------------
|
|
277
|
-
// The addon (built against Homebrew) carries absolute /opt/homebrew refs. Rewrite
|
|
278
|
-
// them to @rpath/<leaf> + add an rpath to the SIBLING bundle so it loads the
|
|
279
|
-
// bundled libgirepository with NO Homebrew — the env-free core-conformance path.
|
|
280
|
-
if (ADDON) {
|
|
281
|
-
if (!existsSync(ADDON)) {
|
|
282
|
-
console.error(`build-gtk-runtime: --addon ${ADDON} not found`);
|
|
283
|
-
process.exit(1);
|
|
284
|
-
}
|
|
285
|
-
const stageDir = STAGE ?? join(OUT, '..', 'staged');
|
|
286
|
-
mkdirSync(stageDir, { recursive: true });
|
|
287
|
-
const addonDest = join(stageDir, 'node_gi.node');
|
|
288
|
-
copyFileSync(ADDON, addonDest);
|
|
289
|
-
// Point the addon at the bundle staged as its sibling `gtk/`.
|
|
290
|
-
cpSync(OUT, join(stageDir, 'gtk'), { recursive: true });
|
|
291
|
-
|
|
292
|
-
for (const dep of otoolDeps(addonDest)) {
|
|
293
|
-
// Any non-system dep (Homebrew absolute path OR @rpath leaf we bundle).
|
|
294
|
-
if (bundledLeaves.has(basename(dep)) || dep.startsWith(brewPrefix) || dep.startsWith('/usr/local/')) {
|
|
295
|
-
execFileSync('install_name_tool', ['-change', dep, `@rpath/${basename(dep)}`, addonDest]);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
try {
|
|
299
|
-
execFileSync('install_name_tool', ['-add_rpath', '@loader_path/gtk/lib', addonDest]);
|
|
300
|
-
} catch {
|
|
301
|
-
// rpath already present — fine.
|
|
302
|
-
}
|
|
303
|
-
execFileSync('codesign', ['--force', '--sign', '-', addonDest]);
|
|
304
|
-
console.log(`build-gtk-runtime: relocated addon → ${addonDest} (rpath @loader_path/gtk/lib)`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// --- manifest + size -------------------------------------------------------
|
|
308
|
-
function dirSize(dir) {
|
|
309
|
-
let total = 0;
|
|
310
|
-
for (const f of readdirSync(dir, { withFileTypes: true })) {
|
|
311
|
-
const p = join(dir, f.name);
|
|
312
|
-
total += f.isDirectory() ? dirSize(p) : statSync(p).size;
|
|
313
|
-
}
|
|
314
|
-
return total;
|
|
315
|
-
}
|
|
316
|
-
const libBytes = dirSize(libOut);
|
|
317
|
-
const typelibBytes = dirSize(typelibOut);
|
|
318
|
-
const shareOut = join(OUT, 'share');
|
|
319
|
-
const dataBytes = WINDOWING && existsSync(shareOut) ? dirSize(shareOut) : 0;
|
|
320
|
-
const manifest = {
|
|
321
|
-
platform: 'darwin-arm64',
|
|
322
|
-
windowing: WINDOWING,
|
|
323
|
-
generatedFrom: brewPrefix,
|
|
324
|
-
dylibs: bundledLeaves.size,
|
|
325
|
-
typelibs: typelibCount,
|
|
326
|
-
libBytes,
|
|
327
|
-
typelibBytes,
|
|
328
|
-
dataBytes,
|
|
329
|
-
totalBytes: libBytes + typelibBytes + dataBytes,
|
|
330
|
-
dylibList: [...bundledLeaves].sort(),
|
|
331
|
-
...(WINDOWING ? { windowingData: windowing } : {}),
|
|
332
|
-
};
|
|
333
|
-
writeFileSync(join(OUT, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
334
|
-
|
|
335
|
-
const mb = (n) => (n / 1024 / 1024).toFixed(1);
|
|
336
|
-
console.log(
|
|
337
|
-
`build-gtk-runtime: DONE → ${OUT}\n` +
|
|
338
|
-
` dylibs: ${bundledLeaves.size} (${mb(libBytes)} MiB)\n` +
|
|
339
|
-
` typelibs: ${typelibCount} (${mb(typelibBytes)} MiB)\n` +
|
|
340
|
-
` total: ${mb(manifest.totalBytes)} MiB`,
|
|
341
|
-
);
|