aboutsummaryrefslogtreecommitdiffstats
path: root/meson/mesonbuild/dependencies/framework.py
diff options
context:
space:
mode:
Diffstat (limited to 'meson/mesonbuild/dependencies/framework.py')
-rw-r--r--meson/mesonbuild/dependencies/framework.py123
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'