koffi 2.3.5 → 2.3.6-beta.1

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +4 -4
  3. package/src/cnoke/cnoke.js +40 -834
  4. package/src/cnoke/package.json +1 -0
  5. package/src/cnoke/src/builder.js +447 -0
  6. package/src/cnoke/src/index.js +20 -0
  7. package/src/cnoke/src/tools.js +401 -0
  8. package/src/koffi/build/2.3.6-beta.1/koffi_darwin_arm64/koffi.node +0 -0
  9. package/src/koffi/build/2.3.6-beta.1/koffi_darwin_x64/koffi.node +0 -0
  10. package/src/koffi/build/2.3.6-beta.1/koffi_freebsd_arm64/koffi.node +0 -0
  11. package/src/koffi/build/2.3.6-beta.1/koffi_freebsd_ia32/koffi.node +0 -0
  12. package/src/koffi/build/2.3.6-beta.1/koffi_freebsd_x64/koffi.node +0 -0
  13. package/src/koffi/build/2.3.6-beta.1/koffi_linux_arm32hf/koffi.node +0 -0
  14. package/src/koffi/build/2.3.6-beta.1/koffi_linux_arm64/koffi.node +0 -0
  15. package/src/koffi/build/2.3.6-beta.1/koffi_linux_ia32/koffi.node +0 -0
  16. package/src/koffi/build/2.3.6-beta.1/koffi_linux_riscv64hf64/koffi.node +0 -0
  17. package/src/koffi/build/2.3.6-beta.1/koffi_linux_x64/koffi.node +0 -0
  18. package/src/koffi/build/2.3.6-beta.1/koffi_openbsd_ia32/koffi.node +0 -0
  19. package/src/koffi/build/2.3.6-beta.1/koffi_openbsd_x64/koffi.node +0 -0
  20. package/src/koffi/build/2.3.6-beta.1/koffi_win32_arm64/koffi.exp +0 -0
  21. package/src/koffi/build/2.3.6-beta.1/koffi_win32_arm64/koffi.lib +0 -0
  22. package/src/koffi/build/2.3.6-beta.1/koffi_win32_arm64/koffi.node +0 -0
  23. package/src/koffi/build/2.3.6-beta.1/koffi_win32_arm64/koffi.pdb +0 -0
  24. package/src/koffi/build/2.3.6-beta.1/koffi_win32_ia32/koffi.exp +0 -0
  25. package/src/koffi/build/2.3.6-beta.1/koffi_win32_ia32/koffi.lib +0 -0
  26. package/src/koffi/build/2.3.6-beta.1/koffi_win32_ia32/koffi.node +0 -0
  27. package/src/koffi/build/2.3.6-beta.1/koffi_win32_ia32/koffi.pdb +0 -0
  28. package/src/koffi/build/2.3.6-beta.1/koffi_win32_x64/koffi.exp +0 -0
  29. package/src/koffi/build/2.3.6-beta.1/koffi_win32_x64/koffi.lib +0 -0
  30. package/src/koffi/build/2.3.6-beta.1/koffi_win32_x64/koffi.node +0 -0
  31. package/src/koffi/build/2.3.6-beta.1/koffi_win32_x64/koffi.pdb +0 -0
  32. package/src/koffi/src/ffi.cc +2 -2
  33. package/src/koffi/src/index.d.ts +3 -3
  34. package/src/koffi/src/index.js +23 -4
  35. package/src/koffi/build/2.3.5/koffi_darwin_arm64.tar.gz +0 -0
  36. package/src/koffi/build/2.3.5/koffi_darwin_x64.tar.gz +0 -0
  37. package/src/koffi/build/2.3.5/koffi_freebsd_arm64.tar.gz +0 -0
  38. package/src/koffi/build/2.3.5/koffi_freebsd_ia32.tar.gz +0 -0
  39. package/src/koffi/build/2.3.5/koffi_freebsd_x64.tar.gz +0 -0
  40. package/src/koffi/build/2.3.5/koffi_linux_arm32hf.tar.gz +0 -0
  41. package/src/koffi/build/2.3.5/koffi_linux_arm64.tar.gz +0 -0
  42. package/src/koffi/build/2.3.5/koffi_linux_ia32.tar.gz +0 -0
  43. package/src/koffi/build/2.3.5/koffi_linux_riscv64hf64.tar.gz +0 -0
  44. package/src/koffi/build/2.3.5/koffi_linux_x64.tar.gz +0 -0
  45. package/src/koffi/build/2.3.5/koffi_openbsd_ia32.tar.gz +0 -0
  46. package/src/koffi/build/2.3.5/koffi_openbsd_x64.tar.gz +0 -0
  47. package/src/koffi/build/2.3.5/koffi_win32_arm64.tar.gz +0 -0
  48. package/src/koffi/build/2.3.5/koffi_win32_ia32.tar.gz +0 -0
  49. package/src/koffi/build/2.3.5/koffi_win32_x64.tar.gz +0 -0
@@ -15,56 +15,31 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const crypto = require('crypto');
19
18
  const fs = require('fs');
20
- const http = require('https');
21
- const os = require('os');
22
- const path = require('path');
23
- const process = require('process');
24
- const zlib = require('zlib');
25
- const { spawnSync } = require('child_process');
26
- const { Buffer } = require('buffer');
27
-
28
- // Globals
29
-
30
- const default_mode = 'RelWithDebInfo';
31
-
32
- let app_dir = null;
33
- let project_dir = null;
34
- let package_dir = null;
35
- let cache_dir = null;
36
- let build_dir = null;
37
- let work_dir = null;
38
-
39
- let runtime_version = null;
40
- let arch = null;
41
- let toolset = null;
42
- let prefer_clang = false;
43
- let mode = default_mode;
44
- let targets = [];
45
- let verbose = false;
46
- let prebuild = false;
47
- let prebuild_url = null;
48
- let prebuild_req = null;
49
-
50
- let cmake_bin = null;
51
-
52
- // Main
19
+ const { Builder } = require('./src/index.js');
20
+
21
+ const VALID_COMMANDS = ['build', 'configure', 'clean'];
22
+ const DEFAULT_MODE = 'RelWithDebInfo';
53
23
 
54
24
  main();
55
25
 
56
26
  async function main() {
57
- let command = build;
27
+ let config = {};
28
+ let command = 'build';
29
+
30
+ // Default options
31
+ config.mode = DEFAULT_MODE;
58
32
 
59
33
  // Parse options
60
34
  {
61
35
  let i = 2;
62
36
 
63
37
  if (process.argv.length >= 3 && process.argv[2][0] != '-') {
64
- switch (process.argv[2]) {
65
- case 'build': { command = build; i++; } break;
66
- case 'configure': { command = configure; i++; } break;
67
- case 'clean': { command = clean; i++; } break;
38
+ let cmd = process.argv[2];
39
+
40
+ if (VALID_COMMANDS.includes(cmd)) {
41
+ command = cmd;
42
+ i++;
68
43
  }
69
44
  }
70
45
 
@@ -97,56 +72,50 @@ async function main() {
97
72
  if (value == null)
98
73
  throw new Error(`Missing value for ${arg}`);
99
74
 
100
- project_dir = fs.realpathSync(value);
101
- } else if ((command == build || command == configure) && (arg == '-v' || arg == '--runtime-version')) {
75
+ config.project_dir = fs.realpathSync(value);
76
+ } else if ((command == 'build' || command == 'configure') && (arg == '-v' || arg == '--runtime-version')) {
102
77
  if (value == null)
103
78
  throw new Error(`Missing value for ${arg}`);
104
79
  if (!value.match(/^[0-9]+\.[0-9]+\.[0-9]+$/))
105
80
  throw new Error(`Malformed runtime version '${value}'`);
106
81
 
107
- runtime_version = value;
108
- } else if ((command == build || command == configure) && (arg == '-a' || arg == '--arch')) {
82
+ config.runtime_version = value;
83
+ } else if ((command == 'build' || command == 'configure') && (arg == '-a' || arg == '--arch')) {
109
84
  if (value == null)
110
85
  throw new Error(`Missing value for ${arg}`);
111
86
 
112
- arch = value;
113
- } else if ((command == build || command == configure) && (arg == '-t' || arg == '--toolset')) {
87
+ config.arch = value;
88
+ } else if ((command == 'build' || command == 'configure') && (arg == '-t' || arg == '--toolset')) {
114
89
  if (value == null)
115
90
  throw new Error(`Missing value for ${arg}`);
116
91
 
117
- toolset = value;
118
- } else if ((command == build || command == configure) && (arg == '-C' || arg == '--prefer-clang')) {
119
- prefer_clang = true;
120
- } else if ((command == build || command == configure) && (arg == '-B' || arg == '--config')) {
92
+ config.toolset = value;
93
+ } else if ((command == 'build' || command == 'configure') && (arg == '-C' || arg == '--prefer-clang')) {
94
+ config.prefer_clang = true;
95
+ } else if ((command == 'build' || command == 'configure') && (arg == '-B' || arg == '--config')) {
121
96
  if (value == null)
122
97
  throw new Error(`Missing value for ${arg}`);
123
98
 
124
99
  switch (value.toLowerCase()) {
125
- case 'relwithdebinfo': { mode = 'RelWithDebInfo'; } break;
126
- case 'debug': { mode = 'Debug'; } break;
127
- case 'release': { mode = 'Release'; } break;
100
+ case 'relwithdebinfo': { config.mode = 'RelWithDebInfo'; } break;
101
+ case 'debug': { config.mode = 'Debug'; } break;
102
+ case 'release': { config.mode = 'Release'; } break;
128
103
 
129
104
  default: {
130
105
  throw new Error(`Unknown value '${value}' for ${arg}`);
131
106
  } break;
132
107
  }
133
- } else if ((command == build || command == configure) && (arg == '-D' || arg == '--debug')) {
134
- mode = 'Debug';
135
- } else if (command == build && arg == '--verbose') {
136
- verbose = true;
137
- } else if (command == build && arg == '--prebuild') {
138
- prebuild = true;
139
- prebuild_url = value;
140
- } else if (command == build && arg == '--require') {
108
+ } else if ((command == 'build' || command == 'configure') && (arg == '-D' || arg == '--debug')) {
109
+ config.mode = 'Debug';
110
+ } else if (command == 'build' && arg == '--verbose') {
111
+ config.verbose = true;
112
+ } else if (command == 'build' && arg == '--prebuild') {
113
+ config.prebuild = true;
114
+ } else if (command == 'build' && (arg == '-T' || arg == '--target')) {
141
115
  if (value == null)
142
116
  throw new Error(`Missing value for ${arg}`);
143
117
 
144
- prebuild_req = value;
145
- } else if (command == build && (arg == '-T' || arg == '--target')) {
146
- if (value == null)
147
- throw new Error(`Missing value for ${arg}`);
148
-
149
- targets = [value];
118
+ config.targets = [value];
150
119
  } else {
151
120
  if (arg[0] == '-') {
152
121
  throw new Error(`Unexpected argument '${arg}'`);
@@ -157,24 +126,9 @@ async function main() {
157
126
  }
158
127
  }
159
128
 
160
- if (runtime_version == null)
161
- runtime_version = process.version;
162
- if (runtime_version.startsWith('v'))
163
- runtime_version = runtime_version.substr(1);
164
- if (arch == null)
165
- arch = determine_arch();
166
-
167
- app_dir = __dirname.replace(/\\/g, '/');
168
- if (project_dir == null)
169
- project_dir = process.cwd();
170
- project_dir = project_dir.replace(/\\/g, '/');
171
- package_dir = find_parent_directory(project_dir, 'package.json');
172
- cache_dir = get_cache_directory();
173
- build_dir = project_dir + '/build';
174
- work_dir = build_dir + `/v${runtime_version}_${arch}`;
175
-
176
129
  try {
177
- await command();
130
+ let builder = new Builder(config);
131
+ await builder[command]();
178
132
  } catch (err) {
179
133
  console.error(err);
180
134
  process.exit(1);
@@ -194,14 +148,13 @@ Options:
194
148
  (default: current working directory)
195
149
 
196
150
  -B, --config <CONFIG> Change build type: RelWithDebInfo, Debug, Release
197
- (default: ${default_mode})
151
+ (default: ${DEFAULT_MODE})
198
152
  -D, --debug Shortcut for --config Debug
199
153
 
200
- --prebuild [URL] Enable use of prebuilt binaries
201
- --require <PATH> Require specified module, drop prebuild if it fails
154
+ --prebuild Use prebuilt binari if available
202
155
 
203
156
  -a, --arch <ARCH> Change architecture and ABI
204
- (default: ${determine_arch()})
157
+ (default: ${cnoke.determine_arch()})
205
158
  -v, --runtime-version <VERSION> Change node version
206
159
  (default: ${process.version})
207
160
  -t, --toolset <TOOLSET> Change default CMake toolset
@@ -219,750 +172,3 @@ The ARCH value is similar to process.arch, with the following differences:
219
172
 
220
173
  console.log(help);
221
174
  }
222
-
223
- // Commands
224
-
225
- async function configure(retry = true) {
226
- let args = [project_dir];
227
-
228
- check_cmake();
229
- check_compatibility();
230
-
231
- console.log(`>> Node: ${runtime_version}`);
232
- console.log(`>> Target: ${process.platform}_${arch}`);
233
-
234
- // Prepare build directory
235
- fs.mkdirSync(cache_dir, { recursive: true, mode: 0o755 });
236
- fs.mkdirSync(build_dir, { recursive: true, mode: 0o755 });
237
- fs.mkdirSync(work_dir, { recursive: true, mode: 0o755 });
238
-
239
- retry &= fs.existsSync(work_dir + '/CMakeCache.txt');
240
-
241
- // Download Node headers
242
- {
243
- let basename = `node-v${runtime_version}-headers.tar.gz`;
244
- let urls = [
245
- `https://nodejs.org/dist/v${runtime_version}/${basename}`,
246
- `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${basename}`
247
- ];
248
- let destname = `${cache_dir}/${basename}`;
249
-
250
- if (!fs.existsSync(destname))
251
- await download(urls, destname);
252
- await extract_targz(destname, work_dir + '/headers', 1);
253
- }
254
-
255
- // Download Node import library (Windows)
256
- if (process.platform === 'win32') {
257
- let dirname;
258
- switch (arch) {
259
- case 'ia32': { dirname = 'win-x86'; } break;
260
- case 'x64': { dirname = 'win-x64'; } break;
261
- case 'arm64': { dirname = 'win-arm64'; } break;
262
-
263
- default: {
264
- throw new Error(`Unsupported architecture '${arch}' for Node on Windows`);
265
- } break;
266
- }
267
-
268
- let destname = `${cache_dir}/node_v${runtime_version}_${arch}.lib`;
269
-
270
- if (!fs.existsSync(destname)) {
271
- let urls = [
272
- `https://nodejs.org/dist/v${runtime_version}/${dirname}/node.lib`,
273
- `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${dirname}/node.lib`
274
- ];
275
- await download(urls, destname);
276
- }
277
-
278
- fs.copyFileSync(destname, work_dir + '/node.lib');
279
- }
280
-
281
- args.push(`-DCMAKE_MODULE_PATH=${app_dir}/assets`);
282
-
283
- args.push(`-DNODE_JS_INCLUDE_DIRS=${work_dir}/headers/include/node`);
284
-
285
- // Set platform flags
286
- switch (process.platform) {
287
- case 'win32': {
288
- fs.copyFileSync(`${app_dir}/assets/win_delay_hook.c`, work_dir + '/win_delay_hook.c');
289
-
290
- args.push(`-DNODE_JS_SOURCES=${work_dir}/win_delay_hook.c`);
291
- args.push(`-DNODE_JS_LIBRARIES=${work_dir}/node.lib`);
292
-
293
- switch (arch) {
294
- case 'ia32': {
295
- args.push('-DNODE_JS_LINK_FLAGS=/DELAYLOAD:node.exe;/SAFESEH:NO');
296
- args.push('-A', 'Win32');
297
- } break;
298
- case 'arm64': {
299
- args.push('-DNODE_JS_LINK_FLAGS=/DELAYLOAD:node.exe;/SAFESEH:NO');
300
- args.push('-A', 'ARM64');
301
- } break;
302
- case 'x64': {
303
- args.push('-DNODE_JS_LINK_FLAGS=/DELAYLOAD:node.exe');
304
- args.push('-A', 'x64');
305
- } break;
306
- }
307
- } break;
308
-
309
- case 'darwin': {
310
- args.push('-DNODE_JS_LINK_FLAGS=-undefined;dynamic_lookup');
311
-
312
- switch (arch) {
313
- case 'arm64': { args.push('-DCMAKE_OSX_ARCHITECTURES=arm64'); } break;
314
- case 'x64': { args.push('-DCMAKE_OSX_ARCHITECTURES=x86_64'); } break;
315
- }
316
- } break;
317
- }
318
-
319
- if (process.platform != 'win32') {
320
- // Prefer Ninja if available
321
- if (spawnSync('ninja', ['--version']).status === 0)
322
- args.push('-G', 'Ninja');
323
-
324
- // Use CCache if available
325
- if (spawnSync('ccache', ['--version']).status === 0) {
326
- args.push('-DCMAKE_C_COMPILER_LAUNCHER=ccache');
327
- args.push('-DCMAKE_CXX_COMPILER_LAUNCHER=ccache');
328
- }
329
- }
330
-
331
- if (prefer_clang) {
332
- if (process.platform == 'win32') {
333
- args.push('-T', 'ClangCL');
334
- } else {
335
- args.push('-DCMAKE_C_COMPILER=clang');
336
- args.push('-DCMAKE_CXX_COMPILER=clang++');
337
- }
338
- }
339
- if (toolset != null)
340
- args.push('-T', toolset);
341
-
342
- args.push(`-DCMAKE_BUILD_TYPE=${mode}`);
343
- for (let type of ['ARCHIVE', 'RUNTIME', 'LIBRARY']) {
344
- for (let suffix of ['', '_DEBUG', '_RELEASE', '_RELWITHDEBINFO'])
345
- args.push(`-DCMAKE_${type}_OUTPUT_DIRECTORY${suffix}=${build_dir}`);
346
- }
347
- args.push('--no-warn-unused-cli');
348
-
349
- console.log('>> Running configuration');
350
-
351
- let proc = spawnSync(cmake_bin, args, { cwd: work_dir, stdio: 'inherit' });
352
- if (proc.status !== 0) {
353
- unlink_recursive(work_dir);
354
- if (retry)
355
- return configure(false);
356
-
357
- throw new Error('Failed to run configure step');
358
- }
359
- }
360
-
361
- async function build() {
362
- check_compatibility();
363
-
364
- if (prebuild) {
365
- let pkg = read_package_json();
366
-
367
- if (prebuild_url == null) {
368
- if (pkg.cnoke.prebuild == null)
369
- throw new Error('Missing prebuild URL');
370
-
371
- prebuild_url = pkg.cnoke.prebuild;
372
- }
373
-
374
- fs.mkdirSync(build_dir, { recursive: true, mode: 0o755 });
375
-
376
- let url = prebuild_url.replace(/{{([a-zA-Z_][a-zA-Z_0-9]*)}}/g, (match, p1) => {
377
- switch (p1) {
378
- case 'version': {
379
- let pkg = read_package_json();
380
- return pkg.version || '';
381
- } break;
382
- case 'platform': return process.platform;
383
- case 'arch': return arch;
384
-
385
- default: return match;
386
- }
387
- });
388
- let basename = path.basename(url);
389
-
390
- try {
391
- let archive_filename = null;
392
-
393
- if (url.startsWith('file:/')) {
394
- if (url.startsWith('file://localhost/')) {
395
- url = url.substr(16);
396
- } else {
397
- let offset = 6;
398
- while (offset < 9 && url[offset] == '/')
399
- offset++;
400
- url = url.substr(offset - 1);
401
- }
402
-
403
- if (process.platform == 'win32' && url.match(/^\/[a-zA-Z]+:[\\\/]/))
404
- url = url.substr(1);
405
- }
406
-
407
- if (url.match(/^[a-z]+:\/\//)) {
408
- archive_filename = build_dir + '/' + basename;
409
- await download(url, archive_filename);
410
- } else {
411
- if (path_is_absolute(url)) {
412
- archive_filename = url;
413
- } else if (package_dir != null) {
414
- archive_filename = package_dir + '/' + url;
415
- } else {
416
- archive_filename = project_dir + '/' + url;
417
- }
418
- if (!fs.existsSync(archive_filename))
419
- throw new Error('Cannot find local prebuilt archive');
420
- }
421
-
422
- console.log('>> Extracting prebuilt binaries...');
423
- await extract_targz(archive_filename, build_dir, 1);
424
-
425
- // Make sure the binary works
426
- if (prebuild_req == null)
427
- prebuild_req = pkg.cnoke.require;
428
- if (prebuild_req != null) {
429
- let proc = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', prebuild_req]);
430
- if (proc.status === 0)
431
- return;
432
- } else {
433
- return;
434
- }
435
-
436
- // Clean it up if it does not, before source build. Only delete files, it is safer and it is enough!
437
- let entries = fs.readdirSync(build_dir, { withFileTypes: true });
438
- for (let entry of entries) {
439
- if (!entry.isDirectory()) {
440
- let filename = path.join(build_dir, entry.name);
441
- fs.unlinkSync(filename);
442
- }
443
- }
444
-
445
- console.error('Failed to load prebuilt binary, rebuilding from source');
446
- } catch (err) {
447
- console.error('Failed to find prebuilt binary for your platform, building manually');
448
- }
449
- }
450
-
451
- check_cmake();
452
-
453
- if (!fs.existsSync(work_dir + '/CMakeCache.txt'))
454
- await configure();
455
-
456
- // In case Make gets used
457
- if (process.env.MAKEFLAGS == null)
458
- process.env.MAKEFLAGS = '-j' + os.cpus().length;
459
-
460
- let args = [
461
- '--build', work_dir,
462
- '--config', mode
463
- ];
464
-
465
- if (verbose)
466
- args.push('--verbose');
467
- for (let target of targets)
468
- args.push('--target', target);
469
-
470
- console.log('>> Running build');
471
-
472
- let proc = spawnSync(cmake_bin, args, { stdio: 'inherit' });
473
- if (proc.status !== 0)
474
- throw new Error('Failed to run build step');
475
- }
476
-
477
- async function clean() {
478
- unlink_recursive(build_dir);
479
- }
480
-
481
- // Utility
482
-
483
- function get_cache_directory() {
484
- if (process.platform == 'win32') {
485
- let cache_dir = process.env['APPDATA'];
486
- if (cache_dir == null)
487
- throw new Error('Missing APPDATA environment variable');
488
-
489
- cache_dir = path.join(cache_dir, 'cnoke');
490
- return cache_dir;
491
- } else {
492
- let cache_dir = process.env['XDG_CACHE_HOME'];
493
-
494
- if (cache_dir == null) {
495
- let home = process.env['HOME'];
496
- if (home == null)
497
- throw new Error('Missing HOME environment variable');
498
-
499
- cache_dir = path.join(home, '.cache');
500
- }
501
-
502
- cache_dir = path.join(cache_dir, 'cnoke');
503
- return cache_dir;
504
- }
505
- }
506
-
507
- function check_cmake() {
508
- if (cmake_bin != null)
509
- return;
510
-
511
- // Check for CMakeLists.txt
512
- if (!fs.existsSync(project_dir + '/CMakeLists.txt'))
513
- throw new Error('This directory does not appear to have a CMakeLists.txt file');
514
-
515
- // Check for CMake
516
- {
517
- let proc = spawnSync('cmake', ['--version']);
518
-
519
- if (proc.status === 0) {
520
- cmake_bin = 'cmake';
521
- } else {
522
- if (process.platform == 'win32') {
523
- // I really don't want to depend on anything in CNoke, and Node.js does not provide
524
- // anything to read from the registry. This is okay, REG.exe exists since Windows XP.
525
- let proc = spawnSync('reg', ['query', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Kitware\\CMake', '/v', 'InstallDir']);
526
-
527
- if (proc.status === 0) {
528
- let matches = proc.stdout.toString('utf-8').match(/InstallDir[ \t]+REG_[A-Z_]+[ \t]+(.*)+/);
529
-
530
- if (matches != null) {
531
- let bin = path.join(matches[1].trim(), 'bin\\cmake.exe');
532
-
533
- if (fs.existsSync(bin))
534
- cmake_bin = bin;
535
- }
536
- }
537
- }
538
-
539
- if (cmake_bin == null)
540
- throw new Error('CMake does not seem to be available');
541
- }
542
- }
543
-
544
- console.log(`>> Using CMake binary: ${cmake_bin}`);
545
- }
546
-
547
- function check_compatibility() {
548
- let pkg = read_package_json();
549
-
550
- if (pkg.cnoke.node != null && cmp_version(runtime_version, pkg.cnoke.node) < 0)
551
- throw new Error(`Project ${pkg.name} requires Node.js >= ${pkg.cnoke.node}`);
552
-
553
- if (pkg.cnoke.napi != null) {
554
- let major = parseInt(runtime_version, 10);
555
- let required = get_napi_version(pkg.cnoke.napi, major);
556
-
557
- if (required == null)
558
- throw new Error(`Project ${pkg.name} does not support the Node ${major}.x branch (old or missing N-API)`);
559
- if (cmp_version(runtime_version, required) < 0)
560
- throw new Error(`Project ${pkg.name} requires Node >= ${required} in the Node ${major}.x branch (with N-API >= ${pkg.engines.napi})`);
561
- }
562
- }
563
-
564
- function read_package_json() {
565
- let pkg = {};
566
-
567
- if (package_dir != null) {
568
- try {
569
- let json = fs.readFileSync(package_dir + '/package.json', { encoding: 'utf-8' });
570
- pkg = JSON.parse(json);
571
- } catch (err) {
572
- if (err.code != 'ENOENT')
573
- throw err;
574
- }
575
- }
576
-
577
- if (pkg.cnoke == null)
578
- pkg.cnoke = {};
579
-
580
- return pkg;
581
- }
582
-
583
- function unlink_recursive(path) {
584
- try {
585
- if (fs.rmSync != null) {
586
- fs.rmSync(path, { recursive: true, maxRetries: process.platform == 'win32' ? 3 : 0 });
587
- } else {
588
- fs.rmdirSync(path, { recursive: true, maxRetries: process.platform == 'win32' ? 3 : 0 });
589
- }
590
- } catch (err) {
591
- if (err.code !== 'ENOENT')
592
- throw err;
593
- }
594
- }
595
-
596
- async function download(url, dest) {
597
- if (Array.isArray(url)) {
598
- let urls = url;
599
-
600
- for (let url of urls) {
601
- try {
602
- await download(url, dest);
603
- return;
604
- } catch (err) {
605
- if (err.code != 404)
606
- throw err;
607
- }
608
- }
609
-
610
- throw new Error('All URLs returned error 404');
611
- }
612
-
613
- console.log('>> Downloading ' + url);
614
-
615
- let [tmp_name, file] = open_temporary_stream(dest);
616
-
617
- try {
618
- await new Promise((resolve, reject) => {
619
- let request = http.get(url, response => {
620
- if (response.statusCode != 200) {
621
- let err = new Error(`Download failed: ${response.statusMessage} [${response.statusCode}]`);
622
- err.code = response.statusCode;
623
-
624
- reject(err);
625
-
626
- return;
627
- }
628
-
629
- response.pipe(file);
630
-
631
- file.on('finish', () => file.close(() => {
632
- try {
633
- fs.renameSync(file.path, dest);
634
- } catch (err) {
635
- if (err.code != 'EBUSY')
636
- reject(err);
637
- }
638
-
639
- resolve();
640
- }));
641
- });
642
-
643
- request.on('error', reject);
644
- file.on('error', reject);
645
- });
646
- } catch (err) {
647
- file.close();
648
-
649
- try {
650
- fs.unlinkSync(tmp_name);
651
- } catch (err) {
652
- if (err.code != 'ENOENT')
653
- throw err;
654
- }
655
-
656
- throw err;
657
- }
658
- }
659
-
660
- function open_temporary_stream(prefix) {
661
- let buf = Buffer.allocUnsafe(4);
662
-
663
- for (;;) {
664
- try {
665
- crypto.randomFillSync(buf);
666
-
667
- let suffix = buf.toString('hex').padStart(8, '0');
668
- let filename = `${prefix}.${suffix}`;
669
-
670
- let file = fs.createWriteStream(filename, { flags: 'wx', mode: 0o644 });
671
- return [filename, file];
672
- } catch (err) {
673
- if (err.code != 'EEXIST')
674
- throw err;
675
- }
676
- }
677
- }
678
-
679
- function read_file_header(filename, read) {
680
- let fd = null;
681
-
682
- try {
683
- let fd = fs.openSync(filename);
684
-
685
- let buf = Buffer.allocUnsafe(read);
686
- let len = fs.readSync(fd, buf);
687
-
688
- return buf.subarray(0, len);
689
- } finally {
690
- if (fd != null)
691
- fs.closeSync(fd);
692
- }
693
- }
694
-
695
- function extract_targz(filename, dest_dir, strip = 0) {
696
- let reader = fs.createReadStream(filename).pipe(zlib.createGunzip());
697
-
698
- return new Promise((resolve, reject) => {
699
- let header = null;
700
- let extended = {};
701
-
702
- reader.on('readable', () => {
703
- try {
704
- for (;;) {
705
- if (header == null) {
706
- let buf = reader.read(512);
707
- if (buf == null)
708
- break;
709
-
710
- // Two zeroed 512-byte blocks end the stream
711
- if (!buf[0])
712
- continue;
713
-
714
- header = {
715
- filename: buf.toString('utf-8', 0, 100).replace(/\0/g, ''),
716
- mode: parseInt(buf.toString('ascii', 100, 109), 8),
717
- size: parseInt(buf.toString('ascii', 124, 137), 8),
718
- type: String.fromCharCode(buf[156])
719
- };
720
-
721
- // UStar filename prefix
722
- /*if (buf.toString('ascii', 257, 263) == 'ustar\0') {
723
- let prefix = buf.toString('utf-8', 345, 500).replace(/\0/g, '');
724
- console.log(prefix);
725
- header.filename = prefix ? (prefix + '/' + header.filename) : header.filename;
726
- }*/
727
-
728
- Object.assign(header, extended);
729
- extended = {};
730
-
731
- // Safety checks
732
- header.filename = header.filename.replace(/\\/g, '/');
733
- if (!header.filename.length)
734
- throw new Error(`Insecure empty filename inside TAR archive`);
735
- if (header.filename[0] == '/')
736
- throw new Error(`Insecure filename starting with / inside TAR archive`);
737
- if (path_has_dotdot(header.filename))
738
- throw new Error(`Insecure filename containing '..' inside TAR archive`);
739
-
740
- for (let i = 0; i < strip; i++)
741
- header.filename = header.filename.substr(header.filename.indexOf('/') + 1);
742
- }
743
-
744
- let aligned = Math.floor((header.size + 511) / 512) * 512;
745
- let data = header.size ? reader.read(aligned) : null;
746
- if (data == null) {
747
- if (header.size)
748
- break;
749
- data = Buffer.alloc(0);
750
- }
751
- data = data.subarray(0, header.size);
752
-
753
- if (header.type == '0' || header.type == '7') {
754
- let filename = dest_dir + '/' + header.filename;
755
- let dirname = path.dirname(filename);
756
-
757
- fs.mkdirSync(dirname, { recursive: true, mode: 0o755 });
758
- fs.writeFileSync(filename, data, { mode: header.mode });
759
- } else if (header.type == '5') {
760
- let filename = dest_dir + '/' + header.filename;
761
- fs.mkdirSync(filename, { recursive: true, mode: header.mode });
762
- } else if (header.type == 'L') { // GNU tar
763
- extended.filename = data.toString('utf-8').replace(/\0/g, '');
764
- } else if (header.type == 'x') { // PAX entry
765
- let str = data.toString('utf-8');
766
-
767
- try {
768
- while (str.length) {
769
- let matches = str.match(/^([0-9]+) ([a-zA-Z0-9\._]+)=(.*)\n/);
770
-
771
- let skip = parseInt(matches[1], 10);
772
- let key = matches[2];
773
- let value = matches[3];
774
-
775
- switch (key) {
776
- case 'path': { extended.filename = value; } break;
777
- case 'size': { extended.size = parseInt(value, 10); } break;
778
- }
779
-
780
- str = str.substr(skip).trimStart();
781
- }
782
- } catch (err) {
783
- throw new Error('Malformed PAX entry');
784
- }
785
- }
786
-
787
- header = null;
788
- }
789
- } catch (err) {
790
- reject(err);
791
- }
792
- });
793
-
794
- reader.on('error', reject);
795
- reader.on('end', resolve);
796
- });
797
- }
798
-
799
- function path_is_absolute(path) {
800
- if (process.platform == 'win32' && path.match(/^[a-zA-Z]:/))
801
- path = path.substr(2);
802
- return is_path_separator(path[0]);
803
- }
804
-
805
- function path_has_dotdot(path) {
806
- let start = 0;
807
-
808
- for (;;) {
809
- let offset = path.indexOf('..', start);
810
- if (offset < 0)
811
- break;
812
- start = offset + 2;
813
-
814
- if (offset && !is_path_separator(path[offset - 1]))
815
- continue;
816
- if (offset + 2 < path.length && !is_path_separator(path[offset + 2]))
817
- continue;
818
-
819
- return true;
820
- }
821
-
822
- return false;
823
- }
824
-
825
- function is_path_separator(c) {
826
- if (c == '/')
827
- return true;
828
- if (process.platform == 'win32' && c == '\\')
829
- return true;
830
-
831
- return false;
832
- }
833
-
834
- function find_parent_directory(dirname, basename)
835
- {
836
- if (process.platform == 'win32')
837
- dirname = dirname.replace(/\\/g, '/');
838
-
839
- do {
840
- if (fs.existsSync(dirname + '/' + basename))
841
- return dirname;
842
-
843
- dirname = path.dirname(dirname);
844
- } while (!dirname.endsWith('/'));
845
-
846
- return null;
847
- }
848
-
849
- function determine_arch() {
850
- let arch = process.arch;
851
-
852
- if (arch == 'riscv32' || arch == 'riscv64') {
853
- let buf = read_file_header(process.execPath, 512);
854
- let header = decode_elf_header(buf);
855
- let float_abi = (header.e_flags & 0x6) >> 1;
856
-
857
- switch (float_abi) {
858
- case 0: { arch += 'sf'; } break;
859
- case 1: { arch += 'hf32'; } break;
860
- case 2: { arch += 'hf64'; } break;
861
- case 3: { arch += 'hf128'; } break;
862
- }
863
- } else if (arch == 'arm') {
864
- arch = 'arm32';
865
-
866
- let buf = read_file_header(process.execPath, 512);
867
- let header = decode_elf_header(buf);
868
-
869
- if (header.e_flags & 0x400) {
870
- arch += 'hf';
871
- } else if (header.e_flags & 0x200) {
872
- arch += 'sf';
873
- } else {
874
- throw new Error('Unknown ARM floating-point ABI');
875
- }
876
- }
877
-
878
- return arch;
879
- }
880
-
881
- function decode_elf_header(buf) {
882
- let header = {};
883
-
884
- if (buf.length < 16)
885
- throw new Error('Truncated header');
886
- if (buf[0] != 0x7F || buf[1] != 69 || buf[2] != 76 || buf[3] != 70)
887
- throw new Error('Invalid magic number');
888
- if (buf[6] != 1)
889
- throw new Error('Invalid ELF version');
890
- if (buf[5] != 1)
891
- throw new Error('Big-endian architectures are not supported');
892
-
893
- let machine = buf.readUInt16LE(18);
894
-
895
- switch (machine) {
896
- case 3: { header.e_machine = 'ia32'; } break;
897
- case 40: { header.e_machine = 'arm'; } break;
898
- case 62: { header.e_machine = 'amd64'; } break;
899
- case 183: { header.e_machine = 'arm64'; } break;
900
- case 243: {
901
- switch (buf[4]) {
902
- case 1: { header.e_machine = 'riscv32'; } break;
903
- case 2: { header.e_machine = 'riscv64'; } break;
904
- }
905
- } break;
906
- default: throw new Error('Unknown ELF machine type');
907
- }
908
-
909
- switch (buf[4]) {
910
- case 1: { // 32 bit
911
- buf = buf.subarray(0, 68);
912
- if (buf.length < 68)
913
- throw new Error('Truncated ELF header');
914
-
915
- header.ei_class = 32;
916
- header.e_flags = buf.readUInt32LE(36);
917
- } break;
918
- case 2: { // 64 bit
919
- buf = buf.subarray(0, 120);
920
- if (buf.length < 120)
921
- throw new Error('Truncated ELF header');
922
-
923
- header.ei_class = 64;
924
- header.e_flags = buf.readUInt32LE(48);
925
- } break;
926
- default: throw new Error('Invalid ELF class');
927
- }
928
-
929
- return header;
930
- }
931
-
932
- // Versioning
933
-
934
- function get_napi_version(napi, major) {
935
- if (napi > 8)
936
- return null;
937
-
938
- // https://nodejs.org/api/n-api.html#node-api-version-matrix
939
- const support = {
940
- 6: ['6.14.2', '6.14.2', '6.14.2'],
941
- 8: ['8.6.0', '8.10.0', '8.11.2'],
942
- 9: ['9.0.0', '9.3.0', '9.11.0'],
943
- 10: ['10.0.0', '10.0.0', '10.0.0', '10.16.0', '10.17.0', '10.20.0', '10.23.0'],
944
- 11: ['11.0.0', '11.0.0', '11.0.0', '11.8.0'],
945
- 12: ['12.0.0', '12.0.0', '12.0.0', '12.0.0', '12.11.0', '12.17.0', '12.19.0', '12.22.0'],
946
- 13: ['13.0.0', '13.0.0', '13.0.0', '13.0.0', '13.0.0'],
947
- 14: ['14.0.0', '14.0.0', '14.0.0', '14.0.0', '14.0.0', '14.0.0', '14.12.0', '14.17.0'],
948
- 15: ['15.0.0', '15.0.0', '15.0.0', '15.0.0', '15.0.0', '15.0.0', '15.0.0', '15.12.0']
949
- };
950
- const max = Math.max(...Object.keys(support).map(k => parseInt(k, 10)));
951
-
952
- if (major > max)
953
- return major + '.0.0';
954
- if (support[major] == null)
955
- return null;
956
-
957
- let required = support[major][napi - 1] || null;
958
- return required;
959
- }
960
-
961
- // Ignores prerelease suffixes
962
- function cmp_version(ver1, ver2) {
963
- ver1 = String(ver1).replace(/-.*$/, '').split('.').reduce((acc, v, idx) => acc + parseInt(v, 10) * Math.pow(10, 2 * (5 - idx)), 0);
964
- ver2 = String(ver2).replace(/-.*$/, '').split('.').reduce((acc, v, idx) => acc + parseInt(v, 10) * Math.pow(10, 2 * (5 - idx)), 0);
965
-
966
- let cmp = Math.min(Math.max(ver1 - ver2, -1), 1);
967
- return cmp;
968
- }