pkg
pkg copied to clipboard
Cant run executable from cmd spawned by itself
Problem
PKG throws erroor when running windows executable from a process spawned by itself
Steps to Reproduce
lets say I have a simple nodejs app like this:
import cp from 'child_process';
cp.exec('start cmd');
so obviously it starts a new cmd every time it gets executed. Now I build it with PKG into start.exe
, then I open a cmd in the folder and run start.exe
. It works fine and opens a new cmd, now, if I run start.exe
in this new cmd, I'll get the following error:
pkg/prelude/bootstrap.js:1523
if (error.code !== 'MODULE_NOT_FOUND') throw error;
^
TypeError: Cannot read property 'charAt' of undefined
[90m at Function.Module._resolveLookupPaths (internal/modules/cjs/loader.js:617:15)[39m
[90m at Function.Module._resolveFilename (internal/modules/cjs/loader.js:834:20)[39m
at Function._resolveFilename (pkg/prelude/bootstrap.js:1521:44)
[90m at Function.Module._load (internal/modules/cjs/loader.js:725:27)[39m
at Function.runMain (pkg/prelude/bootstrap.js:1558:12)
[90m at internal/main/run_main_module.js:17:47[39m
Apparently, it cannot find the file name because argument[0]
is undefined
while it's supposed to be a path
CC @leerob @jesec @igorklopov
btw this works fine when I directly run the source with node
I'm having the same issue!
same issue, run itself, even with bash sub process
same
PKG modify the node native child_process lib. (prelude\bootstrap.js line 1990).
It add a var env PKG_EXECPATH
wich is used in (prelude\bootstrap.js line 77).
if (process.env.PKG_EXECPATH === EXECPATH) {
process.argv.splice(1, 1); // Remove PKG_DUMMY_ENTRYPOINT
if (process.argv[1] && process.argv[1] !== '-') {
// https://github.com/nodejs/node/blob/1a96d83a223ff9f05f7d942fb84440d323f7b596/lib/internal/bootstrap/node.js#L269
process.argv[1] = path.resolve(process.argv[1]);
}
} else {
process.argv[1] = DEFAULT_ENTRYPOINT;
}
[, ENTRYPOINT] = process.argv;
delete process.env.PKG_EXECPATH;
When script.exe spawn itselft, PKG_EXECPATH and EXECPATH is the same.
This cause ENTRYPOINT have the wrong value.
script used to reproduce
const readline = require('readline')
const { spawn } = require('child_process')
const spawnSelf = function () {
return new Promise((resolve, reject) => {
try {
const worker = spawn(process.execPath, ['child'], { env: {} })
worker.on('error', (error) => {
console.error(`Error ${error.stack}`)
return reject(error)
})
worker.on('exit', code => {
console.log(`Exit code ${code}`)
return resolve()
})
const lineParser = readline.createInterface({ input: worker.stdout })
const stderrParser = readline.createInterface({ input: worker.stderr })
stderrParser.on('line', line => {
console.error(`Stderr : ${line.toString()}`)
})
lineParser.on('line', data => {
console.info(`Stdout : ${data.toString()}`)
})
}
catch (error) {
return reject(error)
}
})
}
const test = async function () {
console.log('process.argv0', process.argv0)
console.log('process.execPath', process.execPath)
if (process.argv.includes('child')) {
console.log('CHILD SPAWNED SUCCESS')
process.exit(0)
}
await spawnSelf()
}
test()
.catch(console.error)
Can you help us solve this issue ?
PR?
What do you mean by PR ? I don't have any code fixing this issue to push
You have identified where the issue is, just try to fix it
Of course we will work on this issue and submit a PR when we find a fix.
But it would be nice to have some help on this, it seems many people opened issue regarding this king of bug. So you should have some experience on this point.
There is some part in the code we dont understand. For exemple :
- What is the usage of PKG_DUMMY_ENTRYPOINT
- Why does pkg add PKG_EXECPATH
- What is the purpose of this code (line 80 in bootstrap.js
if (process.argv[1] && process.argv[1] !== '-') {
// https://github.com/nodejs/node/blob/1a96d83a223ff9f05f7d942fb84440d323f7b596/lib/internal/bootstrap/node.js#L269
process.argv[1] = path.resolve(process.argv[1]);
}
Is there any documentation on how pkg work ? It will help us understanding and fixing this issue
I started a wiki for devs some months ago, try to read this: https://github.com/vercel/pkg/wiki/Developers
Thanks
Current logic
FORK : [process.exe, PKG_DUMMY_ENTRYPOINT, child.js, arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH => delete PKG_DUMMY_ENTRYPOINT
CLUSTER : [process.exe, PKG_DUMMY_ENTRYPOINT, process.js, arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH => delete PKG_DUMMY_ENTRYPOINT
SPAWN : [process_2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...] => replace PKG_DUMMY_ENTRYPOINT by DEFAULT_ENTRYPOINT
SPAWN SELF : [process.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH => delete PKG_DUMMY_ENTRYPOINT => /!\ KO [process.exe, arg1, arg2, ...] /!\
Expected logic
FORK : [process.exe, child.js, arg1, arg2, ...]
CLUSTER : [process.exe, process.js, arg1, arg2, ...]
SPAWN : [process2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]
SPAWN SELF : [process.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]
if args[1] === PKG_DUMMY_ENTRYPOINT => replace PKG_DUMMY_ENTRYPOINT with DEFAULT_ENTRYPOINT
Analyse
PKG includes a mecanism to detect forked process in order to fix the args. (bootstrap line 77). It use process.env.PKG_EXECPATH === EXECPATH to detect it.
When we use spawn, execFile, ... to spawn process.execPath
. PKG detect that PKG_EXECPATH is equal to EXECPATH and think this is a fork.
The issue is that the arguments of a fork and a spawn are not the same.
- fork : process.exe, PKG_DUMMY_ENTRYPOINT, child.js, arg1, arg2, ...
- spawn : process_2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...
This cause PKG crashed is that case.
Proposal
Improving the PKG_DUMMY_ENTRYPOINT logic is painfull, because this arg is added through a NodeJS patch in pkg-fetch.
We need to keep the current logic with PKG_EXECPATH.
We should detect the spawn self case and modify the argument of the spawn to match the arguments signature of a fork.
process.argv[1] is the entrypoint of a PKG process.
SPAWN SELF : [process.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...] => [process.exe, PKG_DUMMY_ENTRYPOINT, process.argv[1], arg1, arg2, ...]
Fix
bootstrap.js setOptsEnv()
Pseudo code fix. isNotCalledByFork logic to be defined
if (args[0] === ARGV0 || args[0] === ENTRYPOINT || args[0] === EXECPATH) { // Spawn self
if(isNotCalledByFork()) { // To be defined
if(!Array.isArray(args[1])) args[1] = [];
args[1] = [process.argv[1], ...args[1]];
}
}
Workarounds
spawn(process.execPath, [process.argv[1], arg1, arg2, ...], {})
fork(process.argv[1], [arg1, arg2, ...], {})
Too painfull to detect the right case and apply the fix just for that case. I think i will stop working on this issue.
I can live with the work around. (Adding process.argv[1]
as first arg of the spawn command).
spawn(process.execPath, [process.argv[1], arg1, arg2, ...], options)
spawn(myprocess.exe, [process.argv[1], arg1, arg2, ...], options)
This issue is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 5 days. To ignore this issue entirely you can add the no-stale label
not tried but orgrimarr's workaround seems to work, anyways this is still not fixed
is there a solution to this problem? I facing a similar issue when working with cluster inside pkg.
Any update on this issue?
same as https://github.com/vercel/pkg/issues/1866
this bug is alive in linux also... using the workaround for now, thanks @orgrimarr
In case someone else gets ELF errors, what did it is a modified version of the workaround:
spawn(process.execPath, [process.pkg.entrypoint , arg1, arg2, ...], {})
This issue is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 5 days. To ignore this issue entirely you can add the no-stale label
This issue is now closed due to inactivity, you can of course reopen or reference this issue if you see fit.
Can someone reopen this issue?