aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS1
-rw-r--r--README.md52
-rw-r--r--ci/azure-steps.yml4
-rw-r--r--docs/markdown/Fs-module.md87
-rw-r--r--docs/markdown/Pkgconfig-module.md11
-rw-r--r--docs/markdown/Reference-manual.md37
-rw-r--r--docs/markdown/snippets/more_meson_sample_templates.md6
-rw-r--r--docs/markdown/snippets/native_property.md18
-rw-r--r--docs/markdown/snippets/per_subproject_builtin.md15
-rw-r--r--docs/markdown/snippets/uninstalled-pkgconfig.md8
-rw-r--r--docs/theme/extra/templates/navbar_links.html47
-rw-r--r--mesonbuild/backend/backends.py4
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/cmake/interpreter.py22
-rw-r--r--mesonbuild/cmake/traceparser.py18
-rw-r--r--mesonbuild/coredata.py58
-rw-r--r--mesonbuild/dependencies/base.py8
-rw-r--r--mesonbuild/dependencies/ui.py42
-rw-r--r--mesonbuild/environment.py5
-rw-r--r--mesonbuild/interpreter.py80
-rw-r--r--mesonbuild/mesonlib.py2
-rw-r--r--mesonbuild/minit.py136
-rw-r--r--mesonbuild/mintro.py15
-rw-r--r--mesonbuild/modules/fs.py77
-rw-r--r--mesonbuild/modules/pkgconfig.py86
-rw-r--r--mesonbuild/templates/cstemplates.py126
-rw-r--r--mesonbuild/templates/cudatemplates.py177
-rw-r--r--mesonbuild/templates/javatemplates.py129
-rw-r--r--mesonbuild/templates/mesontemplates.py51
-rw-r--r--mesonbuild/templates/objcpptemplates.py156
-rwxr-xr-xrun_project_tests.py111
-rwxr-xr-xrun_unittests.py48
-rw-r--r--test cases/cmake/1 basic/meson.build6
-rw-r--r--test cases/cmake/1 basic/subprojects/cmMod/CMakeLists.txt4
-rw-r--r--test cases/cmake/1 basic/subprojects/cmMod/cmMod.hpp15
-rw-r--r--test cases/cmake/16 threads/meson.build12
-rw-r--r--test cases/cmake/16 threads/meson_options.txt1
-rw-r--r--test cases/cmake/16 threads/subprojects/cmMod/CMakeLists.txt6
-rw-r--r--test cases/cmake/16 threads/subprojects/cmMod/cmMod.hpp13
-rw-r--r--test cases/cmake/16 threads/test_matrix.json9
-rw-r--r--test cases/common/14 configure file/meson.build6
-rw-r--r--test cases/common/144 custom target multiple outputs/meson.build13
-rw-r--r--test cases/common/192 args flattening/meson.build16
-rw-r--r--test cases/common/227 fs module/meson.build34
-rw-r--r--test cases/common/229 native prop/crossfile.ini3
-rw-r--r--test cases/common/229 native prop/meson.build25
-rw-r--r--test cases/common/229 native prop/nativefile.ini3
-rw-r--r--test cases/common/229 persubproject options/foo.c5
-rw-r--r--test cases/common/229 persubproject options/meson.build10
-rw-r--r--test cases/common/229 persubproject options/subprojects/sub1/foo.c5
-rw-r--r--test cases/common/229 persubproject options/subprojects/sub1/meson.build7
-rw-r--r--test cases/common/229 persubproject options/subprojects/sub2/foo.c5
-rw-r--r--test cases/common/229 persubproject options/subprojects/sub2/meson.build7
-rw-r--r--test cases/common/47 pkgconfig-gen/dependencies/main.c6
-rw-r--r--test cases/common/47 pkgconfig-gen/dependencies/meson.build3
-rw-r--r--test cases/common/47 pkgconfig-gen/meson.build1
-rw-r--r--test cases/failing/99 no native prop/meson.build3
-rw-r--r--test cases/unit/72 summary/meson.build1
58 files changed, 1591 insertions, 269 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index 50c0627..e361e83 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,4 +1,5 @@
* @jpakkane
/mesonbuild/modules/pkgconfig.py @xclaesse
/mesonbuild/modules/cmake.py @mensinda
+/mesonbuild/ast/* @mensinda
/mesonbuild/cmake/* @mensinda
diff --git a/README.md b/README.md
index 073dfa4..4996aa7 100644
--- a/README.md
+++ b/README.md
@@ -20,27 +20,28 @@ build system.
#### Installing from source
-You can run Meson directly from a revision control checkout or an
-extracted tarball. If you wish you can install it locally with the
-standard Python command
+Meson is available on [PyPi](https://pypi.python.org/pypi/meson), so
+it can be installed with `pip3 install meson`. The exact command to
+type to install with `pip` can vary between systems, be sure to use
+the Python 3 version of `pip`.
-```sh
-python3 -m pip install meson <your options here>
-```
+If you wish you can install it locally with the standard Python command:
-Meson is also available from
-[PyPi](https://pypi.python.org/pypi/meson), so it can be installed
-with `pip3 install meson` (this does not require a source checkout,
-pip will download the package automatically). The exact command to
-type to install with Pip can vary between systems, be sure to use the
-Python 3 version of Pip.
+```console
+python3 -m pip install meson
+```
-For builds using Ninja, Ninja can be [downloaded directly](https://github.com/ninja-build/ninja/releases) or via
+For builds using Ninja, Ninja can be downloaded directly from Ninja
+[GitHub release page](https://github.com/ninja-build/ninja/releases)
+or via [PyPi](https://pypi.python.org/pypi/ninja)
-```sh
+```console
python3 -m pip install ninja
```
+More on Installing Meson build can be found at the
+[getting meson page](https://mesonbuild.com/Getting-meson.html).
+
#### Running
Meson requires that you have a source directory and a build directory
@@ -58,27 +59,22 @@ You can omit either of the two directories, and Meson will substitute
the current directory and autodetect what you mean. This allows you to
do things like this:
-`cd source_root; mkdir builddir; cd builddir; meson ..`
-
-or
-
-`cd source_root; mkdir builddir; meson builddir`
+```console
+cd <source root>
+meson builddir
+```
To compile, cd into your build directory and type `ninja`. To run unit
tests, type `ninja test`.
-Install is the same but it can take an extra argument:
-
-`DESTDIR=/destdir/path ninja install`
-
-`DESTDIR` can be omitted. If you are installing to system directories,
-you may need to run this command with sudo.
-
+More on running Meson build system commands can be found at the
+[running meson page](https://mesonbuild.com/Running-Meson.html)
+or by typing `meson --help`.
#### Contributing
We love code contributions. See the [contribution
-page](https://mesonbuild.com/Contributing.html) on the web site for
+page](https://mesonbuild.com/Contributing.html) on the website for
details.
@@ -96,4 +92,4 @@ to connect to this channel.
More information about the Meson build system can be found at the
[project's home page](https://mesonbuild.com).
-Meson is a registered trademark of Jussi Pakkanen.
+Meson is a registered trademark of ***Jussi Pakkanen***.
diff --git a/ci/azure-steps.yml b/ci/azure-steps.yml
index ef31208..66a7eed 100644
--- a/ci/azure-steps.yml
+++ b/ci/azure-steps.yml
@@ -102,6 +102,10 @@ steps:
$vcvars = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
}
+ ## A multiline commit message containing "=" can interact badly with this
+ ## hack to extract the environment from vcvarsall.bat
+ Remove-Item env:BUILD_SOURCEVERSIONMESSAGE
+
## ask cmd.exe to output the environment table after the batch file completes
$tempFile = [IO.Path]::GetTempFileName()
cmd /c " `"$vcvars`" $env:arch && set > `"$tempFile`" "
diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md
index 53bf960..e5941a9 100644
--- a/docs/markdown/Fs-module.md
+++ b/docs/markdown/Fs-module.md
@@ -9,6 +9,13 @@ Non-absolute paths are looked up relative to the directory where the
current `meson.build` file is.
If specified, a leading `~` is expanded to the user home directory.
+Environment variables are not available as is the rule throughout Meson.
+That is, $HOME, %USERPROFILE%, $MKLROOT, etc. have no meaning to the Meson
+filesystem module. If needed, pass such variables into Meson via command
+line options in `meson_options.txt`, native-file or cross-file.
+
+Where possible, symlinks and parent directory notation are resolved to an
+absolute path.
### exists
@@ -19,13 +26,12 @@ special entry such as a device node.
### is_dir
Takes a single string argument and returns true if a directory with
-that name exists on the file system. This method follows symbolic
-links.
+that name exists on the file system.
### is_file
Takes a single string argument and returns true if an file with that
-name exists on the file system. This method follows symbolic links.
+name exists on the file system.
### is_symlink
@@ -34,6 +40,25 @@ by the string is a symbolic link.
## File Parameters
+### is_absolute
+
+*since 0.54.0*
+
+Return a boolean indicating if the path string specified is absolute, WITHOUT expanding `~`.
+
+Examples:
+
+```meson
+fs.is_absolute('~') # false
+
+home = fs.expanduser('~')
+fs.is_absolute(home) # true
+
+fs.is_absolute(home / 'foo') # true, even if ~/foo doesn't exist
+
+fs.is_absolute('foo/bar') # false, even if ./foo/bar exists
+```
+
### hash
The `fs.hash(filename, hash_algorithm)` method returns a string containing
@@ -44,7 +69,6 @@ md5, sha1, sha224, sha256, sha384, sha512.
### size
The `fs.size(filename)` method returns the size of the file in integer bytes.
-Symlinks will be resolved if possible.
### is_samepath
@@ -67,7 +91,7 @@ fs.is_samepath(x, z) # true
fs.is_samepath(x, j) # false
p = 'foo/bar'
-q = 'foo/bar/../baz'
+q = 'foo/bar/baz/..'
r = 'buz' # a symlink pointing to foo/bar
s = 'notapath' # non-existant directory
@@ -76,10 +100,39 @@ fs.is_samepath(p, r) # true
fs.is_samepath(p, s) # false
```
-
## Filename modification
-The files need not actually exist yet for this method, as it's just string manipulation.
+The files need not actually exist yet for these path string manipulation methods.
+
+### expanduser
+
+*since 0.54.0*
+
+A path string with a leading `~` is expanded to the user home directory
+
+Examples:
+
+```meson
+fs.expanduser('~') # user home directory
+
+fs.expanduser('~/foo') # <homedir>/foo
+```
+
+### as_posix
+
+*since 0.54.0*
+
+`fs.as_posix(path)` assumes a Windows path, even if on a Unix-like system.
+Thus, all `'\'` or `'\\'` are turned to '/', even if you meant to escape a character.
+
+Examples
+
+```meson
+fs.as_posix('\\') == '/' # true
+fs.as_posix('\\\\') == '/' # true
+
+fs.as_posix('foo\\bar/baz') == 'foo/bar/baz' # true
+```
### replace_suffix
@@ -118,6 +171,26 @@ new = fs.replace_suffix(original, '') # /opt/foo.dll
Returns the parent directory (i.e. dirname).
+```meson
+new = fs.parent('foo/bar') # foo
+new = fs.parent('foo/bar/baz.dll') # foo/bar
+```
+
### name
Returns the last component of the path (i.e. basename).
+
+```meson
+fs.name('foo/bar/baz.dll.a') # baz.dll.a
+```
+
+### stem
+
+*since 0.54.0*
+
+Returns the last component of the path, dropping the last part of the suffix
+
+```meson
+fs.stem('foo/bar/baz.dll') # baz
+fs.stem('foo/bar/baz.dll.a') # baz.dll
+```
diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md
index 4aa82f6..678090b 100644
--- a/docs/markdown/Pkgconfig-module.md
+++ b/docs/markdown/Pkgconfig-module.md
@@ -54,6 +54,8 @@ keyword arguments.
`Version:` field. (*since 0.46.0*) Defaults to the project version if unspecified.
- `d_module_versions` a list of module version flags used when compiling
D sources referred to by this pkg-config file
+- `uninstalled_variables` used instead of the `variables` keyword argument, when
+ generating the uninstalled pkg-config file. Since *0.54.0*
Since 0.46 a `StaticLibrary` or `SharedLibrary` object can optionally be passed
as first positional argument. If one is provided a default value will be
@@ -62,6 +64,15 @@ provided for all required fields of the pc file:
- `description` is set to the project's name followed by the library's name.
- `name` is set to the library's name.
+Since 0.54.0 uninstalled pkg-config files are generated as well. They are
+located in `<build dir>/meson-uninstalled/`. It is sometimes
+useful to build projects against libraries built by meson without having to
+install them into a prefix. In order to do so, just set
+`PKG_CONFIG_PATH=<builddir>/meson-uninstalled` before building your
+application. That will cause pkg-config to prefer those `-uninstalled.pc` files
+and find libraries and headers from the meson builddir. This is an experimental
+feature provided on a best-effort basis, it might not work in all use-cases.
+
### Implicit dependencies
The exact rules followed to find dependencies that are implicitly added into the
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 57fc1f7..510d443 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -547,11 +547,12 @@ libraries (`.so`, `.dll`, etc) will be linked.
With the Ninja backend, Meson will create a build-time [order-only
dependency](https://ninja-build.org/manual.html#ref_dependencies) on
-all generated input files, including unknown files. For all input
-files (generated and non-generated), Meson uses the [dependency
-file](https://ninja-build.org/manual.html#ref_headers) generated by
-your compiler to determine when to rebuild sources. The behavior is
-similar for other backends.
+all generated input files, including unknown files. This is needed
+to bootstrap the generation of the real dependencies in the
+[depfile](https://ninja-build.org/manual.html#ref_headers)
+generated by your compiler to determine when to rebuild sources.
+Ninja relies on this dependency file for all input files, generated
+and non-generated. The behavior is similar for other backends.
Executable supports the following keyword arguments. Note that just
like the positional arguments above, these keyword arguments can also
@@ -1552,7 +1553,8 @@ arguments:
that override those set in the subproject's `meson_options.txt`
(like `default_options` in `project`, they only have effect when
Meson is run for the first time, and command line arguments override
- any default options in build files)
+ any default options in build files). *Since 0.54.0* `default_library`
+ built-in option can also be overridden.
- `version` keyword argument that works just like the one in
`dependency`. It specifies what version the subproject should be,
as an example `>=1.0.1`
@@ -1773,10 +1775,6 @@ the following methods.
refer to files in the current or any other source directory instead
of constructing paths manually with `meson.current_source_dir()`.
-- `get_cross_property(propname, fallback_value)` returns the given
- property from a cross file, the optional second argument is returned
- if not cross compiling or the given property is not found.
-
- `get_compiler(language)` returns [an object describing a
compiler](#compiler-object), takes one positional argument which is
the language to use. It also accepts one keyword argument, `native`
@@ -1786,6 +1784,19 @@ the following methods.
returns the "cross" compiler if we're currently cross-compiling and
the "native" compiler if we're not.
+- `get_cross_property(propname, fallback_value)`
+ *Consider get_external_property() instead*. Returns the given
+ property from a cross file, the optional fallback_value is returned
+ if not cross compiling or the given property is not found.
+
+- `get_external_property(propname, fallback_value, native: true/false)`
+ *(added 0.54.0)* returns the given property from a native or cross file.
+ The optional fallback_value is returned if the given property is not found.
+ The optional `native: true` forces retrieving a variable from the
+ native file, even when cross-compiling.
+ If `native: false` or not specified, variable is retrieved from the
+ cross-file if cross-compiling, and from the native-file when not cross-compiling.
+
- `has_exe_wrapper()` returns true when doing a cross build if there
is a wrapper command that can be used to execute cross built
binaries (for example when cross compiling from Linux to Windows,
@@ -2299,6 +2310,8 @@ contains a target with the following methods:
NOTE: In most cases using the object itself will do the same job as
this and will also allow Meson to setup inter-target dependencies
correctly. Please file a bug if that doesn't work for you.
+ *Since 0.54.0* it can be also called on indexes objects:
+ `custom_targets[i].full_path()`.
- `[index]` returns an opaque object that references this target, and
can be used as a source in other targets. When it is used as such it
@@ -2306,6 +2319,10 @@ contains a target with the following methods:
source added will be the one that corresponds to the index of the
custom target's output argument.
+- `to_list()` *Since 0.54.0*, returns a list of opaque objects that references
+ this target, and can be used as a source in other targets. This can be used to
+ iterate outputs with `foreach` loop.
+
### `dependency` object
This object is returned by [`dependency()`](#dependency) and contains
diff --git a/docs/markdown/snippets/more_meson_sample_templates.md b/docs/markdown/snippets/more_meson_sample_templates.md
new file mode 100644
index 0000000..e10da9c
--- /dev/null
+++ b/docs/markdown/snippets/more_meson_sample_templates.md
@@ -0,0 +1,6 @@
+## More new sample Meson templates for (`Java`, `Cuda`, and more)
+
+Meson now ships with predefined project templates for `Java`,
+`Cuda`, `Objective-C++`, and `C#`, we provided with associated
+values for corresponding languages, avalable for both library,
+and executable.
diff --git a/docs/markdown/snippets/native_property.md b/docs/markdown/snippets/native_property.md
new file mode 100644
index 0000000..d3808d8
--- /dev/null
+++ b/docs/markdown/snippets/native_property.md
@@ -0,0 +1,18 @@
+## Native file properties
+
+As of Meson 0.54.0, the `--native-file nativefile.ini` can contain:
+
+* binaries
+* paths
+* properties
+
+which are defined and used the same way as in cross files.
+The `properties` are new for Meson 0.54.0, and are read like:
+
+```meson
+x = meson.get_external_property('foobar', 'foo')
+```
+
+where `foobar` is the property name, and the optional `foo` is the fallback string value.
+
+For cross-compiled projects, `get_external_property()` reads the cross-file unless `native: true` is specified. \ No newline at end of file
diff --git a/docs/markdown/snippets/per_subproject_builtin.md b/docs/markdown/snippets/per_subproject_builtin.md
new file mode 100644
index 0000000..44ed1c8
--- /dev/null
+++ b/docs/markdown/snippets/per_subproject_builtin.md
@@ -0,0 +1,15 @@
+## Per subproject `default_library` option
+
+The `default_library` built-in option can now be defined per subproject. This is
+useful for example when building shared libraries in the main project, but static
+link a subproject.
+
+Most of the time this would be used either by the parent project by setting
+subproject's default_options (e.g. `subproject('foo', default_options: 'default_library=static')`),
+or by the user using the command line `-Dfoo:default_library=static`.
+
+The value is overriden in this order:
+- Value from parent project
+- Value from subproject's default_options if set
+- Value from subproject() default_options if set
+- Value from command line if set
diff --git a/docs/markdown/snippets/uninstalled-pkgconfig.md b/docs/markdown/snippets/uninstalled-pkgconfig.md
new file mode 100644
index 0000000..2e265ab
--- /dev/null
+++ b/docs/markdown/snippets/uninstalled-pkgconfig.md
@@ -0,0 +1,8 @@
+## Uninstalled pkg-config files
+
+The `pkgconfig` module now generates uninstalled pc files as well. For any generated
+`foo.pc` file, an extra `foo-uninstalled.pc` file is placed into
+`<builddir>/meson-uninstalled`. They can be used to build applications against
+libraries built by meson without installing them, by pointing `PKG_CONFIG_PATH`
+to that directory. This is an experimental feature provided on a best-effort
+basis, it might not work in all use-cases.
diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html
index 036c20f..6980f81 100644
--- a/docs/theme/extra/templates/navbar_links.html
+++ b/docs/theme/extra/templates/navbar_links.html
@@ -1,21 +1,28 @@
@require(page)
<li class="dropdown">
- <a class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
- Modules <span class="caret"></span>
- </a>
- <ul class="dropdown-menu" id="modules-menu">
- @for tup in (("Gnome-module.html","GNOME"), \
- ("i18n-module.html","i18n"), \
- ("Pkgconfig-module.html","Pkgconfig"), \
- ("Python-module.html","Python"), \
- ("Python-3-module.html","Python 3"), \
- ("Qt4-module.html","Qt4"), \
- ("Qt5-module.html","Qt5"), \
- ("RPM-module.html","RPM"), \
- ("SourceSet-module.html","SourceSet"), \
- ("Windows-module.html","Windows"), \
- ("Hotdoc-module.html","Hotdoc")):
+ <a class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ Modules <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu" id="modules-menu">
+ @for tup in ( \
+ ("CMake-module.html","CMake"), \
+ ("Cuda-module.html","CUDA"), \
+ ("Dlang-module.html","Dlang"), \
+ ("Fs-module.html","Filesystem"), \
+ ("Gnome-module.html","GNOME"), \
+ ("Hotdoc-module.html","Hotdoc"), \
+ ("i18n-module.html","i18n"), \
+ ("Icestorm-module.html","Icestorm"), \
+ ("Kconfig-module.html","kconfig"), \
+ ("Pkgconfig-module.html","Pkgconfig"), \
+ ("Python-module.html","Python"), \
+ ("Python-3-module.html","Python 3"), \
+ ("Qt4-module.html","Qt4"), \
+ ("Qt5-module.html","Qt5"), \
+ ("RPM-module.html","RPM"), \
+ ("SourceSet-module.html","SourceSet"), \
+ ("Windows-module.html","Windows")):
<li>
<a href="@tup[0]">@tup[1]</a>
</li>
@@ -29,11 +36,11 @@
</a>
<ul class="dropdown-menu" id="quick-refs-menu">
@for tup in (("Reference-manual.html", "Functions"), \
- ("Build-options.html", "Options"), \
- ("Configuration.html", "Configuration"), \
- ("Dependencies.html", "Dependencies"), \
- ("Unit-tests.html", "Tests"), \
- ("Syntax.html", "Syntax")):
+ ("Build-options.html", "Options"), \
+ ("Configuration.html", "Configuration"), \
+ ("Dependencies.html", "Dependencies"), \
+ ("Unit-tests.html", "Tests"), \
+ ("Syntax.html", "Syntax")):
<li>
<a href="@tup[0]">@tup[1]</a>
</li>
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index a614be0..5203323 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -165,10 +165,6 @@ class Backend:
def get_target_filename_abs(self, target):
return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
- def get_builtin_options_for_target(self, target):
- return OptionOverrideProxy(target.option_overrides,
- self.environment.coredata.builtins)
-
def get_base_options_for_target(self, target):
return OptionOverrideProxy(target.option_overrides,
self.environment.coredata.builtins,
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 1db930e..0a8ca45 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2250,6 +2250,10 @@ class CustomTarget(Target):
def __delitem__(self, index):
raise NotImplementedError
+ def __iter__(self):
+ for i in self.outputs:
+ yield CustomTargetIndex(self, i)
+
class RunTarget(Target):
def __init__(self, name, command, args, dependencies, subdir, subproject):
self.typename = 'run'
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 2aa0c01..81844a0 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -125,10 +125,13 @@ blacklist_link_libs = [
'advapi32.lib'
]
-generated_target_name_prefix = 'cm_'
-
transfer_dependencies_from = ['header_only']
+_cmake_name_regex = re.compile(r'[^_a-zA-Z0-9]')
+def _sanitize_cmake_name(name: str) -> str:
+ name = _cmake_name_regex.sub('_', name)
+ return 'cm_' + name
+
class OutputTargetMap:
rm_so_version = re.compile(r'(\.[0-9]+)+$')
@@ -234,8 +237,7 @@ class ConverterTarget:
self.override_options = []
# Convert the target name to a valid meson target name
- self.name = self.name.replace('-', '_')
- self.name = generated_target_name_prefix + self.name
+ self.name = _sanitize_cmake_name(self.name)
for i in target.files:
# Determine the meson language
@@ -593,8 +595,7 @@ class ConverterCustomTarget:
self.depends = []
# Convert the target name to a valid meson target name
- self.name = self.name.replace('-', '_')
- self.name = generated_target_name_prefix + self.name
+ self.name = _sanitize_cmake_name(self.name)
def __repr__(self) -> str:
return '<{}: {} {}>'.format(self.__class__.__name__, self.name, self.outputs)
@@ -1192,6 +1193,7 @@ class CMakeInterpreter:
root_cb.lines += [assign(tgt_var, function('custom_target', [tgt.name], tgt_kwargs))]
processed[tgt.name] = {'inc': None, 'src': None, 'dep': None, 'tgt': tgt_var, 'func': 'custom_target'}
+ name_map[tgt.cmake_name] = tgt.name
# Now generate the target function calls
for i in self.custom_targets:
@@ -1208,7 +1210,7 @@ class CMakeInterpreter:
def target_info(self, target: str) -> T.Optional[T.Dict[str, str]]:
# Try resolving the target name
# start by checking if there is a 100% match (excluding the name prefix)
- prx_tgt = generated_target_name_prefix + target
+ prx_tgt = _sanitize_cmake_name(target)
if prx_tgt in self.generated_targets:
return self.generated_targets[prx_tgt]
# check if there exists a name mapping
@@ -1219,11 +1221,7 @@ class CMakeInterpreter:
return None
def target_list(self) -> T.List[str]:
- prx_str = generated_target_name_prefix
- prx_len = len(prx_str)
- res = [x for x in self.generated_targets.keys()]
- res = [x[prx_len:] if x.startswith(prx_str) else x for x in res]
- return res
+ return list(self.internal_name_map.keys())
def _object_lib_workaround(self) -> bool:
return 'link' in self.linkers and self.backend_name.startswith('vs')
diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py
index 5bf9547..4aa34f1 100644
--- a/mesonbuild/cmake/traceparser.py
+++ b/mesonbuild/cmake/traceparser.py
@@ -54,6 +54,13 @@ class CMakeTarget:
propSTR += " '{}': {}\n".format(i, self.properties[i])
return s.format(self.name, self.type, self.imported, propSTR, self.tline)
+ def strip_properties(self) -> None:
+ # Strip the strings in the properties
+ if not self.properties:
+ return
+ for key, val in self.properties.items():
+ self.properties[key] = [x.strip() for x in val]
+
class CMakeGeneratorTarget(CMakeTarget):
def __init__(self, name):
super().__init__(name, 'CUSTOM', {})
@@ -63,11 +70,8 @@ class CMakeGeneratorTarget(CMakeTarget):
class CMakeTraceParser:
def __init__(self, cmake_version: str, build_dir: str, permissive: bool = False):
- # Dict of CMake variables: '<var_name>': ['list', 'of', 'values']
- self.vars = {}
-
- # Dict of CMakeTarget
- self.targets = {}
+ self.vars = {} # type: T.Dict[str, T.List[str]]
+ self.targets = {} # type: T.Dict[str, CMakeTarget]
# T.List of targes that were added with add_custom_command to generate files
self.custom_targets = [] # type: T.List[CMakeGeneratorTarget]
@@ -133,6 +137,10 @@ class CMakeTraceParser:
if(fn):
fn(l)
+ # Postprocess
+ for tgt in self.targets.values():
+ tgt.strip_properties()
+
def get_first_cmake_var_of(self, var_list: T.List[str]) -> T.List[str]:
# Return the first found CMake variable in list var_list
for i in var_list:
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 44eaa02..aea2d90 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -364,11 +364,12 @@ class CoreData:
self.install_guid = str(uuid.uuid4()).upper()
self.target_guids = {}
self.version = version
- self.init_builtins()
- self.backend_options = {} # : T.Dict[str, UserOption]
- self.user_options = {} # : T.Dict[str, UserOption]
+ self.builtins = {} # : OptionDictType
+ self.builtins_per_machine = PerMachine({}, {})
+ self.backend_options = {} # : OptionDictType
+ self.user_options = {} # : OptionDictType
self.compiler_options = PerMachine({}, {})
- self.base_options = {} # : T.Dict[str, UserOption]
+ self.base_options = {} # : OptionDictType
self.cross_files = self.__load_config_files(options, scratch_dir, 'cross')
self.compilers = PerMachine(OrderedDict(), OrderedDict())
@@ -378,6 +379,7 @@ class CoreData:
self.compiler_check_cache = OrderedDict()
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(options, scratch_dir, 'native')
+ self.init_builtins('')
self.libdir_cross_fixup()
@staticmethod
@@ -497,15 +499,25 @@ class CoreData:
raise MesonException(msg.format(option, value, prefix))
return value.as_posix()
- def init_builtins(self):
+ def init_builtins(self, subproject: str):
# Create builtin options with default values
- self.builtins = {}
for key, opt in builtin_options.items():
- self.builtins[key] = opt.init_option()
- self.builtins_per_machine = PerMachine({}, {})
+ self.add_builtin_option(self.builtins, key, opt, subproject)
for for_machine in iter(MachineChoice):
for key, opt in builtin_options_per_machine.items():
- self.builtins_per_machine[for_machine][key] = opt.init_option()
+ self.add_builtin_option(self.builtins_per_machine[for_machine], key, opt, subproject)
+
+ def add_builtin_option(self, opts_map, key, opt, subproject):
+ if subproject:
+ if opt.yielding:
+ # This option is global and not per-subproject
+ return
+ optname = subproject + ':' + key
+ value = opts_map[key].value
+ else:
+ optname = key
+ value = None
+ opts_map[optname] = opt.init_option(key, value, default_prefix())
def init_backend_options(self, backend_name):
if backend_name == 'ninja':
@@ -520,15 +532,20 @@ class CoreData:
'Default project to execute in Visual Studio',
'')
- def get_builtin_option(self, optname):
+ def get_builtin_option(self, optname, subproject=''):
+ raw_optname = optname
+ if subproject:
+ optname = subproject + ':' + optname
for opts in self._get_all_builtin_options():
v = opts.get(optname)
+ if v is None or v.yielding:
+ v = opts.get(raw_optname)
if v is None:
continue
- if optname == 'wrap_mode':
+ if raw_optname == 'wrap_mode':
return WrapMode.from_string(v.value)
return v.value
- raise RuntimeError('Tried to get unknown builtin option %s.' % optname)
+ raise RuntimeError('Tried to get unknown builtin option %s.' % raw_optname)
def _try_set_builtin_option(self, optname, value):
for opts in self._get_all_builtin_options():
@@ -707,11 +724,13 @@ class CoreData:
env.cmd_line_options.setdefault(k, v)
# Set default options as if they were passed to the command line.
- # Subprojects can only define default for user options.
+ # Subprojects can only define default for user options and not yielding
+ # builtin option.
from . import optinterpreter
for k, v in default_options.items():
if subproject:
- if optinterpreter.is_invalid_name(k, log=False):
+ if (k not in builtin_options or builtin_options[k].yielding) \
+ and optinterpreter.is_invalid_name(k, log=False):
continue
k = subproject + ':' + k
env.cmd_line_options.setdefault(k, v)
@@ -951,7 +970,7 @@ class BuiltinOption(T.Generic[_T, _U]):
Currently doesn't support UserIntegerOption, or a few other cases.
"""
- def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: T.Optional[bool] = None, *,
+ def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *,
choices: T.Any = None):
self.opt_type = opt_type
self.description = description
@@ -959,9 +978,11 @@ class BuiltinOption(T.Generic[_T, _U]):
self.choices = choices
self.yielding = yielding
- def init_option(self) -> _U:
+ def init_option(self, name: str, value: T.Optional[T.Any], prefix: str) -> _U:
"""Create an instance of opt_type and return it."""
- keywords = {'yielding': self.yielding, 'value': self.default}
+ if value is None:
+ value = self.prefixed_default(name, prefix)
+ keywords = {'yielding': self.yielding, 'value': value}
if self.choices:
keywords['choices'] = self.choices
return self.opt_type(self.description, **keywords)
@@ -1036,7 +1057,8 @@ builtin_options = OrderedDict([
('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug',
choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
('debug', BuiltinOption(UserBooleanOption, 'Debug', True)),
- ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'])),
+ ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'],
+ yielding=False)),
('errorlogs', BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)),
('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index d014415..a83e3d6 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -1661,6 +1661,14 @@ class DubDependency(ExternalDependency):
lib_file_name = os.path.basename(default_path)
module_build_path = os.path.join(module_path, '.dub', 'build')
+ # If default_path is a path to lib file and
+ # directory of lib don't have subdir '.dub/build'
+ if not os.path.isdir(module_build_path) and os.path.isfile(default_path):
+ if folder_only:
+ return module_path
+ else:
+ return default_path
+
# Get D version implemented in the compiler
# gdc doesn't support this
ret, res = self._call_dubbin(['--version'])
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index 5433150..31f02e5 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -32,6 +32,9 @@ from .base import ExternalDependency, NonExistingExternalProgram
from .base import ExtraFrameworkDependency, PkgConfigDependency
from .base import ConfigToolDependency, DependencyFactory
+if T.TYPE_CHECKING:
+ from ..environment import Environment
+
class GLDependencySystem(ExternalDependency):
def __init__(self, name: str, environment, kwargs):
@@ -360,7 +363,12 @@ class QtBaseDependency(ExternalDependency):
if self.env.machines.host.is_darwin() and not any(s in xspec for s in ['ios', 'tvos']):
mlog.debug("Building for macOS, looking for framework")
self._framework_detect(qvars, mods, kwargs)
- return self.qmake.name
+ # Sometimes Qt is built not as a framework (for instance, when using conan pkg manager)
+ # skip and fall back to normal procedure then
+ if self.is_found:
+ return self.qmake.name
+ else:
+ mlog.debug("Building for macOS, couldn't find framework, falling back to library search")
incdir = qvars['QT_INSTALL_HEADERS']
self.compile_args.append('-I' + incdir)
libdir = qvars['QT_INSTALL_LIBS']
@@ -410,6 +418,9 @@ class QtBaseDependency(ExternalDependency):
suffix += 'd'
if self.qtver == '4':
suffix += '4'
+ if self.env.machines[self.for_machine].is_darwin():
+ if is_debug:
+ suffix += '_debug'
return suffix
def _link_with_qtmain(self, is_debug, libdir):
@@ -432,8 +443,8 @@ class QtBaseDependency(ExternalDependency):
fname = 'Qt' + m
mlog.debug('Looking for qt framework ' + fname)
fwdep = QtExtraFrameworkDependency(fname, self.env, fw_kwargs, language=self.language)
- self.compile_args.append('-F' + libdir)
if fwdep.found():
+ self.compile_args.append('-F' + libdir)
self.compile_args += fwdep.get_compile_args(with_private_headers=self.private_headers,
qt_version=self.version)
self.link_args += fwdep.get_link_args()
@@ -441,8 +452,8 @@ class QtBaseDependency(ExternalDependency):
break
else:
self.is_found = True
- # Used by self.compilers_detect()
- self.bindir = self.get_qmake_host_bins(qvars)
+ # Used by self.compilers_detect()
+ self.bindir = self.get_qmake_host_bins(qvars)
def get_qmake_host_bins(self, qvars):
# Prefer QT_HOST_BINS (qt5, correct for cross and native compiling)
@@ -530,17 +541,30 @@ class WxDependency(ConfigToolDependency):
tools = ['wx-config-3.0', 'wx-config', 'wx-config-gtk3']
tool_name = 'wx-config'
- def __init__(self, environment, kwargs):
- super().__init__('WxWidgets', environment, kwargs)
+ def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ super().__init__('WxWidgets', environment, kwargs, language='cpp')
if not self.is_found:
return
self.requested_modules = self.get_requested(kwargs)
+
+ extra_args = []
+ if self.static:
+ extra_args.append('--static=yes')
+
+ # Check to make sure static is going to work
+ err = Popen_safe(self.config + extra_args)[2]
+ if 'No config found to match' in err:
+ mlog.debug('WxWidgets is missing static libraries.')
+ self.is_found = False
+ return
+
# wx-config seems to have a cflags as well but since it requires C++,
# this should be good, at least for now.
- self.compile_args = self.get_config_value(['--cxxflags'] + self.requested_modules, 'compile_args')
- self.link_args = self.get_config_value(['--libs'] + self.requested_modules, 'link_args')
+ self.compile_args = self.get_config_value(['--cxxflags'] + extra_args + self.requested_modules, 'compile_args')
+ self.link_args = self.get_config_value(['--libs'] + extra_args + self.requested_modules, 'link_args')
- def get_requested(self, kwargs):
+ @staticmethod
+ def get_requested(kwargs: T.Dict[str, T.Any]) -> T.List[str]:
if 'modules' not in kwargs:
return []
candidates = extract_as_list(kwargs, 'modules')
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index ef9480b..554d79b 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -538,6 +538,7 @@ class Environment:
coredata.load_configs(self.coredata.config_files))
binaries.build = BinaryTable(config.get('binaries', {}))
paths.build = Directories(**config.get('paths', {}))
+ properties.build = Properties(config.get('properties', {}))
## Read in cross file(s) to override host machine configuration
@@ -909,7 +910,7 @@ class Environment:
arg = '--vsn'
elif 'ccrx' in compiler_name:
arg = '-v'
- elif 'icl' in compiler_name:
+ elif compiler_name in {'icl', 'icl.exe'}:
# if you pass anything to icl you get stuck in a pager
arg = ''
else:
@@ -1336,6 +1337,8 @@ class Environment:
cls = MonoCompiler
elif "Visual C#" in out:
cls = VisualStudioCsCompiler
+ else:
+ continue
self.coredata.add_lang_args(cls.language, cls, for_machine, self)
return cls(comp, version, for_machine, info)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 8a04842..d824e3c 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -35,8 +35,11 @@ from .modules import ModuleReturnValue
from .cmake import CMakeInterpreter
from pathlib import Path, PurePath
-import os, shutil, uuid
-import re, shlex
+import os
+import shutil
+import uuid
+import re
+import shlex
import subprocess
import collections
from itertools import chain
@@ -871,15 +874,23 @@ class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
-class CustomTargetIndexHolder(InterpreterObject, ObjectHolder):
- def __init__(self, object_to_hold):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, object_to_hold)
+class CustomTargetIndexHolder(TargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ })
+
+ @FeatureNew('custom_target[i].full_path', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def full_path_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
class CustomTargetHolder(TargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method,
+ 'to_list': self.to_list_method,
})
def __repr__(self):
@@ -892,8 +903,17 @@ class CustomTargetHolder(TargetHolder):
def full_path_method(self, args, kwargs):
return self.interpreter.backend.get_target_filename_abs(self.held_object)
+ @FeatureNew('custom_target.to_list', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def to_list_method(self, args, kwargs):
+ result = []
+ for i in self.held_object:
+ result.append(CustomTargetIndexHolder(i, self.interpreter))
+ return result
+
def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index])
+ return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
raise InterpreterException('Cannot set a member of a CustomTarget')
@@ -1783,6 +1803,8 @@ class Summary:
formatted_values.append(mlog.green('YES') if i else mlog.red('NO'))
else:
formatted_values.append(i)
+ if not formatted_values:
+ formatted_values = ['']
self.sections[section][k] = formatted_values
self.max_key_len = max(self.max_key_len, len(k))
@@ -1826,6 +1848,7 @@ class MesonMain(InterpreterObject):
'version': self.version_method,
'project_name': self.project_name_method,
'get_cross_property': self.get_cross_property_method,
+ 'get_external_property': self.get_external_property_method,
'backend': self.backend_method,
})
@@ -1998,7 +2021,7 @@ class MesonMain(InterpreterObject):
@noArgsFlattening
@permittedKwargs({})
- def get_cross_property_method(self, args, kwargs):
+ def get_cross_property_method(self, args, kwargs) -> str:
if len(args) < 1 or len(args) > 2:
raise InterpreterException('Must have one or two arguments.')
propname = args[0]
@@ -2012,6 +2035,34 @@ class MesonMain(InterpreterObject):
return args[1]
raise InterpreterException('Unknown cross property: %s.' % propname)
+ @noArgsFlattening
+ @permittedKwargs({'native'})
+ @FeatureNew('meson.get_external_property', '0.54.0')
+ def get_external_property_method(self, args: T.Sequence[str], kwargs: dict) -> str:
+ if len(args) < 1 or len(args) > 2:
+ raise InterpreterException('Must have one or two positional arguments.')
+ propname = args[0]
+ if not isinstance(propname, str):
+ raise InterpreterException('Property name must be string.')
+
+ def _get_native() -> str:
+ try:
+ props = self.interpreter.environment.properties.build
+ return props[propname]
+ except Exception:
+ if len(args) == 2:
+ return args[1]
+ raise InterpreterException('Unknown native property: %s.' % propname)
+ if 'native' in kwargs:
+ if kwargs['native']:
+ return _get_native()
+ else:
+ return self.get_cross_property_method(args, {})
+ else: # native: not specified
+ if self.build.environment.is_cross_build():
+ return self.get_cross_property_method(args, kwargs)
+ else:
+ return _get_native()
known_library_kwargs = (
build.known_shlib_kwargs |
@@ -2699,19 +2750,21 @@ external dependencies (including libraries) must go to "dependencies".''')
return result
def get_option_internal(self, optname):
+ raw_optname = optname
+ if self.is_subproject():
+ optname = self.subproject + ':' + optname
+
for opts in chain(
[self.coredata.base_options, compilers.base_options, self.coredata.builtins],
self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine),
self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options),
):
v = opts.get(optname)
+ if v is None or v.yielding:
+ v = opts.get(raw_optname)
if v is not None:
return v
- raw_optname = optname
- if self.is_subproject():
- optname = self.subproject + ':' + optname
-
try:
opt = self.coredata.user_options[optname]
if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options:
@@ -2818,6 +2871,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if self.environment.first_invocation:
default_options = self.project_default_options
default_options.update(self.default_project_options)
+ self.coredata.init_builtins(self.subproject)
else:
default_options = {}
self.coredata.set_default_options(default_options, self.subproject, self.environment)
@@ -4317,7 +4371,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s
return BothLibrariesHolder(shared_holder, static_holder, self)
def build_library(self, node, args, kwargs):
- default_library = self.coredata.get_builtin_option('default_library')
+ default_library = self.coredata.get_builtin_option('default_library', self.subproject)
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibraryHolder)
elif default_library == 'static':
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 85d883b..891e7a1 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2015 The Meson development team
+# Copyright 2012-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.
diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py
index 79741e5..5f28f58 100644
--- a/mesonbuild/minit.py
+++ b/mesonbuild/minit.py
@@ -22,13 +22,24 @@ from mesonbuild.environment import detect_ninja
from mesonbuild.templates.ctemplates import (create_exe_c_sample, create_lib_c_sample)
from mesonbuild.templates.cpptemplates import (create_exe_cpp_sample, create_lib_cpp_sample)
+from mesonbuild.templates.cstemplates import (create_exe_cs_sample, create_lib_cs_sample)
+from mesonbuild.templates.cudatemplates import (create_exe_cuda_sample, create_lib_cuda_sample)
from mesonbuild.templates.objctemplates import (create_exe_objc_sample, create_lib_objc_sample)
+from mesonbuild.templates.objcpptemplates import (create_exe_objcpp_sample, create_lib_objcpp_sample)
from mesonbuild.templates.dlangtemplates import (create_exe_d_sample, create_lib_d_sample)
+from mesonbuild.templates.javatemplates import (create_exe_java_sample, create_lib_java_sample)
from mesonbuild.templates.fortrantemplates import (create_exe_fortran_sample, create_lib_fortran_sample)
from mesonbuild.templates.rusttemplates import (create_exe_rust_sample, create_lib_rust_sample)
+'''
+there is currently only one meson template at this time.
+'''
+from mesonbuild.templates.mesontemplates import create_meson_build
+
FORTRAN_SUFFIXES = ['.f', '.for', '.F', '.f90', '.F90']
+UNREACHABLE_CODE = 'Unreachable code'
+
info_message = '''Sample project created. To build it run the
following commands:
@@ -37,53 +48,89 @@ ninja -C builddir
'''
def create_sample(options):
+ '''
+ Based on what arguments are passed we check for a match in language
+ then check for project type and create new Meson samples project.
+ '''
if options.language == 'c':
if options.type == 'executable':
create_exe_c_sample(options.name, options.version)
elif options.type == 'library':
create_lib_c_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
elif options.language == 'cpp':
if options.type == 'executable':
create_exe_cpp_sample(options.name, options.version)
elif options.type == 'library':
create_lib_cpp_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
+ elif options.language == 'cs':
+ if options.type == 'executable':
+ create_exe_cs_sample(options.name, options.version)
+ elif options.type == 'library':
+ create_lib_cs_sample(options.name, options.version)
+ else:
+ raise RuntimeError(UNREACHABLE_CODE)
+ elif options.language == 'cuda':
+ if options.type == 'executable':
+ create_exe_cuda_sample(options.name, options.version)
+ elif options.type == 'library':
+ create_lib_cuda_sample(options.name, options.version)
+ else:
+ raise RuntimeError(UNREACHABLE_CODE)
elif options.language == 'd':
if options.type == 'executable':
create_exe_d_sample(options.name, options.version)
elif options.type == 'library':
create_lib_d_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
elif options.language == 'fortran':
if options.type == 'executable':
create_exe_fortran_sample(options.name, options.version)
elif options.type == 'library':
create_lib_fortran_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
elif options.language == 'rust':
if options.type == 'executable':
create_exe_rust_sample(options.name, options.version)
elif options.type == 'library':
create_lib_rust_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
elif options.language == 'objc':
if options.type == 'executable':
create_exe_objc_sample(options.name, options.version)
elif options.type == 'library':
create_lib_objc_sample(options.name, options.version)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
+ elif options.language == 'objcpp':
+ if options.type == 'executable':
+ create_exe_objcpp_sample(options.name, options.version)
+ elif options.type == 'library':
+ create_lib_objcpp_sample(options.name, options.version)
+ else:
+ raise RuntimeError(UNREACHABLE_CODE)
+ elif options.language == 'java':
+ if options.type == 'executable':
+ create_exe_java_sample(options.name, options.version)
+ elif options.type == 'library':
+ create_lib_java_sample(options.name, options.version)
+ else:
+ raise RuntimeError(UNREACHABLE_CODE)
else:
- raise RuntimeError('Unreachable code')
+ raise RuntimeError(UNREACHABLE_CODE)
print(info_message)
def autodetect_options(options, sample: bool = False):
+ '''
+ Here we autodetect options for args not passed in so don't have to
+ think about it.
+ '''
if not options.name:
options.name = Path().resolve().stem
if not re.match('[a-zA-Z_][a-zA-Z0-9]*', options.name) and sample:
@@ -101,7 +148,7 @@ def autodetect_options(options, sample: bool = False):
if not options.srcfiles:
srcfiles = []
for f in (f for f in Path().iterdir() if f.is_file()):
- if f.suffix in (['.cc', '.cpp', '.c', '.d', '.m', '.rs'] + FORTRAN_SUFFIXES):
+ if f.suffix in (['.c', '.cc', '.cpp', '.cs', '.cu', '.d', '.m', '.mm', '.rs', '.java'] + FORTRAN_SUFFIXES):
srcfiles.append(f)
if not srcfiles:
raise SystemExit('No recognizable source files found.\n'
@@ -111,11 +158,17 @@ def autodetect_options(options, sample: bool = False):
options.srcfiles = [Path(f) for f in options.srcfiles]
if not options.language:
for f in options.srcfiles:
+ if f.suffix == '.c':
+ options.language = 'c'
+ break
if f.suffix in ('.cc', '.cpp'):
options.language = 'cpp'
break
- if f.suffix == '.c':
- options.language = 'c'
+ if f.suffix in '.cs':
+ options.language = 'cs'
+ break
+ if f.suffix == '.cu':
+ options.language = 'cuda'
break
if f.suffix == '.d':
options.language = 'd'
@@ -129,66 +182,37 @@ def autodetect_options(options, sample: bool = False):
if f.suffix == '.m':
options.language = 'objc'
break
+ if f.suffix == '.mm':
+ options.language = 'objcpp'
+ break
+ if f.suffix == '.java':
+ options.language = 'java'
+ break
if not options.language:
raise SystemExit("Can't autodetect language, please specify it with -l.")
print("Detected language: " + options.language)
-
-meson_executable_template = '''project('{project_name}', '{language}',
- version : '{version}',
- default_options : [{default_options}])
-
-executable('{executable}',
- {sourcespec},{depspec}
- install : true)
-'''
-
-def create_meson_build(options):
- if options.type != 'executable':
- raise SystemExit('\nGenerating a meson.build file from existing sources is\n'
- 'supported only for project type "executable".\n'
- 'Run meson init in an empty directory to create a sample project.')
- default_options = ['warning_level=3']
- if options.language == 'cpp':
- # This shows how to set this very common option.
- default_options += ['cpp_std=c++14']
- # If we get a meson.build autoformatter one day, this code could
- # be simplified quite a bit.
- formatted_default_options = ', '.join("'{}'".format(x) for x in default_options)
- sourcespec = ',\n '.join("'{}'".format(x) for x in options.srcfiles)
- depspec = ''
- if options.deps:
- depspec = '\n dependencies : [\n '
- depspec += ',\n '.join("dependency('{}')".format(x)
- for x in options.deps.split(','))
- depspec += '],'
- content = meson_executable_template.format(project_name=options.name,
- language=options.language,
- version=options.version,
- executable=options.executable,
- sourcespec=sourcespec,
- depspec=depspec,
- default_options=formatted_default_options)
- open('meson.build', 'w').write(content)
- print('Generated meson.build file:\n\n' + content)
-
def add_arguments(parser):
- parser.add_argument("srcfiles", metavar="sourcefile", nargs="*",
- help="source files. default: all recognized files in current directory")
+ '''
+ Here we add args for that the user can passed when making a new
+ Meson project.
+ '''
+ parser.add_argument("srcfiles", metavar="sourcefile", nargs="*", help="source files. default: all recognized files in current directory")
parser.add_argument("-n", "--name", help="project name. default: name of current directory")
parser.add_argument("-e", "--executable", help="executable name. default: project name")
parser.add_argument("-d", "--deps", help="dependencies, comma-separated")
- parser.add_argument("-l", "--language", choices=['c', 'cpp', 'd', 'fortran', 'rust', 'objc'],
+ parser.add_argument("-l", "--language", choices=['c', 'cpp', 'cs', 'cuda', 'd', 'fortran', 'java', 'rust', 'objc', 'objcpp'],
help="project language. default: autodetected based on source files")
parser.add_argument("-b", "--build", help="build after generation", action='store_true')
parser.add_argument("--builddir", help="directory for build", default='build')
- parser.add_argument("-f", "--force", action="store_true",
- help="force overwrite of existing files and directories.")
- parser.add_argument('--type', default='executable',
- choices=['executable', 'library'])
+ parser.add_argument("-f", "--force", action="store_true", help="force overwrite of existing files and directories.")
+ parser.add_argument('--type', default='executable', choices=['executable', 'library'])
parser.add_argument('--version', default='0.1')
def run(options) -> int:
+ '''
+ Here we generate the new Meson sample project.
+ '''
if not glob('*'):
autodetect_options(options, sample=True)
if not options.language:
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index dff5ecc..cfa4574 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -180,9 +180,10 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
return tlist
def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]:
- return list_buildoptions(intr.coredata)
+ subprojects = [i['name'] for i in intr.project_data['subprojects']]
+ return list_buildoptions(intr.coredata, subprojects)
-def list_buildoptions(coredata: cdata.CoreData) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]:
+def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]:
optlist = [] # type: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]
dir_option_names = ['bindir',
@@ -206,6 +207,16 @@ def list_buildoptions(coredata: cdata.CoreData) -> T.List[T.Dict[str, T.Union[st
test_options = {k: o for k, o in coredata.builtins.items() if k in test_option_names}
core_options = {k: o for k, o in coredata.builtins.items() if k in core_option_names}
+ if subprojects:
+ # Add per subproject built-in options
+ sub_core_options = {}
+ for sub in subprojects:
+ for k, o in core_options.items():
+ if o.yielding:
+ continue
+ sub_core_options[sub + ':' + k] = o
+ core_options.update(sub_core_options)
+
def add_keys(options: T.Dict[str, cdata.UserOption], section: str, machine: str = 'any') -> None:
for key in sorted(options.keys()):
opt = options[key]
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index 523ad2b..d483111 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -14,12 +14,13 @@
import typing as T
import hashlib
-from pathlib import Path, PurePath
+from pathlib import Path, PurePath, PureWindowsPath
from .. import mlog
from . import ExtensionModule
from . import ModuleReturnValue
from ..mesonlib import MesonException
+from ..interpreterbase import FeatureNew
from ..interpreterbase import stringArgs, noKwargs
if T.TYPE_CHECKING:
@@ -31,18 +32,62 @@ class FSModule(ExtensionModule):
super().__init__(interpreter)
self.snippets.add('generate_dub_file')
+ def _absolute_dir(self, state: 'ModuleState', arg: str) -> Path:
+ """
+ make an absolute path from a relative path, WITHOUT resolving symlinks
+ """
+ return Path(state.source_root) / state.subdir / Path(arg).expanduser()
+
def _resolve_dir(self, state: 'ModuleState', arg: str) -> Path:
"""
- resolves (makes absolute) a directory relative to calling meson.build,
+ resolves symlinks and makes absolute a directory relative to calling meson.build,
if not already absolute
"""
- return Path(state.source_root) / state.subdir / Path(arg).expanduser()
+ path = self._absolute_dir(state, arg)
+ try:
+ # accomodate unresolvable paths e.g. symlink loops
+ path = path.resolve()
+ except Exception:
+ # return the best we could do
+ pass
+ return path
def _check(self, check: str, state: 'ModuleState', args: T.Sequence[str]) -> ModuleReturnValue:
if len(args) != 1:
raise MesonException('fs.{} takes exactly one argument.'.format(check))
test_file = self._resolve_dir(state, args[0])
- return ModuleReturnValue(getattr(test_file, check)(), [])
+ val = getattr(test_file, check)()
+ if isinstance(val, Path):
+ val = str(val)
+ return ModuleReturnValue(val, [])
+
+ @stringArgs
+ @noKwargs
+ @FeatureNew('fs.expanduser', '0.54.0')
+ def expanduser(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
+ if len(args) != 1:
+ raise MesonException('fs.expanduser takes exactly one argument.')
+ return ModuleReturnValue(str(Path(args[0]).expanduser()), [])
+
+ @stringArgs
+ @noKwargs
+ @FeatureNew('fs.is_absolute', '0.54.0')
+ def is_absolute(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
+ if len(args) != 1:
+ raise MesonException('fs.is_absolute takes exactly one argument.')
+ return ModuleReturnValue(PurePath(args[0]).is_absolute(), [])
+
+ @stringArgs
+ @noKwargs
+ @FeatureNew('fs.as_posix', '0.54.0')
+ def as_posix(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
+ """
+ this function assumes you are passing a Windows path, even if on a Unix-like system
+ and so ALL '\' are turned to '/', even if you meant to escape a character
+ """
+ if len(args) != 1:
+ raise MesonException('fs.as_posix takes exactly one argument.')
+ return ModuleReturnValue(PureWindowsPath(args[0]).as_posix(), [])
@stringArgs
@noKwargs
@@ -52,7 +97,9 @@ class FSModule(ExtensionModule):
@stringArgs
@noKwargs
def is_symlink(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
- return self._check('is_symlink', state, args)
+ if len(args) != 1:
+ raise MesonException('fs.is_symlink takes exactly one argument.')
+ return ModuleReturnValue(self._absolute_dir(state, args[0]).is_symlink(), [])
@stringArgs
@noKwargs
@@ -68,7 +115,7 @@ class FSModule(ExtensionModule):
@noKwargs
def hash(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
if len(args) != 2:
- raise MesonException('method takes exactly two arguments.')
+ raise MesonException('fs.hash takes exactly two arguments.')
file = self._resolve_dir(state, args[0])
if not file.is_file():
raise MesonException('{} is not a file and therefore cannot be hashed'.format(file))
@@ -84,7 +131,7 @@ class FSModule(ExtensionModule):
@noKwargs
def size(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
if len(args) != 1:
- raise MesonException('method takes exactly one argument.')
+ raise MesonException('fs.size takes exactly one argument.')
file = self._resolve_dir(state, args[0])
if not file.is_file():
raise MesonException('{} is not a file and therefore cannot be sized'.format(file))
@@ -113,7 +160,7 @@ class FSModule(ExtensionModule):
@noKwargs
def replace_suffix(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
if len(args) != 2:
- raise MesonException('method takes exactly two arguments.')
+ raise MesonException('fs.replace_suffix takes exactly two arguments.')
original = PurePath(args[0])
new = original.with_suffix(args[1])
return ModuleReturnValue(str(new), [])
@@ -122,7 +169,7 @@ class FSModule(ExtensionModule):
@noKwargs
def parent(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
if len(args) != 1:
- raise MesonException('method takes exactly one argument.')
+ raise MesonException('fs.parent takes exactly one argument.')
original = PurePath(args[0])
new = original.parent
return ModuleReturnValue(str(new), [])
@@ -131,10 +178,20 @@ class FSModule(ExtensionModule):
@noKwargs
def name(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
if len(args) != 1:
- raise MesonException('method takes exactly one argument.')
+ raise MesonException('fs.name takes exactly one argument.')
original = PurePath(args[0])
new = original.name
return ModuleReturnValue(str(new), [])
+ @stringArgs
+ @noKwargs
+ @FeatureNew('fs.stem', '0.54.0')
+ def stem(self, state: 'ModuleState', args: T.Sequence[str], kwargs: dict) -> ModuleReturnValue:
+ if len(args) != 1:
+ raise MesonException('fs.stem takes exactly one argument.')
+ original = PurePath(args[0])
+ new = original.stem
+ return ModuleReturnValue(str(new), [])
+
def initialize(*args, **kwargs) -> FSModule:
return FSModule(*args, **kwargs)
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 2341bd2..da0a60e 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -256,24 +256,39 @@ class PkgConfigModule(ExtensionModule):
prefix = prefix.as_posix()
if isinstance(subdir, PurePath):
subdir = subdir.as_posix()
- if subdir.startswith(prefix):
- subdir = subdir.replace(prefix, '')
+ try:
+ if os.path.commonpath([prefix, subdir]) == prefix:
+ skip = len(prefix) + 1
+ subdir = subdir[skip:]
+ except ValueError:
+ pass
return subdir
def generate_pkgconfig_file(self, state, deps, subdirs, name, description,
- url, version, pcfile, conflicts, variables):
+ url, version, pcfile, conflicts, variables,
+ uninstalled=False):
deps.remove_dups()
coredata = state.environment.get_coredata()
- outdir = state.environment.scratch_dir
+ if uninstalled:
+ outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
+ if not os.path.exists(outdir):
+ os.mkdir(outdir)
+ prefix = PurePath(state.environment.get_build_dir())
+ srcdir = PurePath(state.environment.get_source_dir())
+ else:
+ outdir = state.environment.scratch_dir
+ prefix = PurePath(coredata.get_builtin_option('prefix'))
+ # These always return paths relative to prefix
+ libdir = PurePath(coredata.get_builtin_option('libdir'))
+ incdir = PurePath(coredata.get_builtin_option('includedir'))
fname = os.path.join(outdir, pcfile)
- prefix = PurePath(coredata.get_builtin_option('prefix'))
- # These always return paths relative to prefix
- libdir = PurePath(coredata.get_builtin_option('libdir'))
- incdir = PurePath(coredata.get_builtin_option('includedir'))
with open(fname, 'w', encoding='utf-8') as ofile:
ofile.write('prefix={}\n'.format(self._escape(prefix)))
- ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
- ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
+ if uninstalled:
+ ofile.write('srcdir={}\n'.format(self._escape(srcdir)))
+ else:
+ ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
+ ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
if variables:
ofile.write('\n')
for k, v in variables:
@@ -303,17 +318,20 @@ class PkgConfigModule(ExtensionModule):
if isinstance(l, str):
yield l
else:
- install_dir = l.get_custom_install_dir()[0]
+ if uninstalled:
+ install_dir = os.path.dirname(state.backend.get_target_filename_abs(l))
+ else:
+ install_dir = l.get_custom_install_dir()[0]
if install_dir is False:
continue
if 'cs' in l.compilers:
if isinstance(install_dir, str):
- Lflag = '-r${prefix}/%s/%s ' % (self._escape(self._make_relative(prefix, install_dir)), l.filename)
+ Lflag = '-r${prefix}/%s/%s' % (self._escape(self._make_relative(prefix, install_dir)), l.filename)
else: # install_dir is True
Lflag = '-r${libdir}/%s' % l.filename
else:
if isinstance(install_dir, str):
- Lflag = '-L${prefix}/%s ' % self._escape(self._make_relative(prefix, install_dir))
+ Lflag = '-L${prefix}/%s' % self._escape(self._make_relative(prefix, install_dir))
else: # install_dir is True
Lflag = '-L${libdir}'
if Lflag not in Lflags:
@@ -327,22 +345,47 @@ class PkgConfigModule(ExtensionModule):
if 'cs' not in l.compilers:
yield '-l%s' % lname
+ def get_uninstalled_include_dirs(libs):
+ result = []
+ for l in libs:
+ if isinstance(l, str):
+ continue
+ if l.get_subdir() not in result:
+ result.append(l.get_subdir())
+ for i in l.get_include_dirs():
+ curdir = i.get_curdir()
+ for d in i.get_incdirs():
+ path = os.path.join(curdir, d)
+ if path not in result:
+ result.append(path)
+ return result
+
+ def generate_uninstalled_cflags(libs):
+ for d in get_uninstalled_include_dirs(libs):
+ for basedir in ['${prefix}', '${srcdir}']:
+ path = os.path.join(basedir, d)
+ yield '-I%s' % self._escape(path)
+
if len(deps.pub_libs) > 0:
ofile.write('Libs: {}\n'.format(' '.join(generate_libs_flags(deps.pub_libs))))
if len(deps.priv_libs) > 0:
ofile.write('Libs.private: {}\n'.format(' '.join(generate_libs_flags(deps.priv_libs))))
ofile.write('Cflags:')
- for h in subdirs:
- ofile.write(' ')
- if h == '.':
- ofile.write('-I${includedir}')
- else:
- ofile.write(self._escape(PurePath('-I${includedir}') / h))
+ if uninstalled:
+ ofile.write(' '.join(generate_uninstalled_cflags(deps.pub_libs + deps.priv_libs)))
+ else:
+ for h in subdirs:
+ ofile.write(' ')
+ if h == '.':
+ ofile.write('-I${includedir}')
+ else:
+ ofile.write(self._escape(PurePath('-I${includedir}') / h))
for f in deps.cflags:
ofile.write(' ')
ofile.write(self._escape(f))
ofile.write('\n')
+ @FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
@FeatureNewKwargs('pkgconfig.generate', '0.41.0', ['variables'])
@permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
@@ -447,6 +490,11 @@ class PkgConfigModule(ExtensionModule):
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables)
res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot)
+ variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('uninstalled_variables', [])))
+ pcfile = filebase + '-uninstalled.pc'
+ self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
+ version, pcfile, conflicts, variables,
+ uninstalled=True)
# Associate the main library with this generated pc file. If the library
# is used in any subsequent call to the generated, it will generate a
# 'Requires:' or 'Requires.private:'.
diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py
new file mode 100644
index 0000000..baf2e8e
--- /dev/null
+++ b/mesonbuild/templates/cstemplates.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.
+import re
+
+
+hello_cs_template = '''using System;
+
+public class {class_name} {{
+ const String PROJECT_NAME = "{project_name}";
+
+ static int Main(String[] args) {{
+ if (args.Length > 0) {{
+ System.Console.WriteLine(String.Format("{project_name} takes no arguments.."));
+ return 1;
+ }}
+ Console.WriteLine(String.Format("This is project {{0}}.", PROJECT_NAME));
+ return 0;
+ }}
+}}
+
+'''
+
+hello_cs_meson_template = '''project('{project_name}', 'cs',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+exe = executable('{exe_name}', '{source_name}',
+ install : true)
+
+test('basic', exe)
+'''
+
+lib_cs_template = '''
+public class {class_name} {{
+ private const int number = 6;
+
+ public int get_number() {{
+ return number;
+ }}
+}}
+
+'''
+
+lib_cs_test_template = '''using System;
+
+public class {class_test} {{
+ static int Main(String[] args) {{
+ if (args.Length > 0) {{
+ System.Console.WriteLine("{project_name} takes no arguments..");
+ return 1;
+ }}
+ {class_name} c = new {class_name}();
+ Boolean result = true;
+ return result.CompareTo(c.get_number() != 6);
+ }}
+}}
+
+'''
+
+lib_cs_meson_template = '''project('{project_name}', 'cs',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+stlib = shared_library('{lib_name}', '{source_file}',
+ install : true,
+)
+
+test_exe = executable('{test_exe_name}', '{test_source_file}',
+ link_with : stlib)
+test('{test_name}', test_exe)
+
+# Make this library usable as a Meson subproject.
+{ltoken}_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ link_with : stlib)
+
+'''
+
+
+def create_exe_cs_sample(project_name, project_version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ class_name = uppercase_token[0] + lowercase_token[1:]
+ source_name = uppercase_token[0] + lowercase_token[1:] + '.cs'
+ open(source_name, 'w').write(hello_cs_template.format(project_name=project_name,
+ class_name=class_name))
+ open('meson.build', 'w').write(hello_cs_meson_template.format(project_name=project_name,
+ exe_name=project_name,
+ source_name=source_name,
+ version=project_version))
+
+
+def create_lib_cs_sample(project_name, version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ class_name = uppercase_token[0] + lowercase_token[1:]
+ class_test = uppercase_token[0] + lowercase_token[1:] + '_test'
+ project_test = lowercase_token + '_test'
+ lib_cs_name = uppercase_token[0] + lowercase_token[1:] + '.cs'
+ test_cs_name = uppercase_token[0] + lowercase_token[1:] + '_test.cs'
+ kwargs = {'utoken': uppercase_token,
+ 'ltoken': lowercase_token,
+ 'class_test': class_test,
+ 'class_name': class_name,
+ 'source_file': lib_cs_name,
+ 'test_source_file': test_cs_name,
+ 'test_exe_name': project_test,
+ 'project_name': project_name,
+ 'lib_name': lowercase_token,
+ 'test_name': lowercase_token,
+ 'version': version,
+ }
+ open(lib_cs_name, 'w').write(lib_cs_template.format(**kwargs))
+ open(test_cs_name, 'w').write(lib_cs_test_template.format(**kwargs))
+ open('meson.build', 'w').write(lib_cs_meson_template.format(**kwargs))
diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py
new file mode 100644
index 0000000..d083fe8
--- /dev/null
+++ b/mesonbuild/templates/cudatemplates.py
@@ -0,0 +1,177 @@
+# 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.
+import re
+
+
+hello_cuda_template = '''#include <iostream>
+
+#define PROJECT_NAME "{project_name}"
+
+int main(int argc, char **argv) {{
+ if(argc != 1) {{
+ std::cout << argv[0] << "takes no arguments.\\n";
+ return 1;
+ }}
+ std::cout << "This is project " << PROJECT_NAME << ".\\n";
+ return 0;
+}}
+'''
+
+hello_cuda_meson_template = '''project('{project_name}', 'cpp',
+ version : '{version}',
+ default_options : ['warning_level=3',
+ 'cpp_std=c++14'])
+
+exe = executable('{exe_name}', '{source_name}',
+ install : true)
+
+test('basic', exe)
+'''
+
+lib_h_template = '''#pragma once
+#if defined _WIN32 || defined __CYGWIN__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC __declspec(dllimport)
+ #endif
+#else
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
+ #else
+ #define {utoken}_PUBLIC
+ #endif
+#endif
+
+namespace {namespace} {{
+
+class {utoken}_PUBLIC {class_name} {{
+
+public:
+ {class_name}();
+ int get_number() const;
+
+private:
+
+ int number;
+
+}};
+
+}}
+
+'''
+
+lib_cuda_template = '''#include <{header_file}>
+
+namespace {namespace} {{
+
+{class_name}::{class_name}() {{
+ number = 6;
+}}
+
+int {class_name}::get_number() const {{
+ return number;
+}}
+
+}}
+'''
+
+lib_cuda_test_template = '''#include <{header_file}>
+#include <iostream>
+
+int main(int argc, char **argv) {{
+ if(argc != 1) {{
+ std::cout << argv[0] << " takes no arguments.\\n";
+ return 1;
+ }}
+ {namespace}::{class_name} c;
+ return c.get_number() != 6;
+}}
+'''
+
+lib_cuda_meson_template = '''project('{project_name}', 'cuda',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+# These arguments are only used to build the shared library
+# not the executables that use the library.
+lib_args = ['-DBUILDING_{utoken}']
+
+shlib = shared_library('{lib_name}', '{source_file}',
+ install : true,
+ cpp_args : lib_args,
+ gnu_symbol_visibility : 'hidden',
+)
+
+test_exe = executable('{test_exe_name}', '{test_source_file}',
+ link_with : shlib)
+test('{test_name}', test_exe)
+
+# Make this library usable as a Meson subproject.
+{ltoken}_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ link_with : shlib)
+
+# Make this library usable from the system's
+# package manager.
+install_headers('{header_file}', subdir : '{header_dir}')
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(
+ name : '{project_name}',
+ filebase : '{ltoken}',
+ description : 'Meson sample project.',
+ subdirs : '{header_dir}',
+ libraries : shlib,
+ version : '{version}',
+)
+'''
+
+
+def create_exe_cuda_sample(project_name, project_version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ source_name = lowercase_token + '.cu'
+ open(source_name, 'w').write(hello_cuda_template.format(project_name=project_name))
+ open('meson.build', 'w').write(hello_cuda_meson_template.format(project_name=project_name,
+ exe_name=lowercase_token,
+ source_name=source_name,
+ version=project_version))
+
+
+def create_lib_cuda_sample(project_name, version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ class_name = uppercase_token[0] + lowercase_token[1:]
+ namespace = lowercase_token
+ lib_h_name = lowercase_token + '.h'
+ lib_cuda_name = lowercase_token + '.cu'
+ test_cuda_name = lowercase_token + '_test.cu'
+ kwargs = {'utoken': uppercase_token,
+ 'ltoken': lowercase_token,
+ 'header_dir': lowercase_token,
+ 'class_name': class_name,
+ 'namespace': namespace,
+ 'header_file': lib_h_name,
+ 'source_file': lib_cuda_name,
+ 'test_source_file': test_cuda_name,
+ 'test_exe_name': lowercase_token,
+ 'project_name': project_name,
+ 'lib_name': lowercase_token,
+ 'test_name': lowercase_token,
+ 'version': version,
+ }
+ open(lib_h_name, 'w').write(lib_h_template.format(**kwargs))
+ open(lib_cuda_name, 'w').write(lib_cuda_template.format(**kwargs))
+ open(test_cuda_name, 'w').write(lib_cuda_test_template.format(**kwargs))
+ open('meson.build', 'w').write(lib_cuda_meson_template.format(**kwargs))
diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py
new file mode 100644
index 0000000..e8a8c15
--- /dev/null
+++ b/mesonbuild/templates/javatemplates.py
@@ -0,0 +1,129 @@
+# 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.
+import re
+
+
+hello_java_template = '''
+
+public class {class_name} {{
+ final static String PROJECT_NAME = "{project_name}";
+
+ public static void main (String args[]) {{
+ if(args.length != 0) {{
+ System.out.println(args + " takes no arguments.");
+ System.exit(0);
+ }}
+ System.out.println("This is project " + PROJECT_NAME + ".");
+ System.exit(0);
+ }}
+}}
+
+'''
+
+hello_java_meson_template = '''project('{project_name}', 'java',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+exe = jar('{exe_name}', '{source_name}',
+ main_class : '{exe_name}',
+ install : true)
+
+test('basic', exe)
+'''
+
+lib_java_template = '''
+
+public class {class_name} {{
+ final static int number = 6;
+
+ public final int get_number() {{
+ return number;
+ }}
+}}
+
+'''
+
+lib_java_test_template = '''
+
+public class {class_test} {{
+ public static void main (String args[]) {{
+ if(args.length != 0) {{
+ System.out.println(args + " takes no arguments.");
+ System.exit(1);
+ }}
+
+ {class_name} c = new {class_name}();
+ Boolean result = true;
+ System.exit(result.compareTo(c.get_number() != 6));
+ }}
+}}
+
+'''
+
+lib_java_meson_template = '''project('{project_name}', 'java',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+jarlib = jar('{class_name}', '{source_file}',
+ main_class : '{class_name}',
+ install : true,
+)
+
+test_jar = jar('{class_test}', '{test_source_file}',
+ main_class : '{class_test}',
+ link_with : jarlib)
+test('{test_name}', test_jar)
+
+# Make this library usable as a Meson subproject.
+{ltoken}_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ link_with : jarlib)
+'''
+
+
+def create_exe_java_sample(project_name, project_version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ class_name = uppercase_token[0] + lowercase_token[1:]
+ source_name = uppercase_token[0] + lowercase_token[1:] + '.java'
+ open(source_name, 'w').write(hello_java_template.format(project_name=project_name,
+ class_name=class_name))
+ open('meson.build', 'w').write(hello_java_meson_template.format(project_name=project_name,
+ exe_name=class_name,
+ source_name=source_name,
+ version=project_version))
+
+
+def create_lib_java_sample(project_name, version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ class_name = uppercase_token[0] + lowercase_token[1:]
+ class_test = uppercase_token[0] + lowercase_token[1:] + '_test'
+ lib_java_name = uppercase_token[0] + lowercase_token[1:] + '.java'
+ test_java_name = uppercase_token[0] + lowercase_token[1:] + '_test.java'
+ kwargs = {'utoken': uppercase_token,
+ 'ltoken': lowercase_token,
+ 'class_test': class_test,
+ 'class_name': class_name,
+ 'source_file': lib_java_name,
+ 'test_source_file': test_java_name,
+ 'test_exe_name': lowercase_token,
+ 'project_name': project_name,
+ 'lib_name': lowercase_token,
+ 'test_name': lowercase_token,
+ 'version': version,
+ }
+ open(lib_java_name, 'w').write(lib_java_template.format(**kwargs))
+ open(test_java_name, 'w').write(lib_java_test_template.format(**kwargs))
+ open('meson.build', 'w').write(lib_java_meson_template.format(**kwargs))
diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py
new file mode 100644
index 0000000..f9dd442
--- /dev/null
+++ b/mesonbuild/templates/mesontemplates.py
@@ -0,0 +1,51 @@
+# 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.
+
+meson_executable_template = '''project('{project_name}', '{language}',
+ version : '{version}',
+ default_options : [{default_options}])
+
+executable('{executable}',
+ {sourcespec},{depspec}
+ install : true)
+'''
+
+def create_meson_build(options):
+ if options.type != 'executable':
+ raise SystemExit('\nGenerating a meson.build file from existing sources is\n'
+ 'supported only for project type "executable".\n'
+ 'Run meson init in an empty directory to create a sample project.')
+ default_options = ['warning_level=3']
+ if options.language == 'cpp':
+ # This shows how to set this very common option.
+ default_options += ['cpp_std=c++14']
+ # If we get a meson.build autoformatter one day, this code could
+ # be simplified quite a bit.
+ formatted_default_options = ', '.join("'{}'".format(x) for x in default_options)
+ sourcespec = ',\n '.join("'{}'".format(x) for x in options.srcfiles)
+ depspec = ''
+ if options.deps:
+ depspec = '\n dependencies : [\n '
+ depspec += ',\n '.join("dependency('{}')".format(x)
+ for x in options.deps.split(','))
+ depspec += '],'
+ content = meson_executable_template.format(project_name=options.name,
+ language=options.language,
+ version=options.version,
+ executable=options.executable,
+ sourcespec=sourcespec,
+ depspec=depspec,
+ default_options=formatted_default_options)
+ open('meson.build', 'w').write(content)
+ print('Generated meson.build file:\n\n' + content)
diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py
new file mode 100644
index 0000000..329a568
--- /dev/null
+++ b/mesonbuild/templates/objcpptemplates.py
@@ -0,0 +1,156 @@
+# 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.
+import re
+
+
+lib_h_template = '''#pragma once
+#if defined _WIN32 || defined __CYGWIN__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC __declspec(dllimport)
+ #endif
+#else
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
+ #else
+ #define {utoken}_PUBLIC
+ #endif
+#endif
+
+int {utoken}_PUBLIC {function_name}();
+
+'''
+
+lib_objcpp_template = '''#import <{header_file}>
+
+/* This function will not be exported and is not
+ * directly callable by users of this library.
+ */
+int internal_function() {{
+ return 0;
+}}
+
+int {function_name}() {{
+ return internal_function();
+}}
+'''
+
+lib_objcpp_test_template = '''#import <{header_file}>
+#import <iostream>
+
+int main(int argc, char **argv) {{
+ if(argc != 1) {{
+ std::cout << argv[0] << " takes no arguments." << std::endl;
+ return 1;
+ }}
+ return {function_name}();
+}}
+'''
+
+lib_objcpp_meson_template = '''project('{project_name}', 'objcpp',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+# These arguments are only used to build the shared library
+# not the executables that use the library.
+lib_args = ['-DBUILDING_{utoken}']
+
+shlib = shared_library('{lib_name}', '{source_file}',
+ install : true,
+ objcpp_args : lib_args,
+ gnu_symbol_visibility : 'hidden',
+)
+
+test_exe = executable('{test_exe_name}', '{test_source_file}',
+ link_with : shlib)
+test('{test_name}', test_exe)
+
+# Make this library usable as a Meson subproject.
+{ltoken}_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ link_with : shlib)
+
+# Make this library usable from the system's
+# package manager.
+install_headers('{header_file}', subdir : '{header_dir}')
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(
+ name : '{project_name}',
+ filebase : '{ltoken}',
+ description : 'Meson sample project.',
+ subdirs : '{header_dir}',
+ libraries : shlib,
+ version : '{version}',
+)
+'''
+
+hello_objcpp_template = '''#import <iostream>
+
+#define PROJECT_NAME "{project_name}"
+
+int main(int argc, char **argv) {{
+ if(argc != 1) {{
+ std::cout << argv[0] << " takes no arguments." << std::endl;
+ return 1;
+ }}
+ std::cout << "This is project " << PROJECT_NAME << "." << std::endl;
+ return 0;
+}}
+'''
+
+hello_objcpp_meson_template = '''project('{project_name}', 'objcpp',
+ version : '{version}',
+ default_options : ['warning_level=3'])
+
+exe = executable('{exe_name}', '{source_name}',
+ install : true)
+
+test('basic', exe)
+'''
+
+def create_exe_objcpp_sample(project_name, project_version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ source_name = lowercase_token + '.mm'
+ open(source_name, 'w').write(hello_objcpp_template.format(project_name=project_name))
+ open('meson.build', 'w').write(hello_objcpp_meson_template.format(project_name=project_name,
+ exe_name=lowercase_token,
+ source_name=source_name,
+ version=project_version))
+
+def create_lib_objcpp_sample(project_name, version):
+ lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower())
+ uppercase_token = lowercase_token.upper()
+ function_name = lowercase_token[0:3] + '_func'
+ lib_h_name = lowercase_token + '.h'
+ lib_objcpp_name = lowercase_token + '.mm'
+ test_objcpp_name = lowercase_token + '_test.mm'
+ kwargs = {'utoken': uppercase_token,
+ 'ltoken': lowercase_token,
+ 'header_dir': lowercase_token,
+ 'function_name': function_name,
+ 'header_file': lib_h_name,
+ 'source_file': lib_objcpp_name,
+ 'test_source_file': test_objcpp_name,
+ 'test_exe_name': lowercase_token,
+ 'project_name': project_name,
+ 'lib_name': lowercase_token,
+ 'test_name': lowercase_token,
+ 'version': version,
+ }
+ open(lib_h_name, 'w').write(lib_h_template.format(**kwargs))
+ open(lib_objcpp_name, 'w').write(lib_objcpp_template.format(**kwargs))
+ open(test_objcpp_name, 'w').write(lib_objcpp_test_template.format(**kwargs))
+ open('meson.build', 'w').write(lib_objcpp_meson_template.format(**kwargs))
diff --git a/run_project_tests.py b/run_project_tests.py
index 2288abf..aac9f9d 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -15,6 +15,7 @@
# limitations under the License.
import typing as T
+import functools
import itertools
import os
import subprocess
@@ -76,6 +77,28 @@ class TestResult:
self.testtime = testtime
+@functools.total_ordering
+class TestDef:
+ def __init__(self, path: Path, name: T.Optional[str], args: T.List[str], skip: bool = False):
+ self.path = path
+ self.name = name
+ self.args = args
+ self.skip = skip
+
+ def __repr__(self) -> str:
+ return '<{}: {:<48} [{}: {}] -- {}>'.format(type(self).__name__, str(self.path), self.name, self.args, self.skip)
+
+ def display_name(self) -> str:
+ if self.name:
+ return '{} ({})'.format(self.path.as_posix(), self.name)
+ return self.path.as_posix()
+
+ def __lt__(self, other: T.Any) -> T.Union[bool, type(NotImplemented)]:
+ if isinstance(other, TestDef):
+ # None is not sortable, so replace it with an empty string
+ return (self.path, self.name or '') < (other.path, other.name or '')
+ return NotImplemented
+
class AutoDeletedDir:
def __init__(self, d):
self.dir = d
@@ -343,17 +366,19 @@ def parse_test_args(testdir):
# Build directory name must be the same so Ccache works over
# consecutive invocations.
-def create_deterministic_builddir(src_dir):
+def create_deterministic_builddir(src_dir, name):
import hashlib
+ if name:
+ src_dir += name
rel_dirname = 'b ' + hashlib.sha256(src_dir.encode(errors='ignore')).hexdigest()[0:10]
os.mkdir(rel_dirname)
abs_pathname = os.path.join(os.getcwd(), rel_dirname)
return abs_pathname
-def run_test(skipped, testdir, extra_args, compiler, backend, flags, commands, should_fail):
+def run_test(skipped, testdir, name, extra_args, compiler, backend, flags, commands, should_fail):
if skipped:
return None
- with AutoDeletedDir(create_deterministic_builddir(testdir)) as build_dir:
+ with AutoDeletedDir(create_deterministic_builddir(testdir, name)) as build_dir:
with AutoDeletedDir(tempfile.mkdtemp(prefix='i ', dir=os.getcwd())) as install_dir:
try:
return _run_test(testdir, build_dir, install_dir, extra_args, compiler, backend, flags, commands, should_fail)
@@ -475,13 +500,53 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
return TestResult(validate_install(testdir, install_dir, compiler, builddata.environment),
BuildStep.validate, stdo, stde, mesonlog, cicmds, gen_time, build_time, test_time)
-def gather_tests(testdir: Path) -> T.List[Path]:
- test_names = [t.name for t in testdir.glob('*') if t.is_dir()]
- test_names = [t for t in test_names if not t.startswith('.')] # Filter non-tests files (dot files, etc)
- test_nums = [(int(t.split()[0]), t) for t in test_names]
- test_nums.sort()
- tests = [testdir / t[1] for t in test_nums]
- return tests
+def gather_tests(testdir: Path) -> T.Iterator[TestDef]:
+ tests = [t.name for t in testdir.glob('*') if t.is_dir()]
+ tests = [t for t in tests if not t.startswith('.')] # Filter non-tests files (dot files, etc)
+ tests = [TestDef(testdir / t, None, []) for t in tests]
+ all_tests = []
+ for t in tests:
+ matrix_file = t.path / 'test_matrix.json'
+ if not matrix_file.is_file():
+ all_tests += [t]
+ continue
+
+ # Build multiple tests from matrix definition
+ opt_list = [] # type: T.List[T.List[T.Tuple[str, bool]]]
+ matrix = json.loads(matrix_file.read_text())
+ assert "options" in matrix
+ for key, val in matrix["options"].items():
+ assert isinstance(val, list)
+ tmp_opts = [] # type: T.List[T.Tuple[str, bool]]
+ for i in val:
+ assert isinstance(i, dict)
+ assert "val" in i
+ skip = False
+
+ # Skip the matrix entry if environment variable is present
+ if 'skip_on_env' in i:
+ for env in i['skip_on_env']:
+ if env in os.environ:
+ skip = True
+
+ tmp_opts += [('{}={}'.format(key, i['val']), skip)]
+
+ if opt_list:
+ new_opt_list = [] # type: T.List[T.List[T.Tuple[str, bool]]]
+ for i in opt_list:
+ for j in tmp_opts:
+ new_opt_list += [[*i, j]]
+ opt_list = new_opt_list
+ else:
+ opt_list = [[x] for x in tmp_opts]
+
+ for i in opt_list:
+ name = ' '.join([x[0] for x in i])
+ opts = ['-D' + x[0] for x in i]
+ skip = any([x[1] for x in i])
+ all_tests += [TestDef(t.path, name, opts, skip)]
+
+ return sorted(all_tests)
def have_d_compiler():
if shutil.which("ldc2"):
@@ -636,7 +701,7 @@ def should_skip_rust(backend: Backend) -> bool:
return True
return False
-def detect_tests_to_run(only: T.List[str]) -> T.List[T.Tuple[str, T.List[Path], bool]]:
+def detect_tests_to_run(only: T.List[str]) -> T.List[T.Tuple[str, T.List[TestDef], bool]]:
"""
Parameters
----------
@@ -645,7 +710,7 @@ def detect_tests_to_run(only: T.List[str]) -> T.List[T.Tuple[str, T.List[Path],
Returns
-------
- gathered_tests: list of tuple of str, list of pathlib.Path, bool
+ gathered_tests: list of tuple of str, list of TestDef, bool
tests to run
"""
@@ -695,7 +760,7 @@ def detect_tests_to_run(only: T.List[str]) -> T.List[T.Tuple[str, T.List[Path],
gathered_tests = [(name, gather_tests(Path('test cases', subdir)), skip) for name, subdir, skip in all_tests]
return gathered_tests
-def run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
+def run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]],
log_name_base: str, failfast: bool,
extra_args: T.List[str]) -> T.Tuple[int, int, int]:
global logfile
@@ -704,7 +769,7 @@ def run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
logfile = lf
return _run_tests(all_tests, log_name_base, failfast, extra_args)
-def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
+def _run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]],
log_name_base: str, failfast: bool,
extra_args: T.List[str]) -> T.Tuple[int, int, int]:
global stop, executor, futures, system_compiler
@@ -746,8 +811,10 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
for t in test_cases:
# Jenkins screws us over by automatically sorting test cases by name
# and getting it wrong by not doing logical number sorting.
- (testnum, testbase) = t.name.split(' ', 1)
+ (testnum, testbase) = t.path.name.split(' ', 1)
testname = '%.3d %s' % (int(testnum), testbase)
+ if t.name:
+ testname += ' ({})'.format(t.name)
should_fail = False
suite_args = []
if name.startswith('failing'):
@@ -756,7 +823,7 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
suite_args = ['--fatal-meson-warnings']
should_fail = name.split('warning-')[1]
- result = executor.submit(run_test, skipped, t.as_posix(), extra_args + suite_args,
+ result = executor.submit(run_test, skipped or t.skip, t.path.as_posix(), t.name, extra_args + suite_args + t.args,
system_compiler, backend, backend_flags, commands, should_fail)
futures.append((testname, t, result))
for (testname, t, result) in futures:
@@ -765,8 +832,8 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
result = result.result()
except CancelledError:
continue
- if (result is None) or (('MESON_SKIP_TEST' in result.stdo) and (skippable(name, t.as_posix()))):
- print(yellow('Skipping:'), t.as_posix())
+ if (result is None) or (('MESON_SKIP_TEST' in result.stdo) and (skippable(name, t.path.as_posix()))):
+ print(yellow('Skipping:'), t.display_name())
current_test = ET.SubElement(current_suite, 'testcase', {'name': testname,
'classname': name})
ET.SubElement(current_test, 'skipped', {})
@@ -774,7 +841,7 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
else:
without_install = "" if len(install_commands) > 0 else " (without install)"
if result.msg != '':
- print(red('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t.as_posix())))
+ print(red('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t.display_name())))
print('Reason:', result.msg)
failing_tests += 1
if result.step == BuildStep.configure and result.mlog != no_meson_log_msg:
@@ -798,13 +865,13 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[Path], bool]],
for (_, _, res) in futures:
res.cancel()
else:
- print('Succeeded test%s: %s' % (without_install, t.as_posix()))
+ print('Succeeded test%s: %s' % (without_install, t.display_name()))
passing_tests += 1
conf_time += result.conftime
build_time += result.buildtime
test_time += result.testtime
total_time = conf_time + build_time + test_time
- log_text_file(logfile, t, result.stdo, result.stde)
+ log_text_file(logfile, t.path, result.stdo, result.stde)
current_test = ET.SubElement(current_suite, 'testcase', {'name': testname,
'classname': name,
'time': '%.3f' % total_time})
@@ -994,7 +1061,7 @@ if __name__ == '__main__':
except UnicodeError:
print(l.encode('ascii', errors='replace').decode(), '\n')
for name, dirs, _ in all_tests:
- dir_names = (x.name for x in dirs)
+ dir_names = list(set(x.path.name for x in dirs))
for k, g in itertools.groupby(dir_names, key=lambda x: x.split()[0]):
tests = list(g)
if len(tests) != 1:
diff --git a/run_unittests.py b/run_unittests.py
index 5388679..642a05f 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1857,8 +1857,14 @@ class AllPlatformTests(BasePlatformTests):
# N.B. We don't check 'libdir' as it's platform dependent, see
# default_libdir():
}
+
+ if mesonbuild.mesonlib.default_prefix() == '/usr/local':
+ expected[None] = expected['/usr/local']
+
for prefix in expected:
- args = ['--prefix', prefix]
+ args = []
+ if prefix:
+ args += ['--prefix', prefix]
self.init(testdir, extra_args=args, default_args=False)
opts = self.introspect('--buildoptions')
for opt in opts:
@@ -3119,15 +3125,28 @@ int main(int argc, char **argv) {
except EnvironmentException:
pass
try:
+ env.detect_cs_compiler(MachineChoice.HOST)
+ langs.append('cs')
+ except EnvironmentException:
+ pass
+ try:
env.detect_d_compiler(MachineChoice.HOST)
langs.append('d')
except EnvironmentException:
pass
try:
+ env.detect_java_compiler(MachineChoice.HOST)
+ langs.append('java')
+ except EnvironmentException:
+ pass
+ try:
+ env.detect_cuda_compiler(MachineChoice.HOST)
+ langs.append('cuda')
+ except EnvironmentException:
+ pass
+ try:
env.detect_fortran_compiler(MachineChoice.HOST)
- if is_windows() or platform.machine().lower() != 'e2k':
- # Elbrus Fortran compiler can't generate debug information
- langs.append('fortran')
+ langs.append('fortran')
except EnvironmentException:
pass
try:
@@ -3135,6 +3154,11 @@ int main(int argc, char **argv) {
langs.append('objc')
except EnvironmentException:
pass
+ try:
+ env.detect_objcpp_compiler(MachineChoice.HOST)
+ langs.append('objcpp')
+ except EnvironmentException:
+ pass
# FIXME: omitting rust as Windows AppVeyor CI finds Rust but doesn't link correctly
for lang in langs:
@@ -4254,6 +4278,7 @@ recommended as it is not supported on some platforms''')
A list: string
1
True
+ empty list:
A number: 1
yes: YES
no: NO
@@ -5004,6 +5029,21 @@ class LinuxlikeTests(BasePlatformTests):
out = self._run(cmd + ['--libs'], override_envvars=env).strip().split()
self.assertEqual(out, ['-llibmain2', '-llibinternal'])
+ def test_pkgconfig_uninstalled(self):
+ testdir = os.path.join(self.common_test_dir, '47 pkgconfig-gen')
+ self.init(testdir)
+ self.build()
+
+ os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(self.builddir, 'meson-uninstalled')
+ if is_cygwin():
+ os.environ['PATH'] += os.pathsep + self.builddir
+
+ self.new_builddir()
+ testdir = os.path.join(self.common_test_dir, '47 pkgconfig-gen', 'dependencies')
+ self.init(testdir)
+ self.build()
+ self.run_tests()
+
def test_pkg_unfound(self):
testdir = os.path.join(self.unit_test_dir, '23 unfound pkgconfig')
self.init(testdir)
diff --git a/test cases/cmake/1 basic/meson.build b/test cases/cmake/1 basic/meson.build
index a23063d..8e1671a 100644
--- a/test cases/cmake/1 basic/meson.build
+++ b/test cases/cmake/1 basic/meson.build
@@ -3,10 +3,10 @@ project('cmakeSubTest', ['c', 'cpp'])
cm = import('cmake')
sub_pro = cm.subproject('cmMod')
-sub_dep = sub_pro.dependency('cmModLib')
+sub_dep = sub_pro.dependency('cmModLib++')
-assert(sub_pro.target_list() == ['cmModLib'], 'There should be exactly one target')
-assert(sub_pro.target_type('cmModLib') == 'shared_library', 'Target type should be shared_library')
+assert(sub_pro.target_list() == ['cmModLib++'], 'There should be exactly one target')
+assert(sub_pro.target_type('cmModLib++') == 'shared_library', 'Target type should be shared_library')
exe1 = executable('main', ['main.cpp'], dependencies: [sub_dep])
test('test1', exe1)
diff --git a/test cases/cmake/1 basic/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/1 basic/subprojects/cmMod/CMakeLists.txt
index 8b2f7e9..9798209 100644
--- a/test cases/cmake/1 basic/subprojects/cmMod/CMakeLists.txt
+++ b/test cases/cmake/1 basic/subprojects/cmMod/CMakeLists.txt
@@ -7,6 +7,6 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_definitions("-DDO_NOTHING_JUST_A_FLAG=1")
-add_library(cmModLib SHARED cmMod.cpp)
+add_library(cmModLib++ SHARED cmMod.cpp)
include(GenerateExportHeader)
-generate_export_header(cmModLib)
+generate_export_header(cmModLib++)
diff --git a/test cases/cmake/1 basic/subprojects/cmMod/cmMod.hpp b/test cases/cmake/1 basic/subprojects/cmMod/cmMod.hpp
index 52f576b..0e6dc04 100644
--- a/test cases/cmake/1 basic/subprojects/cmMod/cmMod.hpp
+++ b/test cases/cmake/1 basic/subprojects/cmMod/cmMod.hpp
@@ -1,13 +1,14 @@
#pragma once
+#include "cmmodlib++_export.h"
#include <string>
-#include "cmmodlib_export.h"
-class CMMODLIB_EXPORT cmModClass {
- private:
- std::string str;
- public:
- cmModClass(std::string foo);
+class CMMODLIB___EXPORT cmModClass {
+private:
+ std::string str;
- std::string getStr() const;
+public:
+ cmModClass(std::string foo);
+
+ std::string getStr() const;
};
diff --git a/test cases/cmake/16 threads/meson.build b/test cases/cmake/16 threads/meson.build
index d7ade1c..5efd73e 100644
--- a/test cases/cmake/16 threads/meson.build
+++ b/test cases/cmake/16 threads/meson.build
@@ -1,8 +1,12 @@
project('cmMod', ['c', 'cpp'])
-cm = import('cmake')
-cmMod = cm.subproject('cmMod')
-cmModDep = cmMod.dependency('cmModLib')
+cm = import('cmake')
+cmOpts = ['-DUSE_PTHREAD=@0@'.format(get_option('use_pthread'))]
+cmMod = cm.subproject('cmMod', cmake_options: cmOpts)
+cmModDep1 = cmMod.dependency('cmModLib')
+cmModDep2 = cmMod.dependency('cmModLib_shared')
-exe1 = executable('exe1', ['main.cpp'], dependencies: [cmModDep])
+exe1 = executable('exe1', ['main.cpp'], dependencies: [cmModDep1])
+exe2 = executable('exe2', ['main.cpp'], dependencies: [cmModDep2])
test('exe1_OK', exe1)
+test('exe2_OK', exe2)
diff --git a/test cases/cmake/16 threads/meson_options.txt b/test cases/cmake/16 threads/meson_options.txt
new file mode 100644
index 0000000..1fd9068
--- /dev/null
+++ b/test cases/cmake/16 threads/meson_options.txt
@@ -0,0 +1 @@
+option('use_pthread', type: 'combo', choices: ['ON', 'OFF', 'NOT_SET'], value: 'ON')
diff --git a/test cases/cmake/16 threads/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/16 threads/subprojects/cmMod/CMakeLists.txt
index 37d32c1..442a60e 100644
--- a/test cases/cmake/16 threads/subprojects/cmMod/CMakeLists.txt
+++ b/test cases/cmake/16 threads/subprojects/cmMod/CMakeLists.txt
@@ -3,7 +3,13 @@ cmake_minimum_required(VERSION 3.5)
project(cmMod CXX)
set (CMAKE_CXX_STANDARD 14)
+if(NOT USE_PTHREAD STREQUAL NOT_SET)
+ set(THREADS_PREFER_PTHREAD_FLAG ${USE_PTHREAD})
+endif()
find_package(Threads)
add_library(cmModLib STATIC cmMod.cpp)
target_link_libraries(cmModLib PRIVATE Threads::Threads)
+
+add_library(cmModLib_shared SHARED cmMod.cpp)
+target_link_libraries(cmModLib_shared PUBLIC Threads::Threads)
diff --git a/test cases/cmake/16 threads/subprojects/cmMod/cmMod.hpp b/test cases/cmake/16 threads/subprojects/cmMod/cmMod.hpp
index 1c85a8b..81c5ec8 100644
--- a/test cases/cmake/16 threads/subprojects/cmMod/cmMod.hpp
+++ b/test cases/cmake/16 threads/subprojects/cmMod/cmMod.hpp
@@ -1,6 +1,17 @@
#pragma once
-class CmMod {
+#if defined _WIN32 || defined __CYGWIN__
+#define DLL_PUBLIC __declspec(dllexport)
+#else
+#if defined __GNUC__
+#define DLL_PUBLIC __attribute__((visibility("default")))
+#else
+#pragma message("Compiler does not support symbol visibility.")
+#define DLL_PUBLIC
+#endif
+#endif
+
+class DLL_PUBLIC CmMod {
private:
int num = 0;
diff --git a/test cases/cmake/16 threads/test_matrix.json b/test cases/cmake/16 threads/test_matrix.json
new file mode 100644
index 0000000..1c2c545
--- /dev/null
+++ b/test cases/cmake/16 threads/test_matrix.json
@@ -0,0 +1,9 @@
+{
+ "options": {
+ "use_pthread": [
+ { "val": "ON" },
+ { "val": "OFF" },
+ { "val": "NOT_SET" }
+ ]
+ }
+}
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
index 4a2f15a..f40dc52 100644
--- a/test cases/common/14 configure file/meson.build
+++ b/test cases/common/14 configure file/meson.build
@@ -185,6 +185,12 @@ ret = run_command(check_file, inf, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif
+# Now the same, but using a File object as an argument.
+inf2 = files('invalid-utf8.bin.in')[0]
+ret = run_command(check_file, inf2, outf)
+if ret.returncode() != 0
+ error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
+endif
# Test copy of a binary file
outf = configure_file(input : inf,
diff --git a/test cases/common/144 custom target multiple outputs/meson.build b/test cases/common/144 custom target multiple outputs/meson.build
index 6412864..382c9b2 100644
--- a/test cases/common/144 custom target multiple outputs/meson.build
+++ b/test cases/common/144 custom target multiple outputs/meson.build
@@ -21,8 +21,19 @@ custom_target('only-install-first',
install : true,
install_dir : [join_paths(get_option('prefix'), get_option('includedir')), false])
-custom_target('only-install-second',
+targets = custom_target('only-install-second',
output : ['second.h', 'second.sh'],
command : [gen, 'second', '@OUTDIR@'],
install : true,
install_dir : [false, join_paths(get_option('prefix'), get_option('bindir'))])
+
+paths = []
+foreach i : targets.to_list()
+ paths += i.full_path()
+endforeach
+
+# Skip on Windows because paths are not identical, '/' VS '\'.
+if host_machine.system() != 'windows'
+ assert(paths == [meson.current_build_dir() / 'second.h',
+ meson.current_build_dir() / 'second.sh'])
+endif
diff --git a/test cases/common/192 args flattening/meson.build b/test cases/common/192 args flattening/meson.build
index 6da2e8f..1dac2f9 100644
--- a/test cases/common/192 args flattening/meson.build
+++ b/test cases/common/192 args flattening/meson.build
@@ -1,29 +1,31 @@
project('args flattening')
arr = get_variable('does-not-exist', ['bar', 'baz'])
-
assert(arr == ['bar', 'baz'], 'get_variable with array fallback is broken')
set_variable('arr', ['bar', 'baz'])
-
assert(arr == ['bar', 'baz'], 'set_variable(array) is broken')
conf = configuration_data()
-
conf.set('foo', ['bar', 'baz'])
-
assert(conf.get('foo') == ['bar', 'baz'], 'configuration_data.set(array) is broken')
arr = conf.get('does-not-exist', ['bar', 'baz'])
-
assert(arr == ['bar', 'baz'], 'configuration_data.get with array fallback is broken')
arr = meson.get_cross_property('does-not-exist', ['bar', 'baz'])
-
assert(arr == ['bar', 'baz'], 'meson.get_cross_property with array fallback is broken')
+arr = meson.get_external_property('does-not-exist', ['bar', 'baz'])
+assert(arr == ['bar', 'baz'], 'meson.get_external_property with array fallback is broken')
+
+arr = meson.get_external_property('does-not-exist', ['bar', 'baz'], native: true)
+assert(arr == ['bar', 'baz'], 'meson.get_external_property native:true with array fallback is broken')
+
+arr = meson.get_external_property('does-not-exist', ['bar', 'baz'], native: false)
+assert(arr == ['bar', 'baz'], 'meson.get_external_property native:false with array fallback is broken')
+
# Test deprecated behaviour
conf.set(['foo', 'bar'])
-
message(conf.get('foo'))
diff --git a/test cases/common/227 fs module/meson.build b/test cases/common/227 fs module/meson.build
index 25778af..a732768 100644
--- a/test cases/common/227 fs module/meson.build
+++ b/test cases/common/227 fs module/meson.build
@@ -30,8 +30,34 @@ assert(fs.is_dir('subprojects'), 'Dir not detected correctly.')
assert(not fs.is_dir('meson.build'), 'File detected as a dir.')
assert(not fs.is_dir('nonexisting'), 'Bad path detected as a dir.')
-assert(fs.is_dir('~'), 'expanduser not working')
-assert(not fs.is_file('~'), 'expanduser not working')
+assert(fs.is_dir('~'), 'home directory not detected')
+assert(not fs.is_file('~'), 'home directory detected as file')
+
+# -- expanduser
+assert(fs.expanduser('~') != '~','expanduser failed')
+assert(fs.expanduser('~/foo').endswith('foo'), 'expanduser with tail failed')
+
+# -- as_posix
+assert(fs.as_posix('/') == '/', 'as_posix idempotent')
+assert(fs.as_posix('\\') == '/', 'as_posix simple')
+assert(fs.as_posix('\\\\') == '/', 'as_posix simple')
+assert(fs.as_posix('foo\\bar/baz') == 'foo/bar/baz', 'as_posix mixed slash')
+
+# -- is_absolute
+winabs = 'q:/foo'
+unixabs = '/foo'
+if is_windows
+ assert(fs.is_absolute(winabs), 'is_absolute windows not detected')
+ assert(not fs.is_absolute(unixabs), 'is_absolute unix false positive')
+else
+ assert(fs.is_absolute(unixabs), 'is_absolute unix not detected')
+ assert(not fs.is_absolute(winabs), 'is_absolute windows false positive')
+endif
+
+# -- replace_suffix
+
+original = 'foo'
+assert(fs.replace_suffix(original, '') == original, 'replace_suffix idempotent')
original = 'foo.txt'
new = fs.replace_suffix(original, '.ini')
@@ -81,7 +107,11 @@ if not is_windows and build_machine.system() != 'cygwin' and is_git_checkout
assert(fs.is_samepath('a_symlink', 'meson.build'), 'symlink is_samepath fail')
endif
+# parts of path
assert(fs.parent('foo/bar') == 'foo', 'failed to get dirname')
assert(fs.name('foo/bar') == 'bar', 'failed to get basename')
+assert(fs.name('foo/bar/baz.dll.a') == 'baz.dll.a', 'failed to get basename with compound suffix')
+assert(fs.stem('foo/bar/baz.dll') == 'baz', 'failed to get stem with suffix')
+assert(fs.stem('foo/bar/baz.dll.a') == 'baz.dll', 'failed to get stem with compound suffix')
subdir('subdir')
diff --git a/test cases/common/229 native prop/crossfile.ini b/test cases/common/229 native prop/crossfile.ini
new file mode 100644
index 0000000..62d63ed
--- /dev/null
+++ b/test cases/common/229 native prop/crossfile.ini
@@ -0,0 +1,3 @@
+[properties]
+astring = 'cross'
+anarray = ['one', 'two'] \ No newline at end of file
diff --git a/test cases/common/229 native prop/meson.build b/test cases/common/229 native prop/meson.build
new file mode 100644
index 0000000..64da410
--- /dev/null
+++ b/test cases/common/229 native prop/meson.build
@@ -0,0 +1,25 @@
+project('get prop')
+
+x = meson.get_external_property('astring')
+ref = meson.is_cross_build() ? 'cross' : 'mystring'
+assert(x==ref, 'did not get native property string. did you use "meson setup --native-file native.txt"')
+
+x = meson.get_external_property('astring', native: true)
+assert(x=='mystring', 'did not get native property with native:true and non-cross build.')
+
+x = meson.get_external_property('astring', 'fallback', native: false)
+assert(x==ref, 'did not get get native property with native:false and non-cross build.')
+
+
+x = meson.get_external_property('notexist', 'fallback')
+assert(x=='fallback', 'fallback did not work')
+
+x = meson.get_external_property('notexist', 'fallback', native: true)
+assert(x=='fallback', 'fallback native:true did not work')
+
+x = meson.get_external_property('notexist', 'fallback', native: false)
+assert(x=='fallback', 'fallback native:false did not work')
+
+
+x = meson.get_external_property('anarray')
+assert(x==['one', 'two'], 'array did not work') \ No newline at end of file
diff --git a/test cases/common/229 native prop/nativefile.ini b/test cases/common/229 native prop/nativefile.ini
new file mode 100644
index 0000000..03c1e03
--- /dev/null
+++ b/test cases/common/229 native prop/nativefile.ini
@@ -0,0 +1,3 @@
+[properties]
+astring = 'mystring'
+anarray = ['one', 'two'] \ No newline at end of file
diff --git a/test cases/common/229 persubproject options/foo.c b/test cases/common/229 persubproject options/foo.c
new file mode 100644
index 0000000..63e4de6
--- /dev/null
+++ b/test cases/common/229 persubproject options/foo.c
@@ -0,0 +1,5 @@
+int foo(void);
+
+int foo(void) {
+ return 0;
+}
diff --git a/test cases/common/229 persubproject options/meson.build b/test cases/common/229 persubproject options/meson.build
new file mode 100644
index 0000000..6a76697
--- /dev/null
+++ b/test cases/common/229 persubproject options/meson.build
@@ -0,0 +1,10 @@
+project('persubproject options', 'c', default_options : ['default_library=both'])
+
+assert(get_option('default_library') == 'both', 'Parent default_library should be "both"')
+
+# Check it build both by calling a method only both_libraries target implement
+lib = library('lib1', 'foo.c')
+lib.get_static_lib()
+
+subproject('sub1')
+subproject('sub2', default_options : ['default_library=static'])
diff --git a/test cases/common/229 persubproject options/subprojects/sub1/foo.c b/test cases/common/229 persubproject options/subprojects/sub1/foo.c
new file mode 100644
index 0000000..63e4de6
--- /dev/null
+++ b/test cases/common/229 persubproject options/subprojects/sub1/foo.c
@@ -0,0 +1,5 @@
+int foo(void);
+
+int foo(void) {
+ return 0;
+}
diff --git a/test cases/common/229 persubproject options/subprojects/sub1/meson.build b/test cases/common/229 persubproject options/subprojects/sub1/meson.build
new file mode 100644
index 0000000..7afc934
--- /dev/null
+++ b/test cases/common/229 persubproject options/subprojects/sub1/meson.build
@@ -0,0 +1,7 @@
+project('sub1', 'c')
+
+assert(get_option('default_library') == 'both', 'Should inherit parent project default_library')
+
+# Check it build both by calling a method only both_libraries target implement
+lib = library('lib1', 'foo.c')
+lib.get_static_lib()
diff --git a/test cases/common/229 persubproject options/subprojects/sub2/foo.c b/test cases/common/229 persubproject options/subprojects/sub2/foo.c
new file mode 100644
index 0000000..63e4de6
--- /dev/null
+++ b/test cases/common/229 persubproject options/subprojects/sub2/foo.c
@@ -0,0 +1,5 @@
+int foo(void);
+
+int foo(void) {
+ return 0;
+}
diff --git a/test cases/common/229 persubproject options/subprojects/sub2/meson.build b/test cases/common/229 persubproject options/subprojects/sub2/meson.build
new file mode 100644
index 0000000..546884d
--- /dev/null
+++ b/test cases/common/229 persubproject options/subprojects/sub2/meson.build
@@ -0,0 +1,7 @@
+project('sub2', 'c', default_options : ['default_library=shared'])
+
+assert(get_option('default_library') == 'static', 'Parent should override default_library')
+
+# If it doesn't build only a static library, it would make target name clash.
+library('lib1', 'foo.c')
+shared_library('lib1', 'foo.c')
diff --git a/test cases/common/47 pkgconfig-gen/dependencies/main.c b/test cases/common/47 pkgconfig-gen/dependencies/main.c
new file mode 100644
index 0000000..61708d3
--- /dev/null
+++ b/test cases/common/47 pkgconfig-gen/dependencies/main.c
@@ -0,0 +1,6 @@
+#include <simple.h>
+
+int main(int argc, char *argv[])
+{
+ return simple_function() == 42 ? 0 : 1;
+}
diff --git a/test cases/common/47 pkgconfig-gen/dependencies/meson.build b/test cases/common/47 pkgconfig-gen/dependencies/meson.build
index 22bcc47..fb4e6b4 100644
--- a/test cases/common/47 pkgconfig-gen/dependencies/meson.build
+++ b/test cases/common/47 pkgconfig-gen/dependencies/meson.build
@@ -18,6 +18,9 @@ threads_dep = dependency('threads')
custom_dep = declare_dependency(link_with : custom_lib, compile_args : ['-DCUSTOM'])
custom2_dep = declare_dependency(link_args : ['-lcustom2'], compile_args : ['-DCUSTOM2'])
+exe = executable('test1', 'main.c', dependencies : [pc_dep])
+test('Test1', exe)
+
# Generate a PC file:
# - Having libmain in libraries should pull implicitly libexposed and libinternal in Libs.private
# - Having libexposed in libraries should remove it from Libs.private
diff --git a/test cases/common/47 pkgconfig-gen/meson.build b/test cases/common/47 pkgconfig-gen/meson.build
index 7e6c670..09c46c5 100644
--- a/test cases/common/47 pkgconfig-gen/meson.build
+++ b/test cases/common/47 pkgconfig-gen/meson.build
@@ -1,7 +1,6 @@
project('pkgconfig-gen', 'c')
# First check we have pkg-config >= 0.29
-
pkgconfig = find_program('pkg-config', required: false)
if not pkgconfig.found()
error('MESON_SKIP_TEST: pkg-config not found')
diff --git a/test cases/failing/99 no native prop/meson.build b/test cases/failing/99 no native prop/meson.build
new file mode 100644
index 0000000..c956754
--- /dev/null
+++ b/test cases/failing/99 no native prop/meson.build
@@ -0,0 +1,3 @@
+project('missing property')
+
+message(meson.get_external_property('nonexisting'))
diff --git a/test cases/unit/72 summary/meson.build b/test cases/unit/72 summary/meson.build
index c6d94d9..c155889 100644
--- a/test cases/unit/72 summary/meson.build
+++ b/test cases/unit/72 summary/meson.build
@@ -7,6 +7,7 @@ summary({'Some boolean': false,
'Another boolean': true,
'Some string': 'Hello World',
'A list': ['string', 1, true],
+ 'empty list': [],
}, section: 'Configuration')
summary('A number', 1, section: 'Configuration')
summary('yes', true, bool_yn : true, section: 'Configuration')