Skip to content

Commit

Permalink
wrap: Add 'provide' section
Browse files Browse the repository at this point in the history
  • Loading branch information
xclaesse committed Apr 13, 2020
1 parent c574dee commit 92beca3
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 11 deletions.
4 changes: 3 additions & 1 deletion docs/markdown/Reference-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,9 @@ arguments:
In that case, the `fallback` keyword argument can be a single string instead
of a list of 2 strings. *Since 0.55.0* the `fallback` keyword argument can be
omitted when there is a wrap file or a directory with the same `dependency_name`,
and subproject used `meson.override_dependency('dependency_name', subproj_dep)`.
and subproject registered the dependency using
`meson.override_dependency('dependency_name', subproj_dep)`, or when the wrap
file has `dependency_name` in its `[provide]` section.
- `language` *(added 0.42.0)* defines what language-specific
dependency to find if it's available for multiple languages.
- `method` defines the way the dependency is detected, the default is
Expand Down
30 changes: 30 additions & 0 deletions docs/markdown/Wrap-dependency-system-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,36 @@ revision = head
## Accepted configuration properties for wraps
- `directory` - name of the subproject root directory, defaults to the name of the wrap.

## `provide` section

*Since *0.55.0*

Wrap files can define the dependencies it provides in the `[provide]` section.
When a wrap file provides the dependency `foo` any call do `dependency('foo')`
will automatically fallback to that subproject even if no `fallback` keyword
argument is given. Each entry in the format `dependency_name = variable_name`,
where `dependency_name` usually match the corresponding pkg-config name and
`variable_name` is the name of a variable defined in the subproject that should
be returned for that dependency. In the case the subproject uses
`meson.override_dependency('foo', foo_dep)` the `variable_name` can be left empty
in the wrap file.

For example `glib.wrap` provides `glib-2.0`, `gobject-2.0` and `gio-2.0`. A wrap
file for glib would look like:
```ini
[wrap-git]
url=https://gitlab.gnome.org/GNOME/glib.git
revision=glib-2-62

[provide]
glib-2.0=glib_dep
gobject-2.0=gobject_dep
gio-2.0=gio_dep
```

With such wrap file, `dependency('glib-2.0')` will automatically fallback to use
`glib.wrap` and return `glib_dep` variable from the subproject.

### Specific to wrap-file
- `source_url` - download url to retrieve the wrap-file source archive
- `source_filename` - filename of the downloaded source archive
Expand Down
28 changes: 28 additions & 0 deletions docs/markdown/snippets/implicit_fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,31 @@ That means that simply adding `subprojects/foo.wrap` is enough to add fallback
to any `dependency('foo')` call. It is however requires that the subproject call
`meson.override_dependency('foo', foo_dep)` to specify which dependency object
should be used for `foo`.

## Wrap file `provide` section

Wrap files can define the dependencies it provides in the `[provide]` section.
When a wrap file provides the dependency `foo` any call do `dependency('foo')`
will automatically fallback to that subproject even if no `fallback` keyword
argument is given. Each entry in the format `dependency_name = variable_name`,
where `dependency_name` usually match the corresponding pkg-config name and
`variable_name` is the name of a variable defined in the subproject that should
be returned for that dependency. In the case the subproject uses
`meson.override_dependency('foo', foo_dep)` the `variable_name` can be left empty
in the wrap file.

For example `glib.wrap` provides `glib-2.0`, `gobject-2.0` and `gio-2.0`. A wrap
file for glib would look like:
```ini
[wrap-git]
url=https://gitlab.gnome.org/GNOME/glib.git
revision=glib-2-62

[provide]
glib-2.0=glib_dep
gobject-2.0=gobject_dep
gio-2.0=gio_dep
```

With such wrap file, `dependency('glib-2.0')` will automatically fallback to use
`glib.wrap` and return `glib_dep` variable from the subproject.
16 changes: 9 additions & 7 deletions mesonbuild/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2675,8 +2675,7 @@ def do_subproject(self, dirname: str, method: str, kwargs):
self.subproject_dir, dirname))
return subproject

subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
r = wrap.Resolver(subproject_dir_abs, self.coredata.get_builtin_option('wrap_mode'))
r = self.environment.wrap_resolver
try:
resolved = r.resolve(dirname, method)
except wrap.WrapException as e:
Expand All @@ -2694,7 +2693,7 @@ def do_subproject(self, dirname: str, method: str, kwargs):
raise e

subdir = os.path.join(self.subproject_dir, resolved)
subdir_abs = os.path.join(subproject_dir_abs, resolved)
subdir_abs = os.path.join(self.environment.get_source_dir(), subdir)
os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
self.global_args_frozen = True

Expand Down Expand Up @@ -2946,6 +2945,10 @@ def func_project(self, node, args, kwargs):
self.subproject_dir = spdirname

self.build.subproject_dir = self.subproject_dir
if not self.is_subproject():
wrap_mode = self.coredata.get_builtin_option('wrap_mode')
subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
self.environment.wrap_resolver = wrap.Resolver(subproject_dir_abs, wrap_mode)

mesonlib.project_meson_versions[self.subproject] = ''
if 'meson_version' in kwargs:
Expand Down Expand Up @@ -3435,10 +3438,9 @@ def dependency_impl(self, name, display_name, kwargs):
has_fallback = 'fallback' in kwargs
if not has_fallback and name:
# Add an implicit fallback if we have a wrap file or a directory with the same name.
subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
wrap_, directory = wrap.get_directory(subproject_dir_abs, name)
if wrap_ or os.path.exists(os.path.join(subproject_dir_abs, directory)):
kwargs['fallback'] = name
provider = self.environment.wrap_resolver.find_provider(name)
if provider:
kwargs['fallback'] = provider
has_fallback = True

if 'default_options' in kwargs and not has_fallback:
Expand Down
42 changes: 41 additions & 1 deletion mesonbuild/wrap/wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ def __init__(self, fname: str):
self.config.read(fname)
except configparser.Error:
raise WrapException('Failed to parse {}'.format(self.basename))
self.parse_wrap_section()
self.parse_provide_section()

def parse_wrap_section(self):
if len(self.config.sections()) < 1:
raise WrapException('Missing sections in {}'.format(self.basename))
self.wrap_section = self.config.sections()[0]
Expand All @@ -118,6 +122,11 @@ def __init__(self, fname: str):
self.type = self.wrap_section[5:]
self.values = dict(self.config[self.wrap_section])

def parse_provide_section(self):
self.provide = {self.name: None}
if self.config.has_section('provide'):
self.provide.update(self.config['provide'])

def get(self, key: str) -> str:
try:
return self.values[key]
Expand Down Expand Up @@ -150,11 +159,42 @@ def __init__(self, subdir_root: str, wrap_mode=WrapMode.default):
self.wrap_mode = wrap_mode
self.subdir_root = subdir_root
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
self.wraps = {} # type: T.Dict[str, T.Tuple[T.Optional[PackageDefinition], T.Optional[str]]]
self.load_wraps()

def load_wraps(self):
if not os.path.isdir(self.subdir_root):
return
# Load wrap files upfront
for f in os.listdir(self.subdir_root):
if f.endswith('.wrap'):
packagename = f[:-5]
wrap, directory = get_directory(self.subdir_root, packagename)
for k in wrap.provide.keys():
self.wraps[k] = (wrap, directory)
elif os.path.isdir(os.path.join(self.subdir_root, f)):
# Keep it in the case we have dirs with no corresponding wrap file.
self.wraps.setdefault(f, (None, f))

def find_provider(self, packagename: str):
# Return value is in the same format as fallback kwarg:
# ['subproject_name', 'variable_name'], or 'subproject_name'.
wrap, directory = self.wraps.get(packagename, (None, None))
if wrap:
dep_var = wrap.provide[packagename]
if dep_var:
return [wrap.name, dep_var]
return wrap.name
return packagename

def resolve(self, packagename: str, method: str) -> str:
self.packagename = packagename
self.wrap, self.directory = get_directory(self.subdir_root, self.packagename)
self.wrap, self.directory = self.wraps.get(packagename, (None, self.packagename))
if self.wrap and packagename != self.wrap.name:
m = 'subproject() must not be called by the name of a dependency it provides. Expecting {!r} but got {!r}.'
raise WrapException(m.format(self.wrap.name, packagename))
self.dirname = os.path.join(self.subdir_root, self.directory)

meson_file = os.path.join(self.dirname, 'meson.build')
cmake_file = os.path.join(self.dirname, 'CMakeLists.txt')

Expand Down
11 changes: 11 additions & 0 deletions test cases/common/102 subproject subdir/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ assert(not d.found(), 'Dependency should be not-found')
# Verify that implicit fallback works because subprojects/sub_implicit directory exists
d = dependency('sub_implicit')
assert(d.found(), 'Should implicitly fallback')

# Verify that implicit fallback works because sub_implicit.wrap has
# `sub_implicit_provide1=` and the subproject overrides sub_implicit_provide1.
d = dependency('sub_implicit_provide1')
assert(d.found(), 'Should implicitly fallback')

# Verify that implicit fallback works because sub_implicit.wrap has
# `sub_implicit_provide2=sub_implicit_provide2_dep` and does not override
# sub_implicit_provide2.
d = dependency('sub_implicit_provide2')
assert(d.found(), 'Should implicitly fallback')
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[wrap-file]

[provide]
sub_implicit_provide1=
sub_implicit_provide2=sub_implicit_provide2_dep
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ project('sub_implicit', 'c', version : '1.0')

dep = declare_dependency()
meson.override_dependency('sub_implicit', dep)
meson.override_dependency('sub_implicit_provide1', dep)

# This one is not overriden but the wrap file tells the variable name to use.
sub_implicit_provide2_dep = dep
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
The contents of this wrap file are never evaluated so they
can be anything.
[wrap-file]

0 comments on commit 92beca3

Please sign in to comment.