diff options
80 files changed, 1227 insertions, 489 deletions
diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt index 367eba3..6246ffe 100644 --- a/cross/ubuntu-armhf.txt +++ b/cross/ubuntu-armhf.txt @@ -3,6 +3,7 @@ # when cross compiled binaries can't be run we don't do that c = '/usr/bin/arm-linux-gnueabihf-gcc-7' cpp = '/usr/bin/arm-linux-gnueabihf-g++-7' +rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] ar = '/usr/arm-linux-gnueabihf/bin/ar' strip = '/usr/arm-linux-gnueabihf/bin/strip' pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim index e06b2df..83dd66a 100644 --- a/data/syntax-highlighting/vim/syntax/meson.vim +++ b/data/syntax-highlighting/vim/syntax/meson.vim @@ -29,7 +29,7 @@ endif let s:cpo_save = &cpo setlocal cpo&vim -" https://github.com/mesonbuild/meson/wiki/Syntax +" http://mesonbuild.com/Syntax.html syn keyword mesonConditional elif else if endif syn keyword mesonRepeat foreach endforeach syn keyword mesonOperator and not or @@ -55,7 +55,7 @@ syn match mesonEscape "\\N{\a\+\%(\s\a\+\)*}" contained syn match mesonEscape "\\$" " Meson only supports integer numbers -" https://github.com/mesonbuild/meson/wiki/Syntax#numbers +" http://mesonbuild.com/Syntax.html#numbers syn match mesonNumber "\<\d\+\>" " booleans diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md index cd7f07d..9ccdf83 100644 --- a/docs/markdown/Build-options.md +++ b/docs/markdown/Build-options.md @@ -16,6 +16,7 @@ Here is a simple option file. option('someoption', type : 'string', value : 'optval', description : 'An option') option('other_one', type : 'boolean', value : false) option('combo_opt', type : 'combo', choices : ['one', 'two', 'three'], value : 'three') +option('integer_opt', type : 'integer', min : 0, max : 5, value : 3) # Since 0.45.0 option('free_array_opt', type : 'array', value : ['one', 'two']) option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two']) ``` @@ -39,6 +40,14 @@ A combo allows any one of the values in the `choices` parameter to be selected. If no default value is set then the first value will be the default. +## Integers + +An integer option contains a single integer with optional upper and +lower values that are specified with the `min` and `max` keyword +arguments. + +This type is available since Meson version 0.45.0. + ### Arrays Arrays represent an array of strings. By default the array can contain @@ -49,7 +58,7 @@ empty. The `value` parameter specifies the default value of the option and if it is unset then the values of `choices` will be used as the default. -This type is new in version 0.44.0 +This type is available since version 0.44.0 ## Using build options @@ -98,6 +107,31 @@ $ meson configure "-Doption=['a,b', 'c,d']" The inner values must always be single quotes and the outer ones double quotes. +To change values in subprojects prepend the name of the subproject and +a colon: + +```console +$ meson configure -Dsubproject:option=newvalue +``` + **NOTE:** If you cannot call `meson configure` you likely have a old version of Meson. In that case you can call `mesonconf` instead, but that is deprecated in newer versions + +## Yielding to superproject option + +Suppose you have a master project and a subproject. In some cases it +might be useful to have an option that has the same value in both of +them. This can be achieved with the `yield` keyword. Suppose you have +an option definition like this: + +```meson +option('some_option', type : 'string', value : 'value', yield : true) +``` + +If you build this project on its own, this option behaves like +usual. However if you build this project as a subproject of another +project which also has an option called `some_option`, then calling +`get_option` returns the value of the superproject. If the value of +`yield` is `false`, `get_option` returns the value of the subproject's +option. diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md index 4def628..579417a 100644 --- a/docs/markdown/Compiler-properties.md +++ b/docs/markdown/Compiler-properties.md @@ -173,7 +173,7 @@ Does a structure contain a member? == Some platforms have different standard structures. Here's how one -would check if a struct called `mystruct` from header `myheader.h</hh> +would check if a struct called `mystruct` from header `myheader.h` contains a member called `some_member`. ```meson diff --git a/docs/markdown/Configuration.md b/docs/markdown/Configuration.md index 9db6370..d8fa54e 100644 --- a/docs/markdown/Configuration.md +++ b/docs/markdown/Configuration.md @@ -58,7 +58,7 @@ Note that if you want to define a C string, you need to do the quoting yourself like this: ```meson -conf.set('TOKEN', '"value"') +conf_data.set('TOKEN', '"value"') ``` Since this is such a common operation, Meson provides a convenience @@ -66,7 +66,7 @@ method: ```meson plain_var = 'value' -conf.set_quoted('TOKEN', plain_var) # becomes #define TOKEN "value" +conf_data.set_quoted('TOKEN', plain_var) # becomes #define TOKEN "value" ``` Often you have a boolean value in Meson but need to define the C/C++ @@ -74,12 +74,12 @@ token as 0 or 1. Meson provides a convenience function for this use case. ```meson -conf.set10(token, boolean_value) +conf_data.set10(token, boolean_value) # The line above is equivalent to this: if boolean_value - conf.set(token, 1) + conf_data.set(token, 1) else - conf.set(token, 0) + conf_data.set(token, 0) endif ``` @@ -90,19 +90,19 @@ file all the entries in the configuration data object. The replacements are the same as when generating `#mesondefine` entries: ```meson -cdata.set('FOO', '"string"') => #define FOO "string" -cdata.set('FOO', 'a_token') => #define FOO a_token -cdata.set('FOO', true) => #define FOO -cdata.set('FOO', false) => #undef FOO -cdata.set('FOO', 1) => #define FOO 1 -cdata.set('FOO', 0) => #define FOO 0 +conf_data.set('FOO', '"string"') => #define FOO "string" +conf_data.set('FOO', 'a_token') => #define FOO a_token +conf_data.set('FOO', true) => #define FOO +conf_data.set('FOO', false) => #undef FOO +conf_data.set('FOO', 1) => #define FOO 1 +conf_data.set('FOO', 0) => #define FOO 0 ``` In this mode, you can also specify a comment which will be placed before the value so that your generated files are self-documenting. ```meson -cdata.set('BAR', true, description : 'Set BAR if it is available') +conf_data.set('BAR', true, description : 'Set BAR if it is available') ``` Will produce: @@ -129,9 +129,11 @@ subprojects. At the top level we generate the file: ```meson +conf_data = configuration_data() +# Set data configure_file(input : 'projconfig.h.in', output : 'projconfig.h', - configuration : cdata_object) + configuration : conf_data) ``` Immediately afterwards we generate the include object. diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 189db72..6653cc1 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -89,12 +89,16 @@ to build dependencies manually when they are not. To make this work, the dependency must have Meson build definitions and it must declare its own dependency like this: +```meson foo_dep = declare_dependency(...) +``` Then any project that wants to use it can write out the following declaration in their main `meson.build` file. +```meson foo_dep = dependency('foo', fallback : ['foo', 'foo_dep']) +``` What this declaration means is that first Meson tries to look up the dependency from the system (such as by using pkg-config). If it is not diff --git a/docs/markdown/Generating-sources.md b/docs/markdown/Generating-sources.md index ae1302b..2ea1021 100644 --- a/docs/markdown/Generating-sources.md +++ b/docs/markdown/Generating-sources.md @@ -4,7 +4,7 @@ short-description: Generation of source files before compilation # Generating sources - Sometimes source files need to be preprocessed before they are passed to the actual compiler. As an example you might want build an IDL compiler and then run some files through that to generate actual source files. In Meson this is done with [`generator()`](https://github.com/mesonbuild/meson/wiki/Reference-manual#generator) or [`custom_target()`](https://github.com/mesonbuild/meson/wiki/Reference-manual#custom_target). + Sometimes source files need to be preprocessed before they are passed to the actual compiler. As an example you might want build an IDL compiler and then run some files through that to generate actual source files. In Meson this is done with [`generator()`](Reference-manual.md#generator) or [`custom_target()`](Reference-manual.md#custom_target). ## Using custom_target() @@ -45,7 +45,7 @@ Generators are similar to custom targets, except that we define a *generator*, w Note that generators should only be used for outputs that will only be used as inputs for a build target or a custom target. When you use the processed output of a generator in multiple targets, the generator will be run multiple times to create outputs for each target. Each output will be created in a target-private directory `@BUILD_DIR@`. -If you want to generate files for general purposes such as for generating headers to be used by several sources, or data that will be installed, and so on, use a [`custom_target()`](https://github.com/mesonbuild/meson/wiki/Reference-manual#custom_target) instead. +If you want to generate files for general purposes such as for generating headers to be used by several sources, or data that will be installed, and so on, use a [`custom_target()`](Reference-manual.md#custom_target) instead. ```meson diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index b33009d..fbf9530 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -46,6 +46,25 @@ file called `foobar.h`, which you can then include in your sources. Returns an array containing: `[c_source, header_file]` or `[gresource_bundle]` +Example: + +```meson +gnome = import('gnome') + +asresources = gnome.compile_resources( + 'as-resources', 'data/asresources.gresource.xml', + source_dir: 'data', + c_name: 'as' +) + +executable( + meson.project_name(), + asresources, + dependencies: my_deps, + install: true +) +``` + ### gnome.generate_gir() Generates GObject introspection data. Takes one positional argument, diff --git a/docs/markdown/Icestorm-module.md b/docs/markdown/Icestorm-module.md new file mode 100644 index 0000000..896311f --- /dev/null +++ b/docs/markdown/Icestorm-module.md @@ -0,0 +1,27 @@ +# Unstable SIMD module + +This module provides is available since version 0.45.0. + +**Note**:Â this module is unstable. It is only provided as a technology +preview. Its API may change in arbitrary ways between releases or it +might be removed from Meson altogether. + +## Usage + +This module provides an experimental to create FPGA bitstreams using +the [IceStorm](http://www.clifford.at/icestorm/) suite of tools. + +The module exposes only one method called `project` and it is used +like this: + + is.project('projname', + <verilog files>, + constraint_file : <pcf file>, + ) + +The input to this function is the set of Verilog files and a +constraint file. This produces output files called `projname.asc`, +`projname.blif` and `projname.bin`. In addition it creates two run +targets called `projname-time` for running timing analysis and +`projname-upload` that uploads the generated bitstream to an FPGA +devide using the `iceprog` programming executable. diff --git a/docs/markdown/Installing.md b/docs/markdown/Installing.md index 2663ff4..4670544 100644 --- a/docs/markdown/Installing.md +++ b/docs/markdown/Installing.md @@ -26,7 +26,7 @@ Other install commands are the following. ```meson install_headers('header.h', subdir : 'projname') # -> include/projname/header.h install_man('foo.1') # -> share/man/man1/foo.1.gz -install_data('datafile.cat', install_dir : join_paths(get_option('datadir'), 'progname')) # -> share/progname/datafile.dat +install_data('datafile.dat', install_dir : join_paths(get_option('datadir'), 'progname')) # -> share/progname/datafile.dat ``` Sometimes you want to copy an entire subtree directly. For this use case there is the `install_subdir` command, which can be used like this. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index a3e1ef0..a557f0c 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -416,7 +416,7 @@ be passed to [shared and static libraries](#library). dynamically exported, allowing modules built using the [`shared_module`](#shared_module) function to refer to functions, variables and other symbols defined in the executable itself. Implies - the `implib` argument. Since 0.44.0 + the `implib` argument. Since 0.45.0 - `implib` when set to true, an import library is generated for the executable (the name of the import library is based on *exe_name*). Alternatively, when set to a string, that gives the base name for @@ -772,7 +772,7 @@ installed with a `.gz` suffix. ### install_subdir() ``` meson - void install_subdir(subdir_name, install_dir : ..., exclude_files : ..., exclude_directories : ...) + void install_subdir(subdir_name, install_dir : ..., exclude_files : ..., exclude_directories : ..., strip_directory : ...) ``` Installs the entire given subdirectory and its contents from the @@ -786,6 +786,46 @@ The following keyword arguments are supported: - `exclude_directories`: a list of directory names that should not be installed. Names are interpreted as paths relative to the `subdir_name` location. - `install_dir`: the location to place the installed subdirectory. +- `strip_directory`: install directory contents. `strip_directory=false` by default. + If `strip_directory=false` only last component of source path is used. + Since 0.45.0 + +For a given directory `foo`: +```text +foo/ + bar/ + file1 + file2 +``` +`install_subdir('foo', install_dir : 'share', strip_directory : false)` creates +```text +share/ + foo/ + bar/ + file1 + file2 +``` + +`install_subdir('foo', install_dir : 'share', strip_directory : true)` creates +```text +share/ + bar/ + file1 + file2 +``` + +`install_subdir('foo/bar', install_dir : 'share', strip_directory : false)` creates +```text +share/ + bar/ + file1 +``` + +`install_subdir('foo/bar', install_dir : 'share', strip_directory : true)` creates +```text +share/ + file1 +``` ### is_variable() @@ -910,7 +950,8 @@ Project supports the following keyword arguments. 'GPL3']`. Note that the text is informal and is only written to the dependency manifest. Meson does not do any license validation, you are responsible for verifying that you abide by all licensing - terms. + terms. You can access the value in your Meson build files with + `meson.project_license()`. - `meson_version` takes a string describing which Meson version the project requires. Usually something like `>0.28.0`. @@ -944,13 +985,16 @@ respectively. ### run_target ``` meson - buildtarget run_target(target_name, ...) +runtarget run_target(target_name, ...) ``` This function creates a new top-level target that runs a specified command with the specified arguments. Like all top-level targets, this integrates with the selected backend. For instance, with Ninja you can -run it as `ninja target_name`. +run it as `ninja target_name`. Note that a run target produces no +output as far as Meson is concerned. It is only meant for tasks such +as running a code formatter or flashing an external device's firmware +with a built file. The script is run from an *unspecified* directory, and Meson will set three environment variables `MESON_SOURCE_ROOT`, `MESON_BUILD_ROOT` @@ -1247,6 +1291,8 @@ the following methods. - `project_version()` returns the version string specified in `project` function call. +- `project_license()` returns the array of licenses specified in `project` function call. + - `project_name()` returns the project name specified in the `project` function call. - `version()` return a string with the version of Meson. @@ -1539,7 +1585,7 @@ The following methods are defined for all [arrays](Syntax.md#arrays): - `length()`, the size of the array You can also iterate over arrays with the [`foreach` -statement](https://github.com/mesonbuild/meson/wiki/Syntax#foreach-statements). +statement](Syntax.md#foreach-statements). ## Returned objects @@ -1636,6 +1682,8 @@ an external dependency with the following methods: dependency, error out. (*Added 0.44.0*) You can also redefine a variable by passing a list to the `define_variable` parameter that can affect the retrieved variable: `['prefix', '/'])`. + (*Added 0.45.0*) A warning is issued if the variable is not defined, + unless a `default` parameter is specified. - `get_configtool_variable(varname)` (*Added 0.44.0*) will get the command line argument from the config tool (with `--` prepended), or, diff --git a/docs/markdown/Release-notes-for-0.38.0.md b/docs/markdown/Release-notes-for-0.38.0.md index ca6602e..741c349 100644 --- a/docs/markdown/Release-notes-for-0.38.0.md +++ b/docs/markdown/Release-notes-for-0.38.0.md @@ -78,7 +78,7 @@ When using compilers that implement the [`__has_include()` preprocessor macro](h # Array indexing now supports fallback values -The second argument to the array [`.get()`](https://github.com/mesonbuild/meson/wiki/Reference-manual#array-object) function is now returned if the specified index could not be found +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 array = [10, 11, 12, 13] array.get(0) # this will return `10` diff --git a/docs/markdown/Style-guide.md b/docs/markdown/Style-guide.md new file mode 100644 index 0000000..9008592 --- /dev/null +++ b/docs/markdown/Style-guide.md @@ -0,0 +1,36 @@ +--- +short-description: Style recommendations for Meson files +... + +# Style recommendations + +This page lists some recommendations on organizing and formatting your +Meson build files. + +## Tabs or spaces? + +Always spaces. + +## Naming options + +There are two ways of naming project options. As an example for +booleans the first one is `foo` and the second one is +`enable-foo`. The former style is recommended, because in Meson +options have strong type, rather than being just strings. + +You should try to name options the same as is common in other +projects. This is especially important for yielding options, because +they require that both the parent and subproject options have the same +name. + +# Global arguments + +Prefer `add_project_arguments` to `add_global_arguments` because using +the latter prevents using the project as a subproject. + +# Cross compilation arguments + +Try to keep cross compilation arguments away from your build files as +much as possible. Keep them in the cross file instead. This adds +portability, since all changes needed to compile to a different +platform are isolated in one place. diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md index 14f01d4..0d1442e 100644 --- a/docs/markdown/Subprojects.md +++ b/docs/markdown/Subprojects.md @@ -18,7 +18,7 @@ becomes a transparent part of the project. The basic idiom goes something like this. ```meson -dep = dependency('foo', fallback : [subproject_name, variable_name] +dep = dependency('foo', fallback : [subproject_name, variable_name]) ``` As an example, suppose we have a simple project that provides a shared @@ -40,7 +40,7 @@ this. ```meson project('master', 'c') -dep = dependency('simple', fallback : ['simple', 'simple_dep'] +dep = dependency('simple', fallback : ['simple', 'simple_dep']) exe = executable('prog', 'prog.c', dependencies : dep, install : true) ``` diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index 84403f4..1005100 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -58,6 +58,12 @@ y = 3 * 4 d = 5 % 3 # Yields 2. ``` +Hexadecimal literals are supported since version 0.45.0: + +```meson +int_255 = 0xFF +``` + Strings can be converted to a number like this: ```meson diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 0503c22..1665922 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -10,8 +10,13 @@ If you have a project that uses Meson that you want to add to this list, let us - [Arduino sample project](https://github.com/jpakkane/mesonarduino) - [Budgie Desktop](https://github.com/budgie-desktop/budgie-desktop), a desktop environment built on GNOME technologies - [casync](https://github.com/systemd/casync), Content-Addressable Data Synchronization Tool + - [cinnamon-desktop](https://github.com/linuxmint/cinnamon-desktop), the cinnamon desktop library + - [dbus-broker](https://github.com/bus1/dbus-broker), Linux D-Bus Message Broker + - [Dpdk](http://dpdk.org/ml/archives/dev/2018-January/089724.html), Data plane development kit, a set of libraries and drivers for fast packet processing + - [DXVK](https://github.com/doitsujin/dxvk), a Vulkan-based Direct3D 11 implementation for Linux using Wine - [Emeus](https://github.com/ebassi/emeus), Constraint based layout manager for GTK+ - [Frida](https://www.frida.re/), a dynamic binary instrumentation toolkit + - [fwupd](https://github.com/hughsie/fwupd), a simple daemon to allow session software to update firmware - [Geary](https://wiki.gnome.org/Apps/Geary), an email application built around conversations, for the GNOME 3 desktop. - [GLib](https://git.gnome.org/browse/glib/), cross-platform C library used by GTK+ and GStreamer (not the default yet) - [Gnome Builder](https://git.gnome.org/browse/gnome-builder/), an IDE for the Gnome platform @@ -24,30 +29,45 @@ If you have a project that uses Meson that you want to add to this list, let us - [GStreamer](https://cgit.freedesktop.org/gstreamer/gstreamer/), multimedia framework (not the default yet) - [GTK+](https://git.gnome.org/browse/gtk+/), the multi-platform toolkit used by GNOME - [GtkDApp](https://gitlab.com/csoriano/GtkDApp), an application template for developing Flatpak apps with Gtk+ and D + - [Hardcode-Tray](https://github.com/bil-elmoussaoui/Hardcode-Tray), fixes hardcoded tray icons in Linux - [HexChat](https://github.com/hexchat/hexchat), a cross-platform IRC client in C - [IGT](https://cgit.freedesktop.org/xorg/app/intel-gpu-tools/), Linux kernel graphics driver test suite. + - [JsonCpp](https://github.com/open-source-parsers/jsoncpp), a C++ library for interacting with JSON - [Json-glib](https://git.gnome.org/browse/json-glib), GLib-based JSON manipulation library - [Ksh](https://github.com/att/ast), a Korn Shell - [Libdrm](https://cgit.freedesktop.org/drm/libdrm/), a library for abstracting DRM kernel interfaces - [Libepoxy](https://github.com/anholt/libepoxy/), a library for handling OpenGL function pointer management + - [libfuse](https://github.com/libfuse/libfuse), the reference implementation of the Linux FUSE (Filesystem in Userspace) interface - [Libgit2-glib](https://git.gnome.org/browse/libgit2-glib/), a GLib wrapper for libgit2 - [Libhttpseverywhere](https://github.com/grindhold/libhttpseverywhere), a library to enable httpseverywhere on any desktop app + - [Libosmscout](https://github.com/Framstag/libosmscout), a C++ library for offline map rendering, routing and location +lookup based on OpenStreetMap data + - [Libva](https://github.com/intel/libva), an implementation for the VA (VIdeo Acceleration) API - [Lightdm-Webkit2-Greeter](https://github.com/Antergos/lightdm-webkit2-greeter) - [Kiwix libraries](https://github.com/kiwix/kiwix-lib) - [Mesa](https://www.mesa3d.org/), An open source graphics driver project - - [Nautilus](https://git.gnome.org/browse/nautilus/commit/?id=ed5652c89ac0654df2e82b54b00b27d51c825465) the Gnome file manager + - [MiracleCast](https://github.com/albfan/miraclecast), connect external monitors to your system via Wifi-Display specification aka Miracast + - [Nautilus](https://git.gnome.org/browse/nautilus/commit/?id=ed5652c89ac0654df2e82b54b00b27d51c825465), the Gnome file manager + - [Nemo](https://github.com/linuxmint/nemo), the file manager for the Cinnamon desktop environment - [Orc](http://cgit.freedesktop.org/gstreamer/orc/), the Optimized Inner Loop Runtime Compiler (not the default yet) - [Outlier](https://github.com/kerolasa/outlier), a small Hello World style meson example project - [Pango](https://git.gnome.org/browse/pango/), an Internationalized text layout and rendering library (not the default yet) - [Parzip](https://github.com/jpakkane/parzip), a multithreaded reimplementation of Zip + - [PipeWire](https://pipewire.org/), a framework for video and audio for containerized applications + - [Pithos](https://github.com/pithos/pithos), a Pandora Radio client - [Pitivi](http://pitivi.org/), a nonlinear video editor - [Polari](https://git.gnome.org/browse/polari), an IRC client + - [radare2](https://github.com/radare/radare2), unix-like reverse engineering framework and commandline tools (not the default) + - [SSHFS](https://github.com/libfuse/sshfs), allows you to mount a remote filesystem using SFTP - [Sysprof](https://wiki.gnome.org/Apps/Sysprof), a profiling tool - [systemd](https://github.com/systemd/systemd), the init system + - [szl](https://github.com/dimkr/szl), a lightweight, embeddable scripting language - [Taisei Project](https://taisei-project.org/), an open-source Touhou Project clone and fangame - - [Xorg](https://cgit.freedesktop.org/xorg/xserver/) the X.org display server (not the default yet) + - [xi-gtk](https://github.com/eyelash/xi-gtk), a GTK+ front-end for the Xi editor + - [Xorg](https://cgit.freedesktop.org/xorg/xserver/), the X.org display server (not the default yet) - [Valum](https://github.com/valum-framework/valum), a micro web framework written in Vala - [Wayland and Weston](https://lists.freedesktop.org/archives/wayland-devel/2016-November/031984.html), a next generation display server (not merged yet) - - [ZStandard](https://github.com/facebook/zstd/commit/4dca56ed832c6a88108a2484a8f8ff63d8d76d91) a compression algorithm developed at Facebook (not used by default) + - [wlroots](https://github.com/swaywm/wlroots), a modular Wayland compositor library + - [ZStandard](https://github.com/facebook/zstd/commit/4dca56ed832c6a88108a2484a8f8ff63d8d76d91), a compression algorithm developed at Facebook (not used by default) Note that a more up-to-date list of GNOME projects that use Meson can be found [here](https://wiki.gnome.org/Initiatives/GnomeGoals/MesonPorting). diff --git a/docs/markdown/Using-wraptool.md b/docs/markdown/Using-wraptool.md index 08b1bfa..f6023e8 100644 --- a/docs/markdown/Using-wraptool.md +++ b/docs/markdown/Using-wraptool.md @@ -1,9 +1,9 @@ # Using wraptool -Wraptool is a helper tool that allows you to manage your source -dependencies using the WrapDB database. It gives you all things you -would expect, such as installing and updating dependencies. The wrap -tool works on all platforms, the only limitation is that the wrap +Wraptool is a subcommand of Meson that allows you to manage your +source dependencies using the WrapDB database. It gives you all things +you would expect, such as installing and updating dependencies. The +wrap tool works on all platforms, the only limitation is that the wrap definition works on your target platform. If you find some Wraps that don't work, please file bugs or, even better, patches. @@ -16,7 +16,7 @@ are commands to type. The simplest operation to do is to query the list of packages available. To list them all issue the following command: - $ wraptool list + $ meson wrap list box2d enet gtest @@ -33,13 +33,13 @@ available. To list them all issue the following command: Usually you want to search for a specific package. This can be done with the `search` command: - $ wraptool search jpeg + $ meson wrap search jpeg libjpeg To determine which versions of libjpeg are available to install, issue the `info` command: - $ wraptool info libjpeg + $ meson wrap info libjpeg Available versions of libjpeg: 9a 2 @@ -54,7 +54,7 @@ Installing dependencies is just as straightforward. First just create the `subprojects` directory at the top of your source tree and issue the install command. - $ wraptool install libjpeg + $ meson wrap install libjpeg Installed libjpeg branch 9a revision 2 Now you can issue a `subproject('libjpeg')` in your `meson.build` file @@ -62,7 +62,7 @@ to use it. To check if your projects are up to date you can issue the `status` command. - $ wraptool status + $ meson wrap status Subproject status libjpeg up to date. Branch 9a, revision 2. zlib not up to date. Have 1.2.8 2, but 1.2.8 4 is available. @@ -70,12 +70,12 @@ To check if your projects are up to date you can issue the `status` command. In this case `zlib` has a newer release available. Updating it is straightforward: - $ wraptool update zlib + $ meson wrap update zlib Updated zlib to branch 1.2.8 revision 4 Wraptool can do other things besides these. Documentation for these can be found in the command line help, which can be accessed by -`wraptool --help`. +`meson wrap --help`. ## Promoting dependencies diff --git a/docs/markdown/Videos.md b/docs/markdown/Videos.md index 5abfbe4..d9ea34d 100644 --- a/docs/markdown/Videos.md +++ b/docs/markdown/Videos.md @@ -4,34 +4,28 @@ short-description: Videos about Meson # Videos -## An overview of meson + - [The Meson Build System, 4+ years of work to become an overnight + success](https://www.youtube.com/watch?v=gHdTzdXkhRY), Linux.conf.au 2018 -(from Linux.conf.au 2015 -- Auckland, New Zealand) + - [Meson and the changing Linux build + landscape](https://media.ccc.de/v/ASG2017-111-meson_and_the_changing_linux_build_landscape), + All Systems Go 2017 -<div class="video-container"> -<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/KPi0AuVpxLI" allowfullscreen></iframe> -</div> + - [Meson, compiling the world with + Python](https://www.youtube.com/watch?v=sEO4DC8hm34), Europython + 2017 -## Talks about design, goals behind Meson's multiplatform dependency system was held + - [Builds, dependencies and deployment in a modern multiplatform + world](https://www.youtube.com/embed/CTJtKtQ8R5k), Linux.conf.au + 2016 -(From Linux.conf.au 2016 -- Geelong, Australia) + - [New world, new tools](https://www.youtube.com/embed/0-gx1qU2pPo), + Libre Application Summit 2016 -<div class="video-container"> -<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/CTJtKtQ8R5k" allowfullscreen></iframe> -</div> + - [Making build systems not + suck](https://www.youtube.com/embed/KPi0AuVpxLI), Linux.conf.au + 2015, Auckland, New Zealand -## Features and benefits of Meson's multiplatform support for building and dependencies] - -(Libre Application Summit 2016 talk _New world, new tools_ explored further) - -<div class="video-container"> -<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/0-gx1qU2pPo" allowfullscreen></iframe> -</div> - -## The first ever public presentation on Meson - -(lightning talk at FOSDEM 2014) - -<video width="854" height="480" controls> - <source src=http://mirror.onet.pl/pub/mirrors/video.fosdem.org/2014/H2215_Ferrer/Sunday/Introducing_the_Meson_build_system.webm> -</video> + - [Lightning talk at FOSDEM + 2014](http://mirror.onet.pl/pub/mirrors/video.fosdem.org/2014/H2215_Ferrer/Sunday/Introducing_the_Meson_build_system.webm), + The first ever public presentation on Meson diff --git a/docs/markdown/_Sidebar.md b/docs/markdown/_Sidebar.md index 89fc523..2637d68 100644 --- a/docs/markdown/_Sidebar.md +++ b/docs/markdown/_Sidebar.md @@ -7,7 +7,7 @@ * [Tests](Unit-tests.md) * [Syntax](Syntax.md) -### [Modules](https://github.com/mesonbuild/meson/wiki/Module-reference.md) +### [Modules](Module-reference.md) * [gnome](Gnome-module.md) * [i18n](i18n-module.md) diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md index 8fb650a..88f059b 100644 --- a/docs/markdown/i18n-module.md +++ b/docs/markdown/i18n-module.md @@ -40,7 +40,7 @@ This function also defines targets for maintainers to use: ### i18n.merge_file() This merges translations into a text file using `msgfmt`. See -[custom_target](https://github.com/mesonbuild/meson/wiki/Reference%20manual#custom_target) +[custom_target](Reference-manual.md#custom_target) for normal keywords. In addition it accepts these keywords: * `data_dirs`: (*Added 0.41.0*) list of directories for its files (See diff --git a/docs/markdown/snippets/fpga.md b/docs/markdown/snippets/fpga.md new file mode 100644 index 0000000..b5e4938 --- /dev/null +++ b/docs/markdown/snippets/fpga.md @@ -0,0 +1,12 @@ +## Experimental FPGA support + +This version adds support for generating, analysing and uploading FPGA +programs using the [IceStorm +toolchain](http://www.clifford.at/icestorm/). This support is +experimental and is currently limited to the `iCE 40` series of FPGA +chips. + +FPGA generation integrates with other parts of Meson seamlessly. As an +example, [here](https://github.com/jpakkane/lm32) is an example +project that compiles a simple firmware into Verilog and combines that +with an lm32 softcore processor. diff --git a/docs/markdown/snippets/hexnumbers.md b/docs/markdown/snippets/hexnumbers.md new file mode 100644 index 0000000..840c0cb --- /dev/null +++ b/docs/markdown/snippets/hexnumbers.md @@ -0,0 +1,5 @@ +## Hexadecimal string literals + +Hexadecimal integer literals can now be used in build and option files. + + int_255 = 0xFF diff --git a/docs/markdown/snippets/install_subdir-strip_directory.md b/docs/markdown/snippets/install_subdir-strip_directory.md new file mode 100644 index 0000000..9ddb4a4 --- /dev/null +++ b/docs/markdown/snippets/install_subdir-strip_directory.md @@ -0,0 +1,4 @@ +## install_subdir() supports strip_directory + +If strip_directory=true install_subdir() installs directory contents +instead of directory itself, stripping basename of the source directory. diff --git a/docs/markdown/snippets/intopt.md b/docs/markdown/snippets/intopt.md new file mode 100644 index 0000000..daf660b --- /dev/null +++ b/docs/markdown/snippets/intopt.md @@ -0,0 +1,6 @@ +## Integer options + +There is a new integer option type with optional minimum and maximum +values. It can be specified like this in the `meson_options.txt` file: + + option('integer_option', type : 'integer', min : 0, max : 5, value : 3) diff --git a/docs/markdown/snippets/project-license.md b/docs/markdown/snippets/project-license.md new file mode 100644 index 0000000..5da2c6a --- /dev/null +++ b/docs/markdown/snippets/project-license.md @@ -0,0 +1,4 @@ +## New method meson.project_license() + +The `meson` builtin object now has a `project_license()` method that returns a +list of all licenses for the project. diff --git a/docs/markdown/snippets/rust-cross.md b/docs/markdown/snippets/rust-cross.md new file mode 100644 index 0000000..7f18c44 --- /dev/null +++ b/docs/markdown/snippets/rust-cross.md @@ -0,0 +1,16 @@ +## Rust cross-compilation + +Cross-compilation is now supported for Rust targets. Like other +cross-compilers, the Rust binary must be specified in your cross +file. It should specify a `--target` (as installed by `rustup target`) +and a custom linker pointing to your C cross-compiler. For example: + +``` +[binaries] +c = '/usr/bin/arm-linux-gnueabihf-gcc-7' +rust = [ + 'rustc', + '--target', 'arm-unknown-linux-gnueabihf', + '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7', +] +``` diff --git a/docs/markdown/snippets/yield.md b/docs/markdown/snippets/yield.md new file mode 100644 index 0000000..3880e67 --- /dev/null +++ b/docs/markdown/snippets/yield.md @@ -0,0 +1,8 @@ +## Yielding subproject option to superproject + +Normally project options are specific to the current project. However +sometimes you want to have an option whose value is the same over all +projects. This can be achieved with the new `yield` keyword for +options. When set to `true`, getting the value of this option in +`meson.build` files gets the value from the option with the same name +in the master project (if such an option exists). diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 87a5eb5..144ca4a 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -30,6 +30,7 @@ index.md Modules.md Gnome-module.md i18n-module.md + Icestorm-module.md Pkgconfig-module.md Python-3-module.md Qt4-module.md @@ -51,6 +52,7 @@ index.md Project-templates.md Reference-manual.md Reference-tables.md + Style-guide.md FAQ.md Reproducible-builds.md howtox.md diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 292b027..62cc756 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -323,9 +323,12 @@ class Backend: if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so']: continue absdir = os.path.dirname(libpath) - rel_to_src = absdir[len(self.environment.get_source_dir()) + 1:] - assert(not os.path.isabs(rel_to_src)) - paths.append(os.path.join(self.build_to_src, rel_to_src)) + if absdir.startswith(self.environment.get_source_dir()): + rel_to_src = absdir[len(self.environment.get_source_dir()) + 1:] + assert not os.path.isabs(rel_to_src), 'rel_to_src: {} is absolute'.format(rel_to_src) + paths.append(os.path.join(self.build_to_src, rel_to_src)) + else: + paths.append(absdir) return paths def determine_rpath_dirs(self, target): @@ -407,16 +410,10 @@ class Backend: args = [] pchpath = self.get_target_private_dir(target) includeargs = compiler.get_include_args(pchpath, False) - for lang in ['c', 'cpp']: - p = target.get_pch(lang) - if not p: - continue - if compiler.can_compile(p[-1]): - header = p[0] - args += compiler.get_pch_use_args(pchpath, header) - if len(args) > 0: - args = includeargs + args - return args + p = target.get_pch(compiler.get_language()) + if p: + args += compiler.get_pch_use_args(pchpath, p[0]) + return includeargs + args @staticmethod def escape_extra_args(compiler, args): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9318926..6ab67fb 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -38,8 +38,12 @@ else: execute_wrapper = '' rmfile_prefix = 'rm -f {} &&' -def ninja_quote(text): - for char in ('$', ' ', ':'): +def ninja_quote(text, is_build_line=False): + if is_build_line: + qcs = ('$', ' ', ':') + else: + qcs = ('$', ' ') + for char in qcs: text = text.replace(char, '$' + char) if '\n' in text: errmsg = '''Ninja does not support newlines in rules. The content was: @@ -87,13 +91,13 @@ class NinjaBuildElement: def write(self, outfile): self.check_outputs() - line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]), + line = 'build %s: %s %s' % (' '.join([ninja_quote(i, True) for i in self.outfilenames]), self.rule, - ' '.join([ninja_quote(i) for i in self.infilenames])) + ' '.join([ninja_quote(i, True) for i in self.infilenames])) if len(self.deps) > 0: - line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps]) + line += ' | ' + ' '.join([ninja_quote(x, True) for x in self.deps]) if len(self.orderdeps) > 0: - line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps]) + line += ' || ' + ' '.join([ninja_quote(x, True) for x in self.orderdeps]) line += '\n' # This is the only way I could find to make this work on all # platforms including Windows command shell. Slash is a dir separator @@ -843,17 +847,15 @@ int dummy; def generate_subdir_install(self, d): for sd in self.build.get_install_subdirs(): - inst_subdir = sd.installable_subdir.rstrip('/') - idir_parts = inst_subdir.split('/') - if len(idir_parts) > 1: - subdir = os.path.join(sd.source_subdir, '/'.join(idir_parts[:-1])) - inst_dir = idir_parts[-1] - else: - subdir = sd.source_subdir - inst_dir = sd.installable_subdir - src_dir = os.path.join(self.environment.get_source_dir(), subdir) - dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir) - d.install_subdirs.append([src_dir, inst_dir, dst_dir, sd.install_mode, sd.exclude]) + src_dir = os.path.join(self.environment.get_source_dir(), + sd.source_subdir, + sd.installable_subdir).rstrip('/') + dst_dir = os.path.join(self.environment.get_prefix(), + sd.install_dir) + if not sd.strip_directory: + dst_dir = os.path.join(dst_dir, os.path.basename(src_dir)) + d.install_subdirs.append([src_dir, dst_dir, sd.install_mode, + sd.exclude]) def generate_tests(self, outfile): self.serialize_tests() @@ -1291,7 +1293,11 @@ int dummy; # installations for rpath_arg in rpath_args: args += ['-C', 'link-arg=' + rpath_arg + ':' + os.path.join(rustc.get_sysroot(), 'lib')] - element = NinjaBuildElement(self.all_outputs, target_name, 'rust_COMPILER', relsrc) + crstr = '' + if target.is_cross: + crstr = '_CROSS' + compiler_name = 'rust%s_COMPILER' % crstr + element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, relsrc) if len(orderdeps) > 0: element.add_orderdep(orderdeps) element.add_item('ARGS', args) @@ -1579,8 +1585,11 @@ int dummy; outfile.write(restat) outfile.write('\n') - def generate_rust_compile_rules(self, compiler, outfile): - rule = 'rule %s_COMPILER\n' % compiler.get_language() + def generate_rust_compile_rules(self, compiler, outfile, is_cross): + crstr = '' + if is_cross: + crstr = '_CROSS' + rule = 'rule %s%s_COMPILER\n' % (compiler.get_language(), crstr) invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()]) command = ' command = %s $ARGS $in\n' % invoc description = ' description = Compiling Rust source $in.\n' @@ -1671,8 +1680,7 @@ rule FORTRAN_DEP_HACK self.generate_vala_compile_rules(compiler, outfile) return if langname == 'rust': - if not is_cross: - self.generate_rust_compile_rules(compiler, outfile) + self.generate_rust_compile_rules(compiler, outfile, is_cross) return if langname == 'swift': if not is_cross: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 98fd764..400b9e5 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -22,7 +22,7 @@ from . import mlog from .mesonlib import File, MesonException, listify, extract_as_list from .mesonlib import typeslistify, stringlistify, classify_unity_sources from .mesonlib import get_filenames_templates_dict, substitute_values -from .mesonlib import for_windows, for_darwin, for_cygwin +from .mesonlib import for_windows, for_darwin, for_cygwin, for_android from .compilers import is_object, clike_langs, sort_clike, lang_suffixes known_basic_kwargs = {'install': True, @@ -1393,6 +1393,11 @@ class SharedLibrary(BuildTarget): else: # libfoo.dylib self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' + elif for_android(is_cross, env): + prefix = 'lib' + suffix = 'so' + # Android doesn't support shared_library versioning + self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' else: prefix = 'lib' suffix = 'so' @@ -1414,25 +1419,32 @@ class SharedLibrary(BuildTarget): def process_kwargs(self, kwargs, environment): super().process_kwargs(kwargs, environment) - # Shared library version - if 'version' in kwargs: - self.ltversion = kwargs['version'] - if not isinstance(self.ltversion, str): - raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__) - if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion): - raise InvalidArguments('Invalid Shared library version "{0}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.'.format(self.ltversion)) - # Try to extract/deduce the soversion - if 'soversion' in kwargs: - self.soversion = kwargs['soversion'] - if isinstance(self.soversion, int): - self.soversion = str(self.soversion) - if not isinstance(self.soversion, str): - raise InvalidArguments('Shared library soversion is not a string or integer.') - elif self.ltversion: - # library version is defined, get the soversion from that - # We replicate what Autotools does here and take the first - # number of the version by default. - self.soversion = self.ltversion.split('.')[0] + + if not for_android(self.is_cross, self.environment): + supports_versioning = True + else: + supports_versioning = False + + if supports_versioning: + # Shared library version + if 'version' in kwargs: + self.ltversion = kwargs['version'] + if not isinstance(self.ltversion, str): + raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__) + if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion): + raise InvalidArguments('Invalid Shared library version "{0}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.'.format(self.ltversion)) + # Try to extract/deduce the soversion + if 'soversion' in kwargs: + self.soversion = kwargs['soversion'] + if isinstance(self.soversion, int): + self.soversion = str(self.soversion) + if not isinstance(self.soversion, str): + raise InvalidArguments('Shared library soversion is not a string or integer.') + elif self.ltversion: + # library version is defined, get the soversion from that + # We replicate what Autotools does here and take the first + # number of the version by default. + self.soversion = self.ltversion.split('.')[0] # Visual Studio module-definitions file if 'vs_module_defs' in kwargs: path = kwargs['vs_module_defs'] @@ -1637,6 +1649,10 @@ class CustomTarget(Target): for i in self.outputs: if not(isinstance(i, str)): raise InvalidArguments('Output argument not a string.') + if i == '': + raise InvalidArguments('Output must not be empty.') + if i.strip() == '': + raise InvalidArguments('Output must not consist only of whitespace.') if '/' in i: raise InvalidArguments('Output must not contain a path segment.') if '@INPUT@' in i or '@INPUT0@' in i: diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index f738615..a59b7d3 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -184,7 +184,7 @@ class CCompiler(Compiler): return [] def gen_export_dynamic_link_args(self, env): - if for_windows(env.is_cross_build(), env): + if for_windows(env.is_cross_build(), env) or for_cygwin(env.is_cross_build(), env): return ['-Wl,--export-all-symbols'] elif for_darwin(env.is_cross_build(), env): return [] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index dbaf730..034fef4 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -137,8 +137,11 @@ gnulike_buildtype_linker_args = {'plain': [], msvc_buildtype_linker_args = {'plain': [], 'debug': [], 'debugoptimized': [], - 'release': [], - 'minsize': ['/INCREMENTAL:NO'], + # The otherwise implicit REF and ICF linker + # optimisations are disabled by /DEBUG. + # REF implies ICF. + 'release': ['/OPT:REF'], + 'minsize': ['/INCREMENTAL:NO', '/OPT:REF'], } java_buildtype_args = {'plain': [], diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index b93289f..d1a05ed 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -19,9 +19,11 @@ from ..mesonlib import EnvironmentException, Popen_safe from .compilers import Compiler, rust_buildtype_args class RustCompiler(Compiler): - def __init__(self, exelist, version): + def __init__(self, exelist, version, is_cross, exe_wrapper=None): self.language = 'rust' super().__init__(exelist, version) + self.is_cross = is_cross + self.exe_wrapper = exe_wrapper self.id = 'rustc' def needs_static_linker(self): @@ -41,7 +43,16 @@ class RustCompiler(Compiler): pc.wait() if pc.returncode != 0: raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string()) - if subprocess.call(output_name) != 0: + if self.is_cross: + if self.exe_wrapper is None: + # Can't check if the binaries run so we have to assume they do + return + cmdlist = self.exe_wrapper + [output_name] + else: + cmdlist = [output_name] + pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pe.wait() + if pe.returncode != 0: raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string()) def get_dependency_gen_args(self, outfile): diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 0fdac8b..f87e62c 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1,5 +1,4 @@ - -# Copyright 2012-2017 The Meson development team +# Copyright 2012-2018 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. @@ -25,12 +24,19 @@ import ast version = '0.45.0.dev1' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode'] +default_yielding = False + class UserOption: - def __init__(self, name, description, choices): + def __init__(self, name, description, choices, yielding): super().__init__() self.name = name self.choices = choices self.description = description + if yielding is None: + yielding = default_yielding + if not isinstance(yielding, bool): + raise MesonException('Value of "yielding" must be a boolean.') + self.yielding = yielding # Check that the input is a valid value and return the # "cleaned" or "native" version. For example the Boolean @@ -39,8 +45,8 @@ class UserOption: raise RuntimeError('Derived option class did not override validate_value.') class UserStringOption(UserOption): - def __init__(self, name, description, value, choices=None): - super().__init__(name, description, choices) + def __init__(self, name, description, value, choices=None, yielding=None): + super().__init__(name, description, choices, yielding) self.set_value(value) def validate(self, value): @@ -56,8 +62,8 @@ class UserStringOption(UserOption): return value class UserBooleanOption(UserOption): - def __init__(self, name, description, value): - super().__init__(name, description, [True, False]) + def __init__(self, name, description, value, yielding=None): + super().__init__(name, description, [True, False], yielding) self.set_value(value) def tobool(self, thing): @@ -79,11 +85,17 @@ class UserBooleanOption(UserOption): return self.tobool(value) class UserIntegerOption(UserOption): - def __init__(self, name, description, min_value, max_value, value): - super().__init__(name, description, [True, False]) + def __init__(self, name, description, min_value, max_value, value, yielding=None): + super().__init__(name, description, [True, False], yielding) self.min_value = min_value self.max_value = max_value self.set_value(value) + c = [] + if min_value is not None: + c.append('>=' + str(min_value)) + if max_value is not None: + c.append('<=' + str(max_value)) + self.choices = ', '.join(c) def set_value(self, newvalue): if isinstance(newvalue, str): @@ -106,8 +118,8 @@ class UserIntegerOption(UserOption): return self.toint(value) class UserComboOption(UserOption): - def __init__(self, name, description, choices, value): - super().__init__(name, description, choices) + def __init__(self, name, description, choices, value, yielding=None): + super().__init__(name, description, choices, yielding) if not isinstance(self.choices, list): raise MesonException('Combo choices must be an array.') for i in self.choices: @@ -128,7 +140,7 @@ class UserComboOption(UserOption): class UserArrayOption(UserOption): def __init__(self, name, description, value, **kwargs): - super().__init__(name, description, kwargs.get('choices', [])) + super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None)) self.set_value(value, user_input=False) def validate(self, value, user_input): diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index f89e631..66bc3b4 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -61,21 +61,14 @@ class DependencyMethods(Enum): class Dependency: - def __init__(self, type_name, kwargs): - self.name = "null" - self.version = 'none' - self.language = None # None means C-like - self.is_found = False - self.type_name = type_name - self.compile_args = [] - self.link_args = [] - self.sources = [] + @classmethod + def _process_method_kw(cls, kwargs): method = kwargs.get('method', 'auto') if method not in [e.value for e in DependencyMethods]: raise DependencyException('method {!r} is invalid'.format(method)) method = DependencyMethods(method) - # This sets per-too config methods which are deprecated to to the new + # This sets per-tool config methods which are deprecated to to the new # generic CONFIG_TOOL value. if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]: @@ -88,14 +81,27 @@ class Dependency: # Set the detection method. If the method is set to auto, use any available method. # If method is set to a specific string, allow only that detection method. if method == DependencyMethods.AUTO: - self.methods = self.get_methods() - elif method in self.get_methods(): - self.methods = [method] + methods = cls.get_methods() + elif method in cls.get_methods(): + methods = [method] else: raise DependencyException( 'Unsupported detection method: {}, allowed methods are {}'.format( method.value, - mlog.format_list([x.value for x in [DependencyMethods.AUTO] + self.get_methods()]))) + mlog.format_list([x.value for x in [DependencyMethods.AUTO] + cls.get_methods()]))) + + return methods + + def __init__(self, type_name, kwargs): + self.name = "null" + self.version = 'none' + self.language = None # None means C-like + self.is_found = False + self.type_name = type_name + self.compile_args = [] + self.link_args = [] + self.sources = [] + self.methods = self._process_method_kw(kwargs) def __repr__(self): s = '<{0} {1}: {2}>' @@ -115,7 +121,8 @@ class Dependency: As an example, gtest-all.cc when using GTest.""" return self.sources - def get_methods(self): + @staticmethod + def get_methods(): return [DependencyMethods.AUTO] def get_name(self): @@ -246,14 +253,17 @@ class ConfigToolDependency(ExternalDependency): # instantiated and returned. The reduce function (method) is also # attached, since python's pickle module won't be able to do anything # with this dynamically generated class otherwise. - def reduce(_): - return (cls.factory, - (name, environment, language, kwargs, tools, tool_name)) + def reduce(self): + return (cls._unpickle, (), self.__dict__) sub = type('{}Dependency'.format(name.capitalize()), (cls, ), {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce}) return sub(name, environment, language, kwargs) + @classmethod + def _unpickle(cls): + return cls.__new__(cls) + def find_config(self, versions=None): """Helper method that searchs for config tool binaries in PATH and returns the one that best matches the given version requirements. @@ -331,7 +341,8 @@ class ConfigToolDependency(ExternalDependency): return [] return shlex.split(out) - def get_methods(self): + @staticmethod + def get_methods(): return [DependencyMethods.AUTO, DependencyMethods.CONFIG_TOOL] def get_configtool_variable(self, variable_name): @@ -561,12 +572,16 @@ class PkgConfigDependency(ExternalDependency): if not variable: ret, out = self._call_pkgbin(['--print-variables', self.name]) if not re.search(r'^' + variable_name + r'$', out, re.MULTILINE): - mlog.warning("pkgconfig variable '%s' not defined for dependency %s." % (variable_name, self.name)) + if 'default' in kwargs: + variable = kwargs['default'] + else: + mlog.warning("pkgconfig variable '%s' not defined for dependency %s." % (variable_name, self.name)) mlog.debug('Got pkgconfig variable %s : %s' % (variable_name, variable)) return variable - def get_methods(self): + @staticmethod + def get_methods(): return [DependencyMethods.PKGCONFIG] def check_pkgconfig(self): @@ -915,7 +930,12 @@ def find_external_dependency(name, env, kwargs): if lname in packages: if lname not in _packages_accept_language and 'language' in kwargs: raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, )) - dep = packages[lname](env, kwargs) + # Create the dependency object using a factory class method, if one + # exists, otherwise it is just constructed directly. + if getattr(packages[lname], '_factory', None): + dep = packages[lname]._factory(env, kwargs) + else: + dep = packages[lname](env, kwargs) if required and not dep.found(): raise DependencyException('Dependency "%s" not found' % name) return dep diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 2ea8ed1..9e9441f 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -18,7 +18,6 @@ import glob import os import re import shlex -import shutil import sysconfig from pathlib import Path @@ -44,6 +43,10 @@ from .base import ( # - $BOOST_ROOT/boost/*.hpp # - $BOOST_ROOT/lib<arch>-<compiler>/*.lib where arch=32/64 and compiler=msvc-14.1 # +# Note that we should also try to support: +# mingw-w64 / Windows : libboost_<module>-mt.a (location = <prefix>/mingw64/lib/) +# libboost_<module>-mt.dll.a +# # Library names supported: # - libboost_<module>-<compiler>-mt-gd-x_x.lib (static) # - boost_<module>-<compiler>-mt-gd-x_x.lib|.dll (shared) @@ -63,9 +66,12 @@ from .base import ( # **On Unix**, official packaged versions of boost libraries follow the following schemes: # -# Linux / Debian: libboost_<module>.so.1.66.0 -> libboost_<module>.so -# Linux / Red Hat: libboost_<module>.so.1.66.0 -> libboost_<module>.so -# Linux / OpenSuse: libboost_<module>.so.1.66.0 -> libboost_<module>.so +# Linux / Debian: libboost_<module>.so -> libboost_<module>.so.1.66.0 +# Linux / Red Hat: libboost_<module>.so -> libboost_<module>.so.1.66.0 +# Linux / OpenSuse: libboost_<module>.so -> libboost_<module>.so.1.66.0 +# Win / Cygwin: libboost_<module>.dll.a (location = /usr/lib) +# libboost_<module>.a +# cygboost_<module>_1_64.dll (location = /usr/bin) # Mac / homebrew: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /usr/local/lib) # Mac / macports: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /opt/local/lib) # @@ -94,18 +100,13 @@ from .base import ( # 2.2. Find boost libraries with unknown suffixes using file-name globbing. # TODO: Unix: Don't assume we know where the boost dir is, rely on -Idir and -Ldir being set. -# TODO: Determine a suffix (e.g. "-mt" or "") and use it. -# TODO: Get_win_link_args( ) and get_link_args( ) -# TODO: Genericize: 'args += ['-L' + dir] => args += self.compiler.get_linker_search_args(dir) # TODO: Allow user to specify suffix in BOOST_SUFFIX, or add specific options like BOOST_DEBUG for 'd' for debug. -# TODO: fix cross: -# is_windows() -> for_windows(self.want_cross, self.env) -# is_osx() and self.want_cross -> for_darwin(self.want_cross, self.env) class BoostDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('boost', environment, 'cpp', kwargs) self.need_static_link = ['boost_exception', 'boost_test_exec_monitor'] + # FIXME: is this the right way to find the build type? self.is_debug = environment.cmd_line_options.buildtype.startswith('debug') threading = kwargs.get("threading", "multi") self.is_multithreading = threading == "multi" @@ -128,25 +129,39 @@ class BoostDependency(ExternalDependency): self.libdir = os.environ['BOOST_LIBRARYDIR'] if self.boost_root is None: - if mesonlib.is_windows(): + if mesonlib.for_windows(self.want_cross, self.env): self.boost_roots = self.detect_win_roots() else: self.boost_roots = self.detect_nix_roots() - if self.boost_root is None and not self.boost_roots: - self.log_fail() - return - if self.incdir is None: - if mesonlib.is_windows(): + if mesonlib.for_windows(self.want_cross, self.env): self.incdir = self.detect_win_incdir() else: self.incdir = self.detect_nix_incdir() - if self.incdir is None and mesonlib.is_windows(): + if self.check_invalid_modules(): self.log_fail() return + mlog.debug('Boost library root dir is', mlog.bold(self.boost_root)) + mlog.debug('Boost include directory is', mlog.bold(self.incdir)) + + # 1. check if we can find BOOST headers. + self.detect_headers_and_version() + + # 2. check if we can find BOOST libraries. + if self.is_found: + self.detect_lib_modules() + mlog.debug('Boost library directory is', mlog.bold(self.libdir)) + + # 3. Report success or failure + if self.is_found: + self.log_success() + else: + self.log_fail() + + def check_invalid_modules(self): invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS] # previous versions of meson allowed include dirs as modules @@ -162,27 +177,9 @@ class BoostDependency(ExternalDependency): if invalid_modules: mlog.log(mlog.red('ERROR:'), 'Invalid Boost modules: ' + ', '.join(invalid_modules)) - self.log_fail() - return - - mlog.debug('Boost library root dir is', mlog.bold(self.boost_root)) - mlog.debug('Boost include directory is', mlog.bold(self.incdir)) - - self.lib_modules = {} - self.detect_version() - if self.is_found: - self.detect_lib_modules() - mlog.debug('Boost library directory is', mlog.bold(self.libdir)) - for m in self.requested_modules: - if 'boost_' + m not in self.lib_modules: - mlog.debug('Requested Boost library {!r} not found'.format(m)) - self.log_fail() - self.is_found = False - return - self.log_success() + return True else: - self.log_fail() - + return False def log_fail(self): module_str = ', '.join(self.requested_modules) @@ -267,7 +264,7 @@ class BoostDependency(ExternalDependency): raise DependencyException('Boost module argument is not a string.') return candidates - def detect_version(self): + def detect_headers_and_version(self): try: version = self.compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), []) except mesonlib.EnvironmentException: @@ -281,9 +278,23 @@ class BoostDependency(ExternalDependency): self.is_found = True def detect_lib_modules(self): - if mesonlib.is_windows(): - return self.detect_lib_modules_win() - return self.detect_lib_modules_nix() + self.lib_modules = {} + + # 1. Try to find modules using compiler.find_library( ) + if self.find_libraries_with_abi_tags(self.abi_tags()): + pass + # 2. Fall back to the old method + else: + if mesonlib.for_windows(self.want_cross, self.env): + self.detect_lib_modules_win() + else: + self.detect_lib_modules_nix() + + # 3. Check if we can find the modules + for m in self.requested_modules: + if 'boost_' + m not in self.lib_modules: + mlog.debug('Requested Boost library {!r} not found'.format(m)) + self.is_found = False def modname_from_filename(self, filename): modname = os.path.basename(filename) @@ -293,20 +304,94 @@ class BoostDependency(ExternalDependency): modname = modname[3:] return modname - def detect_lib_modules_win(self): - arch = detect_cpu_family(self.env.coredata.compilers) + def compiler_tag(self): + tag = None + compiler = self.env.detect_cpp_compiler(self.want_cross) + if mesonlib.for_windows(self.want_cross, self.env): + if compiler.get_id() == 'msvc': + comp_ts_version = compiler.get_toolset_version() + compiler_ts = comp_ts_version.split('.') + # FIXME - what about other compilers? + tag = '-vc{}{}'.format(compiler_ts[0], compiler_ts[1]) + else: + tag = '' + return tag + + def threading_tag(self): + if not self.is_multithreading: + return '' + + if mesonlib.for_darwin(self.want_cross, self.env): + # - Mac: requires -mt for multithreading, so should not fall back to non-mt libraries. + return '-mt' + elif mesonlib.for_windows(self.want_cross, self.env): + # - Windows: requires -mt for multithreading, so should not fall back to non-mt libraries. + return '-mt' + else: + # - Linux: leaves off -mt but libraries are multithreading-aware. + # - Cygwin: leaves off -mt but libraries are multithreading-aware. + return '' + + def version_tag(self): + return '-' + self.version.replace('.', '_') + + def debug_tag(self): + return '-gd' if self.is_debug else '' + + def versioned_abi_tag(self): + return self.compiler_tag() + self.threading_tag() + self.debug_tag() + self.version_tag() + + # FIXME - how to handle different distributions, e.g. for Mac? Currently we handle homebrew and macports, but not fink. + def abi_tags(self): + if mesonlib.for_windows(self.want_cross, self.env): + return [self.versioned_abi_tag(), self.threading_tag()] + else: + return [self.threading_tag()] + + def sourceforge_dir(self): + if self.env.detect_cpp_compiler(self.want_cross).get_id() != 'msvc': + return None comp_ts_version = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version() - compiler_ts = comp_ts_version.split('.') - compiler = 'vc{}{}'.format(compiler_ts[0], compiler_ts[1]) + arch = detect_cpu_family(self.env.coredata.compilers) + if arch == 'x86': + return 'lib32-msvc-{}'.format(comp_ts_version) + elif arch == 'x86_64': + return 'lib64-msvc-{}'.format(comp_ts_version) + else: + # Does anyone do Boost cross-compiling to other archs on Windows? + return None + + def find_libraries_with_abi_tag(self, tag): + + # All modules should have the same tag + self.lib_modules = {} + + all_found = True + + for module in self.requested_modules: + libname = 'boost_' + module + tag + + args = self.compiler.find_library(libname, self.env, self.extra_lib_dirs()) + if args is None: + mlog.debug("Couldn\'t find library '{}' for boost module '{}' (ABI tag = '{}')".format(libname, module, tag)) + all_found = False + else: + mlog.debug('Link args for boost module "{}" are {}'.format(module, args)) + self.lib_modules['boost_' + module] = args + + return all_found + + def find_libraries_with_abi_tags(self, tags): + for tag in tags: + if self.find_libraries_with_abi_tag(tag): + return True + return False + + def detect_lib_modules_win(self): if not self.libdir: # The libdirs in the distributed binaries (from sf) - if arch == 'x86': - lib_sf = 'lib32-msvc-{}'.format(comp_ts_version) - elif arch == 'x86_64': - lib_sf = 'lib64-msvc-{}'.format(comp_ts_version) - else: - # Does anyone do Boost cross-compiling to other archs on Windows? - lib_sf = None + lib_sf = self.sourceforge_dir() + if self.boost_root: roots = [self.boost_root] else: @@ -327,12 +412,8 @@ class BoostDependency(ExternalDependency): return for name in self.need_static_link: - libname = "lib{}".format(name) + '-' + compiler - if self.is_multithreading: - libname = libname + '-mt' - if self.is_debug: - libname = libname + '-gd' - libname = libname + "-{}.lib".format(self.version.replace('.', '_')) + # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a + libname = 'lib' + name + self.versioned_abi_tag() + '.lib' if os.path.isfile(os.path.join(self.libdir, libname)): self.lib_modules[self.modname_from_filename(libname)] = [libname] else: @@ -343,44 +424,23 @@ class BoostDependency(ExternalDependency): # globber1 applies to a layout=system installation # globber2 applies to a layout=versioned installation globber1 = 'libboost_*' if self.static else 'boost_*' - globber2 = globber1 + '-' + compiler - if self.is_multithreading: - globber2 = globber2 + '-mt' - if self.is_debug: - globber2 = globber2 + '-gd' - globber2 = globber2 + '-{}'.format(self.version.replace('.', '_')) + globber2 = globber1 + self.versioned_abi_tag() + # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib')) for entry in globber2_matches: fname = os.path.basename(entry) self.lib_modules[self.modname_from_filename(fname)] = [fname] if len(globber2_matches) == 0: + # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')): if self.static: fname = os.path.basename(entry) self.lib_modules[self.modname_from_filename(fname)] = [fname] def detect_lib_modules_nix(self): - all_found = True - for module in self.requested_modules: - args = None - libname = 'boost_' + module - if self.is_multithreading and mesonlib.for_darwin(self.want_cross, self.env): - # - Linux leaves off -mt but libraries are multithreading-aware. - # - Mac requires -mt for multithreading, so should not fall back to non-mt libraries. - libname = libname + '-mt' - args = self.compiler.find_library(libname, self.env, self.extra_lib_dirs()) - if args is None: - mlog.debug('Couldn\'t find library "{}" for boost module "{}"'.format(module, libname)) - all_found = False - else: - mlog.debug('Link args for boost module "{}" are {}'.format(module, args)) - self.lib_modules['boost_' + module] = args - if all_found: - return - if self.static: libsuffix = 'a' - elif mesonlib.is_osx() and not self.want_cross: + elif mesonlib.for_darwin(self.want_cross, self.env): libsuffix = 'dylib' else: libsuffix = 'so' @@ -416,29 +476,17 @@ class BoostDependency(ExternalDependency): if modname not in self.lib_modules: self.lib_modules[modname] = [entry] - def get_win_link_args(self): - args = [] - # TODO: should this check self.libdir? - if self.libdir: - args.append('-L' + self.libdir) - for lib in self.requested_modules: - args += self.lib_modules['boost_' + lib] - return args - def extra_lib_dirs(self): - dirs = [] - if self.boost_root: - dirs = [os.path.join(self.boost_root, 'lib')] - elif self.libdir: - dirs = [self.libdir] - return dirs + if self.libdir: + return [self.libdir] + elif self.boost_root: + return [os.path.join(self.boost_root, 'lib')] + return [] def get_link_args(self): - if mesonlib.is_windows(): - return self.get_win_link_args() args = [] for dir in self.extra_lib_dirs(): - args += ['-L' + dir] + args += self.compiler.get_linker_search_args(dir) for lib in self.requested_modules: args += self.lib_modules['boost_' + lib] return args @@ -791,7 +839,8 @@ class Python3Dependency(ExternalDependency): self.version = sysconfig.get_config_var('py_version') self.is_found = True - def get_methods(self): + @staticmethod + def get_methods(): if mesonlib.is_windows(): return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG] elif mesonlib.is_osx(): @@ -809,90 +858,80 @@ class Python3Dependency(ExternalDependency): class PcapDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('pcap', environment, None, kwargs) - kwargs['required'] = False - if DependencyMethods.PKGCONFIG in self.methods: + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + if DependencyMethods.PKGCONFIG in methods: try: pcdep = PkgConfigDependency('pcap', environment, kwargs) if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return + return pcdep except Exception as e: mlog.debug('Pcap not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CONFIG_TOOL in self.methods: + if DependencyMethods.CONFIG_TOOL in methods: try: ctdep = ConfigToolDependency.factory( 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config') if ctdep.found(): - self.config = ctdep.config - self.type_name = 'config-tool' - self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - self.link_args = ctdep.get_config_value(['--libs'], 'link_args') - self.version = self.get_pcap_lib_version() - self.is_found = True - return + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') + ctdep.version = cls.get_pcap_lib_version(ctdep) + return ctdep except Exception as e: mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e)) - def get_methods(self): + return PcapDependency(environment, kwargs) + + @staticmethod + def get_methods(): if mesonlib.is_osx(): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] - def get_pcap_lib_version(self): - return self.compiler.get_return_value('pcap_lib_version', 'string', - '#include <pcap.h>', self.env, [], [self]) + @staticmethod + def get_pcap_lib_version(ctdep): + return ctdep.compiler.get_return_value('pcap_lib_version', 'string', + '#include <pcap.h>', ctdep.env, [], [ctdep]) class CupsDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('cups', environment, None, kwargs) - kwargs['required'] = False - if DependencyMethods.PKGCONFIG in self.methods: + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + if DependencyMethods.PKGCONFIG in methods: try: pcdep = PkgConfigDependency('cups', environment, kwargs) if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return + return pcdep except Exception as e: mlog.debug('cups not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CONFIG_TOOL in self.methods: + if DependencyMethods.CONFIG_TOOL in methods: try: ctdep = ConfigToolDependency.factory( 'cups', environment, None, kwargs, ['cups-config'], 'cups-config') if ctdep.found(): - self.config = ctdep.config - self.type_name = 'config-tool' - self.version = ctdep.version - self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - self.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args') - self.is_found = True - return + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args') + return ctdep except Exception as e: mlog.debug('cups not found via cups-config. Trying next, error was:', str(e)) - if DependencyMethods.EXTRAFRAMEWORK in self.methods: + if DependencyMethods.EXTRAFRAMEWORK in methods: if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency('cups', False, None, self.env, - self.language, kwargs) + fwdep = ExtraFrameworkDependency('cups', False, None, environment, + kwargs.get('language', None), kwargs) if fwdep.found(): - self.is_found = True - self.compile_args = fwdep.get_compile_args() - self.link_args = fwdep.get_link_args() - self.version = fwdep.get_version() - return - mlog.log('Dependency', mlog.bold('cups'), 'found:', mlog.red('NO')) + return fwdep + mlog.log('Dependency', mlog.bold('cups'), 'found:', mlog.red('NO')) + + return CupsDependency(environment, kwargs) - def get_methods(self): + @staticmethod + def get_methods(): if mesonlib.is_osx(): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: @@ -902,36 +941,33 @@ class CupsDependency(ExternalDependency): class LibWmfDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('libwmf', environment, None, kwargs) - if DependencyMethods.PKGCONFIG in self.methods: + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + if DependencyMethods.PKGCONFIG in methods: try: kwargs['required'] = False pcdep = PkgConfigDependency('libwmf', environment, kwargs) if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return + return pcdep except Exception as e: mlog.debug('LibWmf not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CONFIG_TOOL in self.methods: + if DependencyMethods.CONFIG_TOOL in methods: try: ctdep = ConfigToolDependency.factory( 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config') if ctdep.found(): - self.config = ctdep.config - self.type_name = 'config-too' - self.version = ctdep.version - self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - self.link_args = ctdep.get_config_value(['--libs'], 'link_args') - self.is_found = True - return + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') + return ctdep except Exception as e: mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e)) - def get_methods(self): + return LibWmfDependency(environment, kwargs) + + @staticmethod + def get_methods(): if mesonlib.is_osx(): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index c066c31..a6307c4 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -38,19 +38,6 @@ from .base import ConfigToolDependency class GLDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('gl', environment, None, kwargs) - if DependencyMethods.PKGCONFIG in self.methods: - try: - pcdep = PkgConfigDependency('gl', environment, kwargs) - if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return - except Exception: - pass if DependencyMethods.SYSTEM in self.methods: if mesonlib.is_osx(): self.is_found = True @@ -67,7 +54,19 @@ class GLDependency(ExternalDependency): self.version = '1' return - def get_methods(self): + @classmethod + def _factory(cls, environment, kwargs): + if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): + try: + pcdep = PkgConfigDependency('gl', environment, kwargs) + if pcdep.found(): + return pcdep + except Exception: + pass + return GLDependency(environment, kwargs) + + @staticmethod + def get_methods(): if mesonlib.is_osx() or mesonlib.is_windows(): return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM] else: @@ -337,7 +336,8 @@ class QtBaseDependency(ExternalDependency): else: return qvars['QT_INSTALL_BINS'] - def get_methods(self): + @staticmethod + def get_methods(): return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE] def get_exe_args(self, compiler): @@ -380,47 +380,40 @@ class Qt5Dependency(QtBaseDependency): class SDL2Dependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('sdl2', environment, None, kwargs) - kwargs['required'] = False - if DependencyMethods.PKGCONFIG in self.methods: + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + if DependencyMethods.PKGCONFIG in methods: try: pcdep = PkgConfigDependency('sdl2', environment, kwargs) if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return + return pcdep except Exception as e: mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CONFIG_TOOL in self.methods: + if DependencyMethods.CONFIG_TOOL in methods: try: ctdep = ConfigToolDependency.factory( 'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config') if ctdep.found(): - self.type_name = 'config-tool' - self.config = ctdep.config - self.version = ctdep.version - self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - self.links_args = ctdep.get_config_value(['--libs'], 'link_args') - self.is_found = True - return + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.links_args = ctdep.get_config_value(['--libs'], 'link_args') + return ctdep except Exception as e: mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e)) - if DependencyMethods.EXTRAFRAMEWORK in self.methods: + if DependencyMethods.EXTRAFRAMEWORK in methods: if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency('sdl2', False, None, self.env, - self.language, kwargs) + fwdep = ExtraFrameworkDependency('sdl2', False, None, environment, + kwargs.get('language', None), kwargs) if fwdep.found(): - self.is_found = True - self.compile_args = fwdep.get_compile_args() - self.link_args = fwdep.get_link_args() - self.version = '2' # FIXME - return + fwdep.version = '2' # FIXME + return fwdep mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO')) - def get_methods(self): + return SDL2Dependency(environment, kwargs) + + @staticmethod + def get_methods(): if mesonlib.is_osx(): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: @@ -456,20 +449,6 @@ class VulkanDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('vulkan', environment, None, kwargs) - if DependencyMethods.PKGCONFIG in self.methods: - try: - pcdep = PkgConfigDependency('vulkan', environment, kwargs) - if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.compile_args = pcdep.get_compile_args() - self.link_args = pcdep.get_link_args() - self.version = pcdep.get_version() - self.pcdep = pcdep - return - except Exception: - pass - if DependencyMethods.SYSTEM in self.methods: try: self.vulkan_sdk = os.environ['VULKAN_SDK'] @@ -526,5 +505,18 @@ class VulkanDependency(ExternalDependency): self.link_args.append(lib) return - def get_methods(self): + @classmethod + def _factory(cls, environment, kwargs): + if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): + try: + pcdep = PkgConfigDependency('vulkan', environment, kwargs) + if pcdep.found(): + return pcdep + except Exception: + pass + + return VulkanDependency(environment, kwargs) + + @staticmethod + def get_methods(): return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 4737dd3..52c670a 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -278,6 +278,7 @@ class Environment: self.default_objc = ['cc'] self.default_objcpp = ['c++'] self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort'] + self.default_rust = ['rustc'] self.default_static_linker = ['ar'] self.vs_static_linker = ['lib'] self.gcc_static_linker = ['gcc-ar'] @@ -688,16 +689,24 @@ class Environment: return ValaCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') - def detect_rust_compiler(self): - exelist = ['rustc'] - try: - p, out = Popen_safe(exelist + ['--version'])[0:2] - except OSError: - raise EnvironmentException('Could not execute Rust compiler "%s"' % ' '.join(exelist)) - version = search_version(out) - if 'rustc' in out: - return RustCompiler(exelist, version) - raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_rust_compiler(self, want_cross): + popen_exceptions = {} + compilers, ccache, is_cross, exe_wrap = self._get_compilers('rust', 'RUSTC', want_cross) + for compiler in compilers: + if isinstance(compiler, str): + compiler = [compiler] + try: + p, out = Popen_safe(compiler + ['--version'])[0:2] + except OSError as e: + popen_exceptions[compiler] = e + continue + + version = search_version(out) + + if 'rustc' in out: + return RustCompiler(compiler, version, is_cross, exe_wrap) + + self._handle_exceptions(popen_exceptions, compilers) def detect_d_compiler(self, want_cross): is_cross = False diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index edcb92c..c2c4fe3 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -507,13 +507,14 @@ class DataHolder(InterpreterObject, ObjectHolder): return self.held_object.install_dir class InstallDir(InterpreterObject): - def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude): + def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude, strip_directory): InterpreterObject.__init__(self) self.source_subdir = src_subdir self.installable_subdir = inst_subdir self.install_dir = install_dir self.install_mode = install_mode self.exclude = exclude + self.strip_directory = strip_directory class Man(InterpreterObject): @@ -682,6 +683,8 @@ class SubprojectHolder(InterpreterObject, ObjectHolder): varname = args[0] if not isinstance(varname, str): raise InterpreterException('Get_variable takes a string argument.') + if varname not in self.held_object.variables: + raise InvalidArguments('Requested variable "{0}" not found.'.format(varname)) return self.held_object.variables[varname] class CompilerHolder(InterpreterObject): @@ -1168,6 +1171,7 @@ class MesonMain(InterpreterObject): 'add_postconf_script': self.add_postconf_script_method, 'install_dependency_manifest': self.install_dependency_manifest_method, 'project_version': self.project_version_method, + 'project_license': self.project_license_method, 'version': self.version_method, 'project_name': self.project_name_method, 'get_cross_property': self.get_cross_property_method, @@ -1281,6 +1285,9 @@ class MesonMain(InterpreterObject): def project_version_method(self, args, kwargs): return self.build.dep_manifest[self.interpreter.active_projectname]['version'] + def project_license_method(self, args, kwargs): + return self.build.dep_manifest[self.interpreter.active_projectname]['license'] + def version_method(self, args, kwargs): return coredata.version @@ -1386,7 +1393,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'install_data': {'install_dir', 'install_mode', 'sources'}, 'install_headers': {'install_dir', 'subdir'}, 'install_man': {'install_dir'}, - 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode'}, + 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'}, 'jar': jar_kwargs, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, 'run_target': {'command', 'depends'}, @@ -1574,6 +1581,8 @@ class Interpreter(InterpreterBase): self.build.cross_stdlibs[l] = subproj.get_variable_method([depname], {}) except KeyError: pass + except InvalidArguments: + pass @stringArgs @noKwargs @@ -1679,7 +1688,14 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Program or command {!r} not found ' 'or not executable'.format(cmd)) cmd = prog - cmd_path = os.path.relpath(cmd.get_path(), start=srcdir) + try: + cmd_path = os.path.relpath(cmd.get_path(), start=srcdir) + except ValueError: + # On Windows a relative path can't be evaluated for + # paths on two different drives (i.e. c:\foo and f:\bar). + # The only thing left to is is to use the original absolute + # path. + cmd_path = cmd.get_path() if not cmd_path.startswith('..') and cmd_path not in self.build_def_files: self.build_def_files.append(cmd_path) expanded_args = [] @@ -1695,7 +1711,7 @@ external dependencies (including libraries) must go to "dependencies".''') for a in expanded_args: if not os.path.isabs(a): a = os.path.join(builddir if in_builddir else srcdir, self.subdir, a) - if os.path.exists(a): + if os.path.isfile(a): a = os.path.relpath(a, start=srcdir) if not a.startswith('..'): if a not in self.build_def_files: @@ -1719,8 +1735,16 @@ external dependencies (including libraries) must go to "dependencies".''') return self.do_subproject(dirname, kwargs) def do_subproject(self, dirname, kwargs): - if '/' in dirname or '\\' in dirname: - raise InterpreterException('Subproject name must not contain a path separator.') + if dirname == '': + raise InterpreterException('Subproject dir name must not be empty.') + if dirname[0] == '.': + raise InterpreterException('Subproject dir name must not start with a period.') + if '..' in dirname: + raise InterpreterException('Subproject name must not contain a ".." path segment.') + if os.path.isabs(dirname): + raise InterpreterException('Subproject name must not be an absolute path.') + if '\\' in dirname or '/' in dirname: + mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.') if dirname in self.subproject_stack: fullstack = self.subproject_stack + [dirname] incpath = ' => '.join(fullstack) @@ -1763,7 +1787,7 @@ external dependencies (including libraries) must go to "dependencies".''') def func_get_option(self, nodes, args, kwargs): if len(args) != 1: raise InterpreterException('Argument required for get_option.') - optname = args[0] + undecorated_optname = optname = args[0] if ':' in optname: raise InterpreterException('''Having a colon in option name is forbidden, projects are not allowed to directly access options of other subprojects.''') @@ -1782,7 +1806,11 @@ to directly access options of other subprojects.''') if not coredata.is_builtin_option(optname) and self.is_subproject(): optname = self.subproject + ':' + optname try: - return self.environment.coredata.user_options[optname].value + opt = self.environment.coredata.user_options[optname] + if opt.yielding and ':' in optname: + # If option not present in superproject, keep the original. + opt = self.environment.coredata.user_options.get(undecorated_optname, opt) + return opt.value except KeyError: pass if optname.endswith('_link_args'): @@ -1990,9 +2018,9 @@ to directly access options of other subprojects.''') if need_cross_compiler: cross_comp = self.environment.detect_d_compiler(True) elif lang == 'rust': - comp = self.environment.detect_rust_compiler() + comp = self.environment.detect_rust_compiler(False) if need_cross_compiler: - cross_comp = comp # FIXME, not correct. + cross_comp = self.environment.detect_rust_compiler(True) elif lang == 'fortran': comp = self.environment.detect_fortran_compiler(False) if need_cross_compiler: @@ -2182,10 +2210,10 @@ to directly access options of other subprojects.''') def get_subproject_dep(self, name, dirname, varname, required): try: dep = self.subprojects[dirname].get_variable_method([varname], {}) - except KeyError: + except InvalidArguments as e: if required: - raise DependencyException('Could not find dependency {} in subproject {}' - ''.format(varname, dirname)) + raise DependencyException('Could not find dependency {} in subproject {}; {}' + ''.format(varname, dirname, str(e))) # If the dependency is not required, don't raise an exception subproj_path = os.path.join(self.subproject_dir, dirname) mlog.log('Dependency', mlog.bold(name), 'from subproject', @@ -2670,6 +2698,12 @@ root and issuing %s. install_dir = kwargs['install_dir'] if not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') + if 'strip_directory' in kwargs: + if not isinstance(kwargs['strip_directory'], bool): + raise InterpreterException('"strip_directory" keyword must be a boolean.') + strip_directory = kwargs['strip_directory'] + else: + strip_directory = False if 'exclude_files' in kwargs: exclude = extract_as_list(kwargs, 'exclude_files') for f in exclude: @@ -2677,7 +2711,7 @@ root and issuing %s. raise InvalidArguments('Exclude argument not a string.') elif os.path.isabs(f): raise InvalidArguments('Exclude argument cannot be absolute.') - exclude_files = {os.path.join(subdir, f) for f in exclude} + exclude_files = set(exclude) else: exclude_files = set() if 'exclude_directories' in kwargs: @@ -2687,12 +2721,12 @@ root and issuing %s. raise InvalidArguments('Exclude argument not a string.') elif os.path.isabs(d): raise InvalidArguments('Exclude argument cannot be absolute.') - exclude_directories = {os.path.join(subdir, f) for f in exclude} + exclude_directories = set(exclude) else: exclude_directories = set() exclude = (exclude_files, exclude_directories) install_mode = self._get_kwarg_install_mode(kwargs) - idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude) + idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory) self.build.install_dirs.append(idir) return idir @@ -2990,6 +3024,8 @@ different subdirectory. def source_strings_to_files(self, sources): results = [] mesonlib.check_direntry_issues(sources) + if not isinstance(sources, list): + sources = [sources] for s in sources: if isinstance(s, (mesonlib.File, GeneratedListHolder, CustomTargetHolder, CustomTargetIndexHolder)): @@ -3006,6 +3042,8 @@ different subdirectory. def add_target(self, name, tobj): if name == '': raise InterpreterException('Target name must not be empty.') + if name.strip() == '': + raise InterpreterException('Target name must not consist only of whitespace.') if name.startswith('meson-'): raise InvalidArguments("Target names starting with 'meson-' are reserved " "for Meson's internal use. Please rename.") diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 35ca5c8..6618dc8 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -265,6 +265,12 @@ class InterpreterBase: if not isinstance(node.elseblock, mparser.EmptyNode): self.evaluate_codeblock(node.elseblock) + def validate_comparison_types(self, val1, val2): + if type(val1) != type(val2): + mlog.warning('''Trying to compare values of different types ({}, {}). +The result of this is undefined and will become a hard error +in a future Meson release.'''.format(type(val1).__name__, type(val2).__name__)) + def evaluate_comparison(self, node): val1 = self.evaluate_statement(node.left) if is_disabler(val1): @@ -272,15 +278,11 @@ class InterpreterBase: val2 = self.evaluate_statement(node.right) if is_disabler(val2): return val2 + self.validate_comparison_types(val1, val2) if node.ctype == '==': return val1 == val2 elif node.ctype == '!=': return val1 != val2 - elif not isinstance(val1, type(val2)): - raise InterpreterException( - 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__, - type(val2).__name__, - node.ctype)) elif not self.is_elementary_type(val1): raise InterpreterException('{} can only be compared for equality.'.format(node.left.value)) elif not self.is_elementary_type(val2): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index a90cb6e..65b689f 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -282,6 +282,9 @@ def is_osx(): def is_linux(): return platform.system().lower() == 'linux' +def is_android(): + return platform.system().lower() == 'android' + def is_haiku(): return platform.system().lower() == 'haiku' @@ -350,6 +353,18 @@ def for_darwin(is_cross, env): return env.cross_info.config['host_machine']['system'] == 'darwin' return False +def for_android(is_cross, env): + """ + Host machine is Android? + + Note: 'host' is the machine on which compiled binaries will run + """ + if not is_cross: + return is_android() + elif env.cross_info.has_host(): + return env.cross_info.config['host_machine']['system'] == 'android' + return False + def for_haiku(is_cross, env): """ Host machine is Haiku? diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 619aa39..073e505 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -147,10 +147,7 @@ class MesonApp: def generate(self): env = environment.Environment(self.source_dir, self.build_dir, self.meson_script_launcher, self.options, self.original_cmd_line_args) mlog.initialize(env.get_log_dir()) - try: - self._generate(env) - finally: - mlog.shutdown() + self._generate(env) def _generate(self, env): mlog.debug('Build started at', datetime.datetime.now().isoformat()) @@ -374,6 +371,7 @@ def run(original_args, mainfile=None): # Error message mlog.log(e) # Path to log file + mlog.shutdown() logfile = os.path.join(app.build_dir, environment.Environment.log_dir, mlog.log_fname) mlog.log("\nA full log can be found at", mlog.bold(logfile)) if os.environ.get('MESON_FORCE_BACKTRACE'): @@ -383,4 +381,7 @@ def run(original_args, mainfile=None): raise traceback.print_exc() return 1 + finally: + mlog.shutdown() + return 0 diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index aaaf8fc..8cf66af 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -139,6 +139,8 @@ def add_keys(optlist, options): elif isinstance(opt, coredata.UserComboOption): optdict['choices'] = opt.choices typestr = 'combo' + elif isinstance(opt, coredata.UserIntegerOption): + typestr = 'integer' elif isinstance(opt, coredata.UserArrayOption): typestr = 'array' else: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index db85420..218e3b3 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -362,7 +362,7 @@ class GnomeModule(ExtensionModule): ldflags.update([lib]) if isinstance(dep, PkgConfigDependency): - girdir = dep.get_pkgconfig_variable("girdir", {}) + girdir = dep.get_pkgconfig_variable("girdir", {'default': ''}) if girdir: gi_includes.update([girdir]) elif isinstance(dep, (build.StaticLibrary, build.SharedLibrary)): @@ -553,7 +553,7 @@ class GnomeModule(ExtensionModule): if subdir not in typelib_includes: typelib_includes.append(subdir) elif isinstance(dep, PkgConfigDependency): - girdir = dep.get_pkgconfig_variable("girdir", {}) + girdir = dep.get_pkgconfig_variable("girdir", {'default': ''}) if girdir and girdir not in typelib_includes: typelib_includes.append(girdir) # ldflags will be misinterpreted by gir scanner (showing diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py new file mode 100644 index 0000000..0b7b339 --- /dev/null +++ b/mesonbuild/modules/unstable_icestorm.py @@ -0,0 +1,85 @@ +# Copyright 2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# 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. + +from .. import mesonlib, compilers, mlog + +from . import ExtensionModule + +class IceStormModule(ExtensionModule): + + def __init__(self): + super().__init__() + self.snippets.add('project') + self.yosys_bin = None + + def detect_binaries(self, interpreter): + self.yosys_bin = interpreter.func_find_program(None, ['yosys'], {}) + self.arachne_bin = interpreter.func_find_program(None, ['arachne-pnr'], {}) + self.icepack_bin = interpreter.func_find_program(None, ['icepack'], {}) + self.iceprog_bin = interpreter.func_find_program(None, ['iceprog'], {}) + self.icetime_bin = interpreter.func_find_program(None, ['icetime'], {}) + + def project(self, interpreter, state, args, kwargs): + if not self.yosys_bin: + self.detect_binaries(interpreter) + result = [] + if not len(args): + raise mesonlib.MesonException('Project requires at least one argument, which is the project name.') + proj_name = args[0] + arg_sources = args[1:] + if not isinstance(proj_name, str): + raise mesonlib.MesonException('Argument must be a string.') + kwarg_sources = kwargs.get('sources', []) + if not isinstance(kwarg_sources, list): + kwarg_sources = [kwarg_sources] + all_sources = interpreter.source_strings_to_files(interpreter.flatten(arg_sources + kwarg_sources)) + if 'constraint_file' not in kwargs: + raise mesonlib.MesonException('Constraint file not specified.') + + constraint_file = interpreter.source_strings_to_files(kwargs['constraint_file']) + if len(constraint_file) != 1: + raise mesonlib.MesonException('Constraint file must contain one and only one entry.') + blif_name = proj_name + '_blif' + blif_fname = proj_name + '.blif' + asc_name = proj_name + '_asc' + asc_fname = proj_name + '.asc' + bin_name = proj_name + '_bin' + bin_fname = proj_name + '.bin' + time_name = proj_name + '-time' + upload_name = proj_name + '-upload' + + blif_target = interpreter.func_custom_target(None, [blif_name], { + 'input': all_sources, + 'output': blif_fname, + 'command': [self.yosys_bin, '-q', '-p', 'synth_ice40 -blif @OUTPUT@', '@INPUT@']}) + + asc_target = interpreter.func_custom_target(None, [asc_name], { + 'input': blif_target, + 'output': asc_fname, + 'command': [self.arachne_bin, '-q', '-d', '1k', '-p', constraint_file, '@INPUT@', '-o', '@OUTPUT@']}) + + bin_target = interpreter.func_custom_target(None, [bin_name], { + 'input': asc_target, + 'output': bin_fname, + 'command': [self.icepack_bin, '@INPUT@', '@OUTPUT@'], + 'build_by_default' : True}) + + up_target = interpreter.func_run_target(None, [upload_name], { + 'command': [self.iceprog_bin, bin_target]}) + + time_target = interpreter.func_run_target(None, [time_name], { + 'command' : [self.icetime_bin, bin_target]}) + +def initialize(): + return IceStormModule() diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 5df056f..94d56e5 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -71,6 +71,7 @@ class Lexer: # Need to be sorted longest to shortest. ('ignore', re.compile(r'[ \t]')), ('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')), + ('hexnumber', re.compile('0[xX][0-9a-fA-F]+')), ('number', re.compile(r'\d+')), ('eol_cont', re.compile(r'\\\n')), ('eol', re.compile(r'\n')), @@ -152,6 +153,9 @@ class Lexer: line_start = mo.end() - len(lines[-1]) elif tid == 'number': value = int(match_text) + elif tid == 'hexnumber': + tid = 'number' + value = int(match_text, base=16) elif tid == 'eol' or tid == 'eol_cont': lineno += 1 line_start = loc diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 2032e05..a697106 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -228,10 +228,10 @@ class TestHarness: # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, # (i.e., the test or the environment don't explicitly set it), set - # it ourselves. We do this unconditionally because it is extremely - # useful to have in tests. + # it ourselves. We do this unconditionally for regular tests + # because it is extremely useful to have. # Setting MALLOC_PERTURB_="0" will completely disable this feature. - if 'MALLOC_PERTURB_' not in child_env or not child_env['MALLOC_PERTURB_']: + if ('MALLOC_PERTURB_' not in child_env or not child_env['MALLOC_PERTURB_']) and not self.options.benchmark: child_env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) setsid = None @@ -241,8 +241,8 @@ class TestHarness: stdout = subprocess.PIPE stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT - if not is_windows(): - setsid = os.setsid + if not is_windows(): + setsid = os.setsid p = subprocess.Popen(cmd, stdout=stdout, @@ -251,6 +251,7 @@ class TestHarness: cwd=test.workdir, preexec_fn=setsid) timed_out = False + kill_test = False if test.timeout is None: timeout = None else: @@ -261,6 +262,11 @@ class TestHarness: if self.options.verbose: print("%s time out (After %d seconds)" % (test.name, timeout)) timed_out = True + except KeyboardInterrupt: + mlog.warning("CTRL-C detected while running %s" % (test.name)) + kill_test = True + + if kill_test or timed_out: # Python does not provide multiplatform support for # killing a process and all its children so we need # to roll our own. diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index df945ab..16eaf78 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -64,16 +64,21 @@ def permitted_kwargs(permitted): optname_regex = re.compile('[^a-zA-Z0-9_-]') -@permitted_kwargs({'value'}) +@permitted_kwargs({'value', 'yield'}) def StringParser(name, description, kwargs): - return coredata.UserStringOption(name, description, - kwargs.get('value', ''), kwargs.get('choices', [])) + return coredata.UserStringOption(name, + description, + kwargs.get('value', ''), + kwargs.get('choices', []), + kwargs.get('yield', coredata.default_yielding)) -@permitted_kwargs({'value'}) +@permitted_kwargs({'value', 'yield'}) def BooleanParser(name, description, kwargs): - return coredata.UserBooleanOption(name, description, kwargs.get('value', True)) + return coredata.UserBooleanOption(name, description, + kwargs.get('value', True), + kwargs.get('yield', coredata.default_yielding)) -@permitted_kwargs({'value', 'choices'}) +@permitted_kwargs({'value', 'yiel', 'choices'}) def ComboParser(name, description, kwargs): if 'choices' not in kwargs: raise OptionException('Combo option missing "choices" keyword.') @@ -83,9 +88,25 @@ def ComboParser(name, description, kwargs): for i in choices: if not isinstance(i, str): raise OptionException('Combo choice elements must be strings.') - return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0])) - -@permitted_kwargs({'value', 'choices'}) + return coredata.UserComboOption(name, + description, + choices, + kwargs.get('value', choices[0]), + kwargs.get('yield', coredata.default_yielding),) + + +@permitted_kwargs({'value', 'min', 'max', 'yield'}) +def IntegerParser(name, description, kwargs): + if 'value' not in kwargs: + raise OptionException('Integer option must contain value argument.') + return coredata.UserIntegerOption(name, + description, + kwargs.get('min', None), + kwargs.get('max', None), + kwargs['value'], + kwargs.get('yield', coredata.default_yielding)) + +@permitted_kwargs({'value', 'yield', 'choices'}) def string_array_parser(name, description, kwargs): if 'choices' in kwargs: choices = kwargs['choices'] @@ -100,11 +121,16 @@ def string_array_parser(name, description, kwargs): value = kwargs.get('value', []) if not isinstance(value, list): raise OptionException('Array choices must be passed as an array.') - return coredata.UserArrayOption(name, description, value, choices=choices) + return coredata.UserArrayOption(name, + description, + value, + choices=choices, + yielding=kwargs.get('yield', coredata.default_yielding)) option_types = {'string': StringParser, 'boolean': BooleanParser, 'combo': ComboParser, + 'integer': IntegerParser, 'array': string_array_parser, } @@ -128,6 +154,30 @@ class OptionInterpreter: continue self.cmd_line_options[key] = value + def get_bad_options(self): + subproj_len = len(self.subproject) + if subproj_len > 0: + subproj_len += 1 + retval = [] + # The options need to be sorted (e.g. here) to get consistent + # error messages (on all platforms) which is required by some test + # cases that check (also) the order of these options. + for option in sorted(self.cmd_line_options): + if option in list(self.options) + forbidden_option_names: + continue + if any(option[subproj_len:].startswith(p) for p in forbidden_prefixes): + continue + retval += [option] + return retval + + def check_for_bad_options(self): + bad = self.get_bad_options() + if bad: + sub = 'In subproject {}: '.format(self.subproject) if self.subproject else '' + mlog.warning( + '{}Unknown command line options: "{}"\n' + 'This will become a hard error in a future Meson release.'.format(sub, ', '.join(bad))) + def process(self, option_file): try: with open(option_file, 'r', encoding='utf8') as f: @@ -147,14 +197,7 @@ class OptionInterpreter: e.colno = cur.colno e.file = os.path.join('meson_options.txt') raise e - bad = [o for o in sorted(self.cmd_line_options) if not - (o in list(self.options) + forbidden_option_names or - any(o.startswith(p) for p in forbidden_prefixes))] - if bad: - sub = 'In subproject {}: '.format(self.subproject) if self.subproject else '' - mlog.warning( - '{}Unknown command line options: "{}"\n' - 'This will become a hard error in a future Meson release.'.format(sub, ', '.join(bad))) + self.check_for_bad_options() def reduce_single(self, arg): if isinstance(arg, str): diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index d1fad11..47f4cda 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -16,11 +16,6 @@ from mesonbuild import environment import sys, os, subprocess, pathlib -def remove_dir_from_trace(lcov_command, covfile, dirname): - tmpfile = covfile + '.tmp' - subprocess.check_call([lcov_command, '--remove', covfile, dirname, '-o', tmpfile]) - os.replace(tmpfile, covfile) - def coverage(source_root, build_root, log_dir): (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools() if gcovr_exe: @@ -38,6 +33,7 @@ def coverage(source_root, build_root, log_dir): covinfo = os.path.join(log_dir, 'coverage.info') initial_tracefile = covinfo + '.initial' run_tracefile = covinfo + '.run' + raw_tracefile = covinfo + '.raw' subprocess.check_call([lcov_exe, '--directory', build_root, '--capture', @@ -55,11 +51,12 @@ def coverage(source_root, build_root, log_dir): subprocess.check_call([lcov_exe, '-a', initial_tracefile, '-a', run_tracefile, - '-o', covinfo]) - remove_dir_from_trace(lcov_exe, covinfo, '/usr/include/*') - remove_dir_from_trace(lcov_exe, covinfo, '/usr/local/include/*') - remove_dir_from_trace(lcov_exe, covinfo, '/usr/src/*') - remove_dir_from_trace(lcov_exe, covinfo, '/usr/lib/llvm-*/include/*') + '-o', raw_tracefile]) + # Remove all directories outside the source_root from the covinfo + subprocess.check_call([lcov_exe, + '--extract', raw_tracefile, + os.path.join(source_root, '*'), + '--output-file', covinfo]) subprocess.check_call([genhtml_exe, '--prefix', build_root, '--output-directory', htmloutdir, diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 2f9f135..cbc782d 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -128,20 +128,42 @@ def do_copyfile(from_file, to_file): selinux_updates.append(to_file) append_to_log(to_file) -def do_copydir(data, src_prefix, src_dir, dst_dir, exclude): +def do_copydir(data, src_dir, dst_dir, exclude): ''' - Copies the directory @src_prefix (full path) into @dst_dir - - @src_dir is simply the parent directory of @src_prefix + Copies the contents of directory @src_dir into @dst_dir. + + For directory + /foo/ + bar/ + excluded + foobar + file + do_copydir(..., '/foo', '/dst/dir', {'bar/excluded'}) creates + /dst/ + dir/ + bar/ + foobar + file + + Args: + src_dir: str, absolute path to the source directory + dst_dir: str, absolute path to the destination directory + exclude: (set(str), set(str)), tuple of (exclude_files, exclude_dirs), + each element of the set is a path relative to src_dir. ''' + if not os.path.isabs(src_dir): + raise ValueError('src_dir must be absolute, got %s' % src_dir) + if not os.path.isabs(dst_dir): + raise ValueError('dst_dir must be absolute, got %s' % dst_dir) if exclude is not None: exclude_files, exclude_dirs = exclude else: exclude_files = exclude_dirs = set() - for root, dirs, files in os.walk(src_prefix): + for root, dirs, files in os.walk(src_dir): + assert os.path.isabs(root) for d in dirs[:]: - abs_src = os.path.join(src_dir, root, d) - filepart = abs_src[len(src_dir) + 1:] + abs_src = os.path.join(root, d) + filepart = os.path.relpath(abs_src, start=src_dir) abs_dst = os.path.join(dst_dir, filepart) # Remove these so they aren't visited by os.walk at all. if filepart in exclude_dirs: @@ -155,8 +177,8 @@ def do_copydir(data, src_prefix, src_dir, dst_dir, exclude): data.dirmaker.makedirs(abs_dst) shutil.copystat(abs_src, abs_dst) for f in files: - abs_src = os.path.join(src_dir, root, f) - filepart = abs_src[len(src_dir) + 1:] + abs_src = os.path.join(root, f) + filepart = os.path.relpath(abs_src, start=src_dir) if filepart in exclude_files: continue abs_dst = os.path.join(dst_dir, filepart) @@ -195,16 +217,12 @@ def do_install(datafilename): run_install_script(d) def install_subdirs(d): - for (src_dir, inst_dir, dst_dir, mode, exclude) in d.install_subdirs: - if src_dir.endswith('/') or src_dir.endswith('\\'): - src_dir = src_dir[:-1] - src_prefix = os.path.join(src_dir, inst_dir) - print('Installing subdir %s to %s' % (src_prefix, dst_dir)) - dst_dir = get_destdir_path(d, dst_dir) - d.dirmaker.makedirs(dst_dir, exist_ok=True) - do_copydir(d, src_prefix, src_dir, dst_dir, exclude) - dst_prefix = os.path.join(dst_dir, inst_dir) - set_mode(dst_prefix, mode) + for (src_dir, dst_dir, mode, exclude) in d.install_subdirs: + full_dst_dir = get_destdir_path(d, dst_dir) + print('Installing subdir %s to %s' % (src_dir, full_dst_dir)) + d.dirmaker.makedirs(full_dst_dir, exist_ok=True) + do_copydir(d, src_dir, full_dst_dir, exclude) + set_mode(full_dst_dir, mode) def install_data(d): for i in d.data: @@ -332,7 +350,7 @@ def install_targets(d): do_copyfile(pdb_filename, pdb_outname) elif os.path.isdir(fname): fname = os.path.join(d.build_dir, fname.rstrip('/')) - do_copydir(d, fname, os.path.dirname(fname), outdir, None) + do_copydir(d, fname, os.path.join(outdir, os.path.basename(fname)), None) else: raise RuntimeError('Unknown file type for {!r}'.format(fname)) printed_symlink_error = False diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 26a3489..bd440a1 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -164,17 +164,20 @@ class Resolver: if not ret: return False # Submodule has not been added, add it - if out.startswith(b'-'): + if out.startswith(b'+'): + mlog.warning('submodule {} might be out of date'.format(dirname)) + return True + elif out.startswith(b'U'): + raise RuntimeError('submodule {} has merge conflicts'.format(dirname)) + elif out.startswith(b'-'): if subprocess.call(['git', '-C', self.subdir_root, 'submodule', 'update', '--init', dirname]) != 0: return False # Submodule was added already, but it wasn't populated. Do a checkout. elif out.startswith(b' '): if subprocess.call(['git', 'checkout', '.'], cwd=dirname): return True - else: - m = 'Unknown git submodule output: {!r}' - raise AssertionError(m.format(out)) - return True + m = 'Unknown git submodule output: {!r}' + raise RuntimeError(m.format(out)) def get_git(self, p): checkoutdir = os.path.join(self.subdir_root, p.get('directory')) diff --git a/test cases/common/140 get define/meson.build b/test cases/common/140 get define/meson.build index fd87177..9f5539b 100644 --- a/test cases/common/140 get define/meson.build +++ b/test cases/common/140 get define/meson.build @@ -66,8 +66,8 @@ foreach lang : ['c', 'cpp'] if meson.is_cross_build() # Can't use an empty array as a fallback here because of # https://github.com/mesonbuild/meson/issues/1481 - lang_args = meson.get_cross_property(lang + '_args', false) - if lang_args != false + lang_args = meson.get_cross_property(lang + '_args', []) + if lang_args.length() != 0 foreach lang_arg : lang_args if lang_arg.contains('MESON_TEST_ISSUE_1665') run_1665_test = true diff --git a/test cases/common/15 mixed pch/meson.build b/test cases/common/15 mixed pch/meson.build index 19129d8..8e9da93 100644 --- a/test cases/common/15 mixed pch/meson.build +++ b/test cases/common/15 mixed pch/meson.build @@ -1,6 +1,19 @@ project('mixed C and C++ pch test', 'cpp', 'c') -exe = executable('prog', 'main.cc', 'func.c', -c_pch : ['pch/func.h', 'pch/func_pch.c'], -cpp_pch : ['pch/main_pch.cc', 'pch/main.h']) +exe = executable( + 'prog', + files('main.cc', 'func.c'), + c_pch : ['pch/func.h', 'pch/func_pch.c'], + cpp_pch : ['pch/main_pch.cc', 'pch/main.h'], +) + +cc = meson.get_compiler('c') +if cc.get_id() != 'msvc' + exe2 = executable( + 'prog2', + files('main.cc', 'func.c'), + c_pch : 'pch/func.h', + cpp_pch : 'pch/main.h', + ) +endif diff --git a/test cases/common/174 dependency factory/meson.build b/test cases/common/174 dependency factory/meson.build new file mode 100644 index 0000000..54f7d26 --- /dev/null +++ b/test cases/common/174 dependency factory/meson.build @@ -0,0 +1,51 @@ +project('dependency factory') + +dep = dependency('gl', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('SDL2', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('SDL2', method: 'config-tool', required: false) +if dep.found() and dep.type_name() == 'configtool' + dep.get_configtool_variable('prefix') +endif + +dep = dependency('Vulkan', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('pcap', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('pcap', method: 'config-tool', required: false) +if dep.found() and dep.type_name() == 'configtool' + dep.get_configtool_variable('prefix') +endif + +dep = dependency('cups', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('cups', method: 'config-tool', required: false) +if dep.found() and dep.type_name() == 'configtool' + dep.get_configtool_variable('prefix') +endif + +dep = dependency('libwmf', method: 'pkg-config', required: false) +if dep.found() and dep.type_name() == 'pkgconfig' + dep.get_pkgconfig_variable('prefix') +endif + +dep = dependency('libwmf', method: 'config-tool', required: false) +if dep.found() and dep.type_name() == 'configtool' + dep.get_configtool_variable('prefix') +endif diff --git a/test cases/common/175 get project license/bar.c b/test cases/common/175 get project license/bar.c new file mode 100644 index 0000000..864869b --- /dev/null +++ b/test cases/common/175 get project license/bar.c @@ -0,0 +1,6 @@ +#include<stdio.h> + +int main(int argc, char **argv) { + printf("I'm a main project bar.\n"); + return 0; +} diff --git a/test cases/common/175 get project license/meson.build b/test cases/common/175 get project license/meson.build new file mode 100644 index 0000000..37303e3 --- /dev/null +++ b/test cases/common/175 get project license/meson.build @@ -0,0 +1,8 @@ +project('bar', 'c', license: 'Apache') + +executable('bar', 'bar.c') + +license = meson.project_license()[0] +if license != 'Apache' + error('The license should be Apache, but it is: ' + license) +endif diff --git a/test cases/common/176 yield/meson.build b/test cases/common/176 yield/meson.build new file mode 100644 index 0000000..ba3e426 --- /dev/null +++ b/test cases/common/176 yield/meson.build @@ -0,0 +1,6 @@ +project('yield_options', 'c') + +subproject('sub') + +assert(get_option('unshared_option') == 'one', 'Unshared option has wrong value in superproject.') +assert(get_option('shared_option') == 'two', 'Unshared option has wrong value in superproject..') diff --git a/test cases/common/176 yield/meson_options.txt b/test cases/common/176 yield/meson_options.txt new file mode 100644 index 0000000..36bad4b --- /dev/null +++ b/test cases/common/176 yield/meson_options.txt @@ -0,0 +1,2 @@ +option('unshared_option', type : 'string', value : 'one') +option('shared_option', type : 'string', value : 'two') diff --git a/test cases/common/176 yield/subprojects/sub/meson.build b/test cases/common/176 yield/subprojects/sub/meson.build new file mode 100644 index 0000000..3a506e0 --- /dev/null +++ b/test cases/common/176 yield/subprojects/sub/meson.build @@ -0,0 +1,4 @@ +project('subbie', 'c') + +assert(get_option('unshared_option') == 'three', 'Unshared option has wrong value in subproject.') +assert(get_option('shared_option') == 'two', 'Shared option has wrong value in subproject.') diff --git a/test cases/common/176 yield/subprojects/sub/meson_options.txt b/test cases/common/176 yield/subprojects/sub/meson_options.txt new file mode 100644 index 0000000..a96c307 --- /dev/null +++ b/test cases/common/176 yield/subprojects/sub/meson_options.txt @@ -0,0 +1,2 @@ +option('unshared_option', type : 'string', value : 'three', yield : false) +option('shared_option', type : 'string', value : 'four', yield : true) diff --git a/test cases/common/47 options/meson.build b/test cases/common/47 options/meson.build index 863703c..f177aa4 100644 --- a/test cases/common/47 options/meson.build +++ b/test cases/common/47 options/meson.build @@ -25,3 +25,7 @@ endif if get_option('includedir') != 'include' error('Incorrect value in builtin option.') endif + +if get_option('integer_opt') != 3 + error('Incorrect value in integer option.') +endif diff --git a/test cases/common/47 options/meson_options.txt b/test cases/common/47 options/meson_options.txt index 6bd0346..4e1c8d8 100644 --- a/test cases/common/47 options/meson_options.txt +++ b/test cases/common/47 options/meson_options.txt @@ -3,3 +3,4 @@ option('other_one', type : 'boolean', value : false) option('combo_opt', type : 'combo', choices : ['one', 'two', 'combo'], value : 'combo') option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two']) option('free_array_opt', type : 'array') +option('integer_opt', type : 'integer', min : 0, max : 5, value : 3) diff --git a/test cases/common/66 install subdir/installed_files.txt b/test cases/common/66 install subdir/installed_files.txt index e5d2307..3f561d5 100644 --- a/test cases/common/66 install subdir/installed_files.txt +++ b/test cases/common/66 install subdir/installed_files.txt @@ -1,3 +1,9 @@ +usr/share/dircheck/fifth.dat +usr/share/dircheck/seventh.dat +usr/share/dircheck/nineth.dat +usr/share/eighth.dat +usr/share/fourth.dat +usr/share/sixth.dat usr/share/sub1/data1.dat usr/share/sub1/second.dat usr/share/sub1/third.dat diff --git a/test cases/common/66 install subdir/meson.build b/test cases/common/66 install subdir/meson.build index 18e0eaf..403b6f0 100644 --- a/test cases/common/66 install subdir/meson.build +++ b/test cases/common/66 install subdir/meson.build @@ -11,3 +11,7 @@ subdir('subdir') # and read-list perms for owner and group install_subdir('sub1', install_dir : 'share', install_mode : ['rwxr-x--t', 'root']) install_subdir('sub/sub1', install_dir : 'share') + +# strip_directory +install_subdir('sub_elided', install_dir : 'share', strip_directory : true) +install_subdir('nested_elided/sub', install_dir : 'share', strip_directory : true) diff --git a/test cases/common/66 install subdir/nested_elided/sub/dircheck/nineth.dat b/test cases/common/66 install subdir/nested_elided/sub/dircheck/nineth.dat new file mode 100644 index 0000000..c4eaca7 --- /dev/null +++ b/test cases/common/66 install subdir/nested_elided/sub/dircheck/nineth.dat @@ -0,0 +1 @@ +Nested file under nested elided directory. diff --git a/test cases/common/66 install subdir/nested_elided/sub/eighth.dat b/test cases/common/66 install subdir/nested_elided/sub/eighth.dat new file mode 100644 index 0000000..fa9b7b7 --- /dev/null +++ b/test cases/common/66 install subdir/nested_elided/sub/eighth.dat @@ -0,0 +1 @@ +File in nested elided directory. diff --git a/test cases/common/66 install subdir/sub_elided/dircheck/fifth.dat b/test cases/common/66 install subdir/sub_elided/dircheck/fifth.dat new file mode 100644 index 0000000..b6ca009 --- /dev/null +++ b/test cases/common/66 install subdir/sub_elided/dircheck/fifth.dat @@ -0,0 +1 @@ +Data file in a subdir of elided directory. diff --git a/test cases/common/66 install subdir/sub_elided/fourth.dat b/test cases/common/66 install subdir/sub_elided/fourth.dat new file mode 100644 index 0000000..ca5f26a --- /dev/null +++ b/test cases/common/66 install subdir/sub_elided/fourth.dat @@ -0,0 +1 @@ +Test that this file is installed directly into install_dir. diff --git a/test cases/common/66 install subdir/subdir/meson.build b/test cases/common/66 install subdir/subdir/meson.build index a1dadd4..0f81cdb 100644 --- a/test cases/common/66 install subdir/subdir/meson.build +++ b/test cases/common/66 install subdir/subdir/meson.build @@ -1,3 +1,5 @@ install_subdir('sub1', install_dir : 'share', # This mode will be overridden by the mode set in the outer install_subdir install_mode : 'rwxr-x---') + +install_subdir('sub_elided', install_dir : 'share', strip_directory : true) diff --git a/test cases/common/66 install subdir/subdir/sub_elided/dircheck/seventh.dat b/test cases/common/66 install subdir/subdir/sub_elided/dircheck/seventh.dat new file mode 100644 index 0000000..ea0b8dc --- /dev/null +++ b/test cases/common/66 install subdir/subdir/sub_elided/dircheck/seventh.dat @@ -0,0 +1 @@ +Nested file in a subdir. diff --git a/test cases/common/66 install subdir/subdir/sub_elided/sixth.dat b/test cases/common/66 install subdir/subdir/sub_elided/sixth.dat new file mode 100644 index 0000000..140f075 --- /dev/null +++ b/test cases/common/66 install subdir/subdir/sub_elided/sixth.dat @@ -0,0 +1 @@ +Elide test file in a subdir. diff --git a/test cases/common/68 number arithmetic/meson.build b/test cases/common/68 number arithmetic/meson.build index 495a83e..f2e84a8 100644 --- a/test cases/common/68 number arithmetic/meson.build +++ b/test cases/common/68 number arithmetic/meson.build @@ -56,3 +56,9 @@ assert(3 >= 3, 'Gte broken') assert(true.to_int() == 1,'bool to_int() broken') assert(false.to_int() == 0,'bool to_int() broken') + +hex_255 = 0xff +hex2_255 = 0XFF + +assert(hex_255 == 255, 'Hex parsing is broken.') +assert(hex2_255 == 255, 'Uppercase hex parsing is broken.') diff --git a/test cases/fpga/1 simple/meson.build b/test cases/fpga/1 simple/meson.build new file mode 100644 index 0000000..eff8088 --- /dev/null +++ b/test cases/fpga/1 simple/meson.build @@ -0,0 +1,9 @@ +project('lattice', 'c') + +is = import('unstable_icestorm') + +is.project('spin', + 'spin.v', + constraint_file : 'spin.pcf', +) + diff --git a/test cases/fpga/1 simple/spin.pcf b/test cases/fpga/1 simple/spin.pcf new file mode 100644 index 0000000..de06f5d --- /dev/null +++ b/test cases/fpga/1 simple/spin.pcf @@ -0,0 +1,6 @@ +set_io LED1 99 +set_io LED2 98 +set_io LED3 97 +set_io LED4 96 +set_io LED5 95 +set_io clk 21 diff --git a/test cases/fpga/1 simple/spin.v b/test cases/fpga/1 simple/spin.v new file mode 100644 index 0000000..edc40c4 --- /dev/null +++ b/test cases/fpga/1 simple/spin.v @@ -0,0 +1,32 @@ + +module top(input clk, output LED1, output LED2, output LED3, output LED4, output LED5); + + reg ready = 0; + reg [23:0] divider; + reg [3:0] spin; + + always @(posedge clk) begin + if (ready) + begin + if (divider == 6000000) + begin + divider <= 0; + spin <= {spin[2], spin[3], spin[0], spin[1]}; + end + else + divider <= divider + 1; + end + else + begin + ready <= 1; + spin <= 4'b1010; + divider <= 0; + end + end + + assign LED1 = spin[0]; + assign LED2 = spin[1]; + assign LED3 = spin[2]; + assign LED4 = spin[3]; + assign LED5 = 1; +endmodule diff --git a/test cases/frameworks/1 boost/linkexe.cc b/test cases/frameworks/1 boost/linkexe.cc index 240c7c1..e00edee 100644 --- a/test cases/frameworks/1 boost/linkexe.cc +++ b/test cases/frameworks/1 boost/linkexe.cc @@ -1,3 +1,5 @@ +#define _XOPEN_SOURCE 500 + #include<boost/thread.hpp> boost::recursive_mutex m; diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build index 30e5d25..17ee192 100644 --- a/test cases/linuxlike/1 pkg-config/meson.build +++ b/test cases/linuxlike/1 pkg-config/meson.build @@ -17,6 +17,8 @@ test('zlibtest', exe) zprefix = dep.get_pkgconfig_variable('prefix') # Always set but we can't be sure what the value is. # pkg-config returns empty string for not defined variables assert(dep.get_pkgconfig_variable('nonexisting') == '', 'Value of unknown variable is not empty.') +# ... unless default: is used +assert(dep.get_pkgconfig_variable('nonexisting', default: 'foo') == 'foo', 'Value of unknown variable is not defaulted.') # pkg-config is able to replace variables assert(dep.get_pkgconfig_variable('prefix', define_variable: ['prefix', '/tmp']) == '/tmp', 'prefix variable has not been replaced.') diff --git a/test cases/rust/4 polyglot/meson.build b/test cases/rust/4 polyglot/meson.build index a20d766..b7eef57 100644 --- a/test cases/rust/4 polyglot/meson.build +++ b/test cases/rust/4 polyglot/meson.build @@ -1,5 +1,5 @@ project('rust and c polyglot executable', 'c', 'rust') -l = library('stuff', 'stuff.rs', install : true) +l = library('stuff', 'stuff.rs', rust_crate_type: 'cdylib', install : true) e = executable('prog', 'prog.c', link_with : l, install : true) test('polyglottest', e) diff --git a/test cases/unit/19 bad command line options/subprojects/one/meson.build b/test cases/unit/19 bad command line options/subprojects/one/meson.build index 39ae07e..85ef742 100644 --- a/test cases/unit/19 bad command line options/subprojects/one/meson.build +++ b/test cases/unit/19 bad command line options/subprojects/one/meson.build @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -project('one subproject') +project('one subproject', default_options : [ 'b_colorout=never' ]) |