Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 60 additions & 35 deletions mesonbuild/dependencies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from __future__ import annotations
import copy
import dataclasses
import os
import collections
import itertools
Expand All @@ -21,7 +22,7 @@
#from ..interpreterbase import FeatureDeprecated, FeatureNew

if T.TYPE_CHECKING:
from typing_extensions import Literal, TypedDict, TypeAlias
from typing_extensions import Literal, Required, TypedDict, TypeAlias

from ..compilers.compilers import Compiler
from ..environment import Environment
Expand Down Expand Up @@ -51,7 +52,7 @@ class DependencyObjectKWs(TypedDict, total=False):
main: bool
method: DependencyMethods
modules: T.List[str]
native: MachineChoice
native: Required[MachineChoice]
optional_modules: T.List[str]
private_headers: bool
required: bool
Expand All @@ -73,6 +74,8 @@ class DependencyObjectKWs(TypedDict, total=False):
_MissingCompilerBase = object


DepType = T.TypeVar('DepType', bound='ExternalDependency', covariant=True)

class DependencyException(MesonException):
'''Exceptions raised while trying to find dependencies'''

Expand Down Expand Up @@ -129,15 +132,16 @@ class DependencyMethods(Enum):

class Dependency(HoldableObject):

def __init__(self, type_name: DependencyTypeName, kwargs: DependencyObjectKWs) -> None:
type_name: DependencyTypeName

def __init__(self, kwargs: DependencyObjectKWs) -> None:
# This allows two Dependencies to be compared even after being copied.
# The purpose is to allow the name to be changed, but still have a proper comparison
self._id = uuid.uuid4().int
self.name = f'dep{self._id}'
self.version: T.Optional[str] = None
self.language: T.Optional[str] = None # None means C-like
self.language: T.Optional[str] = kwargs.get('language') # None means C-like
self.is_found = False
self.type_name = type_name
self.compile_args: T.List[str] = []
self.link_args: T.List[str] = []
# Raw -L and -l arguments without manual library searching
Expand Down Expand Up @@ -298,6 +302,9 @@ def get_as_shared(self, recursive: bool) -> Dependency:
return self

class InternalDependency(Dependency):

type_name = DependencyTypeName('internal')

def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T.List[str],
link_args: T.List[str],
libraries: T.List[LibTypes],
Expand All @@ -308,7 +315,7 @@ def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T
d_module_versions: T.List[T.Union[str, int]], d_import_dirs: T.List['IncludeDirs'],
objects: T.List['ExtractedObjects'],
name: T.Optional[str] = None):
super().__init__(DependencyTypeName('internal'), {})
super().__init__({'native': MachineChoice.HOST}) # TODO: does the native key actually matter
self.version = version
self.is_found = True
self.include_directories = incdirs
Expand Down Expand Up @@ -412,12 +419,11 @@ def get_as_shared(self, recursive: bool) -> InternalDependency:
return new_dep

class ExternalDependency(Dependency):
def __init__(self, type_name: DependencyTypeName, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None):
Dependency.__init__(self, type_name, kwargs)
def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
Dependency.__init__(self, kwargs)
self.env = environment
self.name = type_name # default
self.name = name
self.is_found = False
self.language = language
self.version_reqs = kwargs.get('version', [])
self.required = kwargs.get('required', True)
self.silent = kwargs.get('silent', False)
Expand All @@ -427,7 +433,7 @@ def __init__(self, type_name: DependencyTypeName, environment: 'Environment', kw
self.static = static
self.libtype = LibType.STATIC if self.static else LibType.PREFER_SHARED
# Is this dependency to be run on the build platform?
self.for_machine = kwargs.get('native', MachineChoice.HOST)
self.for_machine = kwargs['native']
self.clib_compiler = detect_compiler(self.name, environment, self.for_machine, self.language)

def get_compiler(self) -> T.Union['MissingCompiler', 'Compiler']:
Expand Down Expand Up @@ -457,10 +463,6 @@ def log_details(self) -> str:
def log_info(self) -> str:
return ''

@staticmethod
def log_tried() -> str:
return ''

# Check if dependency version meets the requirements
def _check_version(self) -> None:
if not self.is_found:
Expand Down Expand Up @@ -499,8 +501,11 @@ def _check_version(self) -> None:


class NotFoundDependency(Dependency):

type_name = DependencyTypeName('not-found')

def __init__(self, name: str, environment: 'Environment') -> None:
super().__init__(DependencyTypeName('not-found'), {})
super().__init__({'native': MachineChoice.HOST}) # TODO: does this actually matter?
self.env = environment
self.name = name
self.is_found = False
Expand All @@ -514,11 +519,12 @@ def get_partial_dependency(self, *, compile_args: bool = False,


class ExternalLibrary(ExternalDependency):

type_name = DependencyTypeName('library')

def __init__(self, name: str, link_args: T.List[str], environment: 'Environment',
language: str, silent: bool = False) -> None:
super().__init__(DependencyTypeName('library'), environment, {}, language=language)
self.name = name
self.language = language
language: str, for_machine: MachineChoice, silent: bool = False) -> None:
super().__init__(name, environment, {'language': language, 'native': for_machine})
self.is_found = False
if link_args:
self.is_found = True
Expand Down Expand Up @@ -660,25 +666,44 @@ class SystemDependency(ExternalDependency):

"""Dependency base for System type dependencies."""

def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('system'), env, kwargs, language=language)
self.name = name

@staticmethod
def log_tried() -> str:
return 'system'
type_name = DependencyTypeName('system')


class BuiltinDependency(ExternalDependency):

"""Dependency base for Builtin type dependencies."""

def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('builtin'), env, kwargs, language=language)
self.name = name
type_name = DependencyTypeName('builtin')


@dataclasses.dataclass
class DependencyCandidate(T.Generic[DepType]):

callable: T.Union[T.Type[DepType], T.Callable[[str, Environment, DependencyObjectKWs], DepType]]
name: str
method: str
modules: T.Optional[T.List[str]] = None
arguments: T.Optional[T.Tuple[Environment, DependencyObjectKWs]] = dataclasses.field(default=None)

def __call__(self) -> DepType:
if self.arguments is None:
raise mesonlib.MesonBugException('Attempted to instantiate a candidate before setting its arguments')
env, kwargs = self.arguments
if self.modules is not None:
kwargs['modules'] = self.modules.copy()
return self.callable(self.name, env, kwargs)

@classmethod
def from_dependency(cls, name: str, dep: T.Type[DepType],
args: T.Optional[T.Tuple[Environment, DependencyObjectKWs]] = None,
modules: T.Optional[T.List[str]] = None,
) -> DependencyCandidate[DepType]:
tried = str(dep.type_name)

# fixup the cases where type_name and log tried don't match
if tried in {'extraframeworks', 'appleframeworks'}:
tried = 'framework'
elif tried == 'pkgconfig':
tried = 'pkg-config'

@staticmethod
def log_tried() -> str:
return 'builtin'
return cls(dep, name, tried, modules, arguments=args)
9 changes: 5 additions & 4 deletions mesonbuild/dependencies/boost.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,9 @@ def get_link_args(self) -> T.List[str]:
return [self.path.as_posix()]

class BoostDependency(SystemDependency):
def __init__(self, environment: Environment, kwargs: DependencyObjectKWs) -> None:
super().__init__('boost', environment, kwargs, language='cpp')
def __init__(self, name: str, environment: Environment, kwargs: DependencyObjectKWs) -> None:
kwargs['language'] = 'cpp'
super().__init__(name, environment, kwargs)
buildtype = environment.coredata.optstore.get_value_for(OptionKey('buildtype'))
assert isinstance(buildtype, str)
self.debug = buildtype.startswith('debug')
Expand All @@ -361,7 +362,7 @@ def __init__(self, environment: Environment, kwargs: DependencyObjectKWs) -> Non

# Do we need threads?
if 'thread' in self.modules:
if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
if not self._add_sub_dependency(threads_factory(environment, {'native': self.for_machine})):
self.is_found = False
return

Expand Down Expand Up @@ -676,7 +677,7 @@ def detect_roots(self) -> None:
# Try getting the BOOST_ROOT from a boost.pc if it exists. This primarily
# allows BoostDependency to find boost from Conan. See #5438
try:
boost_pc = PkgConfigDependency('boost', self.env, {'required': False})
boost_pc = PkgConfigDependency('boost', self.env, {'required': False, 'native': self.for_machine})
if boost_pc.found():
boost_lib_dir = boost_pc.get_variable(pkgconfig='libdir')
boost_inc_dir = boost_pc.get_variable(pkgconfig='includedir')
Expand Down
32 changes: 7 additions & 25 deletions mesonbuild/dependencies/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from __future__ import annotations

from .base import ExternalDependency, DependencyException, DependencyTypeName
from ..mesonlib import is_windows, MesonException, PerMachine, MachineChoice
from ..mesonlib import is_windows, MesonException, PerMachine
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args, resolve_cmake_trace_targets, cmake_is_debug
from .. import mlog
import importlib.resources
Expand Down Expand Up @@ -39,6 +39,8 @@ class CMakeDependency(ExternalDependency):
class_cmake_generators = ['', 'Ninja', 'Unix Makefiles', 'Visual Studio 10 2010']
class_working_generator: T.Optional[str] = None

type_name = DependencyTypeName('cmake')

def _gen_exception(self, msg: str) -> DependencyException:
return DependencyException(f'Dependency {self.name} not found: {msg}')

Expand Down Expand Up @@ -70,11 +72,12 @@ def _original_module_name(self, module: str) -> str:
# one module
return module

def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> None:
def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, force_use_global_compilers: bool = False) -> None:
# Gather a list of all languages to support
self.language_list: T.List[str] = []
language = kwargs.get('language')
if language is None or force_use_global_compilers:
for_machine = kwargs.get('native', MachineChoice.HOST)
for_machine = kwargs['native']
compilers = environment.coredata.compilers[for_machine]
candidates = ['c', 'cpp', 'fortran', 'objc', 'objcxx']
self.language_list += [x for x in candidates if x in compilers]
Expand All @@ -88,8 +91,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObje
# Ensure that the list is unique
self.language_list = list(set(self.language_list))

super().__init__(DependencyTypeName('cmake'), environment, kwargs, language=language)
self.name = name
super().__init__(name, environment, kwargs)
self.is_libtool = False

# Where all CMake "build dirs" are located
Expand Down Expand Up @@ -607,10 +609,6 @@ def _call_cmake(self,
build_dir = self._setup_cmake_dir(cmake_file)
return self.cmakebin.call(args, build_dir, env=env)

@staticmethod
def log_tried() -> str:
return 'cmake'

def log_details(self) -> str:
modules = [self._original_module_name(x) for x in self.found_modules]
modules = sorted(set(modules))
Expand Down Expand Up @@ -643,22 +641,6 @@ def get_variable(self, *, cmake: T.Optional[str] = None, pkgconfig: T.Optional[s
raise DependencyException(f'Could not get cmake variable and no default provided for {self!r}')


class CMakeDependencyFactory:

def __init__(self, name: T.Optional[str] = None, modules: T.Optional[T.List[str]] = None):
self.name = name
self.modules = modules

def __call__(self, name: str, env: Environment, kwargs: DependencyObjectKWs, language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> CMakeDependency:
if self.modules:
kwargs['modules'] = self.modules
return CMakeDependency(self.name or name, env, kwargs, language, force_use_global_compilers)

@staticmethod
def log_tried() -> str:
return CMakeDependency.log_tried()


def sort_link_args(args: T.List[str]) -> T.List[str]:
itr = iter(args)
result: T.Set[T.Union[T.Tuple[str], T.Tuple[str, str]]] = set()
Expand Down
31 changes: 17 additions & 14 deletions mesonbuild/dependencies/coarrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@

from __future__ import annotations

import functools
import typing as T

from .base import DependencyMethods, detect_compiler, SystemDependency
from .base import DependencyCandidate, DependencyMethods, detect_compiler, SystemDependency
from .cmake import CMakeDependency
from .detect import packages
from .pkgconfig import PkgConfigDependency
Expand All @@ -15,35 +14,40 @@
if T.TYPE_CHECKING:
from . factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
from .base import DependencyObjectKWs


@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE, DependencyMethods.SYSTEM})
def coarray_factory(env: 'Environment',
for_machine: 'MachineChoice',
kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
kwargs['language'] = 'fortran'
for_machine = kwargs['native']
fcid = detect_compiler('coarray', env, for_machine, 'fortran').get_id()
candidates: T.List['DependencyGenerator'] = []

if fcid == 'gcc':
# OpenCoarrays is the most commonly used method for Fortran Coarray with GCC
if DependencyMethods.PKGCONFIG in methods:
for pkg in ['caf-openmpi', 'caf']:
candidates.append(functools.partial(
PkgConfigDependency, pkg, env, kwargs, language='fortran'))
candidates.append(DependencyCandidate.from_dependency(
pkg, PkgConfigDependency, (env, kwargs)))

if DependencyMethods.CMAKE in methods:
nkwargs = kwargs
if not kwargs.get('modules'):
kwargs['modules'] = ['OpenCoarrays::caf_mpi']
candidates.append(functools.partial(
CMakeDependency, 'OpenCoarrays', env, kwargs, language='fortran'))
nkwargs = kwargs.copy()
nkwargs['modules'] = ['OpenCoarrays::caf_mpi']
candidates.append(DependencyCandidate.from_dependency(
'OpenCoarrays', CMakeDependency, (env, nkwargs)))

if DependencyMethods.SYSTEM in methods:
candidates.append(functools.partial(CoarrayDependency, env, kwargs))
candidates.append(DependencyCandidate.from_dependency(
'coarray', CoarrayDependency, (env, kwargs)))

return candidates


packages['coarray'] = coarray_factory


Expand All @@ -56,10 +60,9 @@ class CoarrayDependency(SystemDependency):
Coarrays may be thought of as a high-level language abstraction of
low-level MPI calls.
"""
def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__('coarray', environment, kwargs, language='fortran')
kwargs['required'] = False
kwargs['silent'] = True
def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
kwargs['language'] = 'fortran'
super().__init__(name, environment, kwargs)

cid = self.get_compiler().get_id()
if cid == 'gcc':
Expand Down
Loading
Loading