|
| 1 | +'use strict'; |
| 2 | +const common = require('../common'); |
| 3 | + |
| 4 | +// This tests the creation of a single executable application. |
| 5 | + |
| 6 | +const fixtures = require('../common/fixtures'); |
| 7 | +const tmpdir = require('../common/tmpdir'); |
| 8 | +const { copyFileSync, readFileSync, writeFileSync, existsSync } = require('fs'); |
| 9 | +const { execFileSync } = require('child_process'); |
| 10 | +const { join } = require('path'); |
| 11 | +const { strictEqual } = require('assert'); |
| 12 | +const assert = require('assert'); |
| 13 | + |
| 14 | +if (!process.config.variables.single_executable_application) |
| 15 | + common.skip('Single Executable Application support has been disabled.'); |
| 16 | + |
| 17 | +if (!['darwin', 'win32', 'linux'].includes(process.platform)) |
| 18 | + common.skip(`Unsupported platform ${process.platform}.`); |
| 19 | + |
| 20 | +if (process.platform === 'linux' && process.config.variables.is_debug === 1) |
| 21 | + common.skip('Running the resultant binary fails with `Couldn\'t read target executable"`.'); |
| 22 | + |
| 23 | +if (process.config.variables.node_shared) |
| 24 | + common.skip('Running the resultant binary fails with ' + |
| 25 | + '`/home/iojs/node-tmp/.tmp.2366/sea: error while loading shared libraries: ' + |
| 26 | + 'libnode.so.112: cannot open shared object file: No such file or directory`.'); |
| 27 | + |
| 28 | +if (process.config.variables.icu_gyp_path === 'tools/icu/icu-system.gyp') |
| 29 | + common.skip('Running the resultant binary fails with ' + |
| 30 | + '`/home/iojs/node-tmp/.tmp.2379/sea: error while loading shared libraries: ' + |
| 31 | + 'libicui18n.so.71: cannot open shared object file: No such file or directory`.'); |
| 32 | + |
| 33 | +if (!process.config.variables.node_use_openssl || process.config.variables.node_shared_openssl) |
| 34 | + common.skip('Running the resultant binary fails with `Node.js is not compiled with OpenSSL crypto support`.'); |
| 35 | + |
| 36 | +if (process.config.variables.want_separate_host_toolset !== 0) |
| 37 | + common.skip('Running the resultant binary fails with `Segmentation fault (core dumped)`.'); |
| 38 | + |
| 39 | +if (process.platform === 'linux') { |
| 40 | + const osReleaseText = readFileSync('/etc/os-release', { encoding: 'utf-8' }); |
| 41 | + const isAlpine = /^NAME="Alpine Linux"/m.test(osReleaseText); |
| 42 | + if (isAlpine) common.skip('Alpine Linux is not supported.'); |
| 43 | + |
| 44 | + if (process.arch === 's390x') { |
| 45 | + common.skip('On s390x, postject fails with `memory access out of bounds`.'); |
| 46 | + } |
| 47 | + |
| 48 | + if (process.arch === 'ppc64') { |
| 49 | + common.skip('On ppc64, this test times out.'); |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +const inputFile = fixtures.path('sea.js'); |
| 54 | +const requirableFile = join(tmpdir.path, 'requirable.js'); |
| 55 | +const configFile = join(tmpdir.path, 'sea-config.json'); |
| 56 | +const seaPrepBlob = join(tmpdir.path, 'sea-prep.blob'); |
| 57 | +const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : 'sea'); |
| 58 | + |
| 59 | +tmpdir.refresh(); |
| 60 | + |
| 61 | +writeFileSync(requirableFile, ` |
| 62 | +module.exports = { |
| 63 | + hello: 'world', |
| 64 | +}; |
| 65 | +`); |
| 66 | + |
| 67 | +writeFileSync(configFile, ` |
| 68 | +{ |
| 69 | + "main": "sea.js", |
| 70 | + "output": "sea-prep.blob", |
| 71 | + "disableExperimentalSEAWarning": true |
| 72 | +} |
| 73 | +`); |
| 74 | + |
| 75 | +// Copy input to working directory |
| 76 | +copyFileSync(inputFile, join(tmpdir.path, 'sea.js')); |
| 77 | +execFileSync(process.execPath, ['--experimental-sea-config', 'sea-config.json'], { |
| 78 | + cwd: tmpdir.path |
| 79 | +}); |
| 80 | + |
| 81 | +assert(existsSync(seaPrepBlob)); |
| 82 | + |
| 83 | +copyFileSync(process.execPath, outputFile); |
| 84 | +const postjectFile = fixtures.path('postject-copy', 'node_modules', 'postject', 'dist', 'cli.js'); |
| 85 | +execFileSync(process.execPath, [ |
| 86 | + postjectFile, |
| 87 | + outputFile, |
| 88 | + 'NODE_SEA_BLOB', |
| 89 | + seaPrepBlob, |
| 90 | + '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', |
| 91 | + ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], |
| 92 | +]); |
| 93 | + |
| 94 | +if (process.platform === 'darwin') { |
| 95 | + execFileSync('codesign', [ '--sign', '-', outputFile ]); |
| 96 | + execFileSync('codesign', [ '--verify', outputFile ]); |
| 97 | +} else if (process.platform === 'win32') { |
| 98 | + let signtoolFound = false; |
| 99 | + try { |
| 100 | + execFileSync('where', [ 'signtool' ]); |
| 101 | + signtoolFound = true; |
| 102 | + } catch (err) { |
| 103 | + console.log(err.message); |
| 104 | + } |
| 105 | + if (signtoolFound) { |
| 106 | + let certificatesFound = false; |
| 107 | + try { |
| 108 | + execFileSync('signtool', [ 'sign', '/fd', 'SHA256', outputFile ]); |
| 109 | + certificatesFound = true; |
| 110 | + } catch (err) { |
| 111 | + if (!/SignTool Error: No certificates were found that met all the given criteria/.test(err)) { |
| 112 | + throw err; |
| 113 | + } |
| 114 | + } |
| 115 | + if (certificatesFound) { |
| 116 | + execFileSync('signtool', 'verify', '/pa', 'SHA256', outputFile); |
| 117 | + } |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +const singleExecutableApplicationOutput = execFileSync( |
| 122 | + outputFile, |
| 123 | + [ '-a', '--b=c', 'd' ], |
| 124 | + { env: { COMMON_DIRECTORY: join(__dirname, '..', 'common') } }); |
| 125 | +strictEqual(singleExecutableApplicationOutput.toString(), 'Hello, world! 😊\n'); |
0 commit comments