diff options
43 files changed, 587 insertions, 93 deletions
@@ -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; +} |