Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
configure.js329 linesDownload Raw Back to lib
1'use strict'
2
3const { promises: fs, readFileSync } = require('graceful-fs')
4const path = require('path')
5const log = require('./log')
6const os = require('os')
7const processRelease = require('./process-release')
8const win = process.platform === 'win32'
9const findNodeDirectory = require('./find-node-directory')
10const { createConfigGypi } = require('./create-config-gypi')
11const { format: msgFormat } = require('util')
12const { findAccessibleSync } = require('./util')
13const { findPython } = require('./find-python')
14const { findVisualStudio } = win ? require('./find-visualstudio') : {}
15
16const majorRe = /^#define NODE_MAJOR_VERSION (\d+)/m
17const minorRe = /^#define NODE_MINOR_VERSION (\d+)/m
18const patchRe = /^#define NODE_PATCH_VERSION (\d+)/m
19
20async function configure (gyp, argv) {
21  const buildDir = path.resolve('build')
22  const configNames = ['config.gypi', 'common.gypi']
23  const configs = []
24  let nodeDir
25  const release = processRelease(argv, gyp, process.version, process.release)
26
27  const python = await findPython(gyp.opts.python)
28  return getNodeDir()
29
30  async function getNodeDir () {
31    // 'python' should be set by now
32    process.env.PYTHON = python
33
34    if (!gyp.opts.nodedir &&
35        process.config.variables.use_prefix_to_find_headers) {
36      // check if the headers can be found using the prefix specified
37      // at build time. Use them if they match the version expected
38      const prefix = process.config.variables.node_prefix
39      let availVersion
40      try {
41        const nodeVersionH = readFileSync(path.join(prefix,
42          'include', 'node', 'node_version.h'), { encoding: 'utf8' })
43        const major = nodeVersionH.match(majorRe)[1]
44        const minor = nodeVersionH.match(minorRe)[1]
45        const patch = nodeVersionH.match(patchRe)[1]
46        availVersion = major + '.' + minor + '.' + patch
47      } catch {}
48      if (availVersion === release.version) {
49        // ok version matches, use the headers
50        gyp.opts.nodedir = prefix
51        log.verbose('using local node headers based on prefix',
52          'setting nodedir to ' + gyp.opts.nodedir)
53      }
54    }
55
56    if (gyp.opts.nodedir) {
57      // --nodedir was specified. use that for the dev files
58      nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir())
59      log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir)
60    } else {
61      // if no --nodedir specified, ensure node dependencies are installed
62      if ('v' + release.version !== process.version) {
63        // if --target was given, then determine a target version to compile for
64        log.verbose('get node dir', 'compiling against --target node version: %s', release.version)
65      } else {
66        // if no --target was specified then use the current host node version
67        log.verbose('get node dir', 'no --target version specified, falling back to host node version: %s', release.version)
68      }
69
70      if (!release.semver) {
71        // could not parse the version string with semver
72        throw new Error('Invalid version number: ' + release.version)
73      }
74
75      // If the tarball option is set, always remove and reinstall the headers
76      // into devdir. Otherwise only install if they're not already there.
77      gyp.opts.ensure = !gyp.opts.tarball
78
79      await gyp.commands.install([release.version])
80
81      log.verbose('get node dir', 'target node version installed:', release.versionDir)
82      nodeDir = path.resolve(gyp.devDir, release.versionDir)
83    }
84
85    return createBuildDir()
86  }
87
88  async function createBuildDir () {
89    log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir)
90
91    const isNew = await fs.mkdir(buildDir, { recursive: true })
92    log.verbose(
93      'build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No'
94    )
95    if (win) {
96      let usingMakeGenerator = false
97      for (let i = argv.length - 1; i >= 0; --i) {
98        const arg = argv[i]
99        if (arg === '-f' || arg === '--format') {
100          const format = argv[i + 1]
101          if (typeof format === 'string' && format.startsWith('make')) {
102            usingMakeGenerator = true
103            break
104          }
105        } else if (arg.startsWith('--format=make')) {
106          usingMakeGenerator = true
107          break
108        }
109      }
110      let vsInfo = {}
111      if (!usingMakeGenerator) {
112        vsInfo = await findVisualStudio(release.semver, gyp.opts['msvs-version'])
113      }
114      return createConfigFile(vsInfo)
115    }
116    return createConfigFile(null)
117  }
118
119  async function createConfigFile (vsInfo) {
120    if (win) {
121      process.env.GYP_MSVS_VERSION = Math.min(vsInfo.versionYear, 2015)
122      process.env.GYP_MSVS_OVERRIDE_PATH = vsInfo.path
123    }
124    const configPath = await createConfigGypi({ gyp, buildDir, nodeDir, vsInfo, python })
125    configs.push(configPath)
126    return findConfigs()
127  }
128
129  async function findConfigs () {
130    const name = configNames.shift()
131    if (!name) {
132      return runGyp()
133    }
134
135    const fullPath = path.resolve(name)
136    log.verbose(name, 'checking for gypi file: %s', fullPath)
137    try {
138      await fs.stat(fullPath)
139      log.verbose(name, 'found gypi file')
140      configs.push(fullPath)
141    } catch (err) {
142      // ENOENT will check next gypi filename
143      if (err.code !== 'ENOENT') {
144        throw err
145      }
146    }
147
148    return findConfigs()
149  }
150
151  async function runGyp () {
152    if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
153      if (win) {
154        log.verbose('gyp', 'gyp format was not specified; forcing "msvs"')
155        // force the 'make' target for non-Windows
156        argv.push('-f', 'msvs')
157      } else {
158        log.verbose('gyp', 'gyp format was not specified; forcing "make"')
159        // force the 'make' target for non-Windows
160        argv.push('-f', 'make')
161      }
162    }
163
164    // include all the ".gypi" files that were found
165    configs.forEach(function (config) {
166      argv.push('-I', config)
167    })
168
169    // For AIX and z/OS we need to set up the path to the exports file
170    // which contains the symbols needed for linking.
171    let nodeExpFile
172    let nodeRootDir
173    let candidates
174    let logprefix = 'find exports file'
175    if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
176      const ext = process.platform === 'os390' ? 'x' : 'exp'
177      nodeRootDir = findNodeDirectory()
178
179      if (process.platform === 'aix' || process.platform === 'os400') {
180        candidates = [
181          'include/node/node',
182          'out/Release/node',
183          'out/Debug/node',
184          'node'
185        ].map(function (file) {
186          return file + '.' + ext
187        })
188      } else {
189        candidates = [
190          'out/Release/lib.target/libnode',
191          'out/Debug/lib.target/libnode',
192          'out/Release/obj.target/libnode',
193          'out/Debug/obj.target/libnode',
194          'lib/libnode'
195        ].map(function (file) {
196          return file + '.' + ext
197        })
198      }
199
200      nodeExpFile = findAccessibleSync(logprefix, nodeRootDir, candidates)
201      if (nodeExpFile !== undefined) {
202        log.verbose(logprefix, 'Found exports file: %s', nodeExpFile)
203      } else {
204        const msg = msgFormat('Could not find node.%s file in %s', ext, nodeRootDir)
205        log.error(logprefix, 'Could not find exports file')
206        throw new Error(msg)
207      }
208    }
209
210    // For z/OS we need to set up the path to zoslib include directory,
211    // which contains headers included in v8config.h.
212    let zoslibIncDir
213    if (process.platform === 'os390') {
214      logprefix = "find zoslib's zos-base.h:"
215      let msg
216      let zoslibIncPath = process.env.ZOSLIB_INCLUDES
217      if (zoslibIncPath) {
218        zoslibIncPath = findAccessibleSync(logprefix, zoslibIncPath, ['zos-base.h'])
219        if (zoslibIncPath === undefined) {
220          msg = msgFormat('Could not find zos-base.h file in the directory set ' +
221                          'in ZOSLIB_INCLUDES environment variable: %s; set it ' +
222                          'to the correct path, or unset it to search %s', process.env.ZOSLIB_INCLUDES, nodeRootDir)
223        }
224      } else {
225        candidates = [
226          'include/node/zoslib/zos-base.h',
227          'include/zoslib/zos-base.h',
228          'zoslib/include/zos-base.h',
229          'install/include/node/zoslib/zos-base.h'
230        ]
231        zoslibIncPath = findAccessibleSync(logprefix, nodeRootDir, candidates)
232        if (zoslibIncPath === undefined) {
233          msg = msgFormat('Could not find any of %s in directory %s; set ' +
234                          'environmant variable ZOSLIB_INCLUDES to the path ' +
235                          'that contains zos-base.h', candidates.toString(), nodeRootDir)
236        }
237      }
238      if (zoslibIncPath !== undefined) {
239        zoslibIncDir = path.dirname(zoslibIncPath)
240        log.verbose(logprefix, "Found zoslib's zos-base.h in: %s", zoslibIncDir)
241      } else if (release.version.split('.')[0] >= 16) {
242        // zoslib is only shipped in Node v16 and above.
243        log.error(logprefix, msg)
244        throw new Error(msg)
245      }
246    }
247
248    // this logic ported from the old `gyp_addon` python file
249    const gypScript = path.resolve(__dirname, '..', 'gyp', 'gyp_main.py')
250    const addonGypi = path.resolve(__dirname, '..', 'addon.gypi')
251    let commonGypi = path.resolve(nodeDir, 'include/node/common.gypi')
252    try {
253      await fs.stat(commonGypi)
254    } catch (err) {
255      commonGypi = path.resolve(nodeDir, 'common.gypi')
256    }
257
258    let outputDir = 'build'
259    if (win) {
260      // Windows expects an absolute path
261      outputDir = buildDir
262    }
263    const nodeGypDir = path.resolve(__dirname, '..')
264
265    let nodeLibFile = path.join(nodeDir,
266      !gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)',
267      release.name + '.lib')
268
269    argv.push('-I', addonGypi)
270    argv.push('-I', commonGypi)
271    argv.push('-Dlibrary=shared_library')
272    argv.push('-Dvisibility=default')
273    argv.push('-Dnode_root_dir=' + nodeDir)
274    if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
275      argv.push('-Dnode_exp_file=' + nodeExpFile)
276      if (process.platform === 'os390' && zoslibIncDir) {
277        argv.push('-Dzoslib_include_dir=' + zoslibIncDir)
278      }
279    }
280    argv.push('-Dnode_gyp_dir=' + nodeGypDir)
281
282    // Do this to keep Cygwin environments happy, else the unescaped '\' gets eaten up,
283    // resulting in bad paths, Ex c:parentFolderfolderanotherFolder instead of c:\parentFolder\folder\anotherFolder
284    if (win) {
285      nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\')
286    }
287    argv.push('-Dnode_lib_file=' + nodeLibFile)
288    argv.push('-Dmodule_root_dir=' + process.cwd())
289    argv.push('-Dnode_engine=' +
290        (gyp.opts.node_engine || process.jsEngine || 'v8'))
291    argv.push('--depth=.')
292    argv.push('--no-parallel')
293
294    // tell gyp to write the Makefile/Solution files into output_dir
295    argv.push('--generator-output', outputDir)
296
297    // tell make to write its output into the same dir
298    argv.push('-Goutput_dir=.')
299
300    // enforce use of the "binding.gyp" file
301    argv.unshift('binding.gyp')
302
303    // execute `gyp` from the current target nodedir
304    argv.unshift(gypScript)
305
306    // make sure python uses files that came with this particular node package
307    const pypath = [path.join(__dirname, '..', 'gyp', 'pylib')]
308    if (process.env.PYTHONPATH) {
309      pypath.push(process.env.PYTHONPATH)
310    }
311    process.env.PYTHONPATH = pypath.join(win ? ';' : ':')
312
313    await new Promise((resolve, reject) => {
314      const cp = gyp.spawn(python, argv)
315      cp.on('exit', (code) => {
316        if (code !== 0) {
317          reject(new Error('`gyp` failed with exit code: ' + code))
318        } else {
319          // we're done
320          resolve()
321        }
322      })
323    })
324  }
325}
326
327module.exports = configure
328module.exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module'
329 
codekingpro/portable-devtools · Team Ai