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.
111 lines
5.0 KiB
111 lines
5.0 KiB
# SPDX-License-Identifier: Apache-2.0 |
|
# Copyright 2013-2021 The Meson development team |
|
|
|
from __future__ import annotations |
|
|
|
from .base import DependencyTypeName, ExternalDependency, DependencyException |
|
from ..mesonlib import MesonException, Version, stringlistify |
|
from .. import mlog |
|
from pathlib import Path |
|
import typing as T |
|
|
|
if T.TYPE_CHECKING: |
|
from ..environment import Environment |
|
|
|
class ExtraFrameworkDependency(ExternalDependency): |
|
system_framework_paths: T.Optional[T.List[str]] = None |
|
|
|
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None: |
|
paths = stringlistify(kwargs.get('paths', [])) |
|
super().__init__(DependencyTypeName('extraframeworks'), env, kwargs, language=language) |
|
self.name = name |
|
# Full path to framework directory |
|
self.framework_path: T.Optional[str] = None |
|
if not self.clib_compiler: |
|
raise DependencyException('No C-like compilers are available') |
|
if self.system_framework_paths is None: |
|
try: |
|
self.system_framework_paths = self.clib_compiler.find_framework_paths(self.env) |
|
except MesonException as e: |
|
if 'non-clang' in str(e): |
|
# Apple frameworks can only be found (and used) with the |
|
# system compiler. It is not available so bail immediately. |
|
self.is_found = False |
|
return |
|
raise |
|
self.detect(name, paths) |
|
|
|
def detect(self, name: str, paths: T.List[str]) -> None: |
|
if not paths: |
|
paths = self.system_framework_paths |
|
for p in paths: |
|
mlog.debug(f'Looking for framework {name} in {p}') |
|
# We need to know the exact framework path because it's used by the |
|
# Qt5 dependency class, and for setting the include path. We also |
|
# want to avoid searching in an invalid framework path which wastes |
|
# time and can cause a false positive. |
|
framework_path = self._get_framework_path(p, name) |
|
if framework_path is None: |
|
continue |
|
# We want to prefer the specified paths (in order) over the system |
|
# paths since these are "extra" frameworks. |
|
# For example, Python2's framework is in /System/Library/Frameworks and |
|
# Python3's framework is in /Library/Frameworks, but both are called |
|
# Python.framework. We need to know for sure that the framework was |
|
# found in the path we expect. |
|
allow_system = p in self.system_framework_paths |
|
args = self.clib_compiler.find_framework(name, self.env, [p], allow_system) |
|
if args is None: |
|
continue |
|
self.link_args = args |
|
self.framework_path = framework_path.as_posix() |
|
self.compile_args = ['-F' + self.framework_path] |
|
# We need to also add -I includes to the framework because all |
|
# cross-platform projects such as OpenGL, Python, Qt, GStreamer, |
|
# etc do not use "framework includes": |
|
# https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Tasks/IncludingFrameworks.html |
|
incdir = self._get_framework_include_path(framework_path) |
|
if incdir: |
|
self.compile_args += ['-idirafter' + incdir] |
|
self.is_found = True |
|
return |
|
|
|
def _get_framework_path(self, path: str, name: str) -> T.Optional[Path]: |
|
p = Path(path) |
|
lname = name.lower() |
|
for d in p.glob('*.framework/'): |
|
if lname == d.name.rsplit('.', 1)[0].lower(): |
|
return d |
|
return None |
|
|
|
def _get_framework_latest_version(self, path: Path) -> str: |
|
versions: T.List[Version] = [] |
|
for each in path.glob('Versions/*'): |
|
# macOS filesystems are usually case-insensitive |
|
if each.name.lower() == 'current': |
|
continue |
|
versions.append(Version(each.name)) |
|
if len(versions) == 0: |
|
# most system frameworks do not have a 'Versions' directory |
|
return 'Headers' |
|
return 'Versions/{}/Headers'.format(sorted(versions)[-1]._s) |
|
|
|
def _get_framework_include_path(self, path: Path) -> T.Optional[str]: |
|
# According to the spec, 'Headers' must always be a symlink to the |
|
# Headers directory inside the currently-selected version of the |
|
# framework, but sometimes frameworks are broken. Look in 'Versions' |
|
# for the currently-selected version or pick the latest one. |
|
trials = ('Headers', 'Versions/Current/Headers', |
|
self._get_framework_latest_version(path)) |
|
for each in trials: |
|
trial = path / each |
|
if trial.is_dir(): |
|
return trial.as_posix() |
|
return None |
|
|
|
def log_info(self) -> str: |
|
return self.framework_path or '' |
|
|
|
@staticmethod |
|
def log_tried() -> str: |
|
return 'framework'
|
|
|