7zip-native 0.0.1 → 0.0.6
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 +255 -59
- package/dist/index.cjs +201 -40
- package/dist/index.d.cts +20 -7
- package/dist/index.d.mts +20 -7
- package/dist/index.d.ts +20 -7
- package/dist/index.mjs +202 -41
- package/package.json +3 -1
- package/prebuilds/darwin-arm64/7zip_native.node +0 -0
- package/prebuilds/darwin-arm64/lib7z.dylib +0 -0
- package/prebuilds/darwin-x64/7zip_native.node +0 -0
- package/prebuilds/darwin-x64/lib7z.dylib +0 -0
- package/prebuilds/linux-arm/7z.so +0 -0
- package/prebuilds/linux-arm/7zip_native.node +0 -0
- package/prebuilds/linux-arm64/7z.so +0 -0
- package/prebuilds/linux-arm64/7zip_native.node +0 -0
- package/prebuilds/linux-ia32/7z.so +0 -0
- package/prebuilds/linux-ia32/7zip_native.node +0 -0
- package/prebuilds/win32-arm64/7z.dll +0 -0
- package/prebuilds/win32-arm64/7zip_native.node +0 -0
- package/prebuilds/win32-x64/7z.dll +0 -0
- package/prebuilds/win32-x64/7zip_native.node +0 -0
- package/scripts/build-native.mjs +119 -0
- package/scripts/clean-upstream-build.mjs +9 -0
- package/scripts/verify-prebuild.mjs +29 -0
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, createWriteStream } from 'node:fs';
|
|
2
|
-
import { lstat, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { rename, rm, lstat, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { createRequire } from 'node:module';
|
|
4
5
|
import { availableParallelism } from 'node:os';
|
|
5
6
|
import { dirname, resolve, join, relative, sep } from 'node:path';
|
|
@@ -121,6 +122,19 @@ async function ensureDirectory(directoryPath) {
|
|
|
121
122
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Destination path is not a real directory: ${directoryPath}`);
|
|
122
123
|
}
|
|
123
124
|
}
|
|
125
|
+
async function assertReplaceableOutput(filePath) {
|
|
126
|
+
try {
|
|
127
|
+
const details = await lstat(filePath);
|
|
128
|
+
if (!details.isFile()) {
|
|
129
|
+
throw new ArchiveError('ERR_ARCHIVE_PATH', `Cannot replace a non-file extraction target: ${filePath}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof ArchiveError || error.code !== 'ENOENT') {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
124
138
|
async function ensureExtractionRoot(root) {
|
|
125
139
|
try {
|
|
126
140
|
const details = await lstat(root);
|
|
@@ -164,6 +178,7 @@ class NativeArchiveReadable extends Readable {
|
|
|
164
178
|
maxBytes;
|
|
165
179
|
streamId;
|
|
166
180
|
outputBytes = 0n;
|
|
181
|
+
starting = false;
|
|
167
182
|
constructor(start, resumeNative, cancel, options, maxBytes) {
|
|
168
183
|
super({ highWaterMark: options.highWaterMark, signal: options.signal });
|
|
169
184
|
this.start = start;
|
|
@@ -172,38 +187,63 @@ class NativeArchiveReadable extends Readable {
|
|
|
172
187
|
this.maxBytes = maxBytes;
|
|
173
188
|
}
|
|
174
189
|
_read() {
|
|
190
|
+
if (this.streamId !== undefined) {
|
|
191
|
+
try {
|
|
192
|
+
this.resumeNative(this.streamId);
|
|
193
|
+
}
|
|
194
|
+
catch (cause) {
|
|
195
|
+
this.destroy(extractionError(cause));
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (this.starting) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.starting = true;
|
|
175
203
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
204
|
+
Promise.resolve(this.start((chunk, error, done) => {
|
|
205
|
+
if (this.destroyed) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (error) {
|
|
209
|
+
this.destroy(extractionError(error));
|
|
210
|
+
}
|
|
211
|
+
else if (done) {
|
|
212
|
+
this.push(null);
|
|
213
|
+
}
|
|
214
|
+
else if (chunk) {
|
|
215
|
+
if (this.maxBytes !== null
|
|
216
|
+
&& this.outputBytes + BigInt(chunk.length) > this.maxBytes) {
|
|
217
|
+
this.destroy(new ArchiveError('ERR_RESOURCE_LIMIT', `Stream output exceeds the ${this.maxBytes}-byte limit`));
|
|
179
218
|
return;
|
|
180
219
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
this.outputBytes += BigInt(chunk.length);
|
|
194
|
-
this.push(chunk);
|
|
220
|
+
this.outputBytes += BigInt(chunk.length);
|
|
221
|
+
this.push(chunk);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
this.destroy(new ArchiveError('ERR_ARCHIVE_EXTRACT', 'Native stream returned an empty chunk notification'));
|
|
225
|
+
}
|
|
226
|
+
})).then((streamId) => {
|
|
227
|
+
this.streamId = streamId;
|
|
228
|
+
this.starting = false;
|
|
229
|
+
try {
|
|
230
|
+
if (this.destroyed) {
|
|
231
|
+
this.cancel(streamId);
|
|
195
232
|
}
|
|
196
233
|
else {
|
|
197
|
-
this.
|
|
234
|
+
this.resumeNative(streamId);
|
|
198
235
|
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
236
|
+
}
|
|
237
|
+
catch (cause) {
|
|
238
|
+
this.destroy(extractionError(cause));
|
|
239
|
+
}
|
|
240
|
+
}, (cause) => {
|
|
241
|
+
this.starting = false;
|
|
242
|
+
this.destroy(extractionError(cause));
|
|
243
|
+
});
|
|
205
244
|
}
|
|
206
245
|
catch (cause) {
|
|
246
|
+
this.starting = false;
|
|
207
247
|
this.destroy(extractionError(cause));
|
|
208
248
|
}
|
|
209
249
|
}
|
|
@@ -269,21 +309,41 @@ class Archive {
|
|
|
269
309
|
}
|
|
270
310
|
return (await this.entries()).find(entry => typeof idOrPath === 'number' ? entry.id === idOrPath : entry.path === idOrPath);
|
|
271
311
|
}
|
|
272
|
-
async extract(
|
|
312
|
+
async extract(idOrPathOrPaths) {
|
|
273
313
|
this.assertOpen();
|
|
274
|
-
const
|
|
275
|
-
|
|
314
|
+
const isBatch = Array.isArray(idOrPathOrPaths);
|
|
315
|
+
let selectors;
|
|
316
|
+
if (isBatch) {
|
|
317
|
+
selectors = idOrPathOrPaths;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
selectors = [idOrPathOrPaths];
|
|
321
|
+
}
|
|
322
|
+
const numericIds = selectors.filter((selector) => typeof selector === 'number');
|
|
323
|
+
let ids;
|
|
324
|
+
if (numericIds.length === selectors.length) {
|
|
325
|
+
this.validateIds(numericIds);
|
|
326
|
+
ids = numericIds;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
ids = await this.resolveEntryIds(selectors);
|
|
330
|
+
}
|
|
276
331
|
try {
|
|
277
332
|
const buffers = await native.extract(this.handle, ids);
|
|
278
|
-
return
|
|
333
|
+
return isBatch ? buffers : buffers[0];
|
|
279
334
|
}
|
|
280
335
|
catch (cause) {
|
|
281
336
|
throw extractionError(cause);
|
|
282
337
|
}
|
|
283
338
|
}
|
|
284
|
-
createReadStream(
|
|
339
|
+
createReadStream(idOrPath, options = {}) {
|
|
285
340
|
this.assertOpen();
|
|
286
|
-
|
|
341
|
+
if (typeof idOrPath === 'number') {
|
|
342
|
+
this.validateIds([idOrPath]);
|
|
343
|
+
}
|
|
344
|
+
else if (typeof idOrPath !== 'string') {
|
|
345
|
+
throw new TypeError('Entry selector must be an ID or archive path');
|
|
346
|
+
}
|
|
287
347
|
if (!options || typeof options !== 'object') {
|
|
288
348
|
throw new TypeError('Stream options must be an object');
|
|
289
349
|
}
|
|
@@ -295,13 +355,18 @@ class Archive {
|
|
|
295
355
|
}
|
|
296
356
|
options.signal?.throwIfAborted();
|
|
297
357
|
const maxBytes = normalizeMaxBytes(options.maxBytes, 'maxBytes');
|
|
298
|
-
return new NativeArchiveReadable(
|
|
358
|
+
return new NativeArchiveReadable((callback) => {
|
|
359
|
+
if (typeof idOrPath === 'number') {
|
|
360
|
+
return native.startStream(this.handle, idOrPath, callback);
|
|
361
|
+
}
|
|
362
|
+
return this.resolveEntryId(idOrPath).then(id => native.startStream(this.handle, id, callback));
|
|
363
|
+
}, streamId => native.resumeStream(streamId), streamId => native.cancelStream(streamId), options, maxBytes);
|
|
299
364
|
}
|
|
300
|
-
async extractToStream(
|
|
365
|
+
async extractToStream(idOrPath, destination, options = {}) {
|
|
301
366
|
if (!destination || typeof destination.write !== 'function') {
|
|
302
367
|
throw new TypeError('Destination must be a writable stream');
|
|
303
368
|
}
|
|
304
|
-
await pipeline(this.createReadStream(
|
|
369
|
+
await pipeline(this.createReadStream(idOrPath, options), destination, { signal: options.signal });
|
|
305
370
|
}
|
|
306
371
|
async extractToFolder(destination, options = {}) {
|
|
307
372
|
this.assertOpen();
|
|
@@ -311,10 +376,19 @@ class Archive {
|
|
|
311
376
|
if (!options || typeof options !== 'object') {
|
|
312
377
|
throw new TypeError('Folder extraction options must be an object');
|
|
313
378
|
}
|
|
379
|
+
if (options.overwrite !== undefined && typeof options.overwrite !== 'boolean') {
|
|
380
|
+
throw new TypeError('Folder extraction overwrite option must be a boolean');
|
|
381
|
+
}
|
|
382
|
+
if (options.onProgress !== undefined && typeof options.onProgress !== 'function') {
|
|
383
|
+
throw new TypeError('Folder extraction onProgress option must be a function');
|
|
384
|
+
}
|
|
314
385
|
options.signal?.throwIfAborted();
|
|
315
386
|
const maxBytes = normalizeMaxBytes(options.maxBytes, 'maxBytes');
|
|
316
387
|
const entries = await this.entries();
|
|
317
388
|
let selectedEntries = entries;
|
|
389
|
+
if (options.ids !== undefined && options.paths !== undefined) {
|
|
390
|
+
throw new TypeError('Specify either folder extraction IDs or paths, not both');
|
|
391
|
+
}
|
|
318
392
|
if (options.ids !== undefined) {
|
|
319
393
|
if (!Array.isArray(options.ids)) {
|
|
320
394
|
throw new TypeError('Folder extraction IDs must be an array');
|
|
@@ -329,6 +403,21 @@ class Archive {
|
|
|
329
403
|
return entry;
|
|
330
404
|
});
|
|
331
405
|
}
|
|
406
|
+
else if (options.paths !== undefined) {
|
|
407
|
+
if (!Array.isArray(options.paths)
|
|
408
|
+
|| options.paths.some(entryPath => typeof entryPath !== 'string')) {
|
|
409
|
+
throw new TypeError('Folder extraction paths must be an array of strings');
|
|
410
|
+
}
|
|
411
|
+
const ids = await this.resolveEntryIds(options.paths);
|
|
412
|
+
const entriesById = new Map(entries.map(entry => [entry.id, entry]));
|
|
413
|
+
selectedEntries = ids.map((id) => {
|
|
414
|
+
const entry = entriesById.get(id);
|
|
415
|
+
if (!entry) {
|
|
416
|
+
throw new ArchiveError('ERR_INVALID_ENTRY', `Entry ID ${id} is out of range`);
|
|
417
|
+
}
|
|
418
|
+
return entry;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
332
421
|
const root = resolve(destination);
|
|
333
422
|
const plans = makeOutputPlans(root, selectedEntries);
|
|
334
423
|
const expectedBytes = selectedEntries.reduce((total, entry) => total + (entry.isDirectory ? 0n : entry.size), 0n);
|
|
@@ -338,6 +427,26 @@ class Archive {
|
|
|
338
427
|
options.signal?.throwIfAborted();
|
|
339
428
|
await ensureExtractionRoot(root);
|
|
340
429
|
let extractedBytes = 0n;
|
|
430
|
+
let filesExtracted = 0;
|
|
431
|
+
const totalFiles = plans.filter(plan => !plan.isDirectory).length;
|
|
432
|
+
const reportProgress = () => {
|
|
433
|
+
let percent = 0;
|
|
434
|
+
if (expectedBytes > 0n) {
|
|
435
|
+
const scaledPercent = extractedBytes * 10000n / expectedBytes;
|
|
436
|
+
percent = Number(scaledPercent > 9900n ? 9900n : scaledPercent) / 100;
|
|
437
|
+
}
|
|
438
|
+
if (filesExtracted === totalFiles) {
|
|
439
|
+
percent = 100;
|
|
440
|
+
}
|
|
441
|
+
options.onProgress?.({
|
|
442
|
+
filesExtracted,
|
|
443
|
+
totalFiles,
|
|
444
|
+
bytesProcessed: extractedBytes,
|
|
445
|
+
totalBytes: expectedBytes,
|
|
446
|
+
percent,
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
reportProgress();
|
|
341
450
|
for (const plan of plans) {
|
|
342
451
|
options.signal?.throwIfAborted();
|
|
343
452
|
if (plan.isDirectory) {
|
|
@@ -345,7 +454,6 @@ class Archive {
|
|
|
345
454
|
continue;
|
|
346
455
|
}
|
|
347
456
|
await ensureOutputParents(root, plan.targetPath);
|
|
348
|
-
const output = createWriteStream(plan.targetPath, { flags: 'wx', mode: 0o600 });
|
|
349
457
|
const limit = new Transform({
|
|
350
458
|
transform(chunk, _encoding, callback) {
|
|
351
459
|
const size = BigInt(chunk.length);
|
|
@@ -354,18 +462,52 @@ class Archive {
|
|
|
354
462
|
return;
|
|
355
463
|
}
|
|
356
464
|
extractedBytes += size;
|
|
465
|
+
try {
|
|
466
|
+
reportProgress();
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
357
472
|
callback(null, chunk);
|
|
358
473
|
},
|
|
359
474
|
});
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
475
|
+
if (options.overwrite) {
|
|
476
|
+
await assertReplaceableOutput(plan.targetPath);
|
|
477
|
+
const temporaryPath = join(dirname(plan.targetPath), `.7zip-native-${randomUUID()}.tmp`);
|
|
478
|
+
try {
|
|
479
|
+
await pipeline(this.createReadStream(plan.entry.id, {
|
|
480
|
+
signal: options.signal,
|
|
481
|
+
maxBytes: null,
|
|
482
|
+
}), limit, createWriteStream(temporaryPath, { flags: 'wx', mode: 0o600 }), { signal: options.signal });
|
|
483
|
+
await assertReplaceableOutput(plan.targetPath);
|
|
484
|
+
await rename(temporaryPath, plan.targetPath);
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
await rm(temporaryPath, { force: true });
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
await pipeline(this.createReadStream(plan.entry.id, {
|
|
493
|
+
signal: options.signal,
|
|
494
|
+
maxBytes: null,
|
|
495
|
+
}), limit, createWriteStream(plan.targetPath, { flags: 'wx', mode: 0o600 }), { signal: options.signal });
|
|
496
|
+
}
|
|
497
|
+
filesExtracted++;
|
|
498
|
+
reportProgress();
|
|
364
499
|
}
|
|
365
500
|
}
|
|
366
|
-
async read(
|
|
501
|
+
async read(idOrPath, options) {
|
|
367
502
|
this.assertOpen();
|
|
368
|
-
|
|
503
|
+
let id;
|
|
504
|
+
if (typeof idOrPath === 'number') {
|
|
505
|
+
this.validateIds([idOrPath]);
|
|
506
|
+
id = idOrPath;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
id = await this.resolveEntryId(idOrPath);
|
|
510
|
+
}
|
|
369
511
|
const offset = this.toUnsignedBigInt(options.offset ?? 0, 'offset');
|
|
370
512
|
const length = this.toUnsignedBigInt(options.length, 'length');
|
|
371
513
|
const maxUInt64 = (1n << 64n) - 1n;
|
|
@@ -396,6 +538,25 @@ class Archive {
|
|
|
396
538
|
throw new ArchiveError('ERR_INVALID_ENTRY', 'Entry IDs must be unique non-negative safe integers');
|
|
397
539
|
}
|
|
398
540
|
}
|
|
541
|
+
async resolveEntryId(selector) {
|
|
542
|
+
if (typeof selector === 'number') {
|
|
543
|
+
this.validateIds([selector]);
|
|
544
|
+
return selector;
|
|
545
|
+
}
|
|
546
|
+
if (typeof selector !== 'string') {
|
|
547
|
+
throw new TypeError('Entry selector must be an ID or archive path');
|
|
548
|
+
}
|
|
549
|
+
const entry = await this.getEntry(selector);
|
|
550
|
+
if (!entry) {
|
|
551
|
+
throw new ArchiveError('ERR_INVALID_ENTRY', `Archive entry "${selector}" was not found`);
|
|
552
|
+
}
|
|
553
|
+
return entry.id;
|
|
554
|
+
}
|
|
555
|
+
async resolveEntryIds(selectors) {
|
|
556
|
+
const ids = await Promise.all(selectors.map(selector => this.resolveEntryId(selector)));
|
|
557
|
+
this.validateIds(ids);
|
|
558
|
+
return ids;
|
|
559
|
+
}
|
|
399
560
|
toUnsignedBigInt(value, label) {
|
|
400
561
|
if (typeof value === 'bigint') {
|
|
401
562
|
if (value < 0n) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "7zip-native",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Native 7zip bindings for Node.js",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dist/",
|
|
31
31
|
"prebuilds/",
|
|
32
32
|
"native/",
|
|
33
|
+
"scripts/",
|
|
33
34
|
"vendor/7zip/",
|
|
34
35
|
"binding.gyp",
|
|
35
36
|
"README.md",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"url": "https://github.com/sponsors/ollm"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
52
|
+
"install": "node scripts/verify-prebuild.mjs",
|
|
51
53
|
"prepare": "npm run build && node scripts/clean-upstream-build.mjs",
|
|
52
54
|
"build": "npm run build:native && npm run eslint && npx rollup -c && npm run typecheck",
|
|
53
55
|
"build:native": "node scripts/build-native.mjs",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {spawn} from 'node:child_process';
|
|
2
|
+
import { copyFile, mkdir, rm } from 'node:fs/promises';
|
|
3
|
+
import {createRequire} from 'node:module';
|
|
4
|
+
import {availableParallelism} from 'node:os';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
|
|
8
|
+
const root = resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
9
|
+
const bundleDirectory = join(root, 'vendor', '7zip', 'CPP', '7zip', 'Bundles', 'Format7zF');
|
|
10
|
+
const releaseDirectory = join(root, 'build', 'Release');
|
|
11
|
+
const targetArch = process.env.BUILD_ARCH || process.arch;
|
|
12
|
+
const target = `${process.platform}-${targetArch}`;
|
|
13
|
+
const engineName = process.platform === 'win32'
|
|
14
|
+
? '7z.dll'
|
|
15
|
+
: process.platform === 'darwin' ? 'lib7z.dylib' : '7z.so';
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
|
|
18
|
+
function run(command, args, cwd, env = process.env)
|
|
19
|
+
{
|
|
20
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
21
|
+
const child = spawn(command, args, {cwd, env, stdio: 'inherit'});
|
|
22
|
+
child.once('error', rejectPromise);
|
|
23
|
+
child.once('close', (code, signal) => {
|
|
24
|
+
if(code === 0)
|
|
25
|
+
{
|
|
26
|
+
resolvePromise();
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
{
|
|
30
|
+
rejectPromise(new Error(`${command} exited with ${signal ?? code}`));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if(process.platform === 'win32')
|
|
37
|
+
{
|
|
38
|
+
const upstreamArch = targetArch === 'ia32' ? 'x86' : targetArch;
|
|
39
|
+
await run('nmake', ['/f', 'makefile', `PLATFORM=${upstreamArch}`], bundleDirectory);
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
{
|
|
43
|
+
const args = [
|
|
44
|
+
'-C',
|
|
45
|
+
bundleDirectory,
|
|
46
|
+
'-f',
|
|
47
|
+
'makefile.gcc',
|
|
48
|
+
`-j${process.env.BUILD_JOBS ?? Math.min(availableParallelism(), 2)}`,
|
|
49
|
+
];
|
|
50
|
+
const buildEnv = {...process.env};
|
|
51
|
+
if(targetArch === 'ia32')
|
|
52
|
+
{
|
|
53
|
+
args.push('MY_ARCH=-m32', 'CC=gcc', 'CXX=g++');
|
|
54
|
+
buildEnv.CXXFLAGS = `${buildEnv.CXXFLAGS ?? ''} -m32`.trim();
|
|
55
|
+
buildEnv.LDFLAGS = `${buildEnv.LDFLAGS ?? ''} -m32`.trim();
|
|
56
|
+
}
|
|
57
|
+
else if(targetArch === 'arm')
|
|
58
|
+
{
|
|
59
|
+
const compilerPrefix = 'arm-linux-gnueabihf-';
|
|
60
|
+
args.push(
|
|
61
|
+
`CC=${compilerPrefix}gcc`,
|
|
62
|
+
`CXX=${compilerPrefix}g++`,
|
|
63
|
+
'MY_ARCH=-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard',
|
|
64
|
+
);
|
|
65
|
+
buildEnv.CC = `${compilerPrefix}gcc`;
|
|
66
|
+
buildEnv.CXX = `${compilerPrefix}g++`;
|
|
67
|
+
buildEnv.CXXFLAGS = `${buildEnv.CXXFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
|
|
68
|
+
buildEnv.LDFLAGS = `${buildEnv.LDFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
|
|
69
|
+
}
|
|
70
|
+
if(process.platform === 'darwin')
|
|
71
|
+
{
|
|
72
|
+
args.push(
|
|
73
|
+
'LIB2=-lpthread',
|
|
74
|
+
'PROG=lib7z',
|
|
75
|
+
'SHARED_EXT=.dylib',
|
|
76
|
+
'LDFLAGS=-shared -fPIC -Wl,-install_name,@rpath/lib7z.dylib',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
await run('make', args, root, buildEnv);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const upstreamEnginePath = process.platform === 'win32'
|
|
83
|
+
? join(bundleDirectory, targetArch === 'ia32' ? 'x86' : targetArch, engineName)
|
|
84
|
+
: join(bundleDirectory, '_o', engineName);
|
|
85
|
+
const nodeGypArgs = [
|
|
86
|
+
require.resolve('node-gyp/bin/node-gyp.js'),
|
|
87
|
+
'rebuild',
|
|
88
|
+
];
|
|
89
|
+
const nodeGypEnv = {...process.env};
|
|
90
|
+
if(targetArch !== process.arch)
|
|
91
|
+
{
|
|
92
|
+
nodeGypArgs.push(`--arch=${targetArch}`);
|
|
93
|
+
if(targetArch === 'ia32')
|
|
94
|
+
{
|
|
95
|
+
nodeGypEnv.CC = 'gcc';
|
|
96
|
+
nodeGypEnv.CXX = 'g++';
|
|
97
|
+
nodeGypEnv.CXXFLAGS = `${nodeGypEnv.CXXFLAGS ?? ''} -m32`.trim();
|
|
98
|
+
nodeGypEnv.LDFLAGS = `${nodeGypEnv.LDFLAGS ?? ''} -m32`.trim();
|
|
99
|
+
}
|
|
100
|
+
else if(targetArch === 'arm')
|
|
101
|
+
{
|
|
102
|
+
nodeGypEnv.CC = 'arm-linux-gnueabihf-gcc';
|
|
103
|
+
nodeGypEnv.CXX = 'arm-linux-gnueabihf-g++';
|
|
104
|
+
nodeGypEnv.CXXFLAGS = `${nodeGypEnv.CXXFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
|
|
105
|
+
nodeGypEnv.LDFLAGS = `${nodeGypEnv.LDFLAGS ?? ''} -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard`.trim();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
await run(process.execPath, nodeGypArgs, root, nodeGypEnv);
|
|
109
|
+
|
|
110
|
+
await mkdir(releaseDirectory, {recursive: true});
|
|
111
|
+
const releaseEnginePath = join(releaseDirectory, engineName);
|
|
112
|
+
await copyFile(upstreamEnginePath, releaseEnginePath);
|
|
113
|
+
const prebuildDirectory = join(root, 'prebuilds', target);
|
|
114
|
+
await rm(prebuildDirectory, {recursive: true, force: true});
|
|
115
|
+
await mkdir(prebuildDirectory, {recursive: true});
|
|
116
|
+
await Promise.all([
|
|
117
|
+
copyFile(join(releaseDirectory, '7zip_native.node'), join(prebuildDirectory, '7zip_native.node')),
|
|
118
|
+
copyFile(releaseEnginePath, join(prebuildDirectory, engineName)),
|
|
119
|
+
]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {rm} from 'node:fs/promises';
|
|
2
|
+
import {fileURLToPath} from 'node:url';
|
|
3
|
+
|
|
4
|
+
const upstreamBuildDirectory = new URL(
|
|
5
|
+
'../vendor/7zip/CPP/7zip/Bundles/Format7zF/_o/',
|
|
6
|
+
import.meta.url,
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
await rm(fileURLToPath(upstreamBuildDirectory), {recursive: true, force: true});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {existsSync} from 'node:fs';
|
|
2
|
+
import {dirname, join} from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
|
|
5
|
+
const packageDirectory = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const target = `${process.platform}-${process.arch}`;
|
|
7
|
+
const engineName = process.platform === 'win32'
|
|
8
|
+
? '7z.dll'
|
|
9
|
+
: process.platform === 'darwin' ? 'lib7z.dylib' : '7z.so';
|
|
10
|
+
const runtimeDirectories = [
|
|
11
|
+
join(packageDirectory, `../prebuilds/${target}`),
|
|
12
|
+
join(packageDirectory, '../build/Release'),
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
if(runtimeDirectories.some(directory =>
|
|
16
|
+
existsSync(join(directory, '7zip_native.node')) && existsSync(join(directory, engineName))))
|
|
17
|
+
{
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if(existsSync(join(packageDirectory, '../.git')))
|
|
22
|
+
{
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
throw new Error(
|
|
27
|
+
`No prebuilt 7zip-native binaries are available for ${target}. `
|
|
28
|
+
+ 'Check the supported platforms in the package README.',
|
|
29
|
+
);
|