1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import platform
import subprocess
from distutils.version import StrictVersion
from mozboot.util import get_tools_dir
from mozfile import which
from six import PY3
NODE_MIN_VERSION = StrictVersion("12.22.12")
NPM_MIN_VERSION = StrictVersion("6.14.16")
def find_node_paths():
"""Determines the possible paths for node executables.
Returns a list of paths, which includes the build state directory.
"""
mozbuild_tools_dir = get_tools_dir()
if platform.system() == "Windows":
mozbuild_node_path = os.path.join(mozbuild_tools_dir, "node")
else:
mozbuild_node_path = os.path.join(mozbuild_tools_dir, "node", "bin")
# We still fallback to the PATH, since on OSes that don't have toolchain
# artifacts available to download, Node may be coming from $PATH.
paths = [mozbuild_node_path] + os.environ.get("PATH").split(os.pathsep)
if platform.system() == "Windows":
paths += [
"%s\\nodejs" % os.environ.get("SystemDrive"),
os.path.join(os.environ.get("ProgramFiles"), "nodejs"),
os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"),
os.path.join(os.environ.get("PROGRAMFILES"), "nodejs"),
]
return paths
def check_executable_version(exe, wrap_call_with_node=False):
"""Determine the version of a Node executable by invoking it.
May raise ``subprocess.CalledProcessError`` or ``ValueError`` on failure.
"""
out = None
# npm may be a script (Except on Windows), so we must call it with node.
if wrap_call_with_node and platform.system() != "Windows":
binary, _ = find_node_executable()
if binary:
out = (
subprocess.check_output(
[binary, exe, "--version"], universal_newlines=PY3
)
.lstrip("v")
.rstrip()
)
# If we can't find node, or we don't need to wrap it, fallback to calling
# direct.
if not out:
out = (
subprocess.check_output([exe, "--version"], universal_newlines=PY3)
.lstrip("v")
.rstrip()
)
return StrictVersion(out)
def find_node_executable(
nodejs_exe=os.environ.get("NODEJS"), min_version=NODE_MIN_VERSION
):
"""Find a Node executable from the mozbuild directory.
Returns a tuple containing the the path to an executable binary and a
version tuple. Both tuple entries will be None if a Node executable
could not be resolved.
"""
if nodejs_exe:
try:
version = check_executable_version(nodejs_exe)
except (subprocess.CalledProcessError, ValueError):
return None, None
if version >= min_version:
return nodejs_exe, version.version
return None, None
# "nodejs" is first in the tuple on the assumption that it's only likely to
# exist on systems (probably linux distros) where there is a program in the path
# called "node" that does something else.
return find_executable("node", min_version)
def find_npm_executable(min_version=NPM_MIN_VERSION):
"""Find a Node executable from the mozbuild directory.
Returns a tuple containing the the path to an executable binary and a
version tuple. Both tuple entries will be None if a Node executable
could not be resolved.
"""
return find_executable("npm", min_version, True)
def find_executable(name, min_version, use_node_for_version_check=False):
paths = find_node_paths()
exe = which(name, path=paths)
if not exe:
return None, None
# Verify we can invoke the executable and its version is acceptable.
try:
version = check_executable_version(exe, use_node_for_version_check)
except (subprocess.CalledProcessError, ValueError):
return None, None
if version < min_version:
return None, None
return exe, version.version
|