@flun/nodejs-mobile-gyp 1.0.1 → 1.0.2
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 +56 -36
- package/README.md +256 -252
- package/addon.gypi +13 -1
- package/bin/node-gyp.js +66 -109
- package/gyp/pylib/gyp/MSVSSettings.py +13 -0
- package/lib/build.js +127 -209
- package/lib/clean.js +13 -16
- package/lib/configure.js +183 -265
- package/lib/create-config-gypi.js +102 -156
- package/lib/download.js +29 -42
- package/lib/find-node-directory.js +38 -55
- package/lib/find-python.js +162 -198
- package/lib/find-visualstudio.js +196 -295
- package/lib/install.js +269 -378
- package/lib/list.js +19 -25
- package/lib/log.js +69 -133
- package/lib/node-gyp.js +62 -107
- package/lib/process-release.js +100 -139
- package/lib/rebuild.js +3 -8
- package/lib/remove.js +34 -44
- package/lib/util.js +62 -73
- package/package.json +5 -3
- package/gyp/LICENSE +0 -28
|
@@ -1,163 +1,109 @@
|
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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)
|
|
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
|
+
* 解析 config.gypi 内容为 JSON 对象
|
|
8
|
+
* 逻辑翻译自 Node.js 的 tools/js2c.py
|
|
9
|
+
*/
|
|
10
|
+
parseConfigGypi = config => {
|
|
11
|
+
// 去掉 # 注释,拼接多行字符串,把单引号字符串归一化为双引号
|
|
12
|
+
config = config.replace(/#.*/g, ''), config = config.replace(/'$\s+'/mg, ''), config = config.replace(/'/g, '"');
|
|
13
|
+
return JSON.parse(config);
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* 读取基础 config.gypi
|
|
17
|
+
*/
|
|
18
|
+
getBaseConfigGypi = async ({ gyp, nodeDir }) => {
|
|
19
|
+
// 在以下情况优先读取 $nodeDir/include/node/config.gypi:
|
|
20
|
+
// 1. 指定了 --dist-url 或 --nodedir
|
|
21
|
+
// 2. 且没有指定 --force-process-config
|
|
22
|
+
const useCustomHeaders = gyp.opts.nodedir || gyp.opts.disturl || gyp.opts['dist-url'],
|
|
23
|
+
shouldReadConfigGypi = useCustomHeaders && !gyp.opts['force-process-config'];
|
|
24
|
+
if (shouldReadConfigGypi && nodeDir) {
|
|
25
|
+
try {
|
|
26
|
+
const baseConfigGypi = await fs.readFile(path.resolve(nodeDir, 'include/node/config.gypi'));
|
|
27
|
+
return parseConfigGypi(baseConfigGypi.toString());
|
|
28
|
+
} catch (err) { log.warn('read config.gypi', err.message) }
|
|
37
29
|
}
|
|
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
30
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
31
|
+
return JSON.parse(JSON.stringify(process.config)); // 回退到 process.config
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 基于当前环境生成 config.gypi 内容
|
|
36
|
+
*/
|
|
37
|
+
getCurrentConfigGypi = async ({ gyp, nodeDir, vsInfo, python }) => {
|
|
38
|
+
const config = await getBaseConfigGypi({ gyp, nodeDir });
|
|
39
|
+
if (!config.target_defaults) config.target_defaults = {};
|
|
40
|
+
if (!config.variables) config.variables = {};
|
|
41
|
+
|
|
42
|
+
const defaults = config.target_defaults, variables = config.variables;
|
|
43
|
+
// 不要继承 base config.gypi 的 defaults,
|
|
44
|
+
// 否则当 node 可执行文件在另一台机器编译(lib/include 路径不同)时会出问题
|
|
45
|
+
defaults.cflags = [], defaults.defines = [], defaults.include_dirs = [], defaults.libraries = [];
|
|
46
|
+
|
|
47
|
+
// 设置 default_configuration
|
|
48
|
+
if ('debug' in gyp.opts) defaults.default_configuration = gyp.opts.debug ? 'Debug' : 'Release'
|
|
49
|
+
if (!defaults.default_configuration) defaults.default_configuration = 'Release';
|
|
50
|
+
|
|
51
|
+
// 设置 target_arch
|
|
52
|
+
variables.target_arch = gyp.opts.arch || process.arch || 'ia32';
|
|
53
|
+
if (variables.target_arch === 'arm64')
|
|
54
|
+
defaults.msvs_configuration_platform = 'ARM64', defaults.xcode_configuration_platform = 'arm64';
|
|
55
|
+
|
|
56
|
+
// 设置 Node 开发目录和 Python 路径,默认禁用 -T "thin" 静态归档
|
|
57
|
+
variables.nodedir = nodeDir, variables.python = python;
|
|
58
|
+
variables.standalone_static_library = gyp.opts.thin ? 0 : 1;
|
|
59
|
+
|
|
60
|
+
if (process.platform === 'win32') {
|
|
61
|
+
defaults.msbuild_toolset = vsInfo.toolset;
|
|
62
|
+
if (vsInfo.sdk) defaults.msvs_windows_target_platform_version = vsInfo.sdk;
|
|
63
|
+
if (variables.target_arch === 'arm64') {
|
|
64
|
+
if (vsInfo.versionMajor > 15 || (vsInfo.versionMajor === 15 && vsInfo.versionMajor >= 9))
|
|
65
|
+
defaults.msvs_enable_marmasm = 1;
|
|
66
|
+
else log.warn('仅 Visual Studio 2017 15.9 及以上版本支持编译 ARM64 汇编');
|
|
103
67
|
}
|
|
68
|
+
variables.msbuild_path = vsInfo.msBuild;
|
|
69
|
+
// Windows 原生构建走 MSVC 工具集:必须关闭 clang 与 LTO,
|
|
70
|
+
// 否则 common.gypi 会把 toolset 设为 ClangCL,并向 MSVC 链接器传入 Clang 专用参数
|
|
71
|
+
// (-flto=thin、/opt:lldltojobs),导致 MSB8020 或 LNK1117
|
|
72
|
+
if (!gyp.opts.format || gyp.opts.format === 'msvs')
|
|
73
|
+
variables.clang = 0, variables.enable_lto = 'false', variables.enable_thin_lto = 'false', variables.lto_jobs = '';
|
|
104
74
|
}
|
|
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
75
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
76
|
+
// 把其余未知选项当作变量写入,支持模块自定义 configure 参数,例如:
|
|
77
|
+
// $ node-gyp configure --shared-libxml2
|
|
78
|
+
for (const [k, v] of Object.entries(gyp.opts)) {
|
|
79
|
+
if (k === 'argv' || k in gyp.configDefs) continue;
|
|
80
|
+
variables[k.replace(/-/g, '_')] = v;
|
|
122
81
|
}
|
|
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
82
|
|
|
163
|
-
|
|
83
|
+
return config;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 生成并写出 config.gypi 文件
|
|
88
|
+
*/
|
|
89
|
+
createConfigGypi = async ({ gyp, buildDir, nodeDir, vsInfo, python }) => {
|
|
90
|
+
const configFilename = 'config.gypi', configPath = path.resolve(buildDir, configFilename);
|
|
91
|
+
|
|
92
|
+
log.verbose('build/' + configFilename, 'creating config file');
|
|
93
|
+
const config = await getCurrentConfigGypi({ gyp, nodeDir, vsInfo, python }),
|
|
94
|
+
// 保证 config.gypi 中的布尔值都转成字符串
|
|
95
|
+
boolsToString = (k, v) => {
|
|
96
|
+
if (typeof v === 'boolean') return String(v);
|
|
97
|
+
return v;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
log.silly('build/' + configFilename, config);
|
|
101
|
+
// 把 config.gypi 写入 build/ 目录
|
|
102
|
+
const prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step',
|
|
103
|
+
json = JSON.stringify(config, boolsToString, 2);
|
|
104
|
+
log.verbose('build/' + configFilename, 'writing out config file: %s', configPath);
|
|
105
|
+
await fs.writeFile(configPath, [prefix, json, ''].join('\n'));
|
|
106
|
+
return configPath;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export { createConfigGypi, parseConfigGypi, getCurrentConfigGypi }
|
package/lib/download.js
CHANGED
|
@@ -1,44 +1,31 @@
|
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
proxy: gyp.opts.proxy,
|
|
30
|
-
noProxy: gyp.opts.noproxy
|
|
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
|
+
* 读取 CA 证书文件,按证书边界切分为多个证书
|
|
8
|
+
*/
|
|
9
|
+
readCAFile = async filename => {
|
|
10
|
+
// CA 文件可能包含多份证书,按证书边界切分; [\S\s]*? 用于匹配包括换行在内的所有内容;
|
|
11
|
+
const ca = await fs.readFile(filename, 'utf8'), re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g;
|
|
12
|
+
return ca.match(re);
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* 使用 make-fetch-happen 下载指定 URL
|
|
16
|
+
*/
|
|
17
|
+
download = async (gyp, url) => {
|
|
18
|
+
log.http('GET', url);
|
|
19
|
+
|
|
20
|
+
const requestOpts = {
|
|
21
|
+
headers: { 'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`, Connection: 'keep-alive' },
|
|
22
|
+
proxy: gyp.opts.proxy, noProxy: gyp.opts.noproxy
|
|
23
|
+
}, cafile = gyp.opts.cafile;
|
|
24
|
+
|
|
25
|
+
if (cafile) requestOpts.ca = await readCAFile(cafile);
|
|
26
|
+
const res = await fetch(url, requestOpts);
|
|
27
|
+
log.http(res.status, res.url);
|
|
28
|
+
return res;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
|
|
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 }
|
|
31
|
+
export { readCAFile, download }
|
|
@@ -1,62 +1,45 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
import * as log from './log.js'
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import * as log from './log.js';
|
|
4
4
|
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url)),
|
|
6
|
+
/**
|
|
7
|
+
* 从脚本位置或进程对象推断 Node 根目录
|
|
8
|
+
*/
|
|
9
|
+
findNodeDirectory = (scriptLocation, processObj) => {
|
|
10
|
+
// 未传入时使用默认值,便于回归测试
|
|
11
|
+
if (scriptLocation === undefined) scriptLocation = __dirname;
|
|
12
|
+
if (processObj === undefined) processObj = process;
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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, '..')
|
|
14
|
+
// 看看上层目录结构,判断当前所处位置
|
|
15
|
+
const npmParentDirectory = path.join(scriptLocation, '../../../..');
|
|
16
|
+
log.verbose('node-gyp root', 'npm_parent_directory is ' + path.basename(npmParentDirectory));
|
|
17
|
+
log.verbose('node-gyp root', 'Finding node root directory');
|
|
18
|
+
let nodeRootDir = '';
|
|
19
|
+
if (path.basename(npmParentDirectory) === 'deps') {
|
|
20
|
+
// 处于构建目录中,脚本位于 deps/npm/node_modules/node-gyp/lib
|
|
21
|
+
nodeRootDir = path.join(npmParentDirectory, '..');
|
|
22
|
+
log.verbose('node-gyp root', 'in build directory, root = ' + nodeRootDir);
|
|
23
|
+
} else if (path.basename(npmParentDirectory) === 'node_modules') {
|
|
24
|
+
// 处于 Node 安装目录中,脚本位于
|
|
25
|
+
// lib/node_modules/npm/node_modules/node-gyp/lib 或
|
|
26
|
+
// node_modules/npm/node_modules/node-gyp/lib(视平台而定)
|
|
27
|
+
if (processObj.platform === 'win32') nodeRootDir = path.join(npmParentDirectory, '..');
|
|
28
|
+
else nodeRootDir = path.join(npmParentDirectory, '../..');
|
|
29
|
+
log.verbose('node-gyp root', 'in install directory, root = ' + nodeRootDir);
|
|
37
30
|
} else {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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, '../..')
|
|
31
|
+
// 无法判断,尝试从 node 可执行文件的位置推断
|
|
32
|
+
const nodeDir = path.dirname(processObj.execPath), directoryUp = path.basename(nodeDir);
|
|
33
|
+
if (directoryUp === 'bin') nodeRootDir = path.join(nodeDir, '..');
|
|
34
|
+
else if (directoryUp === 'Release' || directoryUp === 'Debug') {
|
|
35
|
+
// 如果是刚构建的 node,且目录结构为仓库结构:
|
|
36
|
+
// Windows 只需上一层,其余平台两层
|
|
37
|
+
if (processObj.platform === 'win32') nodeRootDir = path.join(nodeDir, '..');
|
|
38
|
+
else nodeRootDir = path.join(nodeDir, '../..');
|
|
55
39
|
}
|
|
40
|
+
// 否则返回默认空串 ""
|
|
56
41
|
}
|
|
57
|
-
|
|
42
|
+
return nodeRootDir;
|
|
58
43
|
}
|
|
59
|
-
return nodeRootDir
|
|
60
|
-
}
|
|
61
44
|
|
|
62
|
-
export { findNodeDirectory }
|
|
45
|
+
export { findNodeDirectory }
|