diff options
54 files changed, 952 insertions, 160 deletions
@@ -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/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/Reference-manual.md b/docs/markdown/Reference-manual.md index a3166e5..2fc61d5 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 @@ -928,6 +928,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 +1022,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 +2073,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 5ccda76..9b0dfaa 100644 --- a/docs/markdown/Release-notes-for-0.49.0.md +++ b/docs/markdown/Release-notes-for-0.49.0.md @@ -44,7 +44,9 @@ 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 +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` 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/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index f8b8b0d..2a5c976 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1822,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 de401ce..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.49.0' +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 2ccd31e..44f40e5 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..4d4445e 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1957,10 +1957,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 +2356,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): @@ -3075,7 +3072,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/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/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 5677efa..91daa1b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1195,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 @@ -1462,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) @@ -2929,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: @@ -2942,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') @@ -2964,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 @@ -3002,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): ''' @@ -4281,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. 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/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' +) |