Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,25 +235,44 @@ Some additional resources for Node.js native addons and writing `gyp` configurat

## Configuration

### package.json

Use the `config` object in your package.json with each key in the form `node_gyp_OPTION_NAME`. Any of the command
options listed above can be set (dashes in option names should be replaced by underscores).

For example, to set `devdir` equal to `/tmp/.gyp`, your package.json would contain this:

```json
{
"config": {
"node_gyp_devdir": "/tmp/.gyp"
}
}
```

### Environment variables

Use the form `npm_config_OPTION_NAME` for any of the command options listed
Use the form `npm_package_config_node_gyp_OPTION_NAME` for any of the command options listed
above (dashes in option names should be replaced by underscores).

For example, to set `devdir` equal to `/tmp/.gyp`, you would:

Run this on Unix:

```bash
export npm_config_devdir=/tmp/.gyp
export npm_package_config_node_gyp_devdir=/tmp/.gyp
```

Or this on Windows:

```console
set npm_config_devdir=c:\temp\.gyp
set npm_package_config_node_gyp_devdir=c:\temp\.gyp
```

Note that in versions of npm before v11 it was possible to use the prefix `npm_config_` for
environement variables. This was deprecated in npm@11 and will be removed in npm@12 so it
is recommened to convert your environment variables to the above format.

### `npm` configuration for npm versions before v9

Use the form `OPTION_NAME` for any of the command options listed above.
Expand Down
46 changes: 28 additions & 18 deletions lib/node-gyp.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,31 +122,41 @@ class Gyp extends EventEmitter {
}

// support for inheriting config env variables from npm
// npm will set environment variables in the following forms:
// - `npm_config_<key>` for values from npm's own config. Setting arbitrary
// options on npm's config was deprecated in npm v11 but node-gyp still
// supports it for backwards compatibility.
// See https://github.com/nodejs/node-gyp/issues/3156
// - `npm_package_config_node_gyp_<key>` for values from the `config` object
// in package.json. This is the preferred way to set options for node-gyp
// since npm v11. The `node_gyp_` prefix is used to avoid conflicts with
// other tools.
// The `npm_package_config_node_gyp_` prefix will take precedence over
// `npm_config_` keys.
const npmConfigPrefix = 'npm_config_'
Object.keys(process.env).forEach((name) => {
if (name.indexOf(npmConfigPrefix) !== 0) {
return
}
const val = process.env[name]
if (name === npmConfigPrefix + 'loglevel') {
log.logger.level = val
} else {
const npmPackageConfigPrefix = 'npm_package_config_node_gyp_'

const configEnvKeys = Object.keys(process.env)
.filter((k) => k.startsWith(npmConfigPrefix) || k.startsWith(npmPackageConfigPrefix))
// sort so that npm_package_config_node_gyp_ keys come last and will override
.sort((a) => a.startsWith(npmConfigPrefix) ? -1 : 1)

for (const key of configEnvKeys) {
// add the user-defined options to the config
name = name.substring(npmConfigPrefix.length)
// gyp@741b7f1 enters an infinite loop when it encounters
// zero-length options so ensure those don't get through.
if (name) {
const name = key.startsWith(npmConfigPrefix)
? key.substring(npmConfigPrefix.length)
: key.substring(npmPackageConfigPrefix.length)
// gyp@741b7f1 enters an infinite loop when it encounters
// zero-length options so ensure those don't get through.
if (name) {
// convert names like force_process_config to force-process-config
if (name.includes('_')) {
name = name.replace(/_/g, '-')
}
this.opts[name] = val
}
this.opts[name.replaceAll('_', '-')] = process.env[key]
}
})
}

if (this.opts.loglevel) {
log.logger.level = this.opts.loglevel
delete this.opts.loglevel
}
log.resume()
}
Expand Down
29 changes: 23 additions & 6 deletions test/test-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,48 @@
const { describe, it } = require('mocha')
const assert = require('assert')
const gyp = require('../lib/node-gyp')
const log = require('../lib/log')

describe('options', function () {
it('options in environment', () => {
// `npm test` dumps a ton of npm_config_* variables in the environment.
Object.keys(process.env)
.filter((key) => /^npm_config_/.test(key))
.filter((key) => /^npm_config_/i.test(key) || /^npm_package_config_node_gyp_/i.test(key))
.forEach((key) => { delete process.env[key] })

// in some platforms, certain keys are stubborn and cannot be removed
const keys = Object.keys(process.env)
.filter((key) => /^npm_config_/.test(key))
.filter((key) => /^npm_config_/i.test(key) || /^npm_package_config_node_gyp_/i.test(key))
.map((key) => key.substring('npm_config_'.length))
.concat('argv', 'x')

// Environment variables with the following prefixes should be added to opts.
// - `npm_config_` for npm versions before v11.
// - `npm_package_config_node_gyp_` for npm versions 11 and later.

// Zero-length keys should get filtered out.
process.env.npm_config_ = '42'
process.env.npm_package_config_node_gyp_ = '42'
// Other keys should get added.
process.env.npm_package_config_node_gyp_foo = '42'
process.env.npm_config_x = '42'
// Except loglevel.
process.env.npm_config_loglevel = 'debug'
process.env.npm_config_y = '41'
// Package config should take precedence over npm_config_ keys.
process.env.npm_package_config_node_gyp_y = '42'
// loglevel does not get added to opts but will change the logger's level.
process.env.npm_config_loglevel = 'silly'

const g = gyp()

assert.strictEqual(log.logger.level.id, 'info')

g.parseArgv(['rebuild']) // Also sets opts.argv.

assert.deepStrictEqual(Object.keys(g.opts).sort(), keys.sort())
assert.strictEqual(log.logger.level.id, 'silly')

assert.deepStrictEqual(Object.keys(g.opts).sort(), [...keys, 'argv', 'x', 'y', 'foo'].sort())
assert.strictEqual(g.opts['x'], '42')
assert.strictEqual(g.opts['y'], '42')
assert.strictEqual(g.opts['foo'], '42')
})

it('options with spaces in environment', () => {
Expand Down
Loading