summaryrefslogtreecommitdiff
path: root/mesonbuild/environment.py
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-10-07 18:22:06 +0200
committerJussi Pakkanen <jussi.pakkanen@mailbox.org>2025-10-29 18:59:30 +0200
commit795e7431ffafa0cc1257d64c5fdd0f2c3cb6d841 (patch)
tree40ff7c80d4ae38cb1b144824a284e0115368e7c0 /mesonbuild/environment.py
parent1614401329f11d3763a56ed5ce207aba511a6a9d (diff)
downloadmeson-795e7431ffafa0cc1257d64c5fdd0f2c3cb6d841.tar.gz
environment: move tool detection functions to a new module
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild/environment.py')
-rw-r--r--mesonbuild/environment.py239
1 files changed, 3 insertions, 236 deletions
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 4d89c88ef..90b10697a 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -5,7 +5,7 @@
from __future__ import annotations
import itertools
-import os, re, shutil
+import os, re
import typing as T
import collections
@@ -17,8 +17,8 @@ from . import options
from .mesonlib import (
MesonException, MachineChoice, Popen_safe, PerMachine,
- PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg,
- search_version, MesonBugException
+ PerMachineDefaultable, PerThreeMachineDefaultable, split_args,
+ MesonBugException
)
from .options import OptionKey
from . import mlog
@@ -78,239 +78,6 @@ def _get_env_var(for_machine: MachineChoice, is_cross: bool, var_name: str) -> T
return value
-def detect_gcovr(gcovr_exe: str = 'gcovr', min_version: str = '3.3', log: bool = False) \
- -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
- try:
- p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- return None, None
- found = search_version(found)
- if p.returncode == 0 and mesonlib.version_compare(found, '>=' + min_version):
- if log:
- mlog.log('Found gcovr-{} at {}'.format(found, quote_arg(shutil.which(gcovr_exe))))
- return gcovr_exe, found
- return None, None
-
-def detect_lcov(lcov_exe: str = 'lcov', log: bool = False) \
- -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
- try:
- p, found = Popen_safe([lcov_exe, '--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- return None, None
- found = search_version(found)
- if p.returncode == 0 and found:
- if log:
- mlog.log('Found lcov-{} at {}'.format(found, quote_arg(shutil.which(lcov_exe))))
- return lcov_exe, found
- return None, None
-
-def detect_llvm_cov(suffix: T.Optional[str] = None) -> T.Optional[str]:
- # If there's a known suffix or forced lack of suffix, use that
- if suffix is not None:
- if suffix == '':
- tool = 'llvm-cov'
- else:
- tool = f'llvm-cov-{suffix}'
- if shutil.which(tool) is not None:
- return tool
- else:
- # Otherwise guess in the dark
- tools = get_llvm_tool_names('llvm-cov')
- for tool in tools:
- if shutil.which(tool):
- return tool
- return None
-
-def compute_llvm_suffix(coredata: coredata.CoreData) -> T.Optional[str]:
- # Check to see if the user is trying to do coverage for either a C or C++ project
- compilers = coredata.compilers[MachineChoice.BUILD]
- cpp_compiler_is_clang = 'cpp' in compilers and compilers['cpp'].id == 'clang'
- c_compiler_is_clang = 'c' in compilers and compilers['c'].id == 'clang'
- # Extract first the C++ compiler if available. If it's a Clang of some kind, compute the suffix if possible
- if cpp_compiler_is_clang:
- suffix = compilers['cpp'].version.split('.')[0]
- return suffix
-
- # Then the C compiler, again checking if it's some kind of Clang and computing the suffix
- if c_compiler_is_clang:
- suffix = compilers['c'].version.split('.')[0]
- return suffix
-
- # Neither compiler is a Clang, or no compilers are for C or C++
- return None
-
-def detect_lcov_genhtml(lcov_exe: str = 'lcov', genhtml_exe: str = 'genhtml') \
- -> T.Tuple[str, T.Optional[str], str]:
- lcov_exe, lcov_version = detect_lcov(lcov_exe)
- if shutil.which(genhtml_exe) is None:
- genhtml_exe = None
-
- return lcov_exe, lcov_version, genhtml_exe
-
-def find_coverage_tools(coredata: coredata.CoreData) -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]:
- gcovr_exe, gcovr_version = detect_gcovr()
-
- llvm_cov_exe = detect_llvm_cov(compute_llvm_suffix(coredata))
- # Some platforms may provide versioned clang but only non-versioned llvm utils
- if llvm_cov_exe is None:
- llvm_cov_exe = detect_llvm_cov('')
-
- lcov_exe, lcov_version, genhtml_exe = detect_lcov_genhtml()
-
- return gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe
-
-def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.Optional[T.List[str]]:
- r = detect_ninja_command_and_version(version, log)
- return r[0] if r else None
-
-def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) -> T.Optional[T.Tuple[T.List[str], str]]:
- env_ninja = os.environ.get('NINJA', None)
- for n in [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu']:
- prog = ExternalProgram(n, silent=True)
- if not prog.found():
- continue
- try:
- p, found = Popen_safe(prog.command + ['--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- continue
- found = found.strip()
- # Perhaps we should add a way for the caller to know the failure mode
- # (not found or too old)
- if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version):
- if log:
- name = os.path.basename(n)
- if name.endswith('-' + found):
- name = name[0:-1 - len(found)]
- if name == 'ninja-build':
- name = 'ninja'
- if name == 'samu':
- name = 'samurai'
- mlog.log('Found {}-{} at {}'.format(name, found,
- ' '.join([quote_arg(x) for x in prog.command])))
- return (prog.command, found)
- return None
-
-def get_llvm_tool_names(tool: str) -> T.List[str]:
- # Ordered list of possible suffixes of LLVM executables to try. Start with
- # base, then try newest back to oldest (3.5 is arbitrary), and finally the
- # devel version. Please note that the development snapshot in Debian does
- # not have a distinct name. Do not move it to the beginning of the list
- # unless it becomes a stable release.
- suffixes = [
- '', # base (no suffix)
- '-21.1', '21.1',
- '-21', '21',
- '-20.1', '20.1',
- '-20', '20',
- '-19.1', '19.1',
- '-19', '19',
- '-18.1', '18.1',
- '-18', '18',
- '-17', '17',
- '-16', '16',
- '-15', '15',
- '-14', '14',
- '-13', '13',
- '-12', '12',
- '-11', '11',
- '-10', '10',
- '-9', '90',
- '-8', '80',
- '-7', '70',
- '-6.0', '60',
- '-5.0', '50',
- '-4.0', '40',
- '-3.9', '39',
- '-3.8', '38',
- '-3.7', '37',
- '-3.6', '36',
- '-3.5', '35',
- '-20', # Debian development snapshot
- '-devel', # FreeBSD development snapshot
- ]
- names: T.List[str] = []
- for suffix in suffixes:
- names.append(tool + suffix)
- return names
-
-def detect_scanbuild() -> T.List[str]:
- """ Look for scan-build binary on build platform
-
- First, if a SCANBUILD env variable has been provided, give it precedence
- on all platforms.
-
- For most platforms, scan-build is found is the PATH contains a binary
- named "scan-build". However, some distribution's package manager (FreeBSD)
- don't. For those, loop through a list of candidates to see if one is
- available.
-
- Return: a single-element list of the found scan-build binary ready to be
- passed to Popen()
- """
- exelist: T.List[str] = []
- if 'SCANBUILD' in os.environ:
- exelist = split_args(os.environ['SCANBUILD'])
-
- else:
- tools = get_llvm_tool_names('scan-build')
- for tool in tools:
- which = shutil.which(tool)
- if which is not None:
- exelist = [which]
- break
-
- if exelist:
- tool = exelist[0]
- if os.path.isfile(tool) and os.access(tool, os.X_OK):
- return [tool]
- return []
-
-def detect_clangformat() -> T.List[str]:
- """ Look for clang-format binary on build platform
-
- Do the same thing as detect_scanbuild to find clang-format except it
- currently does not check the environment variable.
-
- Return: a single-element list of the found clang-format binary ready to be
- passed to Popen()
- """
- tools = get_llvm_tool_names('clang-format')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-def detect_clangtidy() -> T.List[str]:
- """ Look for clang-tidy binary on build platform
-
- Return: a single-element list of the found clang-tidy binary ready to be
- passed to Popen()
- """
- tools = get_llvm_tool_names('clang-tidy')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-def detect_clangapply() -> T.List[str]:
- """ Look for clang-apply-replacements binary on build platform
-
- Return: a single-element list of the found clang-apply-replacements binary
- ready to be passed to Popen()
- """
- tools = get_llvm_tool_names('clang-apply-replacements')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-
class Environment:
private_dir = 'meson-private'
log_dir = 'meson-logs'