Fix consistency in variables kwarg

Share common code to extract the `variables` kwarg in
declare_dependency() and pkg.generate().
pull/7859/head
Xavier Claessens 4 years ago committed by Jussi Pakkanen
parent 2e80c52129
commit bcf369ea3c
  1. 3
      docs/markdown/Pkgconfig-module.md
  2. 2
      docs/markdown/Reference-manual.md
  3. 12
      docs/markdown/snippets/pkg_idep_variables.md
  4. 33
      mesonbuild/interpreter.py
  5. 26
      mesonbuild/modules/pkgconfig.py
  6. 1
      run_unittests.py
  7. 3
      test cases/common/218 dependency get_variable method/meson.build
  8. 3
      test cases/common/47 pkgconfig-gen/meson.build
  9. 2
      test cases/failing/47 pkgconfig variables zero length/test.json
  10. 2
      test cases/failing/48 pkgconfig variables zero length value/test.json
  11. 2
      test cases/failing/49 pkgconfig variables not key value/test.json

@ -49,7 +49,8 @@ keyword arguments.
generated file. The strings must be in the form `name=value` and may
reference other pkgconfig variables,
e.g. `datadir=${prefix}/share`. The names `prefix`, `libdir` and
`includedir` are reserved and may not be used.
`includedir` are reserved and may not be used. *Since 0.56.0* it can also be a
dictionary.
- `version` a string describing the version of this library, used to set the
`Version:` field. (*since 0.46.0*) Defaults to the project version if unspecified.
- `d_module_versions` a list of module version flags used when compiling

@ -427,7 +427,7 @@ keyword arguments:
- `version`: the version of this dependency, such as `1.2.3`
- `variables` *(since 0.54.0)*: a dictionary of arbitrary strings, this is meant to be used
in subprojects where special variables would be provided via cmake or
pkg-config.
pkg-config. *since 0.56.0* it can also be a list of `'key=value'` strings.
### dependency()

@ -0,0 +1,12 @@
## Consistency between `declare_dependency()` and `pkgconfig.generate()` variables
The `variables` keyword argument in `declare_dependency()` used to only support
dictionary and `pkgconfig.generate()` only list of strings. They now both support
dictionary and list of strings in the format `'name=value'`. This makes easier
to share a common set of variables for both:
```meson
vars = {'foo': 'bar'}
dep = declare_dependency(..., variables: vars)
pkg.generate(..., variables: vars)
```

@ -2680,6 +2680,32 @@ class Interpreter(InterpreterBase):
def func_files(self, node, args, kwargs):
return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args]
# Used by declare_dependency() and pkgconfig.generate()
def extract_variables(self, kwargs, argname='variables', list_new=False, dict_new=False):
variables = kwargs.get(argname, {})
if isinstance(variables, dict):
if dict_new and variables:
FeatureNew.single_use('variables as dictionary', '0.56.0', self.subproject)
else:
varlist = mesonlib.stringlistify(variables)
if list_new:
FeatureNew.single_use('variables as list of strings', '0.56.0', self.subproject)
variables = {}
for v in varlist:
try:
(key, value) = v.split('=', 1)
except ValueError:
raise InterpreterException('Variable {!r} must have a value separated by equals sign.'.format(v))
variables[key.strip()] = value.strip()
for k, v in variables.items():
if not k or not v:
raise InterpreterException('Empty variable name or value')
if any(c.isspace() for c in k):
raise InterpreterException('Invalid whitespace in variable name "{}"'.format(k))
if not isinstance(v, str):
raise InterpreterException('variables values must be strings.')
return variables
@FeatureNewKwargs('declare_dependency', '0.46.0', ['link_whole'])
@FeatureNewKwargs('declare_dependency', '0.54.0', ['variables'])
@permittedKwargs(permitted_kwargs['declare_dependency'])
@ -2696,12 +2722,7 @@ class Interpreter(InterpreterBase):
deps = unholder(extract_as_list(kwargs, 'dependencies'))
compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
variables = kwargs.get('variables', {})
if not isinstance(variables, dict):
raise InterpreterException('variables must be a dict.')
if not all(isinstance(v, str) for v in variables.values()):
# Because that is how they will come from pkg-config and cmake
raise InterpreterException('variables values be strings.')
variables = self.extract_variables(kwargs, list_new=True)
final_deps = []
for d in deps:
try:

@ -513,31 +513,17 @@ class PkgConfigModule(ExtensionModule):
deps.remove_dups()
def parse_variable_list(stringlist):
def parse_variable_list(vardict):
reserved = ['prefix', 'libdir', 'includedir']
variables = []
for var in stringlist:
# foo=bar=baz is ('foo', 'bar=baz')
l = var.split('=', 1)
if len(l) < 2:
raise mesonlib.MesonException('Invalid variable "{}". Variables must be in \'name=value\' format'.format(var))
name, value = l[0].strip(), l[1].strip()
if not name or not value:
raise mesonlib.MesonException('Invalid variable "{}". Variables must be in \'name=value\' format'.format(var))
# Variable names must not contain whitespaces
if any(c.isspace() for c in name):
raise mesonlib.MesonException('Invalid whitespace in assignment "{}"'.format(var))
for name, value in vardict.items():
if name in reserved:
raise mesonlib.MesonException('Variable "{}" is reserved'.format(name))
variables.append((name, value))
return variables
variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('variables', [])))
variables = self.interpreter.extract_variables(kwargs, dict_new=True)
variables = parse_variable_list(variables)
pcfile = filebase + '.pc'
pkgroot = kwargs.get('install_dir', default_install_dir)
@ -552,7 +538,9 @@ class PkgConfigModule(ExtensionModule):
version, pcfile, conflicts, variables,
False, dataonly)
res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot)
variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('uninstalled_variables', [])))
variables = self.interpreter.extract_variables(kwargs, argname='uninstalled_variables', dict_new=True)
variables = parse_variable_list(variables)
pcfile = filebase + '-uninstalled.pc'
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,

@ -6040,6 +6040,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertTrue(libhello_nolib.found())
self.assertEqual(libhello_nolib.get_link_args(), [])
self.assertEqual(libhello_nolib.get_compile_args(), [])
self.assertEqual(libhello_nolib.get_pkgconfig_variable('foo', {}), 'bar')
def test_pkgconfig_gen_deps(self):
'''

@ -59,3 +59,6 @@ idep = declare_dependency()
assert(idep.get_variable(pkgconfig : 'foo', cmake : 'foo', configtool : 'foo',
default_value : default) == default,
'something went wrong with an InternalDependency with no variables.')
idep = declare_dependency(variables : ['foo=value'])
assert(idep.get_variable(internal: 'foo') == 'value')

@ -64,7 +64,8 @@ pkgg.generate(
name : 'libhello_nolib',
description : 'A minimalistic pkgconfig file.',
version : libver,
dataonly: true
dataonly: true,
variables : {'foo': 'bar'},
)
# Regression test for 2 cases:

@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/47 pkgconfig variables zero length/meson.build:8:5: ERROR: Invalid variable \"=value\". Variables must be in 'name=value' format"
"line": "test cases/failing/47 pkgconfig variables zero length/meson.build:8:5: ERROR: Empty variable name or value"
}
]
}

@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/48 pkgconfig variables zero length value/meson.build:8:5: ERROR: Invalid variable \"key=\". Variables must be in 'name=value' format"
"line": "test cases/failing/48 pkgconfig variables zero length value/meson.build:8:5: ERROR: Empty variable name or value"
}
]
}

@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/49 pkgconfig variables not key value/meson.build:8:5: ERROR: Invalid variable \"this_should_be_key_value\". Variables must be in 'name=value' format"
"line": "test cases/failing/49 pkgconfig variables not key value/meson.build:8:5: ERROR: Variable 'this_should_be_key_value' must have a value separated by equals sign."
}
]
}

Loading…
Cancel
Save