aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md4
-rw-r--r--docs/markdown/Wrap-dependency-system-manual.md32
-rw-r--r--docs/markdown/snippets/implicit_fallback.md7
-rw-r--r--mesonbuild/interpreter.py18
-rw-r--r--mesonbuild/wrap/wrap.py48
-rw-r--r--test cases/common/102 subproject subdir/meson.build11
-rw-r--r--test cases/common/102 subproject subdir/subprojects/sub_implicit.wrap5
-rw-r--r--test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build4
-rw-r--r--test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap3
9 files changed, 116 insertions, 16 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 293e41f..9bca74b 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -458,7 +458,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` *(since 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
diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md
index e59a6be..cb7c6d6 100644
--- a/docs/markdown/Wrap-dependency-system-manual.md
+++ b/docs/markdown/Wrap-dependency-system-manual.md
@@ -105,7 +105,7 @@ of downloading the file, even if `--wrap-mode` option is set to
valid value (such as a git tag) for the VCS's `checkout` command, or
(for git) `head` to track upstream's default branch. Required.
-## Specific to wrap-git
+### Specific to wrap-git
- `depth` - shallowly clone the repository to X number of commits. Note
that git always allow shallowly cloning branches, but in order to
clone commit ids shallowly, the server must support
@@ -138,6 +138,36 @@ put them somewhere where you can download them.
Meson build patches are only supported for wrap-file mode. When using
wrap-git, the repository must contain all Meson build definitions.
+## `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.
+
## Using wrapped projects
Wraps provide a convenient way of obtaining a project into your subproject directory.
diff --git a/docs/markdown/snippets/implicit_fallback.md b/docs/markdown/snippets/implicit_fallback.md
index 87003d0..bad1c71 100644
--- a/docs/markdown/snippets/implicit_fallback.md
+++ b/docs/markdown/snippets/implicit_fallback.md
@@ -7,3 +7,10 @@ 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. See [Wrap documentation](Wrap-dependency-system-manual.md#provide_section).
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 053db12..7c55932 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2779,10 +2779,9 @@ external dependencies (including libraries) must go to "dependencies".''')
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'), current_subproject=self.subproject)
+ r = self.environment.wrap_resolver
try:
- resolved = r.resolve(dirname, method)
+ resolved = r.resolve(dirname, method, self.subproject)
except wrap.WrapException as e:
subprojdir = os.path.join(self.subproject_dir, r.directory)
if isinstance(e, wrap.WrapNotFoundException):
@@ -2798,7 +2797,7 @@ external dependencies (including libraries) must go to "dependencies".''')
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
@@ -3062,6 +3061,10 @@ external dependencies (including libraries) must go to "dependencies".''')
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)
self.build.projects[self.subproject] = proj_name
mlog.log('Project name:', mlog.bold(proj_name))
@@ -3551,10 +3554,9 @@ external dependencies (including libraries) must go to "dependencies".''')
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:
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 689fb4f..63ee349 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -111,6 +111,10 @@ class PackageDefinition:
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]
@@ -120,6 +124,11 @@ class PackageDefinition:
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]
@@ -145,17 +154,48 @@ def get_directory(subdir_root: str, packagename: str):
return wrap, directory
class Resolver:
- def __init__(self, subdir_root: str, wrap_mode=WrapMode.default, current_subproject: str = ''):
+ def __init__(self, subdir_root: str, wrap_mode=WrapMode.default):
self.wrap_mode = wrap_mode
self.subdir_root = subdir_root
- self.current_subproject = current_subproject
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
self.filesdir = os.path.join(self.subdir_root, 'packagefiles')
+ self.wraps = {} # type: T.Dict[str, T.Tuple[T.Optional[PackageDefinition], T.Optional[str]]]
+ self.load_wraps()
- def resolve(self, packagename: str, method: str) -> str:
+ 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 directory
+
+ def resolve(self, packagename: str, method: str, current_subproject: str = '') -> str:
+ self.current_subproject = current_subproject
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')
diff --git a/test cases/common/102 subproject subdir/meson.build b/test cases/common/102 subproject subdir/meson.build
index 46a2bce..bc202a3 100644
--- a/test cases/common/102 subproject subdir/meson.build
+++ b/test cases/common/102 subproject subdir/meson.build
@@ -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')
diff --git a/test cases/common/102 subproject subdir/subprojects/sub_implicit.wrap b/test cases/common/102 subproject subdir/subprojects/sub_implicit.wrap
new file mode 100644
index 0000000..c14fff0
--- /dev/null
+++ b/test cases/common/102 subproject subdir/subprojects/sub_implicit.wrap
@@ -0,0 +1,5 @@
+[wrap-file]
+
+[provide]
+sub_implicit_provide1=
+sub_implicit_provide2=sub_implicit_provide2_dep
diff --git a/test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build b/test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build
index 613bd05..64374d3 100644
--- a/test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build
+++ b/test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build
@@ -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
diff --git a/test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap b/test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap
index 09ba4e8..11b2178 100644
--- a/test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap
+++ b/test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap
@@ -1,2 +1 @@
-The contents of this wrap file are never evaluated so they
-can be anything.
+[wrap-file]