@flun/nodejs-mobile-gyp 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +29 -0
- package/README.md +252 -0
- package/addon.gypi +209 -0
- package/bin/node-gyp.js +144 -0
- package/gyp/LICENSE +28 -0
- package/gyp/README.md +30 -0
- package/gyp/data/win/large-pdb-shim.cc +12 -0
- package/gyp/gyp +8 -0
- package/gyp/gyp.bat +5 -0
- package/gyp/gyp_main.py +45 -0
- package/gyp/pylib/gyp/MSVSNew.py +365 -0
- package/gyp/pylib/gyp/MSVSProject.py +206 -0
- package/gyp/pylib/gyp/MSVSSettings.py +1270 -0
- package/gyp/pylib/gyp/MSVSToolFile.py +59 -0
- package/gyp/pylib/gyp/MSVSUserFile.py +153 -0
- package/gyp/pylib/gyp/MSVSUtil.py +271 -0
- package/gyp/pylib/gyp/MSVSVersion.py +574 -0
- package/gyp/pylib/gyp/__init__.py +692 -0
- package/gyp/pylib/gyp/common.py +654 -0
- package/gyp/pylib/gyp/easy_xml.py +169 -0
- package/gyp/pylib/gyp/flock_tool.py +55 -0
- package/gyp/pylib/gyp/generator/__init__.py +0 -0
- package/gyp/pylib/gyp/generator/analyzer.py +804 -0
- package/gyp/pylib/gyp/generator/android.py +1173 -0
- package/gyp/pylib/gyp/generator/cmake.py +1318 -0
- package/gyp/pylib/gyp/generator/compile_commands_json.py +123 -0
- package/gyp/pylib/gyp/generator/dump_dependency_json.py +103 -0
- package/gyp/pylib/gyp/generator/eclipse.py +461 -0
- package/gyp/pylib/gyp/generator/gypd.py +89 -0
- package/gyp/pylib/gyp/generator/gypsh.py +58 -0
- package/gyp/pylib/gyp/generator/make.py +2710 -0
- package/gyp/pylib/gyp/generator/msvs.py +3981 -0
- package/gyp/pylib/gyp/generator/ninja.py +2933 -0
- package/gyp/pylib/gyp/generator/xcode.py +1397 -0
- package/gyp/pylib/gyp/input.py +3115 -0
- package/gyp/pylib/gyp/mac_tool.py +771 -0
- package/gyp/pylib/gyp/msvs_emulation.py +1262 -0
- package/gyp/pylib/gyp/ninja_syntax.py +174 -0
- package/gyp/pylib/gyp/simple_copy.py +61 -0
- package/gyp/pylib/gyp/win_tool.py +373 -0
- package/gyp/pylib/gyp/xcode_emulation.py +1935 -0
- package/gyp/pylib/gyp/xcode_ninja.py +302 -0
- package/gyp/pylib/gyp/xcodeproj_file.py +3198 -0
- package/gyp/pylib/gyp/xml_fix.py +65 -0
- package/gyp/pylib/packaging/LICENSE +3 -0
- package/gyp/pylib/packaging/LICENSE.APACHE +177 -0
- package/gyp/pylib/packaging/LICENSE.BSD +23 -0
- package/gyp/pylib/packaging/__init__.py +15 -0
- package/gyp/pylib/packaging/_elffile.py +108 -0
- package/gyp/pylib/packaging/_manylinux.py +252 -0
- package/gyp/pylib/packaging/_musllinux.py +83 -0
- package/gyp/pylib/packaging/_parser.py +359 -0
- package/gyp/pylib/packaging/_structures.py +61 -0
- package/gyp/pylib/packaging/_tokenizer.py +192 -0
- package/gyp/pylib/packaging/markers.py +252 -0
- package/gyp/pylib/packaging/metadata.py +825 -0
- package/gyp/pylib/packaging/py.typed +0 -0
- package/gyp/pylib/packaging/requirements.py +90 -0
- package/gyp/pylib/packaging/specifiers.py +1030 -0
- package/gyp/pylib/packaging/tags.py +553 -0
- package/gyp/pylib/packaging/utils.py +172 -0
- package/gyp/pylib/packaging/version.py +563 -0
- package/lib/Find-VisualStudio.cs +250 -0
- package/lib/build.js +221 -0
- package/lib/clean.js +19 -0
- package/lib/configure.js +295 -0
- package/lib/create-config-gypi.js +163 -0
- package/lib/download.js +44 -0
- package/lib/find-node-directory.js +62 -0
- package/lib/find-python.js +303 -0
- package/lib/find-visualstudio.js +448 -0
- package/lib/install.js +419 -0
- package/lib/list.js +30 -0
- package/lib/log.js +174 -0
- package/lib/node-gyp.js +201 -0
- package/lib/process-release.js +144 -0
- package/lib/rebuild.js +14 -0
- package/lib/remove.js +47 -0
- package/lib/util.js +83 -0
- package/package.json +72 -0
- package/src/win_delay_load_hook.cc +39 -0
package/lib/configure.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import gracefulFs from 'graceful-fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import * as log from './log.js'
|
|
4
|
+
import os from 'os'
|
|
5
|
+
import { processRelease } from './process-release.js'
|
|
6
|
+
import { findNodeDirectory } from './find-node-directory.js'
|
|
7
|
+
import { createConfigGypi } from './create-config-gypi.js'
|
|
8
|
+
import { format as msgFormat } from 'util'
|
|
9
|
+
import { findAccessibleSync } from './util.js'
|
|
10
|
+
import { PythonFinder } from './find-python.js'
|
|
11
|
+
import { fileURLToPath } from 'url'
|
|
12
|
+
|
|
13
|
+
const fs = gracefulFs.promises
|
|
14
|
+
const win = process.platform === 'win32'
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
16
|
+
|
|
17
|
+
// 条件加载:仅在 Windows 上需要
|
|
18
|
+
let findVisualStudio = null
|
|
19
|
+
if (win) {
|
|
20
|
+
const { VisualStudioFinder } = await import('./find-visualstudio.js')
|
|
21
|
+
findVisualStudio = VisualStudioFinder.findVisualStudio
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 生成构建文件(Makefile / MSVC 工程)
|
|
26
|
+
*/
|
|
27
|
+
const configure = async (gyp, argv) => {
|
|
28
|
+
const buildDir = path.resolve('build')
|
|
29
|
+
const configNames = ['config.gypi', 'common.gypi']
|
|
30
|
+
const configs = []
|
|
31
|
+
let nodeDir
|
|
32
|
+
const release = processRelease(argv, gyp, process.version, process.release)
|
|
33
|
+
|
|
34
|
+
const python = await PythonFinder.findPython(gyp.opts.python)
|
|
35
|
+
return getNodeDir()
|
|
36
|
+
|
|
37
|
+
async function getNodeDir () {
|
|
38
|
+
// 此时 python 已确定
|
|
39
|
+
process.env.PYTHON = python
|
|
40
|
+
|
|
41
|
+
if (gyp.opts.nodedir) {
|
|
42
|
+
// 指定了 --nodedir,用它作为开发文件目录
|
|
43
|
+
nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir())
|
|
44
|
+
log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir)
|
|
45
|
+
} else {
|
|
46
|
+
// 未指定 --nodedir,先确保 Node 依赖已安装
|
|
47
|
+
if ('v' + release.version !== process.version) {
|
|
48
|
+
// 指定了 --target,确定目标版本
|
|
49
|
+
log.verbose('get node dir', 'compiling against --target node version: %s', release.version)
|
|
50
|
+
} else {
|
|
51
|
+
// 未指定 --target,使用当前主机 Node 版本
|
|
52
|
+
log.verbose('get node dir', 'no --target version specified, falling back to host node version: %s', release.version)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!release.semver) {
|
|
56
|
+
// 无法用 semver 解析版本字符串
|
|
57
|
+
throw new Error('Invalid version number: ' + release.version)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 如果指定了 tarball 选项,总是删除并重装 headers 到 devdir,
|
|
61
|
+
// 否则只在不存在时安装
|
|
62
|
+
gyp.opts.ensure = !gyp.opts.tarball
|
|
63
|
+
|
|
64
|
+
await gyp.commands.install([release.version])
|
|
65
|
+
|
|
66
|
+
log.verbose('get node dir', 'target node version installed:', release.versionDir)
|
|
67
|
+
nodeDir = path.resolve(gyp.devDir, release.versionDir)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return createBuildDir()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function createBuildDir () {
|
|
74
|
+
log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir)
|
|
75
|
+
|
|
76
|
+
const isNew = await fs.mkdir(buildDir, { recursive: true })
|
|
77
|
+
log.verbose(
|
|
78
|
+
'build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No'
|
|
79
|
+
)
|
|
80
|
+
const vsInfo = win ? await findVisualStudio(release.semver, gyp.opts['msvs-version']) : null
|
|
81
|
+
return createConfigFile(vsInfo)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function createConfigFile (vsInfo) {
|
|
85
|
+
if (win) {
|
|
86
|
+
process.env.GYP_MSVS_VERSION = Math.min(vsInfo.versionYear, 2015)
|
|
87
|
+
process.env.GYP_MSVS_OVERRIDE_PATH = vsInfo.path
|
|
88
|
+
}
|
|
89
|
+
const configPath = await createConfigGypi({ gyp, buildDir, nodeDir, vsInfo, python })
|
|
90
|
+
configs.push(configPath)
|
|
91
|
+
return findConfigs()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function findConfigs () {
|
|
95
|
+
const name = configNames.shift()
|
|
96
|
+
if (!name) {
|
|
97
|
+
return runGyp()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const fullPath = path.resolve(name)
|
|
101
|
+
log.verbose(name, 'checking for gypi file: %s', fullPath)
|
|
102
|
+
try {
|
|
103
|
+
await fs.stat(fullPath)
|
|
104
|
+
log.verbose(name, 'found gypi file')
|
|
105
|
+
configs.push(fullPath)
|
|
106
|
+
} catch (err) {
|
|
107
|
+
// ENOENT 时继续检查下一个 gypi 文件名
|
|
108
|
+
if (err.code !== 'ENOENT') {
|
|
109
|
+
throw err
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return findConfigs()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function runGyp () {
|
|
117
|
+
if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
|
|
118
|
+
if (win) {
|
|
119
|
+
log.verbose('gyp', 'gyp format was not specified; forcing "msvs"')
|
|
120
|
+
// 非 Windows 强制使用 make 目标
|
|
121
|
+
argv.push('-f', ('format' in gyp.opts) ? gyp.opts.format : 'msvs')
|
|
122
|
+
} else {
|
|
123
|
+
log.verbose('gyp', 'gyp format was not specified; forcing "make"')
|
|
124
|
+
// 非 Windows 强制使用 make 目标
|
|
125
|
+
argv.push('-f', ('format' in gyp.opts) ? gyp.opts.format : 'make')
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 引入所有找到的 .gypi 文件
|
|
130
|
+
configs.forEach(function (config) {
|
|
131
|
+
argv.push('-I', config)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// AIX 和 z/OS 需要设置导出文件路径,其中包含链接所需符号
|
|
135
|
+
let nodeExpFile
|
|
136
|
+
let nodeRootDir
|
|
137
|
+
let candidates
|
|
138
|
+
let logprefix = 'find exports file'
|
|
139
|
+
if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
|
|
140
|
+
const ext = process.platform === 'os390' ? 'x' : 'exp'
|
|
141
|
+
nodeRootDir = findNodeDirectory()
|
|
142
|
+
|
|
143
|
+
if (process.platform === 'aix' || process.platform === 'os400') {
|
|
144
|
+
candidates = [
|
|
145
|
+
'include/node/node',
|
|
146
|
+
'out/Release/node',
|
|
147
|
+
'out/Debug/node',
|
|
148
|
+
'node'
|
|
149
|
+
].map(function (file) {
|
|
150
|
+
return file + '.' + ext
|
|
151
|
+
})
|
|
152
|
+
} else {
|
|
153
|
+
candidates = [
|
|
154
|
+
'out/Release/lib.target/libnode',
|
|
155
|
+
'out/Debug/lib.target/libnode',
|
|
156
|
+
'out/Release/obj.target/libnode',
|
|
157
|
+
'out/Debug/obj.target/libnode',
|
|
158
|
+
'lib/libnode'
|
|
159
|
+
].map(function (file) {
|
|
160
|
+
return file + '.' + ext
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
nodeExpFile = findAccessibleSync(logprefix, nodeRootDir, candidates)
|
|
165
|
+
if (nodeExpFile !== undefined) {
|
|
166
|
+
log.verbose(logprefix, 'Found exports file: %s', nodeExpFile)
|
|
167
|
+
} else {
|
|
168
|
+
const msg = msgFormat('Could not find node.%s file in %s', ext, nodeRootDir)
|
|
169
|
+
log.error(logprefix, 'Could not find exports file')
|
|
170
|
+
throw new Error(msg)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// z/OS 需要设置 zoslib 头文件目录,其中包含 v8config.h 引用的头文件
|
|
175
|
+
let zoslibIncDir
|
|
176
|
+
if (process.platform === 'os390') {
|
|
177
|
+
logprefix = "find zoslib's zos-base.h:"
|
|
178
|
+
let msg
|
|
179
|
+
let zoslibIncPath = process.env.ZOSLIB_INCLUDES
|
|
180
|
+
if (zoslibIncPath) {
|
|
181
|
+
zoslibIncPath = findAccessibleSync(logprefix, zoslibIncPath, ['zos-base.h'])
|
|
182
|
+
if (zoslibIncPath === undefined) {
|
|
183
|
+
msg = msgFormat('Could not find zos-base.h file in the directory set ' +
|
|
184
|
+
'in ZOSLIB_INCLUDES environment variable: %s; set it ' +
|
|
185
|
+
'to the correct path, or unset it to search %s', process.env.ZOSLIB_INCLUDES, nodeRootDir)
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
candidates = [
|
|
189
|
+
'include/node/zoslib/zos-base.h',
|
|
190
|
+
'include/zoslib/zos-base.h',
|
|
191
|
+
'zoslib/include/zos-base.h',
|
|
192
|
+
'install/include/node/zoslib/zos-base.h'
|
|
193
|
+
]
|
|
194
|
+
zoslibIncPath = findAccessibleSync(logprefix, nodeRootDir, candidates)
|
|
195
|
+
if (zoslibIncPath === undefined) {
|
|
196
|
+
msg = msgFormat('Could not find any of %s in directory %s; set ' +
|
|
197
|
+
'environmant variable ZOSLIB_INCLUDES to the path ' +
|
|
198
|
+
'that contains zos-base.h', candidates.toString(), nodeRootDir)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (zoslibIncPath !== undefined) {
|
|
202
|
+
zoslibIncDir = path.dirname(zoslibIncPath)
|
|
203
|
+
log.verbose(logprefix, "Found zoslib's zos-base.h in: %s", zoslibIncDir)
|
|
204
|
+
} else if (release.version.split('.')[0] >= 16) {
|
|
205
|
+
// zoslib 只随 Node v16 及以上发布
|
|
206
|
+
log.error(logprefix, msg)
|
|
207
|
+
throw new Error(msg)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 这段逻辑移植自旧的 `gyp_addon` Python 文件
|
|
212
|
+
const gypScript = path.resolve(__dirname, '..', 'gyp', 'gyp_main.py')
|
|
213
|
+
const addonGypi = path.resolve(__dirname, '..', 'addon.gypi')
|
|
214
|
+
let commonGypi = path.resolve(nodeDir, 'include/node/common.gypi')
|
|
215
|
+
try {
|
|
216
|
+
await fs.stat(commonGypi)
|
|
217
|
+
} catch (err) {
|
|
218
|
+
commonGypi = path.resolve(nodeDir, 'common.gypi')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let outputDir = 'build'
|
|
222
|
+
if (win) {
|
|
223
|
+
// Windows 需要绝对路径
|
|
224
|
+
outputDir = buildDir
|
|
225
|
+
}
|
|
226
|
+
const nodeGypDir = path.resolve(__dirname, '..')
|
|
227
|
+
|
|
228
|
+
let nodeLibFile = path.join(nodeDir,
|
|
229
|
+
!gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)',
|
|
230
|
+
release.name + '.lib')
|
|
231
|
+
|
|
232
|
+
argv.push('-I', addonGypi)
|
|
233
|
+
argv.push('-I', commonGypi)
|
|
234
|
+
argv.push('-Dlibrary=shared_library')
|
|
235
|
+
argv.push('-Dvisibility=default')
|
|
236
|
+
argv.push('-Dnode_root_dir=' + nodeDir)
|
|
237
|
+
if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
|
|
238
|
+
argv.push('-Dnode_exp_file=' + nodeExpFile)
|
|
239
|
+
if (process.platform === 'os390' && zoslibIncDir) {
|
|
240
|
+
argv.push('-Dzoslib_include_dir=' + zoslibIncDir)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
argv.push('-Dnode_gyp_dir=' + nodeGypDir)
|
|
244
|
+
|
|
245
|
+
// 这样做是为了让 Cygwin 环境正常工作,否则未转义的 '\' 会被吃掉,
|
|
246
|
+
// 导致路径错误,如 c:parentFolderfolderanotherFolder 而非 c:\parentFolder\folder\anotherFolder
|
|
247
|
+
if (win) {
|
|
248
|
+
nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\')
|
|
249
|
+
}
|
|
250
|
+
argv.push('-Dnode_lib_file=' + nodeLibFile)
|
|
251
|
+
argv.push('-Dmodule_root_dir=' + process.cwd())
|
|
252
|
+
argv.push('-Dnode_engine=' +
|
|
253
|
+
(gyp.opts.node_engine || process.jsEngine || 'v8'))
|
|
254
|
+
argv.push('--depth=.')
|
|
255
|
+
argv.push('--no-parallel')
|
|
256
|
+
|
|
257
|
+
// 告诉 gyp 把 Makefile/Solution 文件写到 output_dir
|
|
258
|
+
argv.push('--generator-output', outputDir)
|
|
259
|
+
if (win) {
|
|
260
|
+
argv.push('-Gmsvs_toolset=v145')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 告诉 make 把输出写到同一目录
|
|
264
|
+
argv.push('-Goutput_dir=.')
|
|
265
|
+
|
|
266
|
+
// 强制使用 binding.gyp 文件
|
|
267
|
+
argv.unshift('binding.gyp')
|
|
268
|
+
|
|
269
|
+
// 从当前目标 nodedir 执行 `gyp`
|
|
270
|
+
argv.unshift(gypScript)
|
|
271
|
+
|
|
272
|
+
// 确保 python 使用该 node 包自带的文件
|
|
273
|
+
const pypath = [path.join(__dirname, '..', 'gyp', 'pylib')]
|
|
274
|
+
if (process.env.PYTHONPATH) {
|
|
275
|
+
pypath.push(process.env.PYTHONPATH)
|
|
276
|
+
}
|
|
277
|
+
process.env.PYTHONPATH = pypath.join(win ? ';' : ':')
|
|
278
|
+
|
|
279
|
+
await new Promise((resolve, reject) => {
|
|
280
|
+
const cp = gyp.spawn(python, argv)
|
|
281
|
+
cp.on('exit', (code) => {
|
|
282
|
+
if (code !== 0) {
|
|
283
|
+
reject(new Error('`gyp` failed with exit code: ' + code))
|
|
284
|
+
} else {
|
|
285
|
+
// 完成
|
|
286
|
+
resolve()
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
configure.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module'
|
|
294
|
+
|
|
295
|
+
export { configure }
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import gracefulFs from 'graceful-fs'
|
|
2
|
+
import * as log from './log.js'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
const fs = gracefulFs.promises
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 解析 config.gypi 内容为 JSON 对象
|
|
9
|
+
* 逻辑翻译自 Node.js 的 tools/js2c.py
|
|
10
|
+
*/
|
|
11
|
+
const parseConfigGypi = (config) => {
|
|
12
|
+
// 1. 去掉 # 注释
|
|
13
|
+
config = config.replace(/#.*/g, '')
|
|
14
|
+
// 2. 拼接多行字符串
|
|
15
|
+
config = config.replace(/'$\s+'/mg, '')
|
|
16
|
+
// 3. 把单引号字符串归一化为双引号
|
|
17
|
+
config = config.replace(/'/g, '"')
|
|
18
|
+
return JSON.parse(config)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 读取基础 config.gypi
|
|
23
|
+
*/
|
|
24
|
+
const getBaseConfigGypi = async ({ gyp, nodeDir }) => {
|
|
25
|
+
// 在以下情况优先读取 $nodeDir/include/node/config.gypi:
|
|
26
|
+
// 1. 指定了 --dist-url 或 --nodedir
|
|
27
|
+
// 2. 且没有指定 --force-process-config
|
|
28
|
+
const useCustomHeaders = gyp.opts.nodedir || gyp.opts.disturl || gyp.opts['dist-url']
|
|
29
|
+
const shouldReadConfigGypi = useCustomHeaders && !gyp.opts['force-process-config']
|
|
30
|
+
if (shouldReadConfigGypi && nodeDir) {
|
|
31
|
+
try {
|
|
32
|
+
const baseConfigGypiPath = path.resolve(nodeDir, 'include/node/config.gypi')
|
|
33
|
+
const baseConfigGypi = await fs.readFile(baseConfigGypiPath)
|
|
34
|
+
return parseConfigGypi(baseConfigGypi.toString())
|
|
35
|
+
} catch (err) {
|
|
36
|
+
log.warn('read config.gypi', err.message)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 回退到 process.config
|
|
41
|
+
return JSON.parse(JSON.stringify(process.config))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 基于当前环境生成 config.gypi 内容
|
|
46
|
+
*/
|
|
47
|
+
const getCurrentConfigGypi = async ({ gyp, nodeDir, vsInfo, python }) => {
|
|
48
|
+
const config = await getBaseConfigGypi({ gyp, nodeDir })
|
|
49
|
+
if (!config.target_defaults) {
|
|
50
|
+
config.target_defaults = {}
|
|
51
|
+
}
|
|
52
|
+
if (!config.variables) {
|
|
53
|
+
config.variables = {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const defaults = config.target_defaults
|
|
57
|
+
const variables = config.variables
|
|
58
|
+
|
|
59
|
+
// 不要继承 base config.gypi 的 defaults,
|
|
60
|
+
// 否则当 node 可执行文件在另一台机器编译(lib/include 路径不同)时会出问题
|
|
61
|
+
defaults.cflags = []
|
|
62
|
+
defaults.defines = []
|
|
63
|
+
defaults.include_dirs = []
|
|
64
|
+
defaults.libraries = []
|
|
65
|
+
|
|
66
|
+
// 设置 default_configuration
|
|
67
|
+
if ('debug' in gyp.opts) {
|
|
68
|
+
defaults.default_configuration = gyp.opts.debug ? 'Debug' : 'Release'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!defaults.default_configuration) {
|
|
72
|
+
defaults.default_configuration = 'Release'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 设置 target_arch
|
|
76
|
+
variables.target_arch = gyp.opts.arch || process.arch || 'ia32'
|
|
77
|
+
if (variables.target_arch === 'arm64') {
|
|
78
|
+
defaults.msvs_configuration_platform = 'ARM64'
|
|
79
|
+
defaults.xcode_configuration_platform = 'arm64'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 设置 Node 开发目录
|
|
83
|
+
variables.nodedir = nodeDir
|
|
84
|
+
|
|
85
|
+
// 设置 Python 路径
|
|
86
|
+
variables.python = python
|
|
87
|
+
|
|
88
|
+
// 默认禁用 -T "thin" 静态归档
|
|
89
|
+
variables.standalone_static_library = gyp.opts.thin ? 0 : 1
|
|
90
|
+
|
|
91
|
+
if (process.platform === 'win32') {
|
|
92
|
+
defaults.msbuild_toolset = vsInfo.toolset
|
|
93
|
+
if (vsInfo.sdk) {
|
|
94
|
+
defaults.msvs_windows_target_platform_version = vsInfo.sdk
|
|
95
|
+
}
|
|
96
|
+
if (variables.target_arch === 'arm64') {
|
|
97
|
+
if (vsInfo.versionMajor > 15 ||
|
|
98
|
+
(vsInfo.versionMajor === 15 && vsInfo.versionMajor >= 9)) {
|
|
99
|
+
defaults.msvs_enable_marmasm = 1
|
|
100
|
+
} else {
|
|
101
|
+
log.warn('Compiling ARM64 assembly is only available in\n' +
|
|
102
|
+
'Visual Studio 2017 version 15.9 and above')
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
variables.msbuild_path = vsInfo.msBuild
|
|
106
|
+
// Windows 原生构建走 MSVC 工具集:必须关闭 clang 与 LTO,
|
|
107
|
+
// 否则 common.gypi 会把 toolset 设为 ClangCL,并向 MSVC 链接器传入 Clang 专用参数
|
|
108
|
+
// (-flto=thin、/opt:lldltojobs),导致 MSB8020 或 LNK1117
|
|
109
|
+
if (!gyp.opts.format || gyp.opts.format === 'msvs') {
|
|
110
|
+
variables.clang = 0
|
|
111
|
+
variables.enable_lto = 'false'
|
|
112
|
+
variables.enable_thin_lto = 'false'
|
|
113
|
+
variables.lto_jobs = ''
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 把其余未知选项当作变量写入,支持模块自定义 configure 参数,例如:
|
|
118
|
+
// $ node-gyp configure --shared-libxml2
|
|
119
|
+
Object.keys(gyp.opts).forEach(function (opt) {
|
|
120
|
+
if (opt === 'argv') {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
if (opt in gyp.configDefs) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
variables[opt.replace(/-/g, '_')] = gyp.opts[opt]
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return config
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 生成并写出 config.gypi 文件
|
|
134
|
+
*/
|
|
135
|
+
const createConfigGypi = async ({ gyp, buildDir, nodeDir, vsInfo, python }) => {
|
|
136
|
+
const configFilename = 'config.gypi'
|
|
137
|
+
const configPath = path.resolve(buildDir, configFilename)
|
|
138
|
+
|
|
139
|
+
log.verbose('build/' + configFilename, 'creating config file')
|
|
140
|
+
|
|
141
|
+
const config = await getCurrentConfigGypi({ gyp, nodeDir, vsInfo, python })
|
|
142
|
+
|
|
143
|
+
// 保证 config.gypi 中的布尔值都转成字符串
|
|
144
|
+
const boolsToString = (k, v) => {
|
|
145
|
+
if (typeof v === 'boolean') {
|
|
146
|
+
return String(v)
|
|
147
|
+
}
|
|
148
|
+
return v
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
log.silly('build/' + configFilename, config)
|
|
152
|
+
|
|
153
|
+
// 把 config.gypi 写入 build/ 目录
|
|
154
|
+
const prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step'
|
|
155
|
+
|
|
156
|
+
const json = JSON.stringify(config, boolsToString, 2)
|
|
157
|
+
log.verbose('build/' + configFilename, 'writing out config file: %s', configPath)
|
|
158
|
+
await fs.writeFile(configPath, [prefix, json, ''].join('\n'))
|
|
159
|
+
|
|
160
|
+
return configPath
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export { createConfigGypi, parseConfigGypi, getCurrentConfigGypi }
|
package/lib/download.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fetch from 'make-fetch-happen'
|
|
2
|
+
import gracefulFs from 'graceful-fs'
|
|
3
|
+
import * as log from './log.js'
|
|
4
|
+
|
|
5
|
+
const { promises: fs } = gracefulFs
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 读取 CA 证书文件,按证书边界切分为多个证书
|
|
9
|
+
*/
|
|
10
|
+
const readCAFile = async (filename) => {
|
|
11
|
+
// CA 文件可能包含多份证书,按证书边界切分
|
|
12
|
+
// [\S\s]*? 用于匹配包括换行在内的所有内容
|
|
13
|
+
const ca = await fs.readFile(filename, 'utf8')
|
|
14
|
+
const re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g
|
|
15
|
+
return ca.match(re)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 使用 make-fetch-happen 下载指定 URL
|
|
20
|
+
*/
|
|
21
|
+
const download = async (gyp, url) => {
|
|
22
|
+
log.http('GET', url)
|
|
23
|
+
|
|
24
|
+
const requestOpts = {
|
|
25
|
+
headers: {
|
|
26
|
+
'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`,
|
|
27
|
+
Connection: 'keep-alive'
|
|
28
|
+
},
|
|
29
|
+
proxy: gyp.opts.proxy,
|
|
30
|
+
noProxy: gyp.opts.noproxy
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const cafile = gyp.opts.cafile
|
|
34
|
+
if (cafile) {
|
|
35
|
+
requestOpts.ca = await readCAFile(cafile)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const res = await fetch(url, requestOpts)
|
|
39
|
+
log.http(res.status, res.url)
|
|
40
|
+
|
|
41
|
+
return res
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { download, readCAFile }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import * as log from './log.js'
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 从脚本位置或进程对象推断 Node 根目录
|
|
9
|
+
*/
|
|
10
|
+
const findNodeDirectory = (scriptLocation, processObj) => {
|
|
11
|
+
// 未传入时使用默认值,便于回归测试
|
|
12
|
+
if (scriptLocation === undefined) {
|
|
13
|
+
scriptLocation = __dirname
|
|
14
|
+
}
|
|
15
|
+
if (processObj === undefined) {
|
|
16
|
+
processObj = process
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 看看上层目录结构,判断当前所处位置
|
|
20
|
+
const npmParentDirectory = path.join(scriptLocation, '../../../..')
|
|
21
|
+
log.verbose('node-gyp root', 'npm_parent_directory is ' +
|
|
22
|
+
path.basename(npmParentDirectory))
|
|
23
|
+
let nodeRootDir = ''
|
|
24
|
+
|
|
25
|
+
log.verbose('node-gyp root', 'Finding node root directory')
|
|
26
|
+
if (path.basename(npmParentDirectory) === 'deps') {
|
|
27
|
+
// 处于构建目录中,脚本位于 deps/npm/node_modules/node-gyp/lib
|
|
28
|
+
nodeRootDir = path.join(npmParentDirectory, '..')
|
|
29
|
+
log.verbose('node-gyp root', 'in build directory, root = ' +
|
|
30
|
+
nodeRootDir)
|
|
31
|
+
} else if (path.basename(npmParentDirectory) === 'node_modules') {
|
|
32
|
+
// 处于 Node 安装目录中,脚本位于
|
|
33
|
+
// lib/node_modules/npm/node_modules/node-gyp/lib 或
|
|
34
|
+
// node_modules/npm/node_modules/node-gyp/lib(视平台而定)
|
|
35
|
+
if (processObj.platform === 'win32') {
|
|
36
|
+
nodeRootDir = path.join(npmParentDirectory, '..')
|
|
37
|
+
} else {
|
|
38
|
+
nodeRootDir = path.join(npmParentDirectory, '../..')
|
|
39
|
+
}
|
|
40
|
+
log.verbose('node-gyp root', 'in install directory, root = ' +
|
|
41
|
+
nodeRootDir)
|
|
42
|
+
} else {
|
|
43
|
+
// 无法判断,尝试从 node 可执行文件的位置推断
|
|
44
|
+
const nodeDir = path.dirname(processObj.execPath)
|
|
45
|
+
const directoryUp = path.basename(nodeDir)
|
|
46
|
+
if (directoryUp === 'bin') {
|
|
47
|
+
nodeRootDir = path.join(nodeDir, '..')
|
|
48
|
+
} else if (directoryUp === 'Release' || directoryUp === 'Debug') {
|
|
49
|
+
// 如果是刚构建的 node,且目录结构为仓库结构:
|
|
50
|
+
// Windows 只需上一层,其余平台两层
|
|
51
|
+
if (processObj.platform === 'win32') {
|
|
52
|
+
nodeRootDir = path.join(nodeDir, '..')
|
|
53
|
+
} else {
|
|
54
|
+
nodeRootDir = path.join(nodeDir, '../..')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 否则返回默认空串 ""
|
|
58
|
+
}
|
|
59
|
+
return nodeRootDir
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { findNodeDirectory }
|