aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--docs/markdown/Builtin-options.md34
-rw-r--r--docs/markdown/Qt5-module.md22
-rw-r--r--docs/markdown/Reference-manual.md15
-rw-r--r--docs/markdown/Reference-tables.md3
-rw-r--r--docs/markdown/Users.md10
-rw-r--r--docs/markdown/snippets/emscripten_threads.md6
-rw-r--r--docs/markdown/snippets/override_dependency.md65
-rw-r--r--docs/markdown/snippets/qt_has_tools.md10
-rw-r--r--docs/markdown/snippets/wasm_ld.md5
-rw-r--r--mesonbuild/ast/interpreter.py7
-rw-r--r--mesonbuild/build.py7
-rw-r--r--mesonbuild/cmake/interpreter.py2
-rw-r--r--mesonbuild/compilers/c.py4
-rw-r--r--mesonbuild/compilers/cpp.py4
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py29
-rw-r--r--mesonbuild/compilers/mixins/islinker.py9
-rw-r--r--mesonbuild/environment.py12
-rw-r--r--mesonbuild/interpreter.py132
-rw-r--r--mesonbuild/linkers.py29
-rw-r--r--mesonbuild/mintro.py11
-rw-r--r--mesonbuild/modules/qt.py21
-rw-r--r--mesonbuild/templates/mesontemplates.py36
-rwxr-xr-xrun_unittests.py64
-rw-r--r--test cases/cmake/3 advanced no dep/subprojects/cmMod/CMakeLists.txt4
-rw-r--r--test cases/cmake/3 advanced no dep/test.json4
-rw-r--r--test cases/common/102 subproject subdir/meson.build21
-rw-r--r--test cases/common/102 subproject subdir/subprojects/sub/lib/meson.build1
-rw-r--r--test cases/common/102 subproject subdir/subprojects/sub/meson.build2
-rw-r--r--test cases/common/102 subproject subdir/subprojects/sub_novar/meson.build4
-rw-r--r--test cases/failing/100 fallback consistency/meson.build7
-rw-r--r--test cases/failing/100 fallback consistency/subprojects/sub/meson.build5
-rw-r--r--test cases/failing/99 fallback consistency/meson.build4
-rw-r--r--test cases/failing/99 fallback consistency/subprojects/sub/meson.build5
-rw-r--r--test cases/frameworks/4 qt/meson.build1
-rw-r--r--test cases/linuxlike/5 dependency versions/meson.build23
-rw-r--r--test cases/linuxlike/5 dependency versions/subprojects/fakezlib/meson.build3
-rw-r--r--test cases/unit/57 introspection/meson.build1
-rw-r--r--test cases/wasm/1 basic/hello.c7
-rw-r--r--test cases/wasm/1 basic/meson.build3
-rw-r--r--test cases/wasm/2 threads/meson.build10
-rw-r--r--test cases/wasm/2 threads/threads.c21
-rw-r--r--test cases/wasm/2 threads/threads.cpp13
43 files changed, 587 insertions, 93 deletions
diff --git a/README.md b/README.md
index 4996aa7..49a1809 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ and that these two are different. In your source root must exist a
file called `meson.build`. To generate the build system run this
command:
-`meson <source directory> <build directory>`
+`meson setup <source directory> <build directory>`
Depending on how you obtained Meson the command might also be called
`meson.py` instead of plain `meson`. In the rest of this document we
@@ -61,7 +61,7 @@ do things like this:
```console
cd <source root>
-meson builddir
+meson setup builddir
```
To compile, cd into your build directory and type `ninja`. To run unit
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index 9c8c54c..c2b10b6 100644
--- a/docs/markdown/Builtin-options.md
+++ b/docs/markdown/Builtin-options.md
@@ -151,20 +151,22 @@ The following options are available. Note that both the options themselves and
the possible values they can take will depend on the target platform or
compiler being used:
-| Option | Default value | Possible values | Description |
-| ------ | ------------- | --------------- | ----------- |
-| c_args | | free-form comma-separated list | C compile arguments to use |
-| c_link_args | | free-form comma-separated list | C link arguments to use |
-| c_std | none | none, c89, c99, c11, c17, c18, gnu89, gnu99, gnu11, gnu17, gnu18 | C language standard to use |
-| c_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
-| cpp_args | | free-form comma-separated list | C++ compile arguments to use |
-| cpp_link_args| | free-form comma-separated list | C++ link arguments to use |
-| cpp_std | none | none, c++98, c++03, c++11, c++14, c++17, <br/>c++1z, gnu++03, gnu++11, gnu++14, gnu++17, gnu++1z, <br/> vc++14, vc++17, vc++latest | C++ language standard to use |
-| cpp_debugstl | false | true, false | C++ STL debug mode |
-| cpp_eh | default | none, default, a, s, sc | C++ exception handling type |
-| cpp_rtti | true | true, false | Whether to enable RTTI (runtime type identification) |
-| cpp_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
-| fortran_std | none | [none, legacy, f95, f2003, f2008, f2018] | Fortran language standard to use |
+| Option | Default value | Possible values | Description |
+| ------ | ------------- | --------------- | ----------- |
+| c_args | | free-form comma-separated list | C compile arguments to use |
+| c_link_args | | free-form comma-separated list | C link arguments to use |
+| c_std | none | none, c89, c99, c11, c17, c18, gnu89, gnu99, gnu11, gnu17, gnu18 | C language standard to use |
+| c_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
+| c_thread_count | 4 | integer value ≥ 0 | Number of threads to use with emcc when using threads |
+| cpp_args | | free-form comma-separated list | C++ compile arguments to use |
+| cpp_link_args | | free-form comma-separated list | C++ link arguments to use |
+| cpp_std | none | none, c++98, c++03, c++11, c++14, c++17, <br/>c++1z, gnu++03, gnu++11, gnu++14, gnu++17, gnu++1z, <br/> vc++14, vc++17, vc++latest | C++ language standard to use |
+| cpp_debugstl | false | true, false | C++ STL debug mode |
+| cpp_eh | default | none, default, a, s, sc | C++ exception handling type |
+| cpp_rtti | true | true, false | Whether to enable RTTI (runtime type identification) |
+| cpp_thread_count | 4 | integer value ≥ 0 | Number of threads to use with emcc when using threads |
+| cpp_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
+| fortran_std | none | [none, legacy, f95, f2003, f2008, f2018] | Fortran language standard to use |
The default values of `c_winlibs` and `cpp_winlibs` are in compiler-specific
argument forms, but the libraries are: kernel32, user32, gdi32, winspool,
@@ -179,3 +181,7 @@ while the `cpp_eh=[value]` will result in `/EH[value]`.
Since *0.51.0* `cpp_eh=default` will result in `/EHsc` on MSVC. When using
gcc-style compilers, nothing is passed (allowing exceptions to work), while
`cpp_eh=none` passes `-fno-exceptions`.
+
+Since *0.54.0* The `<lang>_thread_count` option can be used to control the
+value passed to `-s PTHREAD_POOL_SIZE` when using emcc. No other c/c++
+compiler supports this option.
diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md
index 3a51954..f1c2f6c 100644
--- a/docs/markdown/Qt5-module.md
+++ b/docs/markdown/Qt5-module.md
@@ -23,6 +23,28 @@ This method generates the necessary targets to build translation files with lrel
- `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).
+## has_tools
+
+This method returns `true` if all tools used by this module are found, `false`
+otherwise.
+
+It should be used to compile optional Qt code:
+```meson
+qt5 = import('qt5')
+if qt5.has_tools(required: get_option('qt_feature'))
+ moc_files = qt5.preprocess(...)
+ ...
+endif
+```
+
+This method takes the following keyword arguments:
+- `required`: by default, `required` is set to `false`. If `required` is set to
+ `true` or an enabled [`feature`](Build-options.md#features) and some tools are
+ missing Meson will abort.
+- `method`: method used to find the Qt dependency (`auto` by default).
+
+*Since: 0.54.0*
+
## Dependencies
See [Qt dependencies](Dependencies.md#qt4-qt5)
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index e43ef57..9bb7911 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -445,6 +445,10 @@ arguments:
[`dependency()`](#dependency), etc. Note that this means the
fallback dependency may be a not-found dependency, in which
case the value of the `required:` kwarg will be obeyed.
+ *Since 0.54.0* `'subproj_dep'` argument can be omitted in the case the
+ subproject used `meson.override_dependency('dependency_name', subproj_dep)`.
+ In that case, the `fallback` keyword argument can be a single string instead
+ of a list of 2 strings.
- `language` *(added 0.42.0)* defines what language-specific
dependency to find if it's available for multiple languages.
- `method` defines the way the dependency is detected, the default is
@@ -1825,12 +1829,21 @@ the following methods.
- `override_find_program(progname, program)` [*(Added
0.46.0)*](Release-notes-for-0.46.0.md#can-override-find_program)
specifies that whenever `find_program` is used to find a program
- named `progname`, Meson should not not look it up on the system but
+ named `progname`, Meson should not look it up on the system but
instead return `program`, which may either be the result of
`find_program`, `configure_file` or `executable`.
If `program` is an `executable`, it cannot be used during configure.
+- `override_dependency(name, dep_object)` [*(Added
+ 0.54.0)*](Release-notes-for-0.54.0.md#override-dependency)
+ specifies that whenever `dependency(name, ...)` is used, Meson should not
+ look it up on the system but instead return `dep_object`, which may either be
+ the result of `dependency()` or `declare_dependency()`. It takes optional
+ `native` keyword arguments. Doing this in a subproject allows the parent
+ project to retrieve the dependency without having to know the dependency
+ variable name: `dependency(name, fallback : subproject_name)`.
+
- `project_version()` returns the version string specified in
`project` function call.
diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md
index 9f432f0..59e252f 100644
--- a/docs/markdown/Reference-tables.md
+++ b/docs/markdown/Reference-tables.md
@@ -37,10 +37,11 @@ These are return values of the `get_linker_id` method in a compiler object.
| Value | Linker family |
| ----- | --------------- |
-| ld.bfd | The GNU linker |
+| ld.bfd | The GNU linker |
| ld.gold | The GNU gold linker |
| ld.lld | The LLVM linker, with the GNU interface |
| ld.solaris | Solaris and illumos |
+| ld.wasm | emscripten's wasm-ld linker |
| ld64 | Apple ld64 |
| link | MSVC linker |
| lld-link | The LLVM linker, with the MSVC interface |
diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md
index 06404cf..f215ce9 100644
--- a/docs/markdown/Users.md
+++ b/docs/markdown/Users.md
@@ -23,6 +23,7 @@ topic](https://github.com/topics/meson).
- [Cage](https://github.com/Hjdskes/cage), a Wayland kiosk
- [casync](https://github.com/systemd/casync), Content-Addressable Data Synchronization Tool
- [cinnamon-desktop](https://github.com/linuxmint/cinnamon-desktop), the cinnamon desktop library
+ - [Cozy](https://github.com/geigi/cozy), a modern audio book player for Linux and macOS using GTK+ 3
- [dav1d](https://code.videolan.org/videolan/dav1d), an AV1 decoder
- [dbus-broker](https://github.com/bus1/dbus-broker), Linux D-Bus Message Broker
- [DPDK](http://dpdk.org/browse/dpdk), Data Plane Development Kit, a set of libraries and drivers for fast packet processing
@@ -31,6 +32,8 @@ topic](https://github.com/topics/meson).
- [Emeus](https://github.com/ebassi/emeus), constraint based layout manager for GTK+
- [ESP8266 Arduino sample project](https://github.com/trilader/arduino-esp8266-meson), sample project for using the ESP8266 Arduino port with Meson
- [FeedReader](https://github.com/jangernert/FeedReader), a modern desktop application designed to complement existing web-based RSS accounts
+ - [Flecs](https://github.com/SanderMertens/flecs), a Fast and Lightweight ECS (Entity Component System) C library
+ - [Foliate](https://github.com/johnfactotum/foliate), a simple and modern GTK eBook reader, built with GJS and Epub.js
- [Fractal](https://wiki.gnome.org/Apps/Fractal/), a Matrix messaging client for GNOME
- [Frida](https://github.com/frida/frida-core), a dynamic binary instrumentation toolkit
- [fwupd](https://github.com/hughsie/fwupd), a simple daemon to allow session software to update firmware
@@ -55,6 +58,7 @@ topic](https://github.com/topics/meson).
- [HelenOS](http://helenos.org), a portable microkernel-based multiserver operating system
- [HexChat](https://github.com/hexchat/hexchat), a cross-platform IRC client in C
- [IGT](https://cgit.freedesktop.org/xorg/app/intel-gpu-tools/), Linux kernel graphics driver test suite
+ - [inih](https://github.com/benhoyt/inih) (INI Not Invented Here), a small and simple .INI file parser written in C
- [Irssi](https://github.com/irssi/irssi), a terminal chat client in C
- [iSH](https://github.com/tbodt/ish), Linux shell for iOS
- [Janet](https://github.com/janet-lang/janet), a functional and imperative programming language and bytecode interpreter
@@ -87,6 +91,7 @@ format files
- [mrsh](https://github.com/emersion/mrsh), a minimal POSIX shell
- [Nautilus](https://gitlab.gnome.org/GNOME/nautilus), the GNOME file manager
- [Nemo](https://github.com/linuxmint/nemo), the file manager for the Cinnamon desktop environment
+ - [OcherBook](https://github.com/ccoffing/OcherBook), an open source book reader for Kobo devices
- [oomd](https://github.com/facebookincubator/oomd), a userspace Out-Of-Memory (OOM) killer for Linux systems
- [OpenH264](https://github.com/cisco/openh264), open source H.264 codec
- [OpenHMD](https://github.com/OpenHMD/OpenHMD), a free and open source API and drivers for immersive technology, such as head mounted displays with built in head tracking
@@ -106,6 +111,8 @@ format files
- [qboot](https://github.com/bonzini/qboot), a minimal x86 firmware for booting Linux kernels
- [radare2](https://github.com/radare/radare2), unix-like reverse engineering framework and commandline tools (not the default)
- [RxDock](https://gitlab.com/rxdock/rxdock), a protein-ligand docking software designed for high throughput virtual screening (fork of rDock)
+ - [scrcpy](https://github.com/Genymobile/scrcpy), a cross platform application that provides display and control of Android devices connected on USB or over TCP/IP
+ - [Sequeler](https://github.com/Alecaddd/sequeler), a friendly SQL client for Linux, built with Vala and Gtk
- [SSHFS](https://github.com/libfuse/sshfs), allows you to mount a remote filesystem using SFTP
- [sway](https://github.com/swaywm/sway), i3-compatible Wayland compositor
- [Sysprof](https://git.gnome.org/browse/sysprof), a profiling tool
@@ -114,7 +121,10 @@ format files
- [Taisei Project](https://taisei-project.org/), an open-source Touhou Project clone and fangame
- [Terminology](https://github.com/billiob/terminology), a terminal emulator based on the Enlightenment Foundation Libraries
- [Tilix](https://github.com/gnunn1/tilix), a tiling terminal emulator for Linux using GTK+ 3
+ - [Tizonia](https://github.com/tizonia/tizonia-openmax-il), a command-line cloud music player for Linux with support for Spotify, Google Play Music, YouTube, SoundCloud, TuneIn, Plex servers and Chromecast devices
- [Valum](https://github.com/valum-framework/valum), a micro web framework written in Vala
+ - [Venom](https://github.com/naxuroqa/Venom), a modern Tox client for the GNU/Linux desktop
+ - [VMAF](https://github.com/Netflix/vmaf) (by Netflix), a perceptual video quality assessment based on multi-method fusion
- [Wayland](https://github.com/wayland-project/wayland) and [Weston](https://github.com/wayland-project/weston), a next generation display server
- [wlroots](https://github.com/swaywm/wlroots), a modular Wayland compositor library
- [wxFormBuilder](https://github.com/wxFormBuilder/wxFormBuilder), RAD tool for wxWidgets GUI design
diff --git a/docs/markdown/snippets/emscripten_threads.md b/docs/markdown/snippets/emscripten_threads.md
new file mode 100644
index 0000000..bdf808e
--- /dev/null
+++ b/docs/markdown/snippets/emscripten_threads.md
@@ -0,0 +1,6 @@
+## Emscripten (emcc) now supports threads
+
+In addition to properly setting the compile and linker arguments, a new meson
+builtin has been added to control the PTHREAD_POOL_SIZE option,
+`-D<lang>_thread_count`, which may be set to any integer value greater than 0.
+If it set to 0 then the PTHREAD_POOL_SIZE option will not be passed.
diff --git a/docs/markdown/snippets/override_dependency.md b/docs/markdown/snippets/override_dependency.md
new file mode 100644
index 0000000..ca420bc
--- /dev/null
+++ b/docs/markdown/snippets/override_dependency.md
@@ -0,0 +1,65 @@
+## `dependency()` consistency
+
+The first time a dependency is found, using `dependency('foo', ...)`, the return
+value is now cached. Any subsequent call will return the same value as long as
+version requested match, otherwise not-found dependency is returned. This means
+that if a system dependency is first found, it won't fallback to a subproject
+in a subsequent call any more and will rather return not-found instead if the
+system version does not match. Similarly, if the first call returns the subproject
+fallback dependency, it will also return the subproject dependency in a subsequent
+call even if no fallback is provided.
+
+For example, if the system has `foo` version 1.0:
+```meson
+# d2 is set to foo_dep and not the system dependency, even without fallback argument.
+d1 = dependency('foo', version : '>=2.0', required : false,
+ fallback : ['foo', 'foo_dep'])
+d2 = dependency('foo', version : '>=1.0', required : false)
+```
+```meson
+# d2 is not-found because the first call returned the system dependency, but its version is too old for 2nd call.
+d1 = dependency('foo', version : '>=1.0', required : false)
+d2 = dependency('foo', version : '>=2.0', required : false,
+ fallback : ['foo', 'foo_dep'])
+```
+
+## Override `dependency()`
+
+It is now possible to override the result of `dependency()` to point
+to any dependency object you want. The overriding is global and applies to
+every subproject from there on.
+
+For example, this subproject provides 2 libraries with version 2.0:
+
+```meson
+project(..., version : '2.0')
+
+libfoo = library('foo', ...)
+foo_dep = declare_dependency(link_with : libfoo)
+meson.override_dependency('foo', foo_dep)
+
+libbar = library('bar', ...)
+bar_dep = declare_dependency(link_with : libbar)
+meson.override_dependency('bar', bar_dep)
+```
+
+Assuming the system has `foo` and `bar` 1.0 installed, and master project does this:
+```meson
+foo_dep = dependency('foo', version : '>=2.0', fallback : ['foo', 'foo_dep'])
+bar_dep = dependency('bar')
+```
+
+This used to mix system 1.0 version and subproject 2.0 dependencies, but thanks
+to the override `bar_dep` is now set to the subproject's version instead.
+
+Another case this can be useful is to force a subproject to use a specific dependency.
+If the subproject does `dependency('foo')` but the main project wants to provide
+its own implementation of `foo`, it can for example call
+`meson.override_dependency('foo', declare_dependency(...))` before configuring the
+subproject.
+
+## Simplified `dependency()` fallback
+
+In the case a subproject `foo` calls `meson.override_dependency('foo-2.0', foo_dep)`,
+the parent project can omit the dependency variable name in fallback keyword
+argument: `dependency('foo-2.0', fallback : 'foo')`.
diff --git a/docs/markdown/snippets/qt_has_tools.md b/docs/markdown/snippets/qt_has_tools.md
new file mode 100644
index 0000000..3569ecb
--- /dev/null
+++ b/docs/markdown/snippets/qt_has_tools.md
@@ -0,0 +1,10 @@
+## Added `has_tools` method to qt module
+
+It should be used to compile optional Qt code:
+```meson
+qt5 = import('qt5')
+if qt5.has_tools(required: get_option('qt_feature'))
+ moc_files = qt5.preprocess(...)
+ ...
+endif
+```
diff --git a/docs/markdown/snippets/wasm_ld.md b/docs/markdown/snippets/wasm_ld.md
new file mode 100644
index 0000000..d47767c
--- /dev/null
+++ b/docs/markdown/snippets/wasm_ld.md
@@ -0,0 +1,5 @@
+## Property support emscripten's wasm-ld
+
+Before 0.54.0 we treated emscripten as both compiler and linker, which isn't
+really true. It does have a linker, called wasm-ld (meson's name is ld.wasm).
+This is a special version of clang's lld. This will now be detected properly.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 25dfb80..243f0be 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -35,6 +35,7 @@ from ..mparser import (
IfClauseNode,
IndexNode,
MethodNode,
+ NotNode,
OrNode,
PlusAssignmentNode,
StringNode,
@@ -209,7 +210,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
if isinstance(args, ArgumentNode):
kwargs = {} # type: T.Dict[T.Union[str, BaseNode], TYPE_nvar]
for key, val in args.kwargs.items():
- if isinstance(key, (StringNode, IdNode)):
+ if resolve_key_nodes and isinstance(key, (StringNode, IdNode)):
assert isinstance(key.value, str)
kwargs[key.value] = val
else:
@@ -235,6 +236,10 @@ class AstInterpreter(interpreterbase.InterpreterBase):
self.evaluate_statement(cur.right)
return False
+ def evaluate_notstatement(self, cur: NotNode) -> bool:
+ self.evaluate_statement(cur.value)
+ return False
+
def evaluate_foreach(self, node: ForeachClauseNode) -> None:
try:
self.evaluate_codeblock(node.block)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 12fa6bd..6755dca 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -106,6 +106,12 @@ def get_target_macos_dylib_install_name(ld) -> str:
class InvalidArguments(MesonException):
pass
+class DependencyOverride:
+ def __init__(self, dep, node, explicit=True):
+ self.dep = dep
+ self.node = node
+ self.explicit = explicit
+
class Build:
"""A class that holds the status of one build including
all dependencies and so on.
@@ -141,6 +147,7 @@ class Build:
self.test_setup_default_name = None
self.find_overrides = {}
self.searched_programs = set() # The list of all programs that have been searched for.
+ self.dependency_overrides = PerMachine({}, {})
def copy(self):
other = Build(self.environment)
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index efdb840..dcb9e25 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -1124,7 +1124,7 @@ class CMakeInterpreter:
# Generate target kwargs
tgt_kwargs = {
- 'build_by_default': False,
+ 'build_by_default': tgt.install,
'link_args': tgt.link_flags + tgt.link_libraries,
'link_with': link_with,
'include_directories': id_node(inc_var),
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index a9ad585..f98316b 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -27,7 +27,7 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
from .mixins.clang import ClangCompiler
from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler
-from .mixins.islinker import BasicLinkerIsCompilerMixin, LinkerEnvVarsMixin
+from .mixins.islinker import LinkerEnvVarsMixin
from .mixins.emscripten import EmscriptenMixin
from .compilers import (
gnu_winlibs,
@@ -139,7 +139,7 @@ class AppleClangCCompiler(ClangCCompiler):
_C18_VERSION = '>=11.0.0'
-class EmscriptenCCompiler(LinkerEnvVarsMixin, EmscriptenMixin, BasicLinkerIsCompilerMixin, ClangCCompiler):
+class EmscriptenCCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCCompiler):
def __init__(self, exelist, version, for_machine: MachineChoice,
is_cross: bool, info: 'MachineInfo', exe_wrapper=None, **kwargs):
if not is_cross:
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 26b34e3..2470bd5 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -36,7 +36,7 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
from .mixins.clang import ClangCompiler
from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler
-from .mixins.islinker import BasicLinkerIsCompilerMixin, LinkerEnvVarsMixin
+from .mixins.islinker import LinkerEnvVarsMixin
from .mixins.emscripten import EmscriptenMixin
if T.TYPE_CHECKING:
@@ -207,7 +207,7 @@ class AppleClangCPPCompiler(ClangCPPCompiler):
pass
-class EmscriptenCPPCompiler(LinkerEnvVarsMixin, EmscriptenMixin, BasicLinkerIsCompilerMixin, ClangCPPCompiler):
+class EmscriptenCPPCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCPPCompiler):
def __init__(self, exelist, version, for_machine: MachineChoice,
is_cross: bool, info: 'MachineInfo', exe_wrapper=None, **kwargs):
if not is_cross:
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index 945a67d..10f4b25 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -17,20 +17,13 @@
import os.path
import typing as T
-from ...mesonlib import MesonException
+from ... import coredata
-class EmscriptenMixin:
- def get_option_link_args(self, options):
- return []
-
- def get_soname_args(self, *args, **kwargs):
- raise MesonException('Emscripten does not support shared libraries.')
+if T.TYPE_CHECKING:
+ from ..environment import Environment
- def get_allow_undefined_link_args(self) -> T.List[str]:
- return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0']
- def get_linker_output_args(self, output: str) -> T.List[str]:
- return ['-o', output]
+class EmscriptenMixin:
def _get_compile_output(self, dirname, mode):
# In pre-processor mode, the output is sent to stdout and discarded
@@ -44,3 +37,17 @@ class EmscriptenMixin:
else:
suffix = 'wasm'
return os.path.join(dirname, 'output.' + suffix)
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return ['-s', 'USE_PTHREADS=1']
+
+ def get_options(self):
+ opts = super().get_options()
+ opts.update({
+ '{}_thread_count'.format(self.language): coredata.UserIntegerOption(
+ 'Number of threads to use in web assembly, set to 0 to disable',
+ (0, None, 4), # Default was picked at random
+ ),
+ })
+
+ return opts
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index 4e0af88..3092395 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -130,3 +130,12 @@ class BasicLinkerIsCompilerMixin:
def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
return []
+
+ def get_link_debugfile_name(self, target: str) -> str:
+ return ''
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ return []
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 5e60294..7fda3be 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -58,6 +58,7 @@ from .linkers import (
XilinkDynamicLinker,
CudaLinker,
VisualStudioLikeLinkerMixin,
+ WASMDynamicLinker,
)
from functools import lru_cache
from .compilers import (
@@ -963,9 +964,18 @@ class Environment:
if 'Emscripten' in out:
cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler
self.coredata.add_lang_args(cls.language, cls, for_machine, self)
+ # emcc cannot be queried to get the version out of it (it
+ # ignores -Wl,--version and doesn't have an alternative).
+ # Further, wasm-id *is* lld and will return `LLD X.Y.Z` if you
+ # call `wasm-ld --version`, but a special version of lld that
+ # takes different options.
+ p, o, _ = Popen_safe(['wasm-ld', '--version'])
+ linker = WASMDynamicLinker(
+ compiler, for_machine, cls.LINKER_PREFIX,
+ [], version=search_version(o))
return cls(
ccache + compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version)
+ exe_wrap, linker=linker, full_version=full_version)
if 'armclang' in out:
# The compiler version is not present in the first line of output,
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 3374d26..e8284bb 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1857,6 +1857,7 @@ class MesonMain(InterpreterObject):
'add_postconf_script': self.add_postconf_script_method,
'add_dist_script': self.add_dist_script_method,
'install_dependency_manifest': self.install_dependency_manifest_method,
+ 'override_dependency': self.override_dependency_method,
'override_find_program': self.override_find_program_method,
'project_version': self.project_version_method,
'project_license': self.project_license_method,
@@ -2013,6 +2014,29 @@ class MesonMain(InterpreterObject):
raise InterpreterException('Second argument must be an external program or executable.')
self.interpreter.add_find_program_override(name, exe)
+ @FeatureNew('meson.override_dependency', '0.53.0')
+ @permittedKwargs({'native'})
+ def override_dependency_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Override needs two arguments')
+ name = args[0]
+ dep = args[1]
+ if not isinstance(name, str) or not name:
+ raise InterpreterException('First argument must be a string and cannot be empty')
+ if hasattr(dep, 'held_object'):
+ dep = dep.held_object
+ if not isinstance(dep, dependencies.Dependency):
+ raise InterpreterException('Second argument must be a dependency object')
+ identifier = dependencies.get_dep_identifier(name, kwargs)
+ for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ override = self.build.dependency_overrides[for_machine].get(identifier)
+ if override:
+ m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}'
+ location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
+ raise InterpreterException(m.format(name, location))
+ self.build.dependency_overrides[for_machine][identifier] = \
+ build.DependencyOverride(dep, self.interpreter.current_node)
+
@noPosargs
@permittedKwargs({})
def project_version_method(self, args, kwargs):
@@ -3216,30 +3240,47 @@ external dependencies (including libraries) must go to "dependencies".''')
# Check if we want this as a build-time / build machine or runt-time /
# host machine dep.
for_machine = self.machine_from_native_kwarg(kwargs)
-
identifier = dependencies.get_dep_identifier(name, kwargs)
- cached_dep = self.coredata.deps[for_machine].get(identifier)
- if cached_dep:
+ wanted_vers = mesonlib.stringlistify(kwargs.get('version', []))
+
+ override = self.build.dependency_overrides[for_machine].get(identifier)
+ if override:
+ info = [mlog.blue('(overridden)' if override.explicit else '(cached)')]
+ cached_dep = override.dep
+ # We don't implicitly override not-found dependencies, but user could
+ # have explicitly called meson.override_dependency() with a not-found
+ # dep.
if not cached_dep.found():
mlog.log('Dependency', mlog.bold(name),
- 'found:', mlog.red('NO'), mlog.blue('(cached)'))
+ 'found:', mlog.red('NO'), *info)
return identifier, cached_dep
-
- # Verify the cached dep version match
- wanted_vers = mesonlib.stringlistify(kwargs.get('version', []))
found_vers = cached_dep.get_version()
- if not wanted_vers or mesonlib.version_compare_many(found_vers, wanted_vers)[0]:
- info = [mlog.blue('(cached)')]
- if found_vers:
- info = [mlog.normal_cyan(found_vers), *info]
+ if not self.check_version(wanted_vers, found_vers):
mlog.log('Dependency', mlog.bold(name),
- 'found:', mlog.green('YES'), *info)
- return identifier, cached_dep
+ 'found:', mlog.red('NO'),
+ 'found', mlog.normal_cyan(found_vers), 'but need:',
+ mlog.bold(', '.join(["'{}'".format(e) for e in wanted_vers])),
+ *info)
+ return identifier, NotFoundDependency(self.environment)
+ else:
+ info = [mlog.blue('(cached)')]
+ cached_dep = self.coredata.deps[for_machine].get(identifier)
+ if cached_dep:
+ found_vers = cached_dep.get_version()
+ if not self.check_version(wanted_vers, found_vers):
+ return identifier, None
+
+ if cached_dep:
+ if found_vers:
+ info = [mlog.normal_cyan(found_vers), *info]
+ mlog.log('Dependency', mlog.bold(name),
+ 'found:', mlog.green('YES'), *info)
+ return identifier, cached_dep
return identifier, None
@staticmethod
- def check_subproject_version(wanted, found):
+ def check_version(wanted, found):
if not wanted:
return True
if found == 'undefined' or not mesonlib.version_compare_many(found, wanted)[0]:
@@ -3249,11 +3290,35 @@ external dependencies (including libraries) must go to "dependencies".''')
def notfound_dependency(self):
return DependencyHolder(NotFoundDependency(self.environment), self.subproject)
- def get_subproject_dep(self, display_name, dirname, varname, kwargs):
+ def verify_fallback_consistency(self, dirname, varname, cached_dep):
+ subi = self.subprojects.get(dirname)
+ if not cached_dep or not varname or not subi or not cached_dep.found():
+ return
+ dep = subi.get_variable_method([varname], {})
+ if dep.held_object != cached_dep:
+ m = 'Inconsistency: Subproject has overriden the dependency with another variable than {!r}'
+ raise DependencyException(m.format(varname))
+
+ def get_subproject_dep(self, name, display_name, dirname, varname, kwargs):
+ required = kwargs.get('required', True)
+ wanted = mesonlib.stringlistify(kwargs.get('version', []))
+ subproj_path = os.path.join(self.subproject_dir, dirname)
dep = self.notfound_dependency()
try:
subproject = self.subprojects[dirname]
+ _, cached_dep = self._find_cached_dep(name, kwargs)
+ if varname is None:
+ # Assuming the subproject overriden the dependency we want
+ if cached_dep:
+ if required and not cached_dep.found():
+ m = 'Dependency {!r} is not satisfied'
+ raise DependencyException(m.format(display_name))
+ return DependencyHolder(cached_dep, self.subproject)
+ else:
+ m = 'Subproject {} did not override dependency {}'
+ raise DependencyException(m.format(subproj_path, display_name))
if subproject.found():
+ self.verify_fallback_consistency(dirname, varname, cached_dep)
dep = self.subprojects[dirname].get_variable_method([varname], {})
except InvalidArguments:
pass
@@ -3262,10 +3327,6 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InvalidCode('Fetched variable {!r} in the subproject {!r} is '
'not a dependency object.'.format(varname, dirname))
- required = kwargs.get('required', True)
- wanted = mesonlib.stringlistify(kwargs.get('version', []))
- subproj_path = os.path.join(self.subproject_dir, dirname)
-
if not dep.found():
if required:
raise DependencyException('Could not find dependency {} in subproject {}'
@@ -3276,7 +3337,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return dep
found = dep.held_object.get_version()
- if not self.check_subproject_version(wanted, found):
+ if not self.check_version(wanted, found):
if required:
raise DependencyException('Version {} of subproject dependency {} already '
'cached, requested incompatible version {} for '
@@ -3328,6 +3389,15 @@ external dependencies (including libraries) must go to "dependencies".''')
raise
if not d.found() and not_found_message:
self.message_impl([not_found_message])
+ self.message_impl([not_found_message])
+ # Override this dependency to have consistent results in subsequent
+ # dependency lookups.
+ if name and d.found():
+ for_machine = self.machine_from_native_kwarg(kwargs)
+ identifier = dependencies.get_dep_identifier(name, kwargs)
+ if identifier not in self.build.dependency_overrides[for_machine]:
+ self.build.dependency_overrides[for_machine][identifier] = \
+ build.DependencyOverride(d.held_object, node, explicit=False)
return d
def dependency_impl(self, name, display_name, kwargs):
@@ -3351,6 +3421,9 @@ external dependencies (including libraries) must go to "dependencies".''')
identifier, cached_dep = self._find_cached_dep(name, kwargs)
if cached_dep:
+ if has_fallback:
+ dirname, varname = self.get_subproject_infos(kwargs)
+ self.verify_fallback_consistency(dirname, varname, cached_dep)
if required and not cached_dep.found():
m = 'Dependency {!r} was already checked and was not found'
raise DependencyException(m.format(display_name))
@@ -3361,7 +3434,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if has_fallback:
dirname, varname = self.get_subproject_infos(kwargs)
if dirname in self.subprojects:
- return self.get_subproject_dep(name, dirname, varname, kwargs)
+ return self.get_subproject_dep(name, display_name, dirname, varname, kwargs)
wrap_mode = self.coredata.get_builtin_option('wrap_mode')
forcefallback = wrap_mode == WrapMode.forcefallback and has_fallback
@@ -3369,7 +3442,6 @@ external dependencies (including libraries) must go to "dependencies".''')
self._handle_featurenew_dependencies(name)
kwargs['required'] = required and not has_fallback
dep = dependencies.find_external_dependency(name, self.environment, kwargs)
-
kwargs['required'] = required
# Only store found-deps in the cache
# Never add fallback deps to self.coredata.deps since we
@@ -3381,7 +3453,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return DependencyHolder(dep, self.subproject)
if has_fallback:
- return self.dependency_fallback(display_name, kwargs)
+ return self.dependency_fallback(name, display_name, kwargs)
return self.notfound_dependency()
@@ -3409,13 +3481,15 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.warning(*message, location=self.current_node)
def get_subproject_infos(self, kwargs):
- fbinfo = kwargs['fallback']
- check_stringlist(fbinfo)
- if len(fbinfo) != 2:
- raise InterpreterException('Fallback info must have exactly two items.')
+ fbinfo = mesonlib.stringlistify(kwargs['fallback'])
+ if len(fbinfo) == 1:
+ FeatureNew('Fallback without variable name', '0.53.0').use(self.subproject)
+ return fbinfo[0], None
+ elif len(fbinfo) != 2:
+ raise InterpreterException('Fallback info must have one or two items.')
return fbinfo
- def dependency_fallback(self, display_name, kwargs):
+ def dependency_fallback(self, name, display_name, kwargs):
required = kwargs.get('required', True)
if self.coredata.get_builtin_option('wrap_mode') == WrapMode.nofallback:
mlog.log('Not looking for a fallback subproject for the dependency',
@@ -3437,7 +3511,7 @@ external dependencies (including libraries) must go to "dependencies".''')
'required': required,
}
self.do_subproject(dirname, 'meson', sp_kwargs)
- return self.get_subproject_dep(display_name, dirname, varname, kwargs)
+ return self.get_subproject_dep(name, display_name, dirname, varname, kwargs)
@FeatureNewKwargs('executable', '0.42.0', ['implib'])
@permittedKwargs(permitted_kwargs['executable'])
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index 489525b..73cdeef 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -693,6 +693,35 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna
return []
+class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker):
+
+ """Emscripten's wasm-ld."""
+
+ def __init__(self, *args, **kwargs):
+ super().__init__('ld.wasm', *args, **kwargs)
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ args = ['-s', 'USE_PTHREADS=1']
+ count = env.coredata.compiler_options[self.for_machine]['{}_thread_count'.format(self.language)].value # type: int
+ if count:
+ args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)])
+ return args
+
+ def get_allow_undefined_args(self) -> T.List[str]:
+ return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0']
+
+ def no_undefined_args(self) -> T.List[str]:
+ return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1']
+
+ def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ suffix: str, soversion: str, darwin_versions: T.Tuple[str, str],
+ is_shared_module: bool) -> T.List[str]:
+ raise mesonlib.MesonException('{} does not support shared libraries.'.format(self.id))
+
+ def get_asneeded_args(self) -> T.List[str]:
+ return []
+
+
class CcrxDynamicLinker(DynamicLinker):
"""Linker for Renesis CCrx compiler."""
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index cca4372..9278584 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -95,6 +95,9 @@ def list_installed(installdata):
for t in installdata.targets:
res[os.path.join(installdata.build_dir, t.fname)] = \
os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
+ for alias in t.aliases.keys():
+ res[os.path.join(installdata.build_dir, alias)] = \
+ os.path.join(installdata.prefix, t.outdir, os.path.basename(alias))
for path, installpath, _ in installdata.data:
res[path] = os.path.join(installdata.prefix, installpath)
for path, installdir, _ in installdata.headers:
@@ -154,8 +157,9 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
# Fast lookup table for installation files
install_lookuptable = {}
for i in installdata.targets:
- outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname))
- install_lookuptable[os.path.basename(i.fname)] = str(PurePath(outname))
+ out = [os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname))]
+ out += [os.path.join(installdata.prefix, i.outdir, os.path.basename(x)) for x in i.aliases]
+ install_lookuptable[os.path.basename(i.fname)] = [str(PurePath(x)) for x in out]
for (idname, target) in builddata.get_targets().items():
if not isinstance(target, build.Target):
@@ -174,7 +178,8 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
if installdata and target.should_install():
t['installed'] = True
- t['install_filename'] = [install_lookuptable.get(x, None) for x in target.get_outputs()]
+ t['install_filename'] = [install_lookuptable.get(x, [None]) for x in target.get_outputs()]
+ t['install_filename'] = [x for sublist in t['install_filename'] for x in sublist] # flatten the list
else:
t['installed'] = False
tlist.append(t)
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 56a1406..c7da530 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -19,7 +19,8 @@ from ..mesonlib import MesonException, Popen_safe, extract_as_list, File, unhold
from ..dependencies import Dependency, Qt4Dependency, Qt5Dependency
import xml.etree.ElementTree as ET
from . import ModuleReturnValue, get_include_args, ExtensionModule
-from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs
+from ..interpreterbase import noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs
+from ..interpreter import extract_required_kwarg
_QT_DEPS_LUT = {
4: Qt4Dependency,
@@ -32,6 +33,7 @@ class QtBaseModule(ExtensionModule):
def __init__(self, interpreter, qt_version=5):
ExtensionModule.__init__(self, interpreter)
+ self.snippets.add('has_tools')
self.qt_version = qt_version
def _detect_tools(self, env, method):
@@ -117,6 +119,23 @@ class QtBaseModule(ExtensionModule):
except Exception:
return []
+ @noPosargs
+ @permittedKwargs({'method', 'required'})
+ @FeatureNew('qt.has_tools', '0.54.0')
+ def has_tools(self, interpreter, state, args, kwargs):
+ method = kwargs.get('method', 'auto')
+ disabled, required, feature = extract_required_kwarg(kwargs, state.subproject, default=False)
+ if disabled:
+ mlog.log('qt.has_tools skipped: feature', mlog.bold(feature), 'disabled')
+ return False
+ self._detect_tools(state.environment, method)
+ for tool in (self.moc, self.uic, self.rcc, self.lrelease):
+ if not tool.found():
+ if required:
+ raise MesonException('Qt tools not found')
+ return False
+ return True
+
@FeatureNewKwargs('qt.preprocess', '0.49.0', ['uic_extra_arguments'])
@FeatureNewKwargs('qt.preprocess', '0.44.0', ['moc_extra_arguments'])
@FeatureNewKwargs('qt.preprocess', '0.49.0', ['rcc_extra_arguments'])
diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py
index f9dd442..2739c9d 100644
--- a/mesonbuild/templates/mesontemplates.py
+++ b/mesonbuild/templates/mesontemplates.py
@@ -21,6 +21,18 @@ executable('{executable}',
install : true)
'''
+
+meson_jar_template = '''project('{project_name}', '{language}',
+ version : '{version}',
+ default_options : [{default_options}])
+
+jar('{executable}',
+ {sourcespec},{depspec},
+ main_class: {main_class},
+ install : true)
+'''
+
+
def create_meson_build(options):
if options.type != 'executable':
raise SystemExit('\nGenerating a meson.build file from existing sources is\n'
@@ -40,12 +52,22 @@ def create_meson_build(options):
depspec += ',\n '.join("dependency('{}')".format(x)
for x in options.deps.split(','))
depspec += '],'
- content = meson_executable_template.format(project_name=options.name,
- language=options.language,
- version=options.version,
- executable=options.executable,
- sourcespec=sourcespec,
- depspec=depspec,
- default_options=formatted_default_options)
+ if options.language != 'java':
+ content = meson_executable_template.format(project_name=options.name,
+ language=options.language,
+ version=options.version,
+ executable=options.executable,
+ sourcespec=sourcespec,
+ depspec=depspec,
+ default_options=formatted_default_options)
+ else:
+ content = meson_jar_template.format(project_name=options.name,
+ language=options.language,
+ version=options.version,
+ executable=options.executable,
+ main_class=options.name,
+ sourcespec=sourcespec,
+ depspec=depspec,
+ default_options=formatted_default_options)
open('meson.build', 'w').write(content)
print('Generated meson.build file:\n\n' + content)
diff --git a/run_unittests.py b/run_unittests.py
index adfd927..c9b7563 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1470,6 +1470,7 @@ class BasePlatformTests(unittest.TestCase):
self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks')
self.unit_test_dir = os.path.join(src_root, 'test cases/unit')
self.rewrite_test_dir = os.path.join(src_root, 'test cases/rewrite')
+ self.linuxlike_test_dir = os.path.join(src_root, 'test cases/linuxlike')
# Misc stuff
self.orig_env = os.environ.copy()
if self.backend is Backend.ninja:
@@ -3312,6 +3313,12 @@ int main(int argc, char **argv) {
except EnvironmentException:
pass
# FIXME: omitting rust as Windows AppVeyor CI finds Rust but doesn't link correctly
+ if not is_windows():
+ try:
+ env.detect_rust_compiler(MachineChoice.HOST)
+ langs.append('rust')
+ except EnvironmentException:
+ pass
for lang in langs:
for target_type in ('executable', 'library'):
@@ -3324,11 +3331,16 @@ int main(int argc, char **argv) {
self._run(ninja,
workdir=os.path.join(tmpdir, 'builddir'))
# test directory with existing code file
- if lang in ('c', 'cpp'):
+ if lang in ('c', 'cpp', 'd'):
with tempfile.TemporaryDirectory() as tmpdir:
with open(os.path.join(tmpdir, 'foo.' + lang), 'w') as f:
f.write('int main(void) {}')
self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
+ elif lang in ('java'):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ with open(os.path.join(tmpdir, 'Foo.' + lang), 'w') as f:
+ f.write('public class Foo { public static void main() {} }')
+ self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
# The test uses mocking and thus requires that
# the current process is the one to run the Meson steps.
@@ -3984,11 +3996,17 @@ recommended as it is not supported on some platforms''')
{
'descriptive_name': 'sub',
'name': 'sub',
- 'version': 'undefined'
- }
+ 'version': '1.0'
+ },
+ {
+ 'descriptive_name': 'sub-novar',
+ 'name': 'sub_novar',
+ 'version': '1.0',
+ },
]
}
- self.assertDictEqual(res, expected)
+ res['subprojects'] = sorted(res['subprojects'], key=lambda i: i['name'])
+ self.assertDictEqual(expected, res)
def test_introspection_target_subproject(self):
testdir = os.path.join(self.common_test_dir, '45 subproject')
@@ -4559,7 +4577,7 @@ class FailureTests(BasePlatformTests):
raise unittest.SkipTest('zlib not found with pkg-config')
a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
- ("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"),
+ ("dependency('zlib', version : 1)", "Item must be a list or one of <class 'str'>"),
("dependency('zlib', required : 1)", "[Rr]equired.*boolean"),
("dependency('zlib', method : 1)", "[Mm]ethod.*string"),
("dependency('zlibfail')", self.dnf),)
@@ -4776,6 +4794,17 @@ class FailureTests(BasePlatformTests):
self.assertMesonOutputs("warning('Array:', ['a', 'b'])",
r"WARNING:.* Array: \['a', 'b'\]")
+ def test_override_dependency_twice(self):
+ self.assertMesonRaises("meson.override_dependency('foo', declare_dependency())\n" +
+ "meson.override_dependency('foo', declare_dependency())",
+ """Tried to override dependency 'foo' which has already been resolved or overridden""")
+
+ @unittest.skipIf(is_windows(), 'zlib is not available on Windows')
+ def test_override_resolved_dependency(self):
+ self.assertMesonRaises("dependency('zlib')\n" +
+ "meson.override_dependency('zlib', declare_dependency())",
+ """Tried to override dependency 'zlib' which has already been resolved or overridden""")
+
@unittest.skipUnless(is_windows() or is_cygwin(), "requires Windows (or Windows via Cygwin)")
class WindowsTests(BasePlatformTests):
'''
@@ -5757,6 +5786,31 @@ class LinuxlikeTests(BasePlatformTests):
self.assertIsInstance(docbook_target, dict)
self.assertEqual(os.path.basename(t['filename'][0]), 'generated-gdbus-doc-' + os.path.basename(t['target_sources'][0]['sources'][0]))
+ def test_introspect_installed(self):
+ testdir = os.path.join(self.linuxlike_test_dir, '7 library versions')
+ self.init(testdir)
+
+ install = self.introspect('--installed')
+ install = {os.path.basename(k): v for k, v in install.items()}
+ self.assertDictEqual(install, {
+ 'libmodule.so': '/usr/lib/libmodule.so',
+ 'libnoversion.so': '/usr/lib/libnoversion.so',
+ 'libonlysoversion.so': '/usr/lib/libonlysoversion.so',
+ 'libonlysoversion.so.5': '/usr/lib/libonlysoversion.so.5',
+ 'libonlyversion.so': '/usr/lib/libonlyversion.so',
+ 'libonlyversion.so.1': '/usr/lib/libonlyversion.so.1',
+ 'libonlyversion.so.1.4.5': '/usr/lib/libonlyversion.so.1.4.5',
+ 'libsome.so': '/usr/lib/libsome.so',
+ 'libsome.so.0': '/usr/lib/libsome.so.0',
+ 'libsome.so.1.2.3': '/usr/lib/libsome.so.1.2.3',
+ })
+
+ targets = self.introspect('--targets')
+ for t in targets:
+ if t['name'] != 'some':
+ continue
+ self.assertSetEqual({'/usr/lib/libsome.so', '/usr/lib/libsome.so.0', '/usr/lib/libsome.so.1.2.3'}, set(t['install_filename']))
+
def test_build_rpath(self):
if is_cygwin():
raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
diff --git a/test cases/cmake/3 advanced no dep/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/3 advanced no dep/subprojects/cmMod/CMakeLists.txt
index 38c1fff..4c782cb 100644
--- a/test cases/cmake/3 advanced no dep/subprojects/cmMod/CMakeLists.txt
+++ b/test cases/cmake/3 advanced no dep/subprojects/cmMod/CMakeLists.txt
@@ -16,9 +16,11 @@ generate_export_header(cmModLib)
set_target_properties(cmModLib PROPERTIES VERSION 1.0.1)
add_executable(testEXE main.cpp)
+add_executable(testEXE2 main.cpp)
target_link_libraries(testEXE cmModLib)
+target_link_libraries(testEXE2 cmModLib)
target_compile_definitions(cmModLibStatic PUBLIC CMMODLIB_STATIC_DEFINE)
-install(TARGETS cmModLib testEXE LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
+install(TARGETS cmModLib testEXE testEXE2 LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
diff --git a/test cases/cmake/3 advanced no dep/test.json b/test cases/cmake/3 advanced no dep/test.json
index 3b8b12d..24c89c4 100644
--- a/test cases/cmake/3 advanced no dep/test.json
+++ b/test cases/cmake/3 advanced no dep/test.json
@@ -5,6 +5,8 @@
{"type": "implib", "platform": "!cygwin", "file": "usr/bin/libcm_cmModLib"},
{"type": "pdb", "file": "usr/bin/cm_cmModLib"},
{"type": "pdb", "file": "usr/bin/cm_testEXE"},
- {"type": "exe", "file": "usr/bin/cm_testEXE"}
+ {"type": "exe", "file": "usr/bin/cm_testEXE"},
+ {"type": "pdb", "file": "usr/bin/cm_testEXE2"},
+ {"type": "exe", "file": "usr/bin/cm_testEXE2"}
]
}
diff --git a/test cases/common/102 subproject subdir/meson.build b/test cases/common/102 subproject subdir/meson.build
index ec9fad1..8299a37 100644
--- a/test cases/common/102 subproject subdir/meson.build
+++ b/test cases/common/102 subproject subdir/meson.build
@@ -4,3 +4,24 @@ libSub = dependency('sub', fallback: ['sub', 'libSub'])
exe = executable('prog', 'prog.c', dependencies: libSub)
test('subproject subdir', exe)
+
+# Verify the subproject has placed dependency override.
+dependency('sub-1.0')
+
+# Verify we can now take 'sub' dependency without fallback, but only version 1.0.
+dependency('sub')
+d = dependency('sub', version : '>=2.0', required : false)
+assert(not d.found(), 'version should not match')
+
+# Verify that not-found does not get cached, we can still fallback afterward.
+dependency('sub2', required : false)
+d = dependency('sub2', fallback: ['sub', 'libSub'])
+assert(d.found(), 'Should fallback even if a previous call returned not-found')
+
+# Verify we can get a fallback dependency without specifying the variable name,
+# because the subproject overridden 'sub-novar'.
+dependency('sub-novar', fallback : 'sub_novar')
+
+# Verify a subproject can force a dependency to be not-found
+d = dependency('sub-notfound', fallback : 'sub_novar', required : false)
+assert(not d.found(), 'Dependency should be not-found')
diff --git a/test cases/common/102 subproject subdir/subprojects/sub/lib/meson.build b/test cases/common/102 subproject subdir/subprojects/sub/lib/meson.build
index 731d22b..53233ab 100644
--- a/test cases/common/102 subproject subdir/subprojects/sub/lib/meson.build
+++ b/test cases/common/102 subproject subdir/subprojects/sub/lib/meson.build
@@ -1,2 +1,3 @@
lib = static_library('sub', 'sub.c')
libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib)
+meson.override_dependency('sub-1.0', libSub)
diff --git a/test cases/common/102 subproject subdir/subprojects/sub/meson.build b/test cases/common/102 subproject subdir/subprojects/sub/meson.build
index bf69c25..d8c4dce 100644
--- a/test cases/common/102 subproject subdir/subprojects/sub/meson.build
+++ b/test cases/common/102 subproject subdir/subprojects/sub/meson.build
@@ -1,2 +1,2 @@
-project('sub', 'c')
+project('sub', 'c', version : '1.0')
subdir('lib')
diff --git a/test cases/common/102 subproject subdir/subprojects/sub_novar/meson.build b/test cases/common/102 subproject subdir/subprojects/sub_novar/meson.build
new file mode 100644
index 0000000..6450a10
--- /dev/null
+++ b/test cases/common/102 subproject subdir/subprojects/sub_novar/meson.build
@@ -0,0 +1,4 @@
+project('sub-novar', 'c', version : '1.0')
+
+meson.override_dependency('sub-novar', declare_dependency())
+meson.override_dependency('sub-notfound', dependency('', required : false))
diff --git a/test cases/failing/100 fallback consistency/meson.build b/test cases/failing/100 fallback consistency/meson.build
new file mode 100644
index 0000000..ad2b226
--- /dev/null
+++ b/test cases/failing/100 fallback consistency/meson.build
@@ -0,0 +1,7 @@
+project('proj', 'c')
+
+# The first call succeed and cache the value of 'sub' dependency. The 2nd call
+# should return the cached value, but still verify the fallback variable is
+# consistent.
+dependency('sub', fallback : ['sub', 'dep1'])
+dependency('sub', fallback : ['sub', 'dep2'])
diff --git a/test cases/failing/100 fallback consistency/subprojects/sub/meson.build b/test cases/failing/100 fallback consistency/subprojects/sub/meson.build
new file mode 100644
index 0000000..12a6570
--- /dev/null
+++ b/test cases/failing/100 fallback consistency/subprojects/sub/meson.build
@@ -0,0 +1,5 @@
+project('proj', 'c')
+
+dep1 = declare_dependency()
+dep2 = declare_dependency()
+meson.override_dependency('sub', dep1)
diff --git a/test cases/failing/99 fallback consistency/meson.build b/test cases/failing/99 fallback consistency/meson.build
new file mode 100644
index 0000000..362459c
--- /dev/null
+++ b/test cases/failing/99 fallback consistency/meson.build
@@ -0,0 +1,4 @@
+project('proj', 'c')
+
+# Subproject overrides 'sub' with another variable than dep2. This should fail.
+dependency('sub', fallback : ['sub', 'dep2'])
diff --git a/test cases/failing/99 fallback consistency/subprojects/sub/meson.build b/test cases/failing/99 fallback consistency/subprojects/sub/meson.build
new file mode 100644
index 0000000..12a6570
--- /dev/null
+++ b/test cases/failing/99 fallback consistency/subprojects/sub/meson.build
@@ -0,0 +1,5 @@
+project('proj', 'c')
+
+dep1 = declare_dependency()
+dep2 = declare_dependency()
+meson.override_dependency('sub', dep1)
diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build
index 7934572..8f18809 100644
--- a/test cases/frameworks/4 qt/meson.build
+++ b/test cases/frameworks/4 qt/meson.build
@@ -42,6 +42,7 @@ foreach qt : ['qt4', 'qt5']
qtdep = dependency(qt, modules : qt_modules, main : true, private_headers: true, required : required, method : get_option('method'))
if qtdep.found()
qtmodule = import(qt)
+ assert(qtmodule.has_tools())
# The following has two resource files because having two in one target
# requires you to do it properly or you get linker symbol clashes.
diff --git a/test cases/linuxlike/5 dependency versions/meson.build b/test cases/linuxlike/5 dependency versions/meson.build
index 087db5f..94f424d 100644
--- a/test cases/linuxlike/5 dependency versions/meson.build
+++ b/test cases/linuxlike/5 dependency versions/meson.build
@@ -38,32 +38,32 @@ somelibver = dependency('somelib',
fallback : ['somelibnover', 'some_dep'])
assert(somelibver.type_name() == 'internal', 'somelibver should be of type "internal", not ' + somelibver.type_name())
# Find an internal dependency again with the same name and a specific version
-somelib = dependency('somelib',
+somelib = dependency('somelib2',
version : '== 0.1',
fallback : ['somelib', 'some_dep'])
# Find an internal dependency again even if required = false
-somelib_reqfalse = dependency('somelib',
+somelib_reqfalse = dependency('somelib3',
required: false,
fallback : ['somelib', 'some_dep'])
assert(somelib_reqfalse.found(), 'somelib should have been found')
# Find an internal dependency again with the same name and incompatible version
-somelibver = dependency('somelib',
+somelibver = dependency('somelib4',
version : '>= 0.3',
fallback : ['somelibver', 'some_dep'])
# Find an internal dependency again with impossible multi-version
-somelibver = dependency('somelib',
+somelibver = dependency('somelib5',
version : ['>= 0.3', '<0.3'],
required : false,
fallback : ['somelibver', 'some_dep'])
assert(not somelibver.found(), 'Dependency should not be found')
# Find somelib again, but with a fallback that will fail because subproject does not exist
-somelibfail = dependency('somelib',
+somelibfail = dependency('somelib6',
version : '>= 0.2',
required : false,
fallback : ['somelibfail', 'some_dep'])
assert(somelibfail.found() == false, 'somelibfail found via wrong fallback')
# Find somelib again, but with a fallback that will fail because dependency does not exist
-somefail_dep = dependency('somelib',
+somefail_dep = dependency('somelib7',
version : '>= 0.2',
required : false,
fallback : ['somelib', 'somefail_dep'])
@@ -71,14 +71,21 @@ assert(somefail_dep.found() == false, 'somefail_dep found via wrong fallback')
# Fallback should only be used if the primary was not found
fallbackzlib_dep = dependency('zlib',
- fallback : ['somelib', 'fakezlib_dep'])
+ fallback : ['fakezlib', 'fakezlib_dep'])
assert(fallbackzlib_dep.type_name() == 'pkgconfig', 'fallbackzlib_dep should be of type "pkgconfig", not ' + fallbackzlib_dep.type_name())
# Check that the above dependency was pkgconfig because the fallback wasn't
# checked, not because the fallback didn't work
fakezlib_dep = dependency('fakezlib',
- fallback : ['somelib', 'fakezlib_dep'])
+ fallback : ['fakezlib', 'fakezlib_dep'])
assert(fakezlib_dep.type_name() == 'internal', 'fakezlib_dep should be of type "internal", not ' + fakezlib_dep.type_name())
+# Verify that once we got a system dependency, we won't fallback if a newer
+# version is requested.
+d = dependency('zlib', version: '>= 999',
+ fallback : ['donotexist', 'fakezlib_dep'],
+ required: false)
+assert(not d.found(), 'version should not match and it should not fallback')
+
# Check that you can find a dependency by not specifying a version after not
# finding it by specifying a version. We add `static: true` here so that the
# previously cached zlib dependencies don't get checked.
diff --git a/test cases/linuxlike/5 dependency versions/subprojects/fakezlib/meson.build b/test cases/linuxlike/5 dependency versions/subprojects/fakezlib/meson.build
new file mode 100644
index 0000000..5a07763
--- /dev/null
+++ b/test cases/linuxlike/5 dependency versions/subprojects/fakezlib/meson.build
@@ -0,0 +1,3 @@
+project('some', 'c', version : '0.1')
+
+fakezlib_dep = declare_dependency()
diff --git a/test cases/unit/57 introspection/meson.build b/test cases/unit/57 introspection/meson.build
index a094a55..9716eae 100644
--- a/test cases/unit/57 introspection/meson.build
+++ b/test cases/unit/57 introspection/meson.build
@@ -8,6 +8,7 @@ b1 = get_option('test_opt1')
b2 = get_option('test_opt2')
test_bool = b1 or b2
test_bool = b1 and b2
+test_bool = not test_bool
set_variable('list_test_plusassign', [])
list_test_plusassign += ['bugs everywhere']
diff --git a/test cases/wasm/1 basic/hello.c b/test cases/wasm/1 basic/hello.c
new file mode 100644
index 0000000..a104b37
--- /dev/null
+++ b/test cases/wasm/1 basic/hello.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main() {
+ printf("Hello World\n");
+ return 0;
+}
+
diff --git a/test cases/wasm/1 basic/meson.build b/test cases/wasm/1 basic/meson.build
index 8fe713c..1092a9b 100644
--- a/test cases/wasm/1 basic/meson.build
+++ b/test cases/wasm/1 basic/meson.build
@@ -1,3 +1,4 @@
-project('emcctest', 'cpp')
+project('emcctest', 'c', 'cpp')
+executable('hello-c', 'hello.c')
executable('hello', 'hello.cpp')
diff --git a/test cases/wasm/2 threads/meson.build b/test cases/wasm/2 threads/meson.build
new file mode 100644
index 0000000..cb682b8
--- /dev/null
+++ b/test cases/wasm/2 threads/meson.build
@@ -0,0 +1,10 @@
+project(
+ 'threads',
+ 'c', 'cpp',
+ default_options : ['cpp_std=c++11'],
+)
+
+dep_threads = dependency('threads')
+
+executable('threads-c', 'threads.c', dependencies : dep_threads)
+executable('threads-c++', 'threads.cpp', dependencies : dep_threads)
diff --git a/test cases/wasm/2 threads/threads.c b/test cases/wasm/2 threads/threads.c
new file mode 100644
index 0000000..e79bff1
--- /dev/null
+++ b/test cases/wasm/2 threads/threads.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+void inthread(void * args) {
+ sleep(1);
+ printf("In thread\n");
+}
+
+int main() {
+#ifdef __EMSCRIPTEN_PTHREADS__
+ pthread_t thread_id;
+ printf("Before Thread\n");
+ pthread_create(&thread_id, NULL, (void *)*inthread, NULL);
+ pthread_join(thread_id, NULL);
+ printf("After Thread\n");
+ return 0;
+#else
+# error "threads not enabled\n"
+#endif
+}
diff --git a/test cases/wasm/2 threads/threads.cpp b/test cases/wasm/2 threads/threads.cpp
new file mode 100644
index 0000000..1caa73d
--- /dev/null
+++ b/test cases/wasm/2 threads/threads.cpp
@@ -0,0 +1,13 @@
+#include <unistd.h>
+#include <iostream>
+#include <thread>
+
+int main(void) {
+ std::cout << "Before thread" << std::endl;
+ std::thread t([]() {
+ sleep(1);
+ std::cout << "In a thread." << std::endl;
+ });
+ t.join();
+ std::cout << "After thread" << std::endl;
+}