interpreter: Fix dependency(..., static: true) fallback

It should build the fallback subprject with default_library=static and
override the dependency for both static=True and static kwarg not given.

Fixes: #8050.
pull/9159/head
Xavier Claessens 3 years ago committed by Xavier Claessens
parent 0063eb251e
commit e646130ef1
  1. 5
      docs/markdown/Reference-manual.md
  2. 46
      docs/markdown/snippets/static_fallback_override.md
  3. 11
      mesonbuild/interpreter/dependencyfallbacks.py
  4. 58
      mesonbuild/interpreter/mesonmain.py
  5. 27
      test cases/common/98 subproject subdir/meson.build
  6. 8
      test cases/common/98 subproject subdir/subprojects/sub_static/meson.build
  7. 5
      unittests/allplatformstests.py

@ -568,6 +568,8 @@ This function supports the following keyword arguments:
- `static`: tells the dependency provider to try to get static
libraries instead of dynamic ones (note that this is not supported
by all dependency backends)
*Since 0.60.0* it also sets `default_library` option accordingly on the fallback
subproject if it was not set explicitly in `default_options` keyword argument.
- `version` *(since 0.37.0)*: specifies the required version, a string containing a
comparison operator followed by the version string, examples include
`>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching.
@ -2151,6 +2153,9 @@ the following methods.
`native` keyword arguments. Doing this in a subproject allows the parent
project to retrieve the dependency without having to know the dependency
variable name: `dependency(name, fallback : subproject_name)`.
*Since 0.60.0* `static` boolean keyword argument can be specified to override
static and/or shared dependencies separately. If not specified it is assumed
`dep_object` follows `default_library` option value.
- `project_version()`: returns the version string specified in
`project` function call.

@ -0,0 +1,46 @@
## `static` keyword argument to `meson.override_dependency()`
It is now possible to override shared and/or static dependencies separately.
When the `static` keyword argument is not specified in `dependency()`, the first
override will be used (`static_dep` in the example below).
```meson
static_lib = static_library()
static_dep = declare_dependency(link_with: static_lib)
meson.override_dependency('foo', static_dep, static: true)
shared_lib = shared_library()
shared_dep = declare_dependency(link_with: shared_lib)
meson.override_dependency('foo', shared_dep, static: false)
# Returns static_dep
dependency('foo')
# Returns static_dep
dependency('foo', static: true)
# Returns shared_dep
dependency('foo', static: false)
```
When the `static` keyword argument is not specified in `meson.override_dependency()`,
the dependency is assumed to follow the value of `default_library` option.
```meson
dep = declare_dependency(...)
meson.override_dependency('foo', dep)
# Always works
dependency('foo')
# Works only if default_library is 'static' or 'both'
dependency('foo', static: true)
# Works only if default_library is 'shared' or 'both'
dependency('foo', static: false)
```
## `dependency()` sets `default_library` on fallback subproject
When the `static` keyword argument is set but `default_library` is missing in
`default_options`, `dependency()` will set it when configuring fallback
subproject. `dependency('foo', static: true)` is now equivalent to
`dependency('foo', static: true, default_options: ['default_library=static'])`.

@ -116,6 +116,17 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
mlog.log('Looking for a fallback subproject for the dependency',
mlog.bold(self.display_name))
# dependency('foo', static: true) should implicitly add
# default_options: ['default_library=static']
static = kwargs.get('static')
default_options = stringlistify(func_kwargs.get('default_options', []))
if static is not None and not any('default_library' in i for i in default_options):
default_library = 'static' if static else 'shared'
opt = f'default_library={default_library}'
mlog.log(f'Building fallback subproject with {opt}')
default_options.append(opt)
func_kwargs['default_options'] = default_options
# Configure the subproject
subp_name = self.subproject_name
varname = self.subproject_varname

@ -14,11 +14,16 @@ from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprec
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder,
EnvironmentVariablesObject)
from .type_checking import NATIVE_KW
import typing as T
if T.TYPE_CHECKING:
from .interpreter import Interpreter
from typing_extensions import TypedDict
class FuncOverrideDependency(TypedDict):
native: mesonlib.MachineChoice
static: T.Optional[bool]
class MesonMain(MesonInterpreterObject):
def __init__(self, build: 'build.Build', interpreter: 'Interpreter'):
@ -294,21 +299,54 @@ class MesonMain(MesonInterpreterObject):
raise InterpreterException('Second argument must be an external program or executable.')
self.interpreter.add_find_program_override(name, exe)
@typed_kwargs('meson.override_dependency', NATIVE_KW,
KwargInfo('static', bool, since='0.60.0'))
@typed_pos_args('meson.override_dependency', str, dependencies.Dependency)
@FeatureNew('meson.override_dependency', '0.54.0')
@permittedKwargs({'native'})
def override_dependency_method(self, args, kwargs):
if len(args) != 2:
raise InterpreterException('Override needs two arguments')
name = args[0]
dep = args[1]
if not isinstance(name, str) or not name:
def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency], kwargs: 'FuncOverrideDependency') -> None:
name, dep = args
if not name:
raise InterpreterException('First argument must be a string and cannot be empty')
if not isinstance(dep, dependencies.Dependency):
raise InterpreterException('Second argument must be a dependency object')
optkey = OptionKey('default_library', subproject=self.interpreter.subproject)
default_library = self.interpreter.coredata.get_option(optkey)
assert isinstance(default_library, str), 'for mypy'
static = kwargs['static']
if static is None:
# We don't know if dep represents a static or shared library, could
# be a mix of both. We assume it is following default_library
# value.
self._override_dependency_impl(name, dep, kwargs, static=None)
if default_library == 'static':
self._override_dependency_impl(name, dep, kwargs, static=True)
elif default_library == 'shared':
self._override_dependency_impl(name, dep, kwargs, static=False)
else:
self._override_dependency_impl(name, dep, kwargs, static=True)
self._override_dependency_impl(name, dep, kwargs, static=False)
else:
# dependency('foo') without specifying static kwarg should find this
# override regardless of the static value here. But do not raise error
# if it has already been overridden, which would happend when overriding
# static and shared separately:
# meson.override_dependency('foo', shared_dep, static: false)
# meson.override_dependency('foo', static_dep, static: true)
# In that case dependency('foo') would return the first override.
self._override_dependency_impl(name, dep, kwargs, static=None, permissive=True)
self._override_dependency_impl(name, dep, kwargs, static=static)
def _override_dependency_impl(self, name: str, dep: dependencies.Dependency, kwargs: 'FuncOverrideDependency', static: T.Optional[bool], permissive: bool = False) -> None:
kwargs = kwargs.copy()
if static is None:
del kwargs['static']
else:
kwargs['static'] = static
identifier = dependencies.get_dep_identifier(name, kwargs)
for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
for_machine = kwargs['native']
override = self.build.dependency_overrides[for_machine].get(identifier)
if override:
if permissive:
return
m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}'
location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
raise InterpreterException(m.format(name, location))

@ -65,3 +65,30 @@ assert(d.found(), 'Should be able to fallback to sub-subproject')
# file exists.
d = dependency('subsubsub')
assert(d.found(), 'Should be able to fallback to sub-sub-subproject')
# Verify that `static: true` implies 'default_library=static'.
d = dependency('sub_static', static: true)
assert(d.found())
# Verify that when not specifying static kwarg we can still get fallback dep.
d = dependency('sub_static')
assert(d.found())
# But when asking for shared library explicitly, it is not found.
d = dependency('sub_static', static: false, required: false)
assert(not d.found())
# The subproject also overrides sub_static2 with `static: true`
d = dependency('sub_static2')
assert(d.found())
d = dependency('sub_static2', static: true)
assert(d.found())
d = dependency('sub_static2', static: false, required: false)
assert(not d.found())
# sub_static3 is overridden twice with `static: true` and `static: false`
d = dependency('sub_static3')
assert(d.found())
assert(d.get_variable('static') == 'true')
d = dependency('sub_static3', static: true)
assert(d.found())
assert(d.get_variable('static') == 'true')
d = dependency('sub_static3', static: false)
assert(d.found())
assert(d.get_variable('static') == 'false')

@ -0,0 +1,8 @@
project('sub_static')
assert(get_option('default_library') == 'static')
meson.override_dependency('sub_static', declare_dependency())
meson.override_dependency('sub_static2', declare_dependency(), static: true)
meson.override_dependency('sub_static3', declare_dependency(variables: {'static': 'true'}), static: true)
meson.override_dependency('sub_static3', declare_dependency(variables: {'static': 'false'}), static: false)

@ -2487,6 +2487,11 @@ class AllPlatformTests(BasePlatformTests):
'name': 'sub_novar',
'version': '1.0',
},
{
'descriptive_name': 'sub_static',
'name': 'sub_static',
'version': 'undefined'
},
{
'descriptive_name': 'subsub',
'name': 'subsub',

Loading…
Cancel
Save