diff options
Diffstat (limited to 'python/mozbuild/mozbuild/nodeutil.py')
-rw-r--r-- | python/mozbuild/mozbuild/nodeutil.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/nodeutil.py b/python/mozbuild/mozbuild/nodeutil.py new file mode 100644 index 0000000000..42f2627cd9 --- /dev/null +++ b/python/mozbuild/mozbuild/nodeutil.py @@ -0,0 +1,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 mozboot.util import get_tools_dir +from mozfile import which +from packaging.version import Version +from six import PY3 + +NODE_MIN_VERSION = Version("12.22.12") +NPM_MIN_VERSION = Version("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 Version(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.release + + 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.release |