diff options
Diffstat (limited to 'meson/mesonbuild/dependencies/framework.py')
-rw-r--r-- | meson/mesonbuild/dependencies/framework.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/meson/mesonbuild/dependencies/framework.py b/meson/mesonbuild/dependencies/framework.py new file mode 100644 index 000000000..48223987e --- /dev/null +++ b/meson/mesonbuild/dependencies/framework.py @@ -0,0 +1,123 @@ +# 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 DependencyTypeName, ExternalDependency, DependencyException, DependencyMethods +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 += ['-I' + 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 = [] + 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 + + @staticmethod + def get_methods() -> T.List[DependencyMethods]: + return [DependencyMethods.EXTRAFRAMEWORK] + + def log_info(self) -> str: + return self.framework_path or '' + + def log_tried(self) -> str: + return 'framework' |