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 - `static`: tells the dependency provider to try to get static
libraries instead of dynamic ones (note that this is not supported libraries instead of dynamic ones (note that this is not supported
by all dependency backends) 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 - `version` *(since 0.37.0)*: specifies the required version, a string containing a
comparison operator followed by the version string, examples include comparison operator followed by the version string, examples include
`>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching. `>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 `native` keyword arguments. Doing this in a subproject allows the parent
project to retrieve the dependency without having to know the dependency project to retrieve the dependency without having to know the dependency
variable name: `dependency(name, fallback : subproject_name)`. 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_version()`: returns the version string specified in
`project` function call. `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.log('Looking for a fallback subproject for the dependency',
mlog.bold(self.display_name)) 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 # Configure the subproject
subp_name = self.subproject_name subp_name = self.subproject_name
varname = self.subproject_varname varname = self.subproject_varname

@ -14,11 +14,16 @@ from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprec
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder, from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder, CustomTargetHolder, CustomTargetIndexHolder,
EnvironmentVariablesObject) EnvironmentVariablesObject)
from .type_checking import NATIVE_KW
import typing as T import typing as T
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from .interpreter import Interpreter from .interpreter import Interpreter
from typing_extensions import TypedDict
class FuncOverrideDependency(TypedDict):
native: mesonlib.MachineChoice
static: T.Optional[bool]
class MesonMain(MesonInterpreterObject): class MesonMain(MesonInterpreterObject):
def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): 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.') raise InterpreterException('Second argument must be an external program or executable.')
self.interpreter.add_find_program_override(name, exe) 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') @FeatureNew('meson.override_dependency', '0.54.0')
@permittedKwargs({'native'}) def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency], kwargs: 'FuncOverrideDependency') -> None:
def override_dependency_method(self, args, kwargs): name, dep = args
if len(args) != 2: if not name:
raise InterpreterException('Override needs two arguments')
name = args[0]
dep = args[1]
if not isinstance(name, str) or not name:
raise InterpreterException('First argument must be a string and cannot be empty') 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) 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) override = self.build.dependency_overrides[for_machine].get(identifier)
if override: if override:
if permissive:
return
m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}' 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) location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
raise InterpreterException(m.format(name, location)) raise InterpreterException(m.format(name, location))

@ -65,3 +65,30 @@ assert(d.found(), 'Should be able to fallback to sub-subproject')
# file exists. # file exists.
d = dependency('subsubsub') d = dependency('subsubsub')
assert(d.found(), 'Should be able to fallback to sub-sub-subproject') 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', 'name': 'sub_novar',
'version': '1.0', 'version': '1.0',
}, },
{
'descriptive_name': 'sub_static',
'name': 'sub_static',
'version': 'undefined'
},
{ {
'descriptive_name': 'subsub', 'descriptive_name': 'subsub',
'name': 'subsub', 'name': 'subsub',

Loading…
Cancel
Save