Skip to content

fix(binding): drop node-PATH dependency in binding.gyp (#153)#154

Merged
oorabona merged 4 commits into
masterfrom
fix/binding-gyp-no-node-shellout
May 14, 2026
Merged

fix(binding): drop node-PATH dependency in binding.gyp (#153)#154
oorabona merged 4 commits into
masterfrom
fix/binding-gyp-no-node-shellout

Conversation

@oorabona

Copy link
Copy Markdown
Owner

Summary

Fixes #153npm i -g node-liblzma (and the pnpm / aube variants) fails on Windows with 'node' is not recognized as an internal or external command because binding.gyp invoked <!(node -p "...") from inside gyp's shell-out. npm spawns install scripts in Git Bash on Windows, whose inherited PATH does not include the mise/aube/pnpm-managed node.exe.

The fix replaces every <!(node -p ...) call with <!("<(python)" "<(module_root_dir)/scripts/binding_config.py" ...) — a Python helper invoked via the python variable that node-gyp ≥10 always emits into build/config.gypi. No node PATH dependency remains in binding.gyp.

Why Python

  • node-gyp itself depends on Python and resolves an absolute path to it; that absolute path is exported as the gyp variable <(python), so substitution works regardless of shell PATH.
  • The project's engines.node >= 22.0.0 pins users to npm ≥ 10 → node-gyp ≥ 10, which is guaranteed to provide <(python).

Behavior parity

The Python helper preserves the existing env-var API:

Env var Default (linux/darwin) Default (other)
USE_GLOBAL true false
RUNTIME_LINK shared static
ENABLE_THREAD_SUPPORT yes yes
npm_config_python python3 python

node-addon-api resolution walks upward through node_modules (handles pnpm-isolated layouts) and returns the same node_api.gyp:nothing reference the deprecated require('node-addon-api').gyp API produced — preserving the existing dependency declaration semantics.

Quoting

Every <(python) and <(module_root_dir)/scripts/... reference inside a <!() / <!@() shell command is wrapped in double quotes so a Python interpreter at a path containing spaces (e.g. C:\Program Files\Python311\python.exe — the literal value in the issue logs) is not split into multiple argv tokens by cmd.exe / sh. The action: [...] invocations elsewhere already use gyp's array form (bypasses shell) and are unchanged.

Local verification

  • pnpm exec node-gyp configuregyp info ok
  • pnpm exec node-gyp build → emits Release/node_lzma.node
  • <(python) resolves to /bin/python3 per build/config.gypi after configure
  • env-override path tested: USE_GLOBAL=false RUNTIME_LINK=static node-gyp configure activates the CMake vendored target chain

Pre-PR review

Two rounds of codex xhigh review:

  • R1 → 1 M (path quoting), 1 L (legacy node-gyp fallback) — folded into commit 2.
  • R2 → 1 L (the fallback itself was ineffective due to gyp's variable-resolution pass order; engines pin makes it unreachable) — fallback removed in commit 3.

Test plan

  • CI smoke test on windows-latest (Node 24) passes — exercises node-gyp rebuild under Git Bash on Windows runners
  • CI smoke test on ubuntu-latest + macos-latest continues to pass (no regression on system-liblzma path)
  • Manual: aube a -g node-liblzma --allow-build=node-liblzma on a Windows host with mise — should no longer error on 'node' is not recognized. (Note: the separate node_api.gyp not found error under aube's virtual store layout — visible in the issue logs — is a downstream aube path-resolution bug and is not in scope here.)

Closes #153.

oorabona added 3 commits May 14, 2026 15:22
Replace 5 <!(node -p "...") shell-outs with a Python helper invoked via
<(python). node-gyp injects an absolute python path into build/config.gypi,
so the substitution works regardless of whether `node` is in PATH.

Root cause: npm spawns install scripts in a fresh shell (Git Bash on
Windows) whose PATH does not include mise/aube/pnpm-managed node.exe.
When gyp evaluated `<!(node -p "process.env.USE_GLOBAL || ...")` inside
that shell, `node` could not be resolved and gyp aborted with
`'node' is not recognized as an internal or external command`.

The bridge preserves the existing env-var API (USE_GLOBAL, RUNTIME_LINK,
ENABLE_THREAD_SUPPORT, npm_config_python) by reading them from Python's
os.environ instead of process.env, and resolves node-addon-api via
filesystem walk (returns the same `node_api.gyp:nothing` reference the
deprecated `require('node-addon-api').gyp` API produced).
…llback

Address codex pre-PR review findings on the #153 fix:

- M: quote <(python) and <(module_root_dir)/scripts/... in every <!()
  / <!@() shell-out so a Python interpreter at a path containing spaces
  (e.g. C:\Program Files\Python311\python.exe — the literal value seen
  in the issue logs) is not split by the shell into multiple argv tokens.

- L: define `python%` via an OS-conditional default (python on Windows,
  python3 elsewhere) so the binding still parses on legacy node-gyp
  versions (<10) whose generated config.gypi did not emit a `python`
  variable. node-gyp 10+ (shipped with node>=20) overrides this fallback
  with the absolute path it resolves itself.

The `action: [...]` invocations elsewhere in the file already use gyp's
array form, which bypasses the shell, so no quoting is needed for those.
Codex re-review of commit e30935b flagged L: the `conditions: [...python%...]`
block inside `variables` does not resolve before the sibling `<!()` calls
expand `<(python)` in gyp's variable-resolution pass order. So on legacy
node-gyp (<10) where config.gypi lacks `python`, the fallback was never
reached — gyp would fail at parse time with "Undefined variable python"
before merging the conditional default.

Since the project pins `engines.node >= 22.0.0` and node 22+ ships with
npm 10+ (bundling node-gyp 10+, which always emits `python` into
config.gypi), the fallback is unreachable in supported configurations.
Drop it and document the requirement in the comment.
Copilot AI review requested due to automatic review settings May 14, 2026 13:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Windows install failures caused by binding.gyp shelling out to node -p ... when node.exe is not on the PATH inherited by node-gyp’s subprocess (notably under Git Bash with mise/pnpm/aube). It replaces those Node-based evaluations with a Python helper invoked via gyp’s <(python) variable so configuration can be resolved without requiring node on PATH.

Changes:

  • Added scripts/binding_config.py to resolve env-var defaults and locate node-addon-api paths without invoking node.
  • Updated binding.gyp to replace <!(node -p ...) calls with <!( "<(python)" ... binding_config.py ...) and adjusted source discovery invocation to use <(python).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
scripts/binding_config.py New Python helper to compute binding.gyp variables and resolve node-addon-api include/gyp paths without Node shell-outs.
binding.gyp Switches variable resolution and node-addon-api discovery from node -p to the Python helper / <(python) to remove PATH dependency on Windows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread binding.gyp
Comment thread scripts/binding_config.py
- Add scripts/binding_config.py to package.json files array so it ships
  with npm tarball, fixing install-time resolution failures on consumer
  machines where binding.gyp invokes the helper
- Remove unused py3() function and COMMANDS entry; binding.gyp resolves
  the python interpreter via gyp-injected <(python) variable, never
  calling binding_config.py py3

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@oorabona oorabona merged commit e64062d into master May 14, 2026
22 checks passed
@oorabona oorabona deleted the fix/binding-gyp-no-node-shellout branch May 14, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install fails via npm/pnpm/aube on Windows

2 participants