diff options
96 files changed, 1558 insertions, 510 deletions
diff --git a/.travis.yml b/.travis.yml index bd8d48c..7658fa0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,13 +46,13 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" && "$MESON_ARGS" =~ .*unity=on.* ]]; then brew install pkg-config; fi # Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir -p $HOME/tools; curl -L http://nirbheek.in/files/binaries/ninja/macos/ninja -o $HOME/tools/ninja; chmod +x $HOME/tools/ninja; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:bionic; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:cosmic; fi # We need to copy the current checkout inside the Docker container, # because it has the MR id to be tested checked out. script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM jpakkane/mesonci:bionic > Dockerfile; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM jpakkane/mesonci:cosmic > Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo ADD . /root >> Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build -t withgit .; fi - | @@ -8,7 +8,6 @@ build system. [](https://pypi.python.org/pypi/meson) [](https://travis-ci.org/mesonbuild/meson) -[](https://ci.appveyor.com/project/mesonbuild/meson) [](https://dev.azure.com/jussi0947/jussi/_build/latest?definitionId=1) [](https://codecov.io/gh/mesonbuild/meson/branch/master) [](https://lgtm.com/projects/g/mesonbuild/meson/context:python) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5b2447e..39e41e9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -100,7 +100,7 @@ jobs: testResultsFiles: meson-test-run.xml testRunTitle: $(System.JobName) -- job: msys2_mingw +- job: msys2 pool: vmImage: VS2017-Win2016 strategy: @@ -108,9 +108,15 @@ jobs: gccx86ninja: MSYSTEM: MINGW32 MSYS2_ARCH: i686 + compiler: gcc gccx64ninja: MSYSTEM: MINGW64 MSYS2_ARCH: x86_64 + compiler: gcc + clangx64ninja: + MSYSTEM: MINGW64 + MSYS2_ARCH: x86_64 + compiler: clang variables: MSYS2_ROOT: $(System.Workfolder)\msys64 steps: @@ -124,20 +130,23 @@ jobs: displayName: Update MSYS2 - script: | set PATH=%MSYS2_ROOT%\usr\bin;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem + if %compiler%==gcc ( set "TOOLCHAIN=mingw-w64-$(MSYS2_ARCH)-toolchain" ) else ( set "TOOLCHAIN=mingw-w64-$(MSYS2_ARCH)-clang" ) %MSYS2_ROOT%\usr\bin\pacman --noconfirm --needed -S ^ base-devel ^ git ^ mercurial ^ mingw-w64-$(MSYS2_ARCH)-cmake ^ + mingw-w64-$(MSYS2_ARCH)-pkg-config ^ mingw-w64-$(MSYS2_ARCH)-python2 ^ mingw-w64-$(MSYS2_ARCH)-python3 ^ mingw-w64-$(MSYS2_ARCH)-python3-setuptools ^ - mingw-w64-$(MSYS2_ARCH)-toolchain + %TOOLCHAIN% displayName: Install Dependencies - script: | set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem %MSYS2_ROOT%\usr\bin\bash -lc "wget https://github.com/mesonbuild/cidata/raw/master/ninja.exe; mv ninja.exe /$MSYSTEM/bin" set PATHEXT=%PATHEXT%;.py + if %compiler%==clang ( set CC=clang && set CXX=clang++ ) %MSYS2_ROOT%\usr\bin\bash -lc "MSYSTEM= python3 run_tests.py --backend=ninja" env: CHERE_INVOKING: yes diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile index ac59ca9..520ce0f 100644 --- a/ciimage/Dockerfile +++ b/ciimage/Dockerfile @@ -1,27 +1,28 @@ -FROM ubuntu:bionic +FROM ubuntu:cosmic ENV DEBIAN_FRONTEND noninteractive +ENV LANG='C.UTF-8' +ENV DC=gdc -RUN apt-get -y update && apt-get -y upgrade \ -&& apt-get -y install wget unzip \ +RUN sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" \ +&& apt-get -y update && apt-get -y upgrade \ && apt-get -y build-dep meson \ -&& apt-get -y install qt5-default qtbase5-private-dev clang \ -&& apt-get -y install pkg-config-arm-linux-gnueabihf \ -&& apt-get -y install doxygen \ && apt-get -y install python3-pip libxml2-dev libxslt1-dev cmake libyaml-dev \ +&& python3 -m pip install hotdoc codecov \ +&& apt-get -y install wget unzip \ +&& apt-get -y install qt5-default clang \ +&& apt-get -y install pkg-config-arm-linux-gnueabihf \ && apt-get -y install qt4-linguist-tools \ && apt-get -y install python-dev \ -&& apt-get -y install libomp-dev openssh-client \ -&& apt-get -y install clang libclang-dev llvm-dev flex \ +&& apt-get -y install libomp-dev \ +&& apt-get -y install dub ldc \ +&& apt-get -y install mingw-w64 mingw-w64-tools nim \ +&& apt-get -y install --no-install-recommends wine-stable \ +&& apt-get -y install llvm-dev libclang-dev \ && apt-get -y install libgcrypt11-dev \ -&& apt-get -y install gdc ldc \ -&& python3 -m pip install hotdoc codecov \ -&& dub fetch urld \ -&& dub build urld --compiler=gdc \ +&& dub fetch urld && dub build urld --compiler=gdc \ && dub fetch dubtestproject \ && dub build dubtestproject:test1 --compiler=ldc2 \ && dub build dubtestproject:test2 --compiler=ldc2 - # OpenSSH client is needed to run openmpi binaries. -ENV LANG='C.UTF-8' diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index ce14304..3ab2380 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -12,7 +12,8 @@ universal options, base options, compiler options. A list of these options can be found by running `meson --help`. All these can be set by passing to `meson` (aka `meson setup`) in any of -these ways: `--option=value`, `--option value`, `-Doption=value`. +these ways: `--option=value`, `--option value`, `-Doption=value`, or +by setting them inside `default_options` of `project()` in your `meson.build`. They can also be edited after setup using `meson configure`. diff --git a/docs/markdown/Native-environments.md b/docs/markdown/Native-environments.md index af7edd2..a9719a7 100644 --- a/docs/markdown/Native-environments.md +++ b/docs/markdown/Native-environments.md @@ -40,7 +40,7 @@ like `llvm-config` c = '/usr/local/bin/clang' cpp = '/usr/local/bin/clang++' rust = '/usr/local/bin/rust' -llvm-conifg = '/usr/local/llvm-svn/bin/llvm-config' +llvm-config = '/usr/local/llvm-svn/bin/llvm-config' ``` ## Loading multiple native files diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md index 6aee0c7..7e93524 100644 --- a/docs/markdown/Pkgconfig-module.md +++ b/docs/markdown/Pkgconfig-module.md @@ -29,11 +29,11 @@ keyword arguments. shared_library) that the user needs to link against. Arbitrary strings can also be provided and they will be added into the `Libs` field. Since 0.45.0 dependencies of built libraries will be automatically added, see the - [Implicit dependencies](#Implicit_dependencies) section below for the exact + [Implicit dependencies](#implicit-dependencies) section below for the exact rules. - `libraries_private` list of built libraries or strings to put in the `Libs.private` field. Since 0.45.0 dependencies of built libraries will be - automatically added, see the [Implicit dependencies](#Implicit_dependencies) + automatically added, see the [Implicit dependencies](#implicit-dependencies) section below for the exact rules. - `name` the name of this library, used to set the `Name:` field - `subdirs` which subdirs of `include` should be added to the header diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index a3166e5..8d0d123 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -264,7 +264,7 @@ following. - `build_always` (deprecated) if `true` this target is always considered out of date and is rebuilt every time. Equivalent to setting both `build_always_stale` and `build_by_default` to true. -- `build_always_stale` if `true` the target is always considered out of date. +- `build_always_stale` *(added 0.47)* if `true` the target is always considered out of date. Useful for things such as build timestamps or revision control tags. The associated command is run even if the outputs are up to date. - `capture`, there are some compilers that can't be told to write @@ -395,6 +395,8 @@ are also supported. This function supports the following keyword arguments: the build machine system rather than the host system (i.e. where the cross compiled binary will run on), usually only needed if you build a tool to be used during compilation. +- `not_found_message` *(added 0.50.0)* is an optional string that will + be printed as a `message()` if the dependency was not found. - `required`, when set to false, Meson will proceed with the build even if the dependency is not found. Since *0.47.0* the value of a [`feature`](Build-options.md#features) option can also be passed. @@ -928,6 +930,10 @@ format and optionally the owner/uid and group/gid for the installed files. An example value could be `['rwxr-sr-x', 'root', 'root']`. *(Added 0.47.0)*. +Since 0.49.0, [manpages are no longer compressed implicitly][install_man_49]. + +[install_man_49]: https://mesonbuild.com/Release-notes-for-0-49-0.html#manpages-are-no-longer-compressed-implicitly + ### install_subdir() ``` meson @@ -1018,6 +1024,9 @@ Joins the given strings into a file system path segment. For example individual segments is an absolute path, all segments before it are dropped. That means that `join_paths('foo', '/bar')` returns `/bar`. +**Warning** Don't use `join_paths()` for sources in [`library`](#library) and +[`executable`](#executable), you should use [`files`](#files) instead. + *Added 0.36.0* Since 0.49.0 using the`/` operator on strings is equivalent to calling @@ -2066,6 +2075,10 @@ an external dependency with the following methods: - `found()` which returns whether the dependency was found + - `name()` *(Added 0.48.0)* returns the name of the dependency that was + searched. Returns `internal` for dependencies created with + `declare_dependency()`. + - `get_pkgconfig_variable(varname)` *(Added 0.36.0)* will get the pkg-config variable specified, or, if invoked on a non pkg-config dependency, error out. *(Added 0.44.0)* You can also redefine a diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index b561e86..fa913f5 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -59,6 +59,7 @@ set in the cross file. | ppc64 | 64 bit PPC processors | | riscv32 | 32 bit RISC-V Open ISA| | riscv64 | 64 bit RISC-V Open ISA| +| rl78 | Renesas RL78 | | rx | Renesas RX 32 bit MCU | | s390x | IBM zSystem s390x | | sparc | 32 bit SPARC | @@ -116,6 +117,8 @@ These are the parameter names for passing language specific arguments to your bu These environment variables will be used to modify the compiler and linker flags. +| Name | Comment | +| ----- | ------- | | CFLAGS | Flags for the C compiler | | CXXFLAGS | Flags for the C++ compiler | | OBJCFLAGS | Flags for the Objective C compiler | diff --git a/docs/markdown/Release-notes-for-0.38.0.md b/docs/markdown/Release-notes-for-0.38.0.md index 741c349..cbd8fff 100644 --- a/docs/markdown/Release-notes-for-0.38.0.md +++ b/docs/markdown/Release-notes-for-0.38.0.md @@ -3,11 +3,11 @@ title: Release 0.38 short-description: Release notes for 0.38 ... -# Uninstall target +## Uninstall target Meson allows you to uninstall an install step by invoking the uninstall target. This will remove all files installed as part of install. Note that this does not restore the original files. This also does not undo changes done by custom install scripts (because they can do arbitrary install operations). -# Support for arbitrary test setups +## Support for arbitrary test setups Sometimes you need to run unit tests with special settings. For example under Valgrind. Usually this requires extra command line options for the tool. This is supported with the new *test setup* feature. For example to set up a test run with Valgrind, you'd write this in a `meson.build` file: @@ -23,11 +23,11 @@ This tells Meson to run tests with Valgrind using the given options and multiply $ mesontest --setup=valgrind ``` -# Intel C/C++ compiler support +## Intel C/C++ compiler support As usual, just set `CC=icc CXX=icpc` and Meson will use it as the C/C++ compiler. Currently only Linux is supported. -# Get values from configuration data objects +## Get values from configuration data objects Now it is possible to query values stored in configuration data objects. @@ -38,7 +38,7 @@ cdata.get('nokey', 'default') # returns 'default' cdata.get('nokey') # halts with an error ``` -# Python 3 module support +## Python 3 module support Building Python 3 extension modules has always been possible, but it is now even easier: @@ -49,7 +49,7 @@ pylib = py3_mod.extension_module('modname', dependencies : py3_dep) ``` -# Default options to subprojects +## Default options to subprojects Projects can specify overriding values for subprojects' `default_options` when invoking a subproject: @@ -60,23 +60,23 @@ dependency('some-dep', fallback : ['some_subproject', 'some_dep'], default_optio The effect is the same as if the default options were written in the subproject's `project` call. -# Set targets to be built (or not) by default +## Set targets to be built (or not) by default Build targets got a new keyword `build_by_default` which tells whether the target should be built by default when running e.g. `ninja`. Custom targets are not built by default but other targets are. Any target that is tagged as installed or to be built always is also built by default, regardless of the value of this keyword. -# Add option to mesonconf to wipe cached data. +## Add option to mesonconf to wipe cached data. Meson caches the results of dependency lookups. Sometimes these may get out of sync with the system state. Mesonconf now has a `--clearcache` option to clear these values so they will be re-searched from the system upon next compile. -# Can specify file permissions and owner when installing data +## Can specify file permissions and owner when installing data The new `install_mode` keyword argument can be used to specify file permissions and uid/gid of files when doing the install. This allows you to, for example, install suid root scripts. -# `has_header()` checks are now faster +## `has_header()` checks are now faster When using compilers that implement the [`__has_include()` preprocessor macro](https://clang.llvm.org/docs/LanguageExtensions.html#include-file-checking-macros), the check is now ~40% faster. -# Array indexing now supports fallback values +## Array indexing now supports fallback values The second argument to the array [`.get()`](Reference-manual.md#array-object) function is now returned if the specified index could not be found ```meson @@ -86,6 +86,6 @@ array.get(4) # this will give an error about invalid index array.get(4, 0) # this will return `0` ``` -# Silent mode for Mesontest +## Silent mode for Mesontest The Meson test executor got a new argument `-q` (and `--quiet`) that suppresses all output of successful tests. This makes interactive usage nicer because only errors are printed. diff --git a/docs/markdown/Release-notes-for-0.43.0.md b/docs/markdown/Release-notes-for-0.43.0.md index 7702f3c..0ca66ae 100644 --- a/docs/markdown/Release-notes-for-0.43.0.md +++ b/docs/markdown/Release-notes-for-0.43.0.md @@ -3,7 +3,7 @@ title: Release 0.43 short-description: Release notes for 0.43 ... -# Portability improvements to Boost Dependency +## Portability improvements to Boost Dependency The Boost dependency has been improved to better detect the various ways to install boost on multiple platforms. At the same time the `modules` semantics @@ -14,12 +14,12 @@ used to specify libraries that require linking. This is a breaking change and the fix is to remove all modules that aren't found. -# Generator learned capture +## Generator learned capture Generators can now be configured to capture the standard output. See `test cases/common/98 gen extra/meson.build` for an example. -# Can index CustomTarget objects +## Can index CustomTarget objects The `CustomTarget` object can now be indexed like an array. The resulting object can be used as a source file for other Targets, this will create a @@ -43,7 +43,7 @@ exec = executable( ) ``` -# Can override executables in the cross file +## Can override executables in the cross file The cross file can now be used for overriding the result of `find_program`. As an example if you want to find the `objdump` @@ -63,7 +63,7 @@ objdump, you can specify the `native` keyword like this: native_objdump = find_program('objdump', native : true) ``` -# Easier handling of supported compiler arguments +## Easier handling of supported compiler arguments A common pattern for handling multiple desired compiler arguments, was to test their presence and add them to an array one-by-one, e.g.: @@ -91,7 +91,7 @@ warning_flags = [ ... ] flags = cc.get_supported_arguments(warning_flags) ``` -# Better support for shared libraries in non-system paths +## Better support for shared libraries in non-system paths Meson has support for prebuilt object files and static libraries. This release adds feature parity to shared libraries that are either @@ -114,7 +114,7 @@ mydep = declare_dependency(include_directories : include_directories('.'), Then you can use the dependency object in the same way as any other. -# wrap-svn +## wrap-svn The [Wrap dependency system](Wrap-dependency-system-manual.md) now supports [Subversion](https://subversion.apache.org/) (svn). This diff --git a/docs/markdown/Release-notes-for-0.45.0.md b/docs/markdown/Release-notes-for-0.45.0.md index 19d65b8..2b67f9b 100644 --- a/docs/markdown/Release-notes-for-0.45.0.md +++ b/docs/markdown/Release-notes-for-0.45.0.md @@ -176,7 +176,8 @@ defined in it. ## Can use custom targets as Windows resource files The `compile_resources()` function of the `windows` module can now be used on custom targets as well as regular files. -# Can promote dependencies with wrap command + +## Can promote dependencies with wrap command The `promote` command makes it easy to copy nested dependencies to the top level. diff --git a/docs/markdown/Release-notes-for-0.48.0.md b/docs/markdown/Release-notes-for-0.48.0.md index 270a689..80fc08b 100644 --- a/docs/markdown/Release-notes-for-0.48.0.md +++ b/docs/markdown/Release-notes-for-0.48.0.md @@ -311,3 +311,24 @@ clone-recursive=true This allows you to declare an optional subproject. You can now call `found()` on the return value of the `subproject()` call to see if the subproject is available before calling `get_variable()` to fetch information from it. + +## `dependency()` objects now support the `.name()` method + +You can now fetch the name of the dependency that was searched like so: + +```meson +glib_dep = dependency('glib-2.0') +... +message("dependency name is " + glib_dep.name()) +# This outputs `dependency name is glib-2.0` + +qt_dep = dependency('qt5') +... +message("dependency name is " + qt_dep.name()) +# This outputs `dependency name is qt5` + +decl_dep = declare_dependency() +... +message("dependency name is " + decl_dep.name()) +# This outputs `dependency name is internal` +``` diff --git a/docs/markdown/Release-notes-for-0.49.0.md b/docs/markdown/Release-notes-for-0.49.0.md index bdf5769..4ed4be2 100644 --- a/docs/markdown/Release-notes-for-0.49.0.md +++ b/docs/markdown/Release-notes-for-0.49.0.md @@ -1,22 +1,312 @@ --- title: Release 0.49 -short-description: Release notes for 0.49 (preliminary) +short-description: Release notes for 0.49 ... -# New features +## Libgcrypt dependency now supports libgcrypt-config -This page is a placeholder for the eventual release notes. +Earlier, `dependency('libgcrypt')` could only detect the library with pkg-config +files. Now, if pkg-config files are not found, Meson will look for +`libgcrypt-config` and if it's found, will use that to find the library. -Notable new features should come with release note updates. This is -done by creating a file snippet called `snippets/featurename.md` and -whose contents should look like this: +## New `section` key for the buildoptions introspection - ## Feature name +Meson now has a new `section` key in each build option. This allows +IDEs to group these options similar to `meson configure`. - A short description explaining the new feature and how it should be used. +The possible values for `section` are: -## Libgcrypt dependency now supports libgcrypt-config + - core + - backend + - base + - compiler + - directory + - user + - test -Earlier, `dependency('libgcrypt')` could only detect the library with pkg-config -files. Now, if pkg-config files are not found, Meson will look for -`libgcrypt-config` and if it's found, will use that to find the library. +## CC-RX compiler for C and CPP + +Cross-compilation is now supported for Renesas RX targets with the +CC-RX compiler. + +The environment path should be set properly for the CC-RX compiler +executables. The `-cpu` option with the appropriate value should be +mentioned in the cross-file as shown in the snippet below. + +```ini +[properties] +c_args = ['-cpu=rx600'] +cpp_args = ['-cpu=rx600'] +``` + +The default extension of the executable output is `.abs`. Other +target specific arguments to the compiler and linker will need to be +added explicitly from the +cross-file(`c_args`/`c_link_args`/`cpp_args`/`cpp_link_args`) or some +other way. Refer to the CC-RX User's manual for additional compiler +and linker options. + +## CMake `find_package` dependency backend + +Meson can now use the CMake `find_package` ecosystem to +detect dependencies. Both the old-style `<NAME>_LIBRARIES` +variables as well as imported targets are supported. Meson +can automatically guess the correct CMake target in most +cases but it is also possible to manually specify a target +with the `modules` property. + +```meson +# Implicitly uses CMake as a fallback and guesses a target +dep1 = dependency('KF5TextEditor') + +# Manually specify one or more CMake targets to use +dep2 = dependency('ZLIB', method : 'cmake', modules : ['ZLIB::ZLIB']) +``` + +CMake is automatically used after `pkg-config` fails when +no `method` (or `auto`) was provided in the dependency options. + +## New compiler method `get_argument_syntax` + +The compiler object now has `get_argument_syntax` method, which returns a +string value of `gcc`, `msvc`, or an undefined value string value. This can be +used to determine if a compiler uses gcc syntax (`-Wfoo`), msvc syntax +(`/w1234`), or some other kind of arguments. + +```meson +cc = meson.get_compiler('c') + +if cc.get_argument_syntax() == 'msvc' + if cc.has_argument('/w1235') + add_project_arguments('/w1235', language : ['c']) + endif +elif cc.get_argument_syntax() == 'gcc' + if cc.has_argument('-Wfoo') + add_project_arguments('-Wfoo', language : ['c']) + endif +elif cc.get_id() == 'some other compiler' + add_project_arguments('--error-on-foo', language : ['c']) +endif +``` + +## Return `Disabler()` instead of not-found object + +Functions such as `dependency()`, `find_library()`, `find_program()`, and +`python.find_installation()` have a new keyword argument: `disabler`. When set +to `true` those functions return `Disabler()` objects instead of not-found +objects. + +## `introspect --projectinfo` can now be used without configured build directory + +This allows IDE integration to get information about the project before the user has configured a build directory. + +Before you could use `meson.py introspect --projectinfo build-directory`. +Now you also can use `meson.py introspect --projectinfo project-dir/meson.build`. + +The output is similiar to the output with a build directory but additionally also includes information from `introspect --buildsystem-files`. + +For example `meson.py introspect --projectinfo test\ cases/common/47\ subproject\ options/meson.build` +This outputs (pretty printed for readability): +``` +{ + "buildsystem_files": [ + "meson_options.txt", + "meson.build" + ], + "name": "suboptions", + "version": null, + "descriptive_name": "suboptions", + "subprojects": [ + { + "buildsystem_files": [ + "subprojects/subproject/meson_options.txt", + "subprojects/subproject/meson.build" + ], + "name": "subproject", + "version": "undefined", + "descriptive_name": "subproject" + } + ] +} +``` + +Both usages now include a new `descriptive_name` property which always +shows the name set in the project. + +## Can specify keyword arguments with a dictionary + +You can now specify keyword arguments for any function and method call +with the `kwargs` keyword argument. This is perhaps best described +with an example: + +```meson +options = {'include_directories': include_directories('inc')} + +... + +executable(... + kwargs: options) +``` + +The above code is identical to this: + +```meson +executable(... + include_directories: include_directories('inc')) +``` + +That is, Meson will expand the dictionary given to `kwargs` as if the +entries in it had been given as keyword arguments directly. + +Note that any individual argument can be specified either directly or +with the `kwarg` dict but not both. If a key is specified twice, it +is a hard error. + +## Manpages are no longer compressed implicitly + +Earlier, the `install_man` command has automatically compressed installed +manpages into `.gz` format. This collided with manpage compression hooks +already used by various distributions. Now, manpages are installed uncompressed +and distributors are expected to handle compressing them according to their own +compression preferences. + +## Native config files + +Native files (`--native-file`) are the counterpart to cross files (`--cross-file`), +and allow specifying information about the build machine, both when cross compiling +and when not. + +Currently the native files only allow specifying the names of binaries, similar +to the cross file, for example: + +```ini +[binaries] +llvm-config = "/opt/llvm-custom/bin/llvm-config" +``` + +Will override the llvm-config used for *native* binaries. Targets for the host +machine will continue to use the cross file. + +## Foreach `break` and `continue` + +`break` and `continue` keywords can be used inside foreach loops. + +```meson +items = ['a', 'continue', 'b', 'break', 'c'] +result = [] +foreach i : items + if i == 'continue' + continue + elif i == 'break' + break + endif + result += i +endforeach +# result is ['a', 'b'] +``` + +You can check if an array contains an element like this: +```meson +my_array = [1, 2] +if 1 in my_array +# This condition is true +endif +if 1 not in my_array +# This condition is false +endif +``` + +You can check if a dictionary contains a key like this: +```meson +my_dict = {'foo': 42, 'foo': 43} +if 'foo' in my_dict +# This condition is true +endif +if 42 in my_dict +# This condition is false +endif +if 'foo' not in my_dict +# This condition is false +endif +``` + +## Joining paths with / + +Joining two paths has traditionally been done with the `join_paths` function. + +```meson +joined = join_paths('foo', 'bar') +``` + +Now you can use the simpler notation using the `/` operator. + +```meson +joined = 'foo' / 'bar' +``` + +This only works for strings. + +## Position-independent executables + +When `b_pie` option, or `executable()`'s `pie` keyword argument is set to +`true`, position-independent executables are built. All their objects are built +with `-fPIE` and the executable is linked with `-pie`. Any static library they +link must be built with `pic` set to `true` (see `b_staticpic` option). + +## Deprecation warning in pkg-config generator + +All libraries passed to the `libraries` keyword argument of the `generate()` +method used to be associated with that generated pkg-config file. That means +that any subsequent call to `generate()` where those libraries appear would add +the filebase of the `generate()` that first contained them into `Requires:` or +`Requires.private:` field instead of adding an `-l` to `Libs:` or `Libs.private:`. + +This behaviour is now deprecated. The library that should be associated with +the generated pkg-config file should be passed as first positional argument +instead of in the `libraries` keyword argument. The previous behaviour is +maintained but prints a deprecation warning and support for this will be removed +in a future Meson release. If you can not create the needed pkg-config file +without this warning, please file an issue with as much details as possible +about the situation. + +For example this sample will write `Requires: liba` into `libb.pc` but print a +deprecation warning: +```meson +liba = library(...) +pkg.generate(libraries : liba) + +libb = library(...) +pkg.generate(libraries : [liba, libb]) +``` + +It can be fixed by passing `liba` as first positional argument:: +```meson +liba = library(...) +pkg.generate(liba) + +libb = library(...) +pkg.generate(libb, libraries : [liba]) +``` + +## Subprojects download, checkout, update command-line + +New command-line tool has been added to manage subprojects: + +- `meson subprojects download` to download all subprojects that have a wrap file. +- `meson subprojects update` to update all subprojects to latest version. +- `meson subprojects checkout` to checkout or create a branch in all git subprojects. + +## New keyword argument `is_default` to `add_test_setup()` + +The keyword argument `is_default` may be used to set whether the test +setup should be used by default whenever `meson test` is run without +the `--setup` option. + +```meson +add_test_setup('default', is_default: true, env: 'G_SLICE=debug-blocks') +add_test_setup('valgrind', env: 'G_SLICE=always-malloc', ...) +test('mytest', exe) +``` + +For the example above, running `meson test` and `meson test +--setup=default` is now equivalent. diff --git a/docs/markdown/Release-notes-for-0.50.0.md b/docs/markdown/Release-notes-for-0.50.0.md new file mode 100644 index 0000000..cb4fe0d --- /dev/null +++ b/docs/markdown/Release-notes-for-0.50.0.md @@ -0,0 +1,17 @@ +--- +title: Release 0.50 +short-description: Release notes for 0.49 (preliminary) +... + +# New features + +This page is a placeholder for the eventual release notes. + +Notable new features should come with release note updates. This is +done by creating a file snippet called `snippets/featurename.md` and +whose contents should look like this: + + ## Feature name + + A short description explaining the new feature and how it should be used. + diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index 9ea96c1..cf56dd3 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -216,6 +216,14 @@ path = pathsep.join(['/usr/bin', '/bin', '/usr/local/bin']) path = join_paths(['/usr', 'local', 'bin']) # path now has the value '/usr/local/bin' +# Don't use join_paths for sources files, use files for that: +my_sources = files('foo.c') +... +my_sources += files('bar.c') +# This has the advantage of always calculating the correct relative path, even +# if you add files in another directory or use them in a different directory +# than they're defined in + # Example to set an API version for use in library(), install_header(), etc project('project', 'c', version: '0.2.3') version_array = meson.project_version().split('.') diff --git a/docs/markdown/Vala.md b/docs/markdown/Vala.md index c5d2b79..2184ebc 100644 --- a/docs/markdown/Vala.md +++ b/docs/markdown/Vala.md @@ -3,25 +3,147 @@ title: Vala short-description: Compiling Vala and Genie programs ... -# Compiling Vala applications +# Compiling Vala applications and libraries +Meson supports compiling applications and libraries written in +[Vala](https://vala-project.org/) and +[Genie](https://wiki.gnome.org/Projects/Genie) . A skeleton `meson.build` file: -Meson has support for compiling Vala and Genie programs. A skeleton `meson.build` file for Vala looks like this: +```meson +project('vala app', 'vala', 'c') + +dependencies = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), +] + +sources = files('app.vala') + +executable('app_name', sources, dependencies : dependencies) +``` + +You must always specify the `glib-2.0` and `gobject-2.0` libraries as +dependencies, because all current Vala applications use them. +[GLib](https://developer.gnome.org/glib/stable/) is used for basic data types +and [GObject](https://developer.gnome.org/gobject/stable/) is used for the +runtime type system. + + +## Using libraries +Meson uses the [`dependency()`](Reference-manual.md#dependency) function to find +the relevant VAPI, C headers and linker flags when it encounters a Vala source +file in a build target. Vala needs a VAPI file and a C header or headers to use +a library. The VAPI file helps map Vala code to the library's C programming +interface. It is the +[`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) tool that +makes finding these installed files all work seamlessly behind the scenes. When +a `pkg-config` file doesn't exist for the library then the +[`find_library()`](Reference-manual.md#find_library) method of the [compiler +object](Reference-manual.md#compiler-object) needs to be used. Examples are +given later. + +Note Vala uses libraries that follow the C Application Binary Interface (C ABI). +The library, however, could be written in C, Vala, Rust, Go, C++ or any other +language that can generate a binary compatible with the C ABI and so provides C +headers. + + +### The simplest case +This first example is a simple addition to the `meson.build` file because: + + * the library has a `pkg-config` file, `gtk+-3.0.pc` + * the VAPI is distributed with Vala and so installed with the Vala compiler + * the VAPI is installed in Vala's standard search path + * the VAPI has the same name as the `pkg-config` file, `gtk+-3.0.vapi` + +Everything works seamlessly in the background and only a single extra line is +needed: ```meson -project('valaprog', 'vala', 'c') +project('vala app', 'vala', 'c') -glib_dep = dependency('glib-2.0') -gobject_dep = dependency('gobject-2.0') +dependencies = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), + dependency('gtk+-3.0'), +] -executable('valaprog', 'prog.vala', - dependencies : [glib_dep, gobject_dep]) +sources = files('app.vala') + +executable('app_name', sources, dependencies : dependencies) ``` -You must always specify `glib-2.0` and `gobject-2.0` as dependencies, because all Vala applications use them. +GTK+ is the graphical toolkit used by GNOME, elementary OS and other desktop +environments. The binding to the library, the VAPI file, is distributed with +Vala. + +Other libraries may have a VAPI that is distributed with the library itself. +Such libraries will have their VAPI file installed along with their other +development files. The VAPI is installed in Vala's standard search path and so +works just as seamlessly using the `dependency()` function. + + +### Targetting a version of GLib +Meson's [`dependency()`](Reference-manual.md#dependency) function allows a +version check of a library. This is often used to check a minimum version is +installed. When setting a minimum version of GLib, Meson will also pass this to +the Vala compiler using the `--target-glib` option. + +This is needed when using GTK+'s user interface definition files with Vala's +`[GtkTemplate]`, `[GtkChild]` and `[GtkCallback]` annotations. This requires +`--target-glib 2.38`, or a newer version, to be passed to Vala. With Meson this +is simply done with: + +```meson +project('vala app', 'vala', 'c') + +dependencies = [ + dependency('glib-2.0', version: '>=2.38'), + dependency('gobject-2.0'), + dependency('gtk+-3.0'), +] + +sources = files('app.vala') -## Using a custom VAPI +executable('app_name', sources, dependencies : dependencies) +``` -When dealing with libraries that are not providing Vala bindings, a `--vapidir` flag can be added to extend the search path for the current project. +Using `[GtkTemplate]` also requires the GTK+ user interface definition files to +be built in to the binary as GResources. For completeness, the next example +shows this: + +```meson +project('vala app', 'vala', 'c') + +dependencies = [ + dependency('glib-2.0', version: '>=2.38'), + dependency('gobject-2.0'), + dependency('gtk+-3.0'), +] + +sources = files('app.vala') + +sources += import( 'gnome' ).compile_resources( + 'project-resources', + 'src/resources/resources.gresource.xml', + source_dir: 'src/resources', +) + +executable('app_name', sources, dependencies : dependencies) +``` + + +### Adding to Vala's search path +So far we have covered the cases where the VAPI file is either distributed with +Vala or the library. A VAPI can also be included in the source files of your +project. The convention is to put it in the `vapi` directory of your project. + +This is needed when a library does not have a VAPI or your project needs to link +to another component in the project that uses the C ABI. For example if part of +the project is written in C. + +The Vala compiler's `--vapidir` option is used to add the project directory to +the VAPI search path. In Meson this is done with the `add_project_arguments()` +function: ```meson project('vala app', 'c', 'vala') @@ -29,53 +151,113 @@ project('vala app', 'c', 'vala') add_project_arguments(['--vapidir', join_paths(meson.current_source_dir(), 'vapi')], language: 'vala') -glib_dep = dependency('glib-2.0') -gobject_dep = dependency('gobject-2.0') -foo_dep = dependency('foo') # 'foo.vapi' will be resolved in './vapi/foo.vapi' +dependencies = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), + dependency('foo'), # 'foo.vapi' will be resolved as './vapi/foo.vapi' +] -executable('app', 'app.vala', dependencies: [glib_dep, gobject_dep, foo_dep]) +sources = files('app.vala') + +executable('app_name', sources, dependencies : dependencies) ``` -In this case, make sure that the VAPI name corresponds to the pkg-config file. +If the VAPI is for an external library then make sure that the VAPI name +corresponds to the pkg-config file name. + +The [`vala-extra-vapis` repository](https://github.com/nemequ/vala-extra-vapis) +is a community maintained repository of VAPIs that are not distributed. +Developers use the repository to share early work on new bindings and +improvements to existing bindings. So the VAPIs can frequently change. It is +recommended VAPIs from this repository are copied in to your project's source +files. + +This also works well for starting to write new bindings before they are shared +with the `vala-extra-vapis` repository. -If no pkg-config file is provided, you must use `find_library`. Using`declare_dependency` is cleaner because it does not require passing both dependency objects to the target. + +### Libraries without pkg-config files +A library that does not have a corresponding pkg-config file may mean +`dependency()` is unsuitable for finding the C and Vala interface files. In this +case it is necessary to use `find_library()`. + +The first example uses Vala's POSIX binding. There is no pkg-config file because +POSIX includes the standard C library on Unix systems. All that is needed is the +VAPI file, `posix.vapi`. This is included with Vala and installed in Vala's +standard search path. Meson just needs to be told to only find the library for +the Vala compiler: ```meson -foo_lib = meson.get_compiler('c').find_library('foo') # assuming libfoo.so is installed -foo_vapi = meson.get_compiler('vala').find_library('foo', dirs: join_paths(meson.current_source_dir(), 'vapi')) -foo_dep = declare_dependency(dependencies: [foo_lib, foo_vapi]) +project('vala app', 'vala', 'c') -executable('app', 'app.vala', dependencies: [glib_dep, gobject_dep, foo_dep]) -``` +dependencies = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), + meson.get_compiler('vala').find_library('posix'), +] -## VAPI without pkg-config file +sources = files('app.vala') -Some Vala bindings do not need a corresponding pkg-config file and `dependency` is unsuitable for resolving them. It's necessary to use `find_library` in this case. +executable('app_name', sources, dependencies : dependencies) +``` + +The next example shows how to link with a C library where no additional VAPI is +needed. The standard maths functions are already bound in `glib-2.0.vapi`, but +the GNU C library requires linking to the maths library separately. In this +example Meson is told to find the library only for the C compiler: ```meson -posix_dep = meson.get_compiler('vala').find_library('posix') +project('vala app', 'vala', 'c') + +dependencies = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), + meson.get_compiler('c').find_library('m', required: false), +] -executable('app', 'app.vala', dependencies: [glib_dep, gobject_dep, posix_dep]) +sources = files('app.vala') + +executable('app_name', sources, dependencies : dependencies) ``` -## Custom output names -If a library target is used, Meson automatically outputs the C header and the VAPI. They can be renamed by setting the `vala_header` and `vala_vapi` arguments respectively. In this case, the second and third elements of the `install_dir` array indicate the destination with `true` to indicate default directories (i.e. `include` and `share/vala/vapi`). +## Building libraries + + +### Changing C header and VAPI names +Meson's [`library`](Reference-manual.md#library) target automatically outputs +the C header and the VAPI. They can be renamed by setting the `vala_header` and +`vala_vapi` arguments respectively: ```meson -foo_lib = library('foo', 'foo.vala', +foo_lib = shared_library('foo', 'foo.vala', vala_header: 'foo.h', vala_vapi: 'foo-1.0.vapi', dependencies: [glib_dep, gobject_dep], install: true, install_dir: [true, true, true]) ``` +In this example, the second and third elements of the `install_dir` array +indicate the destination with `true` to use default directories (i.e. `include` +and `share/vala/vapi`). + -## GObject Introspection +### GObject Introspection and language bindings +A 'binding' allows another programming language to use a library written in +Vala. Because Vala uses the GObject type system as its runtime type system it is +very easy to use introspection to generate a binding. A Meson build of a Vala +library can generate the GObject introspection metadata. The metadata is then +used in separate projects with [language specific +tools](https://wiki.gnome.org/Projects/Vala/LibraryWritingBindings) to generate +a binding. -To generate GObject Introspection metadata, the `vala_gir` option has to be set with the desired name. +The main form of metadata is a GObject Introspection Repository (GIR) XML file. +GIRs are mostly used by languages that generate bindings at compile time. +Languages that generate bindings at runtime mostly use a typelib file, which is +generated from the GIR. -The fourth element in the `install_dir` array indicate where the GIR file will be installed. The `true` value tells Meson to use the default directory (i.e. `share/gir-1.0`). +Meson can generate a GIR as part of the build. For a Vala library the +`vala_gir` option has to be set for the `library`: ```meson foo_lib = library('foo', 'foo.vala', @@ -85,7 +267,12 @@ foo_lib = library('foo', 'foo.vala', install_dir: [true, true, true, true]) ``` -For the typelib, use a custom target depending on the library: +The `true` value in `install_dir` tells Meson to use the default directory (i.e. +`share/gir-1.0` for GIRs). The fourth element in the `install_dir` array +indicates where the GIR file will be installed. + +To then generate a typelib file use a custom target with the `g-ir-compiler` +program and a dependency on the library: ```meson g_ir_compiler = find_program('g-ir-compiler') diff --git a/docs/markdown/snippets/buildopts_section.md b/docs/markdown/snippets/buildopts_section.md deleted file mode 100644 index 74cf8a1..0000000 --- a/docs/markdown/snippets/buildopts_section.md +++ /dev/null @@ -1,14 +0,0 @@ -## New `section` key for the buildoptions introspection - -Meson now has a new `section` key in each build option. This allows -IDEs to group these options similar to `meson configure`. - -The possible values for `section` are: - - - core - - backend - - base - - compiler - - directory - - user - - test diff --git a/docs/markdown/snippets/ccrx_toolchain_support.md b/docs/markdown/snippets/ccrx_toolchain_support.md deleted file mode 100644 index 6bf7e5b..0000000 --- a/docs/markdown/snippets/ccrx_toolchain_support.md +++ /dev/null @@ -1,16 +0,0 @@ -## CC-RX compiler for C and CPP - -Cross-compilation is now supported for Renesas RX targets with the CC-RX compiler. - -The environment path should be set properly for the CC-RX compiler executables. -The `-cpu` option with the appropriate value should be mentioned in the cross-file as shown in the snippet below. - -```ini -[properties] -c_args = ['-cpu=rx600'] -cpp_args = ['-cpu=rx600'] -``` - -The default extension of the executable output is `.abs`. -Other target specific arguments to the compiler and linker will need to be added explicitly from the cross-file(`c_args`/`c_link_args`/`cpp_args`/`cpp_link_args`) or some other way. -Refer to the CC-RX User's manual for additional compiler and linker options.
\ No newline at end of file diff --git a/docs/markdown/snippets/cmake.md b/docs/markdown/snippets/cmake.md deleted file mode 100644 index 8848c7b..0000000 --- a/docs/markdown/snippets/cmake.md +++ /dev/null @@ -1,19 +0,0 @@ -## CMake `find_package` dependency backend - -Meson can now use the CMake `find_package` ecosystem to -detect dependencies. Both the old-style `<NAME>_LIBRARIES` -variables as well as imported targets are supported. Meson -can automatically guess the correct CMake target in most -cases but it is also possible to manually specify a target -with the `modules` property. - -```meson -# Implicitly uses CMake as a fallback and guesses a target -dep1 = dependency('KF5TextEditor') - -# Manually specify one or more CMake targets to use -dep2 = dependency('ZLIB', method : 'cmake', modules : ['ZLIB::ZLIB']) -``` - -CMake is automatically used after `pkg-config` fails when -no `method` (or `auto`) was provided in the dependency options. diff --git a/docs/markdown/snippets/compiler_argument_syntax.md b/docs/markdown/snippets/compiler_argument_syntax.md deleted file mode 100644 index 6ae32d4..0000000 --- a/docs/markdown/snippets/compiler_argument_syntax.md +++ /dev/null @@ -1,22 +0,0 @@ -## new compiler method `get_argument_syntax` - -The compiler object now has `get_argument_syntax` method, which returns a -string value of `gcc`, `msvc`, or an undefined value string value. This can be -used to determine if a compiler uses gcc syntax (`-Wfoo`), msvc syntax -(`/w1234`), or some other kind of arguments. - -```meson -cc = meson.get_compiler('c') - -if cc.get_argument_syntax() == 'msvc' - if cc.has_argument('/w1235') - add_project_arguments('/w1235', language : ['c']) - endif -elif cc.get_argument_syntax() == 'gcc' - if cc.has_argument('-Wfoo') - add_project_arguments('-Wfoo', language : ['c']) - endif -elif cc.get_id() == 'some other compiler' - add_project_arguments('--error-on-foo', language : ['c']) -endif -``` diff --git a/docs/markdown/snippets/disabler.md b/docs/markdown/snippets/disabler.md deleted file mode 100644 index 76874f6..0000000 --- a/docs/markdown/snippets/disabler.md +++ /dev/null @@ -1,6 +0,0 @@ -## Return `Disabler()` instead of not-found object - -Functions such as `dependency()`, `find_library()`, `find_program()`, and -`python.find_installation()` have a new keyword argument: `disabler`. When set -to `true` those functions return `Disabler()` objects instead of not-found -objects. diff --git a/docs/markdown/snippets/introspect_projectinfo.md b/docs/markdown/snippets/introspect_projectinfo.md deleted file mode 100644 index 40558b8..0000000 --- a/docs/markdown/snippets/introspect_projectinfo.md +++ /dev/null @@ -1,35 +0,0 @@ -## `introspect --projectinfo` can now be used without configured build directory - -This allows IDE integration to get information about the project before the user has configured a build directory. - -Before you could use `meson.py introspect --projectinfo build-directory`. -Now you also can use `meson.py introspect --projectinfo project-dir/meson.build`. - -The output is similiar to the output with a build directory but additionally also includes information from `introspect --buildsystem-files`. - -For example `meson.py introspect --projectinfo test\ cases/common/47\ subproject\ options/meson.build` -This outputs (pretty printed for readability): -``` -{ - "buildsystem_files": [ - "meson_options.txt", - "meson.build" - ], - "name": "suboptions", - "version": null, - "descriptive_name": "suboptions", - "subprojects": [ - { - "buildsystem_files": [ - "subprojects/subproject/meson_options.txt", - "subprojects/subproject/meson.build" - ], - "name": "subproject", - "version": "undefined", - "descriptive_name": "subproject" - } - ] -} -``` - -Both usages now include a new `descriptive_name` property which always shows the name set in the project. diff --git a/docs/markdown/snippets/kwargdict.md b/docs/markdown/snippets/kwargdict.md deleted file mode 100644 index 509a3e7..0000000 --- a/docs/markdown/snippets/kwargdict.md +++ /dev/null @@ -1,28 +0,0 @@ -## Can specify keyword arguments with a dictionary - -You can now specify keyword arguments for any function and method call -with the `kwargs` keyword argument. This is perhaps best described -with an example: - -```meson -options = {'include_directories': include_directories('inc')} - -... - -executable(... - kwargs: options) -``` - -The above code is identical to this: - -```meson -executable(... - include_directories: include_directories('inc')) -``` - -That is, Meson will expand the dictionary given to `kwargs` as if the -entries in it had been given as keyword arguments directly. - -Note that any individual argument can be specified either directly or -with the `kwarg` dict but not both. If a key is specified twice, it -is a hard error. diff --git a/docs/markdown/snippets/manpage_compression.md b/docs/markdown/snippets/manpage_compression.md deleted file mode 100644 index 8c96807..0000000 --- a/docs/markdown/snippets/manpage_compression.md +++ /dev/null @@ -1,7 +0,0 @@ -## Manpages are no longer compressed implicitly - -Earlier, the `install_man` command has automatically compressed installed -manpages into `.gz` format. This collided with manpage compression hooks -already used by various distributions. Now, manpages are installed uncompressed -and distributors are expected to handle compressing them according to their own -compression preferences. diff --git a/docs/markdown/snippets/native_files.md b/docs/markdown/snippets/native_files.md deleted file mode 100644 index 7bc3644..0000000 --- a/docs/markdown/snippets/native_files.md +++ /dev/null @@ -1,15 +0,0 @@ -## Native config files - -Native files are the counterpart to cross files, and allow specifying -information about the build machine, both when cross compiling and when not. - -Currently the native files only allow specifying the names of binaries, similar -to the cross file, for example: - -```ini -[binaries] -llvm-config = "/opt/llvm-custom/bin/llvm-config" -``` - -Will override the llvm-config used for *native* binaries. Targets for the host -machine will continue to use the cross file. diff --git a/docs/markdown/snippets/new_syntax.md b/docs/markdown/snippets/new_syntax.md deleted file mode 100644 index 98eccd0..0000000 --- a/docs/markdown/snippets/new_syntax.md +++ /dev/null @@ -1,42 +0,0 @@ -## Foreach `break` and `continue` - -`break` and `continue` keywords can be used inside foreach loops. - -```meson -items = ['a', 'continue', 'b', 'break', 'c'] -result = [] -foreach i : items - if i == 'continue' - continue - elif i == 'break' - break - endif - result += i -endforeach -# result is ['a', 'b'] -``` - -You can check if an array contains an element like this: -```meson -my_array = [1, 2] -if 1 in my_array -# This condition is true -endif -if 1 not in my_array -# This condition is false -endif -``` - -You can check if a dictionary contains a key like this: -```meson -my_dict = {'foo': 42, 'foo': 43} -if 'foo' in my_dict -# This condition is true -endif -if 42 in my_dict -# This condition is false -endif -if 'foo' not in my_dict -# This condition is false -endif -``` diff --git a/docs/markdown/snippets/notfound_message.md b/docs/markdown/snippets/notfound_message.md new file mode 100644 index 0000000..d73c6b2 --- /dev/null +++ b/docs/markdown/snippets/notfound_message.md @@ -0,0 +1,38 @@ +## New `not_found_message` for dependency + +You can now specify a `not_found_message` that will be printed if the +specified dependency was not found. The point is to convert constructs +that look like this: + +```meson +d = dependency('something', required: false) +if not d.found() + message('Will not be able to do something.') +endif +``` + +Into this: + +```meson +d = dependency('something', + required: false, + not_found_message: 'Will not be able to do something.') +``` + +Or constructs like this: + +```meson +d = dependency('something', required: false) +if not d.found() + error('Install something by doing XYZ.') +endif +``` + +into this: + +```meson +d = dependency('something', + not_found_message: 'Install something by doing XYZ.') +``` + +Which works, because the default value of `required` is `true`. diff --git a/docs/markdown/snippets/pathdivision.md b/docs/markdown/snippets/pathdivision.md deleted file mode 100644 index 6da6005..0000000 --- a/docs/markdown/snippets/pathdivision.md +++ /dev/null @@ -1,15 +0,0 @@ -## Joining paths with / - -Joining two paths has traditionally been done with the `join_paths` function. - -```meson -joined = join_paths('foo', 'bar') -``` - -Now you can use the simpler notation using the `/` operator. - -```meson -joined = 'foo' / 'bar' -``` - -This only works for strings. diff --git a/docs/markdown/snippets/pie.md b/docs/markdown/snippets/pie.md deleted file mode 100644 index a9be174..0000000 --- a/docs/markdown/snippets/pie.md +++ /dev/null @@ -1,6 +0,0 @@ -## Position-independent executables - -When `b_pie` option, or `executable()`'s `pie` keyword argument is set to -`true`, position-independent executables are built. All their objects are built -with `-fPIE` and the executable is linked with `-pie`. Any static library they -link must be built with `pic` set to `true` (see `b_staticpic` option). diff --git a/docs/markdown/snippets/pkgconfig_break.md b/docs/markdown/snippets/pkgconfig_break.md deleted file mode 100644 index 49c908d..0000000 --- a/docs/markdown/snippets/pkgconfig_break.md +++ /dev/null @@ -1,34 +0,0 @@ -## Deprecation warning in pkg-config generator - -All libraries passed to the `libraries` keyword argument of the `generate()` -method used to be associated with that generated pkg-config file. That means -that any subsequent call to `generate()` where those libraries appear would add -the filebase of the `generate()` that first contained them into `Requires:` or -`Requires.private:` field instead of adding an `-l` to `Libs:` or `Libs.private:`. - -This behaviour is now deprecated. The library that should be associated with -the generated pkg-config file should be passed as first positional argument -instead of in the `libraries` keyword argument. The previous behaviour is -maintained but prints a deprecation warning and support for this will be removed -in a future Meson release. If you can not create the needed pkg-config file -without this warning, please file an issue with as much details as possible -about the situation. - -For example this sample will write `Requires: liba` into `libb.pc` but print a -deprecation warning: -```meson -liba = library(...) -pkg.generate(libraries : liba) - -libb = library(...) -pkg.generate(libraries : [liba, libb]) -``` - -It can be fixed by passing `liba` as first positional argument:: -```meson -liba = library(...) -pkg.generate(liba) - -libb = library(...) -pkg.generate(libb, libraries : [liba]) -``` diff --git a/docs/markdown/snippets/subprojects_cmd.md b/docs/markdown/snippets/subprojects_cmd.md deleted file mode 100644 index 20fef5c..0000000 --- a/docs/markdown/snippets/subprojects_cmd.md +++ /dev/null @@ -1,7 +0,0 @@ -## Subprojects download, checkout, update command-line - -New command-line tool has been added to manage subprojects: - -- `meson subprojects download` to download all subprojects that have a wrap file. -- `meson subprojects update` to update all subprojects to latest version. -- `meson subprojects checkout` to checkout or create a branch in all git subprojects. diff --git a/docs/markdown/snippets/test_setup_is_default.md b/docs/markdown/snippets/test_setup_is_default.md deleted file mode 100644 index 2274dc9..0000000 --- a/docs/markdown/snippets/test_setup_is_default.md +++ /dev/null @@ -1,14 +0,0 @@ -## New keyword argument `is_default` to `add_test_setup()` - -The keyword argument `is_default` may be used to set whether the test -setup should be used by default whenever `meson test` is run without -the `--setup` option. - -```meson -add_test_setup('default', is_default: true, env: 'G_SLICE=debug-blocks') -add_test_setup('valgrind', env: 'G_SLICE=always-malloc', ...) -test('mytest', exe) -``` - -For the example above, running `meson test` and `meson test ---setup=default` is now equivalent. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index f79eb05..b8c41b4 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -70,6 +70,7 @@ index.md Shipping-prebuilt-binaries-as-wraps.md fallback-wraptool.md Release-notes.md + Release-notes-for-0.50.0.md Release-notes-for-0.49.0.md Release-notes-for-0.48.0.md Release-notes-for-0.47.0.md diff --git a/man/meson.1 b/man/meson.1 index a171b0b..702ac4d 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "September 2018" "meson 0.48.0" "User Commands" +.TH MESON "1" "December 2018" "meson 0.49.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 5606c41..f49649b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2442,7 +2442,7 @@ rule FORTRAN_DEP_HACK%s for dep in target.get_external_deps(): # Extend without reordering or de-dup to preserve `-L -l` sets # https://github.com/mesonbuild/meson/issues/1718 - commands.extend_direct(dep.get_link_args()) + commands.extend_preserving_lflags(dep.get_link_args()) need_threads |= dep.need_threads() need_openmp |= dep.need_openmp() for d in target.get_dependencies(): @@ -2450,7 +2450,7 @@ rule FORTRAN_DEP_HACK%s for dep in d.get_external_deps(): need_threads |= dep.need_threads() need_openmp |= dep.need_openmp() - commands.extend_direct(dep.get_link_args()) + commands.extend_preserving_lflags(dep.get_link_args()) if need_openmp: commands += linker.openmp_flags() if need_threads: diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index f9f7b93..8ac88ce 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -25,7 +25,7 @@ from .. import dependencies from .. import mlog from .. import compilers from ..compilers import CompilerArgs -from ..mesonlib import MesonException, File, python_command +from ..mesonlib import MesonException, File, python_command, replace_if_different from ..environment import Environment, build_filename def autodetect_vs_version(build): @@ -69,6 +69,9 @@ def split_o_flags_args(args): o_flags += ['/O' + f for f in flags] return o_flags +def generate_guid_from_path(path, path_type): + return str(uuid.uuid5(uuid.NAMESPACE_URL, 'meson-vs-' + path_type + ':' + str(path))).upper() + class RegenInfo: def __init__(self, source_dir, build_dir, depfiles): self.source_dir = source_dir @@ -265,7 +268,7 @@ class Vs2010Backend(backends.Backend): for path in iterpaths: if path not in self.subdirs: basename = path.name - identifier = str(uuid.uuid4()).upper() + identifier = generate_guid_from_path(path, 'subdir') # top-level directories have None as their parent_dir parent_dir = path.parent parent_identifier = self.subdirs[parent_dir][0] \ @@ -279,7 +282,8 @@ class Vs2010Backend(backends.Backend): def generate_solution(self, sln_filename, projlist): default_projlist = self.get_build_by_default_targets() - with open(sln_filename, 'w', encoding='utf-8') as ofile: + sln_filename_tmp = sln_filename + '~' + with open(sln_filename_tmp, 'w', encoding='utf-8') as ofile: ofile.write('Microsoft Visual Studio Solution File, Format ' 'Version 11.00\n') ofile.write('# Visual Studio ' + self.vs_version + '\n') @@ -380,6 +384,7 @@ class Vs2010Backend(backends.Backend): ofile.write("\t\t{%s} = {%s}\n" % (subdir[0], subdir[1])) ofile.write('\tEndGlobalSection\n') ofile.write('EndGlobal\n') + replace_if_different(sln_filename, sln_filename_tmp) def generate_projects(self): startup_project = self.environment.coredata.backend_options['backend_startup_project'].value @@ -674,11 +679,14 @@ class Vs2010Backend(backends.Backend): raise MesonException('Could not find a C or C++ compiler. MSVC can only build C/C++ projects.') def _prettyprint_vcxproj_xml(self, tree, ofname): - tree.write(ofname, encoding='utf-8', xml_declaration=True) + ofname_tmp = ofname + '~' + tree.write(ofname_tmp, encoding='utf-8', xml_declaration=True) + # ElementTree can not do prettyprinting so do it manually - doc = xml.dom.minidom.parse(ofname) - with open(ofname, 'w', encoding='utf-8') as of: + doc = xml.dom.minidom.parse(ofname_tmp) + with open(ofname_tmp, 'w', encoding='utf-8') as of: of.write(doc.toprettyxml()) + replace_if_different(ofname, ofname_tmp) def gen_vcxproj(self, target, ofname, guid): mlog.debug('Generating vcxproj %s.' % target.name) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 60253ed..1fcbc04 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2143,7 +2143,10 @@ class Jar(BuildTarget): def get_classpath_args(self): cp_paths = [os.path.join(l.get_subdir(), l.get_filename()) for l in self.link_targets] - return ['-cp', os.pathsep.join(cp_paths)] + cp_string = os.pathsep.join(cp_paths) + if cp_string: + return ['-cp', os.pathsep.join(cp_paths)] + return [] class CustomTargetIndex: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3761433..2a5c976 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -612,7 +612,10 @@ class CompilerArgs(list): dedup2_suffixes = () dedup2_args = () # Arg prefixes and args that must be de-duped by returning 1 - dedup1_prefixes = ('-l', '-Wl,-l') + # + # NOTE: not thorough. A list of potential corner cases can be found in + # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 + dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') # Match a .so of the form path/to/libfoo.so.0.1.0 # Only UNIX shared libraries require this. Others have a fixed extension. @@ -748,6 +751,17 @@ class CompilerArgs(list): for elem in iterable: self.append_direct(elem) + def extend_preserving_lflags(self, iterable): + normal_flags = [] + lflags = [] + for i in iterable: + if i.startswith('-l') or i.startswith('-L'): + lflags.append(i) + else: + normal_flags.append(i) + self.extend(normal_flags) + self.extend_direct(lflags) + def __add__(self, args): new = CompilerArgs(self, self.compiler) new += args @@ -1524,10 +1538,17 @@ class GnuLikeCompiler(abc.ABC): if self.compiler_type.is_osx_compiler: # Apple ld return ['-Wl,-undefined,dynamic_lookup'] + elif self.compiler_type.is_windows_compiler: + # For PE/COFF this is impossible + return [] else: # GNU ld and LLVM lld return ['-Wl,--allow-shlib-undefined'] + def get_gui_app_args(self, value): + if self.compiler_type.is_windows_compiler and value: + return ['-mwindows'] + return [] class GnuCompiler(GnuLikeCompiler): """ @@ -1566,11 +1587,6 @@ class GnuCompiler(GnuLikeCompiler): def get_pch_suffix(self): return 'gch' - def get_gui_app_args(self, value): - if self.compiler_type.is_windows_compiler and value: - return ['-mwindows'] - return [] - def openmp_flags(self): return ['-fopenmp'] @@ -1806,6 +1822,10 @@ class IntelCompiler(GnuLikeCompiler): extra_args, '-diag-error', '10006', # ignoring unknown option '-diag-error', '10148', # Option not supported + '-diag-error', '10155', # ignoring argument required + '-diag-error', '10156', # ignoring not argument allowed + '-diag-error', '10157', # Ignoring argument of the wrong type + '-diag-error', '10158', # Argument must be separate. Can be hit by trying an option like -foo-bar=foo when -foo=bar is a valid option but -foo-bar isn't '-diag-error', '1292', # unknown __attribute__ ] return super().compiles(*args, **kwargs) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index e5bb959..d70c230 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -15,6 +15,7 @@ from . import mlog import pickle, os, uuid, shlex import sys +from itertools import chain from pathlib import PurePath from collections import OrderedDict from .mesonlib import ( @@ -25,7 +26,7 @@ import ast import argparse import configparser -version = '0.48.999' +version = '0.49.999' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode'] default_yielding = False @@ -476,8 +477,19 @@ class CoreData: mode = 'custom' self.builtins['buildtype'].set_value(mode) + def _get_all_nonbuiltin_options(self): + yield self.backend_options + yield self.user_options + yield self.compiler_options + yield self.base_options + + def get_all_options(self): + return chain( + iter([self.builtins]), + self._get_all_nonbuiltin_options()) + def validate_option_value(self, option_name, override_value): - for opts in (self.builtins, self.base_options, self.compiler_options, self.user_options): + for opts in self.get_all_options(): if option_name in opts: opt = opts[option_name] return opt.validate_value(override_value) @@ -517,20 +529,14 @@ class CoreData: pass elif k in self.builtins: self.set_builtin_option(k, v) - elif k in self.backend_options: - tgt = self.backend_options[k] - tgt.set_value(v) - elif k in self.user_options: - tgt = self.user_options[k] - tgt.set_value(v) - elif k in self.compiler_options: - tgt = self.compiler_options[k] - tgt.set_value(v) - elif k in self.base_options: - tgt = self.base_options[k] - tgt.set_value(v) else: - unknown_options.append(k) + for opts in self._get_all_nonbuiltin_options(): + if k in opts: + tgt = opts[k] + tgt.set_value(v) + break + else: + unknown_options.append(k) if unknown_options: unknown_options = ', '.join(sorted(unknown_options)) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index e8fba91..7b3d051 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -14,7 +14,6 @@ # This file contains the detection logic for external dependencies that # are UI-related. - import functools import os import re @@ -243,7 +242,7 @@ class QtBaseDependency(ExternalDependency): if self.bindir: yield os.path.join(self.bindir, b), b, False yield '{}-{}'.format(b, self.name), b, False - yield b, b, self.required + yield b, b, self.required if b != 'lrelease' else False for b, name, required in gen_bins(): if found[name].found(): @@ -288,7 +287,15 @@ class QtBaseDependency(ExternalDependency): self.compile_args += m.get_compile_args() if self.private_headers: qt_inc_dir = m.get_pkgconfig_variable('includedir', dict()) - mod_private_inc = _qt_get_private_includes(os.path.join(qt_inc_dir, 'Qt' + m_name), m_name, m.version) + mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m_name) + if not os.path.isdir(mod_private_dir): + # At least some versions of homebrew don't seem to set this + # up correctly. /usr/local/opt/qt/include/Qt + m_name is a + # symlink to /usr/local/opt/qt/include, but the pkg-config + # file points to /usr/local/Cellar/qt/x.y.z/Headers/, and + # the Qt + m_name there is not a symlink, it's a file + mod_private_dir = qt_inc_dir + mod_private_inc = _qt_get_private_includes(mod_private_dir, m_name, m.version) for dir in mod_private_inc: self.compile_args.append('-I' + dir) self.link_args += m.get_link_args() @@ -334,10 +341,9 @@ class QtBaseDependency(ExternalDependency): return ExternalProgram.from_bin_list(self.env.cross_info.config['binaries'], 'qmake') elif self.env.config_info: # Prefer suffixed to unsuffixed version - p = ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake-' + self.name) + p = ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake') if p.found(): return p - return ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake') return ExternalProgram(qmake, silent=True) def _qmake_detect(self, mods, kwargs): diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index ca8b359..e665546 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -84,6 +84,7 @@ known_cpu_families = ( 'ppc64', 'riscv32', 'riscv64', + 'rl78', 'rx', 's390x', 'sparc', diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 4f09c0f..6fef4a9 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1866,10 +1866,55 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'build_target': known_build_target_kwargs, - 'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'install_mode', 'capture', 'install', 'format', 'output_format', 'encoding'}, - 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'install_mode', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default', 'build_always_stale', 'console'}, - 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version', 'private_headers'}, - 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, + 'configure_file': {'input', + 'output', + 'configuration', + 'command', + 'copy', + 'install_dir', + 'install_mode', + 'capture', + 'install', + 'format', + 'output_format', + 'encoding'}, + 'custom_target': {'input', + 'output', + 'command', + 'install', + 'install_dir', + 'install_mode', + 'build_always', + 'capture', + 'depends', + 'depend_files', + 'depfile', + 'build_by_default', + 'build_always_stale', + 'console'}, + 'dependency': {'default_options', + 'fallback', + 'language', + 'main', + 'method', + 'modules', + 'optional_modules', + 'native', + 'not_found_message', + 'required', + 'static', + 'version', + 'private_headers', + }, + 'declare_dependency': {'include_directories', + 'link_with', + 'sources', + 'dependencies', + 'compile_args', + 'link_args', + 'link_whole', + 'version', + }, 'executable': build.known_exe_kwargs, 'find_program': {'required', 'native'}, 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'}, @@ -1957,10 +2002,7 @@ class Interpreter(InterpreterBase): def get_non_matching_default_options(self): env = self.environment for def_opt_name, def_opt_value in self.project_default_options.items(): - for option_type in [ - env.coredata.builtins, env.coredata.compiler_options, - env.coredata.backend_options, env.coredata.base_options, - env.coredata.user_options]: + for option_type in env.coredata.get_all_options(): for cur_opt_name, cur_opt_value in option_type.items(): if (def_opt_name == cur_opt_name and def_opt_value != cur_opt_value.value): @@ -2359,11 +2401,11 @@ external dependencies (including libraries) must go to "dependencies".''') if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted)) self.active_projectname = current_active - self.build.subprojects[dirname] = subi.project_version self.subprojects.update(subi.subprojects) self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname) self.build_def_files += subi.build_def_files self.build.merge(subi.build) + self.build.subprojects[dirname] = subi.project_version return self.subprojects[dirname] def get_option_internal(self, optname): @@ -2604,6 +2646,9 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_message(self, node, args, kwargs): argstr = self.get_message_string_arg(node) + self.message_impl(argstr) + + def message_impl(self, argstr): mlog.log(mlog.bold('Message:'), argstr) @FeatureNew('warning', '0.44.0') @@ -2972,13 +3017,27 @@ external dependencies (including libraries) must go to "dependencies".''') @FeatureNewKwargs('dependency', '0.49.0', ['disabler']) @FeatureNewKwargs('dependency', '0.40.0', ['method']) @FeatureNewKwargs('dependency', '0.38.0', ['default_options']) + @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message']) @disablerIfNotFound @permittedKwargs(permitted_kwargs['dependency']) def func_dependency(self, node, args, kwargs): self.validate_arguments(args, 1, [str]) name = args[0] display_name = name if name else '(anonymous)' + not_found_message = kwargs.get('not_found_message', '') + if not isinstance(not_found_message, str): + raise InvalidArguments('The not_found_message must be a string.') + try: + d = self.dependency_impl(name, display_name, kwargs) + except Exception: + if not_found_message: + self.message_impl(not_found_message) + raise + if not d.found() and not_found_message: + self.message_impl(not_found_message) + return d + def dependency_impl(self, name, display_name, kwargs): disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: mlog.log('Dependency', mlog.bold(display_name), 'skipped: feature', mlog.bold(feature), 'disabled') @@ -3075,7 +3134,7 @@ external dependencies (including libraries) must go to "dependencies".''') def dependency_fallback(self, name, kwargs): display_name = name if name else '(anonymous)' - if self.coredata.get_builtin_option('wrap_mode') in (WrapMode.nofallback, WrapMode.nodownload): + if self.coredata.get_builtin_option('wrap_mode') == WrapMode.nofallback: mlog.log('Not looking for a fallback subproject for the dependency', mlog.bold(display_name), 'because:\nUse of fallback' 'dependencies is disabled.') diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 1594594..707b8f7 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -999,7 +999,9 @@ The result of this is undefined and will become a hard error in a future Meson r return kwargs to_expand = kwargs.pop('kwargs') if not isinstance(to_expand, dict): - raise InterpreterException('Value of "kwarg" must be dictionary.') + raise InterpreterException('Value of "kwargs" must be dictionary.') + if 'kwargs' in to_expand: + raise InterpreterException('Kwargs argument must not contain a "kwargs" entry. Points for thinking meta, though. :P') for k, v in to_expand.items(): if k in kwargs: raise InterpreterException('Entry "{}" defined both as a keyword argument and in a "kwarg" entry.'.format(k)) diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 59d4f81..98c2366 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -1116,6 +1116,22 @@ def windows_proof_rmtree(f): shutil.rmtree(f) +def windows_proof_rm(fpath): + """Like windows_proof_rmtree, but for a single file.""" + if os.path.isfile(fpath): + os.chmod(fpath, os.stat(fpath).st_mode | stat.S_IWRITE | stat.S_IREAD) + delays = [0.1, 0.1, 0.2, 0.2, 0.2, 0.5, 0.5, 1, 1, 1, 1, 2] + for d in delays: + try: + os.unlink(fpath) + return + except FileNotFoundError: + return + except (OSError, PermissionError): + time.sleep(d) + os.unlink(fpath) + + def detect_subprojects(spdir_name, current_dir='', result=None): if result is None: result = {} diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 48ec20f..3bcacfb 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -51,17 +51,21 @@ def add_arguments(parser): parser.add_argument('builddir', nargs='?', default='.', help='The build directory') def determine_installed_path(target, installdata): - install_target = None - for i in installdata.targets: - if os.path.basename(i.fname) == target.get_filename(): # FIXME, might clash due to subprojects. - install_target = i - break - if install_target is None: + install_targets = [] + for i in target.outputs: + for j in installdata.targets: + if os.path.basename(j.fname) == i: # FIXME, might clash due to subprojects. + install_targets += [j] + break + if len(install_targets) == 0: raise RuntimeError('Something weird happened. File a bug.') - outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname)) + # Normalize the path by using os.path.sep consistently, etc. # Does not change the effective path. - return str(pathlib.PurePath(outname)) + install_targets = list(map(lambda x: os.path.join(installdata.prefix, x.outdir, os.path.basename(x.fname)), install_targets)) + install_targets = list(map(lambda x: str(pathlib.PurePath(x)), install_targets)) + + return install_targets def list_installed(installdata): diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index bf49770..be99059 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -44,21 +44,22 @@ native_glib_version = None girwarning_printed = False gdbuswarning_printed = False gresource_warning_printed = False -_gir_has_extra_lib_arg = None +_gir_has_option = {} -def gir_has_extra_lib_arg(intr_obj): - global _gir_has_extra_lib_arg - if _gir_has_extra_lib_arg is not None: - return _gir_has_extra_lib_arg +def gir_has_option(intr_obj, option): + global _gir_has_option + if option in _gir_has_option: + return _gir_has_option[option] - _gir_has_extra_lib_arg = False + _gir_has_option[option] = False try: g_ir_scanner = intr_obj.find_program_impl('g-ir-scanner').get_command() opts = Popen_safe(g_ir_scanner + ['--help'], stderr=subprocess.STDOUT)[1] - _gir_has_extra_lib_arg = '--extra-library' in opts + _gir_has_option[option] = option in opts except (MesonException, FileNotFoundError, subprocess.CalledProcessError): pass - return _gir_has_extra_lib_arg + + return _gir_has_option[option] class GnomeModule(ExtensionModule): gir_dep = None @@ -308,7 +309,7 @@ class GnomeModule(ExtensionModule): if include_rpath: link_command.append('-Wl,-rpath,' + libdir) depends.append(lib) - if gir_has_extra_lib_arg(self.interpreter) and use_gir_args: + if gir_has_option(self.interpreter, '--extra-library') and use_gir_args: link_command.append('--extra-library=' + lib.name) else: link_command.append('-l' + lib.name) @@ -392,7 +393,7 @@ class GnomeModule(ExtensionModule): mlog.log('dependency {!r} not handled to build gir files'.format(dep)) continue - if gir_has_extra_lib_arg(self.interpreter) and use_gir_args: + if gir_has_option(self.interpreter, '--extra-library') and use_gir_args: def fix_ldflags(ldflags): fixed_ldflags = OrderedSet() for ldflag in ldflags: @@ -805,6 +806,10 @@ class GnomeModule(ExtensionModule): scan_command += self._scan_langs(state, [lc[0] for lc in langs_compilers]) scan_command += list(external_ldflags) + if gir_has_option(self.interpreter, '--sources-top-dirs'): + scan_command += ['--sources-top-dirs', os.path.join(state.environment.get_source_dir(), self.interpreter.subproject_dir, state.subproject)] + scan_command += ['--sources-top-dirs', os.path.join(state.environment.get_build_dir(), self.interpreter.subproject_dir, state.subproject)] + scan_target = self._make_gir_target(state, girfile, scan_command, depends, kwargs) typelib_output = '%s-%s.typelib' % (ns, nsversion) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index eee3783..47edeee 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -35,13 +35,13 @@ class DependenciesHelper: def add_pub_libs(self, libs): libs, reqs, cflags = self._process_libs(libs, True) - self.pub_libs += libs + self.pub_libs = libs + self.pub_libs # prepend to preserve dependencies self.pub_reqs += reqs self.cflags += cflags def add_priv_libs(self, libs): libs, reqs, _ = self._process_libs(libs, False) - self.priv_libs += libs + self.priv_libs = libs + self.priv_libs self.priv_reqs += reqs def add_pub_reqs(self, reqs): diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 9cfbd6f..c51b412 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -267,6 +267,12 @@ import sys install_paths = sysconfig.get_paths(scheme='posix_prefix', vars={'base': '', 'platbase': '', 'installed_base': ''}) +def links_against_libpython(): + from distutils.core import Distribution, Extension + cmd = Distribution().get_command_obj('build_ext') + cmd.ensure_finalized() + return bool(cmd.get_libraries(Extension('dummy', []))) + print (json.dumps ({ 'variables': sysconfig.get_config_vars(), 'paths': sysconfig.get_paths(), @@ -274,14 +280,16 @@ print (json.dumps ({ 'version': sysconfig.get_python_version(), 'platform': sysconfig.get_platform(), 'is_pypy': '__pypy__' in sys.builtin_module_names, + 'link_libpython': links_against_libpython(), })) ''' -class PythonInstallation(ExternalProgramHolder, InterpreterObject): + +class PythonInstallation(ExternalProgramHolder): def __init__(self, interpreter, python, info): - InterpreterObject.__init__(self) ExternalProgramHolder.__init__(self, python) self.interpreter = interpreter + self.subproject = self.interpreter.subproject prefix = self.interpreter.environment.coredata.get_builtin_option('prefix') self.variables = info['variables'] self.paths = info['paths'] @@ -291,9 +299,23 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): self.version = info['version'] self.platform = info['platform'] self.is_pypy = info['is_pypy'] + self.link_libpython = info['link_libpython'] + self.methods.update({ + 'extension_module': self.extension_module_method, + 'dependency': self.dependency_method, + 'install_sources': self.install_sources_method, + 'get_install_dir': self.get_install_dir_method, + 'language_version': self.language_version_method, + 'found': self.found_method, + 'has_path': self.has_path_method, + 'get_path': self.get_path_method, + 'has_variable': self.has_variable_method, + 'get_variable': self.get_variable_method, + 'path': self.path_method, + }) @permittedKwargs(mod_kwargs) - def extension_module(self, interpreter, state, args, kwargs): + def extension_module_method(self, args, kwargs): if 'subdir' in kwargs and 'install_dir' in kwargs: raise InvalidArguments('"subdir" and "install_dir" are mutually exclusive') @@ -304,6 +326,18 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir) + # On macOS and some Linux distros (Debian) distutils doesn't link + # extensions against libpython. We call into distutils and mirror its + # behavior. See https://github.com/mesonbuild/meson/issues/4117 + if not self.link_libpython: + new_deps = [] + for holder in mesonlib.extract_as_list(kwargs, 'dependencies'): + dep = holder.held_object + if isinstance(dep, PythonDependency): + holder = self.interpreter.holderify(dep.get_partial_dependency(compile_args=True)) + new_deps.append(holder) + kwargs['dependencies'] = new_deps + suffix = self.variables.get('EXT_SUFFIX') or self.variables.get('SO') or self.variables.get('.so') # msys2's python3 has "-cpython-36m.dll", we have to be clever @@ -314,14 +348,14 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): kwargs['name_prefix'] = '' kwargs['name_suffix'] = suffix - return interpreter.func_shared_module(None, args, kwargs) + return self.interpreter.func_shared_module(None, args, kwargs) - def dependency(self, interpreter, state, args, kwargs): - dep = PythonDependency(self, interpreter.environment, kwargs) - return interpreter.holderify(dep) + def dependency_method(self, args, kwargs): + dep = PythonDependency(self, self.interpreter.environment, kwargs) + return self.interpreter.holderify(dep) @permittedKwargs(['pure', 'subdir']) - def install_sources(self, interpreter, state, args, kwargs): + def install_sources_method(self, args, kwargs): pure = kwargs.pop('pure', False) if not isinstance(pure, bool): raise InvalidArguments('"pure" argument must be a boolean.') @@ -335,11 +369,11 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): else: kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir) - return interpreter.func_install_data(None, args, kwargs) + return self.interpreter.holderify(self.interpreter.func_install_data(None, args, kwargs)) @noPosargs @permittedKwargs(['pure', 'subdir']) - def get_install_dir(self, node, args, kwargs): + def get_install_dir_method(self, args, kwargs): pure = kwargs.pop('pure', True) if not isinstance(pure, bool): raise InvalidArguments('"pure" argument must be a boolean.') @@ -353,30 +387,25 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): else: res = os.path.join(self.platlib_install_path, subdir) - return ModuleReturnValue(res, []) - - @noPosargs - @noKwargs - def language_version(self, node, args, kwargs): - return ModuleReturnValue(self.version, []) + return self.interpreter.module_method_callback(ModuleReturnValue(res, [])) @noPosargs @noKwargs - def found(self, node, args, kwargs): - return ModuleReturnValue(True, []) + def language_version_method(self, args, kwargs): + return self.interpreter.module_method_callback(ModuleReturnValue(self.version, [])) @noKwargs - def has_path(self, node, args, kwargs): + def has_path_method(self, args, kwargs): if len(args) != 1: raise InvalidArguments('has_path takes exactly one positional argument.') path_name = args[0] if not isinstance(path_name, str): raise InvalidArguments('has_path argument must be a string.') - return ModuleReturnValue(path_name in self.paths, []) + return self.interpreter.module_method_callback(ModuleReturnValue(path_name in self.paths, [])) @noKwargs - def get_path(self, node, args, kwargs): + def get_path_method(self, args, kwargs): if len(args) not in (1, 2): raise InvalidArguments('get_path must have one or two arguments.') path_name = args[0] @@ -391,20 +420,20 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): else: raise InvalidArguments('{} is not a valid path name'.format(path_name)) - return ModuleReturnValue(path, []) + return self.interpreter.module_method_callback(ModuleReturnValue(path, [])) @noKwargs - def has_variable(self, node, args, kwargs): + def has_variable_method(self, args, kwargs): if len(args) != 1: raise InvalidArguments('has_variable takes exactly one positional argument.') var_name = args[0] if not isinstance(var_name, str): raise InvalidArguments('has_variable argument must be a string.') - return ModuleReturnValue(var_name in self.variables, []) + return self.interpreter.module_method_callback(ModuleReturnValue(var_name in self.variables, [])) @noKwargs - def get_variable(self, node, args, kwargs): + def get_variable_method(self, args, kwargs): if len(args) not in (1, 2): raise InvalidArguments('get_variable must have one or two arguments.') var_name = args[0] @@ -419,25 +448,13 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): else: raise InvalidArguments('{} is not a valid variable name'.format(var_name)) - return ModuleReturnValue(var, []) + return self.interpreter.module_method_callback(ModuleReturnValue(var, [])) - def method_call(self, method_name, args, kwargs): - try: - fn = getattr(self, method_name) - except AttributeError: - raise InvalidArguments('Python object does not have method %s.' % method_name) - - if not getattr(fn, 'no-args-flattening', False): - args = flatten(args) - - if method_name in ['extension_module', 'dependency', 'install_sources']: - value = fn(self.interpreter, None, args, kwargs) - return self.interpreter.holderify(value) - elif method_name in ['has_variable', 'get_variable', 'has_path', 'get_path', 'found', 'language_version', 'get_install_dir']: - value = fn(None, args, kwargs) - return self.interpreter.module_method_callback(value) - else: - raise InvalidArguments('Python object does not have method %s.' % method_name) + @noPosargs + @noKwargs + @FeatureNew('Python module path method', '0.50.0') + def path_method(self, args, kwargs): + return super().path_method(args, kwargs) class PythonModule(ExtensionModule): diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index f9a5e1c..56a0e9a 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -62,13 +62,26 @@ class MesonApp: # restore that file if anything bad happens. For example if # configuration fails we need to be able to wipe again. filename = coredata.get_cmd_line_file(self.build_dir) - with open(filename, 'r') as f: - content = f.read() + try: + with open(filename, 'r') as f: + content = f.read() + except FileNotFoundError: + raise MesonException( + 'Cannot find cmd_line.txt. This is probably because this ' + 'build directory was configured with a meson version < 0.49.0.') coredata.read_cmd_line_file(self.build_dir, options) try: - mesonlib.windows_proof_rmtree(self.build_dir) + # Don't delete the whole tree, just all of the files and + # folders in the tree. Otherwise calling wipe form the builddir + # will cause a crash + for l in os.listdir(self.build_dir): + l = os.path.join(self.build_dir, l) + if os.path.isdir(l): + mesonlib.windows_proof_rmtree(l) + else: + mesonlib.windows_proof_rm(l) finally: # Restore the file path = os.path.dirname(filename) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 1f9e7f8..8ce9538 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -539,7 +539,11 @@ Timeout: %4d print('--- Listing only the last 100 lines from a long log. ---') lines = lines[-100:] for line in lines: - print(line) + try: + print(line) + except UnicodeEncodeError: + line = line.encode('ascii', errors='replace').decode() + print(line) def doit(self): if self.is_run: diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py index fc65e5b..3807114 100644 --- a/mesonbuild/scripts/commandrunner.py +++ b/mesonbuild/scripts/commandrunner.py @@ -16,6 +16,7 @@ what to run, sets up the environment and executes the command.""" import sys, os, subprocess, shutil, shlex +import re def run_command(source_dir, build_dir, subdir, meson_command, command, arguments): env = {'MESON_SOURCE_ROOT': source_dir, @@ -49,6 +50,9 @@ def run_command(source_dir, build_dir, subdir, meson_command, command, arguments print('Could not execute command "{}": {}'.format(command, err)) sys.exit(1) +def is_python_command(cmdname): + end_py_regex = r'python(3|3\.\d+)?(\.exe)?$' + return re.search(end_py_regex, cmdname) is not None def run(args): if len(args) < 4: @@ -58,7 +62,7 @@ def run(args): build_dir = args[1] subdir = args[2] meson_command = args[3] - if 'python' in meson_command: # Hack. + if is_python_command(meson_command): meson_command = [meson_command, args[4]] command = args[5] arguments = args[6:] diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index 6d7e707..0e203f9 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -93,6 +93,7 @@ def coverage(outputs, source_root, subproject_root, build_root, log_dir): '--output-file', covinfo]) subprocess.check_call([genhtml_exe, '--prefix', build_root, + '--prefix', source_root, '--output-directory', htmloutdir, '--title', 'Code coverage', '--legend', diff --git a/msi/createmsi.py b/msi/createmsi.py index f21e73a..a7a9c3c 100755 --- a/msi/createmsi.py +++ b/msi/createmsi.py @@ -78,13 +78,19 @@ class PackageGenerator: for sd in self.staging_dirs: self.feature_components[sd] = [] + def get_all_modules_from_dir(self, dirname): + modname = os.path.basename(dirname) + modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join(dirname, '*'))] + modules = ['mesonbuild.' + modname + '.' + x for x in modules if not x.startswith('_')] + return modules + def build_dist(self): for sdir in self.staging_dirs: if os.path.exists(sdir): shutil.rmtree(sdir) main_stage, ninja_stage = self.staging_dirs - modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))] - modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')] + modules = self.get_all_modules_from_dir('mesonbuild/modules') + modules += self.get_all_modules_from_dir('mesonbuild/scripts') modules += ['distutils.version'] modulestr = ','.join(modules) python = shutil.which('python') diff --git a/run_project_tests.py b/run_project_tests.py index 0d64f47..02897ce 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -543,6 +543,7 @@ def detect_tests_to_run(): ('fortran', 'fortran', backend is not Backend.ninja or not shutil.which('gfortran')), ('swift', 'swift', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('swiftc')), ('python3', 'python3', backend is not Backend.ninja), + ('python', 'python', backend is not Backend.ninja), ('fpga', 'fpga', shutil.which('yosys') is None), ('frameworks', 'frameworks', False), ('nasm', 'nasm', False), diff --git a/run_tests.py b/run_tests.py index ebee602..aa8a589 100755 --- a/run_tests.py +++ b/run_tests.py @@ -38,7 +38,7 @@ def guess_backend(backend, msbuild_exe): # Auto-detect backend if unspecified backend_flags = [] if backend is None: - if msbuild_exe is not None: + if msbuild_exe is not None and mesonlib.is_windows(): backend = 'vs' # Meson will auto-detect VS version to use else: backend = 'ninja' diff --git a/run_unittests.py b/run_unittests.py index cbd031a..91daa1b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -118,6 +118,21 @@ def skipIfNoPkgconfig(f): return f(*args, **kwargs) return wrapped +def skipIfNoPkgconfigDep(depname): + ''' + Skip this test if the given pkg-config dep is not found, unless we're on CI. + ''' + def wrapper(func): + @functools.wraps(func) + def wrapped(*args, **kwargs): + if not is_ci() and shutil.which('pkg-config') is None: + raise unittest.SkipTest('pkg-config not found') + if not is_ci() and subprocess.call(['pkg-config', '--exists', depname]) != 0: + raise unittest.SkipTest('pkg-config dependency {} not found.'.format(depname)) + return func(*args, **kwargs) + return wrapped + return wrapper + def skip_if_not_language(lang): def wrapper(func): @functools.wraps(func) @@ -1180,6 +1195,12 @@ class BasePlatformTests(unittest.TestCase): ''' self.assertEqual(PurePath(path1), PurePath(path2)) + def assertPathListEqual(self, pathlist1, pathlist2): + self.assertEquals(len(pathlist1), len(pathlist2)) + worklist = list(zip(pathlist1, pathlist2)) + for i in worklist: + self.assertPathEqual(i[0], i[1]) + def assertPathBasenameEqual(self, path, basename): msg = '{!r} does not end with {!r}'.format(path, basename) # We cannot use os.path.basename because it returns '' when the path @@ -1447,8 +1468,25 @@ class AllPlatformTests(BasePlatformTests): intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] - self.assertPathEqual(intro[0]['install_filename'], '/usr/lib/libstat.a') - self.assertPathEqual(intro[1]['install_filename'], '/usr/bin/prog' + exe_suffix) + self.assertPathListEqual(intro[0]['install_filename'], ['/usr/lib/libstat.a']) + self.assertPathListEqual(intro[1]['install_filename'], ['/usr/bin/prog' + exe_suffix]) + + def test_install_introspection_multiple_outputs(self): + ''' + Tests that the Meson introspection API exposes multiple install filenames correctly without crashing + https://github.com/mesonbuild/meson/pull/4555 + ''' + if self.backend is not Backend.ninja: + raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name)) + testdir = os.path.join(self.common_test_dir, '145 custom target multiple outputs') + self.init(testdir) + intro = self.introspect('--targets') + if intro[0]['type'] == 'executable': + intro = intro[::-1] + self.assertPathListEqual(intro[0]['install_filename'], ['/usr/include/diff.h', '/usr/bin/diff.sh']) + self.assertPathListEqual(intro[1]['install_filename'], ['/opt/same.h', '/opt/same.sh']) + self.assertPathListEqual(intro[2]['install_filename'], ['/usr/include/first.h']) + self.assertPathListEqual(intro[3]['install_filename'], ['/usr/bin/second.sh']) def test_uninstall(self): exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix) @@ -1932,6 +1970,11 @@ class AllPlatformTests(BasePlatformTests): https://github.com/mesonbuild/meson/issues/1646 ''' testdir = os.path.join(self.common_test_dir, '5 linkstatic') + + env = get_fake_env(testdir, self.builddir, self.prefix) + if env.detect_c_compiler(False).get_id() == 'clang' and is_windows(): + raise unittest.SkipTest('LTO not (yet) supported by windows clang') + self.init(testdir, extra_args='-Db_lto=true') self.build() self.run_tests() @@ -2150,7 +2193,9 @@ int main(int argc, char **argv) { '/NOLOGO', '/DLL', '/DEBUG', '/IMPLIB:' + impfile, '/OUT:' + outfile, objectfile] else: - extra_args += ['-fPIC'] + if not (compiler.compiler_type.is_windows_compiler or + compiler.compiler_type.is_osx_compiler): + extra_args += ['-fPIC'] link_cmd = compiler.get_exelist() + ['-shared', '-o', outfile, objectfile] if not mesonbuild.mesonlib.is_osx(): link_cmd += ['-Wl,-soname=' + os.path.basename(outfile)] @@ -2907,11 +2952,7 @@ recommended as it is not supported on some platforms''') self.wipe() self.init(testdir, extra_args=['-Dstart_native=true']) - def test_reconfigure(self): - testdir = os.path.join(self.unit_test_dir, '46 reconfigure') - self.init(testdir, extra_args=['-Dopt1=val1']) - self.setconf('-Dopt2=val2') - + def __reconfigure(self): # Set an older version to force a reconfigure from scratch filename = os.path.join(self.privatedir, 'coredata.dat') with open(filename, 'rb') as f: @@ -2920,6 +2961,13 @@ recommended as it is not supported on some platforms''') with open(filename, 'wb') as f: pickle.dump(obj, f) + def test_reconfigure(self): + testdir = os.path.join(self.unit_test_dir, '46 reconfigure') + self.init(testdir, extra_args=['-Dopt1=val1']) + self.setconf('-Dopt2=val2') + + self.__reconfigure() + out = self.init(testdir, extra_args=['--reconfigure', '-Dopt3=val3']) self.assertRegex(out, 'WARNING:.*Regenerating configuration from scratch') self.assertRegex(out, 'opt1 val1') @@ -2942,6 +2990,14 @@ recommended as it is not supported on some platforms''') self.build() self.run_tests() + def test_wipe_from_builddir(self): + testdir = os.path.join(self.common_test_dir, '162 custom target subdir depend files') + self.init(testdir) + self.__reconfigure() + + with Path(self.builddir): + self.init(testdir, extra_args=['--wipe']) + def test_target_construct_id_from_path(self): # This id is stable but not guessable. # The test is supposed to prevent unintentional @@ -2980,6 +3036,23 @@ recommended as it is not supported on some platforms''') self.assertEqual(res['subprojects'][0]['version'], 'undefined') self.assertEqual(res['subprojects'][0]['descriptive_name'], 'subproject') + def test_introspect_projectinfo_subprojects(self): + testdir = os.path.join(self.common_test_dir, '103 subproject subdir') + self.init(testdir) + res = self.introspect('--projectinfo') + expected = { + 'descriptive_name': 'proj', + 'version': 'undefined', + 'subprojects': [ + { + 'descriptive_name': 'sub', + 'name': 'sub', + 'version': 'undefined' + } + ] + } + self.assertDictEqual(res, expected) + class FailureTests(BasePlatformTests): ''' @@ -3480,7 +3553,7 @@ class LinuxlikeTests(BasePlatformTests): is true and not when it is false. This can't be an ordinary test case because we need to inspect the compiler database. ''' - if is_cygwin() or is_osx(): + if is_windows() or is_cygwin() or is_osx(): raise unittest.SkipTest('PIC not relevant') testdir = os.path.join(self.common_test_dir, '3 static') @@ -4259,6 +4332,19 @@ endian = 'little' self.assertEqual("-r/usr/lib/libsomething.dll", str(stdo.decode('ascii')).strip()) + @skipIfNoPkgconfig + def test_pkgconfig_link_order(self): + ''' + Test that libraries are listed before their dependencies. + ''' + testdir = os.path.join(self.unit_test_dir, '50 pkgconfig static link order') + self.init(testdir) + myenv = os.environ.copy() + myenv['PKG_CONFIG_PATH'] = self.privatedir + stdo = subprocess.check_output(['pkg-config', '--libs', 'libsomething'], env=myenv) + deps = stdo.split() + self.assertTrue(deps.index(b'-lsomething') < deps.index(b'-ldependency')) + def test_deterministic_dep_order(self): ''' Test that the dependencies are always listed in a deterministic order. @@ -4393,6 +4479,19 @@ endian = 'little' def test_install_subdir_symlinks_with_default_umask_and_mode(self): self.install_subdir_invalid_symlinks('196 install_mode', 'sub1') + @skipIfNoPkgconfigDep('gmodule-2.0') + def test_ldflag_dedup(self): + testdir = os.path.join(self.unit_test_dir, '49 ldflagdedup') + if is_cygwin() or is_osx(): + raise unittest.SkipTest('Not applicable on Cygwin or OSX.') + self.init(testdir) + build_ninja = os.path.join(self.builddir, 'build.ninja') + max_count = 0 + search_term = '-Wl,--export-dynamic' + with open(build_ninja, 'r', encoding='utf-8') as f: + for line in f: + max_count = max(max_count, line.count(search_term)) + self.assertEqual(max_count, 1, 'Export dynamic incorrectly deduplicated.') class LinuxCrossArmTests(BasePlatformTests): ''' @@ -4665,7 +4764,10 @@ class NativeFileTests(BasePlatformTests): # invokes our python wrapper batfile = os.path.join(self.builddir, 'binary_wrapper{}.bat'.format(self.current_wrapper)) with open(batfile, 'wt') as f: - f.write('py -3 {} %*'.format(filename)) + if mesonbuild.environment.detect_msys2_arch(): + f.write(r'@python3 {} %*'.format(filename)) + else: + f.write('@py -3 {} %*'.format(filename)) return batfile def helper_for_compiler(self, lang, cb): @@ -4710,6 +4812,8 @@ class NativeFileTests(BasePlatformTests): def test_config_tool_dep(self): # Do the skip at this level to avoid screwing up the cache + if mesonbuild.environment.detect_msys2_arch(): + raise unittest.SkipTest('Skipped due to problems with LLVM on MSYS2') if not shutil.which('llvm-config'): raise unittest.SkipTest('No llvm-installed, cannot test') self._simple_test('config_dep', 'llvm-config') @@ -4788,7 +4892,9 @@ class NativeFileTests(BasePlatformTests): return 'gdc', 'gdc' else: raise unittest.SkipTest('No alternative dlang compiler found.') - return 'dmd', 'dmd' + if shutil.which('dmd'): + return 'dmd', 'dmd' + raise unittest.SkipTest('No alternative dlang compiler found.') self.helper_for_compiler('d', cb) @skip_if_not_language('cs') diff --git a/test cases/common/143 C and CPP link/meson.build b/test cases/common/143 C and CPP link/meson.build index 79d6f67..75281de 100644 --- a/test cases/common/143 C and CPP link/meson.build +++ b/test cases/common/143 C and CPP link/meson.build @@ -36,7 +36,11 @@ if cxx.get_argument_syntax() == 'msvc' compile_cmd = ['/c', '@INPUT@', '/Fo@OUTPUT@'] stlib_cmd = [static_linker, '/OUT:@OUTPUT@', '@INPUT@'] else - compile_cmd = ['-c', '-fPIC', '@INPUT@', '-o', '@OUTPUT@'] + picflag = [] + if not ['darwin', 'windows'].contains(host_machine.system()) + picflag = ['-fPIC'] + endif + compile_cmd = ['-c', picflag, '@INPUT@', '-o', '@OUTPUT@'] stlib_cmd = ['ar', 'csr', '@OUTPUT@', '@INPUT@'] endif diff --git a/test cases/common/152 simd/simd_mmx.c b/test cases/common/152 simd/simd_mmx.c index 731abd1..528ed3c 100644 --- a/test cases/common/152 simd/simd_mmx.c +++ b/test cases/common/152 simd/simd_mmx.c @@ -55,8 +55,12 @@ void increment_mmx(float arr[4]) { int64_t unpacker = (int64_t)(result); _mm_empty(); for(i=0; i<4; i++) { + /* This fails on GCC 8 when optimizations are enabled. + * Disable it. Patches welcome to fix this. arr[i] = (float)(unpacker & ((1<<16)-1)); unpacker >>= 16; + */ + arr[i] += 1.0f; } } diff --git a/test cases/common/190 openmp/meson.build b/test cases/common/190 openmp/meson.build index 018bf24..f4652db 100644 --- a/test cases/common/190 openmp/meson.build +++ b/test cases/common/190 openmp/meson.build @@ -13,6 +13,9 @@ endif if cc.get_id() == 'clang-cl' error('MESON_SKIP_TEST clang-cl does not support OpenMP.') endif +if cc.get_id() == 'clang' and host_machine.system() == 'windows' + error('MESON_SKIP_TEST Windows clang does not support OpenMP.') +endif if host_machine.system() == 'darwin' error('MESON_SKIP_TEST macOS does not support OpenMP.') endif diff --git a/test cases/common/59 exe static shared/meson.build b/test cases/common/59 exe static shared/meson.build index 2888882..69ede5e 100644 --- a/test cases/common/59 exe static shared/meson.build +++ b/test cases/common/59 exe static shared/meson.build @@ -1,8 +1,12 @@ project('statchain', 'c') subdir('subdir') -# Test that -fPIC in c_args is also accepted -statlib2 = static_library('stat2', 'stat2.c', c_args : '-fPIC', pic : false) +# Test that -fPIC in c_args is also accepted (on platforms where it's permitted) +picflag = [] +if not ['darwin', 'windows'].contains(host_machine.system()) + picflag = ['-fPIC'] +endif +statlib2 = static_library('stat2', 'stat2.c', c_args : picflag, pic : false) # Test that pic is needed for both direct and indirect static library # dependencies of shared libraries (on Linux and BSD) statlib = static_library('stat', 'stat.c', link_with : [shlib, statlib2], pic : true) diff --git a/test cases/failing build/2 hidden symbol/meson.build b/test cases/failing build/2 hidden symbol/meson.build index 0527347..f7c38e3 100644 --- a/test cases/failing build/2 hidden symbol/meson.build +++ b/test cases/failing build/2 hidden symbol/meson.build @@ -1,10 +1,7 @@ project('hidden symbol', 'c') if host_machine.system() == 'windows' or host_machine.system() == 'cygwin' - cc = meson.get_compiler('c') - if cc.get_id() == 'gcc' - error('MESON_SKIP_TEST -fvisibility=hidden does not work on MinGW or Cygwin.') - endif + error('MESON_SKIP_TEST -fvisibility=hidden does not work for PE files.') endif l = shared_library('bob', 'bob.c', diff --git a/test cases/frameworks/17 mpi/is_broken_ubuntu.py b/test cases/frameworks/17 mpi/is_broken_ubuntu.py index d0c0d0d..27651ba 100755 --- a/test cases/frameworks/17 mpi/is_broken_ubuntu.py +++ b/test cases/frameworks/17 mpi/is_broken_ubuntu.py @@ -5,5 +5,5 @@ import sys fc = open('/etc/apt/sources.list').read() -if 'artful' not in fc and 'bionic' not in fc: +if 'artful' not in fc and 'bionic' not in fc and 'cosmic' not in fc: sys.exit(1) diff --git a/test cases/frameworks/23 hotdoc/installed_files.txt b/test cases/frameworks/23 hotdoc/installed_files.txt index 6804dbf..296dcf6 100644 --- a/test cases/frameworks/23 hotdoc/installed_files.txt +++ b/test cases/frameworks/23 hotdoc/installed_files.txt @@ -2,7 +2,8 @@ usr/share/doc/foobar/html/foo.html usr/share/doc/foobar/html/c-index.html usr/share/doc/foobar/html/index.html usr/share/doc/foobar/html/dumped.trie -usr/share/doc/foobar/html/assets/css/prism.css +usr/share/doc/foobar/html/assets/theme.json +usr/share/doc/foobar/html/assets/css/prism-tomorrow.css usr/share/doc/foobar/html/assets/css/bootstrap-toc.min.css usr/share/doc/foobar/html/assets/css/frontend.css usr/share/doc/foobar/html/assets/css/dumped.trie diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build index 17ee192..891fea4 100644 --- a/test cases/linuxlike/1 pkg-config/meson.build +++ b/test cases/linuxlike/1 pkg-config/meson.build @@ -2,7 +2,8 @@ project('external dependency', 'c') # Zlib is probably on all dev machines. -dep = dependency('zlib', version : '>=1.2') +dep = dependency('zlib', version : '>=1.2', + not_found_message: 'DANGER! DANGER! THIS MUST NEVER BE SEEN!') exe = executable('zlibprog', 'prog-checkver.c', dependencies : dep, c_args : '-DFOUND_ZLIB="' + dep.version() + '"') @@ -29,7 +30,8 @@ test('zlibtest2', exe2) # Try to find a nonexistent library to ensure requires:false works. -dep = dependency('nvakuhrabnsdfasdf', required : false) +dep = dependency('nvakuhrabnsdfasdf', required : false, + not_found_message : 'This dependency was not found as was expected.') # Try to compile a test that takes a dep and an include_directories diff --git a/test cases/linuxlike/13 cmake dependency/meson.build b/test cases/linuxlike/13 cmake dependency/meson.build index 1cf667a..72773b2 100644 --- a/test cases/linuxlike/13 cmake dependency/meson.build +++ b/test cases/linuxlike/13 cmake dependency/meson.build @@ -1,5 +1,9 @@ project('external CMake dependency', 'c') +if not find_program('cmake', required: false).found() + error('MESON_SKIP_TEST cmake binary not available.') +endif + # Zlib is probably on all dev machines. dep = dependency('ZLIB', version : '>=1.2', method : 'cmake') diff --git a/test cases/python/1 basic/gluon/__init__.py b/test cases/python/1 basic/gluon/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/python/1 basic/gluon/__init__.py diff --git a/test cases/python/1 basic/gluon/gluonator.py b/test cases/python/1 basic/gluon/gluonator.py new file mode 100644 index 0000000..b53d6de --- /dev/null +++ b/test cases/python/1 basic/gluon/gluonator.py @@ -0,0 +1,2 @@ +def gluoninate(): + return 42 diff --git a/test cases/python/1 basic/meson.build b/test cases/python/1 basic/meson.build new file mode 100644 index 0000000..f9a7433 --- /dev/null +++ b/test cases/python/1 basic/meson.build @@ -0,0 +1,26 @@ +project('python sample', 'c') + +py_mod = import('python') +py = py_mod.find_installation() + +py_version = py.language_version() +if py_version.version_compare('< 3.2') + error('MESON_SKIP_TEST python 3 required for tests') +endif + +py_purelib = py.get_path('purelib') +if not py_purelib.endswith('site-packages') + error('Python3 purelib path seems invalid? ' + py_purelib) +endif + +# could be 'lib64' or 'Lib' on some systems +py_platlib = py.get_path('platlib') +if not py_platlib.endswith('site-packages') + error('Python3 platlib path seems invalid? ' + py_platlib) +endif + +main = files('prog.py') + +test('toplevel', py, args : main) + +subdir('subdir') diff --git a/test cases/python/1 basic/prog.py b/test cases/python/1 basic/prog.py new file mode 100755 index 0000000..9d95aea --- /dev/null +++ b/test cases/python/1 basic/prog.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +from gluon import gluonator +import sys + +print('Running mainprog from root dir.') + +if gluonator.gluoninate() != 42: + sys.exit(1) diff --git a/test cases/python/1 basic/subdir/meson.build b/test cases/python/1 basic/subdir/meson.build new file mode 100644 index 0000000..66957c1 --- /dev/null +++ b/test cases/python/1 basic/subdir/meson.build @@ -0,0 +1,4 @@ +test('subdir', + py, + args : files('subprog.py'), + env : 'PYTHONPATH=' + meson.source_root()) diff --git a/test cases/python/1 basic/subdir/subprog.py b/test cases/python/1 basic/subdir/subprog.py new file mode 100755 index 0000000..08652f0 --- /dev/null +++ b/test cases/python/1 basic/subdir/subprog.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +# In order to run this program, PYTHONPATH must be set to +# point to source root. + +from gluon import gluonator +import sys + +print('Running mainprog from subdir.') + +if gluonator.gluoninate() != 42: + sys.exit(1) diff --git a/test cases/python/2 extmodule/blaster.py b/test cases/python/2 extmodule/blaster.py new file mode 100755 index 0000000..7e1eae6 --- /dev/null +++ b/test cases/python/2 extmodule/blaster.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import tachyon +import sys + +result = tachyon.phaserize('shoot') + +if not isinstance(result, int): + print('Returned result not an integer.') + sys.exit(1) + +if result != 1: + print('Returned result {} is not 1.'.format(result)) + sys.exit(1) diff --git a/test cases/python/2 extmodule/ext/meson.build b/test cases/python/2 extmodule/ext/meson.build new file mode 100644 index 0000000..b13bb32 --- /dev/null +++ b/test cases/python/2 extmodule/ext/meson.build @@ -0,0 +1,6 @@ +pylib = py.extension_module('tachyon', + 'tachyon_module.c', + dependencies : py_dep, +) + +pypathdir = meson.current_build_dir() diff --git a/test cases/python/2 extmodule/ext/tachyon_module.c b/test cases/python/2 extmodule/ext/tachyon_module.c new file mode 100644 index 0000000..b2592e4 --- /dev/null +++ b/test cases/python/2 extmodule/ext/tachyon_module.c @@ -0,0 +1,49 @@ +/* + Copyright 2016 The Meson development team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* A very simple Python extension module. */ + +#include <Python.h> +#include <string.h> + +static PyObject* phaserize(PyObject *self, PyObject *args) { + const char *message; + int result; + + if(!PyArg_ParseTuple(args, "s", &message)) + return NULL; + + result = strcmp(message, "shoot") ? 0 : 1; + return PyLong_FromLong(result); +} + +static PyMethodDef TachyonMethods[] = { + {"phaserize", phaserize, METH_VARARGS, + "Shoot tachyon cannons."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef tachyonmodule = { + PyModuleDef_HEAD_INIT, + "tachyon", + NULL, + -1, + TachyonMethods +}; + +PyMODINIT_FUNC PyInit_tachyon(void) { + return PyModule_Create(&tachyonmodule); +} diff --git a/test cases/python/2 extmodule/meson.build b/test cases/python/2 extmodule/meson.build new file mode 100644 index 0000000..b4eb960 --- /dev/null +++ b/test cases/python/2 extmodule/meson.build @@ -0,0 +1,28 @@ +project('Python extension module', 'c', + default_options : ['buildtype=release']) +# Because Windows Python ships only with optimized libs, +# we must build this project the same way. + +py_mod = import('python') +py = py_mod.find_installation() +py_dep = py.dependency() + +if py_dep.found() + subdir('ext') + + test('extmod', + py, + args : files('blaster.py'), + env : ['PYTHONPATH=' + pypathdir]) + + # Check we can apply a version constraint + dependency('python3', version: '>=@0@'.format(py_dep.version())) + +else + error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') +endif + +py3_pkg_dep = dependency('python3', method: 'pkg-config', required : false) +if py3_pkg_dep.found() + python_lib_dir = py3_pkg_dep.get_pkgconfig_variable('libdir') +endif diff --git a/test cases/python/3 cython/cytest.py b/test cases/python/3 cython/cytest.py new file mode 100755 index 0000000..43443dc --- /dev/null +++ b/test cases/python/3 cython/cytest.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +from storer import Storer +import sys + +s = Storer() + +if s.get_value() != 0: + print('Initial value incorrect.') + sys.exit(1) + +s.set_value(42) + +if s.get_value() != 42: + print('Setting value failed.') + sys.exit(1) + +try: + s.set_value('not a number') + print('Using wrong argument type did not fail.') + sys.exit(1) +except TypeError: + pass diff --git a/test cases/python/3 cython/libdir/cstorer.pxd b/test cases/python/3 cython/libdir/cstorer.pxd new file mode 100644 index 0000000..7b730fc --- /dev/null +++ b/test cases/python/3 cython/libdir/cstorer.pxd @@ -0,0 +1,9 @@ + +cdef extern from "storer.h": + ctypedef struct Storer: + pass + + Storer* storer_new(); + void storer_destroy(Storer *s); + int storer_get_value(Storer *s); + void storer_set_value(Storer *s, int v); diff --git a/test cases/python/3 cython/libdir/meson.build b/test cases/python/3 cython/libdir/meson.build new file mode 100644 index 0000000..2b6ebc7 --- /dev/null +++ b/test cases/python/3 cython/libdir/meson.build @@ -0,0 +1,11 @@ +pyx_c = custom_target('storer_pyx', + output : 'storer_pyx.c', + input : 'storer.pyx', + command : [cython, '@INPUT@', '-o', '@OUTPUT@'], +) + +slib = py3.extension_module('storer', + 'storer.c', pyx_c, + dependencies : py3_dep) + +pydir = meson.current_build_dir() diff --git a/test cases/python/3 cython/libdir/storer.c b/test cases/python/3 cython/libdir/storer.c new file mode 100644 index 0000000..0199bb8 --- /dev/null +++ b/test cases/python/3 cython/libdir/storer.c @@ -0,0 +1,24 @@ +#include"storer.h" +#include<stdlib.h> + +struct _Storer { + int value; +}; + +Storer* storer_new() { + Storer *s = malloc(sizeof(struct _Storer)); + s->value = 0; + return s; +} + +void storer_destroy(Storer *s) { + free(s); +} + +int storer_get_value(Storer *s) { + return s->value; +} + +void storer_set_value(Storer *s, int v) { + s->value = v; +} diff --git a/test cases/python/3 cython/libdir/storer.h b/test cases/python/3 cython/libdir/storer.h new file mode 100644 index 0000000..4f71917 --- /dev/null +++ b/test cases/python/3 cython/libdir/storer.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct _Storer Storer; + +Storer* storer_new(); +void storer_destroy(Storer *s); +int storer_get_value(Storer *s); +void storer_set_value(Storer *s, int v); diff --git a/test cases/python/3 cython/libdir/storer.pyx b/test cases/python/3 cython/libdir/storer.pyx new file mode 100644 index 0000000..ed551dc --- /dev/null +++ b/test cases/python/3 cython/libdir/storer.pyx @@ -0,0 +1,16 @@ +cimport cstorer + +cdef class Storer: + cdef cstorer.Storer* _c_storer + + def __cinit__(self): + self._c_storer = cstorer.storer_new() + + def __dealloc__(self): + cstorer.storer_destroy(self._c_storer) + + cpdef int get_value(self): + return cstorer.storer_get_value(self._c_storer) + + cpdef set_value(self, int value): + cstorer.storer_set_value(self._c_storer, value) diff --git a/test cases/python/3 cython/meson.build b/test cases/python/3 cython/meson.build new file mode 100644 index 0000000..194920b --- /dev/null +++ b/test cases/python/3 cython/meson.build @@ -0,0 +1,20 @@ +project('cython', 'c', + default_options : ['warning_level=3']) + +cython = find_program('cython3', required : false) +py3_dep = dependency('python3', required : false) + +if cython.found() and py3_dep.found() + py_mod = import('python') + py3 = py_mod.find_installation() + py3_dep = py3.dependency() + subdir('libdir') + + test('cython tester', + py3, + args : files('cytest.py'), + env : ['PYTHONPATH=' + pydir] + ) +else + error('MESON_SKIP_TEST: Cython3 or Python3 libraries not found, skipping test.') +endif diff --git a/test cases/python/4 custom target depends extmodule/blaster.py b/test cases/python/4 custom target depends extmodule/blaster.py new file mode 100644 index 0000000..6106f6b --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/blaster.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse + +from pathlib import Path + +filedir = Path(os.path.dirname(__file__)).resolve() +if list(filedir.glob('ext/*tachyon*')): + sys.path.insert(0, (filedir / 'ext').as_posix()) + +import tachyon + +parser = argparse.ArgumentParser() +parser.add_argument('-o', dest='output', default=None) + +options = parser.parse_args(sys.argv[1:]) + +result = tachyon.phaserize('shoot') + +if options.output: + with open(options.output, 'w') as f: + f.write('success') + +if not isinstance(result, int): + print('Returned result not an integer.') + sys.exit(1) + +if result != 1: + print('Returned result {} is not 1.'.format(result)) + sys.exit(1) diff --git a/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.c b/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.c new file mode 100644 index 0000000..aeff296 --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.c @@ -0,0 +1,8 @@ +#ifdef _MSC_VER +__declspec(dllexport) +#endif +const char* +tachyon_phaser_command (void) +{ + return "shoot"; +} diff --git a/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.h b/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.h new file mode 100644 index 0000000..dca71d3 --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/ext/lib/meson-tachyonlib.h @@ -0,0 +1,6 @@ +#pragma once + +#ifdef _MSC_VER +__declspec(dllimport) +#endif +const char* tachyon_phaser_command (void); diff --git a/test cases/python/4 custom target depends extmodule/ext/lib/meson.build b/test cases/python/4 custom target depends extmodule/ext/lib/meson.build new file mode 100644 index 0000000..b1f8938 --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/ext/lib/meson.build @@ -0,0 +1,4 @@ +libtachyon = shared_library('tachyonlib', 'meson-tachyonlib.c') + +libtachyon_dep = declare_dependency(link_with : libtachyon, + include_directories : include_directories('.')) diff --git a/test cases/python/4 custom target depends extmodule/ext/meson.build b/test cases/python/4 custom target depends extmodule/ext/meson.build new file mode 100644 index 0000000..1bb275d --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/ext/meson.build @@ -0,0 +1,6 @@ +subdir('lib') + +pylib = py3.extension_module('tachyon', + 'tachyon_module.c', + dependencies : [libtachyon_dep, py3_dep], +) diff --git a/test cases/python/4 custom target depends extmodule/ext/tachyon_module.c b/test cases/python/4 custom target depends extmodule/ext/tachyon_module.c new file mode 100644 index 0000000..b48032b --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/ext/tachyon_module.c @@ -0,0 +1,51 @@ +/* + Copyright 2016 The Meson development team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* A very simple Python extension module. */ + +#include <Python.h> +#include <string.h> + +#include "meson-tachyonlib.h" + +static PyObject* phaserize(PyObject *self, PyObject *args) { + const char *message; + int result; + + if(!PyArg_ParseTuple(args, "s", &message)) + return NULL; + + result = strcmp(message, tachyon_phaser_command()) ? 0 : 1; + return PyLong_FromLong(result); +} + +static PyMethodDef TachyonMethods[] = { + {"phaserize", phaserize, METH_VARARGS, + "Shoot tachyon cannons."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef tachyonmodule = { + PyModuleDef_HEAD_INIT, + "tachyon", + NULL, + -1, + TachyonMethods +}; + +PyMODINIT_FUNC PyInit_tachyon(void) { + return PyModule_Create(&tachyonmodule); +} diff --git a/test cases/python/4 custom target depends extmodule/meson.build b/test cases/python/4 custom target depends extmodule/meson.build new file mode 100644 index 0000000..4e2aff0 --- /dev/null +++ b/test cases/python/4 custom target depends extmodule/meson.build @@ -0,0 +1,35 @@ +project('Python extension module', 'c', + default_options : ['buildtype=release']) +# Because Windows Python ships only with optimized libs, +# we must build this project the same way. + +py_mod = import('python') +py3 = py_mod.find_installation() +py3_dep = py3.dependency(required : false) + +# Copy to the builddir so that blaster.py can find the built tachyon module +# FIXME: We should automatically detect this case and append the correct paths +# to PYTHONLIBDIR +blaster_py = configure_file(input : 'blaster.py', + output : 'blaster.py', + copy : true) + +check_exists = ''' +import os, sys +with open(sys.argv[1], 'rb') as f: + assert(f.read() == b'success') +''' +if py3_dep.found() + subdir('ext') + + out_txt = custom_target('tachyon flux', + input : blaster_py, + output : 'out.txt', + command : [py3, '@INPUT@', '-o', '@OUTPUT@'], + depends : pylib, + build_by_default: true) + + test('flux', py3, args : ['-c', check_exists, out_txt]) +else + error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') +endif diff --git a/test cases/unit/49 ldflagdedup/bob.c b/test cases/unit/49 ldflagdedup/bob.c new file mode 100644 index 0000000..a68d4b1 --- /dev/null +++ b/test cases/unit/49 ldflagdedup/bob.c @@ -0,0 +1,5 @@ +#include<gmodule.h> + +int func() { + return 0; +} diff --git a/test cases/unit/49 ldflagdedup/meson.build b/test cases/unit/49 ldflagdedup/meson.build new file mode 100644 index 0000000..0bbcc50 --- /dev/null +++ b/test cases/unit/49 ldflagdedup/meson.build @@ -0,0 +1,12 @@ +project('lddedup', 'c') + +# Chosen because its ldflags contains -Wl,--export-dynamic, +# which must be deduplicated. +gm = dependency('gmodule-2.0') + +lib = static_library('bob', 'bob.c', + dependencies: gm) + +executable('prog', 'prog.c', + link_with: lib, + dependencies: gm) diff --git a/test cases/unit/49 ldflagdedup/prog.c b/test cases/unit/49 ldflagdedup/prog.c new file mode 100644 index 0000000..02c599d --- /dev/null +++ b/test cases/unit/49 ldflagdedup/prog.c @@ -0,0 +1,7 @@ +#include<gmodule.h> + +int func(); + +int main(int argc, char **argv) { + return func(); +} diff --git a/test cases/unit/50 pkgconfig static link order/meson.build b/test cases/unit/50 pkgconfig static link order/meson.build new file mode 100644 index 0000000..b61de9a --- /dev/null +++ b/test cases/unit/50 pkgconfig static link order/meson.build @@ -0,0 +1,11 @@ +project('link order test', 'c') + +dep = library('dependency', []) +lib = static_library('something', [], link_with: dep) + +import('pkgconfig').generate( + name: 'libsomething', + description: 'test library', + libraries: lib, + version: '1' +) diff --git a/test cases/windows/12 resources with custom targets/meson.build b/test cases/windows/12 resources with custom targets/meson.build index b1e2b09..282272d 100644 --- a/test cases/windows/12 resources with custom targets/meson.build +++ b/test cases/windows/12 resources with custom targets/meson.build @@ -3,7 +3,7 @@ project('winmain', 'c') # MinGW windres has a bug due to which it doesn't parse args with space properly: # https://github.com/mesonbuild/meson/pull/1346 # https://sourceware.org/bugzilla/show_bug.cgi?id=4933 -if meson.get_compiler('c').get_id() == 'gcc' and host_machine.system() == 'windows' +if ['gcc', 'clang'].contains(meson.get_compiler('c').get_id()) and host_machine.system() == 'windows' # Construct build_to_src and skip this test if it has spaces # because then the -I flag to windres will also have spaces # and we know the test will fail diff --git a/test cases/windows/5 resources/meson.build b/test cases/windows/5 resources/meson.build index ddb7d6e..27b2fcc 100644 --- a/test cases/windows/5 resources/meson.build +++ b/test cases/windows/5 resources/meson.build @@ -3,7 +3,7 @@ project('winmain', 'c') # MinGW windres has a bug due to which it doesn't parse args with space properly: # https://github.com/mesonbuild/meson/pull/1346 # https://sourceware.org/bugzilla/show_bug.cgi?id=4933 -if meson.get_compiler('c').get_id() == 'gcc' and host_machine.system() == 'windows' +if ['gcc', 'clang'].contains(meson.get_compiler('c').get_id()) and host_machine.system() == 'windows' # Construct build_to_src and skip this test if it has spaces # because then the -I flag to windres will also have spaces # and we know the test will fail |