diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 449504096a77..3b726315537a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -96,7 +96,7 @@ jobs: find /opt/homebrew/Cellar/python* -name EXTERNALLY-MANAGED -print0 | xargs -0 rm -vf # use python3 from homebrew because it is a valid framework, unlike the actions one: # https://github.com/actions/setup-python/issues/58 - - run: brew install pkg-config ninja llvm qt@5 boost ldc hdf5 openmpi lapack scalapack sdl2 boost-python3 gtk-doc zstd ncurses objfw + - run: brew install pkg-config ninja llvm qt@5 boost ldc hdf5 openmpi lapack scalapack sdl2 boost-python3 gtk-doc zstd ncurses objfw libomp - run: | python3 -m pip install --upgrade setuptools python3 -m pip install --upgrade pip diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 1d779842e27a..4fa4a87ed15d 100644 --- a/.github/workflows/os_comp.yml +++ b/.github/workflows/os_comp.yml @@ -26,6 +26,12 @@ on: - ".github/workflows/os_comp.yml" - "run*tests.py" +# make GHA actions use node16 which still works with bionic +# See https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/ +# Unclear how long this will work though +env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + permissions: contents: read diff --git a/docs/markdown/Configuration.md b/docs/markdown/Configuration.md index b5875e55e6d9..48f071e6c9e4 100644 --- a/docs/markdown/Configuration.md +++ b/docs/markdown/Configuration.md @@ -39,7 +39,7 @@ use a single `configuration_data` object as many times as you like, but it becomes immutable after being passed to the `configure_file` function. That is, after it has been used once to generate output the `set` function becomes unusable and trying to call it causes an error. -*Since 1.5.0* Copy of immutable `configuration_data` is however mutable. +Copy of immutable `configuration_data` is still immutable. For more complex configuration file generation Meson provides a second form. To use it, put a line like this in your configuration file. diff --git a/docs/markdown/Release-notes-for-1.5.0.md b/docs/markdown/Release-notes-for-1.5.0.md new file mode 100644 index 000000000000..7dfea9af2b13 --- /dev/null +++ b/docs/markdown/Release-notes-for-1.5.0.md @@ -0,0 +1,214 @@ +--- +title: Release 1.5.0 +short-description: Release notes for 1.5.0 +... + +# New features + +Meson 1.5.0 was released on 10 July 2024 +## Support for `bztar` in `meson dist` + +The `bztar` format is now supported in `meson dist`. This format is also known +as `bzip2`. + +## Cargo dependencies names now include the API version + +Cargo dependencies names are now in the format `--rs`: +- `package_name` is defined in `[package] name = ...` section of the `Cargo.toml`. +- `version` is the API version deduced from `[package] version = ...` as follow: + * `x.y.z` -> 'x' + * `0.x.y` -> '0.x' + * `0.0.x` -> '0' + It allows to make different dependencies for uncompatible versions of the same + crate. +- `-rs` suffix is added to distinguish from regular system dependencies, for + example `gstreamer-1.0` is a system pkg-config dependency and `gstreamer-0.22-rs` + is a Cargo dependency. + +That means the `.wrap` file should have `dependency_names = foo-1-rs` in their +`[provide]` section when `Cargo.toml` has package name `foo` and version `1.2`. + +This is a breaking change (Cargo subprojects are still experimental), previous +versions were using `-rs` format. + +## Added support `Cargo.lock` file + +When a (sub)project has a `Cargo.lock` file at its root, it is loaded to provide +an automatic fallback for dependencies it defines, fetching code from +https://crates.io or git. This is identical as providing `subprojects/*.wrap`, +see [cargo wraps](Wrap-dependency-system-manual.md#cargo-wraps) dependency naming convention. + +## Meson now propagates its build type to CMake + +When the CMake build type variable, `CMAKE_BUILD_TYPE`, is not set via the +`add_cmake_defines` method of the [`cmake options` object](CMake-module.md#cmake-options-object), +it is inferred from the [Meson build type](Builtin-options.md#details-for-buildtype). +Build types of the two build systems do not match perfectly. The mapping from +Meson build type to CMake build type is as follows: + +- `debug` -> `Debug` +- `debugoptimized` -> `RelWithDebInfo` +- `release` -> `Release` +- `minsize` -> `MinSizeRel` + +No CMake build type is set for the `plain` Meson build type. The inferred CMake +build type overrides any `CMAKE_BUILD_TYPE` environment variable. + +## compiler.run() method is now available for all languages + +It used to be only implemented for C-like and D languages, but it is now available +for all languages. + +## dependencies created by compiler.find_library implement the `name()` method + +Previously, for a [[@dep]] that might be returned by either [[dependency]] or +[[compiler.find_library]], the method might or might not exist with no way +of telling. + +## New version_argument kwarg for find_program + +When finding an external program with `find_program`, the `version_argument` +can be used to override the default `--version` argument when trying to parse +the version of the program. + +For example, if the following is used: +```meson +foo = find_program('foo', version_argument: '-version') +``` + +meson will internally run `foo -version` when trying to find the version of `foo`. + +## Meson configure handles changes to options in more cases + +Meson configure now correctly handles updates to the options file without a full +reconfigure. This allows making a change to the `meson.options` or +`meson_options.txt` file without a reconfigure. + +For example, this now works: +```sh +meson setup builddir +git pull +meson configure builddir -Doption-added-by-pull=value +``` + +## New meson format command + +This command is similar to `muon fmt` and allows to format a `meson.build` +document. + +## Added support for GCC's `null_terminated_string_arg` function attribute + +You can now check if a compiler support the `null_terminated_string_arg` +function attribute via the `has_function_attribute()` method on the +[[@compiler]] object. + +```meson +cc = meson.get_compiler('c') + +if cc.has_function_attribute('null_terminated_string_arg') + # We have it... +endif +``` + +## A new dependency for ObjFW is now supported + +For example, you can create a simple application written using ObjFW like this: + +```meson +project('SimpleApp', 'objc') + +objfw_dep = dependency('objfw', version: '>= 1.0') + +executable('SimpleApp', 'SimpleApp.m', + dependencies: [objfw_dep]) +``` + +Modules are also supported. A test case using ObjFWTest can be created like +this: + +```meson +project('Tests', 'objc') + +objfwtest_dep = dependency('objfw', version: '>= 1.1', modules: ['ObjFWTest']) + +executable('Tests', ['FooTest.m', 'BarTest.m'], + dependencies: [objfwtest_dep]) +``` + +## Support of indexed `@PLAINNAME@` and `@BASENAME@` + +In `custom_target()` and `configure_file()` with multiple inputs, +it is now possible to specify index for `@PLAINNAME@` and `@BASENAME@` +macros in `output`: +``` +custom_target('target_name', + output: '@PLAINNAME0@.dl', + input: [dep1, dep2], + command: cmd) +``` + +## Required kwarg on more `compiler` methods + +The following `compiler` methods now support the `required` keyword argument: + +- `compiler.compiles()` +- `compiler.links()` +- `compiler.runs()` + +```meson +cc.compiles(valid, name: 'valid', required : true) +cc.links(valid, name: 'valid', required : true) +cc.run(valid, name: 'valid', required : true) + +assert(not cc.compiles(valid, name: 'valid', required : opt)) +assert(not cc.links(valid, name: 'valid', required : opt)) +res = cc.run(valid, name: 'valid', required : opt) +assert(res.compiled()) +assert(res.returncode() == 0) +assert(res.stdout() == '') +assert(res.stderr() == '') +``` + +## The Meson test program supports a new "--interactive" argument + +`meson test --interactive` invokes tests with stdout, stdin and stderr +connected directly to the calling terminal. This can be useful when running +integration tests that run in containers or virtual machines which can spawn a +debug shell if a test fails. + +## meson test now sets the `MESON_TEST_ITERATION` environment variable + +`meson test` will now set the `MESON_TEST_ITERATION` environment variable to the +current iteration of the test. This will always be `1` unless `--repeat` is used +to run the same test multiple times. + +## The Meson test program supports a new "--max-lines" argument + +By default `meson test` only shows the last 100 lines of test output from tests +that produce large amounts of output. This default can now be changed with the +new `--max-lines` option. For example, `--max-lines=1000` will increase the +maximum number of log output lines from 100 to 1000. + +## Basic support for TI Arm Clang (tiarmclang) + +Support for TI's newer [Clang-based ARM toolchain](https://www.ti.com/tool/ARM-CGT). + +## Support for Texas Instruments C6000 C/C++ compiler + +Meson now supports the TI C6000 C/C++ compiler use for the C6000 cpu family. +The example cross file is available in `cross/ti-c6000.txt`. + +## Wayland stable protocols can be versioned + +The wayland module now accepts a version number for stable protocols. + +```meson +wl_mod = import('unstable-wayland') + +wl_mod.find_protocol( + 'linux-dmabuf', + state: 'stable' + version: 1 +) +``` + diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 19c4ea6438e4..70060ab05247 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -168,7 +168,7 @@ format files - [ThorVG](https://www.thorvg.org/), vector-based scenes and animations library - [Tilix](https://github.com/gnunn1/tilix), a tiling terminal emulator for Linux using GTK+ 3 - [Tizonia](https://github.com/tizonia/tizonia-openmax-il), a command-line cloud music player for Linux with support for Spotify, Google Play Music, YouTube, SoundCloud, TuneIn, Plex servers and Chromecast devices - - [Fossil Logic Standard](https://github.com/fossil-lib), a collection frameworks in C/C++, Objective-C and Objective-C++. + - [Fossil Logic](https://github.com/fossillogic), Fossil Logic is a cutting-edge software development company specializing in C/C++, Python, programming, Android development using Kotlin, and SQL solutions. - [UFJF-MLTK](https://github.com/mateus558/UFJF-Machine-Learning-Toolkit), A C++ cross-platform framework for machine learning algorithms development and testing - [Vala Language Server](https://github.com/benwaffle/vala-language-server), code intelligence engine for the Vala and Genie programming languages - [Valum](https://github.com/valum-framework/valum), a micro web framework written in Vala diff --git a/docs/markdown/snippets/bztar_support.md b/docs/markdown/snippets/bztar_support.md deleted file mode 100644 index 3ee4a91add30..000000000000 --- a/docs/markdown/snippets/bztar_support.md +++ /dev/null @@ -1,4 +0,0 @@ -## Support for `bztar` in `meson dist` - -The `bztar` format is now supported in `meson dist`. This format is also known -as `bzip2`. diff --git a/docs/markdown/snippets/cargo_dep_name.md b/docs/markdown/snippets/cargo_dep_name.md deleted file mode 100644 index b769f2b9034f..000000000000 --- a/docs/markdown/snippets/cargo_dep_name.md +++ /dev/null @@ -1,19 +0,0 @@ -## Cargo dependencies names now include the API version - -Cargo dependencies names are now in the format `--rs`: -- `package_name` is defined in `[package] name = ...` section of the `Cargo.toml`. -- `version` is the API version deduced from `[package] version = ...` as follow: - * `x.y.z` -> 'x' - * `0.x.y` -> '0.x' - * `0.0.x` -> '0' - It allows to make different dependencies for uncompatible versions of the same - crate. -- `-rs` suffix is added to distinguish from regular system dependencies, for - example `gstreamer-1.0` is a system pkg-config dependency and `gstreamer-0.22-rs` - is a Cargo dependency. - -That means the `.wrap` file should have `dependency_names = foo-1-rs` in their -`[provide]` section when `Cargo.toml` has package name `foo` and version `1.2`. - -This is a breaking change (Cargo subprojects are still experimental), previous -versions were using `-rs` format. diff --git a/docs/markdown/snippets/cargo_lock.md b/docs/markdown/snippets/cargo_lock.md deleted file mode 100644 index e38c5ed35340..000000000000 --- a/docs/markdown/snippets/cargo_lock.md +++ /dev/null @@ -1,6 +0,0 @@ -## Added support `Cargo.lock` file - -When a (sub)project has a `Cargo.lock` file at its root, it is loaded to provide -an automatic fallback for dependencies it defines, fetching code from -https://crates.io or git. This is identical as providing `subprojects/*.wrap`, -see [cargo wraps](Wrap-dependency-system-manual.md#cargo-wraps) dependency naming convention. diff --git a/docs/markdown/snippets/cmake_build_type.md b/docs/markdown/snippets/cmake_build_type.md deleted file mode 100644 index af9e84dc8e85..000000000000 --- a/docs/markdown/snippets/cmake_build_type.md +++ /dev/null @@ -1,15 +0,0 @@ -## Meson now propagates its build type to CMake - -When the CMake build type variable, `CMAKE_BUILD_TYPE`, is not set via the -`add_cmake_defines` method of the [`cmake options` object](CMake-module.md#cmake-options-object), -it is inferred from the [Meson build type](Builtin-options.md#details-for-buildtype). -Build types of the two build systems do not match perfectly. The mapping from -Meson build type to CMake build type is as follows: - -- `debug` -> `Debug` -- `debugoptimized` -> `RelWithDebInfo` -- `release` -> `Release` -- `minsize` -> `MinSizeRel` - -No CMake build type is set for the `plain` Meson build type. The inferred CMake -build type overrides any `CMAKE_BUILD_TYPE` environment variable. diff --git a/docs/markdown/snippets/compiler_run.md b/docs/markdown/snippets/compiler_run.md deleted file mode 100644 index f4b0847a5dc4..000000000000 --- a/docs/markdown/snippets/compiler_run.md +++ /dev/null @@ -1,4 +0,0 @@ -## compiler.run() method is now available for all languages - -It used to be only implemented for C-like and D languages, but it is now available -for all languages. diff --git a/docs/markdown/snippets/find_library_name.md b/docs/markdown/snippets/find_library_name.md deleted file mode 100644 index e7b0e1a5bab2..000000000000 --- a/docs/markdown/snippets/find_library_name.md +++ /dev/null @@ -1,5 +0,0 @@ -## dependencies created by compiler.find_library implement the `name()` method - -Previously, for a [[@dep]] that might be returned by either [[dependency]] or -[[compiler.find_library]], the method might or might not exist with no way -of telling. diff --git a/docs/markdown/snippets/find_program_version_argument.md b/docs/markdown/snippets/find_program_version_argument.md deleted file mode 100644 index 99fe62109776..000000000000 --- a/docs/markdown/snippets/find_program_version_argument.md +++ /dev/null @@ -1,12 +0,0 @@ -## New version_argument kwarg for find_program - -When finding an external program with `find_program`, the `version_argument` -can be used to override the default `--version` argument when trying to parse -the version of the program. - -For example, if the following is used: -```meson -foo = find_program('foo', version_argument: '-version') -``` - -meson will internally run `foo -version` when trying to find the version of `foo`. diff --git a/docs/markdown/snippets/meson_configure_options_changes.md b/docs/markdown/snippets/meson_configure_options_changes.md deleted file mode 100644 index c86792ceb52a..000000000000 --- a/docs/markdown/snippets/meson_configure_options_changes.md +++ /dev/null @@ -1,12 +0,0 @@ -## Meson configure handles changes to options in more cases - -Meson configure now correctly handles updates to the options file without a full -reconfigure. This allows making a change to the `meson.options` or -`meson_options.txt` file without a reconfigure. - -For example, this now works: -```sh -meson setup builddir -git pull -meson configure builddir -Doption-added-by-pull=value -``` diff --git a/docs/markdown/snippets/meson_format_cmd.md b/docs/markdown/snippets/meson_format_cmd.md deleted file mode 100644 index 390f15d581c7..000000000000 --- a/docs/markdown/snippets/meson_format_cmd.md +++ /dev/null @@ -1,4 +0,0 @@ -## New meson format command - -This command is similar to `muon fmt` and allows to format a `meson.build` -document. diff --git a/docs/markdown/snippets/null_terminated_string_arg.md b/docs/markdown/snippets/null_terminated_string_arg.md deleted file mode 100644 index 2ba1755758f8..000000000000 --- a/docs/markdown/snippets/null_terminated_string_arg.md +++ /dev/null @@ -1,13 +0,0 @@ -## Added support for GCC's `null_terminated_string_arg` function attribute - -You can now check if a compiler support the `null_terminated_string_arg` -function attribute via the `has_function_attribute()` method on the -[[@compiler]] object. - -```meson -cc = meson.get_compiler('c') - -if cc.has_function_attribute('null_terminated_string_arg') - # We have it... -endif -``` diff --git a/docs/markdown/snippets/objfw_dep.md b/docs/markdown/snippets/objfw_dep.md deleted file mode 100644 index e65da2885b4a..000000000000 --- a/docs/markdown/snippets/objfw_dep.md +++ /dev/null @@ -1,24 +0,0 @@ -## A new dependency for ObjFW is now supported - -For example, you can create a simple application written using ObjFW like this: - -```meson -project('SimpleApp', 'objc') - -objfw_dep = dependency('objfw', version: '>= 1.0') - -executable('SimpleApp', 'SimpleApp.m', - dependencies: [objfw_dep]) -``` - -Modules are also supported. A test case using ObjFWTest can be created like -this: - -```meson -project('Tests', 'objc') - -objfwtest_dep = dependency('objfw', version: '>= 1.1', modules: ['ObjFWTest']) - -executable('Tests', ['FooTest.m', 'BarTest.m'], - dependencies: [objfwtest_dep]) -``` diff --git a/docs/markdown/snippets/pln_bsn_support.md b/docs/markdown/snippets/pln_bsn_support.md deleted file mode 100644 index 394339f1499d..000000000000 --- a/docs/markdown/snippets/pln_bsn_support.md +++ /dev/null @@ -1,11 +0,0 @@ -## Support of indexed `@PLAINNAME@` and `@BASENAME@` - -In `custom_target()` and `configure_file()` with multiple inputs, -it is now possible to specify index for `@PLAINNAME@` and `@BASENAME@` -macros in `output`: -``` -custom_target('target_name', - output: '@PLAINNAME0@.dl', - input: [dep1, dep2], - command: cmd) -``` diff --git a/docs/markdown/snippets/requires_kwarg_on_more_compiler_methods.md b/docs/markdown/snippets/requires_kwarg_on_more_compiler_methods.md deleted file mode 100644 index 693313c31413..000000000000 --- a/docs/markdown/snippets/requires_kwarg_on_more_compiler_methods.md +++ /dev/null @@ -1,21 +0,0 @@ -## Required kwarg on more `compiler` methods - -The following `compiler` methods now support the `required` keyword argument: - -- `compiler.compiles()` -- `compiler.links()` -- `compiler.runs()` - -```meson -cc.compiles(valid, name: 'valid', required : true) -cc.links(valid, name: 'valid', required : true) -cc.run(valid, name: 'valid', required : true) - -assert(not cc.compiles(valid, name: 'valid', required : opt)) -assert(not cc.links(valid, name: 'valid', required : opt)) -res = cc.run(valid, name: 'valid', required : opt) -assert(res.compiled()) -assert(res.returncode() == 0) -assert(res.stdout() == '') -assert(res.stderr() == '') -``` diff --git a/docs/markdown/snippets/test_interactive.md b/docs/markdown/snippets/test_interactive.md deleted file mode 100644 index 907147fd9ca9..000000000000 --- a/docs/markdown/snippets/test_interactive.md +++ /dev/null @@ -1,6 +0,0 @@ -## The Meson test program supports a new "--interactive" argument - -`meson test --interactive` invokes tests with stdout, stdin and stderr -connected directly to the calling terminal. This can be useful when running -integration tests that run in containers or virtual machines which can spawn a -debug shell if a test fails. diff --git a/docs/markdown/snippets/test_iteration.md b/docs/markdown/snippets/test_iteration.md deleted file mode 100644 index 67daf278129d..000000000000 --- a/docs/markdown/snippets/test_iteration.md +++ /dev/null @@ -1,5 +0,0 @@ -## meson test now sets the `MESON_TEST_ITERATION` environment variable - -`meson test` will now set the `MESON_TEST_ITERATION` environment variable to the -current iteration of the test. This will always be `1` unless `--repeat` is used -to run the same test multiple times. diff --git a/docs/markdown/snippets/test_max_lines.md b/docs/markdown/snippets/test_max_lines.md deleted file mode 100644 index ea10fbb2791b..000000000000 --- a/docs/markdown/snippets/test_max_lines.md +++ /dev/null @@ -1,6 +0,0 @@ -## The Meson test program supports a new "--max-lines" argument - -By default `meson test` only shows the last 100 lines of test output from tests -that produce large amounts of output. This default can now be changed with the -new `--max-lines` option. For example, `--max-lines=1000` will increase the -maximum number of log output lines from 100 to 1000. diff --git a/docs/markdown/snippets/ti_armclang.md b/docs/markdown/snippets/ti_armclang.md deleted file mode 100644 index 7f0f912c0ed8..000000000000 --- a/docs/markdown/snippets/ti_armclang.md +++ /dev/null @@ -1,3 +0,0 @@ -## Basic support for TI Arm Clang (tiarmclang) - -Support for TI's newer [Clang-based ARM toolchain](https://www.ti.com/tool/ARM-CGT). diff --git a/docs/markdown/snippets/ti_c6000_compiler_support.md b/docs/markdown/snippets/ti_c6000_compiler_support.md deleted file mode 100644 index 03533e6091cd..000000000000 --- a/docs/markdown/snippets/ti_c6000_compiler_support.md +++ /dev/null @@ -1,4 +0,0 @@ -## Support for Texas Instruments C6000 C/C++ compiler - -Meson now supports the TI C6000 C/C++ compiler use for the C6000 cpu family. -The example cross file is available in `cross/ti-c6000.txt`. diff --git a/docs/markdown/snippets/wayland_stable_prot_version.md b/docs/markdown/snippets/wayland_stable_prot_version.md deleted file mode 100644 index 78d0a50d5c89..000000000000 --- a/docs/markdown/snippets/wayland_stable_prot_version.md +++ /dev/null @@ -1,13 +0,0 @@ -## Wayland stable protocols can be versioned - -The wayland module now accepts a version number for stable protocols. - -```meson -wl_mod = import('unstable-wayland') - -wl_mod.find_protocol( - 'linux-dmabuf', - state: 'stable' - version: 1 -) -``` diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 858eb38c0101..218d1a634230 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -89,6 +89,7 @@ index.md Wrap-best-practices-and-tips.md Shipping-prebuilt-binaries-as-wraps.md Release-notes.md + Release-notes-for-1.5.0.md Release-notes-for-1.4.0.md Release-notes-for-1.3.0.md Release-notes-for-1.2.0.md diff --git a/docs/yaml/objects/cfg_data.yaml b/docs/yaml/objects/cfg_data.yaml index 069cadbf6eb1..03abb170970e 100644 --- a/docs/yaml/objects/cfg_data.yaml +++ b/docs/yaml/objects/cfg_data.yaml @@ -1,14 +1,10 @@ name: cfg_data long_name: Configuration data object description: | - This object encapsulates configuration values to be used for generating - configuration files. A more in-depth description can be found in the - [the configuration page](Configuration.md). - - This object becomes immutable after first use. This means that - calling set() or merge_from() will cause an error if this object has - already been used in any function arguments. However, assignment creates a - mutable copy. + This object encapsulates + configuration values to be used for generating configuration files. A + more in-depth description can be found in the [the configuration wiki + page](Configuration.md). methods: - name: set diff --git a/docs/yaml/objects/env.yaml b/docs/yaml/objects/env.yaml index 3b2e2a851b24..714da4fe422a 100644 --- a/docs/yaml/objects/env.yaml +++ b/docs/yaml/objects/env.yaml @@ -9,11 +9,6 @@ description: | on the same `varname`. Earlier Meson versions would warn and only the last operation took effect. - *Since 1.5.0* This object becomes immutable after first use. This means that - calling append(), prepend() or set() will cause a deprecation warning if this - object has already been used in any function arguments. However, assignment - creates a mutable copy. - example: | ```meson env = environment() @@ -23,14 +18,6 @@ example: | env.append('MY_PATH', '2') env.append('MY_PATH', '3') env.prepend('MY_PATH', '0') - - # Deprecated since 1.5.0 - run_command('script.py', env: env) - env.append('MY_PATH', '4') - - # Allowed and only env2 is modified - env2 = env - env2.append('MY_PATH', '4') ``` methods: diff --git a/man/meson.1 b/man/meson.1 index 182ac17fce19..5c929bc1adf9 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "June 2024" "meson 1.5.0" "User Commands" +.TH MESON "1" "September 2024" "meson 1.5.2" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION @@ -410,7 +410,7 @@ Manage the packagefiles overlay .B meson rewrite modifies the project definition. - + .B meson rewrite [ .I options .B ] [ diff --git a/mesonbuild/_typing.py b/mesonbuild/_typing.py index 05ff2b3da948..8336c46bd06d 100644 --- a/mesonbuild/_typing.py +++ b/mesonbuild/_typing.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2020 The Meson development team -# Copyright © 2020-2023 Intel Corporation +# Copyright © 2020-2024 Intel Corporation """Meson specific typing helpers. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b664300f4006..4c7faa5aa380 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -568,7 +568,8 @@ def get_executable_serialisation( else: extra_paths = [] - if self.environment.need_exe_wrapper(exe_for_machine): + is_cross_built = not self.environment.machines.matches_build_machine(exe_for_machine) + if is_cross_built and self.environment.need_exe_wrapper(): if not self.environment.has_exe_wrapper(): msg = 'An exe_wrapper is needed but was not found. Please define one ' \ 'in cross file and check the command and/or add it to PATH.' @@ -1169,7 +1170,7 @@ def extract_dll_paths(cls, target: build.BuildTarget) -> T.Set[str]: def determine_windows_extra_paths( self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, programs.ExternalProgram, mesonlib.File, str], - extra_bdeps: T.Sequence[T.Union[build.BuildTarget, build.CustomTarget]]) -> T.List[str]: + extra_bdeps: T.Sequence[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]) -> T.List[str]: """On Windows there is no such thing as an rpath. We must determine all locations of DLLs that this exe @@ -1229,7 +1230,7 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali exe_wrapper = self.environment.get_exe_wrapper() machine = self.environment.machines[exe.for_machine] if machine.is_windows() or machine.is_cygwin(): - extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget]] = [] + extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = [] if isinstance(exe, build.CustomTarget): extra_bdeps = list(exe.get_transitive_build_target_deps()) extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps) @@ -1426,7 +1427,7 @@ def get_testlike_targets(self, benchmark: bool = False) -> T.OrderedDict[str, T. continue result[arg.get_id()] = arg for dep in t.depends: - assert isinstance(dep, (build.CustomTarget, build.BuildTarget)) + assert isinstance(dep, (build.CustomTarget, build.BuildTarget, build.CustomTargetIndex)) result[dep.get_id()] = dep return result diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index eabe75865d2c..9c6932884e4a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -98,10 +98,11 @@ def get_rsp_threshold() -> int: # and that has a limit of 8k. limit = 8192 else: - # On Linux, ninja always passes the commandline as a single - # big string to /bin/sh, and the kernel limits the size of a - # single argument; see MAX_ARG_STRLEN - limit = 131072 + # Unix-like OSes usualy have very large command line limits, (On Linux, + # for example, this is limited by the kernel's MAX_ARG_STRLEN). However, + # some programs place much lower limits, notably Wine which enforces a + # 32k limit like Windows. Therefore, we limit the command line to 32k. + limit = 32768 # Be conservative limit = limit // 2 return int(os.environ.get('MESON_RSP_THRESHOLD', limit)) @@ -291,7 +292,7 @@ def length_estimate(self, infiles: str, outfiles: str, estimate = len(command) for m in re.finditer(r'(\${\w+}|\$\w+)?[^$]*', command): if m.start(1) != -1: - estimate -= m.end(1) - m.start(1) + 1 + estimate -= m.end(1) - m.start(1) chunk = m.group(1) if chunk[1] == '{': chunk = chunk[2:-1] @@ -699,10 +700,11 @@ def generate_rust_project_json(self) -> None: return with open(os.path.join(self.environment.get_build_dir(), 'rust-project.json'), 'w', encoding='utf-8') as f: + sysroot = self.environment.coredata.compilers.host['rust'].get_sysroot() json.dump( { - "sysroot_src": os.path.join(self.environment.coredata.compilers.host['rust'].get_sysroot(), - 'lib/rustlib/src/rust/library/'), + "sysroot": sysroot, + "sysroot_src": os.path.join(sysroot, 'lib/rustlib/src/rust/library/'), "crates": [c.to_json() for c in self.rust_crates.values()], }, f, indent=4) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c6039c6819f4..02d2bb94df83 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1280,10 +1280,10 @@ def get_dependencies(self) -> OrderedSet[BuildTargetTypes]: if t not in result: result.add(t) if isinstance(t, StaticLibrary): - t.get_dependencies_recurse(result) + t.get_dependencies_recurse(result, include_proc_macros = self.uses_rust()) return result - def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include_internals: bool = True) -> None: + def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include_internals: bool = True, include_proc_macros: bool = False) -> None: # self is always a static library because we don't need to pull dependencies # of shared libraries. If self is installed (not internal) it already # include objects extracted from all its internal dependencies so we can @@ -1292,14 +1292,14 @@ def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include for t in self.link_targets: if t in result: continue - if t.rust_crate_type == 'proc-macro': + if not include_proc_macros and t.rust_crate_type == 'proc-macro': continue if include_internals or not t.is_internal(): result.add(t) if isinstance(t, StaticLibrary): - t.get_dependencies_recurse(result, include_internals) + t.get_dependencies_recurse(result, include_internals, include_proc_macros) for t in self.link_whole_targets: - t.get_dependencies_recurse(result, include_internals) + t.get_dependencies_recurse(result, include_internals, include_proc_macros) def get_source_subdir(self): return self.subdir diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 33b9d6073e4b..029e5a1b8006 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -54,13 +54,17 @@ toml2json = shutil.which('toml2json') +class TomlImplementationMissing(MesonException): + pass + + def load_toml(filename: str) -> T.Dict[object, object]: if tomllib: with open(filename, 'rb') as f: raw = tomllib.load(f) else: if toml2json is None: - raise MesonException('Could not find an implementation of tomllib, nor toml2json') + raise TomlImplementationMissing('Could not find an implementation of tomllib, nor toml2json') p, out, err = Popen_safe([toml2json, filename]) if p.returncode != 0: @@ -741,7 +745,11 @@ def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition wraps: T.List[PackageDefinition] = [] filename = os.path.join(source_dir, 'Cargo.lock') if os.path.exists(filename): - cargolock = T.cast('manifest.CargoLock', load_toml(filename)) + try: + cargolock = T.cast('manifest.CargoLock', load_toml(filename)) + except TomlImplementationMissing as e: + mlog.warning('Failed to load Cargo.lock:', str(e), fatal=False) + return wraps for package in cargolock['package']: name = package['name'] version = package['version'] diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 1aad0bc3b5bc..89d5d84449e0 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -188,6 +188,8 @@ def make_abs(exe: str) -> str: defaults[prefix + 'COMPILER'] = exe_list if comp_obj.get_id() == 'clang-cl': defaults['CMAKE_LINKER'] = comp_obj.get_linker_exelist() + if lang.startswith('objc') and comp_obj.get_id().startswith('clang'): + defaults[f'{prefix}FLAGS'] = ['-D__STDC__=1'] return defaults diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index bfe436b0b256..e25f18d6746d 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -75,7 +75,7 @@ def get_output_args(self, outputname: str) -> T.List[str]: def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: outargs: T.List[str] = [] for arg in args: - if arg == '-pthread': + if arg in {'-mms-bitfields', '-pthread'}: continue outargs.append(arg) return outargs diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 8fda3a5a508b..cbc1bea95d6e 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -10,6 +10,7 @@ from .. import mlog from ..mesonlib import MesonException, version_compare from .c_function_attributes import C_FUNC_ATTRIBUTES +from .mixins.apple import AppleCompilerMixin from .mixins.clike import CLikeCompiler from .mixins.ccrx import CcrxCompiler from .mixins.xc16 import Xc16Compiler @@ -187,7 +188,7 @@ class ArmLtdClangCCompiler(ClangCCompiler): id = 'armltdclang' -class AppleClangCCompiler(ClangCCompiler): +class AppleClangCCompiler(AppleCompilerMixin, ClangCCompiler): """Handle the differences between Apple Clang and Vanilla Clang. diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index c03f1fd372a0..08a596c7078c 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2022 The Meson development team -# Copyright © 2023 Intel Corporation +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations @@ -936,11 +936,11 @@ def thread_flags(self, env: 'Environment') -> T.List[str]: def thread_link_flags(self, env: 'Environment') -> T.List[str]: return self.linker.thread_flags(env) - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language()) - def openmp_link_flags(self) -> T.List[str]: - return self.openmp_flags() + def openmp_link_flags(self, env: Environment) -> T.List[str]: + return self.openmp_flags(env) def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: return [] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 9467a35c58c1..044cd2b378a4 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -19,6 +19,7 @@ CompileCheckMode, ) from .c_function_attributes import CXX_FUNC_ATTRIBUTES, C_FUNC_ATTRIBUTES +from .mixins.apple import AppleCompilerMixin from .mixins.clike import CLikeCompiler from .mixins.ccrx import CcrxCompiler from .mixins.ti import TICompiler @@ -337,7 +338,7 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): id = 'armltdclang' -class AppleClangCPPCompiler(ClangCPPCompiler): +class AppleClangCPPCompiler(AppleCompilerMixin, ClangCPPCompiler): _CPP23_VERSION = '>=13.0.0' # TODO: We don't know which XCode version will include LLVM 17 yet, so @@ -617,8 +618,8 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] args: T.List[str] = [] key = self.form_langopt_key('std') std = options.get_value(key) - if std.value != 'none': - args.append(self._find_best_cpp_std(std.value)) + if std != 'none': + args.append(self._find_best_cpp_std(std)) key = self.form_langopt_key('eh') non_msvc_eh_options(options.get_value(key), args) @@ -694,7 +695,7 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] 'c++03': 'c++98', 'gnu++03': 'gnu++98' } - args.append('-std=' + remap_cpp03.get(std.value, std)) + args.append('-std=' + remap_cpp03.get(std, std)) if options.get_value(key.evolve('eh')) == 'none': args.append('-fno-exceptions') if not options.get_value(key.evolve('rtti')): diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index dc9cf8addd35..a6049a8439cd 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -552,7 +552,7 @@ def sanity_check(self, work_dir: str, env: 'Environment') -> None: flags += self.get_ccbin_args(env.coredata.optstore) # If cross-compiling, we can't run the sanity check, only compile it. - if env.need_exe_wrapper(self.for_machine) and not env.has_exe_wrapper(): + if self.is_cross and not env.has_exe_wrapper(): # Linking cross built apps is painful. You can't really # tell if you should use -nostdlib or not and for example # on OSX the compiler binary is the same but you need @@ -574,7 +574,7 @@ def sanity_check(self, work_dir: str, env: 'Environment') -> None: raise EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') # Run sanity check (if possible) - if env.need_exe_wrapper(self.for_machine): + if self.is_cross: if not env.has_exe_wrapper(): return else: diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 46cffdd0fc43..c478c040ba9c 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -447,7 +447,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: compile_cmdlist = self.exelist + self.get_output_args(output_name) + self._get_target_arch_args() + [source_name] # If cross-compiling, we can't run the sanity check, only compile it. - if environment.need_exe_wrapper(self.for_machine) and not environment.has_exe_wrapper(): + if self.is_cross and not environment.has_exe_wrapper(): compile_cmdlist += self.get_compile_only_args() pc = subprocess.Popen(compile_cmdlist, cwd=work_dir) @@ -455,7 +455,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: if pc.returncode != 0: raise EnvironmentException('D compiler %s cannot compile programs.' % self.name_string()) - if environment.need_exe_wrapper(self.for_machine): + if self.is_cross: if not environment.has_exe_wrapper(): # Can't check if the binaries run so we have to assume they do return diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 3a678211c5a1..c8b67f479471 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -46,8 +46,8 @@ # There is currently no pgc++ for Windows, only for Mac and Linux. defaults['cpp'] = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl'] defaults['fortran'] = ['ifort', 'gfortran', 'flang', 'pgfortran', 'g95'] - defaults['objc'] = ['clang-cl', 'gcc'] - defaults['objcpp'] = ['clang-cl', 'g++'] + defaults['objc'] = ['clang', 'clang-cl', 'gcc'] + defaults['objcpp'] = ['clang-cl', 'clang-cl', 'g++'] defaults['cs'] = ['csc', 'mcs'] else: if platform.machine().lower() == 'e2k': diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index ad266e9751a9..3e332381d53b 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -256,7 +256,7 @@ def get_module_incdir_args(self) -> T.Tuple[str, ...]: def get_module_outdir_args(self, path: str) -> T.List[str]: return ['-moddir=' + path] - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-xopenmp'] @@ -381,7 +381,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic '3': default_warn_args, 'everything': default_warn_args} - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-mp'] @@ -482,7 +482,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic '3': default_warn_args, 'everything': default_warn_args} - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-mp'] @@ -525,5 +525,5 @@ def get_preprocess_only_args(self) -> T.List[str]: def get_std_exe_link_args(self) -> T.List[str]: return self.get_always_args() - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-openmp'] diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py new file mode 100644 index 000000000000..98c4bfa1a18b --- /dev/null +++ b/mesonbuild/compilers/mixins/apple.py @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + +"""Provides mixins for Apple compilers.""" + +from __future__ import annotations +import typing as T + +from ...mesonlib import MesonException + +if T.TYPE_CHECKING: + from ..._typing import ImmutableListProtocol + from ...environment import Environment + from ..compilers import Compiler +else: + # This is a bit clever, for mypy we pretend that these mixins descend from + # Compiler, so we get all of the methods and attributes defined for us, but + # for runtime we make them descend from object (which all classes normally + # do). This gives up DRYer type checking, with no runtime impact + Compiler = object + + +class AppleCompilerMixin(Compiler): + + """Handle differences between Vanilla Clang and the Clang shipped with XCode.""" + + __BASE_OMP_FLAGS: ImmutableListProtocol[str] = ['-Xpreprocessor', '-fopenmp'] + + def openmp_flags(self, env: Environment) -> T.List[str]: + """Flags required to compile with OpenMP on Apple. + + The Apple Clang Compiler doesn't have builtin support for OpenMP, it + must be provided separately. As such, we need to add the -Xpreprocessor + argument so that an external OpenMP can be found. + + :return: A list of arguments + """ + m = env.machines[self.for_machine] + assert m is not None, 'for mypy' + if m.cpu_family.startswith('x86'): + root = '/usr/local' + else: + root = '/opt/homebrew' + return self.__BASE_OMP_FLAGS + [f'-I{root}/opt/libomp/include'] + + def openmp_link_flags(self, env: Environment) -> T.List[str]: + m = env.machines[self.for_machine] + assert m is not None, 'for mypy' + if m.cpu_family.startswith('x86'): + root = '/usr/local' + else: + root = '/opt/homebrew' + + link = self.find_library('omp', env, [f'{root}/opt/libomp/lib']) + if not link: + raise MesonException("Couldn't find libomp") + return self.__BASE_OMP_FLAGS + link diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index e9e83f28615f..d99dc3abf9f9 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -58,6 +58,8 @@ def __init__(self, defines: T.Optional[T.Dict[str, str]]): # linkers don't have base_options. if isinstance(self.linker, AppleDynamicLinker): self.base_options.add(OptionKey('b_bitcode')) + elif isinstance(self.linker, MSVCDynamicLinker): + self.base_options.add(OptionKey('b_vscrt')) # All Clang backends can also do LLVM IR self.can_compile_suffixes.add('ll') @@ -123,7 +125,7 @@ def has_function(self, funcname: str, prefix: str, env: 'Environment', *, return super().has_function(funcname, prefix, env, extra_args=extra_args, dependencies=dependencies) - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: if mesonlib.version_compare(self.version, '>=3.8.0'): return ['-fopenmp'] elif mesonlib.version_compare(self.version, '>=3.7.0'): diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 70e81a4b85d9..174104b079ee 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -98,12 +98,12 @@ def to_native(self, copy: bool = False) -> T.List[str]: continue # Remove the -isystem and the path if the path is a default path - if (each == '-isystem' and - i < (len(new) - 1) and - self._cached_realpath(new[i + 1]) in real_default_dirs): - bad_idx_list += [i, i + 1] - elif each.startswith('-isystem=') and self._cached_realpath(each[9:]) in real_default_dirs: - bad_idx_list += [i] + if each == '-isystem': + if i < (len(new) - 1) and self._cached_realpath(new[i + 1]) in real_default_dirs: + bad_idx_list += [i, i + 1] + elif each.startswith('-isystem='): + if self._cached_realpath(each[9:]) in real_default_dirs: + bad_idx_list += [i] elif self._cached_realpath(each[8:]) in real_default_dirs: bad_idx_list += [i] for i in reversed(bad_idx_list): @@ -278,7 +278,7 @@ def _sanity_check_impl(self, work_dir: str, environment: 'Environment', mode = CompileCheckMode.LINK if self.is_cross: binname += '_cross' - if environment.need_exe_wrapper(self.for_machine) and not environment.has_exe_wrapper(): + if not environment.has_exe_wrapper(): # Linking cross built C/C++ apps is painful. You can't really # tell if you should use -nostdlib or not and for example # on OSX the compiler binary is the same but you need @@ -308,7 +308,7 @@ def _sanity_check_impl(self, work_dir: str, environment: 'Environment', if pc.returncode != 0: raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') # Run sanity check - if environment.need_exe_wrapper(self.for_machine): + if self.is_cross: if not environment.has_exe_wrapper(): # Can't check if the binaries run so we have to assume they do return @@ -417,7 +417,7 @@ def build_wrapper_args(self, env: 'Environment', else: # TODO: we want to do this in the caller extra_args = mesonlib.listify(extra_args) - extra_args = mesonlib.listify([e(mode.value) if callable(e) else e for e in extra_args]) + extra_args = mesonlib.listify([e(mode) if callable(e) else e for e in extra_args]) if dependencies is None: dependencies = [] @@ -870,11 +870,12 @@ def has_members(self, typename: str, membernames: T.List[str], if extra_args is None: extra_args = [] # Create code that accesses all members - members = ''.join(f'foo.{member};\n' for member in membernames) + members = ''.join(f'(void) ( foo.{member} );\n' for member in membernames) t = f'''{prefix} void bar(void) {{ {typename} foo; {members} + (void) foo; }}''' return self.compiles(t, env, extra_args=extra_args, dependencies=dependencies) @@ -884,7 +885,7 @@ def has_type(self, typename: str, prefix: str, env: 'Environment', dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: t = f'''{prefix} void bar(void) {{ - sizeof({typename}); + (void) sizeof({typename}); }}''' return self.compiles(t, env, extra_args=extra_args, dependencies=dependencies) diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 10df3decb776..71cf722c8192 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2023 Intel Corporation +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations @@ -89,5 +89,5 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] args.append('-std=' + std) return args - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-fopenmp'] diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 587b0cc6d885..4a9eb8848489 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -402,7 +402,7 @@ def get_default_include_dirs(self) -> T.List[str]: return gnulike_default_include_dirs(tuple(self.get_exelist(ccache=False)), self.language).copy() @abc.abstractmethod - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: pass def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]: @@ -585,7 +585,7 @@ def get_optimization_args(self, optimization_level: str) -> T.List[str]: def get_pch_suffix(self) -> str: return 'gch' - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-fopenmp'] def has_arguments(self, args: T.List[str], env: 'Environment', code: str, diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index d38a42ebbf52..902cc748145f 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -19,6 +19,9 @@ from .gnu import GnuLikeCompiler from .visualstudio import VisualStudioLikeCompiler +if T.TYPE_CHECKING: + from ...environment import Environment + # XXX: avoid circular dependencies # TODO: this belongs in a posix compiler class # NOTE: the default Intel optimization is -O2, unlike GNU which defaults to -O0. @@ -78,7 +81,7 @@ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]: def get_pch_name(self, name: str) -> str: return os.path.basename(name) + '.' + self.get_pch_suffix() - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: if mesonlib.version_compare(self.version, '>=15.0.0'): return ['-qopenmp'] else: @@ -154,7 +157,7 @@ def get_toolset_version(self) -> T.Optional[str]: version = int(v1 + v2) return self._calculate_toolset_version(version) - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['/Qopenmp'] def get_debug_args(self, is_debug: bool) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 0d8245a21540..71ad81f38a01 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -51,7 +51,7 @@ def get_pic_args(self) -> T.List[str]: return ['-fPIC'] return [] - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['-mp'] def get_optimization_args(self, optimization_level: str) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 4e2ce099fef4..bdf293bd010f 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -204,10 +204,10 @@ def gen_pch_args(self, header: str, source: str, pchname: str) -> T.Tuple[str, T objname = os.path.splitext(source)[0] + '.obj' return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname] - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return ['/openmp'] - def openmp_link_flags(self) -> T.List[str]: + def openmp_link_flags(self, env: Environment) -> T.List[str]: return [] # FIXME, no idea what these should be. @@ -381,6 +381,8 @@ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool: # As a last resort, try search in a compiled binary return self._symbols_have_underscore_prefix_searchbin(env) + def get_pie_args(self) -> T.List[str]: + return [] class MSVCCompiler(VisualStudioLikeCompiler): @@ -483,3 +485,10 @@ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: return converted else: return dep.get_compile_args() + + def openmp_link_flags(self, env: Environment) -> T.List[str]: + # see https://github.com/mesonbuild/meson/issues/5298 + libs = self.find_library('libomp', env, []) + if libs is None: + raise mesonlib.MesonBugException('Could not find libomp') + return super().openmp_link_flags(env) + libs diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index b130c58ee307..0ac07a8be763 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -86,7 +86,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: if pc.returncode != 0: raise EnvironmentException(f'Rust compiler {self.name_string()} cannot compile programs.') self._native_static_libs(work_dir, source_name) - if environment.need_exe_wrapper(self.for_machine): + if self.is_cross: if not environment.has_exe_wrapper(): # Can't check if the binaries run so we have to assume they do return diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7fb3bca023d8..8c27c3ae1fc9 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -12,7 +12,7 @@ from itertools import chain from pathlib import PurePath from collections import OrderedDict, abc -from dataclasses import dataclass +import dataclasses from .mesonlib import ( MesonBugException, @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.5.0.rc1' +version = '1.5.2' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when @@ -891,7 +891,7 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None: args.cmd_line_options[key] = value delattr(args, name) -@dataclass +@dataclasses.dataclass class OptionsView(abc.Mapping): '''A view on an options dictionary for a given subproject and with overrides. ''' @@ -900,7 +900,7 @@ class OptionsView(abc.Mapping): # python 3.8 or typing_extensions original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, UserOption[Any]]'] subproject: T.Optional[str] = None - overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = None + overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = dataclasses.field(default_factory=dict) def __getitem__(self, key: OptionKey) -> options.UserOption: # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal(). diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 930dbe7f369d..9b218c6432b8 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -1,6 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2013-2018 The Meson development team -# Copyright © 2024 Intel Corporation # This file contains the detection logic for external dependencies. # Custom logic for several other packages are in separate files. @@ -107,9 +106,6 @@ def _process_include_type_kw(cls, kwargs: T.Dict[str, T.Any]) -> str: return kwargs['include_type'] def __init__(self, type_name: DependencyTypeName, kwargs: T.Dict[str, T.Any]) -> None: - # This allows two Dependencies to be compared even after being copied. - # The purpose is to allow the name to be changed, but still have a proper comparison - self.__id = id(self) self.name = f'dep{id(self)}' self.version: T.Optional[str] = None self.language: T.Optional[str] = None # None means C-like @@ -128,14 +124,6 @@ def __init__(self, type_name: DependencyTypeName, kwargs: T.Dict[str, T.Any]) -> self.featurechecks: T.List['FeatureCheckBase'] = [] self.feature_since: T.Optional[T.Tuple[str, str]] = None - def __eq__(self, other: object) -> bool: - if not isinstance(other, Dependency): - return NotImplemented - return self.__id == other.__id - - def __hash__(self) -> int: - return self.__id - def __repr__(self) -> str: return f'<{self.__class__.__name__} {self.name}: {self.is_found}>' diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 72c7cf0a8825..4011c60fb189 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -95,50 +95,47 @@ def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> No # No macro defined for OpenMP, but OpenMP 3.1 is supported. self.version = '3.1' self.is_found = True - self.compile_args = self.link_args = self.clib_compiler.openmp_flags() + self.compile_args = self.link_args = self.clib_compiler.openmp_flags(environment) return if self.clib_compiler.get_id() == 'pgi': # through at least PGI 19.4, there is no macro defined for OpenMP, but OpenMP 3.1 is supported. self.version = '3.1' self.is_found = True - self.compile_args = self.link_args = self.clib_compiler.openmp_flags() + self.compile_args = self.link_args = self.clib_compiler.openmp_flags(environment) + return + + # Set these now so they're available for the following compiler checks + try: + self.compile_args.extend(self.clib_compiler.openmp_flags(environment)) + self.link_args.extend(self.clib_compiler.openmp_link_flags(environment)) + except mesonlib.MesonException as e: + mlog.warning('OpenMP support not available because:', str(e), fatal=False) return try: openmp_date = self.clib_compiler.get_define( - '_OPENMP', '', self.env, self.clib_compiler.openmp_flags(), [self], disable_cache=True)[0] + '_OPENMP', '', self.env, [], [self], disable_cache=True)[0] except mesonlib.EnvironmentException as e: mlog.debug('OpenMP support not available in the compiler') mlog.debug(e) - openmp_date = None - - if openmp_date: - try: - self.version = self.VERSIONS[openmp_date] - except KeyError: - mlog.debug(f'Could not find an OpenMP version matching {openmp_date}') - if openmp_date == '_OPENMP': - mlog.debug('This can be caused by flags such as gcc\'s `-fdirectives-only`, which affect preprocessor behavior.') - return + return - if self.clib_compiler.get_id() == 'clang-cl': - # this is necessary for clang-cl, see https://github.com/mesonbuild/meson/issues/5298 - clangcl_openmp_link_args = self.clib_compiler.find_library("libomp", self.env, []) - if not clangcl_openmp_link_args: - mlog.log(mlog.yellow('WARNING:'), 'OpenMP found but libomp for clang-cl missing.') - return - self.link_args.extend(clangcl_openmp_link_args) - - # Flang has omp_lib.h - header_names = ('omp.h', 'omp_lib.h') - for name in header_names: - if self.clib_compiler.has_header(name, '', self.env, dependencies=[self], disable_cache=True)[0]: - self.is_found = True - self.compile_args.extend(self.clib_compiler.openmp_flags()) - self.link_args.extend(self.clib_compiler.openmp_link_flags()) - break - if not self.is_found: - mlog.log(mlog.yellow('WARNING:'), 'OpenMP found but omp.h missing.') + try: + self.version = self.VERSIONS[openmp_date] + except KeyError: + mlog.debug(f'Could not find an OpenMP version matching {openmp_date}') + if openmp_date == '_OPENMP': + mlog.debug('This can be caused by flags such as gcc\'s `-fdirectives-only`, which affect preprocessor behavior.') + return + + # Flang has omp_lib.h + header_names = ('omp.h', 'omp_lib.h') + for name in header_names: + if self.clib_compiler.has_header(name, '', self.env, dependencies=[self], disable_cache=True)[0]: + self.is_found = True + break + else: + mlog.warning('OpenMP found but omp.h missing.', fatal=False) packages['openmp'] = OpenMPDependency diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index fb4b9969b126..46d12f309855 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -224,6 +224,8 @@ def get_windows_link_args(self, limited_api: bool) -> T.Optional[T.List[str]]: if self.static: libpath = Path('libs') / f'libpython{vernum}.a' else: + if limited_api: + vernum = vernum[0] comp = self.get_compiler() if comp.id == "gcc": if imp_lower == 'pypy' and verdot == '3.8': @@ -234,8 +236,6 @@ def get_windows_link_args(self, limited_api: bool) -> T.Optional[T.List[str]]: else: libpath = Path(f'python{vernum}.dll') else: - if limited_api: - vernum = vernum[0] if self.is_freethreaded: libpath = Path('libs') / f'python{vernum}t.lib' else: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index be40dbcfd4be..484ef45d478d 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -148,6 +148,9 @@ def find_coverage_tools(coredata: coredata.CoreData) -> T.Tuple[T.Optional[str], gcovr_exe, gcovr_version = detect_gcovr() llvm_cov_exe = detect_llvm_cov(compute_llvm_suffix(coredata)) + # Some platforms may provide versioned clang but only non-versioned llvm utils + if llvm_cov_exe is None: + llvm_cov_exe = detect_llvm_cov('') lcov_exe, lcov_version, genhtml_exe = detect_lcov_genhtml() @@ -192,6 +195,8 @@ def get_llvm_tool_names(tool: str) -> T.List[str]: # unless it becomes a stable release. suffixes = [ '', # base (no suffix) + '-19.1', '19.1', + '-19', '19', '-18.1', '18.1', '-18', '18', '-17', '17', @@ -213,7 +218,7 @@ def get_llvm_tool_names(tool: str) -> T.List[str]: '-3.7', '37', '-3.6', '36', '-3.5', '35', - '-19', # Debian development snapshot + '-20', # Debian development snapshot '-devel', # FreeBSD development snapshot ] names: T.List[str] = [] @@ -509,11 +514,11 @@ def machine_info_can_run(machine_info: MachineInfo): if machine_info.system != detect_system(): return False true_build_cpu_family = detect_cpu_family({}) + assert machine_info.cpu_family is not None, 'called on incomplete machine_info' return \ (machine_info.cpu_family == true_build_cpu_family) or \ ((true_build_cpu_family == 'x86_64') and (machine_info.cpu_family == 'x86')) or \ - ((true_build_cpu_family == 'mips64') and (machine_info.cpu_family == 'mips')) or \ - ((true_build_cpu_family == 'aarch64') and (machine_info.cpu_family == 'arm')) + ((true_build_cpu_family == 'mips64') and (machine_info.cpu_family == 'mips')) class Environment: private_dir = 'meson-private' diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 32f05bafb852..79e205d31d0f 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -12,7 +12,7 @@ from .. import build from .. import mlog -from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule +from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule, NewExtensionModule from ..backend.backends import TestProtocol from ..interpreterbase import ( ContainerTypeInfo, KwargInfo, MesonOperator, @@ -284,7 +284,6 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu def __init__(self, obj: mesonlib.EnvironmentVariables, interpreter: 'Interpreter'): super().__init__(obj, interpreter) - MutableInterpreterObject.__init__(self) self.methods.update({'set': self.set_method, 'unset': self.unset_method, 'append': self.append_method, @@ -309,14 +308,12 @@ def warn_if_has_name(self, name: str) -> None: @typed_kwargs('environment.set', ENV_SEPARATOR_KW) def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args - self.check_used(self.interpreter, fatal=False) self.held_object.set(name, values, kwargs['separator']) @FeatureNew('environment.unset', '1.4.0') @typed_pos_args('environment.unset', str) @noKwargs def unset_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> None: - self.check_used(self.interpreter, fatal=False) self.held_object.unset(args[0]) @typed_pos_args('environment.append', str, varargs=str, min_varargs=1) @@ -324,7 +321,6 @@ def unset_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> None: def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args self.warn_if_has_name(name) - self.check_used(self.interpreter, fatal=False) self.held_object.append(name, values, kwargs['separator']) @typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1) @@ -332,7 +328,6 @@ def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSep def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args self.warn_if_has_name(name) - self.check_used(self.interpreter, fatal=False) self.held_object.prepend(name, values, kwargs['separator']) @@ -343,7 +338,6 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte def __init__(self, obj: build.ConfigurationData, interpreter: 'Interpreter'): super().__init__(obj, interpreter) - MutableInterpreterObject.__init__(self) self.methods.update({'set': self.set_method, 'set10': self.set10_method, 'set_quoted': self.set_quoted_method, @@ -355,31 +349,32 @@ def __init__(self, obj: build.ConfigurationData, interpreter: 'Interpreter'): }) def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder': - obj = ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter) + return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter) + + def is_used(self) -> bool: + return self.held_object.used + + def __check_used(self) -> None: if self.is_used(): - # Copies of used ConfigurationData used to be immutable. It is now - # allowed but we need this flag to print a FeatureNew warning if - # that happens. - obj.mutable_feature_new = True - return obj + raise InterpreterException("Can not set values on configuration object that has been used.") @typed_pos_args('configuration_data.set', str, (str, int, bool)) @typed_kwargs('configuration_data.set', _CONF_DATA_SET_KWS) def set_method(self, args: T.Tuple[str, T.Union[str, int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None: - self.check_used(self.interpreter) + self.__check_used() self.held_object.values[args[0]] = (args[1], kwargs['description']) @typed_pos_args('configuration_data.set_quoted', str, str) @typed_kwargs('configuration_data.set_quoted', _CONF_DATA_SET_KWS) def set_quoted_method(self, args: T.Tuple[str, str], kwargs: 'kwargs.ConfigurationDataSet') -> None: - self.check_used(self.interpreter) + self.__check_used() escaped_val = '\\"'.join(args[1].split('"')) self.held_object.values[args[0]] = (f'"{escaped_val}"', kwargs['description']) @typed_pos_args('configuration_data.set10', str, (int, bool)) @typed_kwargs('configuration_data.set10', _CONF_DATA_SET_KWS) def set10_method(self, args: T.Tuple[str, T.Union[int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None: - self.check_used(self.interpreter) + self.__check_used() # bool is a subclass of int, so we need to check for bool explicitly. # We already have typed_pos_args checking that this is either a bool or # an int. @@ -442,7 +437,6 @@ def keys(self) -> T.List[str]: @noKwargs def merge_from_method(self, args: T.Tuple[build.ConfigurationData], kwargs: TYPE_kwargs) -> None: from_object = args[0] - self.check_used(self.interpreter) self.held_object.values.update(from_object.values) @@ -850,6 +844,10 @@ def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwa args = flatten(args) if not getattr(method, 'no-second-level-holder-flattening', False): args, kwargs = resolve_second_level_holders(args, kwargs) + if not self.interpreter.active_projectname: + assert isinstance(modobj, (ExtensionModule, NewExtensionModule)), 'for mypy' + full_method_name = f'{modobj.INFO.name}.{method_name}' + raise mesonlib.MesonException(f'Module methods ({full_method_name}) cannot be invoked during project declaration.') state = ModuleState(self.interpreter) # Many modules do for example self.interpreter.find_program_impl(), # so we have to ensure they use the current interpreter and not the one diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index fb18fa8b6806..4d1f427da210 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -1,9 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2021 The Meson development team -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021 Intel Corporation from __future__ import annotations -import copy import os import typing as T @@ -348,16 +347,6 @@ def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency] if not name: raise InterpreterException('First argument must be a string and cannot be empty') - # Make a copy since we're going to mutate. - # - # dep = declare_dependency() - # meson.override_dependency('foo', dep) - # meson.override_dependency('foo-1.0', dep) - # dep = dependency('foo') - # dep.name() # == 'foo-1.0' - dep = copy.copy(dep) - dep.name = name - optkey = OptionKey('default_library', subproject=self.interpreter.subproject) default_library = self.interpreter.coredata.get_option(optkey) assert isinstance(default_library, str), 'for mypy' diff --git a/mesonbuild/interpreterbase/_unholder.py b/mesonbuild/interpreterbase/_unholder.py index e32b77fc7d4b..c62aafe8e97b 100644 --- a/mesonbuild/interpreterbase/_unholder.py +++ b/mesonbuild/interpreterbase/_unholder.py @@ -5,7 +5,7 @@ import typing as T -from .baseobjects import InterpreterObject, MesonInterpreterObject, ObjectHolder, HoldableTypes, MutableInterpreterObject +from .baseobjects import InterpreterObject, MesonInterpreterObject, ObjectHolder, HoldableTypes from .exceptions import InvalidArguments from ..mesonlib import HoldableObject, MesonBugException @@ -13,8 +13,6 @@ from .baseobjects import TYPE_var def _unholder(obj: InterpreterObject) -> TYPE_var: - if isinstance(obj, MutableInterpreterObject): - obj.mark_used() if isinstance(obj, ObjectHolder): assert isinstance(obj.held_object, HoldableTypes) return obj.held_object diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index c6864a4788c5..9a119a98a75d 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -120,27 +120,6 @@ class MesonInterpreterObject(InterpreterObject): class MutableInterpreterObject: ''' Dummy class to mark the object type as mutable ''' - def __init__(self) -> None: - self.used = False - self.mutable_feature_new = False - - def mark_used(self) -> None: - self.used = True - - def is_used(self) -> bool: - return self.used - - def check_used(self, interpreter: Interpreter, fatal: bool = True) -> None: - from .decorators import FeatureDeprecated, FeatureNew - if self.is_used(): - if fatal: - raise InvalidArguments('Can not modify object after it has been used.') - FeatureDeprecated.single_use('Modify object after it has been used', '1.5.0', - interpreter.subproject, location=interpreter.current_node) - elif self.mutable_feature_new: - FeatureNew.single_use('Modify a copy of an immutable object', '1.5.0', - interpreter.subproject, location=interpreter.current_node) - self.mutable_feature_new = False HoldableTypes = (HoldableObject, int, bool, str, list, dict) TYPE_HoldableTypes = T.Union[TYPE_elementary, HoldableObject] diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index 1db3948c1c59..cd3c7b2efd0f 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -64,7 +64,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty p, o, _ = Popen_safe(compiler + check_args) if 'LLD' in o.split('\n', maxsplit=1)[0]: - if '(compatible with GNU linkers)' in o: + if 'compatible with GNU linkers' in o: return linkers.LLVMDynamicLinker( compiler, for_machine, comp_class.LINKER_PREFIX, override, version=search_version(o)) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 7507f5ab7b6c..4eec82edd177 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -62,7 +62,7 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def thread_link_flags(self, env: 'Environment') -> T.List[str]: return [] - def openmp_flags(self) -> T.List[str]: + def openmp_flags(self, env: Environment) -> T.List[str]: return [] def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -819,23 +819,27 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, if not rpath_paths and not install_rpath and not build_rpath: return ([], set()) args: T.List[str] = [] + rpath_dirs_to_remove: T.Set[bytes] = set() # @loader_path is the equivalent of $ORIGIN on macOS # https://stackoverflow.com/q/26280738 origin_placeholder = '@loader_path' processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) if build_rpath != '': - all_paths.add(build_rpath) + all_paths.update(build_rpath.split(':')) for rp in all_paths: + rpath_dirs_to_remove.add(rp.encode('utf8')) args.extend(self._apply_prefix('-rpath,' + rp)) - return (args, set()) + return (args, rpath_dirs_to_remove) def get_thinlto_cache_args(self, path: str) -> T.List[str]: return ["-Wl,-cache_path_lto," + path] def export_dynamic_args(self, env: 'Environment') -> T.List[str]: - return self._apply_prefix('-export_dynamic') + if mesonlib.version_compare(self.version, '>=224.1'): + return self._apply_prefix('-export_dynamic') + return [] class LLVMLD64DynamicLinker(AppleDynamicLinker): @@ -1322,6 +1326,9 @@ def import_library_args(self, implibname: str) -> T.List[str]: def rsp_file_syntax(self) -> RSPFileSyntax: return RSPFileSyntax.MSVC + def get_pie_args(self) -> T.List[str]: + return [] + class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index e4606306b6d2..d569f6015739 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -22,7 +22,7 @@ from glob import glob from pathlib import Path from mesonbuild.environment import Environment, detect_ninja -from mesonbuild.mesonlib import (MesonException, RealPathAction, get_meson_command, quiet_git, +from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, get_meson_command, quiet_git, windows_proof_rmtree, setup_vsenv, OptionKey) from mesonbuild.msetup import add_arguments as msetup_argparse from mesonbuild.wrap import wrap @@ -79,7 +79,36 @@ def is_git(src_root: str) -> bool: Checks if meson.build file at the root source directory is tracked by git. It could be a subproject part of the parent project git repository. ''' - return quiet_git(['ls-files', '--error-unmatch', 'meson.build'], src_root)[0] + if quiet_git(['ls-files', '--error-unmatch', 'meson.build'], src_root)[0]: + return True + + if os.path.exists(os.path.join(src_root, '.git')): + msg = 'Source tree looks like it may be a git repo, ' + if not GIT: + msg += 'but git is not installed!' + if 'GITLAB_CI' in os.environ: + msg += ' This is a gitlab bug.' + else: + msg += 'but git returned a failure. ' + p, oe = quiet_git(['status'], src_root) + if 'dubious ownership' in oe: + # For a few years now, git has absolved itself of the responsibility to implement + # robust, safe software. Instead of detecting the signs of a problematic scenario, + # they have chosen to consider many legitimate and reasonable use cases as "dangerous", + # and implemented the number one threat to security worldwide: alert fatigue. Having + # done so, they then washed their hands of the matter and permanently tabled the + # notion of adding fine-grained detection. This is not just useless, it is *worse* + # than useless. + # + # In our case, the error is triply meaningless since we are already executing build + # system commands from the same directory. Either way, reject the notion that git is + # well designed or that its error messaging is a valid approach to the problem space. + msg += 'This is a bug in git itself, please set `git config --global safe.directory "*"`' + else: + msg += 'meson.build may not have been committed to git?' + mlog.warning(msg) + return False + def is_hg(src_root: str) -> bool: return os.path.isdir(os.path.join(src_root, '.hg')) @@ -140,7 +169,9 @@ def git_root(self, dir_: str) -> Path: def have_dirty_index(self) -> bool: '''Check whether there are uncommitted changes in git''' - subprocess.check_call(['git', '-C', self.src_root, 'update-index', '-q', '--refresh']) + # Optimistically call update-index, and disregard its return value. It could be read-only, + # and only the output of diff-index matters. + subprocess.call(['git', '-C', self.src_root, 'update-index', '-q', '--refresh']) ret = subprocess.call(['git', '-C', self.src_root, 'diff-index', '--quiet', 'HEAD']) return ret == 1 @@ -220,7 +251,12 @@ def create_dist(self, archives: T.List[str]) -> T.List[str]: class HgDist(Dist): def have_dirty_index(self) -> bool: '''Check whether there are uncommitted changes in hg''' - out = subprocess.check_output(['hg', '-R', self.src_root, 'summary']) + env = os.environ.copy() + env['LC_ALL'] = 'C' + # cpython's gettext has a bug and uses LANGUAGE to override LC_ALL, + # contrary to the gettext spec + env.pop('LANGUAGE', None) + out = subprocess.check_output(['hg', '-R', self.src_root, 'summary'], env=env) return b'commit: (clean)' not in out def create_dist(self, archives: T.List[str]) -> T.List[str]: diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index c486dd846cb9..faa0f426d82a 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -27,6 +27,9 @@ def errorhandler(e: Exception, command: str) -> int: logfile = mlog.shutdown() if logfile is not None: mlog.log("\nA full log can be found at", mlog.bold(logfile)) + contents = mlog.ci_fold_file(logfile, f'CI platform detected, click here for {os.path.basename(logfile)} contents.') + if contents: + print(contents) if os.environ.get('MESON_FORCE_BACKTRACE'): raise e return 1 diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index e20e45851820..7f3dbf01400e 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -276,6 +276,7 @@ def exit_node(self, node: mparser.BaseNode) -> None: def move_whitespaces(self, from_node: mparser.BaseNode, to_node: mparser.BaseNode) -> None: to_node.whitespaces.value = from_node.whitespaces.value + to_node.whitespaces.value + to_node.whitespaces.is_continuation = from_node.whitespaces.is_continuation from_node.whitespaces = None to_node.whitespaces.accept(self) @@ -317,7 +318,10 @@ def visit_WhitespaceNode(self, node: mparser.WhitespaceNode) -> None: for i, line in enumerate(lines): has_nl = line.endswith('\n') line = line.strip() - if line.startswith('#'): + if line.startswith('\\'): + node.value += ' ' # add space before \ + node.is_continuation = True + elif line.startswith('#'): if not in_block_comments: node.value += self.config.indent_before_comments else: @@ -328,6 +332,8 @@ def visit_WhitespaceNode(self, node: mparser.WhitespaceNode) -> None: in_block_comments = True if node.value.endswith('\n'): node.value += self.indent_comments + if node.is_continuation: + node.value += self.config.indent_by def visit_SymbolNode(self, node: mparser.SymbolNode) -> None: super().visit_SymbolNode(node) @@ -338,7 +344,7 @@ def visit_StringNode(self, node: mparser.StringNode) -> None: self.enter_node(node) if self.config.simplify_string_literals: - if node.is_multiline and '\n' not in node.value: + if node.is_multiline and not any(x in node.value for x in ['\n', "'"]): node.is_multiline = False node.value = node.escape() @@ -372,6 +378,8 @@ def visit_ArrayNode(self, node: mparser.ArrayNode) -> None: if node.args.arguments and not node.args.is_multiline and self.config.space_array: self.add_space_after(node.lbracket) self.add_space_after(node.args) + if not node.args.arguments: + self.move_whitespaces(node.lbracket, node.args) def visit_DictNode(self, node: mparser.DictNode) -> None: super().visit_DictNode(node) @@ -388,6 +396,7 @@ def visit_CodeBlockNode(self, node: mparser.CodeBlockNode) -> None: self.in_block_comments = False else: node.pre_whitespaces = mparser.WhitespaceNode(mparser.Token('whitespace', node.filename, 0, 0, 0, (0, 0), '')) + node.pre_whitespaces.block_indent = True for i in node.lines: i.accept(self) @@ -396,7 +405,11 @@ def visit_CodeBlockNode(self, node: mparser.CodeBlockNode) -> None: if node.lines: self.move_whitespaces(node.lines[-1], node) else: + node.whitespaces.value = node.pre_whitespaces.value + node.whitespaces.value + node.pre_whitespaces.value = '' + self.in_block_comments = True node.whitespaces.accept(self) + self.in_block_comments = False if node.condition_level == 0 and self.config.insert_final_newline: self.add_nl_after(node, force=True) @@ -451,6 +464,7 @@ def visit_ForeachClauseNode(self, node: mparser.ForeachClauseNode) -> None: self.add_space_after(node.colon) node.block.whitespaces.value += node.condition_level * self.config.indent_by + node.block.whitespaces.block_indent = True self.move_whitespaces(node.endforeach, node) @@ -466,11 +480,19 @@ def visit_IfClauseNode(self, node: mparser.IfClauseNode) -> None: def visit_IfNode(self, node: mparser.IfNode) -> None: super().visit_IfNode(node) self.add_space_after(node.if_) + self.in_block_comments = True self.move_whitespaces(node.block, node) + self.in_block_comments = False + node.whitespaces.condition_level = node.condition_level + 1 + node.whitespaces.block_indent = True def visit_ElseNode(self, node: mparser.ElseNode) -> None: super().visit_ElseNode(node) + self.in_block_comments = True self.move_whitespaces(node.block, node) + self.in_block_comments = False + node.whitespaces.condition_level = node.condition_level + 1 + node.whitespaces.block_indent = True def visit_TernaryNode(self, node: mparser.TernaryNode) -> None: super().visit_TernaryNode(node) @@ -552,27 +574,36 @@ def visit_ArrayNode(self, node: mparser.ArrayNode) -> None: self.enter_node(node) if node.args.is_multiline: self.level += 1 - self.add_nl_after(node.lbracket, indent=self.level) + if node.args.arguments: + self.add_nl_after(node.lbracket, indent=self.level) + node.lbracket.accept(self) self.is_function_arguments = False node.args.accept(self) if node.args.is_multiline: self.level -= 1 + node.rbracket.accept(self) self.exit_node(node) def visit_DictNode(self, node: mparser.DictNode) -> None: self.enter_node(node) if node.args.is_multiline: self.level += 1 - self.add_nl_after(node.lcurl, indent=self.level) + if node.args.kwargs: + self.add_nl_after(node.lcurl, indent=self.level) + node.lcurl.accept(self) self.is_function_arguments = False node.args.accept(self) if node.args.is_multiline: self.level -= 1 + node.rcurl.accept(self) self.exit_node(node) def visit_MethodNode(self, node: mparser.MethodNode) -> None: self.enter_node(node) node.source_object.accept(self) + is_cont = node.source_object.whitespaces and node.source_object.whitespaces.is_continuation + if is_cont: + self.level += 1 if node.args.is_multiline: self.level += 1 self.add_nl_after(node.lpar, indent=self.level) @@ -580,6 +611,8 @@ def visit_MethodNode(self, node: mparser.MethodNode) -> None: node.args.accept(self) if node.args.is_multiline: self.level -= 1 + if is_cont: + self.level -= 1 self.exit_node(node) def visit_FunctionNode(self, node: mparser.FunctionNode) -> None: @@ -597,8 +630,8 @@ def visit_WhitespaceNode(self, node: mparser.WhitespaceNode) -> None: lines = node.value.splitlines(keepends=True) if lines: indent = (node.condition_level + self.level) * self.config.indent_by - node.value = lines[0] - for line in lines[1:]: + node.value = '' if node.block_indent else lines.pop(0) + for line in lines: if '#' in line and not line.startswith(indent): node.value += indent node.value += line @@ -648,7 +681,8 @@ def visit_ArgumentNode(self, node: mparser.ArgumentNode) -> None: for comma in node.commas[arg_index:-1]: self.add_nl_after(comma, self.level) - self.add_nl_after(node, self.level - 1) + if node.arguments or node.kwargs: + self.add_nl_after(node, self.level - 1) else: if has_trailing_comma and not (node.commas[-1].whitespaces and node.commas[-1].whitespaces.value): @@ -744,11 +778,10 @@ def visit_BreakNode(self, node: mparser.BreakNode) -> None: self.exit_node(node) def split_if_needed(self, node: mparser.ArgumentNode) -> None: - if not node.is_multiline and self.length > self.config.max_line_length: + if len(node) and not node.is_multiline and self.length > self.config.max_line_length: arg = self.argument_stack[self.level] if len(self.argument_stack) > self.level else node - if not arg.is_multiline: - arg.is_multiline = True - self.need_regenerate = True + arg.is_multiline = True + self.need_regenerate = True def visit_ArgumentNode(self, node: mparser.ArgumentNode) -> None: self.argument_stack.append(node) @@ -817,11 +850,15 @@ def load_editor_config(self, source_file: Path) -> EditorConfig: getter = f.metadata['getter'] for section in sections: - value = getter(cp, section, f.name, fallback=None) + try: + value = getter(cp, section, f.name, fallback=None) + except ValueError as e: + raise MesonException(f'Invalid type for key "{f.name}" in "{editorconfig_file}" file:\n{e}') from e if value is not None: setattr(config, f.name, value) - if cp.getboolean(cp.default_section, 'root'): + # Root is not required except in the top level .editorconfig. + if cp.getboolean(cp.default_section, 'root', fallback=False): break return config @@ -835,9 +872,17 @@ def load_configuration(self, configuration_file: T.Optional[Path]) -> FormatterC except ParsingError as e: raise MesonException(f'Unable to parse configuration file "{configuration_file}":\n{e}') from e + extra_keys = sorted(set(cp.defaults()).difference(f.name for f in fields(config))) + if extra_keys: + raise MesonException(f'Unknown config keys: "{", ".join(extra_keys)}" in configuration file "{configuration_file}"') + for f in fields(config): getter = f.metadata['getter'] - value = getter(cp, cp.default_section, f.name, fallback=None) + try: + value = getter(cp, cp.default_section, f.name, fallback=None) + except ValueError as e: + raise MesonException( + f'Error parsing "{str(configuration_file)}", option "{f.name}", error: "{e!s}"') if value is not None: setattr(config, f.name, value) @@ -965,9 +1010,8 @@ def run(options: argparse.Namespace) -> int: # TODO: remove empty newlines when more than N (2...) # TODO: magic comment to prevent formatting -# TODO: handle meson.options ? # TODO: split long lines on binary operators -# TODO: align series of assignements +# TODO: align series of assignments # TODO: align comments # TODO: move comments on long lines diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index a8b0185371d1..bc8faeba7d06 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -22,8 +22,9 @@ from pathlib import Path if T.TYPE_CHECKING: - from ._typing import StringProtocol, SizedStringProtocol + from typing_extensions import Literal + from ._typing import StringProtocol, SizedStringProtocol from .mparser import BaseNode TV_Loggable = T.Union[str, 'AnsiDecorator', StringProtocol] @@ -75,6 +76,7 @@ def setup_console() -> None: pass _in_ci = 'CI' in os.environ +_ci_is_github = 'GITHUB_ACTIONS' in os.environ class _Severity(enum.Enum): @@ -540,3 +542,30 @@ def code_line(text: str, line: str, colno: int) -> str: :return: A formatted string of the text, line, and a caret """ return f'{text}\n{line}\n{" " * colno}^' + +@T.overload +def ci_fold_file(fname: T.Union[str, os.PathLike], banner: str, force: Literal[True] = True) -> str: ... + +@T.overload +def ci_fold_file(fname: T.Union[str, os.PathLike], banner: str, force: Literal[False] = False) -> T.Optional[str]: ... + +def ci_fold_file(fname: T.Union[str, os.PathLike], banner: str, force: bool = False) -> T.Optional[str]: + if not _in_ci and not force: + return None + + if _ci_is_github: + header = f'::group::==== {banner} ====' + footer = '::endgroup::' + elif force: + header = banner + footer = '' + elif 'MESON_FORCE_SHOW_LOGS' in os.environ: + header = f'==== Forcing display of logs for {os.path.basename(fname)} ====' + footer = '' + else: + # only github is implemented + return None + + with open(fname, 'r', encoding='utf-8') as f: + data = f.read() + return f'{header}\n{data}\n{footer}\n' diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index cab7c76a3d86..9d872353ae02 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -478,8 +478,11 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri else: raise MesonException('Compiling GResources into code is only supported in C and C++ projects') - if kwargs['install'] and not gresource: - raise MesonException('The install kwarg only applies to gresource bundles, see install_header') + if kwargs['install']: + if not gresource: + raise MesonException('The install kwarg only applies to gresource bundles, see install_header') + elif not kwargs['install_dir']: + raise MesonException('gnome.compile_resources: "install_dir" keyword argument must be set when "install" is true.') install_header = kwargs['install_header'] if install_header and gresource: diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index ec08ccfb2583..4e359b3b9f20 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -266,6 +266,8 @@ def __init__(self, token: Token[str]): super().__init__(token.lineno, token.colno, token.filename) self.value = '' self.append(token) + self.block_indent = False + self.is_continuation = False def append(self, token: Token[str]) -> None: self.value += token.value @@ -377,7 +379,7 @@ def incorrect_order(self) -> bool: return self.order_error def __len__(self) -> int: - return self.num_args() # Fixme + return self.num_args() + self.num_kwargs() @dataclass(unsafe_hash=True) class ArrayNode(BaseNode): diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 71599f784e73..db9c97d98c6a 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -379,11 +379,14 @@ def fix_elf(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Optiona # note: e.get_rpath() and e.get_runpath() may be useful e.fix_rpath(fname, rpath_dirs_to_remove, new_rpath) -def get_darwin_rpaths(fname: str) -> T.List[str]: +def get_darwin_rpaths(fname: str) -> OrderedSet[str]: p, out, _ = Popen_safe(['otool', '-l', fname], stderr=subprocess.DEVNULL) if p.returncode != 0: raise subprocess.CalledProcessError(p.returncode, p.args, out) - result = [] + # Need to deduplicate rpaths, as macOS's install_name_tool + # is *very* allergic to duplicate -delete_rpath arguments + # when calling depfixer on installation. + result: OrderedSet[str] = OrderedSet() current_cmd = 'FOOBAR' for line in out.split('\n'): line = line.strip() @@ -394,7 +397,7 @@ def get_darwin_rpaths(fname: str) -> T.List[str]: current_cmd = value if key == 'path' and current_cmd == 'LC_RPATH': rp = value.split('(', 1)[0].strip() - result.append(rp) + result.add(rp) return result def fix_darwin(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: str, final_path: str, install_name_mappings: T.Dict[str, str]) -> None: @@ -406,7 +409,7 @@ def fix_darwin(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: str, f return new_rpaths: OrderedSet[str] = OrderedSet() if new_rpath: - new_rpaths.update(new_rpath) + new_rpaths.update(new_rpath.split(':')) # filter out build-only rpath entries, like in # fix_rpathtype_entry remove_rpaths = [x.decode('utf8') for x in rpath_dirs_to_remove] diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 4e98c600cf87..3fe40ed9f322 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -76,9 +76,9 @@ def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool if has_ssl: try: return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(url), timeout=REQ_TIMEOUT)) - except urllib.error.URLError as excp: + except OSError as excp: msg = f'WrapDB connection failed to {urlstring} with error {excp}.' - if isinstance(excp.reason, ssl.SSLCertVerificationError): + if isinstance(excp, urllib.error.URLError) and isinstance(excp.reason, ssl.SSLCertVerificationError): if allow_insecure: mlog.warning(f'{msg}\n\n Proceeding without authentication.') else: @@ -95,7 +95,7 @@ def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool nossl_url = url._replace(scheme='http') try: return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(nossl_url), timeout=REQ_TIMEOUT)) - except urllib.error.URLError as excp: + except OSError as excp: raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}') def get_releases_data(allow_insecure: bool) -> bytes: @@ -704,7 +704,7 @@ def get_data(self, urlstring: str) -> T.Tuple[str, str]: try: req = urllib.request.Request(urlstring, headers=headers) resp = urllib.request.urlopen(req, timeout=REQ_TIMEOUT) - except urllib.error.URLError as e: + except OSError as e: mlog.log(str(e)) raise WrapException(f'could not get {urlstring} is the internet available?') with contextlib.closing(resp) as resp, tmpfile as tmpfile: diff --git a/run_project_tests.py b/run_project_tests.py index 974273fc790e..c11410486e23 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -305,7 +305,6 @@ def __lt__(self, other: object) -> bool: failing_logs: T.List[str] = [] print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ under_ci = 'CI' in os.environ -ci_is_github = 'GITHUB_ACTIONS' in os.environ raw_ci_jobname = os.environ.get('MESON_CI_JOBNAME', None) ci_jobname = raw_ci_jobname if raw_ci_jobname != 'thirdparty' else None do_debug = under_ci or print_debug @@ -437,16 +436,12 @@ def log_text_file(logfile: T.TextIO, testdir: Path, result: TestResult) -> None: def _run_ci_include(args: T.List[str]) -> str: - header = f'Included file {args[0]}:' - footer = '' - if ci_is_github: - header = f'::group::==== {header} ====' - footer = '::endgroup::' if not args: return 'At least one parameter required' + + header = f'Included file {args[0]}:' try: - data = Path(args[0]).read_text(errors='ignore', encoding='utf-8') - return f'{header}\n{data}\n{footer}\n' + return mlog.ci_fold_file(args[0], header, force=True) except Exception: return 'Failed to open {}\n'.format(args[0]) diff --git a/test cases/cmake/24 mixing languages/meson.build b/test cases/cmake/24 mixing languages/meson.build index 55d7a7d68bc9..a4662fe37c0c 100644 --- a/test cases/cmake/24 mixing languages/meson.build +++ b/test cases/cmake/24 mixing languages/meson.build @@ -7,6 +7,12 @@ if not add_languages('objc', required : false) error('MESON_SKIP_TEST: No ObjC compiler') endif +objc = meson.get_compiler('objc') +c = meson.get_compiler('c') +if c.get_argument_syntax() != objc.get_argument_syntax() + error('MESON_SKIP_TEST: cmake cannot mix compiler types on Windows') +endif + cm = import('cmake') sub_pro = cm.subproject('cmTest') diff --git a/test cases/cmake/24 mixing languages/subprojects/cmTest/CMakeLists.txt b/test cases/cmake/24 mixing languages/subprojects/cmTest/CMakeLists.txt index 80a256f0db1d..a1886115bf5f 100644 --- a/test cases/cmake/24 mixing languages/subprojects/cmTest/CMakeLists.txt +++ b/test cases/cmake/24 mixing languages/subprojects/cmTest/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(cmTest) +project(cmTest LANGUAGES C OBJC) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/test cases/common/113 interpreter copy mutable var on assignment/check_env.py b/test cases/common/113 interpreter copy mutable var on assignment/check_env.py deleted file mode 100755 index 034d2924331b..000000000000 --- a/test cases/common/113 interpreter copy mutable var on assignment/check_env.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -if sys.argv[1] not in os.environ: - exit(42) diff --git a/test cases/common/113 interpreter copy mutable var on assignment/meson.build b/test cases/common/113 interpreter copy mutable var on assignment/meson.build index 3d4f3b039c76..d414bfc74e0c 100644 --- a/test cases/common/113 interpreter copy mutable var on assignment/meson.build +++ b/test cases/common/113 interpreter copy mutable var on assignment/meson.build @@ -1,4 +1,4 @@ -project('foo', meson_version: '>=1.5') +project('foo') a = configuration_data() a.set('HELLO', 1) @@ -10,15 +10,6 @@ assert(b.has('HELLO'), 'Original config data should be set on copy') configure_file(output : 'b.h', configuration : b) -testcase expect_error('Can not modify object after it has been used.') - b.set('WORLD', 1) -endtestcase - -# A copy of immutable object is mutable. This should print FeatureNew warning -# if meson_version is lower than 1.5. -c = b -c.set('WORLD', 1) - # This should still work, as we didn't use the original above but a copy! a.set('WORLD', 1) @@ -26,26 +17,3 @@ assert(a.has('WORLD'), 'New config data should have been set') assert(not b.has('WORLD'), 'New config data set should not affect var copied earlier') configure_file(output : 'a.h', configuration : a) - -env1 = environment() -env1.set('FOO', '1') -env2 = env1 -env1.set('BAR', '1') - -# FOO should be in env1 and env2 -run_command('check_env.py', 'FOO', env: env1, check: true) -run_command('check_env.py', 'FOO', env: env2, check: true) - -# BAR should be only in env1 -run_command('check_env.py', 'BAR', env: env1, check: true) -assert(run_command('check_env.py', 'BAR', env: env2, check: false).returncode() == 42) - -# This should print deprecation warning but still work -env1.set('PLOP', '1') -run_command('check_env.py', 'PLOP', env: env1, check: true) - -# A copy of used env should be mutable and not print warning -env3 = env1 -env3.set('BAZ', '1') -run_command('check_env.py', 'PLOP', env: env3, check: true) -run_command('check_env.py', 'BAZ', env: env3, check: true) diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build index 8e7d429c722c..036a562b796c 100644 --- a/test cases/common/14 configure file/meson.build +++ b/test cases/common/14 configure file/meson.build @@ -310,10 +310,16 @@ cdata = configuration_data({ 'A_UNDEFINED' : false, }) +# merging a confdata into another one should not mark the first as immutable +cdata_test = configuration_data() +cdata_test.merge_from(cdata) +cdata.set_quoted('A_PATH', '/random/path') + configure_file(output : 'config9a.h', configuration : cdata, ) + configure_file(output : 'config9b.h', configuration : { 'B_STRING' : '"foo"', diff --git a/test cases/common/184 openmp/meson.build b/test cases/common/184 openmp/meson.build index 4cbe80616970..ab09b2c480d1 100644 --- a/test cases/common/184 openmp/meson.build +++ b/test cases/common/184 openmp/meson.build @@ -16,9 +16,6 @@ endif if cc.get_id() == 'clang' and host_machine.system() == 'windows' error('MESON_SKIP_TEST Windows clang does not support OpenMP.') endif -if host_machine.system() == 'darwin' - error('MESON_SKIP_TEST macOS does not support OpenMP.') -endif openmp = dependency('openmp') env = environment() diff --git a/test cases/common/98 subproject subdir/meson.build b/test cases/common/98 subproject subdir/meson.build index d2bafedf5119..ef053d86c41c 100644 --- a/test cases/common/98 subproject subdir/meson.build +++ b/test cases/common/98 subproject subdir/meson.build @@ -1,7 +1,3 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright 2016-2023 The Meson Developers -# Copyright © 2024 Intel Corporation - project('proj', 'c') subproject('sub') libSub = dependency('sub', fallback: ['sub', 'libSub']) @@ -10,19 +6,7 @@ exe = executable('prog', 'prog.c', dependencies: libSub) test('subproject subdir', exe) # Verify the subproject has placed dependency override. -d = dependency('sub-1.0') - -# verify that the name is the overridden name -assert(d.name() == 'sub-1.0', 'name was not properly set, should have been "sub-1.0", but was @0@'.format(d.name())) - -# Verify that when a dependency object is used for two overrides, the correct -# name is used -meson.override_dependency('new-dep', d) -d2 = dependency('new-dep') -assert(d2.name() == 'new-dep', 'name was not properly set, should have been "new-dep", but was @0@'.format(d2.name())) - -# And that the old dependency wasn't changed -assert(d.name() == 'sub-1.0', 'original dependency was mutated.') +dependency('sub-1.0') # Verify we can now take 'sub' dependency without fallback, but only version 1.0. dependency('sub') diff --git a/test cases/darwin/1 rpath removal on install/bar.c b/test cases/darwin/1 rpath removal on install/bar.c new file mode 100644 index 000000000000..b2fd0a664457 --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/bar.c @@ -0,0 +1,5 @@ +#include "foo/foo.h" + +void bar() { + foo(); +} \ No newline at end of file diff --git a/test cases/darwin/1 rpath removal on install/foo/foo.c b/test cases/darwin/1 rpath removal on install/foo/foo.c new file mode 100644 index 000000000000..e355ed4592d2 --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/foo/foo.c @@ -0,0 +1,3 @@ +int foo() { + return 1 + 2; +} \ No newline at end of file diff --git a/test cases/darwin/1 rpath removal on install/foo/foo.h b/test cases/darwin/1 rpath removal on install/foo/foo.h new file mode 100644 index 000000000000..176e7a3be8bd --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/foo/foo.h @@ -0,0 +1 @@ +int foo(); \ No newline at end of file diff --git a/test cases/darwin/1 rpath removal on install/foo/meson.build b/test cases/darwin/1 rpath removal on install/foo/meson.build new file mode 100644 index 000000000000..cc6fbe4e3d78 --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/foo/meson.build @@ -0,0 +1,3 @@ +foo = library('foo', 'foo.c', + install: true, +) \ No newline at end of file diff --git a/test cases/darwin/1 rpath removal on install/meson.build b/test cases/darwin/1 rpath removal on install/meson.build new file mode 100644 index 000000000000..093d7deba3a8 --- /dev/null +++ b/test cases/darwin/1 rpath removal on install/meson.build @@ -0,0 +1,8 @@ +project('proj', 'c') + +subdir('foo') + +bar = library('bar', 'bar.c', + link_with: foo, + install: true, +) \ No newline at end of file diff --git a/test cases/failing/132 module use inside project decl/meson.build b/test cases/failing/132 module use inside project decl/meson.build new file mode 100644 index 000000000000..8f82a5d3089b --- /dev/null +++ b/test cases/failing/132 module use inside project decl/meson.build @@ -0,0 +1,6 @@ +# GH issue 11393 +project('module use inside project decl', 'c', + version: run_command( + import('python').find_installation('python3') + ) +) diff --git a/test cases/failing/132 module use inside project decl/test.json b/test cases/failing/132 module use inside project decl/test.json new file mode 100644 index 000000000000..33e377b842f3 --- /dev/null +++ b/test cases/failing/132 module use inside project decl/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/132 module use inside project decl/meson.build:4:21: ERROR: Module methods (python.find_installation) cannot be invoked during project declaration." + } + ] + } diff --git a/test cases/failing/70 configuration immutable/input b/test cases/failing/70 configuration immutable/input new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/failing/70 configuration immutable/meson.build b/test cases/failing/70 configuration immutable/meson.build new file mode 100644 index 000000000000..b6cac41265e0 --- /dev/null +++ b/test cases/failing/70 configuration immutable/meson.build @@ -0,0 +1,12 @@ +project('configuration_data is immutable') + +a = configuration_data() + +configure_file( + configuration : a, + input : 'input', + output : 'output', +) + +still_immutable = a +still_immutable.set('hello', 'world') diff --git a/test cases/failing/70 configuration immutable/test.json b/test cases/failing/70 configuration immutable/test.json new file mode 100644 index 000000000000..fc735fa38ece --- /dev/null +++ b/test cases/failing/70 configuration immutable/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/70 configuration immutable/meson.build:12:16: ERROR: Can not set values on configuration object that has been used." + } + ] +} diff --git a/test cases/format/1 default/crazy_comments.meson b/test cases/format/1 default/crazy_comments.meson index f391ca28c263..f7ed67101bfd 100644 --- a/test cases/format/1 default/crazy_comments.meson +++ b/test cases/format/1 default/crazy_comments.meson @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Stone Tickle -# SPDX-License-Identifier: GPL-3.0-only +# This file is used from muon sources, with permission project('a') diff --git a/test cases/format/1 default/gh13242.meson b/test cases/format/1 default/gh13242.meson index b9122ec812d8..67f7dd1bb9e1 100644 --- a/test cases/format/1 default/gh13242.meson +++ b/test cases/format/1 default/gh13242.meson @@ -10,9 +10,7 @@ test( ) test( - should_fail: (settings.get('x', false) and not settings['y'] and dep.version( - - ).version_compare( + should_fail: (settings.get('x', false) and not settings['y'] and dep.version().version_compare( '>=1.2.3', )), ) diff --git a/test cases/format/1 default/indentation.meson b/test cases/format/1 default/indentation.meson index b1edc3a16a52..fe78847f7667 100644 --- a/test cases/format/1 default/indentation.meson +++ b/test cases/format/1 default/indentation.meson @@ -11,7 +11,10 @@ project( ) a = [ + # comment + # comment 1, + # comment 2, 3, [ @@ -36,8 +39,13 @@ a = [ d = {} if meson.project_version().version_compare('>1.2') + # comment + # comment if meson.version().version_compare('>1.0') + # comment + # comment foreach i : a + # comment e = { 'a': 'a', 'b': 'b', @@ -69,9 +77,18 @@ if meson.project_version().version_compare('>1.2') ], } endforeach + + foreach j : a + # comment + # comment + # comment + endforeach elif 42 in d d += {'foo': 43} else # ensure else is correctly indented (issue #13316) + # comment k = 'k' + # comment + # comment endif endif diff --git a/test cases/format/1 default/meson.build b/test cases/format/1 default/meson.build index 35e5b9694fb8..d3bb153eeb92 100644 --- a/test cases/format/1 default/meson.build +++ b/test cases/format/1 default/meson.build @@ -10,6 +10,19 @@ meson_files = { 'gh13242': files('gh13242.meson'), } +# Ensure empty function are formatted correctly on long lines +a = '@0@@1@@2@@3@@4@'.format('one', 'two', 'three', 'four', 'five').strip().strip() + +_opt = get_option('opt') \ + .disable_if( + true, + error_message: 'This is an error message because opt can\'t be enabled', + ) \ + .enable_if( + false, + error_message: 'This is an error message because opt can\'t be disabled', + ) + foreach name, f : meson_files test(name, meson_cmd, args: ['format', '--check-only', f]) endforeach diff --git a/test cases/format/1 default/meson.options b/test cases/format/1 default/meson.options new file mode 100644 index 000000000000..13f5e19c0e68 --- /dev/null +++ b/test cases/format/1 default/meson.options @@ -0,0 +1 @@ +option('opt', type : 'feature', value : 'auto') diff --git a/test cases/format/2 muon/crazy_comments.meson b/test cases/format/2 muon/crazy_comments.meson index 5ebda7d63655..fe5ae1402df5 100644 --- a/test cases/format/2 muon/crazy_comments.meson +++ b/test cases/format/2 muon/crazy_comments.meson @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Stone Tickle -# SPDX-License-Identifier: GPL-3.0-only +# This file is used from muon sources, with permission project('a') diff --git a/test cases/format/3 editorconfig/crazy_comments.meson b/test cases/format/3 editorconfig/crazy_comments.meson index 788ea1c88dff..f9fdc8848d54 100644 --- a/test cases/format/3 editorconfig/crazy_comments.meson +++ b/test cases/format/3 editorconfig/crazy_comments.meson @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Stone Tickle -# SPDX-License-Identifier: GPL-3.0-only +# This file is used from muon sources, with permission project('a') diff --git a/test cases/format/3 editorconfig/meson.build b/test cases/format/3 editorconfig/meson.build index b32974cb9e8e..2468411493c3 100644 --- a/test cases/format/3 editorconfig/meson.build +++ b/test cases/format/3 editorconfig/meson.build @@ -7,6 +7,7 @@ meson_files = { 'self': files('meson.build'), 'comments': files('crazy_comments.meson'), 'indentation': files('indentation.meson'), + 'subdir editorconfig': files('subdir/sub.meson'), } foreach name, f : meson_files diff --git a/test cases/format/3 editorconfig/subdir/.editorconfig b/test cases/format/3 editorconfig/subdir/.editorconfig new file mode 100644 index 000000000000..fac7a92caaba --- /dev/null +++ b/test cases/format/3 editorconfig/subdir/.editorconfig @@ -0,0 +1,2 @@ +[*] +max_line_length = 120 diff --git a/test cases/format/3 editorconfig/subdir/sub.meson b/test cases/format/3 editorconfig/subdir/sub.meson new file mode 100644 index 000000000000..623ca2836a9c --- /dev/null +++ b/test cases/format/3 editorconfig/subdir/sub.meson @@ -0,0 +1,3 @@ +project('line') + +msg = 'this is a very long line, and it should be be wrapped because we have line length limit of 120, not 60' diff --git a/test cases/format/4 config/crazy_comments.meson b/test cases/format/4 config/crazy_comments.meson index 557d5d409f65..71ae8f97fdd0 100644 --- a/test cases/format/4 config/crazy_comments.meson +++ b/test cases/format/4 config/crazy_comments.meson @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Stone Tickle -# SPDX-License-Identifier: GPL-3.0-only +# This file is used from muon sources, with permission project('a') diff --git a/test cases/format/4 config/indentation.meson b/test cases/format/4 config/indentation.meson index 816b5f3026a1..96a977e5fb56 100644 --- a/test cases/format/4 config/indentation.meson +++ b/test cases/format/4 config/indentation.meson @@ -71,3 +71,18 @@ if meson.project_version().version_compare('>1.2') endforeach endif endif + +subproject( + '@0@-@1@-@2@-@3@'.format( + meson.project_name(), + meson.project_version(), + meson.project_build_root(), + meson.project_source_root(), + ), + default_options : [ + 'aaaaaaaa=bbbbbbbbbb', + 'cccccccccccc=ddddddddddddd', + 'eeeeeeeeeeeeeee=fffffffffffff', + 'gggggggggggggggggggggg=hhhhhhhhhhhhhhhhhhhh', + ], +) diff --git a/test cases/format/5 transform/default.expected.meson b/test cases/format/5 transform/default.expected.meson index 4201053e1f18..18af2f8afa79 100644 --- a/test cases/format/5 transform/default.expected.meson +++ b/test cases/format/5 transform/default.expected.meson @@ -47,6 +47,7 @@ d = {'a': 1, 'b': 2, 'c': 3} # string conversion 'This is not a multiline' 'This is not a fstring' +'''This isn't convertible''' # group arg value arguments = [ diff --git a/test cases/format/5 transform/file_compare.py b/test cases/format/5 transform/file_compare.py index 7b0d1b85668f..fd3ce10560a6 100644 --- a/test cases/format/5 transform/file_compare.py +++ b/test cases/format/5 transform/file_compare.py @@ -1,7 +1,31 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation +import argparse import sys +import difflib -with open(sys.argv[1], 'r', encoding='utf-8') as f, open(sys.argv[2], 'r', encoding='utf-8') as g: - if f.read() != g.read(): - sys.exit('contents are not equal') + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument('actual', help='The transformed contents') + parser.add_argument('expected', help='the contents we expected') + args = parser.parse_args() + + with open(args.actual, 'r') as f: + actual = f.readlines() + with open(args.expected, 'r') as f: + expected = f.readlines() + + if actual == expected: + return 0 + + diff = difflib.ndiff(expected, actual) + for line in diff: + print(line, file=sys.stderr, end='') + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test cases/format/5 transform/muon.expected.meson b/test cases/format/5 transform/muon.expected.meson index 871ce27d722d..0e6309c7f722 100644 --- a/test cases/format/5 transform/muon.expected.meson +++ b/test cases/format/5 transform/muon.expected.meson @@ -47,6 +47,7 @@ d = {'a': 1, 'b': 2, 'c': 3} # string conversion '''This is not a multiline''' f'This is not a fstring' +'''This isn't convertible''' # group arg value arguments = [ diff --git a/test cases/format/5 transform/options.expected.meson b/test cases/format/5 transform/options.expected.meson index f7f45658d5c3..9ed6ac2be5af 100644 --- a/test cases/format/5 transform/options.expected.meson +++ b/test cases/format/5 transform/options.expected.meson @@ -29,6 +29,7 @@ d = { # string conversion 'This is not a multiline' 'This is not a fstring' +'''This isn't convertible''' # group arg value arguments = [ diff --git a/test cases/format/5 transform/source.meson b/test cases/format/5 transform/source.meson index 7274d4802699..4482884052d6 100644 --- a/test cases/format/5 transform/source.meson +++ b/test cases/format/5 transform/source.meson @@ -29,6 +29,7 @@ d = {'a': 1, 'b': 2, 'c': 3} # string conversion '''This is not a multiline''' f'This is not a fstring' +'''This isn't convertible''' # group arg value arguments = ['a', '--opt_a', 'opt_a_value', 'b', 'c', '--opt_d', '--opt_e', 'opt_e_value', diff --git a/test cases/frameworks/24 libgcrypt/test.json b/test cases/frameworks/24 libgcrypt/test.json index de9b738f6c45..9c282daa86e7 100644 --- a/test cases/frameworks/24 libgcrypt/test.json +++ b/test cases/frameworks/24 libgcrypt/test.json @@ -1,3 +1,3 @@ { - "expect_skip_on_jobname": ["azure", "msys2"] + "expect_skip_on_jobname": ["arch", "azure", "cygwin", "msys2"] } diff --git a/test cases/linuxlike/1 pkg-config/prog-checkver.c b/test cases/linuxlike/1 pkg-config/prog-checkver.c index ea1ed32f4ffa..14d574642923 100644 --- a/test cases/linuxlike/1 pkg-config/prog-checkver.c +++ b/test cases/linuxlike/1 pkg-config/prog-checkver.c @@ -1,11 +1,41 @@ #include +#include #include #include +static bool check_version(const char *zlib_ver, const char *found_zlib) { + if (zlib_ver == found_zlib) + return true; + + if (strcmp(zlib_ver, found_zlib) == 0) + return true; + +#ifdef ZLIBNG_VERSION + const char *ptr = strstr(zlib_ver, found_zlib); + + // If the needle isn't found or the needle isn't at the start, fail. + if (ptr == NULL || ptr != zlib_ver) + return false; + + /* In zlib-ng, ZLIB_VERSION is of the form X.Y.Z.zlib-ng. This will move the + * pointer to the start of the suffix, .zlib-ng. We know that at this point + * that FOUND_ZLIB is the start of ZLIB_VERSION, so compare the rest. + */ + ptr += strlen(found_zlib); + if (strcmp(ptr, ".zlib-ng") == 0) + return true; +#endif + + return false; +} + int main(void) { void * something = deflate; - if(strcmp(ZLIB_VERSION, FOUND_ZLIB) != 0) { + if (!check_version(ZLIB_VERSION, FOUND_ZLIB)) { printf("Meson found '%s' but zlib is '%s'\n", FOUND_ZLIB, ZLIB_VERSION); +#ifdef ZLIBNG_VERSION + puts("Note that in the case of zlib-ng, a version suffix of .zlib-ng is expected"); +#endif return 2; } if(something != 0) diff --git a/test cases/linuxlike/13 cmake dependency/prog-checkver.c b/test cases/linuxlike/13 cmake dependency/prog-checkver.c index ea1ed32f4ffa..14d574642923 100644 --- a/test cases/linuxlike/13 cmake dependency/prog-checkver.c +++ b/test cases/linuxlike/13 cmake dependency/prog-checkver.c @@ -1,11 +1,41 @@ #include +#include #include #include +static bool check_version(const char *zlib_ver, const char *found_zlib) { + if (zlib_ver == found_zlib) + return true; + + if (strcmp(zlib_ver, found_zlib) == 0) + return true; + +#ifdef ZLIBNG_VERSION + const char *ptr = strstr(zlib_ver, found_zlib); + + // If the needle isn't found or the needle isn't at the start, fail. + if (ptr == NULL || ptr != zlib_ver) + return false; + + /* In zlib-ng, ZLIB_VERSION is of the form X.Y.Z.zlib-ng. This will move the + * pointer to the start of the suffix, .zlib-ng. We know that at this point + * that FOUND_ZLIB is the start of ZLIB_VERSION, so compare the rest. + */ + ptr += strlen(found_zlib); + if (strcmp(ptr, ".zlib-ng") == 0) + return true; +#endif + + return false; +} + int main(void) { void * something = deflate; - if(strcmp(ZLIB_VERSION, FOUND_ZLIB) != 0) { + if (!check_version(ZLIB_VERSION, FOUND_ZLIB)) { printf("Meson found '%s' but zlib is '%s'\n", FOUND_ZLIB, ZLIB_VERSION); +#ifdef ZLIBNG_VERSION + puts("Note that in the case of zlib-ng, a version suffix of .zlib-ng is expected"); +#endif return 2; } if(something != 0) diff --git a/test cases/linuxlike/14 static dynamic linkage/verify_static.py b/test cases/linuxlike/14 static dynamic linkage/verify_static.py index 8d16d48c989e..25e97f368bb1 100755 --- a/test cases/linuxlike/14 static dynamic linkage/verify_static.py +++ b/test cases/linuxlike/14 static dynamic linkage/verify_static.py @@ -5,8 +5,16 @@ def handle_common(path): """Handle the common case.""" - output = subprocess.check_output(['nm', path]).decode('utf-8') - if 'T zlibVersion' in output: + try: + output = subprocess.check_output(['nm', '--defined-only', '-P', '-A', path]).decode('utf-8') + except subprocess.CalledProcessError: + # some NMs only support -U. Older binutils only supports --defined-only. + output = subprocess.check_output(['nm', '-UPA', path]).decode('utf-8') + # POSIX format. Prints all *defined* symbols, looks like this: + # builddir/main_static: zlibVersion T 1190 39 + # or + # builddir/main_static: zlibVersion D 1fde0 30 + if ': zlibVersion ' in output: return 0 return 1 diff --git a/test cases/nasm/1 configure file/hello.asm b/test cases/nasm/1 configure file/hello-x86.asm similarity index 100% rename from test cases/nasm/1 configure file/hello.asm rename to test cases/nasm/1 configure file/hello-x86.asm diff --git a/test cases/nasm/1 configure file/hello-x86_64.asm b/test cases/nasm/1 configure file/hello-x86_64.asm new file mode 100644 index 000000000000..b6cb776babbd --- /dev/null +++ b/test cases/nasm/1 configure file/hello-x86_64.asm @@ -0,0 +1,18 @@ +%include "config.asm" + + section .data +msg: db "Hello World", 10 +len: equ $ - msg + + section .text + global main +main: + mov eax, 1 ; sys_write + mov edi, 1 ; fd = STDOUT_FILENO + mov rsi, msg ; buf = msg + mov rdx, len ; count = len + syscall + + mov eax, 60 ; sys_exit + mov edi, HELLO ; exit code + syscall diff --git a/test cases/nasm/1 configure file/meson.build b/test cases/nasm/1 configure file/meson.build index fac46a687331..46a704ceb633 100644 --- a/test cases/nasm/1 configure file/meson.build +++ b/test cases/nasm/1 configure file/meson.build @@ -24,10 +24,16 @@ conf = configuration_data() conf.set('HELLO', 0) +debug_nasm = [] +if get_option('debug') + debug_nasm = ['-g'] +endif + asm_gen = generator(nasm, output : '@BASENAME@.o', arguments : [ '-f', asm_format, + debug_nasm, '-i', meson.current_source_dir() + '/', '-i', join_paths(meson.current_source_dir(), ''), '-P', join_paths(meson.current_build_dir(), 'config.asm'), @@ -44,7 +50,7 @@ config_file = configure_file( cc = meson.get_compiler('c') link_args = cc.get_supported_link_arguments(['-no-pie']) -exe = executable('hello', asm_gen.process('hello.asm'), +exe = executable('hello', asm_gen.process('hello-' + host_machine.cpu_family() + '.asm'), link_args: link_args, ) diff --git a/test cases/nasm/2 asm language/hello.asm b/test cases/nasm/2 asm language/hello-x86.asm similarity index 100% rename from test cases/nasm/2 asm language/hello.asm rename to test cases/nasm/2 asm language/hello-x86.asm diff --git a/test cases/nasm/2 asm language/hello-x86_64.asm b/test cases/nasm/2 asm language/hello-x86_64.asm new file mode 100644 index 000000000000..7e495f92ab5e --- /dev/null +++ b/test cases/nasm/2 asm language/hello-x86_64.asm @@ -0,0 +1,22 @@ +%include "config.asm" + +%ifdef FOO +%define RETVAL HELLO +%endif + + section .data +msg: db "Hello World", 10 +len: equ $ - msg + + section .text + global main +main: + mov eax, 1 ; sys_write + mov edi, 1 ; fd = STDOUT_FILENO + mov rsi, msg ; buf = msg + mov rdx, len ; count = len + syscall + + mov eax, 60 ; sys_exit + mov edi, RETVAL ; exit code + syscall diff --git a/test cases/nasm/2 asm language/meson.build b/test cases/nasm/2 asm language/meson.build index d5a2ba3eaeb2..594d12dc2496 100644 --- a/test cases/nasm/2 asm language/meson.build +++ b/test cases/nasm/2 asm language/meson.build @@ -46,7 +46,7 @@ config_file = configure_file( cc = meson.get_compiler('c') link_args = cc.get_supported_link_arguments(['-no-pie']) -exe = executable('hello', 'hello.asm', +exe = executable('hello', 'hello-' + host_machine.cpu_family() + '.asm', nasm_args: '-DFOO', link_args: link_args, ) @@ -55,7 +55,7 @@ test('hello', exe) #Test whether pthread dependency gets filtered out threads = dependency('threads') -exe2 = executable('hello_w_threads', 'hello.asm', +exe2 = executable('hello_w_threads', 'hello-' + host_machine.cpu_family() + '.asm', config_file, nasm_args: '-DFOO', link_args: link_args, diff --git a/test cases/objc/1 simple/meson.build b/test cases/objc/1 simple/meson.build index f9d5c145025e..bb45839f184f 100644 --- a/test cases/objc/1 simple/meson.build +++ b/test cases/objc/1 simple/meson.build @@ -1,4 +1,8 @@ project('objective c', 'objc', default_options: ['c_std=c99']) +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + exe = executable('prog', 'prog.m') test('objctest', exe) diff --git a/test cases/objc/2 nsstring/meson.build b/test cases/objc/2 nsstring/meson.build index 2c483d50d687..b12710d73f71 100644 --- a/test cases/objc/2 nsstring/meson.build +++ b/test cases/objc/2 nsstring/meson.build @@ -1,5 +1,9 @@ project('nsstring', 'objc') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + if host_machine.system() == 'darwin' dep = dependency('appleframeworks', modules : 'Foundation') elif host_machine.system() == 'cygwin' diff --git a/test cases/objc/3 objc args/meson.build b/test cases/objc/3 objc args/meson.build index 8887d96bc7ac..dd6f50990b31 100644 --- a/test cases/objc/3 objc args/meson.build +++ b/test cases/objc/3 objc args/meson.build @@ -1,4 +1,8 @@ project('objective c args', 'objc') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + exe = executable('prog', 'prog.m', objc_args : ['-DMESON_TEST']) test('objective c args', exe) diff --git a/test cases/objc/4 c++ project objc subproject/meson.build b/test cases/objc/4 c++ project objc subproject/meson.build index 8a77dedce3c4..946af11f603b 100644 --- a/test cases/objc/4 c++ project objc subproject/meson.build +++ b/test cases/objc/4 c++ project objc subproject/meson.build @@ -1,5 +1,9 @@ project('master', ['cpp']) +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + foo = subproject('foo') dep = foo.get_variable('foo_dep') diff --git a/test cases/objc/4 c++ project objc subproject/subprojects/foo/meson.build b/test cases/objc/4 c++ project objc subproject/subprojects/foo/meson.build index 2dbf8ab26764..5f4d9fd836ba 100644 --- a/test cases/objc/4 c++ project objc subproject/subprojects/foo/meson.build +++ b/test cases/objc/4 c++ project objc subproject/subprojects/foo/meson.build @@ -1,5 +1,9 @@ project('foo', ['objc']) +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + l = static_library('foo', 'foo.m') foo_dep = declare_dependency(link_with : l) diff --git a/test cases/objc/5 objfw/meson.build b/test cases/objc/5 objfw/meson.build index 40ddb7968db4..761b49f67e9c 100644 --- a/test cases/objc/5 objfw/meson.build +++ b/test cases/objc/5 objfw/meson.build @@ -1,5 +1,9 @@ project('objfw build tests', 'objc') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objc is not supported by vs backend') +endif + objfw_dep = dependency('objfw', required: false) objfwtest_dep = dependency('objfw', modules: ['ObjFWTest'], required: false) diff --git a/test cases/objcpp/1 simple/meson.build b/test cases/objcpp/1 simple/meson.build index c9a5c8498195..c3acc879ad1e 100644 --- a/test cases/objcpp/1 simple/meson.build +++ b/test cases/objcpp/1 simple/meson.build @@ -1,4 +1,8 @@ project('Objective C++', 'objcpp', default_options: 'cpp_std=c++14') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objcpp is not supported by vs backend') +endif + exe = executable('objcppprog', 'prog.mm') test('objcpp', exe) diff --git a/test cases/objcpp/2 objc++ args/meson.build b/test cases/objcpp/2 objc++ args/meson.build index e0e34b0f1e17..8797b619d3e8 100644 --- a/test cases/objcpp/2 objc++ args/meson.build +++ b/test cases/objcpp/2 objc++ args/meson.build @@ -1,4 +1,8 @@ project('objective c++ args', 'objcpp') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objcpp is not supported by vs backend') +endif + exe = executable('prog', 'prog.mm', objcpp_args : ['-DMESON_OBJCPP_TEST']) test('objective c++ args', exe) diff --git a/test cases/objcpp/3 objfw/meson.build b/test cases/objcpp/3 objfw/meson.build index da14681ebf1a..3fcc84dd554b 100644 --- a/test cases/objcpp/3 objfw/meson.build +++ b/test cases/objcpp/3 objfw/meson.build @@ -1,5 +1,9 @@ project('objfw build tests', 'objcpp') +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: objcpp is not supported by vs backend') +endif + objfw_dep = dependency('objfw', required: false) objfwtest_dep = dependency('objfw', modules: ['ObjFWTest'], required: false) diff --git a/test cases/rust/12 bindgen/meson.build b/test cases/rust/12 bindgen/meson.build index 09cb02a6d319..57e44a0635c6 100644 --- a/test cases/rust/12 bindgen/meson.build +++ b/test cases/rust/12 bindgen/meson.build @@ -15,7 +15,7 @@ endif cc_id = meson.get_compiler('c').get_id() compiler_specific_args = [] if cc_id == 'gcc' - compiler_specific_args = ['-mtls-dialect=gnu2'] + compiler_specific_args = ['-fipa-pta'] elif cc_id == 'msvc' compiler_specific_args = ['/fp:fast'] endif diff --git a/test cases/rust/18 proc-macro/lib.rs b/test cases/rust/18 proc-macro/lib.rs new file mode 100644 index 000000000000..5242886cc5e4 --- /dev/null +++ b/test cases/rust/18 proc-macro/lib.rs @@ -0,0 +1,8 @@ +extern crate proc_macro_examples; +use proc_macro_examples::make_answer; + +make_answer!(); + +pub fn func() -> u32 { + answer() +} diff --git a/test cases/rust/18 proc-macro/meson.build b/test cases/rust/18 proc-macro/meson.build index c5f0dfc82aee..e8b28eda144e 100644 --- a/test cases/rust/18 proc-macro/meson.build +++ b/test cases/rust/18 proc-macro/meson.build @@ -31,3 +31,14 @@ main = executable( ) test('main_test2', main) + +subdir('subdir') + +staticlib = static_library('staticlib', 'lib.rs', + link_with: pm_in_subdir, + rust_dependency_map : {'proc_macro_examples3' : 'proc_macro_examples'} +) + +executable('transitive-proc-macro', 'transitive-proc-macro.rs', + link_with: staticlib, +) diff --git a/test cases/rust/18 proc-macro/subdir/meson.build b/test cases/rust/18 proc-macro/subdir/meson.build new file mode 100644 index 000000000000..04842c431e78 --- /dev/null +++ b/test cases/rust/18 proc-macro/subdir/meson.build @@ -0,0 +1 @@ +pm_in_subdir = rust.proc_macro('proc_macro_examples3', '../proc.rs') diff --git a/test cases/rust/18 proc-macro/transitive-proc-macro.rs b/test cases/rust/18 proc-macro/transitive-proc-macro.rs new file mode 100644 index 000000000000..4c804b3b6f4d --- /dev/null +++ b/test cases/rust/18 proc-macro/transitive-proc-macro.rs @@ -0,0 +1,7 @@ +extern crate staticlib; +use staticlib::func; + + +fn main() { + assert_eq!(42, func()); +} diff --git a/test cases/rust/21 transitive dependencies/meson.build b/test cases/rust/21 transitive dependencies/meson.build index 37687fd4db2f..3b1f3d63cd4d 100644 --- a/test cases/rust/21 transitive dependencies/meson.build +++ b/test cases/rust/21 transitive dependencies/meson.build @@ -21,6 +21,7 @@ foo = static_library('foo-rs', 'foo.rs', rust_abi: 'c', link_with: pm, ) +shared_library('shared', 'foo.c', link_with : foo) exe = executable('footest', 'foo.c', link_with: foo, ) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index e912e943977e..f471f6a95f46 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1336,6 +1336,7 @@ def test_source_generator_program_cause_rebuild(self): self.utime(os.path.join(testdir, 'srcgen.py')) self.assertRebuiltTarget('basic') + @skipIf(is_ci() and is_cygwin(), 'A GCC update on 2024-07-21 has broken LTO and is being investigated') def test_static_library_lto(self): ''' Test that static libraries can be built with LTO and linked to @@ -1352,6 +1353,7 @@ def test_static_library_lto(self): self.build() self.run_tests() + @skipIf(is_ci() and is_cygwin(), 'A GCC update on 2024-07-21 has broken LTO and is being investigated') @skip_if_not_base_option('b_lto_threads') def test_lto_threads(self): testdir = os.path.join(self.common_test_dir, '6 linkshared') diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index e94a2baac210..6e6a01d40fd3 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -78,6 +78,7 @@ def setUp(self): self.linuxlike_test_dir = os.path.join(src_root, 'test cases/linuxlike') self.objc_test_dir = os.path.join(src_root, 'test cases/objc') self.objcpp_test_dir = os.path.join(src_root, 'test cases/objcpp') + self.darwin_test_dir = os.path.join(src_root, 'test cases/darwin') # Misc stuff self.orig_env = os.environ.copy() diff --git a/unittests/darwintests.py b/unittests/darwintests.py index 7403104771b7..26dd996415af 100644 --- a/unittests/darwintests.py +++ b/unittests/darwintests.py @@ -4,10 +4,11 @@ import subprocess import re import os +import platform import unittest from mesonbuild.mesonlib import ( - MachineChoice, is_osx + MachineChoice, is_osx, version_compare ) from mesonbuild.compilers import ( detect_c_compiler @@ -81,6 +82,7 @@ def test_apple_bitcode_modules(self): self.build() self.run_tests() + @unittest.skipIf(version_compare(platform.mac_ver()[0], '<10.7'), '-export_dynamic was added in 10.7') def test_apple_lto_export_dynamic(self): ''' Tests that -Wl,-export_dynamic is correctly added, when export_dynamic: true is set. @@ -100,6 +102,12 @@ def _get_darwin_versions(self, fname): self.assertIsNotNone(m, msg=out) return m.groups() + def _get_darwin_rpaths(self, fname: str) -> T.List[str]: + out = subprocess.check_output(['otool', '-l', fname], universal_newlines=True) + pattern = re.compile(r'path (.*) \(offset \d+\)') + rpaths = pattern.findall(out) + return rpaths + @skipIfNoPkgconfig def test_library_versioning(self): ''' @@ -154,3 +162,15 @@ def test_darwin_get_object_archs(self): from mesonbuild.mesonlib import darwin_get_object_archs archs = darwin_get_object_archs('/bin/cat') self.assertEqual(archs, ['x86_64', 'aarch64']) + + def test_darwin_meson_rpaths_removed_on_install(self): + testdir = os.path.join(self.darwin_test_dir, '1 rpath removal on install') + self.init(testdir) + self.build() + # Meson-created RPATHs are usually only valid in the build directory + rpaths = self._get_darwin_rpaths(os.path.join(self.builddir, 'libbar.dylib')) + self.assertListEqual(rpaths, ['@loader_path/foo']) + self.install() + # Those RPATHs are no longer valid and should not be present after installation + rpaths = self._get_darwin_rpaths(os.path.join(self.installdir, 'usr/lib/libbar.dylib')) + self.assertListEqual(rpaths, []) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 2b38407bf9b2..775d0dc2bc1e 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -15,8 +15,8 @@ from .baseplatformtests import BasePlatformTests from .helpers import is_ci -from mesonbuild.mesonlib import EnvironmentVariables, ExecutableSerialisation, MesonException, is_linux, python_command -from mesonbuild.mformat import match_path +from mesonbuild.mesonlib import EnvironmentVariables, ExecutableSerialisation, MesonException, is_linux, python_command, windows_proof_rmtree +from mesonbuild.mformat import Formatter, match_path from mesonbuild.optinterpreter import OptionInterpreter, OptionException from run_tests import Backend @@ -334,6 +334,60 @@ def test_editorconfig_match_path(self): for filename, pattern, expected in cases: self.assertTrue(match_path(filename, pattern) is expected, f'{filename} -> {pattern}') + def test_format_invalid_config_key(self) -> None: + fd, fname = tempfile.mkstemp(suffix='.ini', text=True) + self.addCleanup(os.unlink, fname) + + with os.fdopen(fd, 'w', encoding='utf-8') as handle: + handle.write('not_an_option = 42\n') + + with self.assertRaises(MesonException): + Formatter(Path(fname), use_editor_config=False, fetch_subdirs=False) + + def test_format_invalid_config_value(self) -> None: + fd, fname = tempfile.mkstemp(suffix='.ini', text=True) + self.addCleanup(os.unlink, fname) + + with os.fdopen(fd, 'w', encoding='utf-8') as handle: + handle.write('max_line_length = string\n') + + with self.assertRaises(MesonException): + Formatter(Path(fname), use_editor_config=False, fetch_subdirs=False) + + def test_format_invalid_editorconfig_value(self) -> None: + dirpath = tempfile.mkdtemp() + self.addCleanup(windows_proof_rmtree, dirpath) + + editorconfig = Path(dirpath, '.editorconfig') + with open(editorconfig, 'w', encoding='utf-8') as handle: + handle.write('[*]\n') + handle.write('indent_size = string\n') + + formatter = Formatter(None, use_editor_config=True, fetch_subdirs=False) + with self.assertRaises(MesonException): + formatter.load_editor_config(editorconfig) + + def test_format_empty_file(self) -> None: + formatter = Formatter(None, use_editor_config=False, fetch_subdirs=False) + for code in ('', '\n'): + formatted = formatter.format(code, Path()) + self.assertEqual('\n', formatted) + + def test_format_indent_comment_in_brackets(self) -> None: + """Ensure comments in arrays and dicts are correctly indented""" + formatter = Formatter(None, use_editor_config=False, fetch_subdirs=False) + code = 'a = [\n # comment\n]\n' + formatted = formatter.format(code, Path()) + self.assertEqual(code, formatted) + + code = 'a = [\n # comment\n 1,\n]\n' + formatted = formatter.format(code, Path()) + self.assertEqual(code, formatted) + + code = 'a = {\n # comment\n}\n' + formatted = formatter.format(code, Path()) + self.assertEqual(code, formatted) + def test_error_configuring_subdir(self): testdir = os.path.join(self.common_test_dir, '152 index customtarget') out = self.init(os.path.join(testdir, 'subdir'), allow_fail=True)