@mcesystems/apple-kit 1.0.27 → 1.0.28
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/package.json +1 -1
- package/scripts/export-resources.ts +546 -301
package/package.json
CHANGED
|
@@ -17,15 +17,16 @@
|
|
|
17
17
|
* - Windows: Builds from source using MSYS2/MinGW (requires manual setup)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { exec } from "node:child_process";
|
|
20
|
+
import { exec, spawn } from "node:child_process";
|
|
21
21
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
chmodSync,
|
|
23
|
+
copyFileSync,
|
|
24
|
+
existsSync,
|
|
25
|
+
mkdirSync,
|
|
26
|
+
readFileSync,
|
|
27
|
+
readdirSync,
|
|
28
|
+
readlinkSync,
|
|
29
|
+
writeFileSync,
|
|
29
30
|
} from "node:fs";
|
|
30
31
|
import path from "node:path";
|
|
31
32
|
import { fileURLToPath } from "node:url";
|
|
@@ -43,21 +44,21 @@ const execAsync = promisify(exec);
|
|
|
43
44
|
|
|
44
45
|
// Tools we need to bundle
|
|
45
46
|
const REQUIRED_TOOLS = [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
"idevice_id",
|
|
48
|
+
"ideviceinfo",
|
|
49
|
+
"idevicepair",
|
|
50
|
+
"idevicename",
|
|
51
|
+
"idevicedebug",
|
|
52
|
+
"ideviceinstaller",
|
|
53
|
+
"iproxy",
|
|
53
54
|
];
|
|
54
55
|
|
|
55
56
|
// Optional tools (won't fail if not found)
|
|
56
57
|
const OPTIONAL_TOOLS = [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
"ideviceactivation",
|
|
59
|
+
"idevicesyslog",
|
|
60
|
+
"idevicescreenshot",
|
|
61
|
+
"idevicediagnostics",
|
|
61
62
|
];
|
|
62
63
|
|
|
63
64
|
// Homebrew paths on macOS
|
|
@@ -67,17 +68,27 @@ const HOMEBREW_INTEL_PATH = "/usr/local";
|
|
|
67
68
|
// MSYS2 paths on Windows
|
|
68
69
|
const MSYS2_DEFAULT_PATH = "C:\\msys64";
|
|
69
70
|
|
|
71
|
+
// System library prefixes that should NOT be copied (they exist on all macOS)
|
|
72
|
+
const SYSTEM_LIB_PREFIXES = [
|
|
73
|
+
"/System/Library/",
|
|
74
|
+
"/usr/lib/",
|
|
75
|
+
"/Library/Apple/",
|
|
76
|
+
];
|
|
77
|
+
|
|
70
78
|
// ============================================================================
|
|
71
79
|
// Utility Functions
|
|
72
80
|
// ============================================================================
|
|
73
81
|
|
|
74
82
|
function printUsage(): void {
|
|
75
|
-
|
|
83
|
+
console.log(`
|
|
76
84
|
Usage: npx tsx export-resources.ts <target-path>
|
|
77
85
|
|
|
78
86
|
Arguments:
|
|
79
87
|
target-path Directory where resources will be exported
|
|
80
88
|
|
|
89
|
+
Options:
|
|
90
|
+
--skip-verify Skip verification step
|
|
91
|
+
|
|
81
92
|
Examples:
|
|
82
93
|
npx tsx export-resources.ts ./my-app/resources/apple-kit
|
|
83
94
|
npx tsx export-resources.ts /absolute/path/to/resources
|
|
@@ -93,113 +104,277 @@ The script will create the following structure:
|
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
function resolveSymlink(filePath: string): string {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
try {
|
|
108
|
+
const linkTarget = readlinkSync(filePath);
|
|
109
|
+
if (path.isAbsolute(linkTarget)) {
|
|
110
|
+
return linkTarget;
|
|
111
|
+
}
|
|
112
|
+
return path.resolve(path.dirname(filePath), linkTarget);
|
|
113
|
+
} catch {
|
|
114
|
+
return filePath; // Not a symlink
|
|
115
|
+
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Check if a library path is a system library that doesn't need to be copied
|
|
120
|
+
*/
|
|
121
|
+
function isSystemLibrary(libPath: string): boolean {
|
|
122
|
+
return SYSTEM_LIB_PREFIXES.some((prefix) => libPath.startsWith(prefix));
|
|
110
123
|
}
|
|
111
124
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
} catch {
|
|
135
|
-
console.warn(`Warning: Could not get dependencies for ${binaryPath}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return dylibs;
|
|
125
|
+
/**
|
|
126
|
+
* Check if a library path needs to be bundled (is from homebrew or similar)
|
|
127
|
+
*/
|
|
128
|
+
function needsBundling(libPath: string): boolean {
|
|
129
|
+
if (isSystemLibrary(libPath)) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (libPath.startsWith("@")) {
|
|
133
|
+
// Already a relocatable path
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
// Bundle anything from /opt/homebrew, /usr/local/opt, or /usr/local/Cellar
|
|
137
|
+
return (
|
|
138
|
+
libPath.includes("/opt/homebrew") ||
|
|
139
|
+
libPath.includes("/usr/local/opt") ||
|
|
140
|
+
libPath.includes("/usr/local/Cellar") ||
|
|
141
|
+
libPath.includes("/usr/local/lib")
|
|
142
|
+
);
|
|
139
143
|
}
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
interface DylibDependency {
|
|
146
|
+
originalPath: string;
|
|
147
|
+
name: string;
|
|
148
|
+
realPath: string;
|
|
149
|
+
}
|
|
144
150
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Get all dylib dependencies for a binary, including the original path as referenced
|
|
153
|
+
*/
|
|
154
|
+
async function getDylibDependencies(binaryPath: string): Promise<DylibDependency[]> {
|
|
155
|
+
const dylibs: DylibDependency[] = [];
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const { stdout } = await execAsync(`otool -L "${binaryPath}"`);
|
|
159
|
+
const lines = stdout.split("\n");
|
|
160
|
+
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
// Match library paths (with or without version info in parentheses)
|
|
163
|
+
const match = line.match(/^\s+(.+?)\s+\(/);
|
|
164
|
+
if (match) {
|
|
165
|
+
const libPath = match[1].trim();
|
|
166
|
+
if (needsBundling(libPath)) {
|
|
167
|
+
const realPath = resolveSymlink(libPath);
|
|
168
|
+
dylibs.push({
|
|
169
|
+
originalPath: libPath,
|
|
170
|
+
name: path.basename(libPath),
|
|
171
|
+
realPath: existsSync(realPath) ? realPath : libPath,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.warn(`Warning: Could not get dependencies for ${binaryPath}: ${error}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return dylibs;
|
|
181
|
+
}
|
|
148
182
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Recursively collect ALL dylib dependencies
|
|
185
|
+
* Continues until no new dependencies are found
|
|
186
|
+
*/
|
|
187
|
+
async function collectAllDependencies(
|
|
188
|
+
initialBinaries: string[]
|
|
189
|
+
): Promise<Map<string, DylibDependency>> {
|
|
190
|
+
const allDylibs = new Map<string, DylibDependency>();
|
|
191
|
+
const processedPaths = new Set<string>();
|
|
192
|
+
const toProcess: string[] = [...initialBinaries];
|
|
193
|
+
|
|
194
|
+
console.log(" Scanning for dependencies...");
|
|
195
|
+
|
|
196
|
+
let iteration = 0;
|
|
197
|
+
while (toProcess.length > 0) {
|
|
198
|
+
iteration++;
|
|
199
|
+
const currentPath = toProcess.pop()!;
|
|
200
|
+
|
|
201
|
+
if (processedPaths.has(currentPath)) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
processedPaths.add(currentPath);
|
|
205
|
+
|
|
206
|
+
const deps = await getDylibDependencies(currentPath);
|
|
207
|
+
|
|
208
|
+
for (const dep of deps) {
|
|
209
|
+
if (!allDylibs.has(dep.name)) {
|
|
210
|
+
allDylibs.set(dep.name, dep);
|
|
211
|
+
// Add this dylib to be processed for its own dependencies
|
|
212
|
+
if (existsSync(dep.realPath)) {
|
|
213
|
+
toProcess.push(dep.realPath);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log(` Found ${allDylibs.size} unique dylib dependencies (${iteration} files scanned)`);
|
|
220
|
+
return allDylibs;
|
|
221
|
+
}
|
|
157
222
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Fix all library paths in a binary to use @loader_path
|
|
225
|
+
* This rewrites ALL non-system library references
|
|
226
|
+
*/
|
|
227
|
+
async function fixAllLibraryPaths(
|
|
228
|
+
binaryPath: string,
|
|
229
|
+
allDylibNames: Set<string>
|
|
230
|
+
): Promise<string[]> {
|
|
231
|
+
const unfixedPaths: string[] = [];
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const { stdout } = await execAsync(`otool -L "${binaryPath}"`);
|
|
235
|
+
const lines = stdout.split("\n");
|
|
236
|
+
|
|
237
|
+
for (const line of lines) {
|
|
238
|
+
const match = line.match(/^\s+(.+?)\s+\(/);
|
|
239
|
+
if (match) {
|
|
240
|
+
const libPath = match[1].trim();
|
|
241
|
+
|
|
242
|
+
// Skip system libraries and already-fixed paths
|
|
243
|
+
if (isSystemLibrary(libPath) || libPath.startsWith("@")) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const libName = path.basename(libPath);
|
|
248
|
+
|
|
249
|
+
// Check if we have this dylib in our collection
|
|
250
|
+
if (allDylibNames.has(libName)) {
|
|
251
|
+
try {
|
|
252
|
+
await execAsync(
|
|
253
|
+
`install_name_tool -change "${libPath}" "@loader_path/${libName}" "${binaryPath}"`
|
|
254
|
+
);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.warn(` Warning: Could not fix path ${libPath} in ${path.basename(binaryPath)}`);
|
|
257
|
+
unfixedPaths.push(libPath);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
// This is a non-system library we don't have - this is a problem
|
|
261
|
+
unfixedPaths.push(libPath);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.warn(`Warning: Could not process ${binaryPath}: ${error}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return unfixedPaths;
|
|
270
|
+
}
|
|
161
271
|
|
|
162
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Fix the install name (ID) of a dylib to use @loader_path
|
|
274
|
+
*/
|
|
275
|
+
async function fixDylibId(dylibPath: string): Promise<void> {
|
|
276
|
+
const dylibName = path.basename(dylibPath);
|
|
277
|
+
try {
|
|
278
|
+
await execAsync(`install_name_tool -id "@loader_path/${dylibName}" "${dylibPath}"`);
|
|
279
|
+
} catch {
|
|
280
|
+
console.warn(` Warning: Could not fix dylib ID for ${dylibName}`);
|
|
281
|
+
}
|
|
163
282
|
}
|
|
164
283
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
): Promise<
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const depPath = match[1];
|
|
177
|
-
if (
|
|
178
|
-
depPath.includes(dylibName) &&
|
|
179
|
-
!depPath.startsWith("@loader_path")
|
|
180
|
-
) {
|
|
181
|
-
try {
|
|
182
|
-
await execAsync(
|
|
183
|
-
`install_name_tool -change "${depPath}" "@loader_path/${dylibName}" "${binaryPath}"`
|
|
184
|
-
);
|
|
185
|
-
} catch {
|
|
186
|
-
// Ignore errors
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
284
|
+
/**
|
|
285
|
+
* Ad-hoc code sign a binary (required after modifying with install_name_tool on modern macOS)
|
|
286
|
+
*/
|
|
287
|
+
async function codesignBinary(binaryPath: string): Promise<boolean> {
|
|
288
|
+
try {
|
|
289
|
+
await execAsync(`codesign --force --sign - "${binaryPath}"`);
|
|
290
|
+
return true;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.warn(` Warning: Could not code sign ${path.basename(binaryPath)}: ${error}`);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
192
295
|
}
|
|
193
296
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Verify that a binary can be loaded and executed
|
|
299
|
+
*/
|
|
300
|
+
async function verifyBinary(binaryPath: string, binDir: string): Promise<{ success: boolean; error?: string }> {
|
|
301
|
+
const binaryName = path.basename(binaryPath);
|
|
302
|
+
|
|
303
|
+
// Check that otool shows no broken paths
|
|
304
|
+
try {
|
|
305
|
+
const { stdout } = await execAsync(`otool -L "${binaryPath}"`);
|
|
306
|
+
const lines = stdout.split("\n");
|
|
307
|
+
|
|
308
|
+
for (const line of lines) {
|
|
309
|
+
const match = line.match(/^\s+(.+?)\s+\(/);
|
|
310
|
+
if (match) {
|
|
311
|
+
const libPath = match[1].trim();
|
|
312
|
+
|
|
313
|
+
// Skip system libraries
|
|
314
|
+
if (isSystemLibrary(libPath)) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// If it's an @loader_path reference, check the file exists
|
|
319
|
+
if (libPath.startsWith("@loader_path/")) {
|
|
320
|
+
const libName = libPath.replace("@loader_path/", "");
|
|
321
|
+
const fullPath = path.join(binDir, libName);
|
|
322
|
+
if (!existsSync(fullPath)) {
|
|
323
|
+
return { success: false, error: `Missing library: ${libName}` };
|
|
324
|
+
}
|
|
325
|
+
} else if (!libPath.startsWith("@")) {
|
|
326
|
+
// Absolute path that's not a system library - this is a problem
|
|
327
|
+
return { success: false, error: `Unfixed absolute path: ${libPath}` };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
return { success: false, error: `otool failed: ${error}` };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// For executable binaries (not dylibs), try to run them with --help or -h
|
|
336
|
+
if (!binaryName.endsWith(".dylib")) {
|
|
337
|
+
try {
|
|
338
|
+
// Use spawn with a timeout to test if the binary loads
|
|
339
|
+
const result = await new Promise<{ success: boolean; error?: string }>((resolve) => {
|
|
340
|
+
const child = spawn(binaryPath, ["--help"], {
|
|
341
|
+
timeout: 5000,
|
|
342
|
+
env: { ...process.env, DYLD_LIBRARY_PATH: binDir },
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
let stderr = "";
|
|
346
|
+
child.stderr?.on("data", (data) => {
|
|
347
|
+
stderr += data.toString();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
child.on("error", (err) => {
|
|
351
|
+
resolve({ success: false, error: `Spawn error: ${err.message}` });
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
child.on("close", (code) => {
|
|
355
|
+
// Exit codes 0, 1, or 2 are acceptable (0=success, 1=error, 2=usage)
|
|
356
|
+
// What we're checking is that the binary LOADS, not that --help works
|
|
357
|
+
if (code !== null && code <= 2) {
|
|
358
|
+
resolve({ success: true });
|
|
359
|
+
} else if (stderr.includes("Library not loaded") || stderr.includes("image not found")) {
|
|
360
|
+
resolve({ success: false, error: stderr.split("\n")[0] });
|
|
361
|
+
} else {
|
|
362
|
+
// Other exit codes might be fine too
|
|
363
|
+
resolve({ success: true });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (!result.success) {
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
} catch (error) {
|
|
372
|
+
// If spawn fails entirely, that's a problem
|
|
373
|
+
return { success: false, error: `Execution test failed: ${error}` };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { success: true };
|
|
203
378
|
}
|
|
204
379
|
|
|
205
380
|
// ============================================================================
|
|
@@ -207,111 +382,186 @@ async function fixDylibId(dylibPath: string): Promise<void> {
|
|
|
207
382
|
// ============================================================================
|
|
208
383
|
|
|
209
384
|
function getHomebrewPath(): string | null {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
385
|
+
if (existsSync(path.join(HOMEBREW_ARM_PATH, "bin", "idevice_id"))) {
|
|
386
|
+
return HOMEBREW_ARM_PATH;
|
|
387
|
+
}
|
|
388
|
+
if (existsSync(path.join(HOMEBREW_INTEL_PATH, "bin", "idevice_id"))) {
|
|
389
|
+
return HOMEBREW_INTEL_PATH;
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
217
392
|
}
|
|
218
393
|
|
|
219
|
-
async function exportDarwinResources(targetDir: string): Promise<void> {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
394
|
+
async function exportDarwinResources(targetDir: string, skipVerify: boolean): Promise<void> {
|
|
395
|
+
console.log("\n=== Exporting macOS (Darwin) Resources ===\n");
|
|
396
|
+
|
|
397
|
+
const homebrewPath = getHomebrewPath();
|
|
398
|
+
if (!homebrewPath) {
|
|
399
|
+
console.error("Error: Homebrew libimobiledevice not found.");
|
|
400
|
+
console.error("Please install it first: brew install libimobiledevice ideviceinstaller");
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
console.log(`Found Homebrew at: ${homebrewPath}`);
|
|
405
|
+
|
|
406
|
+
const darwinBinDir = path.join(targetDir, "bin", "darwin");
|
|
407
|
+
mkdirSync(darwinBinDir, { recursive: true });
|
|
408
|
+
|
|
409
|
+
// =========================================================================
|
|
410
|
+
// Step 1: Copy all required and optional tools
|
|
411
|
+
// =========================================================================
|
|
412
|
+
console.log("\n--- Step 1: Copying binaries ---");
|
|
413
|
+
|
|
414
|
+
const binaryPaths: string[] = [];
|
|
415
|
+
const copiedBinaries: string[] = [];
|
|
416
|
+
|
|
417
|
+
// Copy required tools
|
|
418
|
+
for (const tool of REQUIRED_TOOLS) {
|
|
419
|
+
const toolPath = path.join(homebrewPath, "bin", tool);
|
|
420
|
+
if (existsSync(toolPath)) {
|
|
421
|
+
const realPath = resolveSymlink(toolPath);
|
|
422
|
+
binaryPaths.push(realPath);
|
|
423
|
+
|
|
424
|
+
const destPath = path.join(darwinBinDir, tool);
|
|
425
|
+
copyFileSync(realPath, destPath);
|
|
426
|
+
chmodSync(destPath, 0o755);
|
|
427
|
+
copiedBinaries.push(destPath);
|
|
428
|
+
console.log(` ✓ Copied ${tool}`);
|
|
429
|
+
} else {
|
|
430
|
+
console.error(` ✗ Required tool not found: ${tool}`);
|
|
431
|
+
console.error(` Please install: brew install libimobiledevice ideviceinstaller`);
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Copy optional tools
|
|
437
|
+
for (const tool of OPTIONAL_TOOLS) {
|
|
438
|
+
const toolPath = path.join(homebrewPath, "bin", tool);
|
|
439
|
+
if (existsSync(toolPath)) {
|
|
440
|
+
const realPath = resolveSymlink(toolPath);
|
|
441
|
+
binaryPaths.push(realPath);
|
|
442
|
+
|
|
443
|
+
const destPath = path.join(darwinBinDir, tool);
|
|
444
|
+
copyFileSync(realPath, destPath);
|
|
445
|
+
chmodSync(destPath, 0o755);
|
|
446
|
+
copiedBinaries.push(destPath);
|
|
447
|
+
console.log(` ✓ Copied ${tool} (optional)`);
|
|
448
|
+
} else {
|
|
449
|
+
console.log(` - Skipping ${tool} (optional, not installed)`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// =========================================================================
|
|
454
|
+
// Step 2: Recursively collect ALL dylib dependencies
|
|
455
|
+
// =========================================================================
|
|
456
|
+
console.log("\n--- Step 2: Collecting dependencies ---");
|
|
457
|
+
|
|
458
|
+
const allDylibs = await collectAllDependencies(binaryPaths);
|
|
459
|
+
|
|
460
|
+
// =========================================================================
|
|
461
|
+
// Step 3: Copy all dylibs
|
|
462
|
+
// =========================================================================
|
|
463
|
+
console.log("\n--- Step 3: Copying dylibs ---");
|
|
464
|
+
|
|
465
|
+
const copiedDylibs: string[] = [];
|
|
466
|
+
const allDylibNames = new Set<string>();
|
|
467
|
+
|
|
468
|
+
for (const [dylibName, dep] of allDylibs) {
|
|
469
|
+
allDylibNames.add(dylibName);
|
|
470
|
+
const destPath = path.join(darwinBinDir, dylibName);
|
|
471
|
+
|
|
472
|
+
if (existsSync(dep.realPath)) {
|
|
473
|
+
copyFileSync(dep.realPath, destPath);
|
|
474
|
+
chmodSync(destPath, 0o755);
|
|
475
|
+
copiedDylibs.push(destPath);
|
|
476
|
+
console.log(` ✓ Copied ${dylibName}`);
|
|
477
|
+
} else {
|
|
478
|
+
console.warn(` ✗ Warning: Dylib not found: ${dep.realPath}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// =========================================================================
|
|
483
|
+
// Step 4: Fix library paths in all copied files
|
|
484
|
+
// =========================================================================
|
|
485
|
+
console.log("\n--- Step 4: Fixing library paths ---");
|
|
486
|
+
|
|
487
|
+
const allCopiedFiles = [...copiedBinaries, ...copiedDylibs];
|
|
488
|
+
const unfixedPaths: Map<string, string[]> = new Map();
|
|
489
|
+
|
|
490
|
+
// First, fix dylib IDs
|
|
491
|
+
for (const dylibPath of copiedDylibs) {
|
|
492
|
+
await fixDylibId(dylibPath);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Then, fix all library references
|
|
496
|
+
for (const filePath of allCopiedFiles) {
|
|
497
|
+
const unfixed = await fixAllLibraryPaths(filePath, allDylibNames);
|
|
498
|
+
if (unfixed.length > 0) {
|
|
499
|
+
unfixedPaths.set(path.basename(filePath), unfixed);
|
|
500
|
+
}
|
|
501
|
+
console.log(` ✓ Fixed paths in ${path.basename(filePath)}`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Report any unfixed paths
|
|
505
|
+
if (unfixedPaths.size > 0) {
|
|
506
|
+
console.log("\n ⚠ Warning: Some library paths could not be fixed:");
|
|
507
|
+
for (const [file, paths] of unfixedPaths) {
|
|
508
|
+
for (const p of paths) {
|
|
509
|
+
console.log(` ${file}: ${p}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// =========================================================================
|
|
515
|
+
// Step 5: Code sign all binaries (required on modern macOS)
|
|
516
|
+
// =========================================================================
|
|
517
|
+
console.log("\n--- Step 5: Code signing ---");
|
|
518
|
+
|
|
519
|
+
let signedCount = 0;
|
|
520
|
+
for (const filePath of allCopiedFiles) {
|
|
521
|
+
const success = await codesignBinary(filePath);
|
|
522
|
+
if (success) {
|
|
523
|
+
signedCount++;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
console.log(` ✓ Signed ${signedCount}/${allCopiedFiles.length} files`);
|
|
527
|
+
|
|
528
|
+
// =========================================================================
|
|
529
|
+
// Step 6: Verify binaries work
|
|
530
|
+
// =========================================================================
|
|
531
|
+
if (!skipVerify) {
|
|
532
|
+
console.log("\n--- Step 6: Verifying binaries ---");
|
|
533
|
+
|
|
534
|
+
let verifyErrors = 0;
|
|
535
|
+
for (const binaryPath of copiedBinaries) {
|
|
536
|
+
const result = await verifyBinary(binaryPath, darwinBinDir);
|
|
537
|
+
if (result.success) {
|
|
538
|
+
console.log(` ✓ ${path.basename(binaryPath)} OK`);
|
|
539
|
+
} else {
|
|
540
|
+
console.log(` ✗ ${path.basename(binaryPath)} FAILED: ${result.error}`);
|
|
541
|
+
verifyErrors++;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Also verify dylibs have valid paths
|
|
546
|
+
for (const dylibPath of copiedDylibs) {
|
|
547
|
+
const result = await verifyBinary(dylibPath, darwinBinDir);
|
|
548
|
+
if (!result.success) {
|
|
549
|
+
console.log(` ✗ ${path.basename(dylibPath)} FAILED: ${result.error}`);
|
|
550
|
+
verifyErrors++;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (verifyErrors > 0) {
|
|
555
|
+
console.log(`\n ⚠ ${verifyErrors} verification errors found`);
|
|
556
|
+
console.log(" Run with DYLD_PRINT_LIBRARIES=1 to debug library loading issues");
|
|
557
|
+
} else {
|
|
558
|
+
console.log(`\n ✓ All binaries verified successfully`);
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
561
|
+
console.log("\n--- Step 6: Verification skipped ---");
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
console.log(`\n✓ macOS resources exported to: ${darwinBinDir}`);
|
|
315
565
|
}
|
|
316
566
|
|
|
317
567
|
// ============================================================================
|
|
@@ -319,53 +569,48 @@ async function exportDarwinResources(targetDir: string): Promise<void> {
|
|
|
319
569
|
// ============================================================================
|
|
320
570
|
|
|
321
571
|
async function exportWindowsResources(targetDir: string): Promise<void> {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
console.log("1. Open MSYS2 MinGW 64-bit shell");
|
|
349
|
-
console.log(`2. Run: bash "${path.join(targetDir, "build-windows.sh")}"`);
|
|
572
|
+
console.log("\n=== Exporting Windows Resources ===\n");
|
|
573
|
+
|
|
574
|
+
// Check if we're on Windows
|
|
575
|
+
if (process.platform !== "win32") {
|
|
576
|
+
console.log("Note: Windows resources can only be built on Windows.");
|
|
577
|
+
console.log("Generating build script for later use...\n");
|
|
578
|
+
generateWindowsBuildScript(targetDir);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Check for MSYS2
|
|
583
|
+
const msys2Path = process.env.MSYS2_PATH || MSYS2_DEFAULT_PATH;
|
|
584
|
+
if (!existsSync(msys2Path)) {
|
|
585
|
+
console.error(`Error: MSYS2 not found at ${msys2Path}`);
|
|
586
|
+
console.error("Please install MSYS2 from https://www.msys2.org/");
|
|
587
|
+
console.error("Or set MSYS2_PATH environment variable to your MSYS2 installation.");
|
|
588
|
+
generateWindowsBuildScript(targetDir);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Generate the build script
|
|
593
|
+
generateWindowsBuildScript(targetDir);
|
|
594
|
+
|
|
595
|
+
console.log("\nTo build Windows resources:");
|
|
596
|
+
console.log("1. Open MSYS2 MinGW 64-bit shell");
|
|
597
|
+
console.log(`2. Run: bash "${path.join(targetDir, "build-windows.sh")}"`);
|
|
350
598
|
}
|
|
351
599
|
|
|
352
600
|
function generateWindowsBuildScript(targetDir: string): void {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const scriptPath = path.join(targetDir, "build-windows.sh");
|
|
367
|
-
writeFileSync(scriptPath, buildScript);
|
|
368
|
-
console.log(`✓ Generated Windows build script: ${scriptPath}`);
|
|
601
|
+
const windowsBinDir = path.join(targetDir, "bin", "windows");
|
|
602
|
+
mkdirSync(windowsBinDir, { recursive: true });
|
|
603
|
+
|
|
604
|
+
// Read the template file
|
|
605
|
+
const templatePath = path.join(__dirname, "build-windows.sh.template");
|
|
606
|
+
let buildScript = readFileSync(templatePath, "utf-8");
|
|
607
|
+
|
|
608
|
+
// Replace the placeholder with the actual target directory
|
|
609
|
+
buildScript = buildScript.replace("{{TARGET_DIR}}", windowsBinDir.replace(/\\/g, "/"));
|
|
610
|
+
|
|
611
|
+
const scriptPath = path.join(targetDir, "build-windows.sh");
|
|
612
|
+
writeFileSync(scriptPath, buildScript);
|
|
613
|
+
console.log(`✓ Generated Windows build script: ${scriptPath}`);
|
|
369
614
|
}
|
|
370
615
|
|
|
371
616
|
// ============================================================================
|
|
@@ -373,10 +618,10 @@ function generateWindowsBuildScript(targetDir: string): void {
|
|
|
373
618
|
// ============================================================================
|
|
374
619
|
|
|
375
620
|
function createLicenseFile(targetDir: string): void {
|
|
376
|
-
|
|
377
|
-
|
|
621
|
+
const licensesDir = path.join(targetDir, "licenses");
|
|
622
|
+
mkdirSync(licensesDir, { recursive: true });
|
|
378
623
|
|
|
379
|
-
|
|
624
|
+
const lgplText = `GNU LESSER GENERAL PUBLIC LICENSE
|
|
380
625
|
Version 2.1, February 1999
|
|
381
626
|
|
|
382
627
|
libimobiledevice and related tools are licensed under the
|
|
@@ -393,9 +638,9 @@ These binaries are bundled for convenience. The source code is available at:
|
|
|
393
638
|
- https://github.com/libimobiledevice/ideviceinstaller
|
|
394
639
|
`;
|
|
395
640
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
641
|
+
const licensePath = path.join(licensesDir, "LGPL-2.1.txt");
|
|
642
|
+
writeFileSync(licensePath, lgplText);
|
|
643
|
+
console.log(`✓ Created license file: ${licensePath}`);
|
|
399
644
|
}
|
|
400
645
|
|
|
401
646
|
// ============================================================================
|
|
@@ -403,42 +648,42 @@ These binaries are bundled for convenience. The source code is available at:
|
|
|
403
648
|
// ============================================================================
|
|
404
649
|
|
|
405
650
|
async function main(): Promise<void> {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
651
|
+
const args = process.argv.slice(2);
|
|
652
|
+
|
|
653
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
654
|
+
printUsage();
|
|
655
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const skipVerify = args.includes("--skip-verify");
|
|
659
|
+
const targetPath = path.resolve(args.find((a) => !a.startsWith("--")) || ".");
|
|
660
|
+
|
|
661
|
+
console.log("=== Apple-kit Resources Export ===");
|
|
662
|
+
console.log(`Platform: ${process.platform}`);
|
|
663
|
+
console.log(`Target: ${targetPath}`);
|
|
664
|
+
|
|
665
|
+
// Create target directory
|
|
666
|
+
mkdirSync(targetPath, { recursive: true });
|
|
667
|
+
|
|
668
|
+
// Export based on platform
|
|
669
|
+
if (process.platform === "darwin") {
|
|
670
|
+
await exportDarwinResources(targetPath, skipVerify);
|
|
671
|
+
} else if (process.platform === "win32") {
|
|
672
|
+
await exportWindowsResources(targetPath);
|
|
673
|
+
} else {
|
|
674
|
+
console.log("\nNote: This script supports macOS and Windows.");
|
|
675
|
+
console.log("For Linux, install libimobiledevice-utils via your package manager.");
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Always create license file
|
|
679
|
+
console.log("");
|
|
680
|
+
createLicenseFile(targetPath);
|
|
681
|
+
|
|
682
|
+
console.log("\n=== Export Complete ===");
|
|
683
|
+
console.log(`Resources exported to: ${targetPath}`);
|
|
438
684
|
}
|
|
439
685
|
|
|
440
686
|
main().catch((error) => {
|
|
441
|
-
|
|
442
|
-
|
|
687
|
+
console.error("Export failed:", error);
|
|
688
|
+
process.exit(1);
|
|
443
689
|
});
|
|
444
|
-
|