aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml9
-rw-r--r--ciimage/Dockerfile1
-rw-r--r--docs/markdown/Build-options.md72
-rw-r--r--docs/markdown/Conference-presentations.md2
-rw-r--r--docs/markdown/Cross-compilation.md26
-rw-r--r--docs/markdown/Dependencies.md49
-rw-r--r--docs/markdown/Qt5-module.md14
-rw-r--r--docs/markdown/Reference-manual.md16
-rw-r--r--docs/markdown/Running-Meson.md102
-rw-r--r--docs/markdown/Subprojects.md87
-rw-r--r--docs/markdown/snippets/builtin-python.md9
-rw-r--r--docs/markdown/snippets/config-tool-variable-method.md11
-rw-r--r--docs/markdown/snippets/if-found.md13
-rw-r--r--docs/markdown/snippets/option-array-type.md18
-rw-r--r--docs/markdown/snippets/system-wide-cross-files.md20
-rw-r--r--mesonbuild/backend/backends.py8
-rw-r--r--mesonbuild/backend/ninjabackend.py12
-rw-r--r--mesonbuild/backend/vs2010backend.py21
-rw-r--r--mesonbuild/backend/vs2017backend.py5
-rw-r--r--mesonbuild/backend/xcodebackend.py2
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/compilers/c.py13
-rw-r--r--mesonbuild/compilers/compilers.py32
-rw-r--r--mesonbuild/compilers/cpp.py4
-rw-r--r--mesonbuild/compilers/vala.py6
-rw-r--r--mesonbuild/coredata.py86
-rw-r--r--mesonbuild/dependencies/base.py219
-rw-r--r--mesonbuild/dependencies/dev.py109
-rw-r--r--mesonbuild/dependencies/misc.py117
-rw-r--r--mesonbuild/dependencies/ui.py192
-rw-r--r--mesonbuild/environment.py14
-rw-r--r--mesonbuild/interpreter.py33
-rw-r--r--mesonbuild/interpreterbase.py5
-rw-r--r--mesonbuild/linkers.py4
-rw-r--r--mesonbuild/mconf.py4
-rw-r--r--mesonbuild/mesonlib.py81
-rw-r--r--mesonbuild/mesonmain.py100
-rw-r--r--mesonbuild/mlog.py6
-rw-r--r--mesonbuild/modules/gnome.py41
-rw-r--r--mesonbuild/modules/python3.py2
-rw-r--r--mesonbuild/modules/qt.py32
-rw-r--r--mesonbuild/mparser.py8
-rw-r--r--mesonbuild/mtest.py54
-rw-r--r--mesonbuild/optinterpreter.py18
-rw-r--r--mesonbuild/scripts/regen_checker.py15
-rwxr-xr-xmsi/createmsi.py4
-rwxr-xr-xrun_project_tests.py27
-rwxr-xr-xrun_tests.py32
-rwxr-xr-xrun_unittests.py199
-rw-r--r--test cases/common/125 shared module/meson.build2
-rw-r--r--test cases/common/126 llvm ir and assembly/meson.build2
-rw-r--r--test cases/common/16 configure file/config5.h.in1
-rw-r--r--test cases/common/16 configure file/config6.h.in19
-rw-r--r--test cases/common/16 configure file/meson.build22
-rw-r--r--test cases/common/16 configure file/prog5.c6
-rw-r--r--test cases/common/16 configure file/prog6.c11
-rw-r--r--test cases/common/165 config tool variable/meson.build31
-rw-r--r--test cases/common/166 array option/meson.build17
-rw-r--r--test cases/common/166 array option/meson_options.txt19
-rw-r--r--test cases/common/167 external program shebang parsing/input.txt1
-rw-r--r--test cases/common/167 external program shebang parsing/main.c72
-rw-r--r--test cases/common/167 external program shebang parsing/meson.build21
-rw-r--r--test cases/common/167 external program shebang parsing/script.int.in2
-rw-r--r--test cases/common/168 subdir if_found/meson.build11
-rw-r--r--test cases/common/168 subdir if_found/subdir/meson.build1
-rw-r--r--test cases/common/42 string operations/meson.build23
-rw-r--r--test cases/common/47 options/meson.build5
-rw-r--r--test cases/common/47 options/meson_options.txt2
-rw-r--r--test cases/common/66 install subdir/subdir/meson.build2
-rw-r--r--test cases/common/68 number arithmetic/meson.build8
-rw-r--r--test cases/common/71 arithmetic bidmas/meson.build2
-rw-r--r--test cases/failing/65 grab sibling/subprojects/b/sneaky.c2
-rw-r--r--test cases/frameworks/16 sdl2/meson.build5
-rw-r--r--test cases/frameworks/19 pcap/meson.build4
-rw-r--r--test cases/frameworks/20 cups/meson.build5
-rw-r--r--test cases/frameworks/21 libwmf/meson.build7
-rw-r--r--test cases/frameworks/4 qt/meson.build2
-rw-r--r--test cases/frameworks/4 qt/q5core.cpp18
-rw-r--r--test cases/frameworks/4 qt/qt4core_fr.ts12
-rw-r--r--test cases/frameworks/4 qt/qt5core_fr.ts12
-rw-r--r--test cases/frameworks/9 wxwidgets/meson.build2
-rw-r--r--test cases/linuxlike/1 pkg-config/meson.build2
-rw-r--r--test cases/osx/2 library versions/CMakeLists.txt15
-rw-r--r--test cases/osx/2 library versions/meson.build12
-rw-r--r--test cases/osx/3 has function xcode8/meson.build16
-rw-r--r--test cases/unit/17 pkgconfig static/meson.build16
-rw-r--r--test cases/unit/18 array option/meson.build15
-rw-r--r--test cases/unit/18 array option/meson_options.txt20
88 files changed, 1747 insertions, 656 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 22a5812..46edbd4 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -78,7 +78,14 @@ install:
- cmd: if %compiler%==msvc2010 ( call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %arch% )
- cmd: if %compiler%==msvc2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch% )
- cmd: if %compiler%==msvc2017 ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%arch% )
- - cmd: if %compiler%==msys2-mingw (if %arch%==x86 (set "PATH=C:\msys64\mingw32\bin;%PATH%") else (set "PATH=C:\msys64\mingw64\bin;%PATH%"))
+ - ps: |
+ If($Env:compiler -eq 'msys2-mingw') {
+ If($Env:arch -eq 'x86') {
+ $env:Path = 'C:\msys64\mingw32\bin;' + $env:Path
+ } Else {
+ $env:Path = 'C:\msys64\mingw64\bin;' + $env:Path
+ }
+ }
- cmd: if not %compiler%==cygwin ( set "PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%;" )
- cmd: if %compiler%==cygwin ( set PYTHON=python3 ) else ( set PYTHON=python )
- cmd: if %compiler%==cygwin ( set WRAPPER=ci\run-in-cygwin.bat )
diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile
index a2e3433..05e679e 100644
--- a/ciimage/Dockerfile
+++ b/ciimage/Dockerfile
@@ -12,4 +12,5 @@ RUN apt-get -y update && apt-get -y upgrade \
&& apt-get -y install fpga-icestorm arachne-pnr yosys \
&& apt-get -y install gtk-sharp2 gtk-sharp2-gapi libglib2.0-cil-dev \
&& apt-get -y install libwmf-dev \
+&& apt-get -y install qt4-linguist-tools qttools5-dev-tools \
&& python3 -m pip install hotdoc codecov
diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md
index cb7b19e..815a662 100644
--- a/docs/markdown/Build-options.md
+++ b/docs/markdown/Build-options.md
@@ -16,18 +16,43 @@ Here is a simple option file.
option('someoption', type : 'string', value : 'optval', description : 'An option')
option('other_one', type : 'boolean', value : false)
option('combo_opt', type : 'combo', choices : ['one', 'two', 'three'], value : 'three')
+option('free_array_opt', type : 'array', value : ['one', 'two'])
+option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two'])
```
-This demonstrates the three basic option types and their usage. String
-option is just a free form string and a boolean option is,
-unsurprisingly, true or false. The combo option can have any value
-from the strings listed in argument `choices`. If `value` is not set,
-it defaults to empty string for strings, `true` for booleans or the
-first element in a combo. You can specify `description`, which is a
-free form piece of text describing the option. It defaults to option
-name.
+All types allow a `description` value to be set describing the option,
+if no option is set then the name of the option will be used instead.
-These options are accessed in Meson code with the `get_option` function.
+### Strings
+
+The string type is a free form string. If the default value is not set
+then an empty string will be used as the default.
+
+### Booleans
+
+Booleans may have values of either `true` or `false`. If not default
+value is supplied then `true` will be used as the default.
+
+### Combos
+
+A combo allows any one of the values in the `choices` parameter to be
+selected. If no default value is set then the first value will be the
+default.
+
+### Arrays
+
+Arrays represent an array of strings. By default the array can contain
+arbitrary strings. To limit the possible values that can used set the
+`choices` parameter. Meson will then only allow the value array to
+contain strings that are in the given list. The array may be
+empty. The `value` parameter specifies the default value of the option
+and if it is unset then the values of `choices` will be used as the
+default.
+
+This type is new in version 0.44.0
+
+
+## Using build options
```meson
optval = get_option('opt_name')
@@ -42,9 +67,9 @@ prefix = get_option('prefix')
```
It should be noted that you can not set option values in your Meson
-scripts. They have to be set externally with the `meson configure` command
-line tool. Running `meson configure` without arguments in a build dir shows
-you all options you can set.
+scripts. They have to be set externally with the `meson configure`
+command line tool. Running `meson configure` without arguments in a
+build dir shows you all options you can set.
To change their values use the `-D`
option:
@@ -53,5 +78,26 @@ option:
$ meson configure -Doption=newvalue
```
+Setting the value of arrays is a bit special. If you only pass a
+single string, then it is considered to have all values separated by
+commas. Thus invoking the following command:
+
+```console
+$ meson configure -Darray_opt=foo,bar
+```
+
+would set the value to an array of two elements, `foo` and `bar`.
+
+If you need to have commas in your string values, then you need to
+pass the value with proper shell quoting like this:
+
+```console
+$ meson configure "-Doption=['a,b', 'c,d']"
+```
+
+The inner values must always be single quotes and the outer ones
+double quotes.
-**NOTE:** If you cannot call `meson configure` you likely have a old version of Meson. In that case you can call `mesonconf` instead, but that is deprecated in newer versions
+**NOTE:** If you cannot call `meson configure` you likely have a old
+ version of Meson. In that case you can call `mesonconf` instead, but
+ that is deprecated in newer versions
diff --git a/docs/markdown/Conference-presentations.md b/docs/markdown/Conference-presentations.md
index 15e4396..abfc52f 100644
--- a/docs/markdown/Conference-presentations.md
+++ b/docs/markdown/Conference-presentations.md
@@ -8,7 +8,7 @@
- GStreamer conference 2015, [Done in 6.0 seconds](https://gstconf.ubicast.tv/videos/done-in-60-seconds-a-new-build-system-for-gstreamer) (jpakkane)
-- LCA 2016, [Builds, dependencies and deployment in the modern multiplatform world](https://www.youtube.com/watch?v=CTJtKtQ8R5k&feature=youtu.be) (jpakkane)
+- LCA 2016, [Builds, dependencies and deployment in the modern multiplatform world](https://www.youtube.com/watch?v=CTJtKtQ8R5k) (jpakkane)
- GUADEC 2016, [Making your GNOME app compile 2.4x faster](https://media.ccc.de/v/44-making_your_gnome_app_compile_24x_faster) (nirbheek)
diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md
index f68b1f5..c1ad317 100644
--- a/docs/markdown/Cross-compilation.md
+++ b/docs/markdown/Cross-compilation.md
@@ -257,3 +257,29 @@ then you can access that using the `meson` object like this:
myvar = meson.get_cross_property('somekey')
# myvar now has the value 'somevalue'
```
+
+## Cross file locations
+
+As of version 0.44.0 meson supports loading cross files from system locations
+on Linux and the BSDs. This will be $XDG_DATA_DIRS/meson/cross, or if
+XDG_DATA_DIRS is undefined, then /usr/local/share/meson/cross and
+/usr/share/meson/cross will be tried in that order, for system wide cross
+files. User local files can be put in $XDG_DATA_HOME/meson/cross, or
+~/.local/share/meson/cross if that is undefined.
+
+The order of locations tried is as follows:
+ - A file relative to the local dir
+ - The user local location
+ - The system wide locations in order
+
+Linux and BSD distributions are encouraged to ship cross files either with
+their cross compiler toolchain packages or as a standalone package, and put
+them in one of the system paths referenced above.
+
+These files can be loaded automatically without adding a path to the cross
+file. For example, if a ~/.local/share/meson/cross contains a file called x86-linux,
+then the following command would start a cross build using that cross files:
+
+```sh
+meson builddir/ --cross-file x86-linux
+```
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md
index 8e780d6..bae3edc 100644
--- a/docs/markdown/Dependencies.md
+++ b/docs/markdown/Dependencies.md
@@ -178,32 +178,51 @@ the list of sources for the target. The `modules` keyword of
`dependency` works just like it does with Boost. It tells which
subparts of Qt the program uses.
-## Pcap
+## Dependencies using config tools
-The pcap library does not ship with pkg-config at the time or writing
-but instead it has its own `pcap-config` util. Meson will use it
-automatically:
+CUPS, LLVM, PCAP, WxWidgets, libwmf, and GnuStep either do not provide
+pkg-config modules or additionally can be detected via a config tool
+(cups-config, llvm-config, etc). Meson has native support for these tools, and
+then can be found like other dependencies:
```meson
pcap_dep = dependency('pcap', version : '>=1.0')
+cups_dep = dependency('cups', version : '>=1.4')
+llvm_dep = dependency('llvm', version : '>=4.0')
```
-## CUPS
-
-The cups library does not ship with pkg-config at the time or writing
-but instead it has its own `cups-config` util. Meson will use it
-automatically:
+Some of these tools (like wmf and cups) provide both pkg-config and config
+tools support. You can force one or another via the method keyword:
```meson
-cups_dep = dependency('cups', version : '>=1.4')
+wmf_dep = dependency('wmf', method : 'config-tool')
```
-## LibWMF
+## LLVM
+
+Meson has native support for LLVM going back to version LLVM version 3.5.
+It supports a few additional features compared to other config-tool based
+dependencies.
+
+As of 0.44.0 Meson supports the `static` keyword argument for LLVM. Before this
+LLVM >= 3.9 would always dynamically link, while older versions would
+statically link, due to a quirk in `llvm-config`.
+
+### Modules, a.k.a. Components
-The libwmf library does not ship with pkg-config at the time or writing
-but instead it has its own `libwmf-config` util. Meson will use it
-automatically:
+Meson wraps LLVM's concept of components in it's own modules concept.
+When you need specific components you add them as modules as meson will do the
+right thing:
```meson
-libwmf_dep = dependency('libwmf', version : '>=0.2.8')
+llvm_dep = dependency('llvm', version : '>= 4.0', modules : ['amdgpu'])
+```
+
+As of 0.44.0 it can also take optional modules (these will affect the arguments
+generated for a static link):
+
+```meson
+llvm_dep = dependency(
+ 'llvm', version : '>= 4.0', modules : ['amdgpu'], optional_modules : ['inteljitevents'],
+)
```
diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md
index aea2ae1..b5393a8 100644
--- a/docs/markdown/Qt5-module.md
+++ b/docs/markdown/Qt5-module.md
@@ -1,7 +1,7 @@
# Qt5 module
The Qt5 module provides tools to automatically deal with the various
-tools and steps required for Qt. The module has one method.
+tools and steps required for Qt. The module has two methods.
## preprocess
@@ -12,6 +12,14 @@ This method takes the following keyword arguments:
It returns an opaque object that should be passed to a main build target.
+## compile_translations (since v0.44.0)
+
+This method generates the necessary targets to build translation files with lrelease, it takes the following keyword arguments:
+ - `ts_files`, the list of input translation files produced by Qt's lupdate tool.
+ - `install` when true, this target is installed during the install step (optional).
+ - `install_dir` directory to install to (optional).
+ - `build_by_default` when set to true, to have this target be built by default, that is, when invoking plain ninja; the default value is false (optional).
+
A simple example would look like this:
```meson
@@ -21,6 +29,7 @@ inc = include_directories('includes')
moc_files = qt5.preprocess(moc_headers : 'myclass.h',
moc_extra_arguments: ['-DMAKES_MY_MOC_HEADER_COMPILE'],
include_directories: inc)
+translations = qt5.compile_translations(ts_files : 'myTranslation_fr.ts', build_by_default : true)
executable('myprog', 'main.cpp', 'myclass.cpp', moc_files,
include_directories: inc,
dependencies : qt5_dep)
@@ -28,5 +37,4 @@ executable('myprog', 'main.cpp', 'myclass.cpp', moc_files,
The 'modules' argument is used to include Qt modules in the project.
-See the Qt documentation for the [list of
-modules](http://doc.qt.io/qt-5/qtmodules.html).
+See the Qt documentation for the [list of modules](http://doc.qt.io/qt-5/qtmodules.html).
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 2aa9665..e6aa9d3 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -430,7 +430,7 @@ be passed to [shared and static libraries](#library).
`project`'s `default_options` overriding the values of these options
for this target only, since 0.40.0
- `d_import_dirs` list of directories to look in for string imports used
- in the D programmling language
+ in the D programming language
- `d_unittest`, when set to true, the D modules are compiled in debug mode
- `d_module_versions` list of module versions set when compiling D sources
@@ -855,6 +855,8 @@ This function prints its argument to stdout.
This function prints its argument to stdout prefixed with WARNING:.
+*Added 0.44.0*
+
### project()
``` meson
@@ -1019,7 +1021,7 @@ has one argument the others don't have:
### subdir()
``` meson
- void subdir(dir_name)
+ void subdir(dir_name, ...)
```
Enters the specified subdirectory and executes the `meson.build` file
@@ -1032,6 +1034,12 @@ current build file and in all subsequent build files executed with
Note that this means that each `meson.build` file in a source tree can
and must only be executed once.
+This function has one keyword argument.
+
+ - `if_found` takes one or several dependency objects and will only
+ recurse in the subdir if they all return `true` when queried with
+ `.found()`
+
### subproject()
``` meson
@@ -1608,6 +1616,10 @@ an external dependency with the following methods:
pkg-config variable specified, or, if invoked on a non pkg-config
dependency, error out
+ - `get_configtool_variable(varname)` (*Added 0.44.0*) will get the
+ command line argument from the config tool (with `--` prepended), or,
+ if invoked on a non config-tool dependency, error out.
+
- `type_name()` which returns a string describing the type of the
dependency, the most common values are `internal` for deps created
with `declare_dependencies` and `pkgconfig` for system dependencies
diff --git a/docs/markdown/Running-Meson.md b/docs/markdown/Running-Meson.md
index 0e8da43..23d5e97 100644
--- a/docs/markdown/Running-Meson.md
+++ b/docs/markdown/Running-Meson.md
@@ -1,17 +1,26 @@
---
-short-description: Building a project with meson
+short-description: Building a project with Meson
...
-# Running meson
+# Running Meson
-There are two different ways of invoking Meson. First, you can run it directly from the source tree with the command `/path/to/source/meson.py`. Meson may also be installed in which case the command is simply `meson`. In this manual we only use the latter format for simplicity.
+There are two different ways of invoking Meson. First, you can run it
+directly from the source tree with the command
+`/path/to/source/meson.py`. Meson may also be installed in which case
+the command is simply `meson`. In this manual we only use the latter
+format for simplicity.
-At the time of writing only a command line version of Meson is available. This means that Meson must be invoked using the terminal. If you wish to use the MSVC compiler, you need to run Meson under "Visual Studio command prompt".
+At the time of writing only a command line version of Meson is
+available. This means that Meson must be invoked using the
+terminal. If you wish to use the MSVC compiler, you need to run Meson
+under "Visual Studio command prompt".
Configuring the source
==
-Let us assume that we have a source tree that has a Meson build system. This means that at the topmost directory has a file called `meson.build`. We run the following commands to get the build started.
+Let us assume that we have a source tree that has a Meson build
+system. This means that at the topmost directory has a file called
+`meson.build`. We run the following commands to get the build started.
cd /path/to/source/root
@@ -19,13 +28,22 @@ Let us assume that we have a source tree that has a Meson build system. This mea
cd builddir
meson ..
-First we create a directory to hold all files generated during the build. Then we go into it and invoke Meson, giving it the location of the source root.
+First we create a directory to hold all files generated during the
+build. Then we go into it and invoke Meson, giving it the location of
+the source root.
-Hint: The syntax of meson is `meson [options] [srcdir] [builddir]`, but you may omit either `srcdir` or `builddir`. Meson will deduce the `srcdir` by the location of `meson.build`. The other one will be your `pwd`.
+Hint: The syntax of meson is `meson [options] [srcdir] [builddir]`,
+but you may omit either `srcdir` or `builddir`. Meson will deduce the
+`srcdir` by the location of `meson.build`. The other one will be your
+`pwd`.
-Meson then loads the build configuration file and writes the corresponding build backend in the build directory. By default Meson generates a *debug build*, which turns on basic warnings and debug information and disables compiler optimizations.
+Meson then loads the build configuration file and writes the
+corresponding build backend in the build directory. By default Meson
+generates a *debug build*, which turns on basic warnings and debug
+information and disables compiler optimizations.
-You can specify a different type of build with the `--buildtype` command line argument. It can have one of the following values.
+You can specify a different type of build with the `--buildtype`
+command line argument. It can have one of the following values.
| value | meaning |
| ------ | -------- |
@@ -34,42 +52,78 @@ You can specify a different type of build with the `--buildtype` command line ar
| `debugoptimized` | debug info is generated and the code is optimized (on most compilers this means `-g -O2`) |
| `release` | full optimization, no debug info |
-The build directory is mandatory. The reason for this is that it simplifies the build process immensely. Meson will not under any circumstances write files inside the source directory (if it does, it is a bug and should be fixed). This means that the user does not need to add a bunch of files to their revision control's ignore list. It also means that you can create arbitrarily many build directories for any given source tree. If we wanted to test building the source code with the Clang compiler instead of the system default, we could just type the following commands.
+The build directory is mandatory. The reason for this is that it
+simplifies the build process immensely. Meson will not under any
+circumstances write files inside the source directory (if it does, it
+is a bug and should be fixed). This means that the user does not need
+to add a bunch of files to their revision control's ignore list. It
+also means that you can create arbitrarily many build directories for
+any given source tree. If we wanted to test building the source code
+with the Clang compiler instead of the system default, we could just
+type the following commands.
cd /path/to/source/root
mkdir buildclang
cd buildclang
CC=clang CXX=clang++ meson ..
-This separation is even more powerful if your code has multiple configuration options (such as multiple data backends). You can create a separate subdirectory for each of them. You can also have build directories for optimized builds, code coverage, static analysis and so on. They are all neatly separated and use the same source tree. Changing between different configurations is just a question of changing to the corresponding directory.
+This separation is even more powerful if your code has multiple
+configuration options (such as multiple data backends). You can create
+a separate subdirectory for each of them. You can also have build
+directories for optimized builds, code coverage, static analysis and
+so on. They are all neatly separated and use the same source
+tree. Changing between different configurations is just a question of
+changing to the corresponding directory.
-Unless otherwise mentioned, all following command line invocations are meant to be run in the build directory.
+Unless otherwise mentioned, all following command line invocations are
+meant to be run in the build directory.
-By default Meson will use the Ninja backend to build your project. If you wish to use any of the other backends, you need to pass the corresponding argument during configuration time. As an example, here is how you would use Meson to generate a Visual studio solution.
+By default Meson will use the Ninja backend to build your project. If
+you wish to use any of the other backends, you need to pass the
+corresponding argument during configuration time. As an example, here
+is how you would use Meson to generate a Visual studio solution.
meson <source dir> <build dir> --backend=vs2010
-You can then open the generated solution with Visual Studio and compile it in the usual way. A list of backends can be obtained with `meson --help`.
+You can then open the generated solution with Visual Studio and
+compile it in the usual way. A list of backends can be obtained with
+`meson --help`.
Building the source
==
-If you are not using an IDE, Meson uses the [Ninja build system](https://ninja-build.org/) to actually build the code. To start the build, simply type the following command.
+If you are not using an IDE, Meson uses the [Ninja build
+system](https://ninja-build.org/) to actually build the code. To start
+the build, simply type the following command.
ninja
-The main usability difference between Ninja and Make is that Ninja will automatically detect the number of CPUs in your computer and parallelize itself accordingly. You can override the amount of parallel processes used with the command line argument `-j <num processes>`.
-
-It should be noted that after the initial configure step `ninja` is the only command you ever need to type to compile. No matter how you alter your source tree (short of moving it to a completely new location), Meson will detect the changes and regenerate itself accordingly. This is especially handy if you have multiple build directories. Often one of them is used for development (the "debug" build) and others only every now and then (such as a "static analysis" build). Any configuration can be built just by `cd`'ing to the corresponding directory and running Ninja.
+The main usability difference between Ninja and Make is that Ninja
+will automatically detect the number of CPUs in your computer and
+parallelize itself accordingly. You can override the amount of
+parallel processes used with the command line argument `-j <num
+processes>`.
+
+It should be noted that after the initial configure step `ninja` is
+the only command you ever need to type to compile. No matter how you
+alter your source tree (short of moving it to a completely new
+location), Meson will detect the changes and regenerate itself
+accordingly. This is especially handy if you have multiple build
+directories. Often one of them is used for development (the "debug"
+build) and others only every now and then (such as a "static analysis"
+build). Any configuration can be built just by `cd`'ing to the
+corresponding directory and running Ninja.
Running tests
==
-Meson provides native support for running tests. The command to do that is simple.
+Meson provides native support for running tests. The command to do
+that is simple.
ninja test
-Meson does not force the use of any particular testing framework. You are free to use GTest, Boost Test, Check or even custom executables.
+Meson does not force the use of any particular testing framework. You
+are free to use GTest, Boost Test, Check or even custom executables.
Installing
==
@@ -78,13 +132,17 @@ Installing the built software is just as simple.
ninja install
-By default Meson installs to `/usr/local`. This can be changed by passing the command line argument `--prefix /your/prefix` to Meson during configure time. Meson also supports the `DESTDIR` variable used in e.g. building packages. It is used like this:
+By default Meson installs to `/usr/local`. This can be changed by
+passing the command line argument `--prefix /your/prefix` to Meson
+during configure time. Meson also supports the `DESTDIR` variable used
+in e.g. building packages. It is used like this:
DESTDIR=/path/to/staging ninja install
Command line help
==
-Meson has a standard command line help feature. It can be accessed with the following command.
+Meson has a standard command line help feature. It can be accessed
+with the following command.
meson --help
diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md
index 923b6a3..14f01d4 100644
--- a/docs/markdown/Subprojects.md
+++ b/docs/markdown/Subprojects.md
@@ -4,69 +4,76 @@ short-description: Using meson projects as subprojects within other meson projec
# Subprojects
-Some platforms do not provide a native packaging system. In these cases it is common to bundle all third party libraries in your source tree. This is usually frowned upon because it makes it hard to add these kinds of projects into e.g. those Linux distributions that forbid bundled libraries.
-
-Meson tries to solve this problem by making it extremely easy to provide both at the same time. The way this is done is that Meson allows you to take any other Meson project and make it a part of your build without (in the best case) any changes to its Meson setup. It becomes a transparent part of the project. The basic idiom goes something like this.
+Some platforms do not provide a native packaging system. In these
+cases it is common to bundle all third party libraries in your source
+tree. This is usually frowned upon because it makes it hard to add
+these kinds of projects into e.g. those Linux distributions that
+forbid bundled libraries.
+
+Meson tries to solve this problem by making it extremely easy to
+provide both at the same time. The way this is done is that Meson
+allows you to take any other Meson project and make it a part of your
+build without (in the best case) any changes to its Meson setup. It
+becomes a transparent part of the project. The basic idiom goes
+something like this.
```meson
-dep = dependency('foo', required : false)
-if dep.found()
- # set up project using external dependency
-else
- subproject('foo')
- # set up rest of project as if foo was provided by this project
-endif
+dep = dependency('foo', fallback : [subproject_name, variable_name]
```
-All Meson features of the subproject, such as project options keep working and can be set in the master project. There are a few limitations, the most important being that global compiler arguments must be set in the main project before calling subproject. Subprojects must not set global arguments because there is no way to do that reliably over multiple subprojects. To check whether you are running as a subproject, use the `is_subproject` function.
-
-As an example, suppose we have a simple project that provides a shared library.
+As an example, suppose we have a simple project that provides a shared
+library. It would be set up like this.
```meson
project('simple', 'c')
i = include_directories('include')
l = shared_library('simple', 'simple.c', include_directories : i, install : true)
+simple_dep = declare_dependency(include_directories : i,
+ link_with : l)
```
-Then we could use that from a master project. First we generate a subdirectory called `subprojects` in the root of the master directory. Then we create a subdirectory called `simple` and put the subproject in that directory. Now the subproject can be used like this.
+Then we could use that from a master project. First we generate a
+subdirectory called `subprojects` in the root of the master
+directory. Then we create a subdirectory called `simple` and put the
+subproject in that directory. Now the subproject can be used like
+this.
```meson
project('master', 'c')
-dep = dependency('simple', required : false)
-if dep.found()
- i = []
- l = []
-else
- sp = subproject('simple') # This is a name of a subdirectory in subprojects.
- i = sp.get_variable('i')
- l = sp.get_variable('l')
-endif
-exe = executable('prog', 'prog.c', include_directories : i, link_with : l,
+dep = dependency('simple', fallback : ['simple', 'simple_dep']
+exe = executable('prog', 'prog.c',
dependencies : dep, install : true)
```
-With this setup the system dependency is used when it is available, otherwise we fall back on the bundled version.
-
-It should be noted that this only works for subprojects that are built with Meson. It can not be used with any other build system. The reason is the simple fact that there is no possible way to do this reliably with mixed build systems.
-
-Subprojects can use other subprojects, but all subprojects must reside in the top level `subprojects` directory. Recursive use of subprojects is not allowed, though, so you can't have subproject `a` that uses subproject `b` and have `b` also use `a`.
-
-## Subprojects and dependencies
-
-A common use case is to use subprojects to provide dependencies on platforms that do not provide them out of the box. This is especially common on Windows. Meson makes this easy while at the same time using system dependencies if are available. The way to do this is to set up a subproject that builds the dependency and has an internal dependency declared like this:
+With this setup the system dependency is used when it is available,
+otherwise we fall back on the bundled version. If you wish to always
+use the embedded version, then you would declare it like this:
```meson
-proj_dep = declare_dependency(...)
+simple_sp = subproject('simple')
+dep = simple_sp.get_variable('simple_dep')
```
-Then you can use the subproject in the master project like this:
+All Meson features of the subproject, such as project options keep
+working and can be set in the master project. There are a few
+limitations, the most important being that global compiler arguments
+must be set in the main project before calling subproject. Subprojects
+must not set global arguments because there is no way to do that
+reliably over multiple subprojects. To check whether you are running
+as a subproject, use the `is_subproject` function.
-```meson
-sp_dep = dependency('subproj_pkgconfig_name', fallback : ['subproj_name', 'proj_dep'])
-```
+It should be noted that this only works for subprojects that are built
+with Meson. It can not be used with any other build system. The reason
+is the simple fact that there is no possible way to do this reliably
+with mixed build systems.
-This uses the system dependency when available and the self built version if not. If you want to always use the subproject, that is also possible, just use `subproject` and `get_variable` as discussed above to get the dependency object.
+Subprojects can use other subprojects, but all subprojects must reside
+in the top level `subprojects` directory. Recursive use of subprojects
+is not allowed, though, so you can't have subproject `a` that uses
+subproject `b` and have `b` also use `a`.
# Obtaining subprojects
-Meson ships with a dependency system to automatically obtain dependency subprojects. It is documented in the [Wrap dependency system manual](Wrap-dependency-system-manual.md).
+Meson ships with a dependency system to automatically obtain
+dependency subprojects. It is documented in the [Wrap dependency
+system manual](Wrap-dependency-system-manual.md).
diff --git a/docs/markdown/snippets/builtin-python.md b/docs/markdown/snippets/builtin-python.md
new file mode 100644
index 0000000..01bb6e0
--- /dev/null
+++ b/docs/markdown/snippets/builtin-python.md
@@ -0,0 +1,9 @@
+# Embedded Python in Windows MSI packages
+
+Meson now ships an internal version of Python in the MSI installer packages.
+This means that it can run Python scripts that are part of your build
+transparently. That is, if you do the following:
+
+ myprog = find_program('myscript.py')
+
+Then Meson will run the script with its internal Python version if necessary.
diff --git a/docs/markdown/snippets/config-tool-variable-method.md b/docs/markdown/snippets/config-tool-variable-method.md
new file mode 100644
index 0000000..7725982
--- /dev/null
+++ b/docs/markdown/snippets/config-tool-variable-method.md
@@ -0,0 +1,11 @@
+# Config-Tool based dependencies gained a method to get arbitrary options
+
+A number of dependencies (CUPS, LLVM, pcap, WxWidgets, GnuStep) use a config
+tool instead of pkg-config. As of this version they now have a
+`get_configtool_variable` method, which is analogous to the
+`get_pkgconfig_variable` for pkg config.
+
+```meson
+dep_llvm = dependency('LLVM')
+llvm_inc_dir = dep_llvm.get_configtool_variable('includedir')
+```
diff --git a/docs/markdown/snippets/if-found.md b/docs/markdown/snippets/if-found.md
new file mode 100644
index 0000000..a8d4932
--- /dev/null
+++ b/docs/markdown/snippets/if-found.md
@@ -0,0 +1,13 @@
+# Added `if_found` to subdir
+
+Added a new keyword argument to the `subdir` command. It is given a
+list of dependency objects and the function will only recurse in the
+subdirectory if they are all found. Typical usage goes like this.
+
+ d1 = dependency('foo') # This is found
+ d2 = dependency('bar') # This is not found
+
+ subdir('somedir', if_found : [d1, d2])
+
+In this case the subdirectory would not be entered since `d2` could
+not be found.
diff --git a/docs/markdown/snippets/option-array-type.md b/docs/markdown/snippets/option-array-type.md
new file mode 100644
index 0000000..9eb1312
--- /dev/null
+++ b/docs/markdown/snippets/option-array-type.md
@@ -0,0 +1,18 @@
+# An array type for user options
+
+Previously to have an option that took more than one value a string value would
+have to be created and split, but validating this was difficult. A new array type
+has been added to the meson_options.txt for this case. It works like a 'combo', but
+allows more than one option to be passed. The values can optionally be validated
+against a list of valid values. When used on the command line (with -D), values
+are passed as a comma separated list.
+
+```meson
+option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one'])
+```
+
+These can be overwritten on the command line,
+
+```meson
+meson _build -Darray_opt=two,three
+```
diff --git a/docs/markdown/snippets/system-wide-cross-files.md b/docs/markdown/snippets/system-wide-cross-files.md
new file mode 100644
index 0000000..66c454f
--- /dev/null
+++ b/docs/markdown/snippets/system-wide-cross-files.md
@@ -0,0 +1,20 @@
+## System wide and user local cross files
+
+Meson has gained the ability to load cross files from predefined locations
+without passing a full path on Linux and the BSD OSes. User local files will be
+loaded from `$XDG_DATA_HOME/meson/cross`, or if XDG_DATA_HOME is undefined,
+`~/.local/share/meson/cross` will be used.
+
+For system wide paths the values of `$XDG_DATA_DIRS` + `/meson/cross` will be used,
+if XDG_DATA_DIRS is undefined then `/usr/local/share/meson/cross:/usr/share/meson/cross`
+will be used instead.
+
+A file relative to the current working directory will be tried first, then the
+user specific path will be tried before the system wide paths.
+
+Assuming that a file x86-linux is located in one of those places a cross build
+can be started with:
+
+```sh
+meson builddir/ --cross-file x86-linux
+```
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 61f7535..df58271 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -330,6 +330,8 @@ class Backend:
link_deps = target.get_all_link_deps()
result = []
for ld in link_deps:
+ if ld is target:
+ continue
prospective = self.get_target_dir(ld)
if prospective not in result:
result.append(prospective)
@@ -444,9 +446,9 @@ class Backend:
copt_proxy = OptionOverrideProxy(target.option_overrides, self.environment.coredata.compiler_options)
# First, the trivial ones that are impossible to override.
#
- # Add -nostdinc/-nostdinc++ if needed; can't be overriden
+ # Add -nostdinc/-nostdinc++ if needed; can't be overridden
commands += self.get_cross_stdlib_args(target, compiler)
- # Add things like /NOLOGO or -pipe; usually can't be overriden
+ # Add things like /NOLOGO or -pipe; usually can't be overridden
commands += compiler.get_always_args()
# Only add warning-flags by default if the buildtype enables it, and if
# we weren't explicitly asked to not emit warnings (for Vala, f.ex)
@@ -568,7 +570,7 @@ class Backend:
if isinstance(exe, build.BuildTarget):
is_cross = is_cross and exe.is_cross
if isinstance(exe, dependencies.ExternalProgram):
- # E.g. an external verificator or simulator program run on a generated executable.
+ # E.g. an external verifier or simulator program run on a generated executable.
# Can always be run.
is_cross = False
if is_cross:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index c633daf..bcda603 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -220,7 +220,7 @@ int dummy;
outfile.write('# Suffix\n\n')
self.generate_utils(outfile)
self.generate_ending(outfile)
- # Only ovewrite the old build file after the new one has been
+ # Only overwrite the old build file after the new one has been
# fully created.
os.replace(tempfilename, outfilename)
self.generate_compdb()
@@ -2236,7 +2236,7 @@ rule FORTRAN_DEP_HACK
# Fortran is a bit weird (again). When you link against a library, just compiling a source file
# requires the mod files that are output when single files are built. To do this right we would need to
# scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so
- # instead just have an ordered dependendy on the library. This ensures all required mod files are created.
+ # instead just have an ordered dependency on the library. This ensures all required mod files are created.
# The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
# produce incorrect dep files but such is life.
def get_fortran_orderdeps(self, target, compiler):
@@ -2387,9 +2387,9 @@ rule FORTRAN_DEP_HACK
commands += compilers.get_base_link_args(self.environment.coredata.base_options,
linker,
isinstance(target, build.SharedModule))
- # Add -nostdlib if needed; can't be overriden
+ # Add -nostdlib if needed; can't be overridden
commands += self.get_cross_stdlib_link_args(target, linker)
- # Add things like /NOLOGO; usually can't be overriden
+ # Add things like /NOLOGO; usually can't be overridden
commands += linker.get_linker_always_args()
# Add buildtype linker args: optimization level, etc.
commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target))
@@ -2524,7 +2524,7 @@ rule FORTRAN_DEP_HACK
gcno_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
script_root = self.environment.get_script_dir()
clean_script = os.path.join(script_root, 'delwithsuffix.py')
- gcno_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcno'])
+ gcno_elem.add_item('COMMAND', mesonlib.python_command + [clean_script, '.', 'gcno'])
gcno_elem.add_item('description', 'Deleting gcno files.')
gcno_elem.write(outfile)
# Alias that runs the target defined above
@@ -2533,7 +2533,7 @@ rule FORTRAN_DEP_HACK
gcda_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
script_root = self.environment.get_script_dir()
clean_script = os.path.join(script_root, 'delwithsuffix.py')
- gcda_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcda'])
+ gcda_elem.add_item('COMMAND', mesonlib.python_command + [clean_script, '.', 'gcda'])
gcda_elem.add_item('description', 'Deleting gcda files.')
gcda_elem.write(outfile)
# Alias that runs the target defined above
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index e4e9696..ea02580 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -23,7 +23,7 @@ from .. import dependencies
from .. import mlog
from .. import compilers
from ..compilers import CompilerArgs
-from ..mesonlib import MesonException, File
+from ..mesonlib import MesonException, File, python_command
from ..environment import Environment
def autodetect_vs_version(build):
@@ -396,10 +396,11 @@ class Vs2010Backend(backends.Backend):
action = ET.SubElement(root, 'ItemDefinitionGroup')
customstep = ET.SubElement(action, 'PostBuildEvent')
cmd_raw = [target.command] + target.args
- cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
- self.environment.get_build_dir(),
- self.environment.get_source_dir(),
- self.get_target_dir(target)] + self.environment.get_build_command()
+ cmd = python_command + \
+ [os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
+ self.environment.get_build_dir(),
+ self.environment.get_source_dir(),
+ self.get_target_dir(target)] + self.environment.get_build_command()
for i in cmd_raw:
if isinstance(i, build.BuildTarget):
cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
@@ -671,9 +672,6 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(type_config, 'DebugInformationFormat').text = 'ProgramDatabase'
elif '/Z7' in buildtype_args:
ET.SubElement(type_config, 'DebugInformationFormat').text = 'OldStyle'
- # Generate Debug info
- if '/DEBUG' in buildtype_link_args:
- ET.SubElement(type_config, 'GenerateDebugInformation').text = 'true'
# Runtime checks
if '/RTC1' in buildtype_args:
ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'EnableFastChecks'
@@ -884,6 +882,9 @@ class Vs2010Backend(backends.Backend):
# vcxproj file (similar to buildtype compiler args) instead of in
# AdditionalOptions?
extra_link_args += compiler.get_buildtype_linker_args(self.buildtype)
+ # Generate Debug info
+ if self.buildtype.startswith('debug'):
+ self.generate_debug_information(link)
if not isinstance(target, build.StaticLibrary):
if isinstance(target, build.SharedModule):
extra_link_args += compiler.get_std_shared_module_link_args()
@@ -1189,3 +1190,7 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
cmd_templ % ('" "'.join(test_command))
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
+
+ def generate_debug_information(self, link):
+ # valid values for vs2015 is 'false', 'true', 'DebugFastLink'
+ ET.SubElement(link, 'GenerateDebugInformation').text = 'true'
diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py
index fe1d7c7..9098226 100644
--- a/mesonbuild/backend/vs2017backend.py
+++ b/mesonbuild/backend/vs2017backend.py
@@ -13,6 +13,7 @@
# limitations under the License.
import os
+import xml.etree.ElementTree as ET
from .vs2010backend import Vs2010Backend
@@ -27,3 +28,7 @@ class Vs2017Backend(Vs2010Backend):
sdk_version = os.environ.get('WindowsSDKVersion', None)
if sdk_version:
self.windows_target_platform_version = sdk_version.rstrip('\\')
+
+ def generate_debug_information(self, link):
+ # valid values for vs2017 is 'false', 'true', 'DebugFastLink', 'DebugFull'
+ ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull'
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 199d7df..aca3aea 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -567,7 +567,7 @@ class XCodeBackend(backends.Backend):
self.write_line('shellPath = /bin/sh;')
script_root = self.environment.get_script_dir()
test_script = os.path.join(script_root, 'meson_test.py')
- cmd = [sys.executable, test_script, test_data, '--wd', self.environment.get_build_dir()]
+ cmd = mesonlib.python_command + [test_script, test_data, '--wd', self.environment.get_build_dir()]
cmdstr = ' '.join(["'%s'" % i for i in cmd])
self.write_line('shellScript = "%s";' % cmdstr)
self.write_line('showEnvVarsInLog = 0;')
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 5f552c2..8eb95dc 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1137,7 +1137,7 @@ class GeneratedList:
class Executable(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
- # Unless overriden, executables have no suffix or prefix. Except on
+ # Unless overridden, executables have no suffix or prefix. Except on
# Windows and with C#/Mono executables where the suffix is 'exe'
if not hasattr(self, 'prefix'):
self.prefix = ''
@@ -1746,7 +1746,7 @@ class Jar(BuildTarget):
class CustomTargetIndex:
- """A special opaque object returned by indexing a CustomTaget. This object
+ """A special opaque object returned by indexing a CustomTarget. This object
exists in meson, but acts as a proxy in the backends, making targets depend
on the CustomTarget it's derived from, but only adding one source file to
the sources.
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 9e85712..2d12314 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -18,6 +18,7 @@ from .. import mlog
from .. import coredata
from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify
from ..mesonlib import for_windows, for_darwin, for_cygwin
+from . import compilers
from .compilers import (
GCC_MINGW,
@@ -89,6 +90,8 @@ class CCompiler(Compiler):
# The default behavior is this, override in MSVC
def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
+ if self.id == 'clang' and self.clang_type == compilers.CLANG_OSX:
+ return self.build_osx_rpath_args(build_dir, rpath_paths, build_rpath)
return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
def get_dependency_gen_args(self, outtarget, outfile):
@@ -724,10 +727,12 @@ class CCompiler(Compiler):
if for_darwin(env.is_cross_build(), env):
shlibext = ['dylib']
elif for_windows(env.is_cross_build(), env):
+ # FIXME: .lib files can be import or static so we should read the
+ # file, figure out which one it is, and reject the wrong kind.
if self.id == 'msvc':
shlibext = ['lib']
else:
- shlibext = ['dll', 'dll.a', 'lib']
+ shlibext = ['dll.a', 'lib', 'dll']
# Yep, static libraries can also be foo.lib
stlibext += ['lib']
elif for_cygwin(env.is_cross_build(), env):
@@ -811,6 +816,12 @@ class ClangCCompiler(ClangCompiler, CCompiler):
def get_option_link_args(self, options):
return []
+ def get_linker_always_args(self):
+ basic = super().get_linker_always_args()
+ if self.clang_type == compilers.CLANG_OSX:
+ return basic + ['-Wl,-headerpad_max_install_names']
+ return basic
+
class GnuCCompiler(GnuCompiler, CCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None):
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 032ca69..011c222 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -451,9 +451,9 @@ class CompilerArgs(list):
Returns whether the argument can be safely de-duped. This is dependent
on three things:
- a) Whether an argument can be 'overriden' by a later argument. For
+ a) Whether an argument can be 'overridden' by a later argument. For
example, -DFOO defines FOO and -UFOO undefines FOO. In this case, we
- can safely remove the previous occurance and add a new one. The same
+ can safely remove the previous occurrence and add a new one. The same
is true for include paths and library paths with -I and -L. For
these we return `2`. See `dedup2_prefixes` and `dedup2_args`.
b) Arguments that once specified cannot be undone, such as `-c` or
@@ -511,10 +511,10 @@ class CompilerArgs(list):
continue
i = self.index(each)
if group_start < 0:
- # First occurance of a library
+ # First occurrence of a library
group_start = i
if group_start >= 0:
- # Last occurance of a library
+ # Last occurrence of a library
self.insert(i + 1, '-Wl,--end-group')
self.insert(group_start, '-Wl,--start-group')
return self.compiler.unix_args_to_native(self)
@@ -548,15 +548,15 @@ class CompilerArgs(list):
raise TypeError('can only concatenate list (not "{}") to list'.format(args))
for arg in args:
# If the argument can be de-duped, do it either by removing the
- # previous occurance of it and adding a new one, or not adding the
- # new occurance.
+ # previous occurrence of it and adding a new one, or not adding the
+ # new occurrence.
dedup = self._can_dedup(arg)
if dedup == 1:
# Argument already exists and adding a new instance is useless
if arg in self or arg in pre or arg in post:
continue
if dedup == 2:
- # Remove all previous occurances of the arg and add it anew
+ # Remove all previous occurrences of the arg and add it anew
if arg in self:
self.remove(arg)
if arg in pre:
@@ -605,7 +605,7 @@ class Compiler:
self.exelist = exelist
else:
raise TypeError('Unknown argument to Compiler')
- # In case it's been overriden by a child class already
+ # In case it's been overridden by a child class already
if not hasattr(self, 'file_suffixes'):
self.file_suffixes = lang_suffixes[self.language]
if not hasattr(self, 'can_compile_suffixes'):
@@ -817,6 +817,16 @@ class Compiler:
def get_instruction_set_args(self, instruction_set):
return None
+ def build_osx_rpath_args(self, build_dir, rpath_paths, build_rpath):
+ if not rpath_paths and not build_rpath:
+ return []
+ # On OSX, rpaths must be absolute.
+ abs_rpaths = [os.path.join(build_dir, p) for p in rpath_paths]
+ if build_rpath != '':
+ abs_rpaths.append(build_rpath)
+ args = ['-Wl,-rpath,' + rp for rp in abs_rpaths]
+ return args
+
def build_unix_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
if not rpath_paths and not install_rpath and not build_rpath:
return []
@@ -879,7 +889,11 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i
elif gcc_type == GCC_OSX:
if is_shared_module:
return []
- return ['-install_name', os.path.join(path, 'lib' + shlib_name + '.dylib')]
+ install_name = prefix + shlib_name
+ if soversion is not None:
+ install_name += '.' + soversion
+ install_name += '.dylib'
+ return ['-install_name', os.path.join('@rpath', install_name)]
else:
raise RuntimeError('Not implemented yet.')
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index a9093b3..15eb9a0 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -76,7 +76,7 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def get_options(self):
return {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use',
- ['none', 'c++03', 'c++11', 'c++14', 'c++1z',
+ ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++1z',
'gnu++11', 'gnu++14', 'gnu++1z'],
'none')}
@@ -102,7 +102,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
def get_options(self):
opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use',
- ['none', 'c++03', 'c++11', 'c++14', 'c++1z',
+ ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++1z',
'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'],
'none'),
'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl',
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
index 9da9b81..b91da6d 100644
--- a/mesonbuild/compilers/vala.py
+++ b/mesonbuild/compilers/vala.py
@@ -94,3 +94,9 @@ class ValaCompiler(Compiler):
return [vapi]
mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname))
return None
+
+ def thread_flags(self):
+ return []
+
+ def thread_link_flags(self):
+ return []
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index e8b23fd..376570c 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -14,6 +14,7 @@
# limitations under the License.
import pickle, os, uuid
+import sys
from pathlib import PurePath
from collections import OrderedDict
from .mesonlib import MesonException, commonpath
@@ -127,24 +128,40 @@ class UserComboOption(UserOption):
class UserStringArrayOption(UserOption):
def __init__(self, name, description, value, **kwargs):
super().__init__(name, description, kwargs.get('choices', []))
- self.set_value(value)
-
- def validate(self, value):
- if isinstance(value, str):
- if not value.startswith('['):
- raise MesonException('Valuestring does not define an array: ' + value)
- newvalue = ast.literal_eval(value)
+ self.set_value(value, user_input=False)
+
+ def validate(self, value, user_input):
+ # User input is for options defined on the command line (via -D
+ # options). Users can put their input in as a comma separated
+ # string, but for defining options in meson_options.txt the format
+ # should match that of a combo
+ if not user_input:
+ if isinstance(value, str):
+ if not value.startswith('['):
+ raise MesonException('Valuestring does not define an array: ' + value)
+ newvalue = ast.literal_eval(value)
+ else:
+ newvalue = value
else:
- newvalue = value
+ assert isinstance(value, str)
+ if value.startswith('['):
+ newvalue = ast.literal_eval(value)
+ else:
+ newvalue = [v.strip() for v in value.split(',')]
if not isinstance(newvalue, list):
raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue)))
for i in newvalue:
if not isinstance(i, str):
raise MesonException('String array element "{0}" is not a string.'.format(str(newvalue)))
+ if self.choices:
+ bad = [x for x in newvalue if x not in self.choices]
+ if bad:
+ raise MesonException('Options "{}" are not in allowed choices: "{}"'.format(
+ ', '.join(bad), ', '.join(self.choices)))
return newvalue
- def set_value(self, newvalue):
- self.value = self.validate(newvalue)
+ def set_value(self, newvalue, user_input=True):
+ self.value = self.validate(newvalue, user_input)
def validate_value(self, value):
self.validate(value)
@@ -172,10 +189,7 @@ class CoreData:
self.external_preprocess_args = {} # CPPFLAGS only
self.external_args = {} # CPPFLAGS + CFLAGS
self.external_link_args = {} # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS)
- if options.cross_file is not None:
- self.cross_file = os.path.join(os.getcwd(), options.cross_file)
- else:
- self.cross_file = None
+ self.cross_file = self.__load_cross_file(options.cross_file)
self.wrap_mode = options.wrap_mode
self.compilers = OrderedDict()
self.cross_compilers = OrderedDict()
@@ -184,6 +198,46 @@ class CoreData:
# Only to print a warning if it changes between Meson invocations.
self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '')
+ @staticmethod
+ def __load_cross_file(filename):
+ """Try to load the cross file.
+
+ If the filename is None return None. If the filename is an absolute
+ (after resolving variables and ~), return that absolute path. Next,
+ check if the file is relative to the current source dir. If the path
+ still isn't resolved do the following:
+ Linux + BSD:
+ - $XDG_DATA_HOME/meson/cross (or ~/.local/share/meson/cross if
+ undefined)
+ - $XDG_DATA_DIRS/meson/cross (or
+ /usr/local/share/meson/cross:/usr/share/meson/cross if undefined)
+ - Error
+ *:
+ - Error
+ BSD follows the Linux path and will honor XDG_* if set. This simplifies
+ the implementation somewhat, especially since most BSD users wont set
+ those environment variables.
+ """
+ if filename is None:
+ return None
+ filename = os.path.expanduser(os.path.expandvars(filename))
+ if os.path.isabs(filename):
+ return filename
+ path_to_try = os.path.abspath(filename)
+ if os.path.exists(path_to_try):
+ return path_to_try
+ if sys.platform == 'linux' or 'bsd' in sys.platform.lower():
+ paths = [
+ os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')),
+ ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
+ for path in paths:
+ path_to_try = os.path.join(path, 'meson', 'cross', filename)
+ if os.path.exists(path_to_try):
+ return path_to_try
+ raise MesonException('Cannot find specified cross file: ' + filename)
+
+ raise MesonException('Cannot find specified cross file: ' + filename)
+
def sanitize_prefix(self, prefix):
if not os.path.isabs(prefix):
raise MesonException('prefix value {!r} must be an absolute path'
@@ -193,6 +247,10 @@ class CoreData:
# string is of type 'C:\' because 'C:' is not an absolute path.
if len(prefix) == 3 and prefix[1] == ':':
pass
+ # If prefix is a single character, preserve it since it is
+ # the root directory.
+ elif len(prefix) == 1:
+ pass
else:
prefix = prefix[:-1]
return prefix
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 8815f1d..f8469c5 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -20,11 +20,15 @@ import sys
import stat
import shlex
import shutil
+import textwrap
from enum import Enum
+from pathlib import PurePath
from .. import mlog
from .. import mesonlib
-from ..mesonlib import MesonException, Popen_safe, version_compare_many, listify
+from ..mesonlib import (
+ MesonException, Popen_safe, version_compare_many, version_compare, listify
+)
# These must be defined in this file to avoid cyclical references.
@@ -43,18 +47,17 @@ class DependencyMethods(Enum):
QMAKE = 'qmake'
# Just specify the standard link arguments, assuming the operating system provides the library.
SYSTEM = 'system'
- # Detect using sdl2-config
- SDLCONFIG = 'sdlconfig'
- # Detect using pcap-config
- PCAPCONFIG = 'pcap-config'
- # Detect using cups-config
- CUPSCONFIG = 'cups-config'
- # Detect using libwmf-config
- LIBWMFCONFIG = 'libwmf-config'
# This is only supported on OSX - search the frameworks directory by name.
EXTRAFRAMEWORK = 'extraframework'
# Detect using the sysconfig module.
SYSCONFIG = 'sysconfig'
+ # Specify using a "program"-config style tool
+ CONFIG_TOOL = 'config-tool'
+ # For backewards compatibility
+ SDLCONFIG = 'sdlconfig'
+ CUPSCONFIG = 'cups-config'
+ PCAPCONFIG = 'pcap-config'
+ LIBWMFCONFIG = 'libwmf-config'
class Dependency:
@@ -72,6 +75,16 @@ class Dependency:
raise DependencyException('method {!r} is invalid'.format(method))
method = DependencyMethods(method)
+ # This sets per-too config methods which are deprecated to to the new
+ # generic CONFIG_TOOL value.
+ if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG,
+ DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]:
+ mlog.warning(textwrap.dedent("""\
+ Configuration method {} has been deprecated in favor of
+ 'config-tool'. This will be removed in a future version of
+ meson.""".format(method)))
+ method = DependencyMethods.CONFIG_TOOL
+
# Set the detection method. If the method is set to auto, use any available method.
# If method is set to a specific string, allow only that detection method.
if method == DependencyMethods.AUTO:
@@ -82,7 +95,7 @@ class Dependency:
raise DependencyException(
'Unsupported detection method: {}, allowed methods are {}'.format(
method.value,
- mlog.format_list(map(lambda x: x.value, [DependencyMethods.AUTO] + self.get_methods()))))
+ mlog.format_list([x.value for x in [DependencyMethods.AUTO] + self.get_methods()])))
def __repr__(self):
s = '<{0} {1}: {2}>'
@@ -120,6 +133,9 @@ class Dependency:
def get_pkgconfig_variable(self, variable_name, kwargs):
raise NotImplementedError('{!r} is not a pkgconfig dependency'.format(self.name))
+ def get_configtool_variable(self, variable_name):
+ raise NotImplementedError('{!r} is not a config-tool dependency'.format(self.name))
+
class InternalDependency(Dependency):
def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps):
@@ -141,9 +157,6 @@ class ExternalDependency(Dependency):
self.name = type_name # default
self.is_found = False
self.language = language
- if language and language not in self.env.coredata.compilers:
- m = self.name.capitalize() + ' requires a {} compiler'
- raise DependencyException(m.format(language.capitalize()))
self.version_reqs = kwargs.get('version', None)
self.required = kwargs.get('required', True)
self.silent = kwargs.get('silent', False)
@@ -161,19 +174,153 @@ class ExternalDependency(Dependency):
compilers = self.env.coredata.cross_compilers
else:
compilers = self.env.coredata.compilers
- self.compiler = compilers.get(self.language or 'c', None)
+ # Set the compiler for this dependency if a language is specified,
+ # else try to pick something that looks usable.
+ if self.language:
+ if self.language not in compilers:
+ m = self.name.capitalize() + ' requires a {} compiler'
+ raise DependencyException(m.format(self.language.capitalize()))
+ self.compiler = compilers[self.language]
+ else:
+ # Try to find a compiler that this dependency can use for compiler
+ # checks. It's ok if we don't find one.
+ for lang in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
+ self.compiler = compilers.get(lang, None)
+ if self.compiler:
+ break
def get_compiler(self):
return self.compiler
+class ConfigToolDependency(ExternalDependency):
+
+ """Class representing dependencies found using a config tool."""
+
+ tools = None
+ tool_name = None
+
+ def __init__(self, name, environment, language, kwargs):
+ super().__init__('config-tool', environment, language, kwargs)
+ self.name = name
+ self.tools = listify(kwargs.get('tools', self.tools))
+
+ req_version = kwargs.get('version', None)
+ tool, version = self.find_config(req_version)
+ self.config = tool
+ self.is_found = self.report_config(version, req_version)
+ if not self.is_found:
+ self.config = None
+ return
+ self.version = version
+
+ @classmethod
+ def factory(cls, name, environment, language, kwargs, tools, tool_name):
+ """Constructor for use in dependencies that can be found multiple ways.
+
+ In addition to the standard constructor values, this constructor sets
+ the tool_name and tools values of the instance.
+ """
+ # This deserves some explanation, because metaprogramming is hard.
+ # This uses type() to create a dynamic subclass of ConfigToolDependency
+ # with the tools and tool_name class attributes set, this class is then
+ # instantiated and returned. The reduce function (method) is also
+ # attached, since python's pickle module won't be able to do anything
+ # with this dynamically generated class otherwise.
+ def reduce(_):
+ return (cls.factory,
+ (name, environment, language, kwargs, tools, tool_name))
+ sub = type('{}Dependency'.format(name.capitalize()), (cls, ),
+ {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce})
+
+ return sub(name, environment, language, kwargs)
+
+ def find_config(self, versions=None):
+ """Helper method that searchs for config tool binaries in PATH and
+ returns the one that best matches the given version requirements.
+ """
+ if not isinstance(versions, list) and versions is not None:
+ versions = listify(versions)
+
+ best_match = (None, None)
+ for tool in self.tools:
+ try:
+ p, out = Popen_safe([tool, '--version'])[:2]
+ except (FileNotFoundError, PermissionError):
+ continue
+ if p.returncode != 0:
+ continue
+
+ out = out.strip()
+ # Some tools, like pcap-config don't supply a version, but also
+ # dont fail with --version, in that case just assume that there is
+ # only one verison and return it.
+ if not out:
+ return (tool, 'none')
+ if versions:
+ is_found = version_compare_many(out, versions)[0]
+ # This allows returning a found version without a config tool,
+ # which is useful to inform the user that you found version x,
+ # but y was required.
+ if not is_found:
+ tool = None
+ if best_match[1]:
+ if version_compare(out, '> {}'.format(best_match[1])):
+ best_match = (tool, out)
+ else:
+ best_match = (tool, out)
+
+ return best_match
+
+ def report_config(self, version, req_version):
+ """Helper method to print messages about the tool."""
+ if self.config is None:
+ if version is not None:
+ mlog.log('found {} {!r} but need:'.format(self.tool_name, version),
+ req_version)
+ else:
+ mlog.log("No {} found; can't detect dependency".format(self.tool_name))
+ mlog.log('Dependency {} found:'.format(self.name), mlog.red('NO'))
+ if self.required:
+ raise DependencyException('Dependency {} not found'.format(self.name))
+ return False
+ mlog.log('Found {}:'.format(self.tool_name), mlog.bold(shutil.which(self.config)),
+ '({})'.format(version))
+ mlog.log('Dependency {} found:'.format(self.name), mlog.green('YES'))
+ return True
+
+ def get_config_value(self, args, stage):
+ p, out, err = Popen_safe([self.config] + args)
+ if p.returncode != 0:
+ if self.required:
+ raise DependencyException(
+ 'Could not generate {} for {}.\n{}'.format(
+ stage, self.name, err))
+ return []
+ return shlex.split(out)
+
+ def get_methods(self):
+ return [DependencyMethods.AUTO, DependencyMethods.CONFIG_TOOL]
+
+ def get_configtool_variable(self, variable_name):
+ p, out, _ = Popen_safe([self.config, '--{}'.format(variable_name)])
+ if p.returncode != 0:
+ if self.required:
+ raise DependencyException(
+ 'Could not get variable "{}" for dependency {}'.format(
+ variable_name, self.name))
+ variable = out.strip()
+ mlog.debug('Got config-tool variable {} : {}'.format(variable_name, variable))
+ return variable
+
+
class PkgConfigDependency(ExternalDependency):
# The class's copy of the pkg-config path. Avoids having to search for it
# multiple times in the same Meson invocation.
class_pkgbin = None
- def __init__(self, name, environment, kwargs):
- super().__init__('pkgconfig', environment, None, kwargs)
+ def __init__(self, name, environment, kwargs, language=None):
+ super().__init__('pkgconfig', environment, language, kwargs)
self.name = name
self.is_libtool = False
# Store a copy of the pkg-config path on the object itself so it is
@@ -265,12 +412,40 @@ class PkgConfigDependency(ExternalDependency):
p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2]
return p.returncode, out.strip()
+ def _convert_mingw_paths(self, args):
+ '''
+ Both MSVC and native Python on Windows cannot handle MinGW-esque /c/foo
+ paths so convert them to C:/foo. We cannot resolve other paths starting
+ with / like /home/foo so leave them as-is so that the user gets an
+ error/warning from the compiler/linker.
+ '''
+ if not mesonlib.is_windows():
+ return args
+ converted = []
+ for arg in args:
+ pargs = []
+ # Library search path
+ if arg.startswith('-L/'):
+ pargs = PurePath(arg[2:]).parts
+ tmpl = '-L{}:/{}'
+ elif arg.startswith('-I/'):
+ pargs = PurePath(arg[2:]).parts
+ tmpl = '-I{}:/{}'
+ # Full path to library or .la file
+ elif arg.startswith('/'):
+ pargs = PurePath(arg).parts
+ tmpl = '{}:/{}'
+ if len(pargs) > 1 and len(pargs[1]) == 1:
+ arg = tmpl.format(pargs[1], '/'.join(pargs[2:]))
+ converted.append(arg)
+ return converted
+
def _set_cargs(self):
ret, out = self._call_pkgbin(['--cflags', self.name])
if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n\n%s' %
(self.name, out))
- self.compile_args = shlex.split(out)
+ self.compile_args = self._convert_mingw_paths(shlex.split(out))
def _set_libs(self):
env = None
@@ -287,7 +462,7 @@ class PkgConfigDependency(ExternalDependency):
(self.name, out))
self.link_args = []
libpaths = []
- for lib in shlex.split(out):
+ for lib in self._convert_mingw_paths(shlex.split(out)):
# If we want to use only static libraries, we have to look for the
# file ourselves instead of depending on the compiler to find it
# with -lfoo or foo.lib. However, we can only do this if we already
@@ -436,7 +611,11 @@ class ExternalProgram:
with open(script) as f:
first_line = f.readline().strip()
if first_line.startswith('#!'):
- commands = first_line[2:].split('#')[0].strip().split()
+ # In a shebang, everything before the first space is assumed to
+ # be the command to run and everything after the first space is
+ # the single argument to pass to that command. So we must split
+ # exactly once.
+ commands = first_line[2:].split('#')[0].strip().split(maxsplit=1)
if mesonlib.is_windows():
# Windows does not have UNIX paths so remove them,
# but don't remove Windows paths
@@ -446,7 +625,7 @@ class ExternalProgram:
commands = commands[1:]
# Windows does not ship python3.exe, but we know the path to it
if len(commands) > 0 and commands[0] == 'python3':
- commands[0] = sys.executable
+ commands = mesonlib.python_command + commands[1:]
return commands + [script]
except Exception:
pass
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 257bf7a..d15d5da 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -17,14 +17,16 @@
import os
import re
-import shlex
import shutil
from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list
-from .base import DependencyException, ExternalDependency, PkgConfigDependency
-from .base import strip_system_libdirs
+from .base import (
+ DependencyException, ExternalDependency, PkgConfigDependency,
+ strip_system_libdirs, ConfigToolDependency,
+)
+
class GTestDependency(ExternalDependency):
def __init__(self, environment, kwargs):
@@ -109,19 +111,19 @@ class GMockDependency(ExternalDependency):
self.is_found = False
-class LLVMDependency(ExternalDependency):
+class LLVMDependency(ConfigToolDependency):
"""
LLVM uses a special tool, llvm-config, which has arguments for getting
c args, cxx args, and ldargs as well as version.
"""
# Ordered list of llvm-config binaries to try. Start with base, then try
- # newest back to oldest (3.5 is abitrary), and finally the devel version.
+ # newest back to oldest (3.5 is arbitrary), and finally the devel version.
# Please note that llvm-config-6.0 is a development snapshot and it should
# not be moved to the beginning of the list. The only difference between
# llvm-config-6.0 and llvm-config-devel is that the former is used by
# Debian and the latter is used by FreeBSD.
- llvm_config_bins = [
+ tools = [
'llvm-config', # base
'llvm-config-5.0', 'llvm-config50', # latest stable release
'llvm-config-4.0', 'llvm-config40', # old stable releases
@@ -132,61 +134,31 @@ class LLVMDependency(ExternalDependency):
'llvm-config-3.5', 'llvm-config35',
'llvm-config-6.0', 'llvm-config-devel', # development snapshot
]
+ tool_name = 'llvm-config'
__cpp_blacklist = {'-DNDEBUG'}
def __init__(self, environment, kwargs):
# It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
# the C linker works fine if only using the C API.
- super().__init__('llvm-config', environment, 'cpp', kwargs)
+ super().__init__('config-tool', environment, 'cpp', kwargs)
self.provided_modules = []
self.required_modules = set()
- self.llvmconfig = None
- self.static = kwargs.get('static', False)
- self.__best_found = None
- # FIXME: Support multiple version requirements ala PkgConfigDependency
- req_version = kwargs.get('version', None)
- self.check_llvmconfig(req_version)
- if self.llvmconfig is None:
- if self.__best_found is not None:
- mlog.log('found {!r} but need:'.format(self.__best_found),
- req_version)
- else:
- mlog.log("No llvm-config found; can't detect dependency")
- mlog.log('Dependency LLVM found:', mlog.red('NO'))
- if self.required:
- raise DependencyException('Dependency LLVM not found')
+ if not self.is_found:
return
+ self.static = kwargs.get('static', False)
- p, out, err = Popen_safe([self.llvmconfig, '--version'])
- if p.returncode != 0:
- mlog.debug('stdout: {}\nstderr: {}'.format(out, err))
- if self.required:
- raise DependencyException('Dependency LLVM not found')
- mlog.log('Dependency LLVM found:', mlog.red('NO'))
- return
-
- mlog.log('Dependency LLVM found:', mlog.green('YES'))
- self.is_found = True
-
- # Currently meson doesn't really atempt to handle pre-release versions,
+ # Currently meson doesn't really attempt to handle pre-release versions,
# so strip the 'svn' off the end, since it will probably cuase problems
# for users who want the patch version.
- self.version = out.strip().rstrip('svn')
-
- p, out, err = Popen_safe([self.llvmconfig, '--components'])
- if p.returncode != 0:
- raise DependencyException('Could not generate modules for LLVM:\n' + err)
- self.provided_modules = shlex.split(out)
+ self.version = self.version.rstrip('svn')
+ self.provided_modules = self.get_config_value(['--components'], 'modules')
modules = stringlistify(extract_as_list(kwargs, 'modules'))
self.check_components(modules)
opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
self.check_components(opt_modules, required=False)
- p, out, err = Popen_safe([self.llvmconfig, '--cppflags'])
- if p.returncode != 0:
- raise DependencyException('Could not generate includedir for LLVM:\n' + err)
- cargs = mesonlib.OrderedSet(shlex.split(out))
+ cargs = set(self.get_config_value(['--cppflags'], 'compile_args'))
self.compile_args = list(cargs.difference(self.__cpp_blacklist))
if version_compare(self.version, '>= 3.9'):
@@ -198,11 +170,9 @@ class LLVMDependency(ExternalDependency):
def _set_new_link_args(self):
"""How to set linker args for LLVM versions >= 3.9"""
link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared']
- p, out, err = Popen_safe(
- [self.llvmconfig, '--libs', '--ldflags'] + link_args + list(self.required_modules))
- if p.returncode != 0:
- raise DependencyException('Could not generate libs for LLVM:\n' + err)
- self.link_args = shlex.split(out)
+ self.link_args = self.get_config_value(
+ ['--libs', '--ldflags'] + link_args + list(self.required_modules),
+ 'link_args')
def _set_old_link_args(self):
"""Setting linker args for older versions of llvm.
@@ -213,20 +183,15 @@ class LLVMDependency(ExternalDependency):
of course we do.
"""
if self.static:
- p, out, err = Popen_safe(
- [self.llvmconfig, '--libs', '--ldflags', '--system-libs'] + list(self.required_modules))
- if p.returncode != 0:
- raise DependencyException('Could not generate libs for LLVM:\n' + err)
- self.link_args = shlex.split(out)
+ self.link_args = self.get_config_value(
+ ['--libs', '--ldflags', '--system-libs'] + list(self.required_modules),
+ 'link_args')
else:
# llvm-config will provide arguments for static linking, so we get
# to figure out for ourselves what to link with. We'll do that by
# checking in the directory provided by --libdir for a library
# called libLLVM-<ver>.(so|dylib|dll)
- p, out, err = Popen_safe([self.llvmconfig, '--libdir'])
- if p.returncode != 0:
- raise DependencyException('Could not generate libs for LLVM:\n' + err)
- libdir = out.strip()
+ libdir = self.get_config_value(['--libdir'], 'link_args')[0]
expected_name = 'libLLVM-{}'.format(self.version)
re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name))
@@ -259,34 +224,6 @@ class LLVMDependency(ExternalDependency):
self.required_modules.add(mod)
mlog.log('LLVM module', mod, 'found:', mlog.green('YES'))
- def check_llvmconfig(self, version_req):
- """Try to find the highest version of llvm-config."""
- for llvmconfig in self.llvm_config_bins:
- try:
- p, out = Popen_safe([llvmconfig, '--version'])[0:2]
- out = out.strip()
- if p.returncode != 0:
- continue
- if version_req:
- if version_compare(out, version_req, strict=True):
- if self.__best_found and version_compare(
- out, '<={}'.format(self.__best_found), strict=True):
- continue
- self.__best_found = out
- self.llvmconfig = llvmconfig
- else:
- # If no specific version is requested use the first version
- # found, since that should be the best.
- self.__best_found = out
- self.llvmconfig = llvmconfig
- break
- except (FileNotFoundError, PermissionError):
- pass
- if self.__best_found:
- mlog.log('Found llvm-config:',
- mlog.bold(shutil.which(self.llvmconfig)),
- '({})'.format(out.strip()))
-
def need_threads(self):
return True
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 17f4b7f..1d19b04 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2017 The Meson development team
+# Copyright 2013-2017 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.
@@ -26,8 +26,11 @@ from .. import mesonlib
from ..mesonlib import Popen_safe, extract_as_list
from ..environment import detect_cpu_family
-from .base import DependencyException, DependencyMethods
-from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency
+from .base import (
+ DependencyException, DependencyMethods, ExternalDependency,
+ ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency,
+ ConfigToolDependency,
+)
# On windows 3 directory layouts are supported:
# * The default layout (versioned) installed:
@@ -405,7 +408,7 @@ class MPIDependency(ExternalDependency):
for pkg in pkgconfig_files:
try:
- pkgdep = PkgConfigDependency(pkg, environment, kwargs)
+ pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
if pkgdep.found():
self.compile_args = pkgdep.get_compile_args()
self.link_args = pkgdep.get_link_args()
@@ -622,8 +625,10 @@ class Python3Dependency(ExternalDependency):
elif mesonlib.is_osx() and DependencyMethods.EXTRAFRAMEWORK in self.methods:
# In OSX the Python 3 framework does not have a version
# number in its name.
- fw = ExtraFrameworkDependency('python', False, None, self.env,
- self.language, kwargs)
+ # There is a python in /System/Library/Frameworks, but that's
+ # python 2, Python 3 will always bin in /Library
+ fw = ExtraFrameworkDependency(
+ 'python', False, '/Library/Frameworks', self.env, self.language, kwargs)
if fw.found():
self.compile_args = fw.get_compile_args()
self.link_args = fw.get_link_args()
@@ -687,9 +692,9 @@ class Python3Dependency(ExternalDependency):
class PcapDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('pcap', environment, None, kwargs)
+ kwargs['required'] = False
if DependencyMethods.PKGCONFIG in self.methods:
try:
- kwargs['required'] = False
pcdep = PkgConfigDependency('pcap', environment, kwargs)
if pcdep.found():
self.type_name = 'pkgconfig'
@@ -700,25 +705,26 @@ class PcapDependency(ExternalDependency):
return
except Exception as e:
mlog.debug('Pcap not found via pkgconfig. Trying next, error was:', str(e))
- if DependencyMethods.PCAPCONFIG in self.methods:
- pcapconf = shutil.which('pcap-config')
- if pcapconf:
- stdo = Popen_safe(['pcap-config', '--cflags'])[1]
- self.compile_args = stdo.strip().split()
- stdo = Popen_safe(['pcap-config', '--libs'])[1]
- self.link_args = stdo.strip().split()
- self.version = self.get_pcap_lib_version()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('pcap'), 'found:',
- mlog.green('YES'), '(%s)' % pcapconf)
- return
- mlog.debug('Could not find pcap-config binary, trying next.')
+ if DependencyMethods.CONFIG_TOOL in self.methods:
+ try:
+ ctdep = ConfigToolDependency.factory(
+ 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config')
+ if ctdep.found():
+ self.config = ctdep.config
+ self.type_name = 'config-tool'
+ self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
+ self.link_args = ctdep.get_config_value(['--libs'], 'link_args')
+ self.version = self.get_pcap_lib_version()
+ self.is_found = True
+ return
+ except Exception as e:
+ mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e))
def get_methods(self):
if mesonlib.is_osx():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK]
else:
- return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
def get_pcap_lib_version(self):
return self.compiler.get_return_value('pcap_lib_version', 'string',
@@ -728,9 +734,9 @@ class PcapDependency(ExternalDependency):
class CupsDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('cups', environment, None, kwargs)
+ kwargs['required'] = False
if DependencyMethods.PKGCONFIG in self.methods:
try:
- kwargs['required'] = False
pcdep = PkgConfigDependency('cups', environment, kwargs)
if pcdep.found():
self.type_name = 'pkgconfig'
@@ -741,20 +747,20 @@ class CupsDependency(ExternalDependency):
return
except Exception as e:
mlog.debug('cups not found via pkgconfig. Trying next, error was:', str(e))
- if DependencyMethods.CUPSCONFIG in self.methods:
- cupsconf = shutil.which('cups-config')
- if cupsconf:
- stdo = Popen_safe(['cups-config', '--cflags'])[1]
- self.compile_args = stdo.strip().split()
- stdo = Popen_safe(['cups-config', '--libs'])[1]
- self.link_args = stdo.strip().split()
- stdo = Popen_safe(['cups-config', '--version'])[1]
- self.version = stdo.strip().split()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('cups'), 'found:',
- mlog.green('YES'), '(%s)' % cupsconf)
- return
- mlog.debug('Could not find cups-config binary, trying next.')
+ if DependencyMethods.CONFIG_TOOL in self.methods:
+ try:
+ ctdep = ConfigToolDependency.factory(
+ 'cups', environment, None, kwargs, ['cups-config'], 'cups-config')
+ if ctdep.found():
+ self.config = ctdep.config
+ self.type_name = 'config-tool'
+ self.version = ctdep.version
+ self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
+ self.link_args = ctdep.get_config_value(['--libs'], 'link_args')
+ self.is_found = True
+ return
+ except Exception as e:
+ mlog.debug('cups not found via cups-config. Trying next, error was:', str(e))
if DependencyMethods.EXTRAFRAMEWORK in self.methods:
if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency('cups', False, None, self.env,
@@ -769,9 +775,9 @@ class CupsDependency(ExternalDependency):
def get_methods(self):
if mesonlib.is_osx():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK]
else:
- return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
class LibWmfDependency(ExternalDependency):
@@ -790,26 +796,27 @@ class LibWmfDependency(ExternalDependency):
return
except Exception as e:
mlog.debug('LibWmf not found via pkgconfig. Trying next, error was:', str(e))
- if DependencyMethods.LIBWMFCONFIG in self.methods:
- libwmfconf = shutil.which('libwmf-config')
- if libwmfconf:
- stdo = Popen_safe(['libwmf-config', '--cflags'])[1]
- self.compile_args = stdo.strip().split()
- stdo = Popen_safe(['libwmf-config', '--libs'])[1]
- self.link_args = stdo.strip().split()
- stdo = Popen_safe(['libwmf-config', '--version'])[1]
- self.version = stdo.strip()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('libwmf'), 'found:',
- mlog.green('YES'), '(%s)' % libwmfconf)
- return
- mlog.debug('Could not find libwmf-config binary, trying next.')
+ if DependencyMethods.CONFIG_TOOL in self.methods:
+ try:
+ ctdep = ConfigToolDependency.factory(
+ 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config')
+ if ctdep.found():
+ self.config = ctdep.config
+ self.type_name = 'config-too'
+ self.version = ctdep.version
+ self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
+ self.link_args = ctdep.get_config_value(['--libs'], 'link_args')
+ self.is_found = True
+ return
+ except Exception as e:
+ mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e))
def get_methods(self):
if mesonlib.is_osx():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK]
else:
- return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
+
# Generated with boost_names.py
BOOST_LIBS = [
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index d19d07e..1db518c 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -23,13 +23,16 @@ from collections import OrderedDict
from .. import mlog
from .. import mesonlib
-from ..mesonlib import MesonException, Popen_safe, version_compare
-from ..mesonlib import extract_as_list, for_windows
+from ..mesonlib import (
+ MesonException, Popen_safe, extract_as_list, for_windows,
+ version_compare_many
+)
from ..environment import detect_cpu
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExternalProgram
from .base import ExtraFrameworkDependency, PkgConfigDependency
+from .base import ConfigToolDependency
class GLDependency(ExternalDependency):
@@ -70,49 +73,48 @@ class GLDependency(ExternalDependency):
return [DependencyMethods.PKGCONFIG]
-class GnuStepDependency(ExternalDependency):
+class GnuStepDependency(ConfigToolDependency):
+
+ tools = ['gnustep-config']
+ tool_name = 'gnustep-config'
+
def __init__(self, environment, kwargs):
super().__init__('gnustep', environment, 'objc', kwargs)
+ if not self.is_found:
+ return
self.modules = kwargs.get('modules', [])
- self.detect()
-
- def detect(self):
- self.confprog = 'gnustep-config'
+ self.compile_args = self.filter_args(
+ self.get_config_value(['--objc-flags'], 'compile_args'))
+ self.link_args = self.weird_filter(self.get_config_value(
+ ['--gui-libs' if 'gui' in self.modules else '--base-libs'],
+ 'link_args'))
+
+ def find_config(self, versions=None):
+ tool = self.tools[0]
try:
- gp = Popen_safe([self.confprog, '--help'])[0]
+ p, out = Popen_safe([tool, '--help'])[:2]
except (FileNotFoundError, PermissionError):
- mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)')
- return
- if gp.returncode != 0:
- mlog.log('Dependency GnuStep found:', mlog.red('NO'))
- return
- if 'gui' in self.modules:
- arg = '--gui-libs'
- else:
- arg = '--base-libs'
- fp, flagtxt, flagerr = Popen_safe([self.confprog, '--objc-flags'])
- if fp.returncode != 0:
- raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr))
- args = flagtxt.split()
- self.compile_args = self.filter_args(args)
- fp, libtxt, liberr = Popen_safe([self.confprog, arg])
- if fp.returncode != 0:
- raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr))
- self.link_args = self.weird_filter(libtxt.split())
- self.version = self.detect_version()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('GnuStep'), 'found:',
- mlog.green('YES'), self.version)
+ return (None, None)
+ if p.returncode != 0:
+ return (None, None)
+ self.config = tool
+ found_version = self.detect_version()
+ if versions and not version_compare_many(found_version, versions)[0]:
+ return (None, found_version)
+
+ return (tool, found_version)
def weird_filter(self, elems):
- """When building packages, the output of the enclosing Make
-is sometimes mixed among the subprocess output. I have no idea
-why. As a hack filter out everything that is not a flag."""
+ """When building packages, the output of the enclosing Make is
+ sometimes mixed among the subprocess output. I have no idea why. As a
+ hack filter out everything that is not a flag.
+ """
return [e for e in elems if e.startswith('-')]
def filter_args(self, args):
- """gnustep-config returns a bunch of garbage args such
- as -O2 and so on. Drop everything that is not needed."""
+ """gnustep-config returns a bunch of garbage args such as -O2 and so
+ on. Drop everything that is not needed.
+ """
result = []
for f in args:
if f.startswith('-D') \
@@ -124,8 +126,8 @@ why. As a hack filter out everything that is not a flag."""
return result
def detect_version(self):
- gmake = self.get_variable('GNUMAKE')
- makefile_dir = self.get_variable('GNUSTEP_MAKEFILES')
+ gmake = self.get_config_value(['--variable=GNUMAKE'], 'variable')[0]
+ makefile_dir = self.get_config_value(['--variable=GNUSTEP_MAKEFILES'], 'variable')[0]
# This Makefile has the GNUStep version set
base_make = os.path.join(makefile_dir, 'Additional', 'base.make')
# Print the Makefile variable passed as the argument. For instance, if
@@ -145,13 +147,6 @@ why. As a hack filter out everything that is not a flag."""
version = '1'
return version
- def get_variable(self, var):
- p, o, e = Popen_safe([self.confprog, '--variable=' + var])
- if p.returncode != 0 and self.required:
- raise DependencyException('{!r} for variable {!r} failed to run'
- ''.format(self.confprog, var))
- return o.strip()
-
class QtBaseDependency(ExternalDependency):
def __init__(self, name, env, kwargs):
@@ -207,13 +202,15 @@ class QtBaseDependency(ExternalDependency):
moc = ExternalProgram(os.path.join(self.bindir, 'moc'), silent=True)
uic = ExternalProgram(os.path.join(self.bindir, 'uic'), silent=True)
rcc = ExternalProgram(os.path.join(self.bindir, 'rcc'), silent=True)
+ lrelease = ExternalProgram(os.path.join(self.bindir, 'lrelease'), silent=True)
else:
# We don't accept unsuffixed 'moc', 'uic', and 'rcc' because they
# are sometimes older, or newer versions.
moc = ExternalProgram('moc-' + self.name, silent=True)
uic = ExternalProgram('uic-' + self.name, silent=True)
rcc = ExternalProgram('rcc-' + self.name, silent=True)
- return moc, uic, rcc
+ lrelease = ExternalProgram('lrelease-' + self.name, silent=True)
+ return moc, uic, rcc, lrelease
def _pkgconfig_detect(self, mods, kwargs):
# We set the value of required to False so that we can try the
@@ -221,7 +218,8 @@ class QtBaseDependency(ExternalDependency):
kwargs['required'] = False
modules = OrderedDict()
for module in mods:
- modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env, kwargs)
+ modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env,
+ kwargs, language=self.language)
for m in modules.values():
if not m.found():
self.is_found = False
@@ -235,7 +233,8 @@ class QtBaseDependency(ExternalDependency):
core = modules['Core']
else:
corekwargs = {'required': 'false', 'silent': 'true'}
- core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs)
+ core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs,
+ language=self.language)
# Used by self.compilers_detect()
self.bindir = self.get_pkgconfig_host_bins(core)
if not self.bindir:
@@ -378,9 +377,9 @@ class Qt5Dependency(QtBaseDependency):
class SDL2Dependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('sdl2', environment, None, kwargs)
+ kwargs['required'] = False
if DependencyMethods.PKGCONFIG in self.methods:
try:
- kwargs['required'] = False
pcdep = PkgConfigDependency('sdl2', environment, kwargs)
if pcdep.found():
self.type_name = 'pkgconfig'
@@ -391,20 +390,20 @@ class SDL2Dependency(ExternalDependency):
return
except Exception as e:
mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e))
- if DependencyMethods.SDLCONFIG in self.methods:
- sdlconf = shutil.which('sdl2-config')
- if sdlconf:
- stdo = Popen_safe(['sdl2-config', '--cflags'])[1]
- self.compile_args = stdo.strip().split()
- stdo = Popen_safe(['sdl2-config', '--libs'])[1]
- self.link_args = stdo.strip().split()
- stdo = Popen_safe(['sdl2-config', '--version'])[1]
- self.version = stdo.strip()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'),
- self.version, '(%s)' % sdlconf)
- return
- mlog.debug('Could not find sdl2-config binary, trying next.')
+ if DependencyMethods.CONFIG_TOOL in self.methods:
+ try:
+ ctdep = ConfigToolDependency.factory(
+ 'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config')
+ if ctdep.found():
+ self.type_name = 'config-tool'
+ self.config = ctdep.config
+ self.version = ctdep.version
+ self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
+ self.links_args = ctdep.get_config_value(['--libs'], 'link_args')
+ self.is_found = True
+ return
+ except Exception as e:
+ mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e))
if DependencyMethods.EXTRAFRAMEWORK in self.methods:
if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency('sdl2', False, None, self.env,
@@ -419,54 +418,25 @@ class SDL2Dependency(ExternalDependency):
def get_methods(self):
if mesonlib.is_osx():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK]
else:
- return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG]
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
-class WxDependency(ExternalDependency):
- wx_found = None
+class WxDependency(ConfigToolDependency):
+
+ tools = ['wx-config-3.0', 'wx-config']
+ tool_name = 'wx-config'
def __init__(self, environment, kwargs):
- super().__init__('wx', environment, None, kwargs)
- self.version = 'none'
- if WxDependency.wx_found is None:
- self.check_wxconfig()
- else:
- self.wxc = WxDependency.wx_found
- if not WxDependency.wx_found:
- mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency")
+ super().__init__('WxWidgets', environment, None, kwargs)
+ if not self.is_found:
return
-
- # FIXME: This should print stdout and stderr using mlog.debug
- p, out = Popen_safe([self.wxc, '--version'])[0:2]
- if p.returncode != 0:
- mlog.log('Dependency wxwidgets found:', mlog.red('NO'))
- else:
- self.version = out.strip()
- # FIXME: Support multiple version reqs like PkgConfigDependency
- version_req = kwargs.get('version', None)
- if version_req is not None:
- if not version_compare(self.version, version_req, strict=True):
- mlog.log('Wxwidgets version %s does not fullfill requirement %s' %
- (self.version, version_req))
- return
- mlog.log('Dependency wxwidgets found:', mlog.green('YES'))
- self.is_found = True
- self.requested_modules = self.get_requested(kwargs)
- # wx-config seems to have a cflags as well but since it requires C++,
- # this should be good, at least for now.
- p, out = Popen_safe([self.wxc, '--cxxflags'])[0:2]
- # FIXME: this error should only be raised if required is true
- if p.returncode != 0:
- raise DependencyException('Could not generate cargs for wxwidgets.')
- self.compile_args = out.split()
-
- # FIXME: this error should only be raised if required is true
- p, out = Popen_safe([self.wxc, '--libs'] + self.requested_modules)[0:2]
- if p.returncode != 0:
- raise DependencyException('Could not generate libs for wxwidgets.')
- self.link_args = out.split()
+ self.requested_modules = self.get_requested(kwargs)
+ # 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'], 'compile_args')
+ self.link_args = self.get_config_value(['--libs'], 'link_args')
def get_requested(self, kwargs):
if 'modules' not in kwargs:
@@ -477,20 +447,6 @@ class WxDependency(ExternalDependency):
raise DependencyException('wxwidgets module argument is not a string')
return candidates
- def check_wxconfig(self):
- for wxc in ['wx-config-3.0', 'wx-config']:
- try:
- p, out = Popen_safe([wxc, '--version'])[0:2]
- if p.returncode == 0:
- mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)),
- '(%s)' % out.strip())
- self.wxc = wxc
- WxDependency.wx_found = wxc
- return
- except (FileNotFoundError, PermissionError):
- pass
- WxDependency.wxconfig_found = False
- mlog.log('Found wx-config:', mlog.red('NO'))
class VulkanDependency(ExternalDependency):
def __init__(self, environment, kwargs):
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index d9146eb..0cb1450 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -154,7 +154,7 @@ def detect_windows_arch(compilers):
# Check if we're using and inside an MSVC toolchain environment
if compiler.id == 'msvc' and 'VCINSTALLDIR' in os.environ:
# 'Platform' is only set when the target arch is not 'x86'.
- # It's 'x64' when targetting x86_64 and 'arm' when targetting ARM.
+ # It's 'x64' when targeting x86_64 and 'arm' when targeting ARM.
platform = os.environ.get('Platform', 'x86').lower()
if platform == 'x86':
return platform
@@ -325,14 +325,10 @@ class Environment:
return self.coredata
def get_build_command(self, unbuffered=False):
- # If running an executable created with cx_freeze,
- # Python might not be installed so don't prefix
- # the command with it.
- if sys.executable.endswith('meson.exe'):
- return [sys.executable]
- if unbuffered:
- [sys.executable, '-u', self.meson_script_launcher]
- return [sys.executable, self.meson_script_launcher]
+ cmd = mesonlib.meson_command[:]
+ if unbuffered and 'python' in cmd[0]:
+ cmd.insert(1, '-u')
+ return cmd
def is_header(self, fname):
return is_header(fname)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 41976af..ff93feb 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -274,6 +274,7 @@ class DependencyHolder(InterpreterObject, ObjectHolder):
'type_name': self.type_name_method,
'version': self.version_method,
'get_pkgconfig_variable': self.pkgconfig_method,
+ 'get_configtool_variable': self.configtool_method,
})
def type_name_method(self, args, kwargs):
@@ -296,6 +297,15 @@ class DependencyHolder(InterpreterObject, ObjectHolder):
raise InterpreterException('Variable name must be a string.')
return self.held_object.get_pkgconfig_variable(varname, kwargs)
+ def configtool_method(self, args, kwargs):
+ args = listify(args)
+ if len(args) != 1:
+ raise InterpreterException('get_configtool_variable takes exactly one argument.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Variable name must be a string.')
+ return self.held_object.get_configtool_variable(varname)
+
class InternalDependencyHolder(InterpreterObject, ObjectHolder):
def __init__(self, dep):
InterpreterObject.__init__(self)
@@ -1358,7 +1368,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'build_target': build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
- 'dependency': {'default_options', 'fallback', 'language', 'method', 'modules', 'native', 'required', 'static', 'version'},
+ 'dependency': {'default_options', 'fallback', 'language', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'},
'executable': exe_kwargs,
'find_program': {'required', 'native'},
@@ -1374,6 +1384,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'shared_library': shlib_kwargs,
'shared_module': shmod_kwargs,
'static_library': stlib_kwargs,
+ 'subdir': {'if_found'},
'subproject': {'version', 'default_options'},
'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'},
'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'},
@@ -2477,7 +2488,7 @@ to directly access options of other subprojects.''')
self.build.man.append(m)
return m
- @noKwargs
+ @permittedKwargs(permitted_kwargs['subdir'])
def func_subdir(self, node, args, kwargs):
self.validate_arguments(args, 1, [str])
if '..' in args[0]:
@@ -2486,6 +2497,11 @@ to directly access options of other subprojects.''')
raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.')
if self.subdir == '' and args[0].startswith('meson-'):
raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().')
+ for i in mesonlib.extract_as_list(kwargs, 'if_found'):
+ if not hasattr(i, 'found_method'):
+ raise InterpreterException('Object used in if_found does not have a found method.')
+ if not i.found_method([], {}):
+ return
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
if os.path.isabs(subdir):
@@ -2983,12 +2999,19 @@ different subdirectory.
def format_string(self, templ, args):
if isinstance(args, mparser.ArgumentNode):
args = args.arguments
- for (i, arg) in enumerate(args):
+ arg_strings = []
+ for arg in args:
arg = self.evaluate_statement(arg)
if isinstance(arg, bool): # Python boolean is upper case.
arg = str(arg).lower()
- templ = templ.replace('@{}@'.format(i), str(arg))
- return templ
+ arg_strings.append(str(arg))
+
+ def arg_replace(match):
+ idx = int(match.group(1))
+ if idx >= len(arg_strings):
+ raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
+ return arg_strings[idx]
+ return re.sub(r'@(\d+)@', arg_replace, templ)
# Only permit object extraction from the same subproject
def validate_extraction(self, buildtarget):
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index cb82e56..7ccc8b2 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -449,6 +449,11 @@ class InterpreterBase:
return obj % 2 != 0
else:
raise InterpreterException('int.is_odd() must have no arguments.')
+ elif method_name == 'to_string':
+ if not posargs:
+ return str(obj)
+ else:
+ raise InterpreterException('int.to_string() must have no arguments.')
else:
raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index e0554e0..de788b7 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -40,10 +40,10 @@ class VisualStudioLinker(StaticLinker):
return []
def get_always_args(self):
- return VisualStudioLinker.always_args
+ return VisualStudioLinker.always_args[:]
def get_linker_always_args(self):
- return VisualStudioLinker.always_args
+ return VisualStudioLinker.always_args[:]
def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
return []
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 09b0f12..771e9ee 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -51,7 +51,7 @@ class Conf:
with open(self.coredata_file, 'wb') as f:
pickle.dump(self.coredata, f)
# We don't write the build file because any changes to it
- # are erased when Meson is executed the next time, i.e. whne
+ # are erased when Meson is executed the next time, i.e. when
# Ninja is run.
def print_aligned(self, arr):
@@ -184,7 +184,7 @@ class Conf:
coarr = []
for k in okeys:
o = self.coredata.base_options[k]
- coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''})
+ coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': o.choices})
self.print_aligned(coarr)
print('')
print('Compiler arguments:')
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index f74c6c1..a35345b 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -22,6 +22,43 @@ import collections
from glob import glob
+def detect_meson_py_location():
+ c = sys.argv[0]
+ c_fname = os.path.split(c)[1]
+ if c_fname == 'meson' or c_fname == 'meson.py':
+ # $ /foo/meson.py <args>
+ if os.path.isabs(c):
+ return c
+ # $ meson <args> (gets run from /usr/bin/meson)
+ in_path_exe = shutil.which(c_fname)
+ if in_path_exe:
+ # Special case: when run like "./meson.py <opts>" and user has
+ # period in PATH, we need to expand it out, because, for example,
+ # "ninja test" will be run from a different directory.
+ if '.' in os.environ['PATH'].split(':'):
+ p, f = os.path.split(in_path_exe)
+ if p == '' or p == '.':
+ return os.path.join(os.getcwd(), f)
+ return in_path_exe
+ # $ python3 ./meson.py <args>
+ if os.path.exists(c):
+ return os.path.join(os.getcwd(), c)
+
+ # The only thing remaining is to try to find the bundled executable and
+ # pray distro packagers have not moved it.
+ fname = os.path.join(os.path.dirname(__file__), '..', 'meson.py')
+ if not os.path.exists(fname):
+ raise RuntimeError('Could not determine how to run Meson. Please file a bug with details.')
+ return fname
+
+if os.path.basename(sys.executable) == 'meson.exe':
+ # In Windows and using the MSI installed executable.
+ meson_command = [sys.executable]
+ python_command = [sys.executable, 'runpython']
+else:
+ python_command = [sys.executable]
+ meson_command = python_command + [detect_meson_py_location()]
+
# Put this in objects that should not get dumped to pickle files
# by accident.
import threading
@@ -394,24 +431,32 @@ def get_library_dirs():
def do_replacement(regex, line, confdata):
- match = re.search(regex, line)
missing_variables = set()
- while match:
- varname = match.group(1)
- if varname in confdata:
- (var, desc) = confdata.get(varname)
- if isinstance(var, str):
- pass
- elif isinstance(var, int):
- var = str(var)
- else:
- raise RuntimeError('Tried to replace a variable with something other than a string or int.')
+
+ def variable_replace(match):
+ # Pairs of escape characters before '@' or '\@'
+ if match.group(0).endswith('\\'):
+ num_escapes = match.end(0) - match.start(0)
+ return '\\' * (num_escapes // 2)
+ # Single escape character and '@'
+ elif match.group(0) == '\\@':
+ return '@'
+ # Template variable to be replaced
else:
- missing_variables.add(varname)
- var = ''
- line = line.replace('@' + varname + '@', var)
- match = re.search(regex, line)
- return line, missing_variables
+ varname = match.group(1)
+ if varname in confdata:
+ (var, desc) = confdata.get(varname)
+ if isinstance(var, str):
+ pass
+ elif isinstance(var, int):
+ var = str(var)
+ else:
+ raise RuntimeError('Tried to replace a variable with something other than a string or int.')
+ else:
+ missing_variables.add(varname)
+ var = ''
+ return var
+ return re.sub(regex, variable_replace, line), missing_variables
def do_mesondefine(line, confdata):
arr = line.split()
@@ -443,7 +488,7 @@ def do_conf_file(src, dst, confdata):
raise MesonException('Could not read input file %s: %s' % (src, str(e)))
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
# Also allow escaping '@' with '\@'
- regex = re.compile(r'[^\\]?@([-a-zA-Z0-9_]+)@')
+ regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
result = []
missing_variables = set()
for line in data:
@@ -489,7 +534,7 @@ def replace_if_different(dst, dst_tmp):
# unnecessary rebuilds.
different = True
try:
- with open(dst, 'r') as f1, open(dst_tmp, 'r') as f2:
+ with open(dst, 'rb') as f1, open(dst_tmp, 'rb') as f2:
if f1.read() == f2.read():
different = False
except FileNotFoundError:
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 8d5fb85..f261935 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -23,12 +23,9 @@ from . import mlog, coredata
from .mesonlib import MesonException
from .wrap import WrapMode, wraptool
-
-parser = argparse.ArgumentParser(prog='meson')
-
default_warning = '1'
-def add_builtin_argument(name, **kwargs):
+def add_builtin_argument(p, name, **kwargs):
k = kwargs.get('dest', name.replace('-', '_'))
c = coredata.get_builtin_option_choices(k)
b = True if kwargs.get('action', None) in ['store_true', 'store_false'] else False
@@ -42,31 +39,45 @@ def add_builtin_argument(name, **kwargs):
kwargs['default'] = default
else:
kwargs['default'] = argparse.SUPPRESS
- parser.add_argument('--' + name, help=h, **kwargs)
+ p.add_argument('--' + name, help=h, **kwargs)
-add_builtin_argument('prefix')
-add_builtin_argument('libdir')
-add_builtin_argument('libexecdir')
-add_builtin_argument('bindir')
-add_builtin_argument('sbindir')
-add_builtin_argument('includedir')
-add_builtin_argument('datadir')
-add_builtin_argument('mandir')
-add_builtin_argument('infodir')
-add_builtin_argument('localedir')
-add_builtin_argument('sysconfdir')
-add_builtin_argument('localstatedir')
-add_builtin_argument('sharedstatedir')
-add_builtin_argument('backend')
-add_builtin_argument('buildtype')
-add_builtin_argument('strip', action='store_true')
-add_builtin_argument('unity')
-add_builtin_argument('werror', action='store_true')
-add_builtin_argument('layout')
-add_builtin_argument('default-library')
-add_builtin_argument('warnlevel', dest='warning_level')
-add_builtin_argument('stdsplit', action='store_false')
-add_builtin_argument('errorlogs', action='store_false')
+def create_parser():
+ p = argparse.ArgumentParser(prog='meson')
+ add_builtin_argument(p, 'prefix')
+ add_builtin_argument(p, 'libdir')
+ add_builtin_argument(p, 'libexecdir')
+ add_builtin_argument(p, 'bindir')
+ add_builtin_argument(p, 'sbindir')
+ add_builtin_argument(p, 'includedir')
+ add_builtin_argument(p, 'datadir')
+ add_builtin_argument(p, 'mandir')
+ add_builtin_argument(p, 'infodir')
+ add_builtin_argument(p, 'localedir')
+ add_builtin_argument(p, 'sysconfdir')
+ add_builtin_argument(p, 'localstatedir')
+ add_builtin_argument(p, 'sharedstatedir')
+ add_builtin_argument(p, 'backend')
+ add_builtin_argument(p, 'buildtype')
+ add_builtin_argument(p, 'strip', action='store_true')
+ add_builtin_argument(p, 'unity')
+ add_builtin_argument(p, 'werror', action='store_true')
+ add_builtin_argument(p, 'layout')
+ add_builtin_argument(p, 'default-library')
+ add_builtin_argument(p, 'warnlevel', dest='warning_level')
+ add_builtin_argument(p, 'stdsplit', action='store_false')
+ add_builtin_argument(p, 'errorlogs', action='store_false')
+ p.add_argument('--cross-file', default=None,
+ help='File describing cross compilation environment.')
+ p.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
+ help='Set the value of an option, can be used several times to set multiple options.')
+ p.add_argument('-v', '--version', action='version',
+ version=coredata.version)
+ # See the mesonlib.WrapMode enum for documentation
+ p.add_argument('--wrap-mode', default=WrapMode.default,
+ type=wrapmodetype, choices=WrapMode,
+ help='Special wrap mode to use')
+ p.add_argument('directories', nargs='*')
+ return p
def wrapmodetype(string):
try:
@@ -76,18 +87,6 @@ def wrapmodetype(string):
msg = 'invalid argument {!r}, use one of {}'.format(string, msg)
raise argparse.ArgumentTypeError(msg)
-parser.add_argument('--cross-file', default=None,
- help='File describing cross compilation environment.')
-parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
- help='Set the value of an option, can be used several times to set multiple options.')
-parser.add_argument('-v', '--version', action='version',
- version=coredata.version)
-# See the mesonlib.WrapMode enum for documentation
-parser.add_argument('--wrap-mode', default=WrapMode.default,
- type=wrapmodetype, choices=WrapMode,
- help='Special wrap mode to use')
-parser.add_argument('directories', nargs='*')
-
class MesonApp:
def __init__(self, dir1, dir2, script_launcher, handshake, options, original_cmd_line_args):
@@ -155,7 +154,7 @@ class MesonApp:
def _generate(self, env):
mlog.debug('Build started at', datetime.datetime.now().isoformat())
- mlog.debug('Python binary:', sys.executable)
+ mlog.debug('Main binary:', sys.executable)
mlog.debug('Python system:', platform.system())
mlog.log(mlog.bold('The Meson build system'))
self.check_pkgconfig_envvar(env)
@@ -278,12 +277,13 @@ def run_script_command(args):
raise MesonException('Unknown internal command {}.'.format(cmdname))
return cmdfunc(cmdargs)
-def run(args, mainfile=None):
+def run(original_args, mainfile=None):
if sys.version_info < (3, 4):
print('Meson works correctly only with python 3.4+.')
print('You have python %s.' % sys.version)
print('Please update your environment')
return 1
+ args = original_args[:]
if len(args) > 0:
# First check if we want to run a subcommand.
cmd_name = args[0]
@@ -300,9 +300,19 @@ def run(args, mainfile=None):
elif cmd_name == 'rewrite':
return rewriter.run(remaining_args)
elif cmd_name == 'configure':
- return mconf.run(remaining_args)
+ try:
+ return mconf.run(remaining_args)
+ except MesonException as e:
+ mlog.log(mlog.red('\nError configuring project:'), e)
+ sys.exit(1)
elif cmd_name == 'wrap':
return wraptool.run(remaining_args)
+ elif cmd_name == 'runpython':
+ import runpy
+ script_file = remaining_args[0]
+ sys.argv[1:] = remaining_args[1:]
+ runpy.run_path(script_file, run_name='__main__')
+ sys.exit(0)
# No special command? Do the basic setup/reconf.
if len(args) >= 2 and args[0] == '--internal':
@@ -319,6 +329,8 @@ def run(args, mainfile=None):
else:
handshake = False
+ parser = create_parser()
+
args = mesonlib.expand_arguments(args)
options = parser.parse_args(args)
args = options.directories
@@ -342,7 +354,7 @@ def run(args, mainfile=None):
try:
if mainfile is None:
raise AssertionError('I iz broken. Sorry.')
- app = MesonApp(dir1, dir2, mainfile, handshake, options, sys.argv)
+ app = MesonApp(dir1, dir2, mainfile, handshake, options, original_args)
except Exception as e:
# Log directory does not exist, so just print
# to stdout.
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index 18c1e6a..a0d07ec 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -18,8 +18,10 @@ import sys, os, platform, io
information about Meson runs. Some output goes to screen,
some to logging dir and some goes to both."""
-colorize_console = platform.system().lower() != 'windows' and os.isatty(sys.stdout.fileno()) and \
- os.environ.get('TERM') != 'dumb'
+if platform.system().lower() == 'windows':
+ colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('ANSICON')
+else:
+ colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('TERM') != 'dumb'
log_dir = None
log_file = None
log_fname = 'meson-log.txt'
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index c80a1f1..56765a5 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -220,9 +220,10 @@ class GnomeModule(ExtensionModule):
input_file,
'--generate-dependencies']
+ # Prefer generated files over source files
+ cmd += ['--sourcedir', state.subdir] # Current build dir
for source_dir in source_dirs:
cmd += ['--sourcedir', os.path.join(state.subdir, source_dir)]
- cmd += ['--sourcedir', state.subdir] # Current dir
pc, stdout, stderr = Popen_safe(cmd, cwd=state.environment.get_source_dir())
if pc.returncode != 0:
@@ -240,25 +241,20 @@ class GnomeModule(ExtensionModule):
#
# If there are multiple generated resource files with the same basename
# then this code will get confused.
-
def exists_in_srcdir(f):
return os.path.exists(os.path.join(state.environment.get_source_dir(), f))
- missing_dep_files = [f for f in dep_files if not exists_in_srcdir(f)]
depends = []
subdirs = []
- for missing in missing_dep_files:
- found = False
- missing_basename = os.path.basename(missing)
-
+ for resfile in dep_files[:]:
+ resbasename = os.path.basename(resfile)
for dep in dependencies:
if hasattr(dep, 'held_object'):
dep = dep.held_object
if isinstance(dep, mesonlib.File):
- if dep.fname != missing_basename:
+ if dep.fname != resbasename:
continue
- found = True
- dep_files.remove(missing)
+ dep_files.remove(resfile)
dep_files.append(dep)
subdirs.append(dep.subdir)
break
@@ -266,12 +262,11 @@ class GnomeModule(ExtensionModule):
fname = None
outputs = {(o, os.path.basename(o)) for o in dep.get_outputs()}
for o, baseo in outputs:
- if baseo == missing_basename:
+ if baseo == resbasename:
fname = o
break
if fname is not None:
- found = True
- dep_files.remove(missing)
+ dep_files.remove(resfile)
dep_files.append(
mesonlib.File(
is_built=True,
@@ -280,16 +275,13 @@ class GnomeModule(ExtensionModule):
depends.append(dep)
subdirs.append(dep.get_subdir())
break
- else:
- raise RuntimeError('Unreachable code.')
-
- if not found:
- raise MesonException(
- 'Resource "%s" listed in "%s" was not found. If this is a '
- 'generated file, pass the target that generates it to '
- 'gnome.compile_resources() using the "dependencies" '
- 'keyword argument.' % (missing, input_file))
-
+ else:
+ if not exists_in_srcdir(resfile):
+ raise MesonException(
+ 'Resource "%s" listed in "%s" was not found. If this is a '
+ 'generated file, pass the target that generates it to '
+ 'gnome.compile_resources() using the "dependencies" '
+ 'keyword argument.' % (resfile, input_file))
return dep_files, depends, subdirs
def _get_link_args(self, state, lib, depends=None, include_rpath=False,
@@ -723,7 +715,8 @@ This will become a hard error in the future.''')
@permittedKwargs({'main_xml', 'main_sgml', 'src_dir', 'dependencies', 'install',
'install_dir', 'scan_args', 'scanobjs_args', 'gobject_typesfile',
'fixxref_args', 'html_args', 'html_assets', 'content_files',
- 'mkdb_args', 'ignore_headers', 'include_directories'})
+ 'mkdb_args', 'ignore_headers', 'include_directories',
+ 'namespace', 'mode', 'expand_content_files'})
def gtkdoc(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Gtkdoc must have one positional argument.')
diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py
index 4fae88b..989e839 100644
--- a/mesonbuild/modules/python3.py
+++ b/mesonbuild/modules/python3.py
@@ -52,7 +52,7 @@ class Python3Module(ExtensionModule):
@noKwargs
def find_python(self, state, args, kwargs):
- py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True)
+ py3 = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True)
return ModuleReturnValue(py3, [py3])
@noKwargs
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 285169b..54e2c73 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -43,13 +43,17 @@ class QtBaseModule:
kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true', 'method': method}
qt = _QT_DEPS_LUT[self.qt_version](env, kwargs)
# Get all tools and then make sure that they are the right version
- self.moc, self.uic, self.rcc = qt.compilers_detect()
+ self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect()
# Moc, uic and rcc write their version strings to stderr.
# Moc and rcc return a non-zero result when doing so.
# What kind of an idiot thought that was a good idea?
- for compiler, compiler_name in ((self.moc, "Moc"), (self.uic, "Uic"), (self.rcc, "Rcc")):
+ for compiler, compiler_name in ((self.moc, "Moc"), (self.uic, "Uic"), (self.rcc, "Rcc"), (self.lrelease, "lrelease")):
if compiler.found():
- stdout, stderr = Popen_safe(compiler.get_command() + ['-v'])[1:3]
+ # Workaround since there is no easy way to know which tool/version support which flag
+ for flag in ['-v', '-version']:
+ p, stdout, stderr = Popen_safe(compiler.get_command() + [flag])[0:3]
+ if p.returncode == 0:
+ break
stdout = stdout.strip()
stderr = stderr.strip()
if 'Qt {}'.format(self.qt_version) in stderr:
@@ -62,7 +66,7 @@ class QtBaseModule:
raise MesonException('{name} preprocessor is not for Qt {version}. Output:\n{stdo}\n{stderr}'.format(
name=compiler_name, version=self.qt_version, stdo=stdout, stderr=stderr))
mlog.log(' {}:'.format(compiler_name.lower()), mlog.green('YES'), '({path}, {version})'.format(
- path=self.moc.get_path(), version=compiler_ver.split()[-1]))
+ path=compiler.get_path(), version=compiler_ver.split()[-1]))
else:
mlog.log(' {}:'.format(compiler_name.lower()), mlog.red('NO'))
self.tools_detected = True
@@ -137,10 +141,28 @@ class QtBaseModule:
moc_output = moc_gen.process_files('Qt{} moc header'.format(self.qt_version), moc_headers, state)
sources.append(moc_output)
if len(moc_sources) > 0:
- arguments = moc_extra_arguments + ['@INPUT@', '-o', '@OUTPUT@']
+ arguments = moc_extra_arguments + inc + ['@INPUT@', '-o', '@OUTPUT@']
moc_kwargs = {'output': '@BASENAME@.moc',
'arguments': arguments}
moc_gen = build.Generator([self.moc], moc_kwargs)
moc_output = moc_gen.process_files('Qt{} moc source'.format(self.qt_version), moc_sources, state)
sources.append(moc_output)
return ModuleReturnValue(sources, sources)
+
+ @permittedKwargs({'ts_files', 'install', 'install_dir', 'build_by_default', 'method'})
+ def compile_translations(self, state, args, kwargs):
+ ts_files, install_dir = extract_as_list(kwargs, 'ts_files', 'install_dir', pop=True)
+ self._detect_tools(state.environment, kwargs.get('method', 'auto'))
+ translations = []
+ for ts in ts_files:
+ cmd = [self.lrelease, '@INPUT@', '-qm', '@OUTPUT@']
+ lrelease_kwargs = {'output': '@BASENAME@.qm',
+ 'input': ts,
+ 'install': kwargs.get('install', False),
+ 'build_by_default': kwargs.get('build_by_default', False),
+ 'command': cmd}
+ if install_dir is not None:
+ lrelease_kwargs['install_dir'] = install_dir
+ lrelease_target = build.CustomTarget('qt{}-compile-{}'.format(self.qt_version, ts), state.subdir, state.subproject, lrelease_kwargs)
+ translations.append(lrelease_target)
+ return ModuleReturnValue(translations, translations)
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 8400a1a..0465d24 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -111,6 +111,7 @@ class Lexer:
par_count = 0
bracket_count = 0
col = 0
+ newline_rx = re.compile(r'(?<!\\)((?:\\\\)*)\\n')
while loc < len(self.code):
matched = False
value = None
@@ -139,10 +140,9 @@ class Lexer:
elif tid == 'dblquote':
raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
elif tid == 'string':
- value = match_text[1:-1]\
- .replace(r"\'", "'")\
- .replace(r" \\ ".strip(), r" \ ".strip())\
- .replace("\\n", "\n")
+ value = match_text[1:-1].replace(r"\'", "'")
+ value = newline_rx.sub(r'\1\n', value)
+ value = value.replace(r" \\ ".strip(), r" \ ".strip())
elif tid == 'multiline_string':
tid = 'string'
value = match_text[3:-3]
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 30322aa..b39f5af 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -482,35 +482,41 @@ TIMEOUT: %4d
numlen = len('%d' % len(tests))
self.open_log_files()
wrap = self.get_wrapper()
+ startdir = os.getcwd()
+ if self.options.wd:
+ os.chdir(self.options.wd)
- for _ in range(self.options.repeat):
- for i, test in enumerate(tests):
- visible_name = self.get_pretty_suite(test)
-
- if self.options.gdb:
- test.timeout = None
-
- if not test.is_parallel or self.options.gdb:
- self.drain_futures(futures)
- futures = []
- res = self.run_single_test(wrap, test)
- self.print_stats(numlen, tests, visible_name, res, i)
- else:
- if not executor:
- executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes)
- f = executor.submit(self.run_single_test, wrap, test)
- futures.append((f, numlen, tests, visible_name, i))
+ try:
+ for _ in range(self.options.repeat):
+ for i, test in enumerate(tests):
+ visible_name = self.get_pretty_suite(test)
+
+ if self.options.gdb:
+ test.timeout = None
+
+ if not test.is_parallel or self.options.gdb:
+ self.drain_futures(futures)
+ futures = []
+ res = self.run_single_test(wrap, test)
+ self.print_stats(numlen, tests, visible_name, res, i)
+ else:
+ if not executor:
+ executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes)
+ f = executor.submit(self.run_single_test, wrap, test)
+ futures.append((f, numlen, tests, visible_name, i))
+ if self.options.repeat > 1 and self.fail_count:
+ break
if self.options.repeat > 1 and self.fail_count:
break
- if self.options.repeat > 1 and self.fail_count:
- break
- self.drain_futures(futures)
- self.print_summary()
- self.print_collected_logs()
+ self.drain_futures(futures)
+ self.print_summary()
+ self.print_collected_logs()
- if self.logfilename:
- print('Full log written to %s' % self.logfilename)
+ if self.logfilename:
+ print('Full log written to %s' % self.logfilename)
+ finally:
+ os.chdir(startdir)
def drain_futures(self, futures):
for i in futures:
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 01dd036..3cca239 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -84,9 +84,27 @@ def ComboParser(name, description, kwargs):
raise OptionException('Combo choice elements must be strings.')
return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0]))
+@permitted_kwargs({'value', 'choices'})
+def string_array_parser(name, description, kwargs):
+ if 'choices' in kwargs:
+ choices = kwargs['choices']
+ if not isinstance(choices, list):
+ raise OptionException('Array choices must be an array.')
+ for i in choices:
+ if not isinstance(i, str):
+ raise OptionException('Array choice elements must be strings.')
+ value = kwargs.get('value', choices)
+ else:
+ choices = None
+ value = kwargs.get('value', [])
+ if not isinstance(value, list):
+ raise OptionException('Array choices must be passed as an array.')
+ return coredata.UserStringArrayOption(name, description, value, choices=choices)
+
option_types = {'string': StringParser,
'boolean': BooleanParser,
'combo': ComboParser,
+ 'array': string_array_parser,
}
class OptionInterpreter:
diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py
index 53c5428..a9b00c7 100644
--- a/mesonbuild/scripts/regen_checker.py
+++ b/mesonbuild/scripts/regen_checker.py
@@ -14,6 +14,7 @@
import sys, os
import pickle, subprocess
+from mesonbuild.mesonlib import meson_command
# This could also be used for XCode.
@@ -32,15 +33,11 @@ def need_regen(regeninfo, regen_timestamp):
return False
def regen(regeninfo, mesonscript, backend):
- if sys.executable.lower().endswith('meson.exe'):
- cmd_exe = [sys.executable]
- else:
- cmd_exe = [sys.executable, mesonscript]
- cmd = cmd_exe + ['--internal',
- 'regenerate',
- regeninfo.build_dir,
- regeninfo.source_dir,
- '--backend=' + backend]
+ cmd = meson_command + ['--internal',
+ 'regenerate',
+ regeninfo.build_dir,
+ regeninfo.source_dir,
+ '--backend=' + backend]
subprocess.check_call(cmd)
def run(args):
diff --git a/msi/createmsi.py b/msi/createmsi.py
index 7f165d8..3ea0958 100755
--- a/msi/createmsi.py
+++ b/msi/createmsi.py
@@ -82,9 +82,7 @@ class PackageGenerator:
modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))]
modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')]
modulestr = ','.join(modules)
- python = 'c:\\Python\python.exe'
- if sys.executable:
- python = sys.executable
+ python = shutil.which('python')
cxfreeze = os.path.join(os.path.dirname(python), "Scripts", "cxfreeze")
if not os.path.isfile(cxfreeze):
print("ERROR: This script requires cx_freeze module")
diff --git a/run_project_tests.py b/run_project_tests.py
index 17e095b..b94a976 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -33,7 +33,7 @@ import time
import multiprocessing
import concurrent.futures as conc
import re
-from run_unittests import get_fake_options, run_configure_inprocess
+from run_unittests import get_fake_options, run_configure
from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
from run_tests import ensure_backend_detects_changes
@@ -322,14 +322,31 @@ def run_test(skipped, testdir, extra_args, compiler, backend, flags, commands, s
finally:
mlog.shutdown() # Close the log file because otherwise Windows wets itself.
+def pass_prefix_to_test(dirname):
+ if '40 prefix' in dirname:
+ return False
+ return True
+
+def pass_libdir_to_test(dirname):
+ if '8 install' in dirname:
+ return False
+ if '39 libdir' in dirname:
+ return False
+ return True
+
def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backend, flags, commands, should_fail):
compile_commands, clean_commands, install_commands, uninstall_commands = commands
test_args = parse_test_args(testdir)
gen_start = time.time()
# Configure in-process
- gen_command = [meson_command, '--prefix', '/usr', '--libdir', 'lib', testdir, test_build_dir]\
- + flags + test_args + extra_args
- (returncode, stdo, stde) = run_configure_inprocess(gen_command)
+ if pass_prefix_to_test(testdir):
+ gen_args = ['--prefix', '/usr']
+ else:
+ gen_args = []
+ if pass_libdir_to_test(testdir):
+ gen_args += ['--libdir', 'lib']
+ gen_args += [testdir, test_build_dir] + flags + test_args + extra_args
+ (returncode, stdo, stde) = run_configure(meson_command, gen_args)
try:
logfile = os.path.join(test_build_dir, 'meson-logs/meson-log.txt')
with open(logfile, errors='ignore') as f:
@@ -603,7 +620,7 @@ def check_meson_commands_work():
testdir = 'test cases/common/1 trivial'
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
print('Checking that configuring works...')
- gen_cmd = [sys.executable, meson_command, testdir, build_dir] + backend_flags
+ gen_cmd = mesonlib.meson_command + [testdir, build_dir] + backend_flags
pc, o, e = Popen_safe(gen_cmd)
if pc.returncode != 0:
raise RuntimeError('Failed to configure {!r}:\n{}\n{}'.format(testdir, e, o))
diff --git a/run_tests.py b/run_tests.py
index 79c9639..1cc3983 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -31,6 +31,12 @@ from glob import glob
Backend = Enum('Backend', 'ninja vs xcode')
+if 'MESON_EXE' in os.environ:
+ import shlex
+ meson_exe = shlex.split(os.environ['MESON_EXE'])
+else:
+ meson_exe = None
+
if mesonlib.is_windows() or mesonlib.is_cygwin():
exe_suffix = '.exe'
else:
@@ -127,26 +133,27 @@ def get_fake_options(prefix):
def should_run_linux_cross_tests():
return shutil.which('arm-linux-gnueabihf-gcc-7') and not platform.machine().lower().startswith('arm')
-def run_configure_inprocess(commandlist):
+def run_configure_inprocess(meson_command, commandlist):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
old_stderr = sys.stderr
sys.stderr = mystderr = StringIO()
try:
- returncode = mesonmain.run(commandlist[1:], commandlist[0])
+ returncode = mesonmain.run(commandlist, meson_command)
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
return returncode, mystdout.getvalue(), mystderr.getvalue()
-class FakeEnvironment(object):
- def __init__(self):
- self.cross_info = None
- self.coredata = lambda: None
- self.coredata.compilers = {}
+def run_configure_external(full_command):
+ pc, o, e = mesonlib.Popen_safe(full_command)
+ return pc.returncode, o, e
- def is_cross_build(self):
- return False
+def run_configure(meson_command, commandlist):
+ global meson_exe
+ if meson_exe:
+ return run_configure_external(meson_exe + commandlist)
+ return run_configure_inprocess(meson_command, commandlist)
def print_system_info():
print(mlog.bold('System information.').get_text(mlog.colorize_console))
@@ -207,11 +214,12 @@ if __name__ == '__main__':
'coverage.process_startup()\n')
env['COVERAGE_PROCESS_START'] = '.coveragerc'
env['PYTHONPATH'] = os.pathsep.join([td] + env.get('PYTHONPATH', []))
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'], env=env)
+ returncode += subprocess.call(mesonlib.python_command + ['run_unittests.py', '-v'], env=env)
# Ubuntu packages do not have a binary without -6 suffix.
if should_run_linux_cross_tests():
print(mlog.bold('Running cross compilation tests.').get_text(mlog.colorize_console))
print()
- returncode += subprocess.call([sys.executable, 'run_cross_test.py', 'cross/ubuntu-armhf.txt'], env=env)
- returncode += subprocess.call([sys.executable, 'run_project_tests.py'] + sys.argv[1:], env=env)
+ returncode += subprocess.call(mesonlib.python_command + ['run_cross_test.py', 'cross/ubuntu-armhf.txt'],
+ env=env)
+ returncode += subprocess.call(mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:], env=env)
sys.exit(returncode)
diff --git a/run_unittests.py b/run_unittests.py
index cb8d71d..0002f6c 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1,4 +1,3 @@
-
#!/usr/bin/env python3
# Copyright 2016-2017 The Meson development team
@@ -19,10 +18,12 @@ import shlex
import subprocess
import re, json
import tempfile
+import textwrap
import os
import shutil
import sys
import unittest
+from unittest import mock
from configparser import ConfigParser
from glob import glob
from pathlib import PurePath
@@ -34,13 +35,14 @@ import mesonbuild.mesonlib
import mesonbuild.coredata
from mesonbuild.interpreter import ObjectHolder
from mesonbuild.mesonlib import is_linux, is_windows, is_osx, is_cygwin, windows_proof_rmtree
+from mesonbuild.mesonlib import python_command, meson_command
from mesonbuild.environment import Environment
from mesonbuild.dependencies import DependencyException
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
-from run_tests import exe_suffix, get_fake_options, FakeEnvironment
+from run_tests import exe_suffix, get_fake_options
from run_tests import get_builddir_target_args, get_backend_commands, Backend
-from run_tests import ensure_backend_detects_changes, run_configure_inprocess
+from run_tests import ensure_backend_detects_changes, run_configure, meson_exe
from run_tests import should_run_linux_cross_tests
@@ -56,7 +58,7 @@ def get_dynamic_section_entry(fname, entry):
m = pattern.search(line)
if m is not None:
return m.group(1)
- raise RuntimeError('Could not determine {}:\n\n'.format(entry) + raw_out)
+ return None # The file did not contain the specified entry.
def get_soname(fname):
return get_dynamic_section_entry(fname, 'soname')
@@ -460,11 +462,12 @@ class BasePlatformTests(unittest.TestCase):
# Get the backend
# FIXME: Extract this from argv?
self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
- self.meson_args = [os.path.join(src_root, 'meson.py'), '--backend=' + self.backend.name]
- self.meson_command = [sys.executable] + self.meson_args
- self.mconf_command = [sys.executable, os.path.join(src_root, 'meson.py'), 'configure']
- self.mintro_command = [sys.executable, os.path.join(src_root, 'meson.py'), 'introspect']
- self.mtest_command = [sys.executable, os.path.join(src_root, 'meson.py'), 'test', '-C', self.builddir]
+ self.meson_mainfile = os.path.join(src_root, 'meson.py')
+ self.meson_args = ['--backend=' + self.backend.name]
+ self.meson_command = meson_command + self.meson_args
+ self.mconf_command = meson_command + ['configure']
+ self.mintro_command = meson_command + ['introspect']
+ self.mtest_command = meson_command + ['test', '-C', self.builddir]
# Backend-specific build commands
self.build_command, self.clean_command, self.test_command, self.install_command, \
self.uninstall_command = get_backend_commands(self.backend)
@@ -527,7 +530,7 @@ class BasePlatformTests(unittest.TestCase):
self.privatedir = os.path.join(self.builddir, 'meson-private')
if inprocess:
try:
- out = run_configure_inprocess(self.meson_args + args + extra_args)[1]
+ out = run_configure(self.meson_mainfile, self.meson_args + args + extra_args)[1]
except:
self._print_meson_log()
raise
@@ -1058,16 +1061,17 @@ class AllPlatformTests(BasePlatformTests):
evalue = os.environ.pop(evar)
# Very rough/strict heuristics. Would never work for actual
# compiler detection, but should be ok for the tests.
- if os.path.basename(evalue).startswith('g'):
+ ebase = os.path.basename(evalue)
+ if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
self.assertIsInstance(ecc, gnu)
self.assertIsInstance(elinker, ar)
- elif 'clang' in os.path.basename(evalue):
+ elif 'clang' in ebase:
self.assertIsInstance(ecc, clang)
self.assertIsInstance(elinker, ar)
- elif os.path.basename(evalue).startswith('ic'):
+ elif ebase.startswith('ic'):
self.assertIsInstance(ecc, intel)
self.assertIsInstance(elinker, ar)
- elif os.path.basename(evalue).startswith('cl'):
+ elif ebase.startswith('cl'):
self.assertIsInstance(ecc, msvc)
self.assertIsInstance(elinker, lib)
else:
@@ -1121,14 +1125,14 @@ class AllPlatformTests(BasePlatformTests):
# exelist + some argument. This is meant to test that setting
# something like `ccache gcc -pipe` or `distcc ccache gcc` works.
wrapper = os.path.join(testdir, 'compiler wrapper.py')
- wrappercc = [sys.executable, wrapper] + cc.get_exelist() + ['-DSOME_ARG']
+ wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
wrappercc_s = ''
for w in wrappercc:
wrappercc_s += shlex.quote(w) + ' '
os.environ[evar] = wrappercc_s
wcc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
# Check static linker too
- wrapperlinker = [sys.executable, wrapper] + linker.get_exelist() + linker.get_always_args()
+ wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
wrapperlinker_s = ''
for w in wrapperlinker:
wrapperlinker_s += shlex.quote(w) + ' '
@@ -1357,12 +1361,15 @@ int main(int argc, char **argv) {
testdir = os.path.join(self.common_test_dir, '46 library chain')
self.init(testdir)
self.build()
- for each in ('prog', 'subdir/liblib1.so', 'subdir/subdir2/liblib2.so',
- 'subdir/subdir3/liblib3.so'):
+ for each in ('prog', 'subdir/liblib1.so', ):
rpath = get_rpath(os.path.join(self.builddir, each))
self.assertTrue(rpath)
for path in rpath.split(':'):
self.assertTrue(path.startswith('$ORIGIN'), msg=(each, path))
+ # These two don't link to anything else, so they do not need an rpath entry.
+ for each in ('subdir/subdir2/liblib2.so', 'subdir/subdir3/liblib3.so'):
+ rpath = get_rpath(os.path.join(self.builddir, each))
+ self.assertTrue(rpath is None)
def test_dash_d_dedup(self):
testdir = os.path.join(self.unit_test_dir, '10 d dedup')
@@ -1392,6 +1399,7 @@ int main(int argc, char **argv) {
env = Environment('', self.builddir, self.meson_command,
get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
+ stlinker = env.detect_static_linker(cc)
if mesonbuild.mesonlib.is_windows():
object_suffix = 'obj'
shared_suffix = 'dll'
@@ -1404,7 +1412,7 @@ int main(int argc, char **argv) {
else:
object_suffix = 'o'
shared_suffix = 'so'
- return (cc, object_suffix, shared_suffix)
+ return (cc, stlinker, object_suffix, shared_suffix)
def pbcompile(self, compiler, source, objectfile, extra_args=[]):
cmd = compiler.get_exelist()
@@ -1416,7 +1424,7 @@ int main(int argc, char **argv) {
def test_prebuilt_object(self):
- (compiler, object_suffix, _) = self.detect_prebuild_env()
+ (compiler, _, object_suffix, _) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '14 prebuilt object')
source = os.path.join(tdir, 'source.c')
objectfile = os.path.join(tdir, 'prebuilt.' + object_suffix)
@@ -1428,13 +1436,18 @@ int main(int argc, char **argv) {
finally:
os.unlink(objectfile)
- def build_static_lib(self, compiler, source, objectfile, outfile, extra_args=None):
+ def build_static_lib(self, compiler, linker, source, objectfile, outfile, extra_args=None):
if extra_args is None:
extra_args = []
if compiler.id == 'msvc':
link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
else:
link_cmd = ['ar', 'csr', outfile, objectfile]
+ link_cmd = linker.get_exelist()
+ link_cmd += linker.get_always_args()
+ link_cmd += linker.get_std_link_args()
+ link_cmd += linker.get_output_args(outfile)
+ link_cmd += [objectfile]
self.pbcompile(compiler, source, objectfile, extra_args=extra_args)
try:
subprocess.check_call(link_cmd)
@@ -1442,12 +1455,12 @@ int main(int argc, char **argv) {
os.unlink(objectfile)
def test_prebuilt_static_lib(self):
- (cc, object_suffix, _) = self.detect_prebuild_env()
+ (cc, stlinker, object_suffix, _) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '15 prebuilt static')
source = os.path.join(tdir, 'libdir/best.c')
objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix)
stlibfile = os.path.join(tdir, 'libdir/libbest.a')
- self.build_static_lib(cc, source, objectfile, stlibfile)
+ self.build_static_lib(cc, stlinker, source, objectfile, stlibfile)
# Run the test
try:
self.init(tdir)
@@ -1474,7 +1487,7 @@ int main(int argc, char **argv) {
os.unlink(objectfile)
def test_prebuilt_shared_lib(self):
- (cc, object_suffix, shared_suffix) = self.detect_prebuild_env()
+ (cc, _, object_suffix, shared_suffix) = self.detect_prebuild_env()
tdir = os.path.join(self.unit_test_dir, '16 prebuilt shared')
source = os.path.join(tdir, 'alexandria.c')
objectfile = os.path.join(tdir, 'alexandria.' + object_suffix)
@@ -1508,7 +1521,7 @@ int main(int argc, char **argv) {
'''
if not shutil.which('pkg-config'):
raise unittest.SkipTest('pkg-config not found')
- (cc, objext, shext) = self.detect_prebuild_env()
+ (cc, stlinker, objext, shext) = self.detect_prebuild_env()
testdir = os.path.join(self.unit_test_dir, '17 pkgconfig static')
source = os.path.join(testdir, 'foo.c')
objectfile = os.path.join(testdir, 'foo.' + objext)
@@ -1521,7 +1534,7 @@ int main(int argc, char **argv) {
else:
shlibfile = os.path.join(testdir, 'libfoo.' + shext)
# Build libs
- self.build_static_lib(cc, source, objectfile, stlibfile, extra_args=['-DFOO_STATIC'])
+ self.build_static_lib(cc, stlinker, source, objectfile, stlibfile, extra_args=['-DFOO_STATIC'])
self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
# Run test
os.environ['PKG_CONFIG_LIBDIR'] = self.builddir
@@ -1549,7 +1562,8 @@ int main(int argc, char **argv) {
'--libdir=' + libdir])
# Find foo dependency
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
- env = FakeEnvironment()
+ env = Environment(testdir, self.builddir, self.meson_command,
+ get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
# Ensure link_args are properly quoted
@@ -1561,6 +1575,72 @@ int main(int argc, char **argv) {
cargs = ['-I' + incdir.as_posix()]
self.assertEqual(foo_dep.get_compile_args(), cargs)
+ def test_array_option_change(self):
+ def get_opt():
+ opts = self.introspect('--buildoptions')
+ for x in opts:
+ if x.get('name') == 'list':
+ return x
+ raise Exception(opts)
+
+ expected = {
+ 'name': 'list',
+ 'description': 'list',
+ 'type': 'stringarray',
+ 'value': ['foo', 'bar'],
+ }
+ tdir = os.path.join(self.unit_test_dir, '18 array option')
+ self.init(tdir)
+ original = get_opt()
+ self.assertDictEqual(original, expected)
+
+ expected['value'] = ['oink', 'boink']
+ self.setconf('-Dlist=oink,boink')
+ changed = get_opt()
+ self.assertEqual(changed, expected)
+
+ def test_array_option_bad_change(self):
+ def get_opt():
+ opts = self.introspect('--buildoptions')
+ for x in opts:
+ if x.get('name') == 'list':
+ return x
+ raise Exception(opts)
+
+ expected = {
+ 'name': 'list',
+ 'description': 'list',
+ 'type': 'stringarray',
+ 'value': ['foo', 'bar'],
+ }
+ tdir = os.path.join(self.unit_test_dir, '18 array option')
+ self.init(tdir)
+ original = get_opt()
+ self.assertDictEqual(original, expected)
+ with self.assertRaises(subprocess.CalledProcessError):
+ self.setconf('-Dlist=bad')
+ changed = get_opt()
+ self.assertDictEqual(changed, expected)
+
+ def opt_has(self, name, value):
+ res = self.introspect('--buildoptions')
+ found = False
+ for i in res:
+ if i['name'] == name:
+ self.assertEqual(i['value'], value)
+ found = True
+ break
+ self.assertTrue(found, "Array option not found in introspect data.")
+
+ def test_free_stringarray_setting(self):
+ testdir = os.path.join(self.common_test_dir, '47 options')
+ self.init(testdir)
+ self.opt_has('free_array_opt', [])
+ self.setconf('-Dfree_array_opt=foo,bar', will_build=False)
+ self.opt_has('free_array_opt', ['foo', 'bar'])
+ self.setconf("-Dfree_array_opt=['a,b', 'c,d']", will_build=False)
+ self.opt_has('free_array_opt', ['a,b', 'c,d'])
+
class FailureTests(BasePlatformTests):
'''
@@ -1586,6 +1666,9 @@ class FailureTests(BasePlatformTests):
Assert that running meson configure on the specified @contents raises
a error message matching regex @match.
'''
+ if meson_exe is not None:
+ # Because the exception happens in a different process.
+ raise unittest.SkipTest('Can not test assert raise tests with an external Meson command.')
if langs is None:
langs = []
with open(self.mbuild, 'w') as f:
@@ -1656,7 +1739,7 @@ class FailureTests(BasePlatformTests):
raise unittest.SkipTest('wx-config or wx-config-3.0 found')
self.assertMesonRaises("dependency('wxwidgets')", self.dnf)
self.assertMesonOutputs("dependency('wxwidgets', required : false)",
- "nor wx-config found")
+ "No wx-config found;")
def test_wx_dependency(self):
if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'):
@@ -1717,12 +1800,12 @@ class WindowsTests(BasePlatformTests):
os.environ['PATH'] += os.pathsep + testdir
prog = ExternalProgram('test-script-ext')
self.assertTrue(prog.found(), msg='test-script-ext not found in PATH')
- self.assertPathEqual(prog.get_command()[0], sys.executable)
+ self.assertPathEqual(prog.get_command()[0], python_command[0])
self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
# Finding a script in PATH with extension works and adds the interpreter
prog = ExternalProgram('test-script-ext.py')
self.assertTrue(prog.found(), msg='test-script-ext.py not found in PATH')
- self.assertPathEqual(prog.get_command()[0], sys.executable)
+ self.assertPathEqual(prog.get_command()[0], python_command[0])
self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
def test_ignore_libs(self):
@@ -1800,7 +1883,8 @@ class LinuxlikeTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
self.init(testdir)
- env = FakeEnvironment()
+ env = Environment(testdir, self.builddir, self.meson_command,
+ get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True}
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
@@ -1953,7 +2037,7 @@ class LinuxlikeTests(BasePlatformTests):
# Verify that -I flags from the `args` kwarg are first
# This is set in the '43 has function' test case
self.assertEqual(cmd[1], '-I/tmp')
- # Verify that -O3 set via the environment is overriden by -O0
+ # Verify that -O3 set via the environment is overridden by -O0
Oargs = [arg for arg in cmd if arg.startswith('-O')]
self.assertEqual(Oargs, [Oflag, '-O0'])
@@ -2184,8 +2268,9 @@ class LinuxlikeTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found')
if not shutil.which('genhtml'):
raise unittest.SkipTest('genhtml not found')
- if 'clang' in os.environ.get('CC', '') and os.environ.get('TRAVIS_OS_NAME', '') == 'linux':
- raise unittest.SkipTest('Gcovr has a bug and does not work with Clang in the CI environment.')
+ if 'clang' in os.environ.get('CC', ''):
+ # We need to use llvm-cov instead of gcovr with clang
+ raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!')
testdir = os.path.join(self.common_test_dir, '1 trivial')
self.init(testdir, ['-Db_coverage=true'])
self.build()
@@ -2218,6 +2303,50 @@ endian = 'little'
self.init(testdir, ['-Db_lto=true'], default_args=False)
self.build('reconfigure')
+ def test_cross_file_system_paths(self):
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ cross_content = textwrap.dedent("""\
+ [binaries]
+ c = '/usr/bin/cc'
+ ar = '/usr/bin/ar'
+ strip = '/usr/bin/ar'
+
+ [properties]
+
+ [host_machine]
+ system = 'linux'
+ cpu_family = 'x86'
+ cpu = 'i686'
+ endian = 'little'
+ """)
+
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
+ self.init(testdir, ['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
+ os.environ.pop('XDG_DATA_HOME', None)
+ self.init(testdir, ['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
+ self.init(testdir, ['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
class LinuxArmCrossCompileTests(BasePlatformTests):
'''
@@ -2246,7 +2375,7 @@ class RewriterTests(unittest.TestCase):
super().setUp()
src_root = os.path.dirname(__file__)
self.testroot = os.path.realpath(tempfile.mkdtemp())
- self.rewrite_command = [sys.executable, os.path.join(src_root, 'mesonrewriter.py')]
+ self.rewrite_command = python_command + [os.path.join(src_root, 'mesonrewriter.py')]
self.tmpdir = os.path.realpath(tempfile.mkdtemp())
self.workdir = os.path.join(self.tmpdir, 'foo')
self.test_dir = os.path.join(src_root, 'test cases/rewrite')
diff --git a/test cases/common/125 shared module/meson.build b/test cases/common/125 shared module/meson.build
index d96d8fc..29277e9 100644
--- a/test cases/common/125 shared module/meson.build
+++ b/test cases/common/125 shared module/meson.build
@@ -6,7 +6,7 @@ l = shared_library('runtime', 'runtime.c')
# is a common approach for plugins that are only used
# with dlopen. Any symbols are resolved dynamically
# at runtime. This requires extra help on Windows, so
-# should be avoided unless really neccessary.
+# should be avoided unless really necessary.
m = shared_module('mymodule', 'module.c')
e = executable('prog', 'prog.c', link_with : l, dependencies : dl)
test('import test', e, args : m)
diff --git a/test cases/common/126 llvm ir and assembly/meson.build b/test cases/common/126 llvm ir and assembly/meson.build
index 4ce4636..acff93f 100644
--- a/test cases/common/126 llvm ir and assembly/meson.build
+++ b/test cases/common/126 llvm ir and assembly/meson.build
@@ -6,7 +6,7 @@ supported_cpus = ['arm', 'x86', 'x86_64']
foreach lang : ['c', 'cpp']
cc = meson.get_compiler(lang)
cc_id = cc.get_id()
- ## Build a trivial executale with mixed LLVM IR source
+ ## Build a trivial executable with mixed LLVM IR source
if cc_id == 'clang'
e = executable('square_ir_' + lang, 'square.ll', 'main.' + lang)
test('test IR square' + lang, e)
diff --git a/test cases/common/16 configure file/config5.h.in b/test cases/common/16 configure file/config5.h.in
new file mode 100644
index 0000000..323bec6
--- /dev/null
+++ b/test cases/common/16 configure file/config5.h.in
@@ -0,0 +1 @@
+#define MESSAGE "@var@"
diff --git a/test cases/common/16 configure file/config6.h.in b/test cases/common/16 configure file/config6.h.in
new file mode 100644
index 0000000..9719f87
--- /dev/null
+++ b/test cases/common/16 configure file/config6.h.in
@@ -0,0 +1,19 @@
+/* No escape */
+#define MESSAGE1 "@var1@"
+
+/* Single escape means no replace */
+#define MESSAGE2 "\@var1@"
+
+/* Replace pairs of escapes before '@' or '\@' with escape characters
+ * (note we have to double number of pairs due to C string escaping)
+ */
+#define MESSAGE3 "\\\\@var1@"
+
+/* Pairs of escapes and then single escape to avoid replace */
+#define MESSAGE4 "\\\\\@var1@"
+
+/* Check escaped variable does not overlap following variable */
+#define MESSAGE5 "\@var1@var2@"
+
+/* Check escape character outside variables */
+#define MESSAGE6 "\\ @ \@ \\\\@ \\\\\@"
diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build
index 1e5a819..eda0a8f 100644
--- a/test cases/common/16 configure file/meson.build
+++ b/test cases/common/16 configure file/meson.build
@@ -109,3 +109,25 @@ configs = [
foreach c : configs
test('@0@'.format(c), file_contains_py, args: [ c, test_string ])
endforeach
+
+# Test variable is substituted only once
+conf5 = configuration_data()
+conf5.set('var', '@var2@')
+conf5.set('var2', 'error')
+configure_file(
+ input : 'config5.h.in',
+ output : '@BASENAME@',
+ configuration : conf5
+)
+test('test5', executable('prog5', 'prog5.c'))
+
+# Test escaping
+conf6 = configuration_data()
+conf6.set('var1', 'foo')
+conf6.set('var2', 'bar')
+configure_file(
+ input : 'config6.h.in',
+ output : '@BASENAME@',
+ configuration : conf6
+)
+test('test6', executable('prog6', 'prog6.c'))
diff --git a/test cases/common/16 configure file/prog5.c b/test cases/common/16 configure file/prog5.c
new file mode 100644
index 0000000..42c08f9
--- /dev/null
+++ b/test cases/common/16 configure file/prog5.c
@@ -0,0 +1,6 @@
+#include <string.h>
+#include <config5.h>
+
+int main(int argc, char **argv) {
+ return strcmp(MESSAGE, "@var2@");
+}
diff --git a/test cases/common/16 configure file/prog6.c b/test cases/common/16 configure file/prog6.c
new file mode 100644
index 0000000..7412404
--- /dev/null
+++ b/test cases/common/16 configure file/prog6.c
@@ -0,0 +1,11 @@
+#include <string.h>
+#include <config6.h>
+
+int main(int argc, char **argv) {
+ return strcmp(MESSAGE1, "foo")
+ || strcmp(MESSAGE2, "@var1@")
+ || strcmp(MESSAGE3, "\\foo")
+ || strcmp(MESSAGE4, "\\@var1@")
+ || strcmp(MESSAGE5, "@var1bar")
+ || strcmp(MESSAGE6, "\\ @ @ \\@ \\@");
+}
diff --git a/test cases/common/165 config tool variable/meson.build b/test cases/common/165 config tool variable/meson.build
new file mode 100644
index 0000000..0643042
--- /dev/null
+++ b/test cases/common/165 config tool variable/meson.build
@@ -0,0 +1,31 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+project('config tool variable', 'cpp')
+
+
+dep_llvm = dependency('llvm', required : false)
+if not dep_llvm.found()
+ error('MESON_SKIP_TEST LLVM not installed.')
+endif
+
+includedir = dep_llvm.get_configtool_variable('includedir')
+includedir = join_paths(includedir, 'llvm')
+if host_machine.system() == 'windows'
+ cmd = run_command(['dir', includedir])
+else
+ cmd = run_command(['ls', includedir])
+endif
+
+assert(cmd.returncode() == 0, 'did not run successfully')
diff --git a/test cases/common/166 array option/meson.build b/test cases/common/166 array option/meson.build
new file mode 100644
index 0000000..bfcde7c
--- /dev/null
+++ b/test cases/common/166 array option/meson.build
@@ -0,0 +1,17 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+project('stringarray default options')
+
+assert(get_option('array') == ['foo', 'bar'], 'Default value for array is not equal to choices')
diff --git a/test cases/common/166 array option/meson_options.txt b/test cases/common/166 array option/meson_options.txt
new file mode 100644
index 0000000..7ed0ac1
--- /dev/null
+++ b/test cases/common/166 array option/meson_options.txt
@@ -0,0 +1,19 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+option(
+ 'array',
+ type : 'array',
+ choices : ['foo', 'bar'],
+)
diff --git a/test cases/common/167 external program shebang parsing/input.txt b/test cases/common/167 external program shebang parsing/input.txt
new file mode 100644
index 0000000..40e30d4
--- /dev/null
+++ b/test cases/common/167 external program shebang parsing/input.txt
@@ -0,0 +1 @@
+some stuff here
diff --git a/test cases/common/167 external program shebang parsing/main.c b/test cases/common/167 external program shebang parsing/main.c
new file mode 100644
index 0000000..a90206b
--- /dev/null
+++ b/test cases/common/167 external program shebang parsing/main.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+ #include <io.h>
+ #include <windows.h>
+#else
+ #include <unistd.h>
+#endif
+
+/* Who cares about stack sizes in test programs anyway */
+#define LINE_LENGTH 4096
+
+static int
+intrp_copyfile (char * src, char * dest)
+{
+#ifdef _WIN32
+ if (!CopyFile (src, dest, FALSE))
+ return 1;
+ return 0;
+#else
+ return execlp ("cp", "copyfile", src, dest, NULL);
+#endif
+}
+
+static void
+parser_get_line (FILE * f, char line[LINE_LENGTH])
+{
+ if (!fgets (line, LINE_LENGTH, f))
+ fprintf (stderr, "%s\n", strerror (errno));
+}
+
+int
+main (int argc, char * argv[])
+{
+ FILE *f = NULL;
+ char line[LINE_LENGTH];
+
+ if (argc != 4) {
+ fprintf (stderr, "Invalid number of arguments: %i\n", argc);
+ goto err;
+ }
+
+ if ((f = fopen (argv[1], "r")) == NULL) {
+ fprintf (stderr, "%s\n", strerror (errno));
+ goto err;
+ }
+
+ parser_get_line (f, line);
+
+ if (!line || line[0] != '#' || line[1] != '!') {
+ fprintf (stderr, "Invalid script\n");
+ goto err;
+ }
+
+ parser_get_line (f, line);
+
+ if (!line || strncmp (line, "copy", 4) != 0) {
+ fprintf (stderr, "Syntax error: %s\n", line);
+ goto err;
+ }
+
+ return intrp_copyfile (argv[2], argv[3]);
+
+err:
+ fclose (f);
+ return 1;
+}
diff --git a/test cases/common/167 external program shebang parsing/meson.build b/test cases/common/167 external program shebang parsing/meson.build
new file mode 100644
index 0000000..c1cc5af
--- /dev/null
+++ b/test cases/common/167 external program shebang parsing/meson.build
@@ -0,0 +1,21 @@
+project('shebang parsing', 'c')
+
+interpreter = executable('aninterp', 'main.c', native : true)
+
+cdata = configuration_data()
+cdata.set('INTRP', interpreter.full_path())
+
+f = configure_file(input : 'script.int.in',
+ output : 'script.int',
+ configuration : cdata)
+
+# Test that parsing a shebang with spaces works properly. See `man execve`,
+# specifically the section on "Interpreter scripts" and the one under "NOTES".
+script = find_program(f)
+
+custom_target('interpthis',
+ input : 'input.txt',
+ output : 'output.txt',
+ depends : interpreter,
+ command : [script, '@INPUT@', '@OUTPUT@'],
+ build_by_default : true)
diff --git a/test cases/common/167 external program shebang parsing/script.int.in b/test cases/common/167 external program shebang parsing/script.int.in
new file mode 100644
index 0000000..77ff909
--- /dev/null
+++ b/test cases/common/167 external program shebang parsing/script.int.in
@@ -0,0 +1,2 @@
+#!/usr/bin/env @INTRP@
+copy
diff --git a/test cases/common/168 subdir if_found/meson.build b/test cases/common/168 subdir if_found/meson.build
new file mode 100644
index 0000000..2c640cf
--- /dev/null
+++ b/test cases/common/168 subdir if_found/meson.build
@@ -0,0 +1,11 @@
+project('subdir if found', 'c')
+
+found_dep = declare_dependency()
+not_found_dep = dependency('nonexisting', required : false)
+
+subdir('nonexisting_dir', if_found : not_found_dep)
+
+variable = 3
+
+subdir('subdir', if_found : found_dep)
+assert(variable == 5, 'Subdir was not properly entered.')
diff --git a/test cases/common/168 subdir if_found/subdir/meson.build b/test cases/common/168 subdir if_found/subdir/meson.build
new file mode 100644
index 0000000..1030e25
--- /dev/null
+++ b/test cases/common/168 subdir if_found/subdir/meson.build
@@ -0,0 +1 @@
+variable = 5
diff --git a/test cases/common/42 string operations/meson.build b/test cases/common/42 string operations/meson.build
index d9f8130..a43de70 100644
--- a/test cases/common/42 string operations/meson.build
+++ b/test cases/common/42 string operations/meson.build
@@ -13,6 +13,8 @@ subs2 = '42'
assert(templ2.format(subs2) == '42', 'String formatting with variables is broken.')
+assert('@@0@@ @@1@@'.format(1, 2) == '@1@ @2@', 'String format is recursive.')
+
long = 'abcde'
prefix = 'abc'
suffix = 'cde'
@@ -74,3 +76,24 @@ multiline string '''.strip() == '''multiline string''', 'Newlines badly stripped
assert('"1.1.20"'.strip('"') == '1.1.20', '" badly stripped')
assert('"1.1.20"'.strip('".') == '1.1.20', '". badly stripped')
assert('"1.1.20" '.strip('" ') == '1.1.20', '". badly stripped')
+
+bs_b = '''\b'''
+bs_bs_b = '''\\b'''
+nl = '''
+'''
+bs_n = '''\n'''
+bs_nl = '''\
+'''
+bs_bs_n = '''\\n'''
+bs_bs_nl = '''\\
+'''
+
+assert('\b' == bs_b, 'Single backslash broken')
+assert('\\b' == bs_b, 'Double backslash broken')
+assert('\\\b' == bs_bs_b, 'Three backslash broken')
+assert('\\\\b' == bs_bs_b, 'Four backslash broken')
+assert('\n' == nl, 'Newline escape broken')
+assert('\\n' == bs_n, 'Double backslash broken before n')
+assert('\\\n' == bs_nl, 'Three backslash broken before n')
+assert('\\\\n' == bs_bs_n, 'Four backslash broken before n')
+assert('\\\\\n' == bs_bs_nl, 'Five backslash broken before n')
diff --git a/test cases/common/47 options/meson.build b/test cases/common/47 options/meson.build
index 2a764f0..863703c 100644
--- a/test cases/common/47 options/meson.build
+++ b/test cases/common/47 options/meson.build
@@ -12,6 +12,11 @@ if get_option('combo_opt') != 'combo'
error('Incorrect value to combo option.')
endif
+if get_option('array_opt') != ['one', 'two']
+ message(get_option('array_opt'))
+ error('Incorrect value for array option')
+endif
+
# If the default changes, update test cases/unit/13 reconfigure
if get_option('b_lto') != false
error('Incorrect value in base option.')
diff --git a/test cases/common/47 options/meson_options.txt b/test cases/common/47 options/meson_options.txt
index 653dd75..6bd0346 100644
--- a/test cases/common/47 options/meson_options.txt
+++ b/test cases/common/47 options/meson_options.txt
@@ -1,3 +1,5 @@
option('testoption', type : 'string', value : 'optval', description : 'An option to do something')
option('other_one', type : 'boolean', value : false)
option('combo_opt', type : 'combo', choices : ['one', 'two', 'combo'], value : 'combo')
+option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two'])
+option('free_array_opt', type : 'array')
diff --git a/test cases/common/66 install subdir/subdir/meson.build b/test cases/common/66 install subdir/subdir/meson.build
index 37d2da4..a1dadd4 100644
--- a/test cases/common/66 install subdir/subdir/meson.build
+++ b/test cases/common/66 install subdir/subdir/meson.build
@@ -1,3 +1,3 @@
install_subdir('sub1', install_dir : 'share',
- # This mode will be overriden by the mode set in the outer install_subdir
+ # This mode will be overridden by the mode set in the outer install_subdir
install_mode : 'rwxr-x---')
diff --git a/test cases/common/68 number arithmetic/meson.build b/test cases/common/68 number arithmetic/meson.build
index 02c7878..495a83e 100644
--- a/test cases/common/68 number arithmetic/meson.build
+++ b/test cases/common/68 number arithmetic/meson.build
@@ -25,16 +25,16 @@ assert((5 % 2) == 1, 'Integer modulo (odd) is broken')
assert((4 % 2) == 0, 'Integer modulo (even) is broken')
if 2 * 1 % 2 != 0
- error('Modulo precendence with multiplication is broken')
+ error('Modulo precedence with multiplication is broken')
endif
if 2 + 1 % 2 != 3
- error('Modulo precendence with addition is broken')
+ error('Modulo precedence with addition is broken')
endif
if 9 / 9 % 2 != 1
- error('Modulo precendence with division is broken')
+ error('Modulo precedence with division is broken')
endif
if 9 - 9 % 2 != 8
- error('Modulo precendence with subtraction is broken')
+ error('Modulo precedence with subtraction is broken')
endif
assert(2.is_even(), 'int is_even() broken for even value')
diff --git a/test cases/common/71 arithmetic bidmas/meson.build b/test cases/common/71 arithmetic bidmas/meson.build
index 2a530c8..c7334b4 100644
--- a/test cases/common/71 arithmetic bidmas/meson.build
+++ b/test cases/common/71 arithmetic bidmas/meson.build
@@ -1,7 +1,7 @@
project('arithmetic bidmas', 'c')
if 5 * 3 - 6 / 2 + 1 != 13
- error('Arithemtic bidmas broken')
+ error('Arithmetic bidmas broken')
endif
if 5 * (3 - 6 / 2) + 1 != 1
error('Arithmetic bidmas with brackets broken')
diff --git a/test cases/failing/65 grab sibling/subprojects/b/sneaky.c b/test cases/failing/65 grab sibling/subprojects/b/sneaky.c
index f1cb916..46718c6 100644
--- a/test cases/failing/65 grab sibling/subprojects/b/sneaky.c
+++ b/test cases/failing/65 grab sibling/subprojects/b/sneaky.c
@@ -1,6 +1,6 @@
#include<stdio.h>
int main(int argc, char **argv) {
- printf("I can only come into existance via trickery.\n");
+ printf("I can only come into existence via trickery.\n");
return 0;
}
diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build
index c79bd46..61a34ef 100644
--- a/test cases/frameworks/16 sdl2/meson.build
+++ b/test cases/frameworks/16 sdl2/meson.build
@@ -6,5 +6,8 @@ e = executable('sdl2prog', 'sdl2prog.c', dependencies : sdl2_dep)
test('sdl2test', e)
-# Ensure that we can find it with sdl2-config too
+# Ensure that we can find it with sdl2-config too, using the legacy method name
configdep = dependency('sdl2', method : 'sdlconfig')
+
+# And the modern method name
+configdep = dependency('sdl2', method : 'config-tool')
diff --git a/test cases/frameworks/19 pcap/meson.build b/test cases/frameworks/19 pcap/meson.build
index c505960..f02f411 100644
--- a/test cases/frameworks/19 pcap/meson.build
+++ b/test cases/frameworks/19 pcap/meson.build
@@ -8,3 +8,7 @@ assert(pcap_ver.split('.').length() > 1, 'pcap version is "@0@"'.format(pcap_ver
e = executable('pcap_prog', 'pcap_prog.c', dependencies : pcap_dep)
test('pcaptest', e)
+
+# Ensure discovery bia the configuration tools work also
+pcap_dep = dependency('pcap', version : '>=1.0', method : 'pcap-config')
+pcap_dep = dependency('pcap', version : '>=1.0', method : 'config-tool')
diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build
index 6c9b6fe..11f6f63 100644
--- a/test cases/frameworks/20 cups/meson.build
+++ b/test cases/frameworks/20 cups/meson.build
@@ -5,3 +5,8 @@ cups_dep = dependency('cups', version : '>=1.4')
e = executable('cups_prog', 'cups_prog.c', dependencies : cups_dep)
test('cupstest', e)
+
+# ensure we can find the cups dependency via the legacy and modern config-tool
+# options
+dep = dependency('cups', version : '>=1.4', method : 'cups-config')
+dep = dependency('cups', version : '>=1.4', method : 'config-tool')
diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build
index a7d9263..b39d8f4 100644
--- a/test cases/frameworks/21 libwmf/meson.build
+++ b/test cases/frameworks/21 libwmf/meson.build
@@ -1,9 +1,14 @@
project('libwmf test', 'c')
-libwmf_dep = dependency('libwmf', version : '>=3.0')
+libwmf_dep = dependency('libwmf', version : '>= 0.2.8')
libwmf_ver = libwmf_dep.version()
assert(libwmf_ver.split('.').length() > 1, 'libwmf version is "@0@"'.format(libwmf_ver))
message('libwmf version is "@0@"'.format(libwmf_ver))
e = executable('libwmf_prog', 'libwmf_prog.c', dependencies : libwmf_dep)
test('libwmftest', e)
+
+# Test using the method keyword:
+
+dependency('libwmf', method : 'config-tool')
+dependency('libwmf', method : 'libwmf-config')
diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build
index 374707a..7c33268 100644
--- a/test cases/frameworks/4 qt/meson.build
+++ b/test cases/frameworks/4 qt/meson.build
@@ -59,6 +59,8 @@ foreach qt : ['qt4', 'qt5']
# We need a console test application because some test environments
# do not have an X server.
+ translations = qtmodule.compile_translations(ts_files : qt+'core_fr.ts', build_by_default : true)
+
qtcore = dependency(qt, modules : 'Core', method : get_option('method'))
qtcoreapp = executable(qt + 'core', 'q5core.cpp',
diff --git a/test cases/frameworks/4 qt/q5core.cpp b/test cases/frameworks/4 qt/q5core.cpp
index 706e4dc..25b80b8 100644
--- a/test cases/frameworks/4 qt/q5core.cpp
+++ b/test cases/frameworks/4 qt/q5core.cpp
@@ -1,8 +1,26 @@
#include <QCoreApplication>
+#include <QtGlobal>
+#include <QString>
+#include <QTranslator>
+#include <QLocale>
+#include <QLibraryInfo>
+#include <QDebug>
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
+ QTranslator qtTranslator;
+ qtTranslator.load("qt_" + QLocale::system().name(),
+ QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+ app.installTranslator(&qtTranslator);
+
+ QTranslator myappTranslator;
+ if(!myappTranslator.load("qt5core_fr") )
+ return 1;
+
+ app.installTranslator(&myappTranslator);
+
+ qDebug() << QObject::tr("Translate me!");
// Don't actually start the main loop so this
// can be run as a unit test.
//return app.exec();
diff --git a/test cases/frameworks/4 qt/qt4core_fr.ts b/test cases/frameworks/4 qt/qt4core_fr.ts
new file mode 100644
index 0000000..0638bd5
--- /dev/null
+++ b/test cases/frameworks/4 qt/qt4core_fr.ts
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr_FR">
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="q5core.cpp" line="23"/>
+ <source>Translate me!</source>
+ <translation>Traduisez moi!</translation>
+ </message>
+</context>
+</TS>
diff --git a/test cases/frameworks/4 qt/qt5core_fr.ts b/test cases/frameworks/4 qt/qt5core_fr.ts
new file mode 100644
index 0000000..4e3116b
--- /dev/null
+++ b/test cases/frameworks/4 qt/qt5core_fr.ts
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fr_FR">
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="q5core.cpp" line="23"/>
+ <source>Translate me!</source>
+ <translation>Traduisez moi!</translation>
+ </message>
+</context>
+</TS>
diff --git a/test cases/frameworks/9 wxwidgets/meson.build b/test cases/frameworks/9 wxwidgets/meson.build
index da3aa26..5f9419c 100644
--- a/test cases/frameworks/9 wxwidgets/meson.build
+++ b/test cases/frameworks/9 wxwidgets/meson.build
@@ -1,4 +1,4 @@
-project('wxwidgets test', 'cpp')
+project('wxwidgets test', 'cpp', default_options : ['cpp_std=c++11'])
wxd = dependency('wxwidgets', version : '>=5', required : false)
wxd = dependency('wxwidgets', version : '>=3.0.0', required : false)
diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build
index f72e111..30e5d25 100644
--- a/test cases/linuxlike/1 pkg-config/meson.build
+++ b/test cases/linuxlike/1 pkg-config/meson.build
@@ -25,7 +25,7 @@ dep2 = declare_dependency(dependencies : dep)
exe2 = executable('zlibprog2', 'prog.c', dependencies : dep2)
test('zlibtest2', exe2)
-# Try to find a nonexistant library to ensure requires:false works.
+# Try to find a nonexistent library to ensure requires:false works.
dep = dependency('nvakuhrabnsdfasdf', required : false)
diff --git a/test cases/osx/2 library versions/CMakeLists.txt b/test cases/osx/2 library versions/CMakeLists.txt
index 82fe1e2..2cd03b9 100644
--- a/test cases/osx/2 library versions/CMakeLists.txt
+++ b/test cases/osx/2 library versions/CMakeLists.txt
@@ -4,23 +4,26 @@ project(dylibversion C)
# This file is here for debugging purposes to easily compare how
# CMake does it.
+# libnoversion.dylib
add_library(noversion SHARED lib.c)
# libonlysoversion.dylib -> libonlysoversion.5.dylib
# libonlyversion.1.4.5.dylib
+# -current_version 1.4.5
add_library(onlyversion SHARED lib.c)
set_target_properties(onlyversion PROPERTIES VERSION 1.4.5)
-# libnoversion.dylib
-# libonlysoversion.5.dylib
+# libonlysoversion.6.dylib
+# -compatibility_version 6.0.0
add_library(onlysoversion SHARED lib.c)
-set_target_properties(onlysoversion PROPERTIES SOVERSION 5)
+set_target_properties(onlysoversion PROPERTIES SOVERSION 6)
# libsome.1.4.5.dylib
-# libsome.5.dylib -> libsome.1.4.5.dylib
-# libsome.dylib -> libsome.5.dylib
+# libsome.6.dylib -> libsome.1.4.5.dylib
+# libsome.dylib -> libsome.6.dylib
+# -current_version 1.4.5 -compatibility_version 5.0.0
add_library(some SHARED lib.c)
-set_target_properties(some PROPERTIES VERSION 1.4.5 SOVERSION 5)
+set_target_properties(some PROPERTIES VERSION 1.4.5 SOVERSION 6)
diff --git a/test cases/osx/2 library versions/meson.build b/test cases/osx/2 library versions/meson.build
index 9624998..3061ed6 100644
--- a/test cases/osx/2 library versions/meson.build
+++ b/test cases/osx/2 library versions/meson.build
@@ -29,15 +29,19 @@ out = custom_target('library-dependency-hack',
# Manually test if the linker can find the above libraries
# i.e., whether they were generated with the right naming scheme
test('manually linked 1', executable('manuallink1', out,
- link_args : ['-L.', '-lsome']))
+ link_args : ['-L.', '-lsome'],
+ build_rpath : meson.current_build_dir()))
test('manually linked 2', executable('manuallink2', out,
- link_args : ['-L.', '-lnoversion']))
+ link_args : ['-L.', '-lnoversion'],
+ build_rpath : meson.current_build_dir()))
test('manually linked 3', executable('manuallink3', out,
- link_args : ['-L.', '-lonlyversion']))
+ link_args : ['-L.', '-lonlyversion'],
+ build_rpath : meson.current_build_dir()))
test('manually linked 4', executable('manuallink4', out,
- link_args : ['-L.', '-lonlysoversion']))
+ link_args : ['-L.', '-lonlysoversion'],
+ build_rpath : meson.current_build_dir()))
shared_module('module', 'lib.c', install : true)
diff --git a/test cases/osx/3 has function xcode8/meson.build b/test cases/osx/3 has function xcode8/meson.build
index 300d352..edd3688 100644
--- a/test cases/osx/3 has function xcode8/meson.build
+++ b/test cases/osx/3 has function xcode8/meson.build
@@ -7,20 +7,24 @@ sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/M
args_10_11 = ['-mmacosx-version-min=10.11'] + sdk_args
args_10_12 = ['-mmacosx-version-min=10.12'] + sdk_args
+# XCode 9 location for the macOS 10.13 SDK
+sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk']
+args_10_12 = ['-mmacosx-version-min=10.13'] + sdk_args
+
# Test requires XCode 8 which has the MacOSX 10.12 SDK
-if cc.version().version_compare('>=8.0')
+if cc.version().version_compare('>=8.0') and cc.version().version_compare('<8.1')
if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include <time.h>')
- error('Should not have found clock_gettime via <time.h> when targetting Mac OS X 10.11')
+ error('Should not have found clock_gettime via <time.h> when targeting Mac OS X 10.11')
endif
if not cc.has_function('clock_gettime', args : args_10_12, prefix : '#include <time.h>')
- error('Did NOT find clock_gettime via <time.h> when targetting Mac OS X 10.12')
+ error('Did NOT find clock_gettime via <time.h> when targeting Mac OS X 10.12')
endif
if not cc.has_function('clock_gettime', args : args_10_11)
- error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.11')
+ error('Did NOT find clock_gettime w/o a prototype when targeting Mac OS X 10.11')
endif
if not cc.has_function('clock_gettime', args : args_10_12)
- error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.12')
+ error('Did NOT find clock_gettime w/o a prototype when targeting Mac OS X 10.12')
endif
else
- message('Test needs XCode 8, skipping...')
+ error('MESON_SKIP_TEST Test needs XCode 8.')
endif
diff --git a/test cases/unit/17 pkgconfig static/meson.build b/test cases/unit/17 pkgconfig static/meson.build
index caeb4aa..d1b0fd5 100644
--- a/test cases/unit/17 pkgconfig static/meson.build
+++ b/test cases/unit/17 pkgconfig static/meson.build
@@ -5,8 +5,22 @@ if build_machine.system() != 'windows'
else
# pkg-config files should not use paths with \
prefix_parts = meson.source_root().split('\\')
- prefix = '/'.join(prefix_parts)
+ # If the path is C:/foo/bar, convert it to /c/foo/bar so we can test if our
+ # automatic conversion to C:/foo/bar inside PkgConfigDependency is working.
+ if prefix_parts[0][1] == ':'
+ drive = prefix_parts[0][0]
+ else
+ drive = prefix_parts[0]
+ endif
+ new_parts = []
+ foreach part : prefix_parts
+ if part != prefix_parts[0]
+ new_parts += part
+ endif
+ endforeach
+ prefix = '/@0@/@1@'.format(drive, '/'.join(new_parts))
endif
+message(prefix)
# Escape spaces
prefix_parts = prefix.split(' ')
diff --git a/test cases/unit/18 array option/meson.build b/test cases/unit/18 array option/meson.build
new file mode 100644
index 0000000..2b44181
--- /dev/null
+++ b/test cases/unit/18 array option/meson.build
@@ -0,0 +1,15 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+project('array option test')
diff --git a/test cases/unit/18 array option/meson_options.txt b/test cases/unit/18 array option/meson_options.txt
new file mode 100644
index 0000000..0ccdcc4
--- /dev/null
+++ b/test cases/unit/18 array option/meson_options.txt
@@ -0,0 +1,20 @@
+# Copyright © 2017 Intel Corporation
+#
+# 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.
+
+option(
+ 'list',
+ type : 'array',
+ value : ['foo', 'bar'],
+ choices : ['foo', 'bar', 'oink', 'boink'],
+)