diff options
-rw-r--r-- | docs/markdown/Reference-manual.md | 4 | ||||
-rw-r--r-- | docs/markdown/Wrap-dependency-system-manual.md | 32 | ||||
-rw-r--r-- | docs/markdown/snippets/implicit_fallback.md | 7 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 18 | ||||
-rw-r--r-- | mesonbuild/wrap/wrap.py | 48 | ||||
-rw-r--r-- | test cases/common/102 subproject subdir/meson.build | 11 | ||||
-rw-r--r-- | test cases/common/102 subproject subdir/subprojects/sub_implicit.wrap | 5 | ||||
-rw-r--r-- | test cases/common/102 subproject subdir/subprojects/sub_implicit/meson.build | 4 | ||||
-rw-r--r-- | test cases/unit/12 promote/subprojects/s2/subprojects/athing.wrap | 3 |
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] |