@shopify/react-native-skia 2.4.21 → 2.5.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/android/CMakeLists.txt +12 -49
- package/android/build.gradle +37 -1
- package/apple/SkiaCVPixelBufferUtils.h +6 -2
- package/apple/SkiaCVPixelBufferUtils.mm +208 -36
- package/apple/SkiaUIView.mm +4 -0
- package/cpp/api/JsiSkColor.h +53 -1
- package/cpp/rnwgpu/ArrayBuffer.h +7 -2
- package/lib/commonjs/renderer/Canvas.d.ts +2 -0
- package/lib/commonjs/renderer/Canvas.js +0 -3
- package/lib/commonjs/renderer/Canvas.js.map +1 -1
- package/lib/commonjs/sksg/Container.native.js +3 -5
- package/lib/commonjs/sksg/Container.native.js.map +1 -1
- package/lib/module/renderer/Canvas.d.ts +2 -0
- package/lib/module/renderer/Canvas.js +0 -3
- package/lib/module/renderer/Canvas.js.map +1 -1
- package/lib/module/sksg/Container.native.js +3 -5
- package/lib/module/sksg/Container.native.js.map +1 -1
- package/lib/typescript/src/renderer/Canvas.d.ts +2 -0
- package/package.json +10 -30
- package/react-native-skia.podspec +110 -58
- package/src/renderer/Canvas.tsx +2 -3
- package/src/sksg/Container.native.ts +3 -4
- package/scripts/install-skia.mjs +0 -728
package/scripts/install-skia.mjs
DELETED
|
@@ -1,728 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import https from "https";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import os from "os";
|
|
5
|
-
import crypto from "crypto";
|
|
6
|
-
import { spawn } from "child_process";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
11
|
-
// Allow skipping download via environment variable (useful for CI builds)
|
|
12
|
-
if (process.env.SKIP_SKIA_DOWNLOAD === '1' || process.env.SKIP_SKIA_DOWNLOAD === 'true') {
|
|
13
|
-
console.log("⏭️ Skipping Skia download (SKIP_SKIA_DOWNLOAD is set)");
|
|
14
|
-
process.exit(0);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const repo = "shopify/react-native-skia";
|
|
18
|
-
|
|
19
|
-
const packageJsonPath = path.join(__dirname, "..", "package.json");
|
|
20
|
-
|
|
21
|
-
const getPackageJson = () => {
|
|
22
|
-
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const getSkiaConfig = (graphite = false) => {
|
|
26
|
-
const packageJson = getPackageJson();
|
|
27
|
-
return graphite ? packageJson["skia-graphite"] : packageJson.skia;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const getSkiaVersion = (graphite = false) => {
|
|
31
|
-
const packageJson = getPackageJson();
|
|
32
|
-
const skiaConfig = getSkiaConfig(graphite);
|
|
33
|
-
return skiaConfig?.version || packageJson.skiaVersion;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const updateSkiaChecksums = (checksums, graphite = false) => {
|
|
37
|
-
const packageJson = getPackageJson();
|
|
38
|
-
const field = graphite ? "skia-graphite" : "skia";
|
|
39
|
-
|
|
40
|
-
if (!packageJson[field]) {
|
|
41
|
-
packageJson[field] = { version: packageJson[field]?.version || "m142", checksums: {} };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
packageJson[field].checksums = checksums;
|
|
45
|
-
|
|
46
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf8");
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const GRAPHITE = !!process.env.SK_GRAPHITE;
|
|
50
|
-
const prefix = GRAPHITE ? "skia-graphite" : "skia";
|
|
51
|
-
|
|
52
|
-
// Build artifact names based on platform and Graphite mode
|
|
53
|
-
const names = [
|
|
54
|
-
`${prefix}-android-arm`,
|
|
55
|
-
`${prefix}-android-arm-64`,
|
|
56
|
-
`${prefix}-android-arm-x64`,
|
|
57
|
-
`${prefix}-android-arm-x86`,
|
|
58
|
-
`${prefix}-apple-ios-xcframeworks`,
|
|
59
|
-
`${prefix}-apple-macos-xcframeworks`,
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
// Add tvOS only for non-Graphite builds
|
|
63
|
-
if (!GRAPHITE) {
|
|
64
|
-
names.push(`${prefix}-apple-tvos-xcframeworks`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Note: macCatalyst is now included in the iOS xcframeworks, no separate download needed
|
|
68
|
-
|
|
69
|
-
if (GRAPHITE) {
|
|
70
|
-
names.push(`${prefix}-headers`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const skiaVersion = getSkiaVersion(GRAPHITE);
|
|
74
|
-
const releaseTag = GRAPHITE ? `skia-graphite-${skiaVersion}` : `skia-${skiaVersion}`;
|
|
75
|
-
console.log(
|
|
76
|
-
`📦 Downloading Skia prebuilt binaries for ${releaseTag}`
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const runCommand = (command, args, options = {}) => {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
const child = spawn(command, args, {
|
|
82
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
83
|
-
...options,
|
|
84
|
-
});
|
|
85
|
-
child.on("error", (error) => {
|
|
86
|
-
reject(error);
|
|
87
|
-
});
|
|
88
|
-
child.on("close", (code) => {
|
|
89
|
-
if (code === 0) {
|
|
90
|
-
resolve();
|
|
91
|
-
} else {
|
|
92
|
-
reject(new Error(`Command ${command} exited with code ${code}`));
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const runCommandWithOutput = (command, args, options = {}) => {
|
|
99
|
-
return new Promise((resolve, reject) => {
|
|
100
|
-
const child = spawn(command, args, {
|
|
101
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
102
|
-
...options,
|
|
103
|
-
});
|
|
104
|
-
let stdout = "";
|
|
105
|
-
let stderr = "";
|
|
106
|
-
child.stdout.on("data", (data) => {
|
|
107
|
-
stdout += data.toString();
|
|
108
|
-
});
|
|
109
|
-
child.stderr.on("data", (data) => {
|
|
110
|
-
stderr += data.toString();
|
|
111
|
-
});
|
|
112
|
-
child.on("error", (error) => {
|
|
113
|
-
reject(error);
|
|
114
|
-
});
|
|
115
|
-
child.on("close", (code) => {
|
|
116
|
-
if (code === 0) {
|
|
117
|
-
resolve(stdout.trim());
|
|
118
|
-
} else {
|
|
119
|
-
reject(new Error(`Command ${command} exited with code ${code}: ${stderr}`));
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const skiaDir = path.resolve(__dirname, "../../../externals/skia");
|
|
126
|
-
|
|
127
|
-
const checkoutSkiaBranch = async (version) => {
|
|
128
|
-
// Extract base version (e.g., "m144a" -> "m144", "m142" -> "m142")
|
|
129
|
-
const baseVersion = version.match(/^(m\d+)/)?.[1] || version;
|
|
130
|
-
const branchName = `chrome/${baseVersion}`;
|
|
131
|
-
|
|
132
|
-
// Check if the skia directory exists and is a git repo
|
|
133
|
-
// (won't exist when installed via npm - submodule is not included in the package)
|
|
134
|
-
if (!fs.existsSync(skiaDir) || !fs.existsSync(path.join(skiaDir, ".git"))) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log(`🔀 Checking out Skia branch: ${branchName}`);
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
// Get current branch/commit
|
|
142
|
-
const currentRef = await runCommandWithOutput("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: skiaDir });
|
|
143
|
-
|
|
144
|
-
if (currentRef === branchName) {
|
|
145
|
-
console.log(` ✓ Already on branch ${branchName}`);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Fetch the branch from origin
|
|
150
|
-
console.log(` Fetching branch ${branchName} from origin...`);
|
|
151
|
-
try {
|
|
152
|
-
await runCommand("git", ["fetch", "origin", `${branchName}:${branchName}`], { cwd: skiaDir });
|
|
153
|
-
} catch (e) {
|
|
154
|
-
// Branch might already exist locally, try to update it
|
|
155
|
-
await runCommand("git", ["fetch", "origin", branchName], { cwd: skiaDir });
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Checkout the branch (use -f to discard local changes in the submodule)
|
|
159
|
-
console.log(` Checking out ${branchName}...`);
|
|
160
|
-
await runCommand("git", ["checkout", "-f", branchName], { cwd: skiaDir });
|
|
161
|
-
|
|
162
|
-
console.log(` ✓ Successfully checked out ${branchName}`);
|
|
163
|
-
} catch (error) {
|
|
164
|
-
throw new Error(`Failed to checkout branch ${branchName}: ${error.message}`);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
169
|
-
|
|
170
|
-
const downloadToFile = (url, destPath, maxRetries = 5) => {
|
|
171
|
-
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
172
|
-
|
|
173
|
-
const attemptDownload = (retryCount = 0) => {
|
|
174
|
-
return new Promise((resolve, reject) => {
|
|
175
|
-
const request = (currentUrl) => {
|
|
176
|
-
https
|
|
177
|
-
.get(currentUrl, { headers: { "User-Agent": "node" } }, (res) => {
|
|
178
|
-
if (
|
|
179
|
-
res.statusCode &&
|
|
180
|
-
[301, 302, 303, 307, 308].includes(res.statusCode)
|
|
181
|
-
) {
|
|
182
|
-
const { location } = res.headers;
|
|
183
|
-
if (location) {
|
|
184
|
-
res.resume();
|
|
185
|
-
request(location);
|
|
186
|
-
} else {
|
|
187
|
-
reject(new Error(`Redirect without location for ${currentUrl}`));
|
|
188
|
-
}
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (res.statusCode !== 200) {
|
|
193
|
-
const error = new Error(
|
|
194
|
-
`Failed to download: ${res.statusCode} ${res.statusMessage}`
|
|
195
|
-
);
|
|
196
|
-
error.statusCode = res.statusCode;
|
|
197
|
-
reject(error);
|
|
198
|
-
res.resume();
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const fileStream = fs.createWriteStream(destPath);
|
|
203
|
-
res.pipe(fileStream);
|
|
204
|
-
|
|
205
|
-
fileStream.on("finish", () => {
|
|
206
|
-
fileStream.close((err) => {
|
|
207
|
-
if (err) {
|
|
208
|
-
// If closing the stream errors, perform the same cleanup and reject.
|
|
209
|
-
fileStream.destroy();
|
|
210
|
-
fs.unlink(destPath, () => reject(err));
|
|
211
|
-
} else {
|
|
212
|
-
resolve();
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
const cleanup = (error) => {
|
|
218
|
-
fileStream.destroy();
|
|
219
|
-
fs.unlink(destPath, () => reject(error));
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
res.on("error", cleanup);
|
|
223
|
-
fileStream.on("error", cleanup);
|
|
224
|
-
})
|
|
225
|
-
.on("error", reject);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
request(url);
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const downloadWithRetry = async (retryCount = 0) => {
|
|
233
|
-
try {
|
|
234
|
-
await attemptDownload(retryCount);
|
|
235
|
-
} catch (error) {
|
|
236
|
-
const isRateLimit = error.statusCode === 403 || error.message.includes('rate limit');
|
|
237
|
-
const shouldRetry = retryCount < maxRetries && (isRateLimit || error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT');
|
|
238
|
-
|
|
239
|
-
if (shouldRetry) {
|
|
240
|
-
const delay = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s, 8s, 16s
|
|
241
|
-
console.log(` ⚠️ Download failed (${error.message}), retrying in ${delay/1000}s... (attempt ${retryCount + 1}/${maxRetries})`);
|
|
242
|
-
await sleep(delay);
|
|
243
|
-
return downloadWithRetry(retryCount + 1);
|
|
244
|
-
} else {
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
return downloadWithRetry();
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const extractTarGz = async (archivePath, destDir) => {
|
|
254
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
255
|
-
|
|
256
|
-
const args = [
|
|
257
|
-
"-xzf",
|
|
258
|
-
archivePath,
|
|
259
|
-
"-C",
|
|
260
|
-
destDir,
|
|
261
|
-
];
|
|
262
|
-
const candidates =
|
|
263
|
-
process.platform === "win32"
|
|
264
|
-
? [
|
|
265
|
-
"tar.exe",
|
|
266
|
-
path.join(
|
|
267
|
-
process.env.SystemRoot ?? "C:\\Windows",
|
|
268
|
-
"System32",
|
|
269
|
-
"tar.exe"
|
|
270
|
-
),
|
|
271
|
-
"bsdtar.exe",
|
|
272
|
-
"bsdtar",
|
|
273
|
-
]
|
|
274
|
-
: ["tar"];
|
|
275
|
-
|
|
276
|
-
let lastError;
|
|
277
|
-
for (const candidate of candidates) {
|
|
278
|
-
try {
|
|
279
|
-
await runCommand(candidate, args);
|
|
280
|
-
return;
|
|
281
|
-
} catch (err) {
|
|
282
|
-
if (err.code === "ENOENT") {
|
|
283
|
-
lastError = new Error(`Command ${candidate} not found`);
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
lastError = err;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
throw new Error(
|
|
291
|
-
`Failed to extract ${path.basename(
|
|
292
|
-
archivePath
|
|
293
|
-
)}. Please install a compatible tar binary. Last error: ${
|
|
294
|
-
lastError?.message ?? "unknown error"
|
|
295
|
-
}`
|
|
296
|
-
);
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// Helper function to download and extract a tar.gz file
|
|
300
|
-
const downloadAndExtract = async (url, destDir, tempDir) => {
|
|
301
|
-
const archivePath = path.join(tempDir, path.basename(url));
|
|
302
|
-
await downloadToFile(url, archivePath);
|
|
303
|
-
try {
|
|
304
|
-
await extractTarGz(archivePath, destDir);
|
|
305
|
-
} finally {
|
|
306
|
-
if (fs.existsSync(archivePath)) {
|
|
307
|
-
fs.unlinkSync(archivePath);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const artifactsDir = path.resolve(
|
|
313
|
-
__dirname,
|
|
314
|
-
"../../../packages/skia/artifacts"
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
const libsDir = path.resolve(__dirname, "../libs");
|
|
318
|
-
|
|
319
|
-
// Function to calculate the checksum of a directory
|
|
320
|
-
const calculateDirectoryChecksum = (directory) => {
|
|
321
|
-
if (!fs.existsSync(directory)) {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const hash = crypto.createHash("sha256");
|
|
326
|
-
const files = [];
|
|
327
|
-
|
|
328
|
-
const collectFiles = (dir) => {
|
|
329
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
330
|
-
for (const entry of entries) {
|
|
331
|
-
const fullPath = path.join(dir, entry.name);
|
|
332
|
-
if (entry.isDirectory()) {
|
|
333
|
-
collectFiles(fullPath);
|
|
334
|
-
} else if (entry.isFile()) {
|
|
335
|
-
files.push(fullPath);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
collectFiles(directory);
|
|
341
|
-
files.sort();
|
|
342
|
-
|
|
343
|
-
for (const file of files) {
|
|
344
|
-
const relativePath = path.relative(directory, file);
|
|
345
|
-
hash.update(relativePath);
|
|
346
|
-
hash.update(fs.readFileSync(file));
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return hash.digest("hex");
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
// Function to calculate all library checksums
|
|
353
|
-
const calculateLibraryChecksums = () => {
|
|
354
|
-
const checksums = {};
|
|
355
|
-
|
|
356
|
-
// Android architectures
|
|
357
|
-
const androidArchs = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"];
|
|
358
|
-
for (const arch of androidArchs) {
|
|
359
|
-
const archDir = path.join(libsDir, "android", arch);
|
|
360
|
-
const checksum = calculateDirectoryChecksum(archDir);
|
|
361
|
-
if (checksum) {
|
|
362
|
-
const checksumKey = `android-${arch}`;
|
|
363
|
-
checksums[checksumKey] = checksum;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Apple platforms - calculate separate checksums for each platform
|
|
368
|
-
// Note: maccatalyst is included in iOS xcframeworks, not a separate artifact
|
|
369
|
-
const applePlatforms = GRAPHITE
|
|
370
|
-
? ["ios", "macos"]
|
|
371
|
-
: ["ios", "tvos", "macos"];
|
|
372
|
-
|
|
373
|
-
for (const platform of applePlatforms) {
|
|
374
|
-
const platformDir = path.join(libsDir, "apple", platform);
|
|
375
|
-
const checksum = calculateDirectoryChecksum(platformDir);
|
|
376
|
-
if (checksum) {
|
|
377
|
-
checksums[`apple-${platform}-xcframeworks`] = checksum;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return checksums;
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
// Function to verify if checksums match
|
|
385
|
-
const verifyChecksums = () => {
|
|
386
|
-
const skiaConfig = getSkiaConfig(GRAPHITE);
|
|
387
|
-
const expectedChecksums = skiaConfig?.checksums || {};
|
|
388
|
-
|
|
389
|
-
// If no checksums in package.json, we need to download
|
|
390
|
-
if (Object.keys(expectedChecksums).length === 0) {
|
|
391
|
-
console.log("⚠️ No checksums found in package.json");
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const actualChecksums = calculateLibraryChecksums();
|
|
396
|
-
|
|
397
|
-
// Check if all expected checksums match
|
|
398
|
-
for (const [key, expectedChecksum] of Object.entries(expectedChecksums)) {
|
|
399
|
-
const actualChecksum = actualChecksums[key];
|
|
400
|
-
if (actualChecksum !== expectedChecksum) {
|
|
401
|
-
console.log(`⚠️ Checksum mismatch for ${key}`);
|
|
402
|
-
console.log(` Expected: ${expectedChecksum}`);
|
|
403
|
-
console.log(` Actual: ${actualChecksum || "missing"}`);
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
console.log("✅ All checksums match");
|
|
409
|
-
return true;
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
// Function to check if prebuilt binaries are already installed
|
|
413
|
-
const areBinariesInstalled = () => {
|
|
414
|
-
if (!fs.existsSync(libsDir)) {
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Check for Android libraries
|
|
419
|
-
const androidDir = path.join(libsDir, "android");
|
|
420
|
-
const androidArchs = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"];
|
|
421
|
-
for (const arch of androidArchs) {
|
|
422
|
-
const archDir = path.join(androidDir, arch);
|
|
423
|
-
if (!fs.existsSync(archDir) || fs.readdirSync(archDir).length === 0) {
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Check for Apple platform frameworks
|
|
429
|
-
const applePlatforms = GRAPHITE
|
|
430
|
-
? ["ios", "macos"]
|
|
431
|
-
: ["ios", "tvos", "macos", "maccatalyst"];
|
|
432
|
-
|
|
433
|
-
for (const platform of applePlatforms) {
|
|
434
|
-
const platformDir = path.join(libsDir, "apple", platform);
|
|
435
|
-
if (!fs.existsSync(platformDir) || fs.readdirSync(platformDir).length === 0) {
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return true;
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
// Function to clear directory contents
|
|
444
|
-
const clearDirectory = (directory) => {
|
|
445
|
-
if (fs.existsSync(directory)) {
|
|
446
|
-
fs.readdirSync(directory).forEach((file) => {
|
|
447
|
-
const curPath = path.join(directory, file);
|
|
448
|
-
if (fs.lstatSync(curPath).isDirectory()) {
|
|
449
|
-
clearDirectory(curPath);
|
|
450
|
-
fs.rmdirSync(curPath);
|
|
451
|
-
} else {
|
|
452
|
-
fs.unlinkSync(curPath);
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
//console.log(`❌ ${directory}`);
|
|
456
|
-
} else {
|
|
457
|
-
console.log(`Directory ${directory} does not exist, creating it...`);
|
|
458
|
-
fs.mkdirSync(directory, { recursive: true });
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const main = async () => {
|
|
463
|
-
// Ensure the skia submodule is on the correct branch for copying headers
|
|
464
|
-
await checkoutSkiaBranch(skiaVersion);
|
|
465
|
-
|
|
466
|
-
// Check if binaries are installed and checksums match
|
|
467
|
-
if (areBinariesInstalled() && verifyChecksums()) {
|
|
468
|
-
console.log("✅ Prebuilt binaries already installed with matching checksums, skipping download");
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (areBinariesInstalled()) {
|
|
473
|
-
console.log("⚠️ Binaries installed but checksums don't match, re-downloading...");
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
console.log("🧹 Clearing existing artifacts...");
|
|
477
|
-
clearDirectory(artifactsDir);
|
|
478
|
-
|
|
479
|
-
console.log(`⬇️ Downloading release assets to ${artifactsDir}`);
|
|
480
|
-
const tempDownloadDir = fs.mkdtempSync(
|
|
481
|
-
path.join(os.tmpdir(), "skia-download-")
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
for (const artifactName of names) {
|
|
485
|
-
const assetName = `${artifactName}-${releaseTag}.tar.gz`;
|
|
486
|
-
const extractDir = path.join(artifactsDir, artifactName);
|
|
487
|
-
const downloadUrl = `https://github.com/${repo}/releases/download/${releaseTag}/${assetName}`;
|
|
488
|
-
|
|
489
|
-
console.log(` Downloading ${assetName}...`);
|
|
490
|
-
try {
|
|
491
|
-
await downloadAndExtract(downloadUrl, extractDir, tempDownloadDir);
|
|
492
|
-
console.log(` ✓ ${assetName} extracted`);
|
|
493
|
-
} catch (error) {
|
|
494
|
-
console.error(` ✗ Failed to download ${assetName}:`, error);
|
|
495
|
-
fs.rmSync(tempDownloadDir, { recursive: true, force: true });
|
|
496
|
-
throw error;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
fs.rmSync(tempDownloadDir, { recursive: true, force: true });
|
|
500
|
-
|
|
501
|
-
// Copy artifacts to libs folder in the required structure
|
|
502
|
-
console.log("📦 Copying artifacts to libs folder...");
|
|
503
|
-
clearDirectory(libsDir);
|
|
504
|
-
|
|
505
|
-
// Create android directory structure
|
|
506
|
-
const androidDir = path.join(libsDir, "android");
|
|
507
|
-
fs.mkdirSync(androidDir, { recursive: true });
|
|
508
|
-
|
|
509
|
-
// Copy android artifacts
|
|
510
|
-
const androidArchs = GRAPHITE ? [
|
|
511
|
-
{ artifact: `${prefix}-android-arm`, srcSubdir: "arm", dest: "armeabi-v7a" },
|
|
512
|
-
{ artifact: `${prefix}-android-arm-64`, srcSubdir: "arm64", dest: "arm64-v8a" },
|
|
513
|
-
{ artifact: `${prefix}-android-arm-x86`, srcSubdir: "x86", dest: "x86" },
|
|
514
|
-
{ artifact: `${prefix}-android-arm-x64`, srcSubdir: "x64", dest: "x86_64" },
|
|
515
|
-
] : [
|
|
516
|
-
{ artifact: `${prefix}-android-arm`, srcSubdir: "armeabi-v7a", dest: "armeabi-v7a" },
|
|
517
|
-
{ artifact: `${prefix}-android-arm-64`, srcSubdir: "arm64-v8a", dest: "arm64-v8a" },
|
|
518
|
-
{ artifact: `${prefix}-android-arm-x86`, srcSubdir: "x86", dest: "x86" },
|
|
519
|
-
{ artifact: `${prefix}-android-arm-x64`, srcSubdir: "x86_64", dest: "x86_64" },
|
|
520
|
-
];
|
|
521
|
-
|
|
522
|
-
androidArchs.forEach(({ artifact, srcSubdir, dest }) => {
|
|
523
|
-
// The tar file extracts to artifactName/srcSubdir
|
|
524
|
-
const srcDir = path.join(artifactsDir, artifact, srcSubdir);
|
|
525
|
-
const destDir = path.join(androidDir, dest);
|
|
526
|
-
console.log(` Checking ${srcDir} -> ${destDir}`);
|
|
527
|
-
if (fs.existsSync(srcDir)) {
|
|
528
|
-
console.log(` ✓ Copying ${artifact}/${srcSubdir}`);
|
|
529
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
530
|
-
|
|
531
|
-
const copyDir = (srcPath, destPath) => {
|
|
532
|
-
fs.mkdirSync(destPath, { recursive: true });
|
|
533
|
-
fs.readdirSync(srcPath).forEach((file) => {
|
|
534
|
-
const srcFile = path.join(srcPath, file);
|
|
535
|
-
const destFile = path.join(destPath, file);
|
|
536
|
-
const stat = fs.lstatSync(srcFile);
|
|
537
|
-
|
|
538
|
-
// Skip sockets and other special files
|
|
539
|
-
if (stat.isSocket() || stat.isFIFO() || stat.isCharacterDevice() || stat.isBlockDevice()) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (stat.isDirectory()) {
|
|
544
|
-
copyDir(srcFile, destFile);
|
|
545
|
-
} else {
|
|
546
|
-
fs.copyFileSync(srcFile, destFile);
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
copyDir(srcDir, destDir);
|
|
552
|
-
} else {
|
|
553
|
-
console.log(` ✗ Source directory not found: ${srcDir}`);
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
// Create apple directory structure - now per-platform
|
|
558
|
-
const appleDir = path.join(libsDir, "apple");
|
|
559
|
-
fs.mkdirSync(appleDir, { recursive: true });
|
|
560
|
-
|
|
561
|
-
// Define the platform artifacts to process
|
|
562
|
-
// Note: maccatalyst is included in iOS xcframeworks, not a separate artifact
|
|
563
|
-
const applePlatformArtifacts = GRAPHITE
|
|
564
|
-
? [
|
|
565
|
-
{ artifact: `${prefix}-apple-ios-xcframeworks`, srcSubdir: "ios", dest: "ios" },
|
|
566
|
-
{ artifact: `${prefix}-apple-macos-xcframeworks`, srcSubdir: "macos", dest: "macos" },
|
|
567
|
-
]
|
|
568
|
-
: [
|
|
569
|
-
{ artifact: `${prefix}-apple-ios-xcframeworks`, srcSubdir: "ios", dest: "ios" },
|
|
570
|
-
{ artifact: `${prefix}-apple-tvos-xcframeworks`, srcSubdir: "tvos", dest: "tvos" },
|
|
571
|
-
{ artifact: `${prefix}-apple-macos-xcframeworks`, srcSubdir: "macos", dest: "macos" },
|
|
572
|
-
];
|
|
573
|
-
|
|
574
|
-
applePlatformArtifacts.forEach(({ artifact, srcSubdir, dest }) => {
|
|
575
|
-
// The tar file extracts to artifact_name/srcSubdir (e.g., skia-apple-ios-xcframeworks/ios)
|
|
576
|
-
const appleSrcDir = path.join(artifactsDir, artifact, srcSubdir);
|
|
577
|
-
const destDir = path.join(appleDir, dest);
|
|
578
|
-
|
|
579
|
-
console.log(` Checking ${appleSrcDir} -> ${destDir}`);
|
|
580
|
-
if (fs.existsSync(appleSrcDir)) {
|
|
581
|
-
console.log(` ✓ Copying ${artifact}/${srcSubdir}`);
|
|
582
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
583
|
-
|
|
584
|
-
// Copy all xcframeworks
|
|
585
|
-
fs.readdirSync(appleSrcDir).forEach((item) => {
|
|
586
|
-
const srcPath = path.join(appleSrcDir, item);
|
|
587
|
-
const destPath = path.join(destDir, item);
|
|
588
|
-
|
|
589
|
-
if (fs.lstatSync(srcPath).isDirectory()) {
|
|
590
|
-
// Copy directory recursively
|
|
591
|
-
const copyDir = (src, dest) => {
|
|
592
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
593
|
-
fs.readdirSync(src).forEach((file) => {
|
|
594
|
-
const srcFile = path.join(src, file);
|
|
595
|
-
const destFile = path.join(dest, file);
|
|
596
|
-
if (fs.lstatSync(srcFile).isDirectory()) {
|
|
597
|
-
copyDir(srcFile, destFile);
|
|
598
|
-
} else {
|
|
599
|
-
fs.copyFileSync(srcFile, destFile);
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
};
|
|
603
|
-
copyDir(srcPath, destPath);
|
|
604
|
-
} else {
|
|
605
|
-
fs.copyFileSync(srcPath, destPath);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
} else {
|
|
609
|
-
console.log(` ✗ Source directory not found: ${appleSrcDir}`);
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
// Create or remove Graphite marker files based on build type
|
|
614
|
-
const androidMarkerFile = path.join(androidDir, "graphite.enabled");
|
|
615
|
-
// Apple marker file stays at the apple root level (libs/apple/graphite.enabled)
|
|
616
|
-
const appleMarkerFile = path.join(appleDir, "graphite.enabled");
|
|
617
|
-
|
|
618
|
-
if (GRAPHITE) {
|
|
619
|
-
// Create marker files for Graphite builds
|
|
620
|
-
fs.writeFileSync(androidMarkerFile, "");
|
|
621
|
-
fs.writeFileSync(appleMarkerFile, "");
|
|
622
|
-
console.log("✓ Created Graphite marker files");
|
|
623
|
-
} else {
|
|
624
|
-
// Ensure marker files don't exist for non-Graphite builds
|
|
625
|
-
if (fs.existsSync(androidMarkerFile)) {
|
|
626
|
-
fs.unlinkSync(androidMarkerFile);
|
|
627
|
-
}
|
|
628
|
-
if (fs.existsSync(appleMarkerFile)) {
|
|
629
|
-
fs.unlinkSync(appleMarkerFile);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Copy Graphite headers if using Graphite
|
|
634
|
-
if (GRAPHITE) {
|
|
635
|
-
console.log("📦 Copying Graphite headers...");
|
|
636
|
-
const cppDir = path.resolve(__dirname, "../cpp");
|
|
637
|
-
const headersSrcDir = path.join(artifactsDir, `${prefix}-headers`);
|
|
638
|
-
|
|
639
|
-
console.log(` Looking for headers in: ${headersSrcDir}`);
|
|
640
|
-
console.log(` Headers dir exists: ${fs.existsSync(headersSrcDir)}`);
|
|
641
|
-
|
|
642
|
-
if (fs.existsSync(headersSrcDir)) {
|
|
643
|
-
console.log(` Contents: ${fs.readdirSync(headersSrcDir).join(", ")}`);
|
|
644
|
-
|
|
645
|
-
// The asset contains packages/skia/cpp structure, so we need to navigate into it
|
|
646
|
-
const packagesDir = path.join(headersSrcDir, "packages", "skia", "cpp");
|
|
647
|
-
console.log(` Looking for packages dir: ${packagesDir}`);
|
|
648
|
-
console.log(` Packages dir exists: ${fs.existsSync(packagesDir)}`);
|
|
649
|
-
|
|
650
|
-
if (fs.existsSync(packagesDir)) {
|
|
651
|
-
console.log(` Packages contents: ${fs.readdirSync(packagesDir).join(", ")}`);
|
|
652
|
-
|
|
653
|
-
// Copy dawn/include
|
|
654
|
-
const dawnIncludeSrc = path.join(packagesDir, "dawn", "include");
|
|
655
|
-
const dawnIncludeDest = path.join(cppDir, "dawn", "include");
|
|
656
|
-
console.log(` Dawn source: ${dawnIncludeSrc}`);
|
|
657
|
-
console.log(` Dawn source exists: ${fs.existsSync(dawnIncludeSrc)}`);
|
|
658
|
-
|
|
659
|
-
if (fs.existsSync(dawnIncludeSrc)) {
|
|
660
|
-
const copyDir = (src, dest) => {
|
|
661
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
662
|
-
fs.readdirSync(src).forEach((file) => {
|
|
663
|
-
const srcFile = path.join(src, file);
|
|
664
|
-
const destFile = path.join(dest, file);
|
|
665
|
-
if (fs.lstatSync(srcFile).isDirectory()) {
|
|
666
|
-
copyDir(srcFile, destFile);
|
|
667
|
-
} else {
|
|
668
|
-
fs.copyFileSync(srcFile, destFile);
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
};
|
|
672
|
-
copyDir(dawnIncludeSrc, dawnIncludeDest);
|
|
673
|
-
console.log(" ✓ Dawn headers copied");
|
|
674
|
-
} else {
|
|
675
|
-
console.log(" ✗ Dawn headers not found");
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// Copy graphite headers
|
|
679
|
-
const graphiteSrc = path.join(packagesDir, "skia", "src", "gpu", "graphite");
|
|
680
|
-
const graphiteDest = path.join(cppDir, "skia", "src", "gpu", "graphite");
|
|
681
|
-
console.log(` Graphite source: ${graphiteSrc}`);
|
|
682
|
-
console.log(` Graphite source exists: ${fs.existsSync(graphiteSrc)}`);
|
|
683
|
-
|
|
684
|
-
if (fs.existsSync(graphiteSrc)) {
|
|
685
|
-
const copyDir = (src, dest) => {
|
|
686
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
687
|
-
fs.readdirSync(src).forEach((file) => {
|
|
688
|
-
const srcFile = path.join(src, file);
|
|
689
|
-
const destFile = path.join(dest, file);
|
|
690
|
-
if (fs.lstatSync(srcFile).isDirectory()) {
|
|
691
|
-
copyDir(srcFile, destFile);
|
|
692
|
-
} else {
|
|
693
|
-
fs.copyFileSync(srcFile, destFile);
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
};
|
|
697
|
-
copyDir(graphiteSrc, graphiteDest);
|
|
698
|
-
console.log(" ✓ Graphite headers copied");
|
|
699
|
-
} else {
|
|
700
|
-
console.log(" ✗ Graphite headers not found");
|
|
701
|
-
}
|
|
702
|
-
} else {
|
|
703
|
-
console.log(" ✗ Packages directory not found in headers asset");
|
|
704
|
-
}
|
|
705
|
-
} else {
|
|
706
|
-
console.log(" ✗ Headers directory not found");
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
console.log("✅ Completed installation of Skia prebuilt binaries.");
|
|
711
|
-
|
|
712
|
-
// Calculate and update checksums in package.json
|
|
713
|
-
console.log("🔐 Calculating and updating checksums...");
|
|
714
|
-
const newChecksums = calculateLibraryChecksums();
|
|
715
|
-
updateSkiaChecksums(newChecksums, GRAPHITE);
|
|
716
|
-
console.log("✅ Checksums updated in package.json");
|
|
717
|
-
|
|
718
|
-
// Clean up artifacts directory
|
|
719
|
-
console.log("🗑️ Cleaning up artifacts directory...");
|
|
720
|
-
clearDirectory(artifactsDir);
|
|
721
|
-
fs.rmdirSync(artifactsDir);
|
|
722
|
-
};
|
|
723
|
-
|
|
724
|
-
// Run the main function
|
|
725
|
-
main().catch((error) => {
|
|
726
|
-
console.error("❌ Error:", error);
|
|
727
|
-
process.exit(1);
|
|
728
|
-
});
|