mirror of
https://github.com/mesonbuild/meson.git
synced 2026-11-06 05:16:42 +00:00
environment: move tool detection functions to a new module
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
1 parent
1614401329
commit
795e7431ff
17 files changed
+292
-272
No files matched your search
@@ -20,10 +20,11 @@ import typing as T
|
||||
|
||||
from . import backends
|
||||
from .. import modules
|
||||
from .. import environment, mesonlib
|
||||
from .. import mesonlib
|
||||
from .. import build
|
||||
from .. import mlog
|
||||
from .. import compilers
|
||||
from .. import tooldetect
|
||||
from ..arglist import CompilerArgs
|
||||
from ..compilers import Compiler, is_library
|
||||
from ..linkers import ArLikeLinker, RSPFileSyntax
|
||||
@@ -595,7 +596,7 @@ class NinjaBackend(backends.Backend):
|
||||
mlog.debug('cuda enabled globally, disabling thin archives for {}, since nvcc/nvlink cannot handle thin archives natively'.format(for_machine))
|
||||
self.allow_thin_archives[for_machine] = False
|
||||
|
||||
ninja = environment.detect_ninja_command_and_version(log=True)
|
||||
ninja = tooldetect.detect_ninja_command_and_version(log=True)
|
||||
if self.environment.coredata.optstore.get_value_for(OptionKey('vsenv')):
|
||||
builddir = Path(self.environment.get_build_dir())
|
||||
try:
|
||||
@@ -656,7 +657,7 @@ class NinjaBackend(backends.Backend):
|
||||
key = OptionKey('b_coverage')
|
||||
if key in self.environment.coredata.optstore and\
|
||||
self.environment.coredata.optstore.get_value_for('b_coverage'):
|
||||
gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe = environment.find_coverage_tools(self.environment.coredata)
|
||||
gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe = tooldetect.find_coverage_tools(self.environment.coredata)
|
||||
mlog.debug(f'Using {gcovr_exe} ({gcovr_version}), {lcov_exe} and {llvm_cov_exe} for code coverage')
|
||||
if gcovr_exe or (lcov_exe and genhtml_exe):
|
||||
self.add_build_comment(NinjaComment('Coverage rules'))
|
||||
@@ -3875,7 +3876,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
|
||||
self.add_build(elem)
|
||||
|
||||
def generate_scanbuild(self) -> None:
|
||||
if not environment.detect_scanbuild():
|
||||
if not tooldetect.detect_scanbuild():
|
||||
return
|
||||
if 'scan-build' in self.all_outputs:
|
||||
return
|
||||
@@ -3916,16 +3917,16 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
|
||||
self.add_build(elem)
|
||||
|
||||
def generate_clangformat(self) -> None:
|
||||
if not environment.detect_clangformat():
|
||||
if not tooldetect.detect_clangformat():
|
||||
return
|
||||
self.generate_clangtool('format')
|
||||
self.generate_clangtool('format', 'check')
|
||||
|
||||
def generate_clangtidy(self) -> None:
|
||||
if not environment.detect_clangtidy():
|
||||
if not tooldetect.detect_clangtidy():
|
||||
return
|
||||
self.generate_clangtool('tidy', need_pch=True)
|
||||
if not environment.detect_clangapply():
|
||||
if not tooldetect.detect_clangapply():
|
||||
return
|
||||
self.generate_clangtool('tidy', 'fix', need_pch=True)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import functools
|
||||
from mesonbuild.interpreterbase.decorators import FeatureDeprecated
|
||||
|
||||
from .. import mesonlib, mlog
|
||||
from ..environment import get_llvm_tool_names
|
||||
from ..tooldetect import get_llvm_tool_names
|
||||
from ..mesonlib import version_compare, version_compare_many, search_version
|
||||
from .base import DependencyException, DependencyMethods, detect_compiler, strip_system_includedirs, strip_system_libdirs, SystemDependency, ExternalDependency, DependencyTypeName
|
||||
from .cmake import CMakeDependency
|
||||
|
||||
+3
-236
@@ -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'
|
||||
|
||||
@@ -18,7 +18,7 @@ from . import mlog
|
||||
from . import mesonlib
|
||||
from .options import OptionKey
|
||||
from .mesonlib import MesonException, RealPathAction, join_args, listify_array_value, setup_vsenv
|
||||
from mesonbuild.environment import detect_ninja
|
||||
from mesonbuild.tooldetect import detect_ninja
|
||||
from mesonbuild import build
|
||||
|
||||
if T.TYPE_CHECKING:
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@ import typing as T
|
||||
from dataclasses import dataclass
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from mesonbuild.environment import Environment, detect_ninja
|
||||
from mesonbuild.environment import Environment
|
||||
from mesonbuild.tooldetect import detect_ninja
|
||||
from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, get_meson_command, quiet_git,
|
||||
windows_proof_rmtree, setup_vsenv)
|
||||
from .options import OptionKey
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ import typing as T
|
||||
|
||||
from mesonbuild import build, mesonlib, mlog
|
||||
from mesonbuild.coredata import FORBIDDEN_TARGET_NAMES
|
||||
from mesonbuild.environment import detect_ninja
|
||||
from mesonbuild.tooldetect import detect_ninja
|
||||
from mesonbuild.templates.mesontemplates import create_meson_build
|
||||
from mesonbuild.templates.samplefactory import sample_generator
|
||||
from mesonbuild.options import OptionKey
|
||||
|
||||
@@ -15,7 +15,7 @@ import sys
|
||||
import typing as T
|
||||
import re
|
||||
|
||||
from . import build, environment
|
||||
from . import build, tooldetect
|
||||
from .backend.backends import InstallData
|
||||
from .mesonlib import (MesonException, Popen_safe, RealPathAction, is_windows,
|
||||
is_aix, setup_vsenv, pickle_load, is_osx)
|
||||
@@ -803,7 +803,7 @@ def rebuild_all(wd: str, backend: str) -> bool:
|
||||
print('Only ninja backend is supported to rebuild the project before installation.')
|
||||
return True
|
||||
|
||||
ninja = environment.detect_ninja()
|
||||
ninja = tooldetect.detect_ninja()
|
||||
if not ninja:
|
||||
print("Can't find ninja, can't rebuild test.")
|
||||
return False
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ import unicodedata
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
from . import build
|
||||
from . import environment
|
||||
from . import tooldetect
|
||||
from . import mlog
|
||||
from .coredata import MesonVersionMismatchException, major_versions_differ
|
||||
from .coredata import version as coredata_version
|
||||
@@ -1702,7 +1702,7 @@ class TestHarness:
|
||||
if self.options.no_rebuild:
|
||||
return
|
||||
|
||||
self.ninja = environment.detect_ninja()
|
||||
self.ninja = tooldetect.detect_ninja()
|
||||
if not self.ninja:
|
||||
print("Can't find ninja, can't rebuild test.")
|
||||
# If ninja can't be found return exit code 127, indicating command
|
||||
|
||||
@@ -8,7 +8,7 @@ from pathlib import Path
|
||||
import sys
|
||||
|
||||
from .run_tool import run_clang_tool, run_with_buffered_output
|
||||
from ..environment import detect_clangformat
|
||||
from ..tooldetect import detect_clangformat
|
||||
from ..mesonlib import version_compare
|
||||
from ..programs import ExternalProgram
|
||||
import typing as T
|
||||
|
||||
@@ -12,7 +12,7 @@ import shutil
|
||||
import sys
|
||||
|
||||
from .run_tool import run_with_buffered_output, run_clang_tool_on_sources
|
||||
from ..environment import detect_clangtidy, detect_clangapply
|
||||
from ..tooldetect import detect_clangtidy, detect_clangapply
|
||||
import typing as T
|
||||
|
||||
async def run_clang_tidy(fname: Path, tidyexe: list, builddir: Path, fixesdir: T.Optional[Path]) -> int:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from mesonbuild import environment, mesonlib
|
||||
from mesonbuild import tooldetect, mesonlib
|
||||
|
||||
import argparse, re, sys, os, subprocess, pathlib, stat, shutil
|
||||
import typing as T
|
||||
@@ -16,11 +16,11 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build
|
||||
if gcovr_exe == '':
|
||||
gcovr_exe = None
|
||||
else:
|
||||
gcovr_exe, gcovr_version = environment.detect_gcovr(gcovr_exe)
|
||||
gcovr_exe, gcovr_version = tooldetect.detect_gcovr(gcovr_exe)
|
||||
if llvm_cov_exe == '' or shutil.which(llvm_cov_exe) is None:
|
||||
llvm_cov_exe = None
|
||||
|
||||
lcov_exe, lcov_version, genhtml_exe = environment.detect_lcov_genhtml()
|
||||
lcov_exe, lcov_version, genhtml_exe = tooldetect.detect_lcov_genhtml()
|
||||
|
||||
# load config files for tools if available in the source tree
|
||||
# - lcov requires manually specifying a per-project config
|
||||
|
||||
@@ -7,7 +7,7 @@ import subprocess
|
||||
import shutil
|
||||
import tempfile
|
||||
from ..cmdline import get_cmd_line_file, CmdLineFileParser
|
||||
from ..environment import detect_ninja, detect_scanbuild
|
||||
from ..tooldetect import detect_ninja, detect_scanbuild
|
||||
from ..mesonlib import windows_proof_rmtree, determine_worker_count
|
||||
from pathlib import Path
|
||||
import typing as T
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright 2012-2020 The Meson development team
|
||||
# Copyright © 2023-2025 Intel Corporation
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import typing as T
|
||||
|
||||
from . import coredata
|
||||
from . import mesonlib
|
||||
from . import mlog
|
||||
from .mesonlib import MachineChoice, Popen_safe, search_version, quote_arg, split_args
|
||||
from .programs import ExternalProgram
|
||||
|
||||
|
||||
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 []
|
||||
@@ -82,6 +82,7 @@ modules = [
|
||||
'mesonbuild/options.py',
|
||||
'mesonbuild/programs.py',
|
||||
'mesonbuild/rewriter.py',
|
||||
'mesonbuild/tooldetect.py',
|
||||
]
|
||||
additional = [
|
||||
'run_mypy.py',
|
||||
|
||||
+2
-1
@@ -32,8 +32,9 @@ from mesonbuild import mesonlib
|
||||
from mesonbuild import mesonmain
|
||||
from mesonbuild import mtest
|
||||
from mesonbuild import mlog
|
||||
from mesonbuild.environment import Environment, detect_ninja
|
||||
from mesonbuild.environment import Environment
|
||||
from mesonbuild.envconfig import detect_machine_info
|
||||
from mesonbuild.tooldetect import detect_ninja
|
||||
from mesonbuild.coredata import version as meson_version
|
||||
from mesonbuild.options import backendlist
|
||||
from mesonbuild.mesonlib import setup_vsenv
|
||||
|
||||
@@ -231,6 +231,7 @@
|
||||
"mesonbuild.programs",
|
||||
"mesonbuild.scripts",
|
||||
"mesonbuild.scripts.meson_exe",
|
||||
"mesonbuild.tooldetect",
|
||||
"mesonbuild.utils",
|
||||
"mesonbuild.utils.core",
|
||||
"mesonbuild.utils.platform",
|
||||
@@ -239,6 +240,6 @@
|
||||
"mesonbuild.wrap",
|
||||
"mesonbuild.wrap.wrap"
|
||||
],
|
||||
"count": 70
|
||||
"count": 71
|
||||
}
|
||||
}
|
||||
@@ -2497,7 +2497,7 @@ class AllPlatformTests(BasePlatformTests):
|
||||
self.assertIn('ERROR: compiler.has_header_symbol got unknown keyword arguments "prefixxx"', cm.exception.output)
|
||||
|
||||
def test_templates(self):
|
||||
ninja = mesonbuild.environment.detect_ninja()
|
||||
ninja = mesonbuild.tooldetect.detect_ninja()
|
||||
if ninja is None:
|
||||
raise SkipTest('This test currently requires ninja. Fix this once "meson build" works.')
|
||||
|
||||
@@ -4290,14 +4290,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '1 trivial')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
@@ -4310,14 +4310,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage_complex(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '105 generatorcustom')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
@@ -4330,14 +4330,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage_html(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '1 trivial')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
@@ -4350,14 +4350,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage_text(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '1 trivial')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
@@ -4370,14 +4370,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage_xml(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '1 trivial')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
@@ -4390,14 +4390,14 @@ class AllPlatformTests(BasePlatformTests):
|
||||
def test_coverage_escaping(self):
|
||||
if mesonbuild.envconfig.detect_msys2_arch():
|
||||
raise SkipTest('Skipped due to problems with coverage on MSYS2')
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
|
||||
gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
|
||||
if not gcovr_exe:
|
||||
raise SkipTest('gcovr not found, or too old')
|
||||
testdir = os.path.join(self.common_test_dir, '243 escape++')
|
||||
env = get_fake_env(testdir, self.builddir, self.prefix)
|
||||
cc = detect_c_compiler(env, MachineChoice.HOST)
|
||||
if cc.get_id() == 'clang':
|
||||
if not mesonbuild.environment.detect_llvm_cov():
|
||||
if not mesonbuild.tooldetect.detect_llvm_cov():
|
||||
raise SkipTest('llvm-cov not found')
|
||||
if cc.get_id() == 'msvc':
|
||||
raise SkipTest('Test only applies to non-MSVC compilers')
|
||||
|
||||
Reference in new issue
Block a user