aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Python-module.md4
-rw-r--r--docs/markdown/Reference-manual.md6
-rw-r--r--docs/markdown/Running-Meson.md257
-rw-r--r--docs/markdown/snippets/introspect_meson_info.md6
-rw-r--r--mesonbuild/backend/backends.py9
-rw-r--r--mesonbuild/backend/ninjabackend.py8
-rw-r--r--mesonbuild/compilers/c.py3
-rw-r--r--mesonbuild/environment.py7
-rw-r--r--mesonbuild/interpreter.py2
-rw-r--r--mesonbuild/mconf.py4
-rw-r--r--mesonbuild/mesonmain.py4
-rw-r--r--mesonbuild/mintro.py191
-rw-r--r--mesonbuild/modules/rpm.py104
-rw-r--r--mesonbuild/msetup.py18
-rw-r--r--mesonbuild/munstable_coredata.py126
-rwxr-xr-xrun_unittests.py14
-rw-r--r--test cases/common/97 test workdir/meson.build2
-rwxr-xr-xtest cases/common/97 test workdir/subdir/checker.py5
-rw-r--r--test cases/common/97 test workdir/subdir/meson.build4
19 files changed, 519 insertions, 255 deletions
diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md
index a50a33d..a963a32 100644
--- a/docs/markdown/Python-module.md
+++ b/docs/markdown/Python-module.md
@@ -220,7 +220,7 @@ It exposes the same methods as its parent class.
[shared_module]: Reference-manual.md#shared_module
[external program]: Reference-manual.md#external-program-object
[dependency]: Reference-manual.md#dependency
-[install_data]: Reference-manual.md#install-data
-[configure_file]: Reference-manual.md#configure-file
+[install_data]: Reference-manual.md#install_data
+[configure_file]: Reference-manual.md#configure_file
[dependency object]: Reference-manual.md#dependency-object
[buildtarget object]: Reference-manual.md#build-target-object
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 0ddd4a9..3d4379a 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -565,6 +565,8 @@ be passed to [shared and static libraries](#library).
- `d_module_versions` list of module version identifiers set when compiling D sources
- `d_debug` list of module debug identifiers set when compiling D sources
- `pie` *(added 0.49.0)* build a position-independent executable
+- `native`, is a boolean controlling whether the target is compiled for the
+ build or host machines. Defaults to false, building for the host machine.
The list of `sources`, `objects`, and `dependencies` is always
flattened, which means you can freely nest and add lists while
@@ -1107,8 +1109,8 @@ This function prints its argument to stdout prefixed with WARNING:.
The first argument to this function must be a string defining the name
of this project. It is followed by programming languages that the
project uses. Supported values for languages are `c`, `cpp` (for
-`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and
-`vala`. In versions before `0.40.0` you must have at least one
+`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`),
+`vala` and `rust`. In versions before `0.40.0` you must have at least one
language listed.
The project name can be any string you want, it's not used for
diff --git a/docs/markdown/Running-Meson.md b/docs/markdown/Running-Meson.md
index 426e87d..910513c 100644
--- a/docs/markdown/Running-Meson.md
+++ b/docs/markdown/Running-Meson.md
@@ -4,49 +4,45 @@ short-description: Building a project with Meson
# Running Meson
-There are two different ways of invoking Meson. First, you can run it
-directly from the source tree with the command
-`/path/to/source/meson.py`. Meson may also be installed in which case
-the command is simply `meson`. In this manual we only use the latter
-format for simplicity.
+There are two different ways of invoking Meson. First, you can run it directly
+from the source tree with the command `/path/to/source/meson.py`. Meson may
+also be installed in which case the command is simply `meson`. In this manual
+we only use the latter format for simplicity.
-Additionally, the invocation can pass options to meson.
-The list of options is documented [here](Builtin-options.md).
+Additionally, the invocation can pass options to meson. The list of options is
+documented [here](Builtin-options.md).
-At the time of writing only a command line version of Meson is
-available. This means that Meson must be invoked using the
-terminal. If you wish to use the MSVC compiler, you need to run Meson
-under "Visual Studio command prompt".
+At the time of writing only a command line version of Meson is available. This
+means that Meson must be invoked using the terminal. If you wish to use the
+MSVC compiler, you need to run Meson under "Visual Studio command prompt".
-Configuring the source
-==
+## Configuring the build directory
-Let us assume that we have a source tree that has a Meson build
-system. This means that at the topmost directory has a file called
-`meson.build`. We run the following commands to get the build started.
+Let us assume that we have a source tree that has a Meson build system. This
+means that at the topmost directory has a file called `meson.build`. We run the
+following commands to get the build started.
+```sh
+cd /path/to/source/root
+meson setup builddir
+```
- cd /path/to/source/root
- mkdir builddir
- cd builddir
- meson ..
+We invoke Meson with the `setup` command, giving it the location of the build
+directory. Meson uses [out of source
+builds](http://voices.canonical.com/jussi.pakkanen/2013/04/16/why-you-should-consider-using-separate-build-directories/).
-First we create a directory to hold all files generated during the
-build. Then we go into it and invoke Meson, giving it the location of
-the source root.
+Hint: The syntax of meson is `meson [command] [arguments] [options]`. The
+`setup` command takes a `builddir` and a `srcdir` argument. If no `srcdir` is
+given Meson will deduce the `srcdir` based on `pwd` and the location of
+`meson.build`.
-Hint: The syntax of meson is `meson [options] [srcdir] [builddir]`,
-but you may omit either `srcdir` or `builddir`. Meson will deduce the
-`srcdir` by the location of `meson.build`. The other one will be your
-`pwd`.
+Meson then loads the build configuration file and writes the corresponding
+build backend in the build directory. By default Meson generates a *debug
+build*, which turns on basic warnings and debug information and disables
+compiler optimizations.
-Meson then loads the build configuration file and writes the
-corresponding build backend in the build directory. By default Meson
-generates a *debug build*, which turns on basic warnings and debug
-information and disables compiler optimizations.
-
-You can specify a different type of build with the `--buildtype`
-command line argument. It can have one of the following values.
+You can specify a different type of build with the `--buildtype` command line
+argument. It can have one of the following values.
| value | meaning |
| ------ | -------- |
@@ -55,122 +51,123 @@ command line argument. It can have one of the following values.
| `debugoptimized` | debug info is generated and the code is optimized (on most compilers this means `-g -O2`) |
| `release` | full optimization, no debug info |
-The build directory is mandatory. The reason for this is that it
-simplifies the build process immensely. Meson will not under any
-circumstances write files inside the source directory (if it does, it
-is a bug and should be fixed). This means that the user does not need
-to add a bunch of files to their revision control's ignore list. It
-also means that you can create arbitrarily many build directories for
-any given source tree. If we wanted to test building the source code
-with the Clang compiler instead of the system default, we could just
-type the following commands.
-
- cd /path/to/source/root
- mkdir buildclang
- cd buildclang
- CC=clang CXX=clang++ meson ..
-
-This separation is even more powerful if your code has multiple
-configuration options (such as multiple data backends). You can create
-a separate subdirectory for each of them. You can also have build
-directories for optimized builds, code coverage, static analysis and
-so on. They are all neatly separated and use the same source
-tree. Changing between different configurations is just a question of
-changing to the corresponding directory.
-
-Unless otherwise mentioned, all following command line invocations are
-meant to be run in the build directory.
-
-By default Meson will use the Ninja backend to build your project. If
-you wish to use any of the other backends, you need to pass the
-corresponding argument during configuration time. As an example, here
-is how you would use Meson to generate a Visual studio solution.
-
- meson <source dir> <build dir> --backend=vs2010
-
-You can then open the generated solution with Visual Studio and
-compile it in the usual way. A list of backends can be obtained with
-`meson --help`.
-
-Environment Variables
---
-
-Sometimes you want to add extra compiler flags, this can be done by
-passing them in environment variables when calling meson. See [the
-reference
-tables](Reference-tables.md#compiler-and-linker-flag-envrionment-variables)
-for a list of all the environment variables. Be aware however these
-environment variables are only used for the native compiler and will
-not affect the compiler used for cross-compiling, where the flags
-specified in the cross file will be used.
-
-Furthermore it is possible to stop meson from adding flags itself by
-using the `--buildtype=plain` option, in this case you must provide
-the full compiler and linker arguments needed.
-
-Building the source
-==
+The build directory is mandatory. The reason for this is that it simplifies the
+build process immensely. Meson will not under any circumstances write files
+inside the source directory (if it does, it is a bug and should be fixed). This
+means that the user does not need to add a bunch of files to their revision
+control's ignore list. It also means that you can create arbitrarily many build
+directories for any given source tree.
+
+For example, if we wanted to test building the source code with the Clang
+compiler instead of the system default, we could just type the following
+commands:
+
+```sh
+cd /path/to/source/root
+CC=clang CXX=clang++ meson setup buildclang
+```
+
+This separation is even more powerful if your code has multiple configuration
+options (such as multiple data backends). You can create a separate
+subdirectory for each of them. You can also have build directories for
+optimized builds, code coverage, static analysis and so on. They are all neatly
+separated and use the same source tree. Changing between different
+configurations is just a question of changing to the corresponding directory.
+
+Unless otherwise mentioned, all following command line invocations are meant to
+be run in the source directory.
+
+By default Meson will use the Ninja backend to build your project. If you wish
+to use any of the other backends, you need to pass the corresponding argument
+during configuration time. As an example, here is how you would use Meson to
+generate a Visual studio solution.
+
+```sh
+meson setup <build dir> --backend=vs2010
+```
+
+You can then open the generated solution with Visual Studio and compile it in
+the usual way. A list of backends can be obtained with `meson setup --help`.
+
+## Environment variables
+
+Sometimes you want to add extra compiler flags, this can be done by passing
+them in environment variables when calling meson. See [the reference
+tables](Reference-tables.md#compiler-and-linker-flag-envrionment-variables) for
+a list of all the environment variables. Be aware however these environment
+variables are only used for the native compiler and will not affect the
+compiler used for cross-compiling, where the flags specified in the cross file
+will be used.
+
+Furthermore it is possible to stop meson from adding flags itself by using the
+`--buildtype=plain` option, in this case you must provide the full compiler and
+linker arguments needed.
+
+## Building from the source
If you are not using an IDE, Meson uses the [Ninja build
-system](https://ninja-build.org/) to actually build the code. To start
-the build, simply type the following command.
+system](https://ninja-build.org/) to actually build the code. To start the
+build, simply type the following command.
- ninja
+```sh
+ninja -C builddir
+```
-The main usability difference between Ninja and Make is that Ninja
-will automatically detect the number of CPUs in your computer and
-parallelize itself accordingly. You can override the amount of
-parallel processes used with the command line argument `-j <num
-processes>`.
+The main usability difference between Ninja and Make is that Ninja will
+automatically detect the number of CPUs in your computer and parallelize itself
+accordingly. You can override the amount of parallel processes used with the
+command line argument `-j <num processes>`.
-It should be noted that after the initial configure step `ninja` is
-the only command you ever need to type to compile. No matter how you
-alter your source tree (short of moving it to a completely new
-location), Meson will detect the changes and regenerate itself
-accordingly. This is especially handy if you have multiple build
-directories. Often one of them is used for development (the "debug"
-build) and others only every now and then (such as a "static analysis"
-build). Any configuration can be built just by `cd`'ing to the
-corresponding directory and running Ninja.
+It should be noted that after the initial configure step `ninja` is the only
+command you ever need to type to compile. No matter how you alter your source
+tree (short of moving it to a completely new location), Meson will detect the
+changes and regenerate itself accordingly. This is especially handy if you have
+multiple build directories. Often one of them is used for development (the
+"debug" build) and others only every now and then (such as a "static analysis"
+build). Any configuration can be built just by `cd`'ing to the corresponding
+directory and running Ninja.
-Running tests
-==
+## Running tests
-Meson provides native support for running tests. The command to do
-that is simple.
+Meson provides native support for running tests. The command to do that is
+simple.
- ninja test
+```sh
+ninja -C builddir test
+```
-Meson does not force the use of any particular testing framework. You
-are free to use GTest, Boost Test, Check or even custom executables.
+Meson does not force the use of any particular testing framework. You are free
+to use GTest, Boost Test, Check or even custom executables.
-Installing
-==
+## Installing
Installing the built software is just as simple.
- ninja install
+```sh
+ninja -C builddir install
+```
Note that Meson will only install build targets explicitly tagged as
-installable, as detailed in the [installing targets documentation](Installing.md).
+installable, as detailed in the [installing targets
+documentation](Installing.md).
-By default Meson installs to `/usr/local`. This can be changed by
-passing the command line argument `--prefix /your/prefix` to Meson
-during configure time. Meson also supports the `DESTDIR` variable used
-in e.g. building packages. It is used like this:
+By default Meson installs to `/usr/local`. This can be changed by passing the
+command line argument `--prefix /your/prefix` to Meson during configure time.
+Meson also supports the `DESTDIR` variable used in e.g. building packages. It
+is used like this:
- DESTDIR=/path/to/staging ninja install
+```sh
+DESTDIR=/path/to/staging ninja -C builddir install
+```
-Command line help
-==
+## Command line help
-Meson has a standard command line help feature. It can be accessed
-with the following command.
+Meson has a standard command line help feature. It can be accessed with the
+following command.
meson --help
-Exit status
-==
+## Exit status
-Meson exits with status 0 if successful, 1 for problems with the command line or
-meson.build file, and 2 for internal errors.
+Meson exits with status 0 if successful, 1 for problems with the command line
+or meson.build file, and 2 for internal errors.
diff --git a/docs/markdown/snippets/introspect_meson_info.md b/docs/markdown/snippets/introspect_meson_info.md
new file mode 100644
index 0000000..42f2fda
--- /dev/null
+++ b/docs/markdown/snippets/introspect_meson_info.md
@@ -0,0 +1,6 @@
+## Added the `meson-info.json` introspection file
+
+Meson now generates a `meson-info.json` file in the `meson-info` directory
+to provide introspection information about the latest meson run. This file
+is updated when the build configuration is changed and the build files are
+(re)generated.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 39aa365..0637905 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -725,7 +725,7 @@ class Backend:
elif isinstance(a, str):
cmd_args.append(a)
elif isinstance(a, build.Target):
- cmd_args.append(self.get_target_filename(a))
+ cmd_args.append(self.construct_target_rel_path(a, t.workdir))
else:
raise MesonException('Bad object in test command.')
ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross,
@@ -737,6 +737,13 @@ class Backend:
def write_test_serialisation(self, tests, datafile):
pickle.dump(self.create_test_serialisation(tests), datafile)
+ def construct_target_rel_path(self, a, workdir):
+ if workdir is None:
+ return self.get_target_filename(a)
+ assert(os.path.isabs(workdir))
+ abs_path = self.get_target_filename_abs(a)
+ return os.path.relpath(abs_path, workdir)
+
def generate_depmf_install(self, d):
if self.build.dep_manifest_name is None:
return
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 3688f29..debb4fb 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1473,7 +1473,7 @@ int dummy;
command_template = ' command = {executable} $ARGS {output_args} $in $LINK_ARGS {cross_args} $aliasing\n'
command = command_template.format(
executable=' '.join(compiler.get_linker_exelist()),
- cross_args=' '.join(cross_args),
+ cross_args=' '.join([quote_func(i) for i in cross_args]),
output_args=' '.join(compiler.get_linker_output_args('$out'))
)
description = ' description = Linking target $out.\n'
@@ -1601,7 +1601,7 @@ rule FORTRAN_DEP_HACK%s
command_template = ' command = {executable} $ARGS {cross_args} {output_args} {compile_only_args} $in\n'
command = command_template.format(
executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
- cross_args=' '.join(compiler.get_cross_extra_flags(self.environment, False)) if is_cross else '',
+ cross_args=' '.join([quote_func(i) for i in compiler.get_cross_extra_flags(self.environment, False)]) if is_cross else '',
output_args=' '.join(compiler.get_output_args('$out')),
compile_only_args=' '.join(compiler.get_compile_only_args())
)
@@ -1659,7 +1659,7 @@ rule FORTRAN_DEP_HACK%s
command_template = ' command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n'
command = command_template.format(
executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
- cross_args=' '.join(cross_args),
+ cross_args=' '.join([quote_func(i) for i in cross_args]),
dep_args=' '.join(quoted_depargs),
output_args=' '.join(compiler.get_output_args('$out')),
compile_only_args=' '.join(compiler.get_compile_only_args())
@@ -1703,7 +1703,7 @@ rule FORTRAN_DEP_HACK%s
output = ' '.join(compiler.get_output_args('$out'))
command = " command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n".format(
executable=' '.join(compiler.get_exelist()),
- cross_args=' '.join(cross_args),
+ cross_args=' '.join([quote_func(i) for i in cross_args]),
dep_args=' '.join(quoted_depargs),
output_args=output,
compile_only_args=' '.join(compiler.get_compile_only_args())
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 6350eee..f7864c0 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -1659,6 +1659,9 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
def get_linker_output_args(self, outputname):
return ['-output=%s' % outputname]
+ def get_werror_args(self):
+ return ['-change_message=error']
+
def get_include_args(self, path, is_system):
if path == '':
path = '.'
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index e99174c..4bf77ac 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -246,6 +246,10 @@ def detect_cpu_family(compilers):
trial = 'arm'
# Add more quirks here as bugs are reported. Keep in sync with detect_cpu()
# below.
+ elif trial == 'parisc64':
+ # ATM there is no 64 bit userland for PA-RISC. Thus always
+ # report it as 32 bit for simplicity.
+ trial = 'parisc'
if trial not in known_cpu_families:
mlog.warning('Unknown CPU family {!r}, please report this at '
@@ -1019,7 +1023,8 @@ class Environment:
return comp, cross_comp
def detect_static_linker(self, compiler):
- linker = self.binaries.host.lookup_entry('ar')
+ for_machine = MachineChoice.HOST if compiler.is_cross else MachineChoice.BUILD
+ linker = self.binaries[for_machine].lookup_entry('ar')
if linker is not None:
linkers = [linker]
else:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 9ebce70..83ca336 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2713,8 +2713,6 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log('Cross', cross_comp.get_display_language(), 'compiler:',
mlog.bold(' '.join(cross_comp.get_exelist())), version_string)
self.build.add_cross_compiler(cross_comp)
- if self.environment.is_cross_build() and not need_cross_compiler:
- self.build.add_cross_compiler(comp)
self.add_base_options(comp)
return success
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 2863b0c..b8fb3c6 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -148,6 +148,7 @@ class Conf:
def run(options):
coredata.parse_cmd_line_options(options)
builddir = os.path.abspath(os.path.realpath(options.builddir))
+ c = None
try:
c = Conf(builddir)
save = False
@@ -163,7 +164,10 @@ def run(options):
if save:
c.save()
mintro.update_build_options(c.coredata, c.build.environment.info_dir)
+ mintro.write_meson_info_file(c.build, [])
except ConfException as e:
print('Meson configurator encountered an error:')
+ if c is not None and c.build is not None:
+ mintro.write_meson_info_file(c.build, [e])
raise e
return 0
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index c11d044..7236d1a 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -20,7 +20,7 @@ import argparse
from . import mesonlib
from . import mlog
-from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects
+from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata
from .mesonlib import MesonException
from .environment import detect_msys2_arch
from .wrap import wraptool
@@ -57,6 +57,8 @@ class CommandLineParser:
help=argparse.SUPPRESS)
self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command,
help=argparse.SUPPRESS)
+ self.add_command('unstable-coredata', munstable_coredata.add_arguments, munstable_coredata.run,
+ help=argparse.SUPPRESS)
def add_command(self, name, add_arguments_func, run_func, help):
# FIXME: Cannot have hidden subparser:
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index 3382e0d..df9fe73 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -33,25 +33,64 @@ from .backend import backends
import sys, os
import pathlib
+def get_meson_info_file(info_dir: str):
+ return os.path.join(info_dir, 'meson-info.json')
+
+def get_meson_introspection_version():
+ return '1.0.0'
+
+def get_meson_introspection_types(coredata: cdata.CoreData = None, builddata: build.Build = None, backend: backends.Backend = None):
+ if backend and builddata:
+ benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks())
+ testdata = backend.create_test_serialisation(builddata.get_tests())
+ installdata = backend.create_install_data()
+ else:
+ benchmarkdata = testdata = installdata = None
+
+ return {
+ 'benchmarks': {
+ 'func': lambda: list_benchmarks(benchmarkdata),
+ 'desc': 'List all benchmarks.',
+ },
+ 'buildoptions': {
+ 'func': lambda: list_buildoptions(coredata),
+ 'desc': 'List all build options.',
+ },
+ 'buildsystem_files': {
+ 'func': lambda: list_buildsystem_files(builddata),
+ 'desc': 'List files that make up the build system.',
+ 'key': 'buildsystem-files',
+ },
+ 'dependencies': {
+ 'func': lambda: list_deps(coredata),
+ 'desc': 'List external dependencies.',
+ },
+ 'installed': {
+ 'func': lambda: list_installed(installdata),
+ 'desc': 'List all installed files and directories.',
+ },
+ 'projectinfo': {
+ 'func': lambda: list_projinfo(builddata),
+ 'desc': 'Information about projects.',
+ },
+ 'targets': {
+ 'func': lambda: list_targets(builddata, installdata, backend),
+ 'desc': 'List top level targets.',
+ },
+ 'tests': {
+ 'func': lambda: list_tests(testdata),
+ 'desc': 'List all unit tests.',
+ }
+ }
+
def add_arguments(parser):
- parser.add_argument('--targets', action='store_true', dest='list_targets', default=False,
- help='List top level targets.')
- parser.add_argument('--installed', action='store_true', dest='list_installed', default=False,
- help='List all installed files and directories.')
+ intro_types = get_meson_introspection_types()
+ for key, val in intro_types.items():
+ flag = '--' + val.get('key', key)
+ parser.add_argument(flag, action='store_true', dest=key, default=False, help=val['desc'])
+
parser.add_argument('--target-files', action='store', dest='target_files', default=None,
help='List source files for a given target.')
- parser.add_argument('--buildsystem-files', action='store_true', dest='buildsystem_files', default=False,
- help='List files that make up the build system.')
- parser.add_argument('--buildoptions', action='store_true', dest='buildoptions', default=False,
- help='List all build options.')
- parser.add_argument('--tests', action='store_true', dest='tests', default=False,
- help='List all unit tests.')
- parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False,
- help='List all benchmarks.')
- parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False,
- help='List external dependencies.')
- parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False,
- help='Information about projects.')
parser.add_argument('--backend', choices=cdata.backendlist, dest='backend', default='ninja',
help='The backend to use for the --buildoptions introspection.')
parser.add_argument('-a', '--all', action='store_true', dest='all', default=False,
@@ -74,7 +113,7 @@ def list_installed(installdata):
res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
for path, installpath, unused_custom_install_mode in installdata.man:
res[path] = os.path.join(installdata.prefix, installpath)
- return ('installed', res)
+ return res
def list_targets(builddata: build.Build, installdata, backend: backends.Backend):
tlist = []
@@ -110,7 +149,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
else:
t['installed'] = False
tlist.append(t)
- return ('targets', tlist)
+ return tlist
class BuildoptionsOptionHelper:
# mimic an argparse namespace
@@ -178,7 +217,7 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter):
return flattend_args
def add_languages(self, args):
- need_cross_compiler = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler()
+ need_cross_compiler = self.environment.is_cross_build()
for lang in sorted(args, key=compilers.sort_clink):
lang = lang.lower()
if lang not in self.coredata.compilers:
@@ -257,8 +296,7 @@ def list_buildoptions_from_source(sourcedir, backend, indent):
intr.analyze()
# Reenable logging just in case
mlog.enable()
- buildoptions = list_buildoptions(intr.coredata)[1]
- print(json.dumps(buildoptions, indent=indent))
+ print(json.dumps(list_buildoptions(intr.coredata), indent=indent))
def list_target_files(target_name, targets, builddata: build.Build):
result = []
@@ -279,7 +317,7 @@ def list_target_files(target_name, targets, builddata: build.Build):
# TODO Remove this line in a future PR with other breaking changes
result = list(map(lambda x: os.path.relpath(x, builddata.environment.get_source_dir()), result))
- return ('target_files', result)
+ return result
def list_buildoptions(coredata: cdata.CoreData):
optlist = []
@@ -312,7 +350,7 @@ def list_buildoptions(coredata: cdata.CoreData):
add_keys(optlist, dir_options, 'directory')
add_keys(optlist, coredata.user_options, 'user')
add_keys(optlist, test_options, 'test')
- return ('buildoptions', optlist)
+ return optlist
def add_keys(optlist, options, section):
keys = list(options.keys())
@@ -349,7 +387,7 @@ def find_buildsystem_files_list(src_dir):
def list_buildsystem_files(builddata: build.Build):
src_dir = builddata.environment.get_source_dir()
filelist = find_buildsystem_files_list(src_dir)
- return ('buildsystem_files', filelist)
+ return filelist
def list_deps(coredata: cdata.CoreData):
result = []
@@ -358,7 +396,7 @@ def list_deps(coredata: cdata.CoreData):
result += [{'name': d.name,
'compile_args': d.get_compile_args(),
'link_args': d.get_link_args()}]
- return ('dependencies', result)
+ return result
def get_test_list(testdata):
result = []
@@ -382,10 +420,10 @@ def get_test_list(testdata):
return result
def list_tests(testdata):
- return ('tests', get_test_list(testdata))
+ return get_test_list(testdata)
def list_benchmarks(benchdata):
- return ('benchmarks', get_test_list(benchdata))
+ return get_test_list(benchdata)
def list_projinfo(builddata: build.Build):
result = {'version': builddata.project_version,
@@ -397,7 +435,7 @@ def list_projinfo(builddata: build.Build):
'descriptive_name': builddata.projects.get(k)}
subprojects.append(c)
result['subprojects'] = subprojects
- return ('projectinfo', result)
+ return result
class ProjectInfoInterperter(astinterpreter.AstInterpreter):
def __init__(self, source_root, subdir):
@@ -482,30 +520,21 @@ def run(options):
results = []
toextract = []
+ intro_types = get_meson_introspection_types()
+
+ for i in intro_types.keys():
+ if options.all or getattr(options, i, False):
+ toextract += [i]
- if options.all or options.benchmarks:
- toextract += ['benchmarks']
- if options.all or options.buildoptions:
- toextract += ['buildoptions']
- if options.all or options.buildsystem_files:
- toextract += ['buildsystem_files']
- if options.all or options.dependencies:
- toextract += ['dependencies']
- if options.all or options.list_installed:
- toextract += ['installed']
- if options.all or options.projectinfo:
- toextract += ['projectinfo']
- if options.all or options.list_targets:
- toextract += ['targets']
+ # Handle the one option that does not have its own JSON file (meybe deprecate / remove this?)
if options.target_files is not None:
targets_file = os.path.join(infodir, 'intro-targets.json')
with open(targets_file, 'r') as fp:
targets = json.load(fp)
builddata = build.load(options.builddir) # TODO remove this in a breaking changes PR
- results += [list_target_files(options.target_files, targets, builddata)]
- if options.all or options.tests:
- toextract += ['tests']
+ results += [('target_files', list_target_files(options.target_files, targets, builddata))]
+ # Extract introspection information from JSON
for i in toextract:
curr = os.path.join(infodir, 'intro-{}.json'.format(i))
if not os.path.isfile(curr):
@@ -527,7 +556,10 @@ def run(options):
print(json.dumps(out, indent=indent))
return 0
+updated_introspection_files = []
+
def write_intro_info(intro_info, info_dir):
+ global updated_introspection_files
for i in intro_info:
out_file = os.path.join(info_dir, 'intro-{}.json'.format(i[0]))
tmp_file = os.path.join(info_dir, 'tmp_dump.json')
@@ -535,29 +567,70 @@ def write_intro_info(intro_info, info_dir):
json.dump(i[1], fp)
fp.flush() # Not sure if this is needed
os.replace(tmp_file, out_file)
+ updated_introspection_files += [i[0]]
def generate_introspection_file(builddata: build.Build, backend: backends.Backend):
coredata = builddata.environment.get_coredata()
- benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks())
- testdata = backend.create_test_serialisation(builddata.get_tests())
- installdata = backend.create_install_data()
+ intro_types = get_meson_introspection_types(coredata=coredata, builddata=builddata, backend=backend)
+ intro_info = []
- intro_info = [
- list_benchmarks(benchmarkdata),
- list_buildoptions(coredata),
- list_buildsystem_files(builddata),
- list_deps(coredata),
- list_installed(installdata),
- list_projinfo(builddata),
- list_targets(builddata, installdata, backend),
- list_tests(testdata)
- ]
+ for key, val in intro_types.items():
+ intro_info += [(key, val['func']())]
write_intro_info(intro_info, builddata.environment.info_dir)
def update_build_options(coredata: cdata.CoreData, info_dir):
intro_info = [
- list_buildoptions(coredata)
+ ('buildoptions', list_buildoptions(coredata))
]
write_intro_info(intro_info, info_dir)
+
+def split_version_string(version: str):
+ vers_list = version.split('.')
+ return {
+ 'full': version,
+ 'major': int(vers_list[0] if len(vers_list) > 0 else 0),
+ 'minor': int(vers_list[1] if len(vers_list) > 1 else 0),
+ 'patch': int(vers_list[2] if len(vers_list) > 2 else 0)
+ }
+
+def write_meson_info_file(builddata: build.Build, errors: list, build_files_updated: bool = False):
+ global updated_introspection_files
+ info_dir = builddata.environment.info_dir
+ info_file = get_meson_info_file(info_dir)
+ intro_types = get_meson_introspection_types()
+ intro_info = {}
+
+ for i in intro_types.keys():
+ intro_info[i] = {
+ 'file': 'intro-{}.json'.format(i),
+ 'updated': i in updated_introspection_files
+ }
+
+ info_data = {
+ 'meson_version': split_version_string(cdata.version),
+ 'directories': {
+ 'source': builddata.environment.get_source_dir(),
+ 'build': builddata.environment.get_build_dir(),
+ 'info': info_dir,
+ },
+ 'introspection': {
+ 'version': split_version_string(get_meson_introspection_version()),
+ 'information': intro_info,
+ },
+ 'build_files_updated': build_files_updated,
+ }
+
+ if len(errors) > 0:
+ info_data['error'] = True
+ info_data['error_list'] = [x if isinstance(x, str) else str(x) for x in errors]
+ else:
+ info_data['error'] = False
+
+ # Write the data to disc
+ tmp_file = os.path.join(info_dir, 'tmp_dump.json')
+ with open(tmp_file, 'w') as fp:
+ json.dump(info_data, fp)
+ fp.flush()
+ os.replace(tmp_file, info_file)
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
index ba5bcaa..9774286 100644
--- a/mesonbuild/modules/rpm.py
+++ b/mesonbuild/modules/rpm.py
@@ -29,39 +29,16 @@ import os
class RPMModule(ExtensionModule):
@noKwargs
- def generate_spec_template(self, state, args, kwargs):
- compiler_deps = set()
- for compiler in state.compilers.values():
- # Elbrus has one 'lcc' package for every compiler
- if isinstance(compiler, compilers.GnuCCompiler):
- compiler_deps.add('gcc')
- elif isinstance(compiler, compilers.GnuCPPCompiler):
- compiler_deps.add('gcc-c++')
- elif isinstance(compiler, compilers.ElbrusCCompiler):
- compiler_deps.add('lcc')
- elif isinstance(compiler, compilers.ElbrusCPPCompiler):
- compiler_deps.add('lcc')
- elif isinstance(compiler, compilers.ElbrusFortranCompiler):
- compiler_deps.add('lcc')
- elif isinstance(compiler, compilers.ValaCompiler):
- compiler_deps.add('vala')
- elif isinstance(compiler, compilers.GnuFortranCompiler):
- compiler_deps.add('gcc-gfortran')
- elif isinstance(compiler, compilers.GnuObjCCompiler):
- compiler_deps.add('gcc-objc')
- elif compiler == compilers.GnuObjCPPCompiler:
- compiler_deps.add('gcc-objc++')
- else:
- mlog.log('RPM spec file will not created, generating not allowed for:',
- mlog.bold(compiler.get_id()))
- return
- proj = state.project_name.replace(' ', '_').replace('\t', '_')
+ def generate_spec_template(self, coredata, args, kwargs):
+ self.coredata = coredata
+ required_compilers = self.__get_required_compilers()
+ proj = coredata.project_name.replace(' ', '_').replace('\t', '_')
so_installed = False
devel_subpkg = False
files = set()
files_devel = set()
to_delete = set()
- for target in state.targets.values():
+ for target in coredata.targets.values():
if isinstance(target, build.Executable) and target.need_install:
files.add('%%{_bindir}/%s' % target.get_filename())
elif isinstance(target, build.SharedLibrary) and target.need_install:
@@ -80,18 +57,19 @@ class RPMModule(ExtensionModule):
files_devel.add('%%{_datadir}/gir-1.0/%s' % target.get_filename()[0])
elif isinstance(target, TypelibTarget) and target.should_install():
files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0])
- for header in state.headers:
+ for header in coredata.headers:
if len(header.get_install_subdir()) > 0:
files_devel.add('%%{_includedir}/%s/' % header.get_install_subdir())
else:
for hdr_src in header.get_sources():
files_devel.add('%%{_includedir}/%s' % hdr_src)
- for man in state.man:
+ for man in coredata.man:
for man_file in man.get_sources():
files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file))
if len(files_devel) > 0:
devel_subpkg = True
- filename = os.path.join(state.environment.get_build_dir(),
+
+ filename = os.path.join(coredata.environment.get_build_dir(),
'%s.spec' % proj)
with open(filename, 'w+') as fn:
fn.write('Name: %s\n' % proj)
@@ -102,24 +80,28 @@ class RPMModule(ExtensionModule):
fn.write('\n')
fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n')
fn.write('\n')
- for compiler in compiler_deps:
+ fn.write('BuildRequires: meson\n')
+ for compiler in required_compilers:
fn.write('BuildRequires: %s\n' % compiler)
- for dep in state.environment.coredata.deps:
+ for dep in coredata.environment.coredata.deps:
fn.write('BuildRequires: pkgconfig(%s)\n' % dep[0])
- for lib in state.environment.coredata.ext_libs.values():
- name = lib.get_name()
- fn.write('BuildRequires: {} # FIXME\n'.format(name))
- mlog.warning('replace', mlog.bold(name), 'with the real package.',
- 'You can use following command to find package which '
- 'contains this lib:',
- mlog.bold("dnf provides '*/lib{}.so'".format(name)))
- for prog in state.environment.coredata.ext_progs.values():
- if not prog.found():
- fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' %
- prog.get_name())
- else:
- fn.write('BuildRequires: {}\n'.format(prog.get_path()))
- fn.write('BuildRequires: meson\n')
+# ext_libs and ext_progs have been removed from coredata so the following code
+# no longer works. It is kept as a reminder of the idea should anyone wish
+# to re-implement it.
+#
+# for lib in state.environment.coredata.ext_libs.values():
+# name = lib.get_name()
+# fn.write('BuildRequires: {} # FIXME\n'.format(name))
+# mlog.warning('replace', mlog.bold(name), 'with the real package.',
+# 'You can use following command to find package which '
+# 'contains this lib:',
+# mlog.bold("dnf provides '*/lib{}.so'".format(name)))
+# for prog in state.environment.coredata.ext_progs.values():
+# if not prog.found():
+# fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' %
+# prog.get_name())
+# else:
+# fn.write('BuildRequires: {}\n'.format(prog.get_path()))
fn.write('\n')
fn.write('%description\n')
fn.write('\n')
@@ -167,5 +149,33 @@ class RPMModule(ExtensionModule):
mlog.log('RPM spec template written to %s.spec.\n' % proj)
return ModuleReturnValue(None, [])
+ def __get_required_compilers(self):
+ required_compilers = set()
+ for compiler in self.coredata.compilers.values():
+ # Elbrus has one 'lcc' package for every compiler
+ if isinstance(compiler, compilers.GnuCCompiler):
+ required_compilers.add('gcc')
+ elif isinstance(compiler, compilers.GnuCPPCompiler):
+ required_compilers.add('gcc-c++')
+ elif isinstance(compiler, compilers.ElbrusCCompiler):
+ required_compilers.add('lcc')
+ elif isinstance(compiler, compilers.ElbrusCPPCompiler):
+ required_compilers.add('lcc')
+ elif isinstance(compiler, compilers.ElbrusFortranCompiler):
+ required_compilers.add('lcc')
+ elif isinstance(compiler, compilers.ValaCompiler):
+ required_compilers.add('vala')
+ elif isinstance(compiler, compilers.GnuFortranCompiler):
+ required_compilers.add('gcc-gfortran')
+ elif isinstance(compiler, compilers.GnuObjCCompiler):
+ required_compilers.add('gcc-objc')
+ elif compiler == compilers.GnuObjCPPCompiler:
+ required_compilers.add('gcc-objc++')
+ else:
+ mlog.log('RPM spec file not created, generation not allowed for:',
+ mlog.bold(compiler.get_id()))
+ return required_compilers
+
+
def initialize(*args, **kwargs):
return RPMModule(*args, **kwargs)
diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py
index 67559a1..023afdb 100644
--- a/mesonbuild/msetup.py
+++ b/mesonbuild/msetup.py
@@ -185,11 +185,15 @@ class MesonApp:
mlog.log('Target machine cpu:', mlog.bold(intr.builtin['target_machine'].cpu_method([], {})))
mlog.log('Build machine cpu family:', mlog.bold(intr.builtin['build_machine'].cpu_family_method([], {})))
mlog.log('Build machine cpu:', mlog.bold(intr.builtin['build_machine'].cpu_method([], {})))
- if self.options.profile:
- fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log')
- profile.runctx('intr.run()', globals(), locals(), filename=fname)
- else:
- intr.run()
+ try:
+ if self.options.profile:
+ fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log')
+ profile.runctx('intr.run()', globals(), locals(), filename=fname)
+ else:
+ intr.run()
+ except Exception as e:
+ mintro.write_meson_info_file(b, [e])
+ raise
# Print all default option values that don't match the current value
for def_opt_name, def_opt_value, cur_opt_value in intr.get_non_matching_default_options():
mlog.log('Option', mlog.bold(def_opt_name), 'is:',
@@ -224,7 +228,9 @@ class MesonApp:
profile.runctx('mintro.generate_introspection_file(b, intr.backend)', globals(), locals(), filename=fname)
else:
mintro.generate_introspection_file(b, intr.backend)
- except:
+ mintro.write_meson_info_file(b, [], True)
+ except Exception as e:
+ mintro.write_meson_info_file(b, [e])
if 'cdf' in locals():
old_cdf = cdf + '.prev'
if os.path.exists(old_cdf):
diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py
new file mode 100644
index 0000000..78f3f34
--- /dev/null
+++ b/mesonbuild/munstable_coredata.py
@@ -0,0 +1,126 @@
+# Copyright 2019 The Meson development team
+
+# 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.
+
+
+from . import coredata as cdata
+
+import os.path
+import pprint
+import textwrap
+
+def add_arguments(parser):
+ parser.add_argument('--all', action='store_true', dest='all', default=False,
+ help='Show data not used by current backend.')
+
+ parser.add_argument('builddir', nargs='?', default='.', help='The build directory')
+
+
+def dump_compilers(compilers):
+ for lang, compiler in compilers.items():
+ print(' ' + lang + ':')
+ print(' Id: ' + compiler.id)
+ print(' Command: ' + ' '.join(compiler.exelist))
+ print(' Full version: ' + compiler.full_version)
+ print(' Detected version: ' + compiler.version)
+ print(' Detected type: ' + repr(compiler.compiler_type))
+ #pprint.pprint(compiler.__dict__)
+
+
+def dump_guids(d):
+ for name, value in d.items():
+ print(' ' + name + ': ' + value)
+
+
+def run(options):
+ datadir = 'meson-private'
+ if options.builddir is not None:
+ datadir = os.path.join(options.builddir, datadir)
+ if not os.path.isdir(datadir):
+ print('Current directory is not a build dir. Please specify it or '
+ 'change the working directory to it.')
+ return 1
+
+ all = options.all
+
+ print('This is a dump of the internal unstable cache of meson. This is for debugging only.')
+ print('Do NOT parse, this will change from version to version in incompatible ways')
+ print('')
+
+ coredata = cdata.load(options.builddir)
+ backend = coredata.get_builtin_option('backend')
+ for k, v in sorted(coredata.__dict__.items()):
+ if k in ('backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'):
+ # use `meson configure` to view these
+ pass
+ elif k in ['install_guid', 'test_guid', 'regen_guid']:
+ if all or backend.startswith('vs'):
+ print(k + ': ' + v)
+ elif k == 'target_guids':
+ if all or backend.startswith('vs'):
+ print(k + ':')
+ dump_guids(v)
+ elif k in ['lang_guids']:
+ if all or backend.startswith('vs') or backend == 'xcode':
+ print(k + ':')
+ dump_guids(v)
+ elif k == 'meson_command':
+ if all or backend.startswith('vs'):
+ print('Meson command used in build file regeneration: ' + ' '.join(v))
+ elif k == 'pkgconf_envvar':
+ print('Last seen PKGCONFIG enviroment variable value: ' + v)
+ elif k == 'version':
+ print('Meson version: ' + v)
+ elif k == 'cross_file':
+ print('Cross File: ' + (v or 'None'))
+ elif k == 'config_files':
+ if v:
+ print('Native File: ' + ' '.join(v))
+ elif k == 'compilers':
+ print('Cached native compilers:')
+ dump_compilers(v)
+ elif k == 'cross_compilers':
+ print('Cached cross compilers:')
+ dump_compilers(v)
+ elif k == 'deps':
+ native = []
+ cross = []
+ for dep_key, dep in sorted(v.items()):
+ if dep_key[2]:
+ cross.append((dep_key, dep))
+ else:
+ native.append((dep_key, dep))
+
+ def print_dep(dep_key, dep):
+ print(' ' + dep_key[0] + ": ")
+ print(' compile args: ' + repr(dep.get_compile_args()))
+ print(' link args: ' + repr(dep.get_link_args()))
+ if dep.get_sources():
+ print(' sources: ' + repr(dep.get_sources()))
+ print(' version: ' + repr(dep.get_version()))
+
+ if native:
+ print('Cached native dependencies:')
+ for dep_key, dep in native:
+ print_dep(dep_key, dep)
+ if cross:
+ print('Cached dependencies:')
+ for dep_key, dep in cross:
+ print_dep(dep_key, dep)
+ elif k == 'external_preprocess_args':
+ for lang, opts in v.items():
+ if opts:
+ print('Preprocessor args for ' + lang + ': ' + ' '.join(opts))
+ else:
+ print(k + ':')
+ print(textwrap.indent(pprint.pformat(v), ' '))
diff --git a/run_unittests.py b/run_unittests.py
index f7737ab..55a5bd6 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -3291,6 +3291,20 @@ recommended as it is not supported on some platforms''')
self.assertEqual(res_all, res_file)
+ def test_introspect_meson_info(self):
+ testdir = os.path.join(self.unit_test_dir, '49 introspection')
+ introfile = os.path.join(self.builddir, 'meson-info', 'meson-info.json')
+ self.init(testdir)
+ self.assertPathExists(introfile)
+ with open(introfile, 'r') as fp:
+ res1 = json.load(fp)
+
+ for i in ['meson_version', 'directories', 'introspection', 'build_files_updated', 'error']:
+ self.assertIn(i, res1)
+
+ self.assertEqual(res1['error'], False)
+ self.assertEqual(res1['build_files_updated'], True)
+
def test_introspect_config_update(self):
testdir = os.path.join(self.unit_test_dir, '49 introspection')
introfile = os.path.join(self.builddir, 'meson-info', 'intro-buildoptions.json')
diff --git a/test cases/common/97 test workdir/meson.build b/test cases/common/97 test workdir/meson.build
index 1323a17..a8290f7 100644
--- a/test cases/common/97 test workdir/meson.build
+++ b/test cases/common/97 test workdir/meson.build
@@ -4,3 +4,5 @@ exe = executable('opener', 'opener.c')
test('basic', exe, workdir : meson.source_root())
test('shouldfail', exe, should_fail : true)
+
+subdir('subdir')
diff --git a/test cases/common/97 test workdir/subdir/checker.py b/test cases/common/97 test workdir/subdir/checker.py
new file mode 100755
index 0000000..66e287d
--- /dev/null
+++ b/test cases/common/97 test workdir/subdir/checker.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python3
+
+import sys
+
+data = open(sys.argv[1], 'rb').read()
diff --git a/test cases/common/97 test workdir/subdir/meson.build b/test cases/common/97 test workdir/subdir/meson.build
new file mode 100644
index 0000000..687a1cf
--- /dev/null
+++ b/test cases/common/97 test workdir/subdir/meson.build
@@ -0,0 +1,4 @@
+exe2 = executable('dummy', '../opener.c')
+test('subdir', find_program('checker.py'),
+ workdir : meson.source_root(),
+ args: [exe2])