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.
121 lines
5.2 KiB
121 lines
5.2 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 ExternalDependency, DependencyException, DependencyMethods
|
||
|
from ..mesonlib import MesonException, Version
|
||
|
from .. import mlog
|
||
|
from pathlib import Path
|
||
|
import typing as T
|
||
|
|
||
|
class ExtraFrameworkDependency(ExternalDependency):
|
||
|
system_framework_paths = None
|
||
|
|
||
|
def __init__(self, name, env, kwargs, language: T.Optional[str] = None):
|
||
|
paths = kwargs.get('paths', [])
|
||
|
super().__init__('extraframeworks', env, kwargs, language=language)
|
||
|
self.name = name
|
||
|
# Full path to framework directory
|
||
|
self.framework_path = 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, paths):
|
||
|
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, name):
|
||
|
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):
|
||
|
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):
|
||
|
# 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():
|
||
|
return [DependencyMethods.EXTRAFRAMEWORK]
|
||
|
|
||
|
def log_info(self):
|
||
|
return self.framework_path
|
||
|
|
||
|
def log_tried(self):
|
||
|
return 'framework'
|