0
# Utility Functions
1
2
Utility functions provide core infrastructure for file operations, network requests, system registry access, and cross-platform process execution used throughout node-gyp.
3
4
## Capabilities
5
6
### HTTP Download Utilities
7
8
Handles HTTP/HTTPS downloads with proper headers, proxy support, and CA certificate handling.
9
10
```javascript { .api }
11
/**
12
* Downloads files with proxy support and proper headers
13
* @param {Gyp} gyp - The gyp instance for configuration
14
* @param {string} url - URL to download from
15
* @returns {Promise<Response>} Fetch response object
16
*/
17
async function download(gyp: Gyp, url: string): Promise<Response>;
18
19
/**
20
* Reads CA certificate file for HTTPS downloads
21
* @param {string} filename - Path to CA certificate file
22
* @returns {Promise<string>} CA certificate content
23
*/
24
async function readCAFile(filename: string): Promise<string>;
25
```
26
27
**Usage Examples:**
28
29
```javascript
30
const { download, readCAFile } = require('node-gyp/lib/download');
31
const gyp = require('node-gyp');
32
33
// Download with gyp configuration
34
const gypInstance = gyp();
35
gypInstance.opts.proxy = 'http://proxy.company.com:8080';
36
37
try {
38
const response = await download(gypInstance, 'https://nodejs.org/dist/v16.14.0/node-v16.14.0-headers.tar.gz');
39
const buffer = await response.buffer();
40
console.log('Downloaded', buffer.length, 'bytes');
41
} catch (error) {
42
console.error('Download failed:', error.message);
43
}
44
45
// Use custom CA certificate
46
try {
47
const caCert = await readCAFile('/path/to/ca-certificates.crt');
48
console.log('Loaded CA certificate');
49
} catch (error) {
50
console.error('Failed to read CA file:', error.message);
51
}
52
```
53
54
**Download Features:**
55
- **Proxy Support**: Honors proxy and noproxy configuration
56
- **Custom CA**: Supports custom CA certificate files
57
- **User Agent**: Sets appropriate User-Agent headers
58
- **Timeout Handling**: Configurable request timeouts
59
- **Error Handling**: Detailed error reporting for network issues
60
61
### Process Execution Utilities
62
63
Cross-platform utilities for executing external processes with proper error handling.
64
65
```javascript { .api }
66
/**
67
* Promise wrapper for child_process.execFile with proper error handling
68
* @param {...any} args - Arguments passed to execFile (command, args, options, callback)
69
* @returns {Promise<[Error?, string?, string?]>} Tuple of [error, stdout, stderr]
70
*/
71
async function execFile(...args: any[]): Promise<[Error?, string?, string?]>;
72
```
73
74
**Usage Examples:**
75
76
```javascript
77
const { execFile } = require('node-gyp/lib/util');
78
79
// Execute command and handle results
80
const [error, stdout, stderr] = await execFile('python', ['--version']);
81
82
if (error) {
83
console.error('Command failed:', error.message);
84
} else {
85
console.log('Python version:', stdout.trim());
86
}
87
88
// Execute with options
89
const [error, stdout, stderr] = await execFile('make', ['-j4'], {
90
cwd: './build',
91
encoding: 'utf8',
92
timeout: 30000
93
});
94
95
if (!error) {
96
console.log('Build completed successfully');
97
}
98
```
99
100
### Windows Registry Utilities
101
102
Windows-specific utilities for reading registry values, used for toolchain discovery.
103
104
```javascript { .api }
105
/**
106
* Gets a value from the Windows registry
107
* @param {string} key - Registry key path
108
* @param {string} value - Value name to read
109
* @param {string[]} [addOpts] - Additional reg.exe options
110
* @returns {Promise<string>} Registry value content
111
*/
112
async function regGetValue(key: string, value: string, addOpts?: string[]): Promise<string>;
113
114
/**
115
* Searches multiple registry keys for a value, returning the first match
116
* @param {string[]} keys - Array of registry key paths to search
117
* @param {string} value - Value name to find
118
* @param {string[]} [addOpts] - Additional reg.exe options
119
* @returns {Promise<string>} First matching registry value
120
*/
121
async function regSearchKeys(keys: string[], value: string, addOpts?: string[]): Promise<string>;
122
```
123
124
**Usage Examples:**
125
126
```javascript
127
const { regGetValue, regSearchKeys } = require('node-gyp/lib/util');
128
129
// Windows only - get specific registry value
130
if (process.platform === 'win32') {
131
try {
132
const vsPath = await regGetValue(
133
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\Setup\\VS',
134
'ProductDir'
135
);
136
console.log('Visual Studio 2015 path:', vsPath);
137
} catch (error) {
138
console.log('Visual Studio 2015 not found');
139
}
140
141
// Search multiple keys for Visual Studio installations
142
const vsKeys = [
143
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\16.0\\Setup\\VS',
144
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\15.0\\Setup\\VS',
145
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\Setup\\VS'
146
];
147
148
try {
149
const vsPath = await regSearchKeys(vsKeys, 'ProductDir');
150
console.log('Found Visual Studio at:', vsPath);
151
} catch (error) {
152
console.log('No Visual Studio installations found');
153
}
154
}
155
```
156
157
### File Access Utilities
158
159
Cross-platform file access validation utilities.
160
161
```javascript { .api }
162
/**
163
* Returns the first file or directory from candidates that is readable by current user
164
* @param {string} logprefix - Prefix for log messages
165
* @param {string} dir - Base directory to search in
166
* @param {string[]} candidates - Array of candidate file/directory names
167
* @returns {string | undefined} Path to first accessible candidate or undefined
168
*/
169
function findAccessibleSync(logprefix: string, dir: string, candidates: string[]): string | undefined;
170
```
171
172
**Usage Examples:**
173
174
```javascript
175
const { findAccessibleSync } = require('node-gyp/lib/util');
176
177
// Find first accessible Python executable
178
const pythonDir = '/usr/bin';
179
const pythonCandidates = ['python3.9', 'python3.8', 'python3', 'python'];
180
181
const pythonPath = findAccessibleSync('python', pythonDir, pythonCandidates);
182
if (pythonPath) {
183
console.log('Found Python at:', pythonPath);
184
} else {
185
console.log('No accessible Python found');
186
}
187
188
// Find Node.js headers
189
const nodeDir = '/usr/local/include';
190
const headerCandidates = ['node/node.h', 'nodejs/node.h'];
191
192
const headerPath = findAccessibleSync('headers', nodeDir, headerCandidates);
193
if (headerPath) {
194
console.log('Found Node.js headers at:', headerPath);
195
}
196
```
197
198
## Logging Integration
199
200
All utilities integrate with the node-gyp logging system:
201
202
```javascript
203
const log = require('node-gyp/lib/log');
204
const { withPrefix } = log;
205
206
// Create prefixed logger for utility functions
207
const utilLog = withPrefix('util');
208
209
utilLog.verbose('Executing command:', 'python --version');
210
utilLog.silly('Registry query:', 'HKLM\\SOFTWARE\\...');
211
```
212
213
## Error Handling Patterns
214
215
### Network Error Handling
216
217
```javascript
218
const { download } = require('node-gyp/lib/download');
219
220
try {
221
const response = await download(gypInstance, url);
222
if (!response.ok) {
223
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
224
}
225
} catch (error) {
226
if (error.code === 'ENOTFOUND') {
227
console.error('DNS resolution failed for:', url);
228
} else if (error.code === 'ECONNREFUSED') {
229
console.error('Connection refused, check proxy settings');
230
} else if (error.code === 'CERT_UNTRUSTED') {
231
console.error('SSL certificate error, check CA configuration');
232
} else {
233
console.error('Download failed:', error.message);
234
}
235
}
236
```
237
238
### Process Execution Error Handling
239
240
```javascript
241
const { execFile } = require('node-gyp/lib/util');
242
243
const [error, stdout, stderr] = await execFile('python', ['--version']);
244
245
if (error) {
246
if (error.code === 'ENOENT') {
247
console.error('Python executable not found in PATH');
248
} else if (error.code === 'EACCES') {
249
console.error('Permission denied executing Python');
250
} else if (error.signal) {
251
console.error('Python process killed by signal:', error.signal);
252
} else {
253
console.error('Python execution failed:', error.message);
254
}
255
}
256
```
257
258
### Registry Access Error Handling
259
260
```javascript
261
const { regGetValue } = require('node-gyp/lib/util');
262
263
if (process.platform === 'win32') {
264
try {
265
const value = await regGetValue(key, valueName);
266
} catch (error) {
267
if (error.message.includes('cannot find')) {
268
console.log('Registry key not found');
269
} else if (error.message.includes('Access is denied')) {
270
console.error('Registry access denied - run as administrator?');
271
} else {
272
console.error('Registry error:', error.message);
273
}
274
}
275
}
276
```
277
278
## Integration Examples
279
280
### Custom Download with Progress
281
282
```javascript
283
const { download } = require('node-gyp/lib/download');
284
const fs = require('fs');
285
286
async function downloadWithProgress(gyp, url, outputPath) {
287
const response = await download(gyp, url);
288
const total = parseInt(response.headers.get('content-length') || '0');
289
let downloaded = 0;
290
291
const fileStream = fs.createWriteStream(outputPath);
292
293
response.body.on('data', (chunk) => {
294
downloaded += chunk.length;
295
const percent = total ? (downloaded / total * 100).toFixed(1) : '?';
296
process.stdout.write(`\rDownloading: ${percent}%`);
297
});
298
299
await new Promise((resolve, reject) => {
300
response.body.pipe(fileStream);
301
response.body.on('end', resolve);
302
response.body.on('error', reject);
303
});
304
305
console.log('\nDownload completed');
306
}
307
```
308
309
### Toolchain Validation Utility
310
311
```javascript
312
const { execFile, findAccessibleSync } = require('node-gyp/lib/util');
313
314
async function validateBuildTools() {
315
const results = {
316
python: false,
317
make: false,
318
compiler: false
319
};
320
321
// Check Python
322
const [pythonError] = await execFile('python', ['--version']);
323
results.python = !pythonError;
324
325
// Check Make
326
const [makeError] = await execFile('make', ['--version']);
327
results.make = !makeError;
328
329
// Check compiler
330
const compilerCandidates = ['gcc', 'clang', 'cl.exe'];
331
for (const compiler of compilerCandidates) {
332
const [compilerError] = await execFile(compiler, ['--version']);
333
if (!compilerError) {
334
results.compiler = compiler;
335
break;
336
}
337
}
338
339
return results;
340
}
341
342
// Usage
343
const toolchainStatus = await validateBuildTools();
344
console.log('Toolchain status:', toolchainStatus);
345
```
346
347
### Cross-Platform Path Resolution
348
349
```javascript
350
const { findAccessibleSync } = require('node-gyp/lib/util');
351
const path = require('path');
352
353
function findExecutable(name, searchPaths = []) {
354
const defaultPaths = process.env.PATH.split(path.delimiter);
355
const allPaths = [...searchPaths, ...defaultPaths];
356
357
const extensions = process.platform === 'win32' ? ['.exe', '.cmd', '.bat'] : [''];
358
359
for (const searchPath of allPaths) {
360
const candidates = extensions.map(ext => name + ext);
361
const found = findAccessibleSync('executable', searchPath, candidates);
362
if (found) {
363
return found;
364
}
365
}
366
367
return null;
368
}
369
370
// Usage
371
const pythonPath = findExecutable('python3', ['/usr/local/bin', '/opt/python/bin']);
372
console.log('Python found at:', pythonPath);
373
```
374
375
## Logging Utilities
376
377
Advanced logging functions for creating custom loggers and direct output control.
378
379
```javascript { .api }
380
/**
381
* Creates log functions with predefined prefix
382
* @param {string} prefix - Prefix for log messages
383
* @returns {object} Object with logging functions for each level
384
*/
385
function withPrefix(prefix: string): object;
386
387
/**
388
* Direct stdout output function (can be nullified in tests)
389
* @param {...any} args - Arguments to output
390
*/
391
function stdout(...args: any[]): void;
392
393
/**
394
* Direct access to the logger instance
395
* @type {Logger}
396
*/
397
const logger: Logger;
398
```
399
400
**Usage Examples:**
401
402
```javascript
403
const { withPrefix, stdout, logger } = require('node-gyp/lib/log');
404
405
// Create prefixed logger
406
const myLog = withPrefix('custom');
407
myLog.info('This is a custom prefixed message');
408
myLog.verbose('Detailed information');
409
myLog.error('Error with custom prefix');
410
411
// Direct stdout access
412
stdout('Direct console output without logging formatting');
413
414
// Direct logger access
415
logger.level = 'verbose';
416
console.log('Current log level:', logger.level.id);
417
```
418
419
## Process Release Utilities
420
421
Functions for processing Node.js release information and generating download URLs.
422
423
```javascript { .api }
424
/**
425
* Processes Node.js release information for downloads and configuration
426
* @param {string[]} argv - Command arguments
427
* @param {Gyp} gyp - Gyp instance for configuration
428
* @param {string} defaultVersion - Default Node.js version
429
* @param {object} defaultRelease - Default release object
430
* @returns {object} Release information with URLs and paths
431
*/
432
function processRelease(
433
argv: string[],
434
gyp: Gyp,
435
defaultVersion: string,
436
defaultRelease: object
437
): object;
438
```
439
440
**Usage Example:**
441
442
```javascript
443
const processRelease = require('node-gyp/lib/process-release');
444
const gyp = require('node-gyp');
445
446
const gypInstance = gyp();
447
const releaseInfo = processRelease(
448
['16.14.0'],
449
gypInstance,
450
process.version,
451
process.release
452
);
453
454
console.log('Download URLs:', {
455
tarball: releaseInfo.tarballUrl,
456
headers: releaseInfo.headersUrl,
457
lib32: releaseInfo.libUrl32,
458
lib64: releaseInfo.libUrl64
459
});
460
```
461
462
## Command Usage Information
463
464
Each command module exports a usage string for help text generation.
465
466
```javascript { .api }
467
// All command modules export both function and usage
468
const buildCommand = require('node-gyp/lib/build');
469
const cleanCommand = require('node-gyp/lib/clean');
470
// etc.
471
472
/**
473
* Usage description for each command
474
* @type {string}
475
*/
476
buildCommand.usage: string;
477
cleanCommand.usage: string;
478
configureCommand.usage: string;
479
installCommand.usage: string;
480
listCommand.usage: string;
481
rebuildCommand.usage: string;
482
removeCommand.usage: string;
483
```
484
485
**Usage Example:**
486
487
```javascript
488
const buildCommand = require('node-gyp/lib/build');
489
const cleanCommand = require('node-gyp/lib/clean');
490
491
console.log('Build command:', buildCommand.usage);
492
// "Invokes `msbuild` (on Windows) or `make` (on other platforms) and builds the module"
493
494
console.log('Clean command:', cleanCommand.usage);
495
// "Removes any generated build files and the \"out\" dir"
496
497
// Generate help for all commands
498
const commands = ['build', 'clean', 'configure', 'install', 'list', 'rebuild', 'remove'];
499
commands.forEach(cmd => {
500
const cmdModule = require(`node-gyp/lib/${cmd}`);
501
console.log(`${cmd}: ${cmdModule.usage}`);
502
});
503
```