aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS1
-rwxr-xr-xci/ciimage/arch/install.sh2
-rwxr-xr-xci/ciimage/fedora/install.sh2
-rw-r--r--docs/markdown/Builtin-options.md20
-rw-r--r--docs/markdown/CMake-module.md1
-rw-r--r--docs/markdown/Wayland-module.md64
-rw-r--r--docs/markdown/_Sidebar.md1
-rw-r--r--docs/markdown/snippets/cmake_configure_package_config_dict.md5
-rw-r--r--docs/markdown/snippets/python_module_env.md9
-rw-r--r--docs/markdown/snippets/wayland-module.md4
-rw-r--r--docs/sitemap.txt1
-rw-r--r--docs/theme/extra/templates/navbar_links.html1
-rw-r--r--docs/yaml/functions/summary.yaml33
-rw-r--r--mesonbuild/coredata.py2
-rw-r--r--mesonbuild/mdevenv.py10
-rw-r--r--mesonbuild/mesonlib/universal.py4
-rw-r--r--mesonbuild/modules/__init__.py13
-rw-r--r--mesonbuild/modules/cmake.py53
-rw-r--r--mesonbuild/modules/python.py21
-rw-r--r--mesonbuild/modules/sourceset.py241
-rw-r--r--mesonbuild/modules/unstable_wayland.py120
-rw-r--r--mesonbuild/mtest.py7
-rwxr-xr-xrun_mypy.py1
-rwxr-xr-xrun_project_tests.py10
-rw-r--r--test cases/cmake/20 cmake file/meson.build4
-rw-r--r--test cases/vala/14 target glib version and gresources/test.vala2
-rw-r--r--test cases/wayland/1 client/main.c9
-rw-r--r--test cases/wayland/1 client/meson.build16
-rw-r--r--test cases/wayland/2 server/main.c9
-rw-r--r--test cases/wayland/2 server/meson.build16
-rw-r--r--test cases/wayland/3 local/main.c9
-rw-r--r--test cases/wayland/3 local/meson.build11
-rw-r--r--test cases/wayland/3 local/test.xml6
33 files changed, 582 insertions, 126 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index e6be76c..b531c26 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -3,6 +3,7 @@
/mesonbuild/modules/cmake.py @mensinda
/mesonbuild/modules/unstable_external_project.py @xclaesse
/mesonbuild/modules/unstable_rust.py @dcbaker
+/mesonbuild/modules/unstable_wayland.py @markbolhuis
/mesonbuild/ast/ @mensinda
/mesonbuild/cmake/ @mensinda
/mesonbuild/compilers/ @dcbaker
diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh
index 72816ab..9628635 100755
--- a/ci/ciimage/arch/install.sh
+++ b/ci/ciimage/arch/install.sh
@@ -14,7 +14,7 @@ pkgs=(
itstool gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz
doxygen vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools
libwmf valgrind cmake netcdf-fortran openmpi nasm gnustep-base gettext
- python-lxml hotdoc rust-bindgen qt6-base qt6-tools
+ python-lxml hotdoc rust-bindgen qt6-base qt6-tools wayland wayland-protocols
# cuda
)
diff --git a/ci/ciimage/fedora/install.sh b/ci/ciimage/fedora/install.sh
index df1d853..2f1ae7d 100755
--- a/ci/ciimage/fedora/install.sh
+++ b/ci/ciimage/fedora/install.sh
@@ -15,7 +15,7 @@ pkgs=(
doxygen vulkan-devel vulkan-validation-layers-devel openssh mercurial gtk-sharp2-devel libpcap-devel gpgme-devel
qt5-qtbase-devel qt5-qttools-devel qt5-linguist qt5-qtbase-private-devel
libwmf-devel valgrind cmake openmpi-devel nasm gnustep-base-devel gettext-devel ncurses-devel
- libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel libgcrypt-devel
+ libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel libgcrypt-devel wayland-devel wayland-protocols-devel
)
# Sys update
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index db3c3e8..c8e98dd 100644
--- a/docs/markdown/Builtin-options.md
+++ b/docs/markdown/Builtin-options.md
@@ -271,10 +271,11 @@ name with the module name: `-D<module>.<option>=<value>` (e.g. `-Dpython.platlib
### Python module
-| Option | Default value | Possible values | Description |
-| ------ | ------------- | --------------- | ----------- |
-| platlibdir | | Directory path | Directory for site-specific, platform-specific files (Since 0.60.0) |
-| purelibdir | | Directory path | Directory for site-specific, non-platform-specific files (Since 0.60.0) |
+| Option | Default value | Possible values | Description |
+| ------ | ------------- | ----------------- | ----------- |
+| install_env | prefix | {auto,prefix,system,venv} | Which python environment to install to (Since 0.62.0) |
+| platlibdir | | Directory path | Directory for site-specific, platform-specific files (Since 0.60.0) |
+| purelibdir | | Directory path | Directory for site-specific, non-platform-specific files (Since 0.60.0) |
*Since 0.60.0* `python.platlibdir` and `python.purelibdir` options are used by
python module methods `python.install_sources()` and `python.get_install_dir()`.
@@ -283,3 +284,14 @@ relative to the installation `prefix`, which will often result in installed pyth
modules to not be found by the interpreter unless `prefix` is `/usr` on Linux,
or for example `C:\Python39` on Windows. These options can be absolute paths
outside of `prefix`.
+
+*Since 0.62.0* The `python.install_env` option is used to detect the correct
+installation path. Setting to `system` will avoid making the paths relative to
+`prefix` and instead use the global site-packages of the selected python
+interpreter directly, even if it is a venv. Setting to `venv` will instead use
+the paths for the virtualenv the python found installation comes from (or fail
+if it is not a virtualenv). Setting to `auto` will check if the found
+installation is a virtualenv, and use `venv` or `system` as appropriate (but
+never `prefix`). This option is mutually exclusive with the `platlibdir`/`purelibdir`.
+
+For backwards compatibility purposes, the default `install_env` is `prefix`.
diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md
index 8e6c4e9..a5c0c7e 100644
--- a/docs/markdown/CMake-module.md
+++ b/docs/markdown/CMake-module.md
@@ -262,6 +262,7 @@ the `configuration` parameter.
* `input`: the template file where that will be treated for variable substitutions contained in `configuration`.
* `install_dir`: optional installation directory, it defaults to `$(libdir)/cmake/$(name)`.
* `configuration`: a `configuration_data` object that will be used for variable substitution in the template file.
+ *Since 0.62.0* it can take a dictionary instead.
Example:
diff --git a/docs/markdown/Wayland-module.md b/docs/markdown/Wayland-module.md
new file mode 100644
index 0000000..d30627c
--- /dev/null
+++ b/docs/markdown/Wayland-module.md
@@ -0,0 +1,64 @@
+# Unstable Wayland Module
+
+This module is available since version 0.62.0.
+
+This module provides helper functions to find wayland protocol
+xmls and to generate .c and .h files using wayland-scanner
+
+**Note**: this module is unstable. It is only provided as a technology
+preview. Its API may change in arbitrary ways between releases or it
+might be removed from Meson altogether.
+
+## Quick Usage
+
+```meson
+project('hello-wayland', 'c')
+
+wl_dep = dependency('wayland-client')
+wl_mod = import('unstable-wayland')
+
+xml = wl_mod.find_protocol('xdg-shell')
+xdg_shell = wl_mod.scan_xml(xml)
+
+executable('hw', 'main.c', xdg_shell, dependencies : wl_dep)
+```
+
+## Methods
+
+### find_protocol
+
+```meson
+xml = wl_mod.find_protocol(
+ 'xdg-decoration',
+ state : 'unstable',
+ version : 1,
+)
+```
+This function requires one positional argument: the protocol base name.
+- `state` Optional arg that specifies the current state of the protocol.
+Either stable, staging, or unstable.
+The default is stable.
+- `version` The backwards incompatible version number.
+Required for staging or unstable. An error is raised for stable.
+
+### scan_xml
+```meson
+generated = wl_mod.scan_xml(
+ 'my-protocol.xml',
+ side : 'client',
+ scope : 'private',
+)
+```
+This function accepts one or more arguments of either string or file type.
+
+- `side` Optional arg that specifies if client or server side code is generated.
+The default is client side.
+- `scope` Optional arg that specifies the scope of the generated code.
+Either public or private.
+The default is private.
+
+
+## Links
+- [Official Wayland Documentation](https://wayland.freedesktop.org/docs/html/)
+- [Wayland GitLab](https://gitlab.freedesktop.org/wayland)
+- [Wayland Book](https://wayland-book.com/)
diff --git a/docs/markdown/_Sidebar.md b/docs/markdown/_Sidebar.md
index 0ca1762..ce73d5a 100644
--- a/docs/markdown/_Sidebar.md
+++ b/docs/markdown/_Sidebar.md
@@ -13,3 +13,4 @@
* [i18n](i18n-module.md)
* [pkgconfig](Pkgconfig-module.md)
* [rust](Rust-module.md)
+* [wayland](Wayland-module.md)
diff --git a/docs/markdown/snippets/cmake_configure_package_config_dict.md b/docs/markdown/snippets/cmake_configure_package_config_dict.md
new file mode 100644
index 0000000..253a887
--- /dev/null
+++ b/docs/markdown/snippets/cmake_configure_package_config_dict.md
@@ -0,0 +1,5 @@
+## cmake.configure_package_config_file can now take a dict
+
+The `configuration` kwarg of the `configure_package_config_file()` function
+from the `cmake` module can now take a dict object, just like the regular
+`configure_file()` function.
diff --git a/docs/markdown/snippets/python_module_env.md b/docs/markdown/snippets/python_module_env.md
new file mode 100644
index 0000000..87a156d
--- /dev/null
+++ b/docs/markdown/snippets/python_module_env.md
@@ -0,0 +1,9 @@
+## New option to choose python installation environment
+
+It is now possible to specify `-Dpython.install_env` and choose how python modules are installed.
+
+- `venv`: assume that a virtualenv is active and install to that
+- `system`: install to the global site-packages of the selected interpreter
+ (the one that the venv module calls --system-site-packages)
+- `prefix`: preserve existing behavior
+- `auto`: autodetect whether to use venv or system
diff --git a/docs/markdown/snippets/wayland-module.md b/docs/markdown/snippets/wayland-module.md
new file mode 100644
index 0000000..cd5e5dc
--- /dev/null
+++ b/docs/markdown/snippets/wayland-module.md
@@ -0,0 +1,4 @@
+## New unstable wayland module
+
+This module can search for protocol xml files from the wayland-protocols
+package, and generate .c and .h files using wayland-scanner.
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index 82e0a7b..11b64e0 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -58,6 +58,7 @@ index.md
SourceSet-module.md
Windows-module.md
i18n-module.md
+ Wayland-module.md
Java.md
Vala.md
D.md
diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html
index c518de5..65a21a2 100644
--- a/docs/theme/extra/templates/navbar_links.html
+++ b/docs/theme/extra/templates/navbar_links.html
@@ -26,6 +26,7 @@
("Rust-module.html","Rust"), \
("Simd-module.html","Simd"), \
("SourceSet-module.html","SourceSet"), \
+ ("Wayland-module.html","Wayland"), \
("Windows-module.html","Windows")]:
<li>
<a href="@tup[0]">@tup[1]</a>
diff --git a/docs/yaml/functions/summary.yaml b/docs/yaml/functions/summary.yaml
index 3e7d463..29bff0b 100644
--- a/docs/yaml/functions/summary.yaml
+++ b/docs/yaml/functions/summary.yaml
@@ -10,7 +10,7 @@ description: |
the section keyword argument is omitted, those key/value pairs are
implicitly grouped into a section with no title. key/value pairs can
optionally be grouped into a dictionary, but keep in mind that
- dictionaries does not guarantee ordering. `key` must be string,
+ dictionaries do not guarantee ordering. `key` must be string,
`value` can be:
- an integer, boolean or string
@@ -62,10 +62,33 @@ example: |
```
posargs:
- key:
- type: str
- description: The name of the new entry
+ key_or_dict:
+ type: str | dict[str | bool | int | dep | external_program | list[str | bool | int | dep | external_program]]
+ description: |
+ The name of the new entry, or a dict containing multiple entries. If a
+ dict is passed it is equivalent to calling summary() once for each
+ key-value pair. Keep in mind that dictionaries do not guarantee
+ ordering.
+optargs:
value:
type: str | bool | int | dep | external_program | list[str | bool | int | dep | external_program]
- description: The value to print for the `key`
+ description: |
+ The value to print for the `key`. Only valid if `key_or_dict` is a str.
+
+kwargs:
+ bool_yn:
+ type: bool
+ default: false
+ description: Convert bool values to yes and no
+ section:
+ type: str
+ description: The section to put this summary information under. If the
+ section keyword argument is omitted, key/value pairs are implicitly
+ grouped into a section with no title.
+ list_sep:
+ type: str
+ since: 0.54.0
+ description: |
+ The separator to use when printing list values in this summary. If no
+ separator is given, each list item will be printed on its own line.
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 4a84467..e5c0287 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -1218,6 +1218,8 @@ BUILTIN_CORE_OPTIONS: 'KeyedOptionDictType' = OrderedDict([
(OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
# Python module
+ (OptionKey('install_env', module='python'),
+ BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])),
(OptionKey('platlibdir', module='python'),
BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')),
(OptionKey('purelibdir', module='python'),
diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py
index eb05020..3c522a6 100644
--- a/mesonbuild/mdevenv.py
+++ b/mesonbuild/mdevenv.py
@@ -48,7 +48,11 @@ def run(options: argparse.Namespace) -> int:
args = options.command
if not args:
prompt_prefix = f'[{b.project_name}]'
- if is_windows():
+ shell_env = os.environ.get("SHELL")
+ # Prefer $SHELL in a MSYS2 bash despite it being Windows
+ if shell_env and os.path.exists(shell_env):
+ args = [shell_env]
+ elif is_windows():
shell = get_windows_shell()
if shell == 'powershell.exe':
args = ['powershell.exe']
@@ -62,9 +66,7 @@ def run(options: argparse.Namespace) -> int:
args = [os.environ.get("SHELL", os.path.realpath("/bin/sh"))]
if "bash" in args[0] and not os.environ.get("MESON_DISABLE_PS1_OVERRIDE"):
tmprc = tempfile.NamedTemporaryFile(mode='w')
- bashrc = os.path.expanduser('~/.bashrc')
- if os.path.exists(bashrc):
- tmprc.write(f'. {bashrc}\n')
+ tmprc.write('[ -e ~/.bashrc ] && . ~/.bashrc\n')
tmprc.write(f'export PS1="{prompt_prefix} $PS1"')
tmprc.flush()
# Let the GC remove the tmp file
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py
index e86fb99..1bbc2c6 100644
--- a/mesonbuild/mesonlib/universal.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -245,7 +245,7 @@ def is_ascii_string(astring: T.Union[str, bytes]) -> bool:
return True
-def check_direntry_issues(direntry_array: T.Union[T.List[T.Union[str, bytes]], str, bytes]) -> None:
+def check_direntry_issues(direntry_array: T.Union[T.Iterable[T.Union[str, bytes]], str, bytes]) -> None:
import locale
# Warn if the locale is not UTF-8. This can cause various unfixable issues
# such as os.stat not being able to decode filenames with unicode in them.
@@ -253,7 +253,7 @@ def check_direntry_issues(direntry_array: T.Union[T.List[T.Union[str, bytes]], s
# encoding, so we can just warn about it.
e = locale.getpreferredencoding()
if e.upper() != 'UTF-8' and not is_windows():
- if not isinstance(direntry_array, list):
+ if isinstance(direntry_array, (str, bytes)):
direntry_array = [direntry_array]
for de in direntry_array:
if is_ascii_string(de):
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index 73e22ff..85bec0b 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -80,8 +80,10 @@ class ModuleState:
def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True,
version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None,
- wanted: T.Optional[str] = None, silent: bool = False) -> 'ExternalProgram':
- return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent)
+ wanted: T.Optional[str] = None, silent: bool = False,
+ for_machine: MachineChoice = MachineChoice.HOST) -> 'ExternalProgram':
+ return self._interpreter.find_program_impl(prog, required=required, version_func=version_func,
+ wanted=wanted, silent=silent, for_machine=for_machine)
def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]],
workdir: T.Optional[str] = None,
@@ -103,6 +105,13 @@ class ModuleState:
module: T.Optional[str] = None) -> T.Union[str, int, bool, 'WrapMode']:
return self.environment.coredata.get_option(mesonlib.OptionKey(name, subproject, machine, lang, module))
+ def is_user_defined_option(self, name: str, subproject: str = '',
+ machine: MachineChoice = MachineChoice.HOST,
+ lang: T.Optional[str] = None,
+ module: T.Optional[str] = None) -> bool:
+ key = mesonlib.OptionKey(name, subproject, machine, lang, module)
+ return key in self._interpreter.user_defined_options.cmd_line_options
+
class ModuleObject(HoldableObject):
"""Base class for all objects returned by modules
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index 73882c7..b046371 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -37,10 +37,13 @@ from ..interpreterbase import (
typed_kwargs,
KwargInfo,
+ ContainerTypeInfo,
)
if T.TYPE_CHECKING:
- class WriteBasicPackageVersionFile(T.TypedDict):
+ from typing_extensions import TypedDict
+
+ class WriteBasicPackageVersionFile(TypedDict):
arch_independent: bool
compatibility: str
@@ -48,6 +51,13 @@ if T.TYPE_CHECKING:
name: str
version: str
+ class ConfigurePackageConfigFile(TypedDict):
+
+ configuration: T.Union[build.ConfigurationData, dict]
+ input: T.Union[str, mesonlib.File]
+ install_dir: T.Optional[str]
+ name: str
+
COMPATIBILITIES = ['AnyNewerVersion', 'SameMajorVersion', 'SameMinorVersion', 'ExactVersion']
# Taken from https://github.com/Kitware/CMake/blob/master/Modules/CMakePackageConfigHelpers.cmake
@@ -334,42 +344,37 @@ class CmakeModule(ExtensionModule):
shutil.copymode(infile, outfile_tmp)
mesonlib.replace_if_different(outfile, outfile_tmp)
- @permittedKwargs({'input', 'name', 'install_dir', 'configuration'})
- def configure_package_config_file(self, state, args, kwargs):
- if args:
- raise mesonlib.MesonException('configure_package_config_file takes only keyword arguments.')
-
- if 'input' not in kwargs:
- raise mesonlib.MesonException('configure_package_config_file requires "input" keyword.')
+ @noPosargs
+ @typed_kwargs(
+ 'cmake.configure_package_config_file',
+ KwargInfo('configuration', (build.ConfigurationData, dict), required=True),
+ KwargInfo('input',
+ (str, mesonlib.File, ContainerTypeInfo(list, mesonlib.File)), required=True,
+ validator=lambda x: 'requires exactly one file' if isinstance(x, list) and len(x) != 1 else None,
+ convertor=lambda x: x[0] if isinstance(x, list) else x),
+ KwargInfo('install_dir', (str, NoneType), default=None),
+ KwargInfo('name', str, required=True),
+ )
+ def configure_package_config_file(self, state, args, kwargs: 'ConfigurePackageConfigFile'):
inputfile = kwargs['input']
- if isinstance(inputfile, list):
- if len(inputfile) != 1:
- m = "Keyword argument 'input' requires exactly one file"
- raise mesonlib.MesonException(m)
- inputfile = inputfile[0]
- if not isinstance(inputfile, (str, mesonlib.File)):
- raise mesonlib.MesonException("input must be a string or a file")
if isinstance(inputfile, str):
inputfile = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, inputfile)
ifile_abs = inputfile.absolute_path(state.environment.source_dir, state.environment.build_dir)
- if 'name' not in kwargs:
- raise mesonlib.MesonException('"name" not specified.')
name = kwargs['name']
(ofile_path, ofile_fname) = os.path.split(os.path.join(state.subdir, f'{name}Config.cmake'))
ofile_abs = os.path.join(state.environment.build_dir, ofile_path, ofile_fname)
- install_dir = kwargs.get('install_dir', os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'cmake', name))
- if not isinstance(install_dir, str):
- raise mesonlib.MesonException('"install_dir" must be a string.')
+ install_dir = kwargs['install_dir']
+ if install_dir is None:
+ install_dir = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'cmake', name)
- if 'configuration' not in kwargs:
- raise mesonlib.MesonException('"configuration" not specified.')
conf = kwargs['configuration']
- if not isinstance(conf, build.ConfigurationData):
- raise mesonlib.MesonException('Argument "configuration" is not of type configuration_data')
+ if isinstance(conf, dict):
+ FeatureNew.single_use('cmake.configure_package_config_file dict as configuration', '0.62.0', state.subproject, location=state.current_node)
+ conf = build.ConfigurationData(conf)
prefix = state.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
abs_install_dir = install_dir
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 2b62ea3..a91ec58 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -338,11 +338,13 @@ variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)})
print(json.dumps({
'variables': variables,
'paths': paths,
+ 'sysconfig_paths': sysconfig.get_paths(),
'install_paths': install_paths,
'sys_paths': sys.path,
'version': sysconfig.get_python_version(),
'platform': sysconfig.get_platform(),
'is_pypy': '__pypy__' in sys.builtin_module_names,
+ 'is_venv': sys.prefix != variables['base_prefix'],
'link_libpython': links_against_libpython(),
}))
'''
@@ -352,7 +354,9 @@ if T.TYPE_CHECKING:
install_paths: T.Dict[str, str]
is_pypy: bool
+ is_venv: bool
link_libpython: bool
+ sysconfig_paths: T.Dict[str, str]
paths: T.Dict[str, str]
platform: str
suffix: str
@@ -377,7 +381,9 @@ class PythonExternalProgram(ExternalProgram):
self.info: 'PythonIntrospectionDict' = {
'install_paths': {},
'is_pypy': False,
+ 'is_venv': False,
'link_libpython': False,
+ 'sysconfig_paths': {},
'paths': {},
'platform': 'sentinal',
'variables': {},
@@ -422,7 +428,22 @@ class PythonExternalProgram(ExternalProgram):
return rel_path
value = state.get_option(f'{key}dir', module='python')
if value:
+ if state.is_user_defined_option('install_env', module='python'):
+ raise mesonlib.MesonException(f'python.{key}dir and python.install_env are mutually exclusive')
return value
+
+ install_env = state.get_option('install_env', module='python')
+ if install_env == 'auto':
+ install_env = 'venv' if self.info['is_venv'] else 'system'
+
+ if install_env == 'system':
+ rel_path = os.path.join(self.info['variables']['prefix'], rel_path)
+ elif install_env == 'venv':
+ if not self.info['is_venv']:
+ raise mesonlib.MesonException('python.install_env cannot be set to "venv" unless you are in a venv!')
+ # inside a venv, deb_system is *never* active hence info['paths'] may be wrong
+ rel_path = self.info['sysconfig_paths'][key]
+
# Use python's path relative to prefix, and warn if that's not a location
# python will lookup for modules.
abs_path = Path(state.get_option('prefix'), rel_path)
diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py
index 515e670..dd78c45 100644
--- a/mesonbuild/modules/sourceset.py
+++ b/mesonbuild/modules/sourceset.py
@@ -12,23 +12,76 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from collections import namedtuple
-from .. import mesonlib
-from .. import build
-from ..mesonlib import listify, OrderedSet
+from __future__ import annotations
+import typing as T
+
from . import ExtensionModule, ModuleObject, MutableModuleObject
+from .. import build
+from .. import dependencies
+from .. import mesonlib
from ..interpreterbase import (
- noPosargs, noKwargs, permittedKwargs,
+ noPosargs, noKwargs,
InterpreterException, InvalidArguments, InvalidCode, FeatureNew,
)
+from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args
+from ..mesonlib import OrderedSet
+
+if T.TYPE_CHECKING:
+ from typing_extensions import TypedDict
+
+ from . import ModuleState
+ from ..interpreter import Interpreter
+ from ..interpreterbase import TYPE_var, TYPE_kwargs
+
+ class AddKwargs(TypedDict):
-SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps')
-SourceFiles = namedtuple('SourceFiles', 'sources dependencies')
+ when: T.List[T.Union[str, dependencies.Dependency]]
+ if_true: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]]
+ if_false: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]
+
+ class AddAllKw(TypedDict):
+
+ when: T.List[T.Union[str, dependencies.Dependency]]
+ if_true: T.List[SourceSetImpl]
+
+ class ApplyKw(TypedDict):
+
+ strict: bool
+
+
+_WHEN_KW: KwargInfo[T.List[T.Union[str, dependencies.Dependency]]] = KwargInfo(
+ 'when',
+ ContainerTypeInfo(list, (str, dependencies.Dependency)),
+ listify=True,
+ default=[],
+)
-class SourceSet(MutableModuleObject):
- def __init__(self, interpreter):
+
+class SourceSetRule(T.NamedTuple):
+ keys: T.List[str]
+ sources: T.Any
+ if_false: T.Any
+ sourcesets: T.List[SourceSetImpl]
+ dependencies: T.List[dependencies.Dependency]
+
+
+class SourceFiles(T.NamedTuple):
+ sources: OrderedSet[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]
+ dependencies: OrderedSet[dependencies.Dependency]
+
+
+class SourceSet:
+ """Base class to avoid circular references.
+
+ Because of error messages, this class is called SourceSet, and the actual
+ implementation is an Impl.
+ """
+
+
+class SourceSetImpl(SourceSet, MutableModuleObject):
+ def __init__(self, interpreter: Interpreter):
super().__init__()
- self.rules = []
+ self.rules: T.List[SourceSetRule] = []
self.subproject = interpreter.subproject
self.environment = interpreter.environment
self.subdir = interpreter.subdir
@@ -41,71 +94,104 @@ class SourceSet(MutableModuleObject):
'apply': self.apply_method,
})
- def check_source_files(self, arg, allow_deps):
- sources = []
- deps = []
- for x in arg:
- if isinstance(x, (str, mesonlib.File,
- build.GeneratedList, build.CustomTarget,
- build.CustomTargetIndex)):
- sources.append(x)
- elif hasattr(x, 'found'):
- if not allow_deps:
- msg = 'Dependencies are not allowed in the if_false argument.'
- raise InvalidArguments(msg)
+ def check_source_files(self, args: T.Sequence[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]],
+ ) -> T.Tuple[T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]], T.List[dependencies.Dependency]]:
+ sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] = []
+ deps: T.List[dependencies.Dependency] = []
+ for x in args:
+ if isinstance(x, dependencies.Dependency):
deps.append(x)
else:
- msg = 'Sources must be strings or file-like objects.'
- raise InvalidArguments(msg)
- mesonlib.check_direntry_issues(sources)
+ sources.append(x)
+ to_check: T.List[str] = []
+
+ # Get the actual output names to check
+ for s in sources:
+ if isinstance(s, str):
+ to_check.append(s)
+ elif isinstance(s, mesonlib.File):
+ to_check.append(s.fname)
+ else:
+ to_check.extend(s.get_outputs())
+ mesonlib.check_direntry_issues(to_check)
return sources, deps
- def check_conditions(self, arg):
- keys = []
- deps = []
- for x in listify(arg):
+ def check_conditions(self, args: T.Sequence[T.Union[str, dependencies.Dependency]]
+ ) -> T.Tuple[T.List[str], T.List[dependencies.Dependency]]:
+ keys: T.List[str] = []
+ deps: T.List[dependencies.Dependency] = []
+ for x in args:
if isinstance(x, str):
keys.append(x)
- elif hasattr(x, 'found'):
- deps.append(x)
else:
- raise InvalidArguments('Conditions must be strings or dependency object')
+ deps.append(x)
return keys, deps
- @permittedKwargs(['when', 'if_false', 'if_true'])
- def add_method(self, state, args, kwargs):
+ @typed_pos_args('sourceset.add', varargs=(str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, dependencies.Dependency))
+ @typed_kwargs(
+ 'sourceset.add',
+ _WHEN_KW,
+ KwargInfo(
+ 'if_true',
+ ContainerTypeInfo(list, (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, dependencies.Dependency)),
+ listify=True,
+ default=[],
+ ),
+ KwargInfo(
+ 'if_false',
+ ContainerTypeInfo(list, (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex)),
+ listify=True,
+ default=[],
+ ),
+ )
+ def add_method(self, state: ModuleState,
+ args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]]],
+ kwargs: AddKwargs) -> None:
if self.frozen:
raise InvalidCode('Tried to use \'add\' after querying the source set')
- when = listify(kwargs.get('when', []))
- if_true = listify(kwargs.get('if_true', []))
- if_false = listify(kwargs.get('if_false', []))
- if not when and not if_true and not if_false:
- if_true = args
- elif args:
+ when = kwargs['when']
+ if_true = kwargs['if_true']
+ if_false = kwargs['if_false']
+ if not any([when, if_true, if_false]):
+ if_true = args[0]
+ elif args[0]:
raise InterpreterException('add called with both positional and keyword arguments')
keys, dependencies = self.check_conditions(when)
- sources, extra_deps = self.check_source_files(if_true, True)
- if_false, _ = self.check_source_files(if_false, False)
- self.rules.append(SourceSetRule(keys, sources, if_false, [], dependencies, extra_deps))
+ sources, extra_deps = self.check_source_files(if_true)
+ if_false, _ = self.check_source_files(if_false)
+ self.rules.append(SourceSetRule(keys, sources, if_false, [], dependencies + extra_deps))
- @permittedKwargs(['when', 'if_true'])
- def add_all_method(self, state, args, kwargs):
+ @typed_pos_args('sourceset.add_all', varargs=SourceSet)
+ @typed_kwargs(
+ 'sourceset.add_all',
+ _WHEN_KW,
+ KwargInfo(
+ 'if_true',
+ ContainerTypeInfo(list, SourceSet),
+ listify=True,
+ default=[],
+ )
+ )
+ def add_all_method(self, state: ModuleState, args: T.Tuple[T.List[SourceSetImpl]],
+ kwargs: AddAllKw) -> None:
if self.frozen:
raise InvalidCode('Tried to use \'add_all\' after querying the source set')
- when = listify(kwargs.get('when', []))
- if_true = listify(kwargs.get('if_true', []))
+ when = kwargs['when']
+ if_true = kwargs['if_true']
if not when and not if_true:
- if_true = args
- elif args:
+ if_true = args[0]
+ elif args[0]:
raise InterpreterException('add_all called with both positional and keyword arguments')
keys, dependencies = self.check_conditions(when)
for s in if_true:
- if not isinstance(s, SourceSet):
+ if not isinstance(s, SourceSetImpl):
raise InvalidCode('Arguments to \'add_all\' after the first must be source sets')
- s.frozen = True
- self.rules.append(SourceSetRule(keys, [], [], if_true, dependencies, []))
+ s.frozen = True
+ self.rules.append(SourceSetRule(keys, [], [], if_true, dependencies))
- def collect(self, enabled_fn, all_sources, into=None):
+ def collect(self, enabled_fn: T.Callable[[str], bool],
+ all_sources: bool,
+ into: T.Optional['SourceFiles'] = None) -> SourceFiles:
if not into:
into = SourceFiles(OrderedSet(), OrderedSet())
for entry in self.rules:
@@ -113,7 +199,6 @@ class SourceSet(MutableModuleObject):
all(enabled_fn(key) for key in entry.keys):
into.sources.update(entry.sources)
into.dependencies.update(entry.dependencies)
- into.dependencies.update(entry.extra_deps)
for ss in entry.sourcesets:
ss.collect(enabled_fn, all_sources, into)
if not all_sources:
@@ -123,7 +208,8 @@ class SourceSet(MutableModuleObject):
@noKwargs
@noPosargs
- def all_sources_method(self, state, args, kwargs):
+ def all_sources_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs
+ ) -> T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]:
self.frozen = True
files = self.collect(lambda x: True, True)
return list(files.sources)
@@ -131,31 +217,32 @@ class SourceSet(MutableModuleObject):
@noKwargs
@noPosargs
@FeatureNew('source_set.all_dependencies() method', '0.52.0')
- def all_dependencies_method(self, state, args, kwargs):
+ def all_dependencies_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs
+ ) -> T.List[dependencies.Dependency]:
self.frozen = True
files = self.collect(lambda x: True, True)
return list(files.dependencies)
- @permittedKwargs(['strict'])
- def apply_method(self, state, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Apply takes exactly one argument')
+ @typed_pos_args('sourceset.apply', (build.ConfigurationData, dict))
+ @typed_kwargs('sourceset.apply', KwargInfo('strict', bool, default=True))
+ def apply_method(self, state: ModuleState, args: T.Tuple[T.Union[build.ConfigurationData, T.Dict[str, TYPE_var]]], kwargs: ApplyKw) -> SourceFilesObject:
config_data = args[0]
self.frozen = True
- strict = kwargs.get('strict', True)
+ strict = kwargs['strict']
if isinstance(config_data, dict):
- def _get_from_config_data(key):
+ def _get_from_config_data(key: str) -> bool:
+ assert isinstance(config_data, dict), 'for mypy'
if strict and key not in config_data:
raise InterpreterException(f'Entry {key} not in configuration dictionary.')
- return config_data.get(key, False)
+ return bool(config_data.get(key, False))
else:
- config_cache = dict()
+ config_cache: T.Dict[str, bool] = {}
- def _get_from_config_data(key):
- nonlocal config_cache
+ def _get_from_config_data(key: str) -> bool:
+ assert isinstance(config_data, build.ConfigurationData), 'for mypy'
if key not in config_cache:
if key in config_data:
- config_cache[key] = config_data.get(key)[0]
+ config_cache[key] = bool(config_data.get(key)[0])
elif strict:
raise InvalidArguments(f'sourceset.apply: key "{key}" not in passed configuration, and strict set.')
else:
@@ -167,7 +254,7 @@ class SourceSet(MutableModuleObject):
return res
class SourceFilesObject(ModuleObject):
- def __init__(self, files):
+ def __init__(self, files: SourceFiles):
super().__init__()
self.files = files
self.methods.update({
@@ -177,26 +264,28 @@ class SourceFilesObject(ModuleObject):
@noPosargs
@noKwargs
- def sources_method(self, state, args, kwargs):
+ def sources_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs
+ ) -> T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]:
return list(self.files.sources)
@noPosargs
@noKwargs
- def dependencies_method(self, state, args, kwargs):
+ def dependencies_method(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs
+ ) -> T.List[dependencies.Dependency]:
return list(self.files.dependencies)
class SourceSetModule(ExtensionModule):
@FeatureNew('SourceSet module', '0.51.0')
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ def __init__(self, interpreter: Interpreter):
+ super().__init__(interpreter)
self.methods.update({
'source_set': self.source_set,
})
@noKwargs
@noPosargs
- def source_set(self, state, args, kwargs):
- return SourceSet(self.interpreter)
+ def source_set(self, state: ModuleState, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> SourceSetImpl:
+ return SourceSetImpl(self.interpreter)
-def initialize(*args, **kwargs):
- return SourceSetModule(*args, **kwargs)
+def initialize(interp: Interpreter) -> SourceSetModule:
+ return SourceSetModule(interp)
diff --git a/mesonbuild/modules/unstable_wayland.py b/mesonbuild/modules/unstable_wayland.py
new file mode 100644
index 0000000..85da2b7
--- /dev/null
+++ b/mesonbuild/modules/unstable_wayland.py
@@ -0,0 +1,120 @@
+# Copyright 2022 Mark Bolhuis <mark@bolhuis.dev>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from . import ExtensionModule, ModuleReturnValue
+from ..build import CustomTarget
+from ..interpreter.type_checking import NoneType, in_set_validator
+from ..interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, KwargInfo
+from ..mesonlib import File, MesonException, MachineChoice
+
+
+class WaylandModule(ExtensionModule):
+
+ @FeatureNew('wayland module', '0.62.0')
+ def __init__(self, interpreter):
+ super().__init__(interpreter)
+
+ self.protocols_dep = None
+ self.pkgdatadir = None
+ self.scanner_bin = None
+
+ self.methods.update({
+ 'scan_xml': self.scan_xml,
+ 'find_protocol': self.find_protocol,
+ })
+
+ @typed_pos_args('wayland.scan_xml', varargs=(str, File), min_varargs=1)
+ @typed_kwargs(
+ 'wayland.scan_xml',
+ KwargInfo('side', str, default='client', validator=in_set_validator({'client', 'server'})),
+ KwargInfo('scope', str, default='private', validator=in_set_validator({'private', 'public'})),
+ )
+ def scan_xml(self, state, args, kwargs):
+ if self.scanner_bin is None:
+ self.scanner_bin = state.find_program('wayland-scanner', for_machine=MachineChoice.BUILD)
+
+ scope = kwargs['scope']
+ side = kwargs['side']
+
+ xml_files = self.interpreter.source_strings_to_files(args[0])
+ targets = []
+ for xml_file in xml_files:
+ name = os.path.splitext(os.path.basename(xml_file.fname))[0]
+
+ code = CustomTarget(
+ f'{name}-protocol',
+ state.subdir,
+ state.subproject,
+ [self.scanner_bin, f'{scope}-code', '@INPUT@', '@OUTPUT@'],
+ [xml_file],
+ [f'{name}-protocol.c'],
+ backend=state.backend,
+ )
+ targets.append(code)
+
+ header = CustomTarget(
+ f'{name}-{side}-protocol',
+ state.subdir,
+ state.subproject,
+ [self.scanner_bin, f'{side}-header', '@INPUT@', '@OUTPUT@'],
+ [xml_file],
+ [f'{name}-{side}-protocol.h'],
+ backend=state.backend,
+ )
+ targets.append(header)
+
+ return ModuleReturnValue(targets, targets)
+
+ @typed_pos_args('wayland.find_protocol', str)
+ @typed_kwargs(
+ 'wayland.find_protocol',
+ KwargInfo('state', str, default='stable', validator=in_set_validator({'stable', 'staging', 'unstable'})),
+ KwargInfo('version', (int, NoneType)),
+ )
+ def find_protocol(self, state, args, kwargs):
+ base_name = args[0]
+ xml_state = kwargs['state']
+ version = kwargs['version']
+
+ if xml_state != 'stable' and version is None:
+ raise MesonException(f'{xml_state} protocols require a version number.')
+
+ if xml_state == 'stable' and version is not None:
+ raise MesonException('stable protocols do not require a version number.')
+
+ if self.protocols_dep is None:
+ self.protocols_dep = self.interpreter.func_dependency(state.current_node, ['wayland-protocols'], {})
+
+ if self.pkgdatadir is None:
+ self.pkgdatadir = self.protocols_dep.get_variable(pkgconfig='pkgdatadir', internal='pkgdatadir')
+
+ if xml_state == 'stable':
+ xml_name = f'{base_name}.xml'
+ elif xml_state == 'staging':
+ xml_name = f'{base_name}-v{version}.xml'
+ else:
+ xml_name = f'{base_name}-unstable-v{version}.xml'
+
+ path = os.path.join(self.pkgdatadir, xml_state, base_name, xml_name)
+
+ if not os.path.exists(path):
+ raise MesonException(f'The file {path} does not exist.')
+
+ return File.from_absolute_file(path)
+
+
+def initialize(interpreter):
+ return WaylandModule(interpreter)
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index c844645..02b0cba 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -1348,9 +1348,12 @@ class SingleTestRunner:
self.console_mode = ConsoleUser.LOGGER
def _get_test_cmd(self) -> T.Optional[T.List[str]]:
- if self.test.fname[0].endswith('.jar'):
+ testentry = self.test.fname[0]
+ if self.options.no_rebuild and not os.path.isfile(testentry):
+ raise TestException(f'The test program {testentry!r} does not exist. Cannot run tests before building them.')
+ if testentry.endswith('.jar'):
return ['java', '-jar'] + self.test.fname
- elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]):
+ elif not self.test.is_cross_built and run_with_mono(testentry):
return ['mono'] + self.test.fname
elif self.test.cmd_is_built and self.test.is_cross_built and self.test.needs_exe_wrapper:
if self.test.exe_wrapper is None:
diff --git a/run_mypy.py b/run_mypy.py
index 00d490b..2d7232b 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -46,6 +46,7 @@ modules = [
'mesonbuild/modules/java.py',
'mesonbuild/modules/keyval.py',
'mesonbuild/modules/qt.py',
+ 'mesonbuild/modules/sourceset.py',
'mesonbuild/modules/unstable_external_project.py',
'mesonbuild/modules/unstable_rust.py',
'mesonbuild/modules/windows.py',
diff --git a/run_project_tests.py b/run_project_tests.py
index 926f4ef..ea8f901 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -84,7 +84,7 @@ if T.TYPE_CHECKING:
ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'failing-build', 'failing-test',
'keyval', 'platform-osx', 'platform-windows', 'platform-linux',
'java', 'C#', 'vala', 'cython', 'rust', 'd', 'objective c', 'objective c++',
- 'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm',
+ 'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm', 'wayland'
]
@@ -1033,6 +1033,13 @@ def should_skip_rust(backend: Backend) -> bool:
return True
return False
+def should_skip_wayland() -> bool:
+ if mesonlib.is_windows() or mesonlib.is_osx():
+ return True
+ if not shutil.which('wayland-scanner'):
+ return True
+ return False
+
def detect_tests_to_run(only: T.Dict[str, T.List[str]], use_tmp: bool) -> T.List[T.Tuple[str, T.List[TestDef], bool]]:
"""
Parameters
@@ -1089,6 +1096,7 @@ def detect_tests_to_run(only: T.Dict[str, T.List[str]], use_tmp: bool) -> T.List
TestCategory('frameworks', 'frameworks'),
TestCategory('nasm', 'nasm'),
TestCategory('wasm', 'wasm', shutil.which('emcc') is None or backend is not Backend.ninja),
+ TestCategory('wayland', 'wayland', should_skip_wayland()),
]
categories = [t.category for t in all_tests]
diff --git a/test cases/cmake/20 cmake file/meson.build b/test cases/cmake/20 cmake file/meson.build
index 758bbee..5c45d66 100644
--- a/test cases/cmake/20 cmake file/meson.build
+++ b/test cases/cmake/20 cmake file/meson.build
@@ -4,11 +4,9 @@ project(
cmake = import('cmake')
-cmake_conf = configuration_data()
-cmake_conf.set_quoted('foo', 'bar')
cmake.configure_package_config_file(
name : 'foolib',
input : 'foolib.cmake.in',
install_dir : get_option('libdir') / 'cmake',
- configuration : cmake_conf,
+ configuration : {'foo': '"bar"'},
)
diff --git a/test cases/vala/14 target glib version and gresources/test.vala b/test cases/vala/14 target glib version and gresources/test.vala
index 79ef47d..f10bbe4 100644
--- a/test cases/vala/14 target glib version and gresources/test.vala
+++ b/test cases/vala/14 target glib version and gresources/test.vala
@@ -9,7 +9,7 @@ public class TestWidget : Box {
}
[GtkChild]
- private Entry entry;
+ private unowned Entry entry;
public TestWidget (string text) {
this.text = text;
diff --git a/test cases/wayland/1 client/main.c b/test cases/wayland/1 client/main.c
new file mode 100644
index 0000000..6aca80d
--- /dev/null
+++ b/test cases/wayland/1 client/main.c
@@ -0,0 +1,9 @@
+#include "xdg-shell-client-protocol.h"
+
+int main() {
+#ifdef XDG_SHELL_CLIENT_PROTOCOL_H
+ return 0;
+#else
+ return 1;
+#endif
+}
diff --git a/test cases/wayland/1 client/meson.build b/test cases/wayland/1 client/meson.build
new file mode 100644
index 0000000..7ca868b
--- /dev/null
+++ b/test cases/wayland/1 client/meson.build
@@ -0,0 +1,16 @@
+project('wayland-test-client', 'c')
+
+wl_protocols_dep = dependency('wayland-protocols', required : false)
+if not wl_protocols_dep.found()
+ error('MESON_SKIP_TEST: wayland-protocols not installed')
+endif
+
+wl_dep = dependency('wayland-client')
+wl_mod = import('unstable-wayland')
+
+xdg_shell_xml = wl_mod.find_protocol('xdg-shell')
+xdg_shell = wl_mod.scan_xml(xdg_shell_xml)
+
+exe = executable('client', 'main.c', xdg_shell, dependencies : wl_dep)
+
+test('client', exe)
diff --git a/test cases/wayland/2 server/main.c b/test cases/wayland/2 server/main.c
new file mode 100644
index 0000000..3307499
--- /dev/null
+++ b/test cases/wayland/2 server/main.c
@@ -0,0 +1,9 @@
+#include "xdg-shell-server-protocol.h"
+
+int main() {
+#ifdef XDG_SHELL_SERVER_PROTOCOL_H
+ return 0;
+#else
+ return 1;
+#endif
+}
diff --git a/test cases/wayland/2 server/meson.build b/test cases/wayland/2 server/meson.build
new file mode 100644
index 0000000..c93ff11
--- /dev/null
+++ b/test cases/wayland/2 server/meson.build
@@ -0,0 +1,16 @@
+project('wayland-test-server', 'c')
+
+wl_protocols_dep = dependency('wayland-protocols', required : false)
+if not wl_protocols_dep.found()
+ error('MESON_SKIP_TEST: wayland-protocols not installed')
+endif
+
+wl_dep = dependency('wayland-server')
+wl_mod = import('unstable-wayland')
+
+xdg_shell_xml = wl_mod.find_protocol('xdg-shell')
+xdg_shell = wl_mod.scan_xml(xdg_shell_xml, side : 'server')
+
+exe = executable('server', 'main.c', xdg_shell, dependencies : wl_dep)
+
+test('client', exe)
diff --git a/test cases/wayland/3 local/main.c b/test cases/wayland/3 local/main.c
new file mode 100644
index 0000000..97bfa56
--- /dev/null
+++ b/test cases/wayland/3 local/main.c
@@ -0,0 +1,9 @@
+#include "test-client-protocol.h"
+
+int main() {
+#ifdef TEST_CLIENT_PROTOCOL_H
+ return 0;
+#else
+ return 1;
+#endif
+}
diff --git a/test cases/wayland/3 local/meson.build b/test cases/wayland/3 local/meson.build
new file mode 100644
index 0000000..7a470d6
--- /dev/null
+++ b/test cases/wayland/3 local/meson.build
@@ -0,0 +1,11 @@
+project('wayland-test-local', 'c')
+
+wl_dep = dependency('wayland-client')
+wl_mod = import('unstable-wayland')
+
+xmls = files('test.xml')
+gen = wl_mod.scan_xml(xmls)
+
+exe = executable('local', 'main.c', gen, dependencies : wl_dep)
+
+test('local', exe)
diff --git a/test cases/wayland/3 local/test.xml b/test cases/wayland/3 local/test.xml
new file mode 100644
index 0000000..f3c6db1
--- /dev/null
+++ b/test cases/wayland/3 local/test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="test">
+ <interface name="ext_test" version="1">
+ <request name="destroy" type="destructor"/>
+ </interface>
+</protocol>