Skip to content

Commit

Permalink
Add find_framework() method to compiler object
Browse files Browse the repository at this point in the history
Signed-off-by: Julia DeMille <me@jdemille.com>
  • Loading branch information
judemille committed Feb 4, 2024
1 parent 6fcf863 commit e82a823
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 4 deletions.
7 changes: 7 additions & 0 deletions docs/markdown/snippets/compiler-find-framework.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Compiler object now has `find_framework()` method

Returns a dependency equivalent to one from `dependency(..., method: 'extraframeworks')`.
Allows the user to specify search paths for the framework.

**NOTE:** Only use this function over other methods to find frameworks if it is necessary
to specify the paths in which the framework may be found.
42 changes: 42 additions & 0 deletions docs/yaml/objects/compiler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,48 @@ methods:
When the `has_headers` kwarg is also used, this argument is passed to
[[compiler.has_header]] as `prefix`.
- name: find_framework
returns: dep
description: Tries to find the framework specified in the positional argument.

posargs:
libname:
type: str
description: The framework to find.
kwargs:
required:
type: bool | feature
default: true
description: |
If set `true`, Meson will abort with an error if the framework could not
be found. Otherwise, Meson will continue and the found method of the
returned object will return `false`.
When set to a [`feature`](Build-options.md#features) option, the feature
will control if it is searched and whether to fail if not found.
The value of a `feature` option can also be passed here.
disabler:
type: bool
default: false
description: If `true`, this method will return a [[@disabler]] on a failed check.
dirs:
type: list[str]
description: |
Additional directories to search in.
By default the framework is searched for in the system framework directories
(e.g. /Library/Frameworks and /System/Library/Frameworks). Specifying more
directories here, causes Meson to search in those directories as well as the
system directories.
extra_header_dirs:
type: list[str]
description: |
Additional directories in which headers for this framework live.
This is useful for frameworks that package their headers separately from the
framework itself.
# Compiler arguments
- name: has_argument
returns: bool
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/dependencies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
ExternalLibrary, DependencyException, DependencyMethods,
BuiltinDependency, SystemDependency, get_leaf_external_dependencies)
from .detect import find_external_dependency, get_dep_identifier, packages, _packages_accept_language
from .framework import ExtraFrameworkDependency

__all__ = [
'Dependency',
'InternalDependency',
'ExternalDependency',
'ExtraFrameworkDependency',
'SystemDependency',
'BuiltinDependency',
'NotFoundDependency',
Expand Down
9 changes: 7 additions & 2 deletions mesonbuild/dependencies/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

class ExtraFrameworkDependency(ExternalDependency):
system_framework_paths: T.Optional[T.List[str]] = None
extra_header_dirs: T.List[str] = None

def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, skip_finding: bool = False) -> None:
paths = stringlistify(kwargs.get('paths', []))
self.extra_header_dirs = stringlistify(kwargs.get('extra_header_dirs', []))
super().__init__(DependencyTypeName('extraframeworks'), env, kwargs, language=language)
self.name = name
# Full path to framework directory
Expand All @@ -33,7 +35,8 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], la
self.is_found = False
return
raise
self.detect(name, paths)
if not skip_finding:
self.detect(name, paths)

def detect(self, name: str, paths: T.List[str]) -> None:
if not paths:
Expand Down Expand Up @@ -67,6 +70,8 @@ def detect(self, name: str, paths: T.List[str]) -> None:
incdir = self._get_framework_include_path(framework_path)
if incdir:
self.compile_args += ['-idirafter' + incdir]
for extra_incdir in self.extra_header_dirs:
self.compile_args += ['-idirafter' + extra_incdir]
self.is_found = True
return

Expand Down
40 changes: 40 additions & 0 deletions mesonbuild/interpreter/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class FindLibraryKW(ExtractRequired, ExtractSearchDirs):
header_prefix: str
header_required: T.Union[bool, coredata.UserFeatureOption]

class FindFrameworkKW(ExtractRequired, ExtractSearchDirs):
disabler: bool
extra_header_dirs: T.List[str]

class PreprocessKW(TypedDict):
output: str
compile_args: T.List[str]
Expand Down Expand Up @@ -209,6 +213,7 @@ def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'):
'version': self.version_method,
'cmd_array': self.cmd_array_method,
'find_library': self.find_library_method,
'find_framework': self.find_framework_method,
'has_argument': self.has_argument_method,
'has_function_attribute': self.has_func_attribute_method,
'get_supported_function_attributes': self.get_supported_function_attributes_method,
Expand Down Expand Up @@ -705,6 +710,41 @@ def find_library_method(self, args: T.Tuple[str], kwargs: 'FindLibraryKW') -> 'd
self.compiler.language)
return lib

def _notfound_framework(self, fwname: str) -> 'dependencies.framework.ExtraFrameworkDependency':
return dependencies.framework.ExtraFrameworkDependency(fwname, self.environment, {}, language=self.compiler.language, skip_finding=True)

@disablerIfNotFound
@typed_pos_args('compiler.find_framework', str)
@typed_kwargs(
'compiler.find_framework',
KwargInfo('required', (bool, coredata.UserFeatureOption), default=True),
KwargInfo('disabler', bool, default=False),
KwargInfo('dirs', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('extra_header_dirs', ContainerTypeInfo(list, str), listify=True, default=[])
)
def find_framework_method(self, args: T.Tuple[str], kwargs: 'FindFrameworkKW') -> 'dependencies.framework.ExtraFrameworkDependency':
FeatureNew.single_use('compiler.find_framework()', '1.4.0', self.subproject, location=self.current_node)
fwname = args[0]

disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Framework', mlog.bold(fwname), 'skipped: feature', mlog.bold(feature), 'disabled')
return self._notfound_framework(fwname)

search_dirs = extract_search_dirs(kwargs)

fw = dependencies.framework.ExtraFrameworkDependency(
fwname,
self.environment,
{'paths': search_dirs,
'extra_header_dirs': kwargs.get('extra_header_dirs')},
language=self.compiler.language
)

if required and not fw.is_found:
raise InterpreterException('framework {!r} not found'.format(fwname))
return fw

def _has_argument_impl(self, arguments: T.Union[str, T.List[str]],
mode: _TestMode = _TestMode.COMPILER,
kwargs: T.Optional['ExtractRequired'] = None) -> bool:
Expand Down
3 changes: 3 additions & 0 deletions test cases/osx/10 find framework/main.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main(void) {
return 0;
}
10 changes: 10 additions & 0 deletions test cases/osx/10 find framework/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
project('find_framework test', 'objc')

objcc = meson.get_compiler('objc')

cf_dep = objcc.find_framework('CoreFoundation', dirs: ['/System/Library/Frameworks'])

executable('find_framework_tester',
'main.m',
dependencies: [cf_dep],
)
3 changes: 2 additions & 1 deletion test cases/unit/116 empty project/expected_mods.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
"mesonbuild.dependencies",
"mesonbuild.dependencies.base",
"mesonbuild.dependencies.detect",
"mesonbuild.dependencies.framework",
"mesonbuild.depfile",
"mesonbuild.envconfig",
"mesonbuild.environment",
Expand Down Expand Up @@ -237,6 +238,6 @@
"mesonbuild.wrap",
"mesonbuild.wrap.wrap"
],
"count": 68
"count": 69
}
}
2 changes: 1 addition & 1 deletion unittests/platformagnostictests.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def test_setup_loaded_modules(self):
expected = json.load(f)['meson']['modules']

self.assertEqual(data['modules'], expected)
self.assertEqual(data['count'], 68)
self.assertEqual(data['count'], 69)

def test_meson_package_cache_dir(self):
# Copy testdir into temporary directory to not pollute meson source tree.
Expand Down

0 comments on commit e82a823

Please sign in to comment.