diff options
74 files changed, 954 insertions, 519 deletions
diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 3db6cc0..1bf333f 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -227,9 +227,11 @@ useful when running the application locally for example during tests. ### gnome.gdbus_codegen() Compiles the given XML schema into gdbus source code. Takes two -positional arguments, the first one specifies the name of the source -files and the second specifies the XML file name. +positional arguments, the first one specifies the base name to use +while creating the output source and header and the second specifies +one XML file. +* `sources`: list of XML files * `interface_prefix`: prefix for the interface * `namespace`: namespace of the interface * `object_manager`: *(Added 0.40.0)* if true generates object manager code @@ -257,7 +259,8 @@ Example: gnome = import('gnome') # The returned source would be passed to another target -gdbus_src = gnome.gdbus_codegen('example-interface', 'com.example.Sample.xml', +gdbus_src = gnome.gdbus_codegen('example-interface', + sources: 'com.example.Sample.xml', interface_prefix : 'com.example.', namespace : 'Sample', annotations : [ diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md index cad74c9..51721f0 100644 --- a/docs/markdown/Python-module.md +++ b/docs/markdown/Python-module.md @@ -142,7 +142,7 @@ This function expects no arguments or keyword arguments. #### `get_path()` ``` meson -string py_installation.get_path(path_name) +string py_installation.get_path(path_name, fallback) ``` Get a path as defined by the `sysconfig` module. @@ -153,15 +153,28 @@ For example: purelib = py_installation.get_path('purelib') ``` -This function accepts a single argument, `path_name`, which is expected to -be a non-empty string. +This function requires at least one argument, `path_name`, +which is expected to be a non-empty string. + +If `fallback` is specified, it will be returned if no path +with the given name exists. Otherwise, attempting to read +a non-existing path will cause a fatal error. **Returns**: A string +#### `has_path()` + +``` meson + bool py_installation.has_path(path_name) +``` + +**Returns**: true if a path named `path_name` can be retrieved with +[][`get_path()`], false otherwise. + #### `get_variable()` ``` meson -string py_installation.get_variable(variable_name) +string py_installation.get_variable(variable_name, fallback) ``` Get a variable as defined by the `sysconfig` module. @@ -169,14 +182,27 @@ Get a variable as defined by the `sysconfig` module. For example: ``` meson -py_bindir = py_installation.get_variable('BINDIR') +py_bindir = py_installation.get_variable('BINDIR', '') ``` -This function accepts a single argument, `variable_name`, which is expected to -be a non-empty string. +This function requires at least one argument, `variable_name`, +which is expected to be a non-empty string. + +If `fallback` is specified, it will be returned if no variable +with the given name exists. Otherwise, attempting to read +a non-existing variable will cause a fatal error. **Returns**: A string +#### `has_variable()` + +``` meson + bool py_installation.has_variable(variable_name) +``` + +**Returns**: true if a variable named `variable_name` can be retrieved with +[][`get_variable()`], false otherwise. + ## `python_dependency` object This [dependency object] subclass will try various methods to obtain the diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 74e7ff2..776703c 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1256,6 +1256,13 @@ Keyword arguments are the following: - `workdir` absolute path that will be used as the working directory for the test +- `depends` specifies that this test depends on the specified + target(s), even though it does not take any of them as a command + line argument. This is meant for cases where test finds those + targets internally, e.g. plugins or globbing. Those targets are built + before test is executed even if they have `build_by_default : false`. + Since 0.46.0 + Defined tests can be run in a backend-agnostic way by calling `meson test` inside the build dir, or by using backend-specific commands, such as `ninja test` or `msbuild RUN_TESTS.vcxproj`. @@ -1729,7 +1736,11 @@ A build target is either an [executable](#executable), [shared module](#shared_module). - `extract_all_objects()` is same as `extract_objects` but returns all - object files generated by this target + object files generated by this target. Since 0.46.0 keyword argument + `recursive` must be set to `true` to also return objects passed to the + `object` argument of this target. By default only objects built for this + target are returned to maintain backward compatibility with previous versions. + The default will eventually be changed to `true` in a future version. - `extract_objects()` returns an opaque value representing the generated object files of arguments, usually used to take single diff --git a/docs/markdown/Release-notes-for-0.46.0.md b/docs/markdown/Release-notes-for-0.46.0.md index e062459..c9f9bbd 100644 --- a/docs/markdown/Release-notes-for-0.46.0.md +++ b/docs/markdown/Release-notes-for-0.46.0.md @@ -1,23 +1,316 @@ --- title: Release 0.46 -short-description: Release notes for 0.46 (preliminary) +short-description: Release notes for 0.46 ... # New features -This page is a placeholder for the eventual release notes. - -Notable new features should come with release note updates. This is -done by creating a file snippet called `snippets/featurename.md` and -whose contents should look like this: - - ## Feature name - - A short description explaining the new feature and how it should be used. - ## Allow early return from a script Added the function `subdir_done()`. Its invocation exits the current script at the point of invocation. All previously invoked build targets and commands are build/executed. All following ones are ignored. If the current script was invoked via `subdir()` the parent script continues normally. + +## Log output slightly changed + +The format of some human-readable diagnostic messages has changed in +minor ways. In case you are parsing these messages, you may need to +adjust your code. + +## ARM compiler for C and CPP + +Cross-compilation is now supported for ARM targets using ARM compiler - ARMCC. +The current implementation does not support shareable libraries. +The default extension of the output is .axf. +The environment path should be set properly for the ARM compiler executables. +The '--cpu' option with the appropriate target type should be mentioned +in the cross file as shown in the snippet below. + +```ini +[properties] +c_args = ['--cpu=Cortex-M0plus'] +cpp_args = ['--cpu=Cortex-M0plus'] + +``` + +## Building both shared and static libraries + +A new function `both_libraries()` has been added to build both shared and static +libraries at the same time. Source files will be compiled only once and object +files will be reused to build both shared and static libraries, unless +`b_staticpic` user option or `pic:` keyword argument are set to false in which +case sources will be compiled twice. + +The returned `buildtarget` object always represents the shared library. + +## Compiler object can now be passed to run_command() + +This can be used to run the current compiler with the specified arguments +to obtain additional information from it. +One of the use cases is to get the location of development files for the +GCC plugins: + +```meson +cc = meson.get_compiler('c') +result = run_command(cc, '-print-file-name=plugin') +plugin_dev_path = result.stdout().strip() +``` + +## declare_dependency() now supports `link_whole:` + +`declare_dependency()` now supports the `link_whole:` keyword argument which +transparently works for build targets which use that dependency. + +## Old command names are now errors + +The old executable names `mesonintrospect`, `mesonconf`, `mesonrewriter` +and `mesontest` have been deprecated for a long time. Starting from +this version they no longer do anything but instead always error +out. All functionality is available as subcommands in the main `meson` +binary. + +## Meson and meson configure now accept the same arguments + +Previously meson required that builtin arguments (like prefix) be passed as +`--prefix` to `meson` and `-Dprefix` to `meson configure`. `meson` now accepts -D +form like `meson configure` has. `meson configure` also accepts the `--prefix` +form, like `meson` has. + +## Recursively extract objects + +The `recursive:` keyword argument has been added to `extract_all_objects()`. When set +to `true` it will also return objects passed to the `objects:` argument of this +target. By default only objects built for this target are returned to maintain +backward compatibility with previous versions. The default will eventually be +changed to `true` in a future version. + +```meson +lib1 = static_library('a', 'source.c', objects : 'prebuilt.o') +lib2 = static_library('b', objects : lib1.extract_all_objects(recursive : true)) +``` + +## Can override find_program() + +It is now possible to override the result of `find_program` to point +to a custom program you want. The overriding is global and applies to +every subproject from there on. Here is how you would use it. + +In master project + +```meson +subproject('mydep') +``` + +In the called subproject: + +```meson +prog = find_program('my_custom_script') +meson.override_find_program('mycodegen', prog) +``` + +In master project (or, in fact, any subproject): + +```meson +genprog = find_program('mycodegen') +``` + +Now `genprog` points to the custom script. If the dependency had come +from the system, then it would point to the system version. + +You can also use the return value of `configure_file()` to override +a program in the same way as above: + +```meson +prog_script = configure_file(input : 'script.sh.in', + output : 'script.sh', + configuration : cdata) +meson.override_find_program('mycodegen', prog_script) +``` + +## New functions: has_link_argument() and friends + +A new set of methods has been added to [compiler objects](Reference-manual.md#compiler-object) +to test if the linker supports given arguments. + +- `has_link_argument()` +- `has_multi_link_arguments()` +- `get_supported_link_arguments()` +- `first_supported_link_argument()` + +## "meson help" now shows command line help + +Command line parsing is now less surprising. "meson help" is now +equivalent to "meson --help" and "meson help <subcommand>" is +equivalent to "meson <subcommand> --help", instead of creating a build +directory called "help" in these cases. + +## Autogeneration of simple meson.build files + +A feature to generate a meson.build file compiling given C/C++ source +files into a single executable has been added to "meson init". By +default, it will take all recognizable source files in the current +directory. You can also specify a list of dependencies with the -d +flag and automatically invoke a build with the -b flag to check if the +code builds with those dependencies. + +For example, + +```meson +meson init -fbd sdl2,gl +``` + +will look for C or C++ files in the current directory, generate a +meson.build for them with the dependencies of sdl2 and gl and +immediately try to build it, overwriting any previous meson.build and +build directory. + +## install_data() supports `rename:` + +The `rename:` keyword argument is used to change names of the installed +files. Here's how you install and rename the following files: + +- `file1.txt` into `share/myapp/dir1/data.txt` +- `file2.txt` into `share/myapp/dir2/data.txt` + +```meson +install_data(['file1.txt', 'file2.txt'], + rename : ['dir1/data.txt', 'dir2/data.txt'], + install_dir : 'share/myapp') +``` + +## Support for lcc compiler for e2k (Elbrus) architecture + +In this version, a support for lcc compiler for Elbrus processors +based on [e2k microarchitecture](https://en.wikipedia.org/wiki/Elbrus_2000) +has been added. + +Examples of such CPUs: +* [Elbrus-8S](https://en.wikipedia.org/wiki/Elbrus-8S); +* Elbrus-4S; +* [Elbrus-2S+](https://en.wikipedia.org/wiki/Elbrus-2S%2B). + +Such compiler have a similar behavior as gcc (basic option compatibility), +but, in is not strictly compatible with gcc as of current version. + +Major differences as of version 1.21.22: +* it does not support LTO and PCH; +* it suffers from the same dependency file creation error as icc; +* it has minor differences in output, especially version output; +* it differently reacts to lchmod() detection; +* some backend messages are produced in ru_RU.KOI8-R even if LANG=C; +* its preprocessor treats some characters differently. + +So every noted difference is properly handled now in meson. + +## String escape character sequence update + +Single-quoted strings in meson have been taught the same set of escape +sequences as in Python. It is therefore now possible to use arbitrary bytes in +strings, like for example `NUL` (`\0`) and other ASCII control characters. See +the chapter about [*Strings* in *Syntax*](Syntax.md#strings) for more +details. + +Potential backwards compatibility issue: Any valid escape sequence according to +the new rules will be interpreted as an escape sequence instead of the literal +characters. Previously only the following escape sequences were supported in +single-quote strings: `\'`, `\\` and `\n`. + +Note that the behaviour of triple-quoted (multiline) strings has not changed. +They behave like raw strings and do not support any escape sequences. + +## New `forcefallback` wrap mode + +A new wrap mode was added, `--wrap-mode=forcefallback`. When this is set, +dependencies for which a fallback was provided will always use it, even +if an external dependency exists and satisfies the version requirements. + +## Relaxing of target name requirements + +In earlier versions of Meson you could only have one target of a given name for each type. +For example you could not have two executables named `foo`. This requirement is now +relaxed so that you can have multiple targets with the same name, as long as they are in +different subdirectories. + +Note that projects that have multiple targets with the same name can not be built with +the `flat` layout or any backend that writes outputs in the same directory. + +## Addition of OpenMP dependency + +An OpenMP dependency (`openmp`) has been added that encapsulates the various +flags used by compilers to enable OpenMP and checks for the existence of the +`omp.h` header. The `language` keyword may be passed to force the use of a +specific compiler for the checks. + +## Added new partial_dependency method to dependencies and libraries + +It is now possible to use only part of a dependency in a target. This allows, +for example, to only use headers with convenience libraries to avoid linking +to the same library multiple times. + +```meson +dep = dependency('xcb') + +helper = static_library( + 'helper', + ['helper1.c', 'helper2.c'], + dependencies : dep.partial_dependency(includes : true), +] + +final = shared_library( + 'final', + ['final.c'], + dependencyes : dep, +) +``` + +A partial dependency will have the same name version as the full dependency it +is derived from, as well as any values requested. + +## Improved generation of pkg-config files for static only libraries. + +Previously pkg-config files generated by the pkgconfig modules for static libraries +with dependencies could only be used in a dependencies with `static: true`. + +Now the generated file contains the needed dependencies libraries directly within +`Requires` and `Libs` for build static libraries passed via the `libraries` keyword +argument. + +Projects that install both a static and a shared version of a library should use +the result of [`both_libraries()`](Reference-manual.md#both_libraries) to the +pkg-config file generator or use [`configure_file()`](Reference-manual.md#configure_file) +for more complicated setups. + +## Improvements to pkgconfig module + +A `StaticLibrary` or `SharedLibrary` object can optionally be passed +as first positional argument of the `generate()` method. If one is provided a +default value will be provided for all required fields of the pc file: +- `install_dir` is set to `pkgconfig` folder in the same location than the provided library. +- `description` is set to the project's name followed by the library's name. +- `name` is set to the library's name. + +Generating a .pc file is now as simple as: + +``` +pkgconfig.generate(mylib) +``` + +## pkgconfig.generate() requires parameters non-string arguments + +`pkgconfig.generate()` `requires:` and `requires_private:` keyword arguments +now accept pkgconfig-dependencies and libraries that pkgconfig-files were +generated for. + +## Generic python module + +Meson now has is a revamped and generic (python 2 and 3) version of the python3 +module. With [this new interface](Python-module.md), projects can now fully +specify the version of python they want to build against / install sources to, +and can do so against multiple major or minor versions in parallel. + +## test() now supports the `depends:` keyword argument + +Build targets and custom targets can be listed in the `depends:` keyword argument +of test function. These targets will be built before test is run even if they have +`build_by_default : false`. diff --git a/docs/markdown/Release-notes-for-0.47.0.md b/docs/markdown/Release-notes-for-0.47.0.md new file mode 100644 index 0000000..58d47ee --- /dev/null +++ b/docs/markdown/Release-notes-for-0.47.0.md @@ -0,0 +1,23 @@ +--- +title: Release 0.47 +short-description: Release notes for 0.46 (preliminary) +... + +# New features + +This page is a placeholder for the eventual release notes. + +Notable new features should come with release note updates. This is +done by creating a file snippet called `snippets/featurename.md` and +whose contents should look like this: + + ## Feature name + + A short description explaining the new feature and how it should be used. + +## Allow early return from a script + +Added the function `subdir_done()`. Its invocation exits the current script at +the point of invocation. All previously invoked build targets and commands are +build/executed. All following ones are ignored. If the current script was +invoked via `subdir()` the parent script continues normally. diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index 01c8c6e..30eedf8 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -131,7 +131,8 @@ int main (int argc, char ** argv) { }''' ``` -This can also be combined with the string formatting functionality +These are raw strings that do not support the escape sequences listed above. +These strings can also be combined with the string formatting functionality described below. #### String formatting diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 558378c..0a30d9c 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -9,6 +9,7 @@ listed in the [`meson` GitHub topic](https://github.com/topics/meson). - [AQEMU](https://github.com/tobimensch/aqemu), a Qt GUI for QEMU virtual machines, since version 0.9.3 - [Arduino sample project](https://github.com/jpakkane/mesonarduino) + - [bolt](https://gitlab.freedesktop.org/bolt/bolt) Userpsace daemon to enable security levels for Thunderboltâ„¢ 3 on Linux - [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 diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index d97fc9a..38e1ab2 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -93,9 +93,9 @@ revision=head The format is straightforward. The only thing to note is the revision element that can have one of two values. The first is `head` which will cause Meson to track the master head (doing a repull whenever the -build definition is altered). The second type is a commit hash. In -this case Meson will use the commit specified (with `git checkout -[hash id]`). +build definition is altered). The second type is a commit hash or a +tag. In this case Meson will use the commit specified (with `git +checkout [hash/tag id]`). Note that in this case you cannot specify an extra patch file to use. The git repo must contain all necessary Meson build definitions. diff --git a/docs/markdown/snippets/altered-logging.md b/docs/markdown/snippets/altered-logging.md deleted file mode 100644 index 4ff9bb0..0000000 --- a/docs/markdown/snippets/altered-logging.md +++ /dev/null @@ -1,5 +0,0 @@ -## Log output slightly changed - -The format of some human-readable diagnostic messages has changed in -minor ways. In case you are parsing these messages, you may need to -adjust your code. diff --git a/docs/markdown/snippets/armcc-cross.md b/docs/markdown/snippets/armcc-cross.md deleted file mode 100644 index 668f0ab..0000000 --- a/docs/markdown/snippets/armcc-cross.md +++ /dev/null @@ -1,15 +0,0 @@ -## ARM compiler for C and CPP - -Cross-compilation is now supported for ARM targets using ARM compiler - ARMCC. -The current implementation does not support shareable libraries. -The default extension of the output is .axf. -The environment path should be set properly for the ARM compiler executables. -The '--cpu' option with the appropriate target type should be mentioned -in the cross file as shown in the snippet below. - -``` -[properties] -c_args = ['--cpu=Cortex-M0plus'] -cpp_args = ['--cpu=Cortex-M0plus'] - -``` diff --git a/docs/markdown/snippets/both-libraries.md b/docs/markdown/snippets/both-libraries.md deleted file mode 100644 index 1632f63..0000000 --- a/docs/markdown/snippets/both-libraries.md +++ /dev/null @@ -1,9 +0,0 @@ -## Building both shared and static libraries - -A new function `both_libraries()` has been added to build both shared and static -libraries at the same time. Source files will be compiled only once and object -files will be reused to build both shared and static libraries, unless -`b_staticpic` user option or `pic` argument are set to false in which case -sources will be compiled twice. - -The returned `buildtarget` object always represents the shared library. diff --git a/docs/markdown/snippets/compiler-object-run_command.md b/docs/markdown/snippets/compiler-object-run_command.md deleted file mode 100644 index 0308416..0000000 --- a/docs/markdown/snippets/compiler-object-run_command.md +++ /dev/null @@ -1,10 +0,0 @@ -## Compiler object can now be passed to run_command() - -This can be used to run the current compiler with the specified arguments -to obtain additional information from it. -One of the use cases is to get the location of development files for the -GCC plugins: - - cc = meson.get_compiler('c') - result = run_command(cc, '-print-file-name=plugin') - plugin_dev_path = result.stdout().strip() diff --git a/docs/markdown/snippets/d-options-for-meson-setup.md b/docs/markdown/snippets/d-options-for-meson-setup.md deleted file mode 100644 index 37afbe0..0000000 --- a/docs/markdown/snippets/d-options-for-meson-setup.md +++ /dev/null @@ -1,6 +0,0 @@ -## Meson and meson configure now accept the same arguments - -Previously meson required that builtin arguments (like prefix) be passed as -`--prefix` to `meson` and `-Dprefix` to `meson configure`. `meson` now accepts -D -form like `meson configure` has. `meson configure` also accepts the `--prefix` -form, like `meson` has. diff --git a/docs/markdown/snippets/declare_dependency-link_whole.md b/docs/markdown/snippets/declare_dependency-link_whole.md deleted file mode 100644 index 827b1f6..0000000 --- a/docs/markdown/snippets/declare_dependency-link_whole.md +++ /dev/null @@ -1,4 +0,0 @@ -## declare_dependency() supports link_whole - -`declare_dependency()` supports `link_whole` parameter. -`link_whole` propagates to build target that uses dependency. diff --git a/docs/markdown/snippets/del-old-names.md b/docs/markdown/snippets/del-old-names.md deleted file mode 100644 index 5ac5873..0000000 --- a/docs/markdown/snippets/del-old-names.md +++ /dev/null @@ -1,7 +0,0 @@ -## Old command names are now errors - -Old executable names `mesonintrospect`, `mesonconf`, `mesonrewriter` -and `mesontest` have been deprecated for a long time. Starting from -this version they no longer do anything but instead always error -out. All functionality is available as subcommands in the main `meson` -binary. diff --git a/docs/markdown/snippets/find-override.md b/docs/markdown/snippets/find-override.md deleted file mode 100644 index ef3a4a2..0000000 --- a/docs/markdown/snippets/find-override.md +++ /dev/null @@ -1,37 +0,0 @@ -## Can override find_program - -It is now possible to override the result of `find_program` to point -to a custom program you want. The overriding is global and applies to -every subproject from there on. Here is how you would use it. - -In master project - -```meson -subproject('mydep') -``` - -In the called subproject: - -```meson -prog = find_program('my_custom_script') -meson.override_find_program('mycodegen', prog) -``` - -In master project (or, in fact, any subproject): - -```meson -genprog = find_program('mycodegen') -``` - -Now `genprog` points to the custom script. If the dependency had come -from the system, then it would point to the system version. - -You can also use the return value of `configure_file()` to override -a program in the same way as above: - -```meson -prog_script = configure_file(input : 'script.sh.in', - output : 'script.sh', - configuration : cdata) -meson.override_find_program('mycodegen', prog_script) -``` diff --git a/docs/markdown/snippets/has-link-argument.md b/docs/markdown/snippets/has-link-argument.md deleted file mode 100644 index 7beda63..0000000 --- a/docs/markdown/snippets/has-link-argument.md +++ /dev/null @@ -1,9 +0,0 @@ -## has_link_argument() and friends - -A new set of methods has been added on compiler objects to test if the linker -supports given arguments. - -- `has_link_argument()` -- `has_multi_link_arguments()` -- `get_supported_link_arguments()` -- `first_supported_link_argument()` diff --git a/docs/markdown/snippets/improved-help.md b/docs/markdown/snippets/improved-help.md deleted file mode 100644 index db7e852..0000000 --- a/docs/markdown/snippets/improved-help.md +++ /dev/null @@ -1,6 +0,0 @@ -## "meson help" now shows command line help - -Command line parsing is now less surprising. "meson help" is now -equivalent to "meson --help" and "meson help <subcommand>" is -equivalent to "meson <subcommand> --help", instead of creating a build -directory called "help" in these cases. diff --git a/docs/markdown/snippets/improved-meson-init.md b/docs/markdown/snippets/improved-meson-init.md deleted file mode 100644 index ec17bc4..0000000 --- a/docs/markdown/snippets/improved-meson-init.md +++ /dev/null @@ -1,19 +0,0 @@ -## Autogeneration of simple meson.build files - -A feature to generate a meson.build file compiling given C/C++ source -files into a single executable has been added to "meson init". By -default, it will take all recognizable source files in the current -directory. You can also specify a list of dependencies with the -d -flag and automatically invoke a build with the -b flag to check if the -code builds with those dependencies. - -For example, - -```meson -meson init -fbd sdl2,gl -``` - -will look for C or C++ files in the current directory, generate a -meson.build for them with the dependencies of sdl2 and gl and -immediately try to build it, overwriting any previous meson.build and -build directory. diff --git a/docs/markdown/snippets/install_data-rename.md b/docs/markdown/snippets/install_data-rename.md deleted file mode 100644 index 6378d0f..0000000 --- a/docs/markdown/snippets/install_data-rename.md +++ /dev/null @@ -1,11 +0,0 @@ -## install_data() supports rename - -`rename` parameter is used to change names of the installed files. -In order to install -- `file1.txt` into `share/myapp/dir1/data.txt` -- `file2.txt` into `share/myapp/dir2/data.txt` -```meson -install_data(['file1.txt', 'file2.txt'], - rename : ['dir1/data.txt', 'dir2/data.txt'], - install_dir : 'share/myapp') -``` diff --git a/docs/markdown/snippets/lcc.md b/docs/markdown/snippets/lcc.md deleted file mode 100644 index 2ce300d..0000000 --- a/docs/markdown/snippets/lcc.md +++ /dev/null @@ -1,23 +0,0 @@ -## Support for lcc compiler for e2k (Elbrus) architecture - -In this version, a support for lcc compiler for Elbrus processors -based on [e2k microarchitecture](https://en.wikipedia.org/wiki/Elbrus_2000) -has been added. - -Examples of such CPUs: -* [Elbrus-8S](https://en.wikipedia.org/wiki/Elbrus-8S); -* Elbrus-4S; -* [Elbrus-2S+](https://en.wikipedia.org/wiki/Elbrus-2S%2B). - -Such compiler have a similar behavior as gcc (basic option compatibility), -but, in is not strictly compatible with gcc as of current version. - -Major differences as of version 1.21.22: -* it does not support LTO and PCH; -* it suffers from the same dependency file creation error as icc; -* it has minor differences in output, especially version output; -* it differently reacts to lchmod() detection; -* some backend messages are produced in ru_RU.KOI8-R even if LANG=C; -* its preprocessor treats some characters differently. - -So every noted difference is properly handled now in meson.
\ No newline at end of file diff --git a/docs/markdown/snippets/more-escape-sequences.md b/docs/markdown/snippets/more-escape-sequences.md deleted file mode 100644 index 2894079..0000000 --- a/docs/markdown/snippets/more-escape-sequences.md +++ /dev/null @@ -1,17 +0,0 @@ -## String escape character update - -The strings (both single-quoted and triple-quoted) in meson has been taught the -same set of escape sequences as in Python. It is therefore now possible to use -arbitrary bytes in strings, like for example NUL (`\0`) and other ASCII control -characters. See the chapter about *Strings* in *Syntax* for more details. - -Potential backwards compatibility issue: Any valid escape sequence according to -the new rules will be interpreted as an escape sequence instead of the literal -characters. Previously only single-quote strings supported escape sequences and -the supported sequences were `\'`, `\\` and `\n`. - -The most likely breakage is usage of backslash-n in triple-quoted strings. It -is now written in the same way as in single-quoted strings: `\\n` instead of -`\n`. In general it is now recommended to escape any usage of backslash. -However, backslash-c (`\c`), for example, is still backslash-c because it isn't -a valid escape sequence. diff --git a/docs/markdown/snippets/new-wrap-mode.md b/docs/markdown/snippets/new-wrap-mode.md deleted file mode 100644 index e33dd83..0000000 --- a/docs/markdown/snippets/new-wrap-mode.md +++ /dev/null @@ -1,3 +0,0 @@ -A new wrap mode was added, `--wrap-mode=forcefallback`. When this is set, -dependencies for which a fallback was provided will always use it, even -if an external dependency exists and satisfies the version requirements. diff --git a/docs/markdown/snippets/non-unique-target-names.md b/docs/markdown/snippets/non-unique-target-names.md deleted file mode 100644 index 9b3f917..0000000 --- a/docs/markdown/snippets/non-unique-target-names.md +++ /dev/null @@ -1,9 +0,0 @@ -## Relaxing of target name requirements - -In earlier versions of Meson you could only have one target of a given name for each type. -For example you could not have two executables named `foo`. This requirement is now -relaxed so that you can have multiple targets with the same name, as long as they are in -different subdirectories. - -Note that projects that have multiple targets with the same name can not be built with -the `flat` layout or any backend that writes outputs in the same directory. diff --git a/docs/markdown/snippets/openmp-dependency.md b/docs/markdown/snippets/openmp-dependency.md deleted file mode 100644 index ad70011..0000000 --- a/docs/markdown/snippets/openmp-dependency.md +++ /dev/null @@ -1,6 +0,0 @@ -## Addition of OpenMP dependency - -An OpenMP dependency (`openmp`) has been added that encapsulates the various -flags used by compilers to enable OpenMP and checks for the existence of the -`omp.h` header. The `language` keyword may be passed to force the use of a -specific compiler for the checks. diff --git a/docs/markdown/snippets/partial-dependencies.md b/docs/markdown/snippets/partial-dependencies.md deleted file mode 100644 index 8d2136a..0000000 --- a/docs/markdown/snippets/partial-dependencies.md +++ /dev/null @@ -1,25 +0,0 @@ -## Added new partial_dependency method to dependencies and libraries - -It is now possible to use only part of a dependency in a target. This allows, -for example, to only use headers with convenience libraries to avoid linking -to the same library multiple times. - -```meson - -dep = dependency('xcb') - -helper = static_library( - 'helper', - ['helper1.c', 'helper2.c'], - dependencies : dep.partial_dependency(includes : true), -] - -final = shared_library( - 'final', - ['final.c'], - dependencyes : dep, -) -``` - -A partial dependency will have the same name version as the full dependency it -is derived from, as well as any values requested. diff --git a/docs/markdown/snippets/pkg-config-fix-static-only.md b/docs/markdown/snippets/pkg-config-fix-static-only.md deleted file mode 100644 index 31cd389..0000000 --- a/docs/markdown/snippets/pkg-config-fix-static-only.md +++ /dev/null @@ -1,12 +0,0 @@ -## Improved generation of pkg-config files for static only libraries. - -Previously pkg-config files generated by the pkgconfig modules for static libraries -with dependencies could only be used in a dependencies with `static: true`. - -Now the generated file contains the needed dependencies libraries directly within -`Requires` and `Libs` for build static libraries passed via the `libraries` keyword -argument. - -Projects that install both a static and a shared version of a library should use -the result of `both_libraries` to the pkg config file generator or use -configure_file for more complicated setups. diff --git a/docs/markdown/snippets/pkgconfig-generator.md b/docs/markdown/snippets/pkgconfig-generator.md deleted file mode 100644 index 93920d3..0000000 --- a/docs/markdown/snippets/pkgconfig-generator.md +++ /dev/null @@ -1,14 +0,0 @@ -## Improvements to pkgconfig module - -A `StaticLibrary` or `SharedLibrary` object can optionally be passed -as first positional argument of the `generate()` method. If one is provided a -default value will be provided for all required fields of the pc file: -- `install_dir` is set to `pkgconfig` folder in the same location than the provided library. -- `description` is set to the project's name followed by the library's name. -- `name` is set to the library's name. - -Generating a .pc file is now as simple as: - -``` -pkgconfig.generate(mylib) -``` diff --git a/docs/markdown/snippets/pkgconfig-requires-non-string.md b/docs/markdown/snippets/pkgconfig-requires-non-string.md deleted file mode 100644 index abf85b0..0000000 --- a/docs/markdown/snippets/pkgconfig-requires-non-string.md +++ /dev/null @@ -1,5 +0,0 @@ -## pkgconfig.generate() requires parameters non-string arguments - -`pkgconfig.generate()` `requires` and `requires_private` parameters -accept pkgconfig-dependencies and libraries that pkgconfig-files were -generated for. diff --git a/docs/markdown/snippets/python-module.md b/docs/markdown/snippets/python-module.md deleted file mode 100644 index c2e8138..0000000 --- a/docs/markdown/snippets/python-module.md +++ /dev/null @@ -1,6 +0,0 @@ -## Generic python module - -This is a revamped and generic (python 2 and 3) version of the python3 -module. With this new interface, projects can now fully specify the version -of python they want to build against / install sources to, and can do so -against multiple major or minor versions in parallel. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index b439b69..46b3b6a 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -66,6 +66,7 @@ index.md Shipping-prebuilt-binaries-as-wraps.md fallback-wraptool.md Release-notes.md + Release-notes-for-0.47.0.md Release-notes-for-0.46.0.md Release-notes-for-0.45.0.md Release-notes-for-0.44.0.md diff --git a/man/meson.1 b/man/meson.1 index 19ad737..b6f09de 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "March 2018" "meson 0.45.0" "User Commands" +.TH MESON "1" "April 2018" "meson 0.46.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/man/mesonconf.1 b/man/mesonconf.1 index b189663..9ead6ae 100644 --- a/man/mesonconf.1 +++ b/man/mesonconf.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "March 2018" "mesonconf 0.45.0" "User Commands" +.TH MESONCONF "1" "April 2018" "mesonconf 0.46.0" "User Commands" .SH NAME mesonconf - a tool to configure Meson builds .SH DESCRIPTION diff --git a/man/mesonintrospect.1 b/man/mesonintrospect.1 index 61aa381..3251299 100644 --- a/man/mesonintrospect.1 +++ b/man/mesonintrospect.1 @@ -1,4 +1,4 @@ -.TH MESONINTROSPECT "1" "March 2017" "mesonintrospect 0.45.0" "User Commands" +.TH MESONINTROSPECT "1" "April 2018" "mesonintrospect 0.46.0" "User Commands" .SH NAME mesonintrospect - a tool to extract information about a Meson build .SH DESCRIPTION diff --git a/man/mesontest.1 b/man/mesontest.1 index 9a9f743..97f8f0e 100644 --- a/man/mesontest.1 +++ b/man/mesontest.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "March 2018" "meson 0.45.0" "User Commands" +.TH MESON "1" "April 2018" "meson 0.46.0" "User Commands" .SH NAME mesontest - test tool for the Meson build system .SH DESCRIPTION diff --git a/man/wraptool.1 b/man/wraptool.1 index 93ec457..9d3a056 100644 --- a/man/wraptool.1 +++ b/man/wraptool.1 @@ -1,4 +1,4 @@ -.TH WRAPTOOL "1" "March 2018" "meson 0.45.0" "User Commands" +.TH WRAPTOOL "1" "April 2018" "meson 0.46.0" "User Commands" .SH NAME wraptool - source dependency downloader .SH DESCRIPTION diff --git a/manual tests/2 multiwrap/subprojects/libpng.wrap b/manual tests/2 multiwrap/subprojects/libpng.wrap index fb34a89..283775c 100644 --- a/manual tests/2 multiwrap/subprojects/libpng.wrap +++ b/manual tests/2 multiwrap/subprojects/libpng.wrap @@ -1,10 +1,10 @@ [wrap-file] -directory = libpng-1.6.17 +directory = libpng-1.6.34 -source_url = ftp://ftp.simplesystems.org/pub/libpng/png/src/history/libpng16/libpng-1.6.17.tar.xz -source_filename = libpng-1.6.17.tar.xz -source_hash = 98507b55fbe5cd43c51981f2924e4671fd81fe35d52dc53357e20f2c77fa5dfd +source_url = ftp://ftp-osl.osuosl.org/pub/libpng/src/libpng16/libpng-1.6.34.tar.xz +source_filename = libpng-1.6.34.tar.xz +source_hash = 2f1e960d92ce3b3abd03d06dfec9637dfbd22febf107a536b44f7a47c60659f6 -patch_url = https://wrapdb.mesonbuild.com/v1/projects/libpng/1.6.17/6/get_zip -patch_filename = libpng-1.6.17-6-wrap.zip -patch_hash = 8bd272e28e6ae84691935e84bca9f5eb02632221e6faccf427eb71bf745a7295 +patch_url = https://wrapdb.mesonbuild.com/v1/projects/libpng/1.6.34/1/get_zip +patch_filename = libpng-1.6.34-1-wrap.zip +patch_hash = 2123806eba8180c164e33a210f2892bbeb2473b69e56aecc786574e9221e6f20 diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 694700e..5a401fe 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -206,6 +206,8 @@ class Backend: return os.path.join(self.get_target_private_dir(target), src) def get_unity_source_file(self, target, suffix): + # There is a potential conflict here, but it is unlikely that + # anyone both enables unity builds and has a file called foo-unity.cpp. osrc = target.name + '-unity.' + suffix return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc) @@ -239,8 +241,11 @@ class Backend: os.path.join('dummyprefixdir', fromdir)) def flatten_object_list(self, target, proj_dir_to_build_root=''): + return self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root) + + def _flatten_object_list(self, target, objects, proj_dir_to_build_root): obj_list = [] - for obj in target.get_objects(): + for obj in objects: if isinstance(obj, str): o = os.path.join(proj_dir_to_build_root, self.build_to_src, target.get_subdir(), obj) @@ -248,7 +253,9 @@ class Backend: elif isinstance(obj, mesonlib.File): obj_list.append(obj.rel_to_builddir(self.build_to_src)) elif isinstance(obj, build.ExtractedObjects): - obj_list += self.determine_ext_objs(target, obj, proj_dir_to_build_root) + if obj.recursive: + obj_list += self._flatten_object_list(obj.target, obj.objlist, proj_dir_to_build_root) + obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root) else: raise MesonException('Unknown data type in object list.') return obj_list @@ -361,15 +368,11 @@ class Backend: result += [rp] return result - def object_filename_from_source(self, target, source, is_unity): + def object_filename_from_source(self, target, source): assert isinstance(source, mesonlib.File) build_dir = self.environment.get_build_dir() rel_src = source.rel_to_builddir(self.build_to_src) - if (not self.environment.is_source(rel_src) or - self.environment.is_header(rel_src)) and not is_unity: - return None - # foo.vala files compile down to foo.c and then foo.c.o, not foo.vala.o if rel_src.endswith(('.vala', '.gs')): # See description in generate_vala_compile for this logic. @@ -379,8 +382,6 @@ class Backend: rel_src = os.path.relpath(rel_src, self.get_target_private_dir(target)) else: rel_src = os.path.basename(rel_src) - if is_unity: - return 'meson-generated_' + rel_src[:-5] + '.c.' + self.environment.get_object_suffix() # A meson- prefixed directory is reserved; hopefully no-one creates a file name with such a weird prefix. source = 'meson-generated_' + rel_src[:-5] + '.c' elif source.is_built: @@ -398,24 +399,10 @@ class Backend: os.path.join(self.environment.get_source_dir(), target.get_subdir())) return source.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() - def determine_ext_objs(self, target, extobj, proj_dir_to_build_root): + def determine_ext_objs(self, extobj, proj_dir_to_build_root): result = [] - targetdir = self.get_target_private_dir(extobj.target) - # With unity builds, there's just one object that contains all the - # sources, and we only support extracting all the objects in this mode, - # so just return that. - if self.is_unity(target): - comp = get_compiler_for_source(extobj.target.compilers.values(), - extobj.srclist[0]) - # There is a potential conflict here, but it is unlikely that - # anyone both enables unity builds and has a file called foo-unity.cpp. - osrc = self.get_unity_source_file(extobj.target, - comp.get_default_suffix()) - objname = self.object_filename_from_source(extobj.target, osrc, True) - objname = objname.replace('/', '_').replace('\\', '_') - objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) - return [objpath] + # Merge sources and generated sources sources = list(extobj.srclist) for gensrc in extobj.genlist: for s in gensrc.get_outputs(): @@ -423,11 +410,30 @@ class Backend: dirpart, fnamepart = os.path.split(path) sources.append(File(True, dirpart, fnamepart)) + # Filter out headers and all non-source files + sources = [s for s in sources if self.environment.is_source(s) and not self.environment.is_header(s)] + + # extobj could contain only objects and no sources + if not sources: + return result + + targetdir = self.get_target_private_dir(extobj.target) + + # With unity builds, there's just one object that contains all the + # sources, and we only support extracting all the objects in this mode, + # so just return that. + if self.is_unity(extobj.target): + compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources) + sources = [] + for comp in compsrcs.keys(): + osrc = self.get_unity_source_file(extobj.target, + comp.get_default_suffix()) + sources.append(osrc) + for osrc in sources: - objname = self.object_filename_from_source(extobj.target, osrc, False) - if objname: - objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) - result.append(objpath) + objname = self.object_filename_from_source(extobj.target, osrc) + objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) + result.append(objpath) return result @@ -721,6 +727,9 @@ class Backend: if not isinstance(arg, (build.CustomTarget, build.BuildTarget)): continue result[arg.get_id()] = arg + for dep in t.depends: + assert isinstance(dep, (build.CustomTarget, build.BuildTarget)) + result[dep.get_id()] = dep return result def get_custom_target_provided_libraries(self, target): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index bc3a8ef..eeac388 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -252,6 +252,8 @@ int dummy; # Get all generated headers. Any source file might need them so # we need to add an order dependency to them. def get_generated_headers(self, target): + if hasattr(target, 'cached_generated_headers'): + return target.cached_generated_headers header_deps = [] # XXX: Why don't we add deps to CustomTarget headers here? for genlist in target.get_generated_sources(): @@ -267,6 +269,7 @@ int dummy; for dep in itertools.chain(target.link_targets, target.link_whole_targets): if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)): header_deps += self.get_generated_headers(dep) + target.cached_generated_headers = header_deps return header_deps def get_target_generated_sources(self, target): @@ -1637,7 +1640,7 @@ int dummy; outfile.write(description) outfile.write('\n') - def generate_fortran_dep_hack(self, outfile): + def generate_fortran_dep_hack(self, outfile, crstr): if mesonlib.is_windows(): cmd = 'cmd /C ""' else: @@ -1645,13 +1648,13 @@ int dummy; template = '''# Workaround for these issues: # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485 -rule FORTRAN_DEP_HACK +rule FORTRAN_DEP_HACK%s command = %s description = Dep hack restat = 1 ''' - outfile.write(template % cmd) + outfile.write(template % (crstr, cmd)) def generate_llvm_ir_compile_rule(self, compiler, is_cross, outfile): if getattr(self, 'created_llvm_ir_rule', False): @@ -1704,12 +1707,12 @@ rule FORTRAN_DEP_HACK if not is_cross: self.generate_swift_compile_rules(compiler, outfile) return - if langname == 'fortran': - self.generate_fortran_dep_hack(outfile) if is_cross: crstr = '_CROSS' else: crstr = '' + if langname == 'fortran': + self.generate_fortran_dep_hack(outfile, crstr) rule = 'rule %s%s_COMPILER\n' % (langname, crstr) depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE') quoted_depargs = [] @@ -2203,7 +2206,7 @@ rule FORTRAN_DEP_HACK raise AssertionError('BUG: broken generated source file handling for {!r}'.format(src)) else: raise InvalidArguments('Invalid source type: {!r}'.format(src)) - obj_basename = self.object_filename_from_source(target, src, self.is_unity(target)) + obj_basename = self.object_filename_from_source(target, src) rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) dep_file = compiler.depfile_for_object(rel_obj) @@ -2245,7 +2248,7 @@ rule FORTRAN_DEP_HACK modfile = os.path.join(self.get_target_private_dir(target), compiler.module_name_to_filename(modname)) if srcfile == src: - depelem = NinjaBuildElement(self.all_outputs, modfile, 'FORTRAN_DEP_HACK', rel_obj) + depelem = NinjaBuildElement(self.all_outputs, modfile, 'FORTRAN_DEP_HACK' + crstr, rel_obj) depelem.write(outfile) commands += compiler.get_module_outdir_args(self.get_target_private_dir(target)) @@ -2353,10 +2356,11 @@ rule FORTRAN_DEP_HACK return pch_objects def generate_shsym(self, outfile, target): - target_name = self.get_target_filename(target) + target_name = target.get_filename() + target_file = self.get_target_filename(target) targetdir = self.get_target_private_dir(target) symname = os.path.join(targetdir, target_name + '.symbols') - elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_name) + elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_file) if self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler(): elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system']) elem.write(outfile) @@ -2608,7 +2612,7 @@ rule FORTRAN_DEP_HACK def get_dependency_filename(self, t): if isinstance(t, build.SharedLibrary): - return os.path.join(self.get_target_private_dir(t), self.get_target_filename(t) + '.symbols') + return os.path.join(self.get_target_private_dir(t), t.get_filename() + '.symbols') elif isinstance(t, mesonlib.File): if t.is_built: return t.relative_name() diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 3a12b9b..09822ed 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1074,7 +1074,7 @@ class Vs2010Backend(backends.Backend): self.add_additional_options(lang, inc_cl, file_args) self.add_preprocessor_defines(lang, inc_cl, file_defines) self.add_include_dirs(lang, inc_cl, file_inc_dirs) - ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s, False) + ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) for s in gen_src: inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s) lang = Vs2010Backend.lang_from_source_file(s) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e707053..352f857 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -213,40 +213,49 @@ class ExtractedObjects: ''' Holds a list of sources for which the objects must be extracted ''' - def __init__(self, target, srclist, genlist, is_unity): + def __init__(self, target, srclist=[], genlist=[], objlist=[], recursive=True): self.target = target + self.recursive = recursive self.srclist = srclist self.genlist = genlist - if is_unity: + self.objlist = objlist + if self.target.is_unity: self.check_unity_compatible() def __repr__(self): r = '<{0} {1!r}: {2}>' return r.format(self.__class__.__name__, self.target.name, self.srclist) + def classify_all_sources(self, sources, generated_sources): + # Merge sources and generated sources + sources = list(sources) + for gensrc in generated_sources: + for s in gensrc.get_outputs(): + # We cannot know the path where this source will be generated, + # but all we need here is the file extension to determine the + # compiler. + sources.append(s) + + # Filter out headers and all non-source files + sources = [s for s in sources if environment.is_source(s) and not environment.is_header(s)] + + return classify_unity_sources(self.target.compilers.values(), sources) + def check_unity_compatible(self): # Figure out if the extracted object list is compatible with a Unity # build. When we're doing a Unified build, we go through the sources, # and create a single source file from each subset of the sources that # can be compiled with a specific compiler. Then we create one object - # from each unified source file. - # If the list of sources for which we want objects is the same as the - # list of sources that go into each unified build, we're good. - srclist_set = set(self.srclist) - # Objects for all the sources are required, so we're compatible - if srclist_set == set(self.target.sources): - return - # Check if the srclist is a subset (of the target's sources) that is - # going to form a unified source file and a single object - compsrcs = classify_unity_sources(self.target.compilers.values(), - self.target.sources) - for srcs in compsrcs.values(): - if srclist_set == set(srcs): - return - msg = 'Single object files can not be extracted in Unity builds. ' \ - 'You can only extract all the object files at once.' - raise MesonException(msg) + # from each unified source file. So for each compiler we can either + # extra all its sources or none. + cmpsrcs = self.classify_all_sources(self.target.sources, self.target.generated) + extracted_cmpsrcs = self.classify_all_sources(self.srclist, self.genlist) + for comp, srcs in extracted_cmpsrcs.items(): + if set(srcs) != set(cmpsrcs[comp]): + raise MesonException('Single object files can not be extracted ' + 'in Unity builds. You can only extract all ' + 'the object files for each compiler at once.') class EnvironmentVariables: def __init__(self): @@ -637,13 +646,11 @@ class BuildTarget(Target): if src not in self.sources: raise MesonException('Tried to extract unknown source %s.' % src) obj_src.append(src) - return ExtractedObjects(self, obj_src, [], self.is_unity) + return ExtractedObjects(self, obj_src) - def extract_all_objects(self): - # FIXME: We should add support for transitive extract_objects() - if self.objects: - raise MesonException('Cannot extract objects from a target that itself has extracted objects') - return ExtractedObjects(self, self.sources, self.generated, self.is_unity) + def extract_all_objects(self, recursive=True): + return ExtractedObjects(self, self.sources, self.generated, self.objects, + recursive) def get_all_link_deps(self): return self.get_transitive_link_deps() diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 1230e3f..91c9a16 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -329,7 +329,7 @@ class CCompiler(Compiler): def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - return self.compile(code, args.to_native(), mode, want_output=want_output) + return self.compile(code, args, mode, want_output=want_output) def links(self, code, env, extra_args=None, dependencies=None): return self.compiles(code, env, extra_args, dependencies, mode='link') diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 4887125..3dfb429 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -22,7 +22,7 @@ from .mesonlib import default_libdir, default_libexecdir, default_prefix import ast import argparse -version = '0.46.0.dev1' +version = '0.47.0.dev1' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode'] default_yielding = False diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 1c67311..5259c5b 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -15,7 +15,7 @@ from .boost import BoostDependency from .base import ( # noqa: F401 Dependency, DependencyException, DependencyMethods, ExternalProgram, NonExistingExternalProgram, - ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, + ExternalDependency, NotFoundDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language) from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency from .misc import (MPIDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 27a5fcb..0114a14 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -257,6 +257,14 @@ class ExternalDependency(Dependency): return new +class NotFoundDependency(Dependency): + def __init__(self, environment): + super().__init__('not-found', {}) + self.env = environment + self.name = 'not-found' + self.is_found = False + + class ConfigToolDependency(ExternalDependency): """Class representing dependencies found using a config tool.""" diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c7841a8..c948778 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -23,9 +23,9 @@ from .wrap import wrap, WrapMode from . import mesonlib from .mesonlib import FileMode, Popen_safe, listify, extract_as_list, has_path_sep from .dependencies import ExternalProgram -from .dependencies import InternalDependency, Dependency, DependencyException +from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException from .interpreterbase import InterpreterBase -from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs, permittedMethodKwargs +from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler from .modules import ModuleReturnValue @@ -37,6 +37,10 @@ from pathlib import PurePath import importlib +permitted_method_kwargs = { + 'partial_dependency': {'compile_args', 'link_args', 'links', 'includes', + 'sources'}, +} def stringifyUserArguments(args): if isinstance(args, list): @@ -66,15 +70,23 @@ class TryRunResultHolder(InterpreterObject): 'stderr': self.stderr_method, }) + @noPosargs + @permittedKwargs({}) def returncode_method(self, args, kwargs): return self.res.returncode + @noPosargs + @permittedKwargs({}) def compiled_method(self, args, kwargs): return self.res.compiled + @noPosargs + @permittedKwargs({}) def stdout_method(self, args, kwargs): return self.res.stdout + @noPosargs + @permittedKwargs({}) def stderr_method(self, args, kwargs): return self.res.stderr @@ -116,12 +128,18 @@ class RunProcess(InterpreterObject): except FileNotFoundError: raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array)) + @noPosargs + @permittedKwargs({}) def returncode_method(self, args, kwargs): return self.returncode + @noPosargs + @permittedKwargs({}) def stdout_method(self, args, kwargs): return self.stdout + @noPosargs + @permittedKwargs({}) def stderr_method(self, args, kwargs): return self.stderr @@ -146,7 +164,6 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder): repr_str = "<{0}: {1}>" return repr_str.format(self.__class__.__name__, self.held_object.envvars) - @stringArgs def add_var(self, method, args, kwargs): if not isinstance(kwargs.get("separator", ""), str): raise InterpreterException("EnvironmentVariablesHolder methods 'separator'" @@ -157,12 +174,18 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder): " following one are values") self.held_object.envvars.append((method, args[0], args[1:], kwargs)) + @stringArgs + @permittedKwargs({'separator'}) def set_method(self, args, kwargs): self.add_var(self.held_object.set, args, kwargs) + @stringArgs + @permittedKwargs({'separator'}) def append_method(self, args, kwargs): self.add_var(self.held_object.append, args, kwargs) + @stringArgs + @permittedKwargs({'separator'}) def prepend_method(self, args, kwargs): self.add_var(self.held_object.prepend, args, kwargs) @@ -278,17 +301,24 @@ class DependencyHolder(InterpreterObject, ObjectHolder): 'partial_dependency': self.partial_dependency_method, }) + @noPosargs + @permittedKwargs({}) def type_name_method(self, args, kwargs): return self.held_object.type_name + @noPosargs + @permittedKwargs({}) def found_method(self, args, kwargs): if self.held_object.type_name == 'internal': return True return self.held_object.found() + @noPosargs + @permittedKwargs({}) def version_method(self, args, kwargs): return self.held_object.get_version() + @permittedKwargs({'define_variable'}) def pkgconfig_method(self, args, kwargs): args = listify(args) if len(args) != 1: @@ -298,6 +328,7 @@ class DependencyHolder(InterpreterObject, ObjectHolder): raise InterpreterException('Variable name must be a string.') return self.held_object.get_pkgconfig_variable(varname, kwargs) + @permittedKwargs({}) def configtool_method(self, args, kwargs): args = listify(args) if len(args) != 1: @@ -307,9 +338,9 @@ class DependencyHolder(InterpreterObject, ObjectHolder): raise InterpreterException('Variable name must be a string.') return self.held_object.get_configtool_variable(varname) + @noPosargs + @permittedKwargs(permitted_method_kwargs['partial_dependency']) def partial_dependency_method(self, args, kwargs): - if args: - raise InterpreterException('partial_dependency takes no positional arguments') return DependencyHolder(self.held_object.get_partial_dependency(**kwargs)) class InternalDependencyHolder(InterpreterObject, ObjectHolder): @@ -321,15 +352,19 @@ class InternalDependencyHolder(InterpreterObject, ObjectHolder): 'partial_dependency': self.partial_dependency_method, }) + @noPosargs + @permittedKwargs({}) def found_method(self, args, kwargs): return True + @noPosargs + @permittedKwargs({}) def version_method(self, args, kwargs): return self.held_object.get_version() + @noPosargs + @permittedKwargs(permitted_method_kwargs['partial_dependency']) def partial_dependency_method(self, args, kwargs): - if args: - raise InterpreterException('get_partial_dependency takes no positional arguments') return DependencyHolder(self.held_object.get_partial_dependency(**kwargs)) class ExternalProgramHolder(InterpreterObject, ObjectHolder): @@ -339,9 +374,13 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): self.methods.update({'found': self.found_method, 'path': self.path_method}) + @noPosargs + @permittedKwargs({}) def found_method(self, args, kwargs): return self.found() + @noPosargs + @permittedKwargs({}) def path_method(self, args, kwargs): return self.held_object.get_path() @@ -365,6 +404,8 @@ class ExternalLibraryHolder(InterpreterObject, ObjectHolder): def found(self): return self.held_object.found() + @noPosargs + @permittedKwargs({}) def found_method(self, args, kwargs): return self.found() @@ -380,9 +421,9 @@ class ExternalLibraryHolder(InterpreterObject, ObjectHolder): def get_exe_args(self): return self.held_object.get_exe_args() + @noPosargs + @permittedKwargs(permitted_method_kwargs['partial_dependency']) def partial_dependency_method(self, args, kwargs): - if args: - raise InterpreterException('partial_dependency takes no positional arguments') return DependencyHolder(self.held_object.get_partial_dependency(**kwargs)) class GeneratorHolder(InterpreterObject, ObjectHolder): @@ -392,6 +433,7 @@ class GeneratorHolder(InterpreterObject, ObjectHolder): ObjectHolder.__init__(self, build.Generator(args, kwargs)) self.methods.update({'process': self.process_method}) + @permittedKwargs({'extra_args', 'preserve_path_from'}) def process_method(self, args, kwargs): extras = mesonlib.stringlistify(kwargs.get('extra_args', [])) if 'preserve_path_from' in kwargs: @@ -439,15 +481,23 @@ class BuildMachine(InterpreterObject, ObjectHolder): 'endian': self.endian_method, }) + @noPosargs + @permittedKwargs({}) def cpu_family_method(self, args, kwargs): return self.held_object.cpu_family + @noPosargs + @permittedKwargs({}) def cpu_method(self, args, kwargs): return self.held_object.cpu + @noPosargs + @permittedKwargs({}) def system_method(self, args, kwargs): return self.held_object.system + @noPosargs + @permittedKwargs({}) def endian_method(self, args, kwargs): return self.held_object.endian @@ -473,15 +523,23 @@ class CrossMachineInfo(InterpreterObject, ObjectHolder): 'endian': self.endian_method, }) + @noPosargs + @permittedKwargs({}) def cpu_family_method(self, args, kwargs): return self.held_object.cpu_family + @noPosargs + @permittedKwargs({}) def cpu_method(self, args, kwargs): return self.held_object.cpu + @noPosargs + @permittedKwargs({}) def system_method(self, args, kwargs): return self.held_object.system + @noPosargs + @permittedKwargs({}) def endian_method(self, args, kwargs): return self.held_object.endian @@ -592,24 +650,41 @@ class BuildTargetHolder(TargetHolder): def is_cross(self): return self.held_object.is_cross() + @noPosargs + @permittedKwargs({}) def private_dir_include_method(self, args, kwargs): return IncludeDirsHolder(build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self.held_object)])) + @noPosargs + @permittedKwargs({}) def full_path_method(self, args, kwargs): return self.interpreter.backend.get_target_filename_abs(self.held_object) + @noPosargs + @permittedKwargs({}) def outdir_method(self, args, kwargs): return self.interpreter.backend.get_target_dir(self.held_object) + @permittedKwargs({}) def extract_objects_method(self, args, kwargs): gobjs = self.held_object.extract_objects(args) return GeneratedObjectsHolder(gobjs) + @noPosargs + @permittedKwargs({'recursive'}) def extract_all_objects_method(self, args, kwargs): - gobjs = self.held_object.extract_all_objects() + recursive = kwargs.get('recursive', False) + gobjs = self.held_object.extract_all_objects(recursive) + if gobjs.objlist and 'recursive' not in kwargs: + mlog.warning('extract_all_objects called without setting recursive ' + 'keyword argument. Meson currently defaults to ' + 'non-recursive to maintain backward compatibility but ' + 'the default will be changed in the future.') return GeneratedObjectsHolder(gobjs) + @noPosargs + @permittedKwargs({}) def get_id_method(self, args, kwargs): return self.held_object.get_id() @@ -644,9 +719,13 @@ class BothLibrariesHolder(BuildTargetHolder): h2 = self.static_holder.held_object return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename) + @noPosargs + @permittedKwargs({}) def get_shared_lib_method(self, args, kwargs): return self.shared_holder + @noPosargs + @permittedKwargs({}) def get_static_lib_method(self, args, kwargs): return self.static_holder @@ -674,6 +753,8 @@ class CustomTargetHolder(TargetHolder): h = self.held_object return r.format(self.__class__.__name__, h.get_id(), h.command) + @noPosargs + @permittedKwargs({}) def full_path_method(self, args, kwargs): return self.interpreter.backend.get_target_filename_abs(self.held_object) @@ -697,12 +778,14 @@ class RunTargetHolder(InterpreterObject, ObjectHolder): return r.format(self.__class__.__name__, h.get_id(), h.command) class Test(InterpreterObject): - def __init__(self, name, project, suite, exe, is_parallel, cmd_args, env, should_fail, timeout, workdir): + def __init__(self, name, project, suite, exe, depends, is_parallel, + cmd_args, env, should_fail, timeout, workdir): InterpreterObject.__init__(self) self.name = name self.suite = suite self.project_name = project self.exe = exe + self.depends = depends self.is_parallel = is_parallel self.cmd_args = cmd_args self.env = env @@ -724,6 +807,7 @@ class SubprojectHolder(InterpreterObject, ObjectHolder): self.methods.update({'get_variable': self.get_variable_method, }) + @permittedKwargs({}) def get_variable_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Get_variable takes one argument.') @@ -768,11 +852,13 @@ class CompilerHolder(InterpreterObject): 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, }) - @permittedMethodKwargs({}) + @noPosargs + @permittedKwargs({}) def version_method(self, args, kwargs): return self.compiler.version - @permittedMethodKwargs({}) + @noPosargs + @permittedKwargs({}) def cmd_array_method(self, args, kwargs): return self.compiler.exelist @@ -812,7 +898,7 @@ class CompilerHolder(InterpreterObject): deps = final_deps return deps - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'args', 'dependencies', @@ -831,7 +917,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='') return result - @permittedMethodKwargs({ + @permittedKwargs({ 'name', 'no_builtin_args', 'include_directories', @@ -863,11 +949,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" runs: ', h, sep='') return TryRunResultHolder(result) - @permittedMethodKwargs({}) + @noPosargs + @permittedKwargs({}) def get_id_method(self, args, kwargs): return self.compiler.get_id() - @permittedMethodKwargs({}) + @noPosargs + @permittedKwargs({}) def symbols_have_underscore_prefix_method(self, args, kwargs): ''' Check if the compiler prefixes _ (underscore) to global C symbols @@ -875,7 +963,8 @@ class CompilerHolder(InterpreterObject): ''' return self.compiler.symbols_have_underscore_prefix(self.environment) - @permittedMethodKwargs({}) + @noPosargs + @permittedKwargs({}) def unittest_args_method(self, args, kwargs): ''' This function is deprecated and should not be used. @@ -886,7 +975,7 @@ class CompilerHolder(InterpreterObject): build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src) - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -914,7 +1003,7 @@ class CompilerHolder(InterpreterObject): '" has member "', mlog.bold(membername), '": ', hadtxt, sep='') return had - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -941,7 +1030,7 @@ class CompilerHolder(InterpreterObject): '" has members ', members, ': ', hadtxt, sep='') return had - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -966,7 +1055,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for function "', mlog.bold(funcname), '": ', hadtxt, sep='') return had - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -991,7 +1080,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='') return had - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'low', 'high', @@ -1024,7 +1113,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Computing int of "%s": %d' % (expression, res)) return res - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -1045,7 +1134,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for size of "%s": %d' % (element, esize)) return esize - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -1066,7 +1155,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Fetching value of define "%s": %s' % (element, value)) return value - @permittedMethodKwargs({ + @permittedKwargs({ 'name', 'no_builtin_args', 'include_directories', @@ -1096,7 +1185,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" compiles: ', h, sep='') return result - @permittedMethodKwargs({ + @permittedKwargs({ 'name', 'no_builtin_args', 'include_directories', @@ -1126,7 +1215,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" links: ', h, sep='') return result - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -1151,7 +1240,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Has header "%s":' % hname, h) return haz - @permittedMethodKwargs({ + @permittedKwargs({ 'prefix', 'no_builtin_args', 'include_directories', @@ -1177,7 +1266,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Header <{0}> has symbol "{1}":'.format(hname, symbol), h) return haz - @permittedMethodKwargs({ + @permittedKwargs({ 'required', 'dirs', }) @@ -1202,14 +1291,14 @@ class CompilerHolder(InterpreterObject): self.compiler.language) return ExternalLibraryHolder(lib) - @permittedMethodKwargs({}) + @permittedKwargs({}) def has_argument_method(self, args, kwargs): args = mesonlib.stringlistify(args) if len(args) != 1: raise InterpreterException('has_argument takes exactly one argument.') return self.has_multi_arguments_method(args, kwargs) - @permittedMethodKwargs({}) + @permittedKwargs({}) def has_multi_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.has_multi_arguments(args, self.environment) @@ -1223,7 +1312,7 @@ class CompilerHolder(InterpreterObject): h) return result - @permittedMethodKwargs({}) + @permittedKwargs({}) def get_supported_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) supported_args = [] @@ -1232,7 +1321,7 @@ class CompilerHolder(InterpreterObject): supported_args.append(arg) return supported_args - @permittedMethodKwargs({}) + @permittedKwargs({}) def first_supported_argument_method(self, args, kwargs): for i in mesonlib.stringlistify(args): if self.has_argument_method(i, kwargs): @@ -1241,14 +1330,14 @@ class CompilerHolder(InterpreterObject): mlog.log('First supported argument:', mlog.red('None')) return [] - @permittedMethodKwargs({}) + @permittedKwargs({}) def has_link_argument_method(self, args, kwargs): args = mesonlib.stringlistify(args) if len(args) != 1: raise InterpreterException('has_link_argument takes exactly one argument.') return self.has_multi_link_arguments_method(args, kwargs) - @permittedMethodKwargs({}) + @permittedKwargs({}) def has_multi_link_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.has_multi_link_arguments(args, self.environment) @@ -1262,7 +1351,7 @@ class CompilerHolder(InterpreterObject): h) return result - @permittedMethodKwargs({}) + @permittedKwargs({}) def get_supported_link_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) supported_args = [] @@ -1271,7 +1360,7 @@ class CompilerHolder(InterpreterObject): supported_args.append(arg) return supported_args - @permittedMethodKwargs({}) + @permittedKwargs({}) def first_supported_link_argument_method(self, args, kwargs): for i in mesonlib.stringlistify(args): if self.has_link_argument_method(i, kwargs): @@ -1378,6 +1467,7 @@ class MesonMain(InterpreterObject): raise InterpreterException(m.format(name)) return build.RunScript(found.get_command(), args) + @permittedKwargs({}) def add_install_script_method(self, args, kwargs): if len(args) < 1: raise InterpreterException('add_install_script takes one or more arguments') @@ -1385,6 +1475,7 @@ class MesonMain(InterpreterObject): script = self._find_source_script(args[0], args[1:]) self.build.install_scripts.append(script) + @permittedKwargs({}) def add_postconf_script_method(self, args, kwargs): if len(args) < 1: raise InterpreterException('add_postconf_script takes one or more arguments') @@ -1392,6 +1483,8 @@ class MesonMain(InterpreterObject): script = self._find_source_script(args[0], args[1:]) self.build.postconf_scripts.append(script) + @noPosargs + @permittedKwargs({}) def current_source_dir_method(self, args, kwargs): src = self.interpreter.environment.source_dir sub = self.interpreter.subdir @@ -1399,6 +1492,8 @@ class MesonMain(InterpreterObject): return src return os.path.join(src, sub) + @noPosargs + @permittedKwargs({}) def current_build_dir_method(self, args, kwargs): src = self.interpreter.environment.build_dir sub = self.interpreter.subdir @@ -1406,15 +1501,23 @@ class MesonMain(InterpreterObject): return src return os.path.join(src, sub) + @noPosargs + @permittedKwargs({}) def backend_method(self, args, kwargs): return self.interpreter.backend.name + @noPosargs + @permittedKwargs({}) def source_root_method(self, args, kwargs): return self.interpreter.environment.source_dir + @noPosargs + @permittedKwargs({}) def build_root_method(self, args, kwargs): return self.interpreter.environment.build_dir + @noPosargs + @permittedKwargs({}) def has_exe_wrapper_method(self, args, kwargs): if self.is_cross_build_method(None, None) and \ 'binaries' in self.build.environment.cross_info.config and \ @@ -1427,9 +1530,12 @@ class MesonMain(InterpreterObject): # Need to revisit this. return True + @noPosargs + @permittedKwargs({}) def is_cross_build_method(self, args, kwargs): return self.build.environment.is_cross_build() + @permittedKwargs({'native'}) def get_compiler_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('get_compiler_method must have one and only one argument.') @@ -1450,15 +1556,20 @@ class MesonMain(InterpreterObject): return CompilerHolder(clist[cname], self.build.environment) raise InterpreterException('Tried to access compiler for unspecified language "%s".' % cname) + @noPosargs + @permittedKwargs({}) def is_unity_method(self, args, kwargs): optval = self.interpreter.environment.coredata.get_builtin_option('unity') if optval == 'on' or (optval == 'subprojects' and self.interpreter.subproject != ''): return True return False + @noPosargs + @permittedKwargs({}) def is_subproject_method(self, args, kwargs): return self.interpreter.is_subproject() + @permittedKwargs({}) def install_dependency_manifest_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Must specify manifest install file name') @@ -1466,6 +1577,7 @@ class MesonMain(InterpreterObject): raise InterpreterException('Argument must be a string.') self.build.dep_manifest_name = args[0] + @permittedKwargs({}) def override_find_program_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('Override needs two arguments') @@ -1486,18 +1598,27 @@ class MesonMain(InterpreterObject): raise InterpreterException('Second argument must be an external program.') self.interpreter.add_find_program_override(name, exe) + @noPosargs + @permittedKwargs({}) def project_version_method(self, args, kwargs): return self.build.dep_manifest[self.interpreter.active_projectname]['version'] + @noPosargs + @permittedKwargs({}) def project_license_method(self, args, kwargs): return self.build.dep_manifest[self.interpreter.active_projectname]['license'] + @noPosargs + @permittedKwargs({}) def version_method(self, args, kwargs): return coredata.version + @noPosargs + @permittedKwargs({}) def project_name_method(self, args, kwargs): return self.interpreter.active_projectname + @permittedKwargs({}) def get_cross_property_method(self, args, kwargs): if len(args) < 1 or len(args) > 2: raise InterpreterException('Must have one or two arguments.') @@ -1555,7 +1676,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'library': known_library_kwargs, 'subdir': {'if_found'}, 'subproject': {'version', 'default_options'}, - 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, + 'test': {'args', 'depends', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'}, } @@ -2492,7 +2613,7 @@ to directly access options of other subprojects.''') if name == '': if required: raise InvalidArguments('Dependency is both required and not-found') - return DependencyHolder(Dependency('not-found', {})) + return DependencyHolder(NotFoundDependency(self.environment)) if '<' in name or '>' in name or '=' in name: raise InvalidArguments('Characters <, > and = are forbidden in dependency names. To specify' @@ -2516,7 +2637,7 @@ to directly access options of other subprojects.''') # We need to actually search for this dep exception = None - dep = None + dep = NotFoundDependency(self.environment) # Search for it outside the project if self.coredata.wrap_mode != WrapMode.forcefallback or 'fallback' not in kwargs: @@ -2528,7 +2649,7 @@ to directly access options of other subprojects.''') exception = DependencyException("fallback for %s not found" % name) # Search inside the projects list - if not dep or not dep.found(): + if not dep.found(): if 'fallback' in kwargs: fallback_dep = self.dependency_fallback(name, kwargs) if fallback_dep: @@ -2536,7 +2657,7 @@ to directly access options of other subprojects.''') # cannot cache them. They must always be evaluated else # we won't actually read all the build files. return fallback_dep - if not dep: + if required: assert(exception is not None) raise exception @@ -2734,7 +2855,7 @@ root and issuing %s. if 'command' not in kwargs: raise InterpreterException('Missing "command" keyword argument') all_args = extract_as_list(kwargs, 'command') - deps = extract_as_list(kwargs, 'depends') + deps = extract_as_list(kwargs, 'depends', unholder=True) else: raise InterpreterException('Run_target needs at least one positional argument.') @@ -2749,10 +2870,6 @@ root and issuing %s. raise InterpreterException('First argument must be a string.') cleaned_deps = [] for d in deps: - try: - d = d.held_object - except AttributeError: - pass if not isinstance(d, (build.BuildTarget, build.CustomTarget)): raise InterpreterException('Depends items must be build targets.') cleaned_deps.append(d) @@ -2835,7 +2952,12 @@ root and issuing %s. if len(s) > 0: s = ':' + s suite.append(prj.replace(' ', '_').replace(':', '_') + s) - t = Test(args[0], prj, suite, exe.held_object, par, cmd_args, env, should_fail, timeout, workdir) + depends = extract_as_list(kwargs, 'depends', unholder=True) + for dep in depends: + if not isinstance(dep, (build.CustomTarget, build.BuildTarget)): + raise InterpreterException('Depends items must be build targets.') + t = Test(args[0], prj, suite, exe.held_object, depends, par, cmd_args, + env, should_fail, timeout, workdir) if is_base_test: self.build.tests.append(t) mlog.debug('Adding test "', mlog.bold(args[0]), '".', sep='') @@ -3152,11 +3274,9 @@ different subdirectory. if ":" not in setup_name: setup_name = (self.subproject if self.subproject else self.build.project_name) + ":" + setup_name try: - inp = extract_as_list(kwargs, 'exe_wrapper') + inp = extract_as_list(kwargs, 'exe_wrapper', unholder=True) exe_wrapper = [] for i in inp: - if hasattr(i, 'held_object'): - i = i.held_object if isinstance(i, str): exe_wrapper.append(i) elif isinstance(i, dependencies.ExternalProgram): diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index f957d90..bedcf26 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -31,28 +31,51 @@ def check_stringlist(a, msg='Arguments must be strings.'): mlog.debug('Element not a string:', str(a)) raise InvalidArguments(msg) +def _get_callee_args(wrapped_args): + # Functions have 4 positional args and methods have 3. + s = wrapped_args[0] + if len(wrapped_args) == 3: + node_or_state = None + args = wrapped_args[1] + kwargs = wrapped_args[2] + elif len(wrapped_args) == 4: + node_or_state = wrapped_args[1] + args = wrapped_args[2] + kwargs = wrapped_args[3] + else: + raise InvalidArguments('Expecting 3 or 4 args, got {}'.format(len(wrapped_args))) + + # Sometimes interpreter methods are called internally with None instead of + # empty list/dict + args = args if args is not None else [] + kwargs = kwargs if kwargs is not None else {} + return s, node_or_state, args, kwargs + def noPosargs(f): @wraps(f) - def wrapped(self, node, args, kwargs): + def wrapped(*wrapped_args, **wrapped_kwargs): + args = _get_callee_args(wrapped_args)[2] if args: raise InvalidArguments('Function does not take positional arguments.') - return f(self, node, args, kwargs) + return f(*wrapped_args, **wrapped_kwargs) return wrapped def noKwargs(f): @wraps(f) - def wrapped(self, node, args, kwargs): + def wrapped(*wrapped_args, **wrapped_kwargs): + kwargs = _get_callee_args(wrapped_args)[3] if kwargs: raise InvalidArguments('Function does not take keyword arguments.') - return f(self, node, args, kwargs) + return f(*wrapped_args, **wrapped_kwargs) return wrapped def stringArgs(f): @wraps(f) - def wrapped(self, node, args, kwargs): + def wrapped(*wrapped_args, **wrapped_kwargs): + args = _get_callee_args(wrapped_args)[2] assert(isinstance(args, list)) check_stringlist(args) - return f(self, node, args, kwargs) + return f(*wrapped_args, **wrapped_kwargs) return wrapped class permittedKwargs: @@ -62,12 +85,13 @@ class permittedKwargs: def __call__(self, f): @wraps(f) - def wrapped(s, node_or_state, args, kwargs): + def wrapped(*wrapped_args, **wrapped_kwargs): + s, node_or_state, args, kwargs = _get_callee_args(wrapped_args) loc = types.SimpleNamespace() if hasattr(s, 'subdir'): loc.subdir = s.subdir loc.lineno = s.current_lineno - elif hasattr(node_or_state, 'subdir'): + elif node_or_state and hasattr(node_or_state, 'subdir'): loc.subdir = node_or_state.subdir loc.lineno = node_or_state.current_lineno else: @@ -76,26 +100,9 @@ class permittedKwargs: if k not in self.permitted: mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=loc) mlog.warning('This will become a hard error in the future.') - return f(s, node_or_state, args, kwargs) - return wrapped - - -class permittedMethodKwargs: - - def __init__(self, permitted): - self.permitted = permitted - - def __call__(self, f): - @wraps(f) - def wrapped(obj, args, kwargs): - for k in kwargs: - if k not in self.permitted: - mlog.warning('''Passed invalid keyword argument "{}".'''.format(k)) - mlog.warning('This will become a hard error in the future.') - return f(obj, args, kwargs) + return f(*wrapped_args, **wrapped_kwargs) return wrapped - class InterpreterException(mesonlib.MesonException): pass diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 6cbaf60..afefaa6 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -119,9 +119,8 @@ def _log_error(severity, *args, **kwargs): else: assert False, 'Invalid severity ' + severity - if 'location' in kwargs: - location = kwargs['location'] - del kwargs['location'] + location = kwargs.pop('location', None) + if location is not None: location_str = '{}:{}:'.format(os.path.join(location.subdir, environment.build_filename), location.lineno) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 5455118..abefe05 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -67,8 +67,13 @@ class GnomeModule(ExtensionModule): global native_glib_version if native_glib_version is None: glib_dep = PkgConfigDependency('glib-2.0', state.environment, - {'native': True}) - native_glib_version = glib_dep.get_version() + {'native': True, 'required': False}) + if glib_dep.found(): + native_glib_version = glib_dep.get_version() + else: + mlog.warning('Could not detect glib version, assuming 2.54. ' + 'You may get build errors if your glib is older.') + native_glib_version = '2.54' return native_glib_version def __print_gresources_warning(self, state): @@ -869,12 +874,12 @@ This will become a hard error in the future.''') return [] @permittedKwargs({'interface_prefix', 'namespace', 'object_manager', 'build_by_default', - 'annotations', 'docbook', 'install', 'install_header'}) + 'annotations', 'docbook', 'install_header', 'install_dir', 'sources'}) def gdbus_codegen(self, state, args, kwargs): - if len(args) != 2: - raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') + if len(args) not in (1, 2): + raise MesonException('Gdbus_codegen takes at most two arguments, name and xml file.') namebase = args[0] - xml_file = args[1] + xml_files = [args[1:]] target_name = namebase + '-gdbus' cmd = [self.interpreter.find_program_impl('gdbus-codegen')] if 'interface_prefix' in kwargs: @@ -883,6 +888,8 @@ This will become a hard error in the future.''') cmd += ['--c-namespace', kwargs.pop('namespace')] if kwargs.get('object_manager', False): cmd += ['--c-generate-object-manager'] + if 'sources' in kwargs: + xml_files += mesonlib.listify(kwargs.pop('sources')) build_by_default = kwargs.get('build_by_default', False) # Annotations are a bit ugly in that they are a list of lists of strings... @@ -904,7 +911,7 @@ This will become a hard error in the future.''') install_dir = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('includedir')) output = namebase + '.c' - custom_kwargs = {'input': xml_file, + custom_kwargs = {'input': xml_files, 'output': output, 'command': cmd + ['--body', '--output', '@OUTDIR@/' + output, '@INPUT@'], 'build_by_default': build_by_default @@ -912,7 +919,7 @@ This will become a hard error in the future.''') targets.append(build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs)) output = namebase + '.h' - custom_kwargs = {'input': xml_file, + custom_kwargs = {'input': xml_files, 'output': output, 'command': cmd + ['--header', '--output', '@OUTDIR@/' + output, '@INPUT@'], 'build_by_default': build_by_default, @@ -929,7 +936,7 @@ This will become a hard error in the future.''') docbook_cmd = cmd + ['--output-directory', '@OUTDIR@', '--generate-docbook', docbook, '@INPUT@'] output = namebase + '-docbook' - custom_kwargs = {'input': xml_file, + custom_kwargs = {'input': xml_files, 'output': output, 'command': docbook_cmd, 'build_by_default': build_by_default @@ -952,7 +959,7 @@ This will become a hard error in the future.''') self._print_gdbus_warning() cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] outputs = [namebase + '.c', namebase + '.h'] - custom_kwargs = {'input': xml_file, + custom_kwargs = {'input': xml_files, 'output': outputs, 'command': cmd, 'build_by_default': build_by_default diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 934e2f4..a3ba973 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -59,7 +59,7 @@ class DependenciesHelper: elif hasattr(obj, 'pcdep'): pcdeps = mesonlib.listify(obj.pcdep) for d in pcdeps: - processed_reqs += d.name + processed_reqs.append(d.name) self.add_version_reqs(d.name, obj.version_reqs) elif isinstance(obj, dependencies.PkgConfigDependency): if obj.found(): diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 16bdfd6..afcae8f 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -313,32 +313,58 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): return ModuleReturnValue(True, []) @noKwargs - def get_path(self, node, args, kwargs): + def has_path(self, node, args, kwargs): if len(args) != 1: - raise InvalidArguments('get_path takes exactly one positional argument.') + raise InvalidArguments('has_path takes exactly one positional argument.') path_name = args[0] if not isinstance(path_name, str): - raise InvalidArguments('get_path argument must be a string.') + raise InvalidArguments('has_path argument must be a string.') + + return ModuleReturnValue(path_name in self.paths, []) - path = self.paths.get(path_name) + @noKwargs + def get_path(self, node, args, kwargs): + if len(args) not in (1, 2): + raise InvalidArguments('get_path must have one or two arguments.') + path_name = args[0] + if not isinstance(path_name, str): + raise InvalidArguments('get_path argument must be a string.') - if path is None: - raise InvalidArguments('{} is not a valid path name'.format(path_name)) + try: + path = self.paths[path_name] + except KeyError: + if len(args) == 2: + path = args[1] + else: + raise InvalidArguments('{} is not a valid path name'.format(path_name)) return ModuleReturnValue(path, []) @noKwargs - def get_variable(self, node, args, kwargs): + def has_variable(self, node, args, kwargs): if len(args) != 1: - raise InvalidArguments('get_variable takes exactly one positional argument.') + raise InvalidArguments('has_variable takes exactly one positional argument.') var_name = args[0] if not isinstance(var_name, str): - raise InvalidArguments('get_variable argument must be a string.') + raise InvalidArguments('has_variable argument must be a string.') + + return ModuleReturnValue(var_name in self.variables, []) - var = self.variables.get(var_name) + @noKwargs + def get_variable(self, node, args, kwargs): + if len(args) not in (1, 2): + raise InvalidArguments('get_variable must have one or two arguments.') + var_name = args[0] + if not isinstance(var_name, str): + raise InvalidArguments('get_variable argument must be a string.') - if var is None: - raise InvalidArguments('{} is not a valid path name'.format(var_name)) + try: + var = self.variables[var_name] + except KeyError: + if len(args) == 2: + var = args[1] + else: + raise InvalidArguments('{} is not a valid variable name'.format(var_name)) return ModuleReturnValue(var, []) @@ -351,7 +377,7 @@ class PythonInstallation(ExternalProgramHolder, InterpreterObject): if method_name in ['extension_module', 'dependency', 'install_sources']: value = fn(self.interpreter, None, args, kwargs) return self.interpreter.holderify(value) - elif method_name in ['get_variable', 'get_path', 'found', 'language_version', 'get_install_dir']: + elif method_name in ['has_variable', 'get_variable', 'has_path', 'get_path', 'found', 'language_version', 'get_install_dir']: value = fn(None, args, kwargs) return self.interpreter.module_method_callback(value) else: diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 9e43065..8cef377 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -28,18 +28,6 @@ ESCAPE_SEQUENCE_SINGLE_RE = re.compile(r''' | \\[\\'abfnrtv] # Single-character escapes )''', re.UNICODE | re.VERBOSE) -# This is the regex for the supported escape sequences of a multiline string -# literal, like '''abc\x00'''. The only difference is that single quote (') -# doesn't require escaping. -ESCAPE_SEQUENCE_MULTI_RE = re.compile(r''' - ( \\U........ # 8-digit hex escapes - | \\u.... # 4-digit hex escapes - | \\x.. # 2-digit hex escapes - | \\[0-7]{1,3} # Octal escapes - | \\N\{[^}]+\} # Unicode characters by name - | \\[\\abfnrtv] # Single-character escapes - )''', re.UNICODE | re.VERBOSE) - class MesonUnicodeDecodeError(MesonException): def __init__(self, match): super().__init__("%s" % match) @@ -187,10 +175,6 @@ This will become a hard error in a future Meson release.""", self.getline(line_s elif tid == 'multiline_string': tid = 'string' value = match_text[3:-3] - try: - value = ESCAPE_SEQUENCE_MULTI_RE.sub(decode_match, value) - except MesonUnicodeDecodeError as err: - raise MesonException("Failed to parse escape sequence: '{}' in string:\n{}".format(err.match, match_text)) lines = match_text.split('\n') if len(lines) > 1: lineno += len(lines) - 1 diff --git a/run_project_tests.py b/run_project_tests.py index a1d36ef..8c02a9e 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -21,10 +21,7 @@ from io import StringIO from ast import literal_eval from enum import Enum import tempfile -from mesonbuild import mtest -from mesonbuild import environment -from mesonbuild import mesonlib -from mesonbuild import mlog +from mesonbuild import build, environment, mesonlib, mlog, mtest from mesonbuild.mesonlib import stringlistify, Popen_safe from mesonbuild.coredata import backendlist import argparse @@ -137,9 +134,9 @@ def get_relative_files_list_from_dir(fromdir): paths.append(path) return paths -def platform_fix_name(fname, compiler): +def platform_fix_name(fname, compiler, env): if '?lib' in fname: - if mesonlib.is_cygwin(): + if mesonlib.for_cygwin(env.is_cross_build(), env): fname = re.sub(r'lib/\?lib(.*)\.so$', r'bin/cyg\1.dll', fname) fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname) else: @@ -147,7 +144,7 @@ def platform_fix_name(fname, compiler): if fname.endswith('?exe'): fname = fname[:-4] - if mesonlib.is_windows() or mesonlib.is_cygwin(): + if mesonlib.for_windows(env.is_cross_build(), env) or mesonlib.for_cygwin(env.is_cross_build(), env): return fname + '.exe' if fname.startswith('?msvc:'): @@ -162,7 +159,7 @@ def platform_fix_name(fname, compiler): return fname -def validate_install(srcdir, installdir, compiler): +def validate_install(srcdir, installdir, compiler, env): # List of installed files info_file = os.path.join(srcdir, 'installed_files.txt') # If this exists, the test does not install any other files @@ -175,7 +172,7 @@ def validate_install(srcdir, installdir, compiler): elif os.path.exists(info_file): with open(info_file) as f: for line in f: - line = platform_fix_name(line.strip(), compiler) + line = platform_fix_name(line.strip(), compiler, env) if line: expected[line] = False # Check if expected files were found @@ -207,8 +204,16 @@ def log_text_file(logfile, testdir, stdo, stde): logfile.write(stde) logfile.write('\n\n---\n\n') if print_debug: - print(stdo) - print(stde, file=sys.stderr) + try: + print(stdo) + except UnicodeError: + sanitized_out = stdo.encode('ascii', errors='replace').decode() + print(sanitized_out) + try: + print(stde, file=sys.stderr) + except UnicodeError: + sanitized_err = stde.encode('ascii', errors='replace').decode() + print(sanitized_err, file=sys.stderr) if stop: print("Aborting..") for f in futures: @@ -314,7 +319,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen (returncode, stdo, stde) = run_configure(meson_command, gen_args) try: logfile = os.path.join(test_build_dir, 'meson-logs/meson-log.txt') - with open(logfile, errors='ignore') as f: + with open(logfile, encoding='utf-8', errors='ignore') as f: mesonlog = f.read() except Exception: mesonlog = no_meson_log_msg @@ -328,6 +333,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen return TestResult('Test that should have failed succeeded', BuildStep.configure, stdo, stde, mesonlog, gen_time) if returncode != 0: return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, gen_time) + builddata = build.load(test_build_dir) # Touch the meson.build file to force a regenerate so we can test that # regeneration works before a build is run. ensure_backend_detects_changes(backend) @@ -381,7 +387,8 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time) if not install_commands: return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time) - return TestResult(validate_install(testdir, install_dir, compiler), BuildStep.validate, stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult(validate_install(testdir, install_dir, compiler, builddata.environment), + BuildStep.validate, stdo, stde, mesonlog, gen_time, build_time, test_time) def gather_tests(testdir): tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(testdir + '/*')] @@ -516,7 +523,7 @@ def detect_tests_to_run(): def run_tests(all_tests, log_name_base, extra_args): global logfile txtname = log_name_base + '.txt' - with open(txtname, 'w', encoding="utf_8") as lf: + with open(txtname, 'w', encoding='utf-8', errors='ignore') as lf: logfile = lf return _run_tests(all_tests, log_name_base, extra_args) @@ -694,7 +701,10 @@ if __name__ == '__main__': if failing_tests > 0: print('\nMesonlogs of failing tests\n') for l in failing_logs: - print(l, '\n') + try: + print(l, '\n') + except UnicodeError: + print(l.encode('ascii', errors='replace').decode(), '\n') for name, dirs, skip in all_tests: dirs = (os.path.basename(x) for x in dirs) for k, g in itertools.groupby(dirs, key=lambda x: x.split()[0]): diff --git a/run_unittests.py b/run_unittests.py index e0cd1ec..ce18a1a 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -439,6 +439,7 @@ class InternalTests(unittest.TestCase): kwargs = {'sources': [1, 2, 3], 'pch_sources': [4, 5, 6]} self.assertEqual([[1, 2, 3], [4, 5, 6]], extract(kwargs, 'sources', 'pch_sources')) + @unittest.skipIf(not os.path.isdir('docs'), 'Doc dir not found, presumably because this is a tarball release.') def test_snippets(self): hashcounter = re.compile('^ *(#)+') snippet_dir = Path('docs/markdown/snippets') @@ -456,7 +457,6 @@ class InternalTests(unittest.TestCase): self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name) def test_pkgconfig_module(self): - deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib") class Mock: pass @@ -465,9 +465,17 @@ class InternalTests(unittest.TestCase): mock.pcdep = Mock() mock.pcdep.name = "some_name" mock.version_reqs = [] + + # pkgconfig dependency as lib + deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib") deps.add_pub_libs([mock]) self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name") + # pkgconfig dependency as requires + deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib") + deps.add_pub_reqs([mock]) + self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name") + class BasePlatformTests(unittest.TestCase): def setUp(self): @@ -634,9 +642,11 @@ class BasePlatformTests(unittest.TestCase): return self.build(target=target) def setconf(self, arg, will_build=True): + if not isinstance(arg, list): + arg = [arg] if will_build: ensure_backend_detects_changes(self.backend) - self._run(self.mconf_command + [arg, self.builddir]) + self._run(self.mconf_command + arg + [self.builddir]) def wipe(self): windows_proof_rmtree(self.builddir) @@ -2012,6 +2022,58 @@ recommended as it can lead to undefined behaviour on some platforms''') self.builddir = exebuilddir self.assertRebuiltTarget('app') + def test_conflicting_d_dash_option(self): + testdir = os.path.join(self.unit_test_dir, '30 mixed command line args') + with self.assertRaises(subprocess.CalledProcessError) as e: + self.init(testdir, extra_args=['-Dbindir=foo', '--bindir=bar']) + # Just to ensure that we caught the correct error + self.assertIn('passed as both', e.stderr) + + def _test_same_option_twice(self, arg, args): + testdir = os.path.join(self.unit_test_dir, '30 mixed command line args') + self.init(testdir, extra_args=args) + opts = self.introspect('--buildoptions') + for item in opts: + if item['name'] == arg: + self.assertEqual(item['value'], 'bar') + return + raise Exception('Missing {} value?'.format(arg)) + + def test_same_dash_option_twice(self): + self._test_same_option_twice('bindir', ['--bindir=foo', '--bindir=bar']) + + def test_same_d_option_twice(self): + self._test_same_option_twice('bindir', ['-Dbindir=foo', '-Dbindir=bar']) + + def test_same_project_d_option_twice(self): + self._test_same_option_twice('one', ['-Done=foo', '-Done=bar']) + + def _test_same_option_twice_configure(self, arg, args): + testdir = os.path.join(self.unit_test_dir, '30 mixed command line args') + self.init(testdir) + self.setconf(args) + opts = self.introspect('--buildoptions') + for item in opts: + if item['name'] == arg: + self.assertEqual(item['value'], 'bar') + return + raise Exception('Missing {} value?'.format(arg)) + + def test_same_dash_option_twice_configure(self): + with self.assertRaises(subprocess.CalledProcessError) as e: + self._test_same_option_twice_configure( + 'bindir', ['--bindir=foo', '--bindir=bar']) + self.assertIn('Pick one.', e.stderr) + + def test_same_d_option_twice_configure(self): + self._test_same_option_twice_configure( + 'bindir', ['-Dbindir=foo', '-Dbindir=bar']) + + def test_same_project_d_option_twice_configure(self): + self._test_same_option_twice_configure( + 'one', ['-Done=foo', '-Done=bar']) + + class FailureTests(BasePlatformTests): ''' @@ -2957,7 +3019,7 @@ class LinuxArmCrossCompileTests(BasePlatformTests): https://github.com/mesonbuild/meson/issues/3089 ''' testdir = os.path.join(self.unit_test_dir, '29 cross file overrides always args') - self.meson_cross_file = os.path.join(self.unit_test_dir, 'ubuntu-armhf-overrides.txt') + self.meson_cross_file = os.path.join(testdir, 'ubuntu-armhf-overrides.txt') self.init(testdir) compdb = self.get_compdb() self.assertRegex(compdb[0]['command'], '-D_FILE_OFFSET_BITS=64.*-U_FILE_OFFSET_BITS') diff --git a/test cases/common/132 dependency file generation/meson.build b/test cases/common/132 dependency file generation/meson.build index cd66cb7..b5ee47b 100644 --- a/test cases/common/132 dependency file generation/meson.build +++ b/test cases/common/132 dependency file generation/meson.build @@ -3,7 +3,7 @@ project('dep file gen', 'c') cc_id = meson.get_compiler('c').get_id() cc_ver = meson.get_compiler('c').version() -if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08')) # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja # (correctly) thinks that the rule has multiple outputs and errors out: # 'depfile has multiple output paths' diff --git a/test cases/common/183 same target name/file.c b/test cases/common/196 same target name/file.c index 6f1c172..6f1c172 100644 --- a/test cases/common/183 same target name/file.c +++ b/test cases/common/196 same target name/file.c diff --git a/test cases/common/183 same target name/meson.build b/test cases/common/196 same target name/meson.build index 4e585d5..4e585d5 100644 --- a/test cases/common/183 same target name/meson.build +++ b/test cases/common/196 same target name/meson.build diff --git a/test cases/common/183 same target name/sub/file2.c b/test cases/common/196 same target name/sub/file2.c index a5e453d..a5e453d 100644 --- a/test cases/common/183 same target name/sub/file2.c +++ b/test cases/common/196 same target name/sub/file2.c diff --git a/test cases/common/183 same target name/sub/meson.build b/test cases/common/196 same target name/sub/meson.build index 610a4a3..610a4a3 100644 --- a/test cases/common/183 same target name/sub/meson.build +++ b/test cases/common/196 same target name/sub/meson.build diff --git a/test cases/common/197 test depends/gen.py b/test cases/common/197 test depends/gen.py new file mode 100755 index 0000000..ee4ed98 --- /dev/null +++ b/test cases/common/197 test depends/gen.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import sys + + +def main(): + with open(sys.argv[1], 'w') as out: + out.write(sys.argv[2]) + out.write('\n') + + +if __name__ == '__main__': + main() diff --git a/test cases/common/197 test depends/main.c b/test cases/common/197 test depends/main.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/test cases/common/197 test depends/main.c @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/test cases/common/197 test depends/meson.build b/test cases/common/197 test depends/meson.build new file mode 100644 index 0000000..888c451 --- /dev/null +++ b/test cases/common/197 test depends/meson.build @@ -0,0 +1,26 @@ +project('test depends', 'c') + +gen = find_program('gen.py') + +custom_dep = custom_target('custom_dep', + build_by_default : false, + output : 'custom_dep.txt', + command : [gen, '@OUTPUT@', 'custom_dep'], +) + +exe_dep = executable('exe_dep', 'main.c', + build_by_default : false, +) + +test_prog = find_program('test.py') +test('string dependencies', test_prog, + args : [ + # This is declared for convenience, + # real use case might have some obscure method + # to find these dependencies, e.g. automatic plugin loading. + 'custom_dep.txt', + exe_dep.full_path(), + ], + depends : [custom_dep, exe_dep], + workdir : meson.current_build_dir(), +) diff --git a/test cases/common/197 test depends/test.py b/test cases/common/197 test depends/test.py new file mode 100755 index 0000000..5b9f65c --- /dev/null +++ b/test cases/common/197 test depends/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import os +import os.path +import sys + + +def main(): + print('Looking in:', os.getcwd()) + not_found = list() + for f in sys.argv[1:]: + if not os.path.exists(f): + not_found.append(f) + if not_found: + print('Not found:', ', '.join(not_found)) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/test cases/common/22 header in file list/meson.build b/test cases/common/22 header in file list/meson.build index ff42cc4..79eec8d 100644 --- a/test cases/common/22 header in file list/meson.build +++ b/test cases/common/22 header in file list/meson.build @@ -3,7 +3,7 @@ project('header in file list', 'c') cc_id = meson.get_compiler('c').get_id() cc_ver = meson.get_compiler('c').version() -if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08')) # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja # (correctly) thinks that the rule has multiple outputs and errors out: # 'depfile has multiple output paths' diff --git a/test cases/common/32 multiline string/meson.build b/test cases/common/32 multiline string/meson.build index 1f952f1..262cb15 100644 --- a/test cases/common/32 multiline string/meson.build +++ b/test cases/common/32 multiline string/meson.build @@ -23,3 +23,13 @@ quote2 = '\'' if quote1 != quote2 error('Single quote quoting is broken.') endif + +cc = meson.get_compiler('c') +prog = ''' +int main(int argc, char **argv) { + int num = 1; + printf("%d\n", num); + return 0; +}''' + +assert(cc.compiles(prog), 'multline test compile failed') diff --git a/test cases/common/33 try compile/meson.build b/test cases/common/33 try compile/meson.build index cb1037d..09ca395 100644 --- a/test cases/common/33 try compile/meson.build +++ b/test cases/common/33 try compile/meson.build @@ -1,11 +1,11 @@ project('try compile', 'c', 'cpp') code = '''#include<stdio.h> -void func() { printf("Something.\\n"); } +void func() { printf("Something.\n"); } ''' breakcode = '''#include<nonexisting.h> -void func() { printf("This won't work.\\n"); } +void func() { printf("This won't work.\n"); } ''' foreach compiler : [meson.get_compiler('c'), meson.get_compiler('cpp')] diff --git a/test cases/common/39 tryrun/meson.build b/test cases/common/39 tryrun/meson.build index daf5be7..c64446f 100644 --- a/test cases/common/39 tryrun/meson.build +++ b/test cases/common/39 tryrun/meson.build @@ -13,8 +13,8 @@ endif ok_code = '''#include<stdio.h> int main(int argc, char **argv) { - printf("%s\\n", "stdout"); - fprintf(stderr, "%s\\n", "stderr"); + printf("%s\n", "stdout"); + fprintf(stderr, "%s\n", "stderr"); return 0; } ''' diff --git a/test cases/common/42 string operations/meson.build b/test cases/common/42 string operations/meson.build index 1c289eb..6596142 100644 --- a/test cases/common/42 string operations/meson.build +++ b/test cases/common/42 string operations/meson.build @@ -78,15 +78,17 @@ assert('"1.1.20"'.strip('".') == '1.1.20', '". badly stripped') assert('"1.1.20" '.strip('" ') == '1.1.20', '". badly stripped') bs_c = '''\c''' -bs_bs_c = '''\\\c''' +bs_bs_c = '''\\c''' nl = ''' ''' -bs_n = '''\\n''' +bs_n = '''\n''' bs_nl = '''\ ''' -bs_bs_n = '''\\\\n''' -bs_bs_nl = '''\\\\ +bs_bs_n = '''\\n''' +bs_bs_nl = '''\\ ''' +bs_bs = '''\\''' +bs = '''\''' assert('\c' == bs_c, 'Single backslash broken') assert('\\c' == bs_c, 'Double backslash broken') @@ -97,3 +99,5 @@ assert('\\n' == bs_n, 'Double backslash broken before n') assert('\\\n' == bs_nl, 'Three backslash broken before n') assert('\\\\n' == bs_bs_n, 'Four backslash broken before n') assert('\\\\\n' == bs_bs_nl, 'Five backslash broken before n') +assert('\\\\' == bs_bs, 'Double-backslash broken') +assert('\\' == bs, 'Backslash broken') diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build index 2279513..d43915a 100644 --- a/test cases/common/64 custom header generator/meson.build +++ b/test cases/common/64 custom header generator/meson.build @@ -3,7 +3,7 @@ project('custom header generator', 'c') cc_id = meson.get_compiler('c').get_id() cc_ver = meson.get_compiler('c').version() -if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08')) # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja # (correctly) thinks that the rule has multiple outputs and errors out: # 'depfile has multiple output paths' diff --git a/test cases/common/88 extract all/meson.build b/test cases/common/88 extract all/meson.build index 91a8d5f..4f08a4f 100644 --- a/test cases/common/88 extract all/meson.build +++ b/test cases/common/88 extract all/meson.build @@ -2,8 +2,12 @@ project('extract all', 'c') a = static_library('a', 'one.c', 'two.c') b = static_library('b', 'three.c', 'four.c') -c = static_library('c', - objects : [a.extract_all_objects(), b.extract_all_objects()]) +c = static_library('c', objects : [a.extract_all_objects(), b.extract_all_objects()]) +d = static_library('d', objects : [a.extract_all_objects(), b.extract_all_objects(), c.extract_all_objects()]) +d_recursive = static_library('d_recursive', objects : [c.extract_all_objects(recursive : true)]) -e = executable('proggie', 'prog.c', link_with : c) +e = executable('proggie', 'prog.c', link_with : d) test('extall', e) + +e = executable('proggie_recursive', 'prog.c', link_with : d_recursive) +test('extall_recursive', e) diff --git a/test cases/frameworks/7 gnome/gdbus/meson.build b/test cases/frameworks/7 gnome/gdbus/meson.build index 57d7f23..68ad706 100644 --- a/test cases/frameworks/7 gnome/gdbus/meson.build +++ b/test cases/frameworks/7 gnome/gdbus/meson.build @@ -7,7 +7,8 @@ gdbus_src = gnome.gdbus_codegen('generated-gdbus-no-docbook', 'com.example.Sampl ) assert(gdbus_src.length() == 2, 'expected 2 targets') -gdbus_src = gnome.gdbus_codegen('generated-gdbus', 'com.example.Sample.xml', +gdbus_src = gnome.gdbus_codegen('generated-gdbus', + sources : 'com.example.Sample.xml', interface_prefix : 'com.example.', namespace : 'Sample', annotations : [ diff --git a/test cases/unit/27 forcefallback/meson.build b/test cases/unit/27 forcefallback/meson.build index e6a90ea..d5c06c3 100644 --- a/test cases/unit/27 forcefallback/meson.build +++ b/test cases/unit/27 forcefallback/meson.build @@ -2,7 +2,8 @@ project('mainproj', 'c', default_options : ['wrap_mode=forcefallback']) zlib_dep = dependency('zlib', fallback: ['notzlib', 'zlib_dep']) +notfound_dep = dependency('cannotabletofind', fallback: ['definitelynotfound', 'some_var'], required : false) -test_not_zlib = executable('test_not_zlib', ['test_not_zlib.c'], dependencies: [zlib_dep]) +test_not_zlib = executable('test_not_zlib', ['test_not_zlib.c'], dependencies: [zlib_dep, notfound_dep]) test('test_not_zlib', test_not_zlib) diff --git a/test cases/unit/30 mixed command line args/meson.build b/test cases/unit/30 mixed command line args/meson.build new file mode 100644 index 0000000..af5cdc7 --- /dev/null +++ b/test cases/unit/30 mixed command line args/meson.build @@ -0,0 +1 @@ +project('Mixed command line arguments') diff --git a/test cases/unit/30 mixed command line args/meson_options.txt b/test cases/unit/30 mixed command line args/meson_options.txt new file mode 100644 index 0000000..5a4bc22 --- /dev/null +++ b/test cases/unit/30 mixed command line args/meson_options.txt @@ -0,0 +1,10 @@ +option( + 'one', + type : 'string', +) +option( + 'two', + type : 'combo', + choices : ['foo', 'bar'], + value : 'foo', +) |