The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
8.3 KiB
210 lines
8.3 KiB
4 years ago
|
# Copyright 2013-2021 The Meson development team
|
||
|
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
from .base import Dependency, DependencyException, DependencyMethods, NotFoundDependency
|
||
|
from .cmake import CMakeDependency
|
||
|
from .dub import DubDependency
|
||
|
from .framework import ExtraFrameworkDependency
|
||
|
from .pkgconfig import PkgConfigDependency
|
||
|
|
||
|
from ..mesonlib import listify, MachineChoice, PerMachine
|
||
|
from .. import mlog
|
||
|
import functools
|
||
|
import typing as T
|
||
|
|
||
|
if T.TYPE_CHECKING:
|
||
|
from ..environment import Environment
|
||
|
from .factory import DependencyType
|
||
|
|
||
|
# These must be defined in this file to avoid cyclical references.
|
||
|
packages = {}
|
||
|
_packages_accept_language = set()
|
||
|
|
||
|
def get_dep_identifier(name, kwargs) -> T.Tuple:
|
||
|
identifier = (name, )
|
||
|
from ..interpreter import permitted_dependency_kwargs
|
||
|
assert len(permitted_dependency_kwargs) == 19, \
|
||
|
'Extra kwargs have been added to dependency(), please review if it makes sense to handle it here'
|
||
|
for key, value in kwargs.items():
|
||
|
# 'version' is irrelevant for caching; the caller must check version matches
|
||
|
# 'native' is handled above with `for_machine`
|
||
|
# 'required' is irrelevant for caching; the caller handles it separately
|
||
|
# 'fallback' and 'allow_fallback' is not part of the cache because,
|
||
|
# once a dependency has been found through a fallback, it should
|
||
|
# be used for the rest of the Meson run.
|
||
|
# 'default_options' is only used in fallback case
|
||
|
# 'not_found_message' has no impact on the dependency lookup
|
||
|
# 'include_type' is handled after the dependency lookup
|
||
|
if key in ('version', 'native', 'required', 'fallback', 'allow_fallback', 'default_options',
|
||
|
'not_found_message', 'include_type'):
|
||
|
continue
|
||
|
# All keyword arguments are strings, ints, or lists (or lists of lists)
|
||
|
if isinstance(value, list):
|
||
|
value = frozenset(listify(value))
|
||
|
identifier += (key, value)
|
||
|
return identifier
|
||
|
|
||
|
display_name_map = {
|
||
|
'boost': 'Boost',
|
||
|
'cuda': 'CUDA',
|
||
|
'dub': 'DUB',
|
||
|
'gmock': 'GMock',
|
||
|
'gtest': 'GTest',
|
||
|
'hdf5': 'HDF5',
|
||
|
'llvm': 'LLVM',
|
||
|
'mpi': 'MPI',
|
||
|
'netcdf': 'NetCDF',
|
||
|
'openmp': 'OpenMP',
|
||
|
'wxwidgets': 'WxWidgets',
|
||
|
}
|
||
|
|
||
|
def find_external_dependency(name, env, kwargs):
|
||
|
assert(name)
|
||
|
required = kwargs.get('required', True)
|
||
|
if not isinstance(required, bool):
|
||
|
raise DependencyException('Keyword "required" must be a boolean.')
|
||
|
if not isinstance(kwargs.get('method', ''), str):
|
||
|
raise DependencyException('Keyword "method" must be a string.')
|
||
|
lname = name.lower()
|
||
|
if lname not in _packages_accept_language and 'language' in kwargs:
|
||
|
raise DependencyException(f'{name} dependency does not accept "language" keyword argument')
|
||
|
if not isinstance(kwargs.get('version', ''), (str, list)):
|
||
|
raise DependencyException('Keyword "Version" must be string or list.')
|
||
|
|
||
|
# display the dependency name with correct casing
|
||
|
display_name = display_name_map.get(lname, lname)
|
||
|
|
||
|
for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST
|
||
|
|
||
|
type_text = PerMachine('Build-time', 'Run-time')[for_machine] + ' dependency'
|
||
|
|
||
|
# build a list of dependency methods to try
|
||
|
candidates = _build_external_dependency_list(name, env, for_machine, kwargs)
|
||
|
|
||
|
pkg_exc = []
|
||
|
pkgdep = []
|
||
|
details = ''
|
||
|
|
||
|
for c in candidates:
|
||
|
# try this dependency method
|
||
|
try:
|
||
|
d = c()
|
||
|
d._check_version()
|
||
|
pkgdep.append(d)
|
||
|
except DependencyException as e:
|
||
|
pkg_exc.append(e)
|
||
|
mlog.debug(str(e))
|
||
|
else:
|
||
|
pkg_exc.append(None)
|
||
|
details = d.log_details()
|
||
|
if details:
|
||
|
details = '(' + details + ') '
|
||
|
if 'language' in kwargs:
|
||
|
details += 'for ' + d.language + ' '
|
||
|
|
||
|
# if the dependency was found
|
||
|
if d.found():
|
||
|
|
||
|
info = []
|
||
|
if d.version:
|
||
|
info.append(mlog.normal_cyan(d.version))
|
||
|
|
||
|
log_info = d.log_info()
|
||
|
if log_info:
|
||
|
info.append('(' + log_info + ')')
|
||
|
|
||
|
mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), *info)
|
||
|
|
||
|
return d
|
||
|
|
||
|
# otherwise, the dependency could not be found
|
||
|
tried_methods = [d.log_tried() for d in pkgdep if d.log_tried()]
|
||
|
if tried_methods:
|
||
|
tried = '{}'.format(mlog.format_list(tried_methods))
|
||
|
else:
|
||
|
tried = ''
|
||
|
|
||
|
mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.red('NO'),
|
||
|
f'(tried {tried})' if tried else '')
|
||
|
|
||
|
if required:
|
||
|
# if an exception occurred with the first detection method, re-raise it
|
||
|
# (on the grounds that it came from the preferred dependency detection
|
||
|
# method)
|
||
|
if pkg_exc and pkg_exc[0]:
|
||
|
raise pkg_exc[0]
|
||
|
|
||
|
# we have a list of failed ExternalDependency objects, so we can report
|
||
|
# the methods we tried to find the dependency
|
||
|
raise DependencyException('Dependency "%s" not found' % (name) +
|
||
|
(', tried %s' % (tried) if tried else ''))
|
||
|
|
||
|
return NotFoundDependency(env)
|
||
|
|
||
|
|
||
|
def _build_external_dependency_list(name: str, env: 'Environment', for_machine: MachineChoice,
|
||
|
kwargs: T.Dict[str, T.Any]) -> T.List['DependencyType']:
|
||
|
# First check if the method is valid
|
||
|
if 'method' in kwargs and kwargs['method'] not in [e.value for e in DependencyMethods]:
|
||
|
raise DependencyException('method {!r} is invalid'.format(kwargs['method']))
|
||
|
|
||
|
# Is there a specific dependency detector for this dependency?
|
||
|
lname = name.lower()
|
||
|
if lname in packages:
|
||
|
# Create the list of dependency object constructors using a factory
|
||
|
# class method, if one exists, otherwise the list just consists of the
|
||
|
# constructor
|
||
|
if isinstance(packages[lname], type) and issubclass(packages[lname], Dependency):
|
||
|
dep = [functools.partial(packages[lname], env, kwargs)]
|
||
|
else:
|
||
|
dep = packages[lname](env, for_machine, kwargs)
|
||
|
return dep
|
||
|
|
||
|
candidates = []
|
||
|
|
||
|
# If it's explicitly requested, use the dub detection method (only)
|
||
|
if 'dub' == kwargs.get('method', ''):
|
||
|
candidates.append(functools.partial(DubDependency, name, env, kwargs))
|
||
|
return candidates
|
||
|
|
||
|
# If it's explicitly requested, use the pkgconfig detection method (only)
|
||
|
if 'pkg-config' == kwargs.get('method', ''):
|
||
|
candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs))
|
||
|
return candidates
|
||
|
|
||
|
# If it's explicitly requested, use the CMake detection method (only)
|
||
|
if 'cmake' == kwargs.get('method', ''):
|
||
|
candidates.append(functools.partial(CMakeDependency, name, env, kwargs))
|
||
|
return candidates
|
||
|
|
||
|
# If it's explicitly requested, use the Extraframework detection method (only)
|
||
|
if 'extraframework' == kwargs.get('method', ''):
|
||
|
# On OSX, also try framework dependency detector
|
||
|
if env.machines[for_machine].is_darwin():
|
||
|
candidates.append(functools.partial(ExtraFrameworkDependency, name, env, kwargs))
|
||
|
return candidates
|
||
|
|
||
|
# Otherwise, just use the pkgconfig and cmake dependency detector
|
||
|
if 'auto' == kwargs.get('method', 'auto'):
|
||
|
candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs))
|
||
|
|
||
|
# On OSX, also try framework dependency detector
|
||
|
if env.machines[for_machine].is_darwin():
|
||
|
candidates.append(functools.partial(ExtraFrameworkDependency, name, env, kwargs))
|
||
|
|
||
|
# Only use CMake as a last resort, since it might not work 100% (see #6113)
|
||
|
candidates.append(functools.partial(CMakeDependency, name, env, kwargs))
|
||
|
|
||
|
return candidates
|