diff options
237 files changed, 2222 insertions, 2168 deletions
diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 9dd1355..e5a1bd5 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -71,6 +71,13 @@ jobs: vala zlib-devel + - name: workaround wrong python version + run: | + export PATH=/usr/sbin:/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32 + update-alternatives --verbose --set python /usr/bin/python3.8 + update-alternatives --verbose --set python3 /usr/bin/python3.8 + shell: C:\cygwin\bin\bash.exe --noprofile --norc -o igncr -eo pipefail '{0}' + - name: Run pip run: | export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32 diff --git a/.github/workflows/lint_mypy.yml b/.github/workflows/lint.yml index 195d733..cdfed11 100644 --- a/.github/workflows/lint_mypy.yml +++ b/.github/workflows/lint.yml @@ -1,30 +1,29 @@ -name: LintMypy +name: Lint concurrency: - group: mypy-${{ github.head_ref }} + group: lint-${{ github.head_ref }} cancel-in-progress: true on: push: paths: - "**.py" - - ".github/workflows/lint_mypy.yml" + - ".github/workflows/lint.yml" pull_request: paths: - "**.py" - - ".github/workflows/lint_mypy.yml" + - ".github/workflows/lint.yml" jobs: - lint: + pylint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: '3.x' - # pylint version constraint can be removed when https://github.com/PyCQA/pylint/issues/3524 is resolved - - run: python -m pip install pylint==2.4.4 + - run: python -m pip install pylint - run: pylint mesonbuild custom_lint: diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index a71ee33..e3d3821 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -61,4 +61,11 @@ jobs: file: docs/_build/reference_manual.json tag: ${{ github.ref }} if: ${{ github.event_name == 'release' }} + - name: Release the current man docs + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: docs/_build/meson-reference.3 + tag: ${{ github.ref }} + if: ${{ github.event_name == 'release' }} @@ -14,7 +14,7 @@ build system. #### Dependencies - - [Python](https://python.org) (version 3.6 or newer) + - [Python](https://python.org) (version 3.7 or newer) - [Ninja](https://ninja-build.org) (version 1.8.2 or newer) #### Installing from source diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2696fc1..2bdc65a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -62,7 +62,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.6' + versionSpec: '3.7' addToPath: true architecture: 'x64' - task: PowerShell@2 diff --git a/ci/ciimage/bionic/install.sh b/ci/ciimage/bionic/install.sh index 90858f0..74b407f 100755 --- a/ci/ciimage/bionic/install.sh +++ b/ci/ciimage/bionic/install.sh @@ -10,6 +10,7 @@ export DC=gdc pkgs=( python3-pip libxml2-dev libxslt1-dev libyaml-dev libjson-glib-dev + python3.7 python3.7-dev wget unzip cmake doxygen clang pkg-config-arm-linux-gnueabihf @@ -48,7 +49,13 @@ done # packages eatmydata apt-get -y install "${pkgs[@]}" -install_python_packages +# Actually select the right python version +update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 \ + --slave /usr/lib/x86_64-linux-gnu/pkgconfig/python3.pc python3.pc /usr/lib/x86_64-linux-gnu/pkgconfig/python-3.6.pc +update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2 \ + --slave /usr/lib/x86_64-linux-gnu/pkgconfig/python3.pc python3.pc /usr/lib/x86_64-linux-gnu/pkgconfig/python-3.7.pc + +python3 -m pip install -U "${base_python_pkgs[@]}" "${python_pkgs[@]}" # Install the ninja 0.10 wget https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip @@ -20,7 +20,7 @@ if ($env:arch -eq 'x64') { # Rust puts its shared stdlib in a secret place, but it is needed to run tests. $env:Path += ";$HOME/.rustup/toolchains/stable-i686-pc-windows-msvc/bin" # Need 32-bit Python for tests that need the Python dependency - $env:Path = "C:\hostedtoolcache\windows\Python\3.6.8\x86;C:\hostedtoolcache\windows\Python\3.6.8\x86\Scripts;$env:Path" + $env:Path = "C:\hostedtoolcache\windows\Python\3.7.9\x86;C:\hostedtoolcache\windows\Python\3.7.9\x86\Scripts;$env:Path" } # Set the CI env var for the meson test framework diff --git a/docs/markdown/Continuous-Integration.md b/docs/markdown/Continuous-Integration.md index f24bdb0..ec0b4e0 100644 --- a/docs/markdown/Continuous-Integration.md +++ b/docs/markdown/Continuous-Integration.md @@ -207,7 +207,7 @@ GCC on Linux, Mac and Windows. The optional `on:` parameters only run this CI when the C code is changed--corresponding ci_python.yml might run only on "**.py" file changes. -```yml +```yaml name: ci_meson on: diff --git a/docs/markdown/Getting-meson.md b/docs/markdown/Getting-meson.md index ec6f1c6..c3c3cc8 100644 --- a/docs/markdown/Getting-meson.md +++ b/docs/markdown/Getting-meson.md @@ -1,6 +1,6 @@ # Getting Meson -Meson is implemented in Python 3, and requires 3.6 or newer. If your +Meson is implemented in Python 3, and requires 3.7 or newer. If your operating system provides a package manager, you should install it with that. For platforms that don't have a package manager, you need to download it from [Python's home page]. See below for @@ -14,7 +14,7 @@ itself without doing anything special. On Windows, if you did not install Python with the installer options that make Python scripts executable, you will have to run `python -/path/to/meson.py`, where `python` is Python 3.6 or newer. +/path/to/meson.py`, where `python` is Python 3.7 or newer. The newest development code can be obtained directly from [Git], and we strive to ensure that it will always be working and usable. All diff --git a/docs/markdown/Getting-meson_ptbr.md b/docs/markdown/Getting-meson_ptbr.md index b5af345..f4f3ac1 100644 --- a/docs/markdown/Getting-meson_ptbr.md +++ b/docs/markdown/Getting-meson_ptbr.md @@ -1,6 +1,6 @@ # Obtendo o Meson -Meson é implementado em Python 3, e requer a versão 3.6 ou mais nova. +Meson é implementado em Python 3, e requer a versão 3.7 ou mais nova. se o seu sistema operacional provê um gerenciador de pacotes, você deve instalar o Meson com ele. Para plataformas que não tem um gerenciador de pacotes, você precisa baixa-lo da [página inicial do Python]. Veja abaixo @@ -14,7 +14,7 @@ do git sem fazer nada de especial. No Windows, se você não instalar o Python com a opção do instalador que fazem os *scripts* Python executáveis, você vai ter que executar `python -/path/to/meson.py`, onde `python` é o Python 3.6 ou mais novo. +/path/to/meson.py`, onde `python` é o Python 3.7 ou mais novo. O código de desenvolvimento mais recente pode ser obtido diretamente do [Git], e nós lutamos para garatir que ele vai estar sempre funcionando e usável. Todos diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 4f08c6d..616562a 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -133,8 +133,7 @@ argument is the basename of the output files. * `nostdinc`: if true, don't include the standard marshallers from glib * `prefix`: the prefix to use for symbols * `skip_source`: if true, skip source location comments -* `sources` []str *required*: List of string sources to consume -* `sources`: the list of sources to use as inputs +* `sources` [](str | File) *required*: the list of sources to use as inputs * `stdinc`: if true, include the standard marshallers from glib * `valist_marshallers`: if true, generate va_list marshallers @@ -321,8 +320,8 @@ VAPI or Vala binaries. languages: []string, symlink_media: bool = true): void ``` -Installs help documentation using Yelp. The first argument is the -project id. +Installs help documentation for Yelp using itstool and gettext. The first +argument is the project id. Additionally, sources can be passed as additional positional arguments. This was, however, undocumented and never officially supported. Due to a longstanding diff --git a/docs/markdown/RPM-module.md b/docs/markdown/RPM-module.md deleted file mode 100644 index cab6d96..0000000 --- a/docs/markdown/RPM-module.md +++ /dev/null @@ -1,16 +0,0 @@ -# RPM module - -The RPM module can be used to create a sample rpm spec file for a -Meson project. It autodetects installed files, dependencies and so -on. Using it is very simple. At the very end of your Meson project -(that is, the end of your top level `meson.build` file) add these two -lines. - -```meson -rpm = import('rpm') -rpm.generate_spec_template() -``` - -Run Meson once on your code and the template will be written in your -build directory. Then remove the two lines above and manually edit the -template to add missing information. After this it is ready for use. diff --git a/docs/markdown/Release-notes-for-0.61.0.md b/docs/markdown/Release-notes-for-0.61.0.md new file mode 100644 index 0000000..8798453 --- /dev/null +++ b/docs/markdown/Release-notes-for-0.61.0.md @@ -0,0 +1,138 @@ +--- +title: Release 0.61.0 +short-description: Release notes for 0.61.0 +... + +# New features + +## backend_startup_project + +`backend_startup_project` will no longer erase the last project in a VS +solution if it is not the specified project. + +## Windows.compile_resources CustomTarget + +Previously the Windows module only accepted CustomTargets with one output, it +now accepts them with more than one output, and creates a windows resource +target for each output. Additionally it now accepts indexes of CustomTargets + +```meson + +ct = custom_target( + 'multiple', + output : ['resource', 'another resource'], + ... +) + +ct2 = custom_target( + 'slice', + output : ['resource', 'not a resource'], + ... +) + +resources = windows.compile_resources(ct, ct2[0]) +``` + +## Add a man page backend to refman + +The refman docs (function and object reference) can now be generated as a man +page. + +## ``extract_objects()`` supports generated sources + +Custom targets or generated files (returned by ``generator.process()``) +can now be passed to ``extract_objects()``. + +## Python 3.6 support will be dropped in the next release + +The final [Python 3.6 release was 3.6.15 in September](https://www.python.org/dev/peps/pep-0494/#lifespan). +This release series is now End-of-Life (EOL). The only LTS distribution that +still ships Python 3.5 as the default Python is Ubuntu 18.04, which has Python +3.8 available as well. + +Python 3.7 has various features that we find useful such as future annotations, +the importlib.resources module, and dataclasses. + +As a result, we will begin requiring Python 3.7 or newer in Meson 0.62, which +is the next release. Starting with Meson 0.61, we now print a `NOTICE:` when +a `meson` command is run on Python 3.6 to inform users about this. + +## Warning if check kwarg of run_command is missing + +The `check` kwarg of `run_command` currently defaults to `false`. +Because we want to change that, running +```meson +run_command('cmd') +``` +now results in: +```text +WARNING: You should add the boolean check kwarg to the run_command call. + It currently defaults to false, + but it will default to true in future releases of meson. + See also: https://github.com/mesonbuild/meson/issues/9300 +``` + +## `meson rewrite` can modify `extra_files` + +The build script rewriter can now modify targets' `extra_files` lists, +or create them if absent. It it used in the same way as with rewriting +source lists: + +```bash +meson rewrite target <target name/id> {add_extra_files/rm_extra_files} [list of extra files] +``` + +The rewriter's script mode also supports these actions: + +```json +{ + "type": "target", + "target": "<target name>", + "operation": "extra_files_add / extra_files_rm", + "sources": ["list", "of", "extra", "files", "to", "add, remove"], +} +``` + +## `meson rewrite target <target> info` outputs *target*'s `extra_files` + +Targets' `extra_files` lists are now included in the rewriter's target info dump +as a list of file paths, in the same way `sources` are. This applies to both +`meson rewrite` CLI and script mode. + +## Visual Studio 2022 backend + +As Visual Studio 2022 is released recently, it's time to support the +new version in Meson. This mainly includes the new "v143" platform tools. + +The usage is similar to other backends. For example +```meson +meson setup builddir --backend=vs2022 +``` +will configure "builddir" for projects compatible with Visual Studio 2022. + +## Support for CMake <3.14 is now deprecated for CMake subprojects + +In CMake 3.14, the File API was introduced and the old CMake server API was +deprecated (and removed in CMake 3.20). Thus support for this API will also +be removed from Meson in future releases. + +This deprecation only affects CMake subprojects. + +## Added support for sccache + +Meson now supports [sccache](https://github.com/mozilla/sccache) just +like it has supported CCache. If both sccache and CCache are +available, the autodetection logic prefers sccache. + +## install_symlink function + +It is now possible to request for symbolic links to be installed during +installation. The `install_symlink` function takes a positional argument to +the link name, and installs a symbolic link pointing to `pointing_to` target. +The link will be created under `install_dir` directory and cannot contain path +separators. + +```meson +install_symlink('target', pointing_to: '../bin/target', install_dir: '/usr/sbin') +``` + diff --git a/docs/markdown/Running-Meson.md b/docs/markdown/Running-Meson.md index 2873cbc..2bc0b9b 100644 --- a/docs/markdown/Running-Meson.md +++ b/docs/markdown/Running-Meson.md @@ -31,7 +31,7 @@ meson setup builddir We invoke Meson with the `setup` command, giving it the location of the build directory. Meson uses [out of source -builds](http://voices.canonical.com/jussi.pakkanen/2013/04/16/why-you-should-consider-using-separate-build-directories/). +builds](http://web.archive.org/web/20190715081007/http://voices.canonical.com/jussi.pakkanen/2013/04/16/why-you-should-consider-using-separate-build-directories/). Hint: The syntax of Meson is `meson [command] [arguments] [options]`. The `setup` command takes a `builddir` and a `srcdir` argument. If no diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 9f8b09b..54834b7 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -34,7 +34,7 @@ that automatically. Additional, test only dependencies may be passed via the dependencies argument. -### bindgen(*, input: string | BuildTarget | []string | []BuildTarget, output: string, include_directories: []include_directories, c_args: []string, args: []string) +### bindgen(*, input: string | BuildTarget | [](string | BuildTarget), output: string, include_directories: []include_directories, c_args: []string, args: []string) This function wraps bindgen to simplify creating rust bindings around C libraries. This has two advantages over hand-rolling ones own with a @@ -60,8 +60,8 @@ rust = import('unstable-rust') inc = include_directories('..'¸ '../../foo') generated = rust.bindgen( - 'myheader.h', - 'generated.rs', + input : 'myheader.h', + output : 'generated.rs', include_directories : [inc, include_directories('foo')], args : ['--no-rustfmt-bindings'], c_args : ['-DFOO=1'], @@ -77,7 +77,7 @@ h1 = custom_target(...) h2 = custom_target(...) r1 = rust.bindgen( - [h1, h2], # h1 includes h2, - 'out.rs', + input : [h1, h2], # h1 includes h2, + output : 'out.rs', ) ``` diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md index 7cb04e0..fef9d11 100644 --- a/docs/markdown/Subprojects.md +++ b/docs/markdown/Subprojects.md @@ -153,7 +153,7 @@ Because this is such a common operation, Meson provides a shortcut for this use case. ```meson -dep = dependency('foo', fallback : [subproject_name, variable_name]) +dep = dependency('foo', fallback : ['subproject_name', 'variable_name']) ``` The `fallback` keyword argument takes two items, the name of the diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index bd146d7..8510d6e 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -94,7 +94,7 @@ format files - [Libvirt](https://libvirt.org), a toolkit to manage virtualization platforms - [Libzim](https://github.com/openzim/libzim), the reference implementation for the ZIM file format - [Marker](https://github.com/fabiocolacio/Marker), a GTK-3 markdown editor - - [Mesa](https://gitlab.freedesktop.org/mesa/mesa/), an open source graphics driver project + - [Mesa](https://mesa3d.org/), an open source graphics driver project - [MiracleCast](https://github.com/albfan/miraclecast), connect external monitors to your system via Wifi-Display specification aka Miracast - [mpv](https://github.com/mpv-player/mpv), a free, open source, and cross-platform media player - [mrsh](https://github.com/emersion/mrsh), a minimal POSIX shell diff --git a/docs/markdown/fallback-wraptool.md b/docs/markdown/fallback-wraptool.md deleted file mode 100644 index d4f5af2..0000000 --- a/docs/markdown/fallback-wraptool.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: fallback wraptool -... - -# In case of emergency - -In case wraptool is down we have created a backup script that you can -use to download wraps directly from the GitHub repos. It is not as -slick and may have bugs but at least it will allow you to use wraps. - -## Using it - -To list all available wraps: - - ghwt.py list - -To install a wrap, go to your source root, make sure that the -`subprojects` directory exists and run this command: - - ghwt.py install <projectname> [<branchname>] - -This will stage the subproject ready to use. If you have multiple -subprojects you need to download them all manually. - -Specifying branch name is optional. If not specified, the list of -potential branches is sorted alphabetically and the last branch is -used. - -*Note* The tool was added in 0.32.0, for versions older than that you -need to delete the `foo.wrap` file to work around this issue. - -## How to upgrade an existing dir/fix broken state/any other problem - -Nuke the contents of `subprojects` and start again. - -## Known issues - -Some repositories show up in the list but are not installable. They -would not show up in the real WrapDB because they are works in -progress. - -GitHub web API limits the amount of queries you can do to 60/hour. If -you exceed that you need to wait for the timer to reset. diff --git a/docs/markdown/index.md b/docs/markdown/index.md index 14bdfbe..427cc05 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -31,14 +31,16 @@ read [this beginner guide](SimpleStart.md) to get started. ## Community -There are two main methods of connecting with other Meson -developers. The easiest way for most people is a web chat. The channel -to use is `#mesonbuild` either via Matrix ([web +The easiest way for most people to connect to other Meson developers is +a web chat. The channel to use is `#mesonbuild` either via Matrix ([web interface](https://app.element.io/#/room/#mesonbuild:matrix.org)) or [OFTC IRC](https://www.oftc.net/). -The second one is the mailing list, which is hosted at -[Google Groups](https://groups.google.com/forum/#!forum/mesonbuild). +Other methods of communication include the [mailing +list](https://groups.google.com/forum/#!forum/mesonbuild) (hosted by +Google Groups) and the +[Discussions](https://github.com/mesonbuild/meson/discussions) section +of the Meson GitHub repository. ### [Projects using Meson](Users.md) diff --git a/docs/markdown/snippets/about_minimum_python_version.md b/docs/markdown/snippets/about_minimum_python_version.md new file mode 100644 index 0000000..fa44a75 --- /dev/null +++ b/docs/markdown/snippets/about_minimum_python_version.md @@ -0,0 +1,7 @@ +## Minimum required Python version updated to 3.7 + +Meson now requires at least Python version 3.7 to run as Python 3.6 reached EOL +on December 2021. In practice this should only affect people developing on +Ubuntu Bionic, who will need to manually install python3.8 from the official +repositories. + diff --git a/docs/markdown/snippets/armclang-support.md b/docs/markdown/snippets/armclang-support.md new file mode 100644 index 0000000..80e7af2 --- /dev/null +++ b/docs/markdown/snippets/armclang-support.md @@ -0,0 +1,6 @@ +## Support for ARM Ltd. Clang toolchain + +Support for the `armltdclang` compiler has been added. This differs from the +existing `armclang` toolchain in that it is a fork of Clang by ARM Ltd. and +supports native compilation. The Keil `armclang` toolchain only supports +cross-compilation to embedded devices. diff --git a/docs/markdown/snippets/check_false_warning.md b/docs/markdown/snippets/check_false_warning.md deleted file mode 100644 index 50214b7..0000000 --- a/docs/markdown/snippets/check_false_warning.md +++ /dev/null @@ -1,14 +0,0 @@ -## Warning if check kwarg of run_command is missing - -The `check` kwarg of `run_command` currently defaults to `false`. -Because we want to change that, running -```meson -run_command('cmd') -``` -now results in: -```text -WARNING: You should add the boolean check kwarg to the run_command call. - It currently defaults to false, - but it will default to true in future releases of meson. - See also: https://github.com/mesonbuild/meson/issues/9300 -``` diff --git a/docs/markdown/snippets/cmake_deprecated.md b/docs/markdown/snippets/cmake_deprecated.md deleted file mode 100644 index 9e456e7..0000000 --- a/docs/markdown/snippets/cmake_deprecated.md +++ /dev/null @@ -1,7 +0,0 @@ -## Support for CMake <3.14 is now deprecated for CMake subprojects - -In CMake 3.14, the File API was introduced and the old CMake server API was -deprecated (and removed in CMake 3.20). Thus support for this API will also -be removed from Meson in future releases. - -This deprecation only affects CMake subprojects. diff --git a/docs/markdown/snippets/extract-more-objects.md b/docs/markdown/snippets/extract-more-objects.md deleted file mode 100644 index 73a4757..0000000 --- a/docs/markdown/snippets/extract-more-objects.md +++ /dev/null @@ -1,4 +0,0 @@ -## ``extract_objects()`` supports generated sources - -Custom targets or generated files (returned by ``generator.process()``) -can now be passed to ``extract_objects()``. diff --git a/docs/markdown/snippets/fix_backend_startup_project.md b/docs/markdown/snippets/fix_backend_startup_project.md deleted file mode 100644 index 8269ef6..0000000 --- a/docs/markdown/snippets/fix_backend_startup_project.md +++ /dev/null @@ -1,4 +0,0 @@ -## backend_startup_project - -`backend_startup_project` will no longer erase the last project in a VS -solution if it is not the specified project. diff --git a/docs/markdown/snippets/install_symlink.md b/docs/markdown/snippets/install_symlink.md deleted file mode 100644 index 752c422..0000000 --- a/docs/markdown/snippets/install_symlink.md +++ /dev/null @@ -1,11 +0,0 @@ -## install_symlink function - -It is now possible to request for symbolic links to be installed during -installation. The `install_symlink` function takes a positional argument to -the link name, and installs a symbolic link pointing to `pointing_to` target. -The link will be created under `install_dir` directory and cannot contain path -separators. - -```meson -install_symlink('target', pointing_to: '../bin/target', install_dir: '/usr/sbin') -``` diff --git a/docs/markdown/snippets/removing_rpm_module.md b/docs/markdown/snippets/removing_rpm_module.md new file mode 100644 index 0000000..c492d51 --- /dev/null +++ b/docs/markdown/snippets/removing_rpm_module.md @@ -0,0 +1,10 @@ +## Removal of the RPM module + +Due to lack of interest, lack of maintainership, and lack of a clear purpose, +the RPM module has been removed. + +Users interested in one-shot tools to generate an RPM spec file template for +distro packaging, are encouraged develop an external tool that reads the +introspection data. + +For more details, see https://github.com/mesonbuild/meson/issues/9764 diff --git a/docs/markdown/snippets/rewriter_extra_files.md b/docs/markdown/snippets/rewriter_extra_files.md deleted file mode 100644 index c46f076..0000000 --- a/docs/markdown/snippets/rewriter_extra_files.md +++ /dev/null @@ -1,26 +0,0 @@ -## `meson rewrite` can modify `extra_files` - -The build script rewriter can now modify targets' `extra_files` lists, -or create them if absent. It it used in the same way as with rewriting -source lists: - -```bash -meson rewrite target <target name/id> {add_extra_files/rm_extra_files} [list of extra files] -``` - -The rewriter's script mode also supports these actions: - -```json -{ - "type": "target", - "target": "<target name>", - "operation": "extra_files_add / extra_files_rm", - "sources": ["list", "of", "extra", "files", "to", "add, remove"], -} -``` - -## `meson rewrite target <target> info` outputs *target*'s `extra_files` - -Targets' `extra_files` lists are now included in the rewriter's target info dump -as a list of file paths, in the same way `sources` are. This applies to both -`meson rewrite` CLI and script mode. diff --git a/docs/markdown/snippets/sccache.md b/docs/markdown/snippets/sccache.md deleted file mode 100644 index 72bdf5f..0000000 --- a/docs/markdown/snippets/sccache.md +++ /dev/null @@ -1,5 +0,0 @@ -## Added support for sccache - -Meson now supports [sccache](https://github.com/mozilla/sccache) just -like it has supported CCache. If both sccache and CCache are -available, the autodetection logic prefers sccache. diff --git a/docs/markdown/snippets/vs_2022.md b/docs/markdown/snippets/vs_2022.md deleted file mode 100644 index 0c3ff02..0000000 --- a/docs/markdown/snippets/vs_2022.md +++ /dev/null @@ -1,10 +0,0 @@ -## Visual Studio 2022 backend - -As Visual Studio 2022 is released recently, it's time to support the -new version in Meson. This mainly includes the new "v143" platform tools. - -The usage is similar to other backends. For example -```meson -meson setup builddir --backend=vs2022 -``` -will configure "builddir" for projects compatible with Visual Studio 2022. diff --git a/docs/markdown/snippets/windows_custom_targets.md b/docs/markdown/snippets/windows_custom_targets.md deleted file mode 100644 index cbc2f9d..0000000 --- a/docs/markdown/snippets/windows_custom_targets.md +++ /dev/null @@ -1,22 +0,0 @@ -## Windows.compile_resources CustomTarget - -Previously the Windows module only accepted CustomTargets with one output, it -now accepts them with more than one output, and creates a windows resource -target for each output. Additionally it now accepts indexes of CustomTargets - -```meson - -ct = custom_target( - 'multiple', - output : ['resource', 'another resource'], - ... -) - -ct2 = custom_target( - 'slice', - output : ['resource', 'not a resource'], - ... -) - -resources = windows.compile_resources(ct, ct2[0]) -``` diff --git a/docs/meson.build b/docs/meson.build index 53d4392..00e90ed 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -63,6 +63,22 @@ refman_json = custom_target( ], ) +refman_man = custom_target( + 'gen_refman_man', + build_by_default: true, + input: refman_binary, + output: 'meson-reference.3', + command: [ + genrefman, + '-l', 'pickle', + '-g', 'man', + '-i', '@INPUT@', + '-o', '@OUTPUT@', + '--force-color', + '--no-modules', + ], +) + test('validate_docs', find_program('./jsonvalidator.py'), args: [refman_json]) hotdoc_prog = find_program('hotdoc', version: '>=0.13.7') diff --git a/docs/refman/generatorbase.py b/docs/refman/generatorbase.py index e93166f..08ce492 100644 --- a/docs/refman/generatorbase.py +++ b/docs/refman/generatorbase.py @@ -43,6 +43,11 @@ class GeneratorBase(metaclass=ABCMeta): return f'0_{fn.name}' return sorted([x for x in raw if not x.hidden], key=key_fn) + @staticmethod + def _extract_meson_version() -> str: + from mesonbuild.coredata import version + return version + @property def functions(self) -> T.List[Function]: return GeneratorBase.sorted_and_filtered(self.manual.functions) diff --git a/docs/refman/generatorjson.py b/docs/refman/generatorjson.py index f5164d4..9f92342 100644 --- a/docs/refman/generatorjson.py +++ b/docs/refman/generatorjson.py @@ -92,13 +92,6 @@ class GeneratorJSON(GeneratorBase): 'methods': {x.name: self._generate_function(x) for x in self.sorted_and_filtered(obj.methods)}, } - def _extract_meson_version(self) -> str: - # Hack around python relative imports to get to the Meson version - import sys - sys.path.append(Path(__file__).resolve().parents[2].as_posix()) - from mesonbuild.coredata import version - return version - def generate(self) -> None: data: J.Root = { 'version_major': J.VERSION_MAJOR, diff --git a/docs/refman/generatorman.py b/docs/refman/generatorman.py new file mode 100644 index 0000000..dfd211f --- /dev/null +++ b/docs/refman/generatorman.py @@ -0,0 +1,382 @@ +import re +from pathlib import Path + +from .generatorbase import GeneratorBase +from .model import ( + ReferenceManual, + Function, + Object, + PosArg, + VarArgs, + Kwarg, +) + +import typing as T + + +class ManPage: + def __init__(self, path: Path): + self.path = path + self.text = "" + + def reset_font(self) -> None: + self.text += ".P\n" + + def title(self, name: str, section: int) -> None: + import datetime + + date = datetime.date.today() + self.reset_font() + self.text += f'.TH "{name}" "{section}" "{date}"\n' + + def section(self, name: str) -> None: + self.reset_font() + self.text += f".SH {name}\n" + + def subsection(self, name: str) -> None: + self.reset_font() + self.text += f".SS {name}\n" + + def par(self, text: str) -> None: + self.reset_font() + self.text += f"{text}\n" + + def indent(self, amount: int = 4) -> None: + self.text += f".RS {amount}\n" + + def unindent(self) -> None: + self.text += ".RE\n" + + def br(self) -> None: + self.text += ".br\n" + + def nl(self) -> None: + self.text += "\n" + + def line(self, text: str) -> None: + if text and text[0] in [".", "'"]: + self.text += "\\" + self.text += f"{text}\n" + + def inline(self, text: str) -> None: + self.text += f"{text}" + + def write(self) -> None: + self.path.write_text(self.text, encoding="utf-8") + + @staticmethod + def bold(text: str) -> str: + return f"\\fB{text}\\fR" + + @staticmethod + def italic(text: str) -> str: + return f"\\fI{text}\\fR" + + +class GeneratorMan(GeneratorBase): + def __init__( + self, manual: ReferenceManual, out: Path, enable_modules: bool + ) -> None: + super().__init__(manual) + self.out = out + self.enable_modules = enable_modules + self.links: T.List[str] = [] + + def generate_description(self, page: ManPage, desc: str) -> None: + def italicise(match: T.Match[str]) -> str: + v = match.group(1) + if v[0] == "@": + v = v[1:] + + return ManPage.italic(v) + + desc = re.sub(re.compile(r"\[\[(.*?)\]\]", re.DOTALL), italicise, desc) + + def linkify(match: T.Match[str]) -> str: + replacement = ManPage.italic(match.group(1)) + + if match.group(2)[0] != "#": + if match.group(2) in self.links: + num = self.links.index(match.group(2)) + else: + self.links.append(match.group(2)) + num = len(self.links) + + replacement += f"[{num}]" + + return replacement + + desc = re.sub(re.compile(r"\[(.*?)\]\((.*?)\)", re.DOTALL), linkify, desc) + + def bold(match: T.Match[str]) -> str: + return ManPage.bold(match.group(1)) + + desc = re.sub(re.compile(r"\*(.*?)\*"), bold, desc) + + isCode = False + for chunk in desc.split("```"): + if isCode: + page.indent() + lines = chunk.strip().split("\n") + if lines[0] == "meson": + lines = lines[1:] + + for line in lines: + page.line(line) + page.br() + page.unindent() + else: + inList = False + for line in chunk.strip().split("\n"): + if len(line) == 0: + page.nl() + if inList: + page.nl() + inList = False + elif line[0:2] in ["- ", "* "]: + if inList: + page.nl() + page.br() + else: + inList = True + + page.inline(line.strip() + " ") + elif inList and line[0] == " ": + page.inline(line.strip() + " ") + else: + inList = False + page.line(line) + + if inList: + page.nl() + + isCode = not isCode + + def function_name(self, f: Function, o: Object = None) -> str: + name = "" + if o is not None: + name += f"{o.name}." + + name += f.name + return name + + def generate_function_signature( + self, page: ManPage, f: Function, o: Object = None + ) -> None: + args = [] + + if f.posargs: + args += [arg.name for arg in f.posargs] + + if f.varargs: + args += [f.varargs.name + "..."] + + if f.optargs: + args += [f"[{arg.name}]" for arg in f.optargs] + + for kwarg in self.sorted_and_filtered(list(f.kwargs.values())): + kw = kwarg.name + ":" + if kwarg.default: + kw += " " + ManPage.bold(kwarg.default) + args += [kw] + + ret = ManPage.italic(f.returns.raw) + " " + + prefix = f"{ret}{self.function_name(f, o)}(" + sig = ", ".join(args) + suffix = ")" + + if len(prefix) + len(sig) + len(suffix) > 70: + page.line(prefix) + page.br() + page.indent() + for arg in args: + page.line(arg + ",") + page.br() + page.unindent() + page.line(suffix) + else: + page.line(prefix + sig + suffix) + + def base_info( + self, x: T.Union[PosArg, VarArgs, Kwarg, Function, Object] + ) -> T.List[str]: + info = [] + if x.deprecated: + info += [ManPage.bold("deprecated") + f" since {x.deprecated}"] + if x.since: + info += [f"since {x.since}"] + + return info + + def generate_function_arg( + self, + page: ManPage, + arg: T.Union[PosArg, VarArgs, Kwarg], + isOptarg: bool = False, + ) -> None: + required = ( + arg.required + if isinstance(arg, Kwarg) + else not isOptarg and not isinstance(arg, VarArgs) + ) + + page.line(ManPage.bold(arg.name)) + + info = [ManPage.italic(arg.type.raw)] + + if required: + info += [ManPage.bold("required")] + if isinstance(arg, (PosArg, Kwarg)) and arg.default: + info += [f"default: {arg.default}"] + if isinstance(arg, VarArgs): + mn = 0 if arg.min_varargs < 0 else arg.min_varargs + mx = "N" if arg.max_varargs < 0 else arg.max_varargs + info += [f"{mn}...{mx} times"] + + info += self.base_info(arg) + + page.line(", ".join(info)) + + page.br() + page.indent(2) + self.generate_description(page, arg.description.strip()) + page.unindent() + page.nl() + + def generate_function_argument_section( + self, + page: ManPage, + name: str, + args: T.Sequence[T.Union[PosArg, VarArgs, Kwarg]], + isOptarg: bool = False, + ) -> None: + if not args: + return + + page.line(ManPage.bold(name)) + page.indent() + for arg in args: + self.generate_function_arg(page, arg, isOptarg) + page.unindent() + + def generate_sub_sub_section( + self, page: ManPage, name: str, text: T.List[str], process: bool = True + ) -> None: + page.line(ManPage.bold(name)) + page.indent() + if process: + for line in text: + self.generate_description(page, line.strip()) + else: + page.line("\n\n".join([line.strip() for line in text])) + page.unindent() + + def generate_function(self, page: ManPage, f: Function, obj: Object = None) -> None: + page.subsection(self.function_name(f, obj) + "()") + page.indent(0) + + page.line(ManPage.bold("SYNOPSIS")) + page.indent() + self.generate_function_signature(page, f, obj) + + info = self.base_info(f) + if info: + page.nl() + page.line(", ".join(info)) + page.unindent() + page.nl() + + self.generate_sub_sub_section(page, "DESCRIPTION", [f.description]) + page.nl() + + self.generate_function_argument_section(page, "POSARGS", f.posargs) + if f.varargs: + self.generate_function_argument_section(page, "VARARGS", [f.varargs]) + self.generate_function_argument_section(page, "OPTARGS", f.optargs, True) + self.generate_function_argument_section( + page, "KWARGS", self.sorted_and_filtered(list(f.kwargs.values())) + ) + + if f.notes: + self.generate_sub_sub_section(page, "NOTES", f.notes) + if f.warnings: + self.generate_sub_sub_section(page, "WARNINGS", f.warnings) + if f.example: + self.generate_sub_sub_section(page, "EXAMPLE", [f.example]) + + page.unindent() + + def generate_object(self, page: ManPage, obj: Object) -> None: + page.subsection(obj.name) + page.indent(2) + + info = self.base_info(obj) + if info: + page.line(", ".join(info)) + page.br() + + if obj.extends: + page.line(ManPage.bold("extends: ") + obj.extends) + page.br() + + ret = [x.name for x in self.sorted_and_filtered(obj.returned_by)] + if ret: + page.line(ManPage.bold("returned_by: ") + ", ".join(ret)) + page.br() + + ext = [x.name for x in self.sorted_and_filtered(obj.extended_by)] + if ext: + page.line(ManPage.bold("extended_by: ") + ", ".join(ext)) + page.br() + + page.nl() + + self.generate_description(page, obj.description.strip()) + page.nl() + + if obj.notes: + self.generate_sub_sub_section(page, "NOTES", obj.notes) + if obj.warnings: + self.generate_sub_sub_section(page, "WARNINGS", obj.warnings) + if obj.example: + self.generate_sub_sub_section(page, "EXAMPLE", [obj.example]) + + page.unindent() + + def generate(self) -> None: + page = ManPage(self.out) + + page.title("meson-reference", 3) + + page.section("NAME") + page.par( + f"meson-reference v{self._extract_meson_version()}" + + " - a reference for meson functions and objects" + ) + + page.section("DESCRIPTION") + self.generate_description( + page, + """This manual is divided into two sections, *FUNCTIONS* and *OBJECTS*. *FUNCTIONS* contains a reference for all meson functions and methods. Methods are denoted by [[object_name]].[[method_name]](). *OBJECTS* contains additional information about each object.""", + ) + + page.section("FUNCTIONS") + for f in self.sorted_and_filtered(self.functions): + self.generate_function(page, f) + + for obj in self.sorted_and_filtered(self.objects): + for f in self.sorted_and_filtered(obj.methods): + self.generate_function(page, f, obj) + + page.section("OBJECTS") + for obj in self.sorted_and_filtered(self.objects): + self.generate_object(page, obj) + + page.section("SEE ALSO") + for i in range(len(self.links)): + link = self.links[i] + page.line(f"[{i + 1}] {link}") + page.br() + + page.write() diff --git a/docs/refman/main.py b/docs/refman/main.py index 5bc40f7..e2654c9 100644 --- a/docs/refman/main.py +++ b/docs/refman/main.py @@ -27,13 +27,14 @@ from .generatorjson import GeneratorJSON from .generatorprint import GeneratorPrint from .generatorpickle import GeneratorPickle from .generatormd import GeneratorMD +from .generatorman import GeneratorMan meson_root = Path(__file__).absolute().parents[2] def main() -> int: parser = argparse.ArgumentParser(description='Meson reference manual generator') parser.add_argument('-l', '--loader', type=str, default='yaml', choices=['yaml', 'pickle'], help='Information loader backend') - parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md', 'json'], required=True, help='Generator backend') + parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md', 'json', 'man'], required=True, help='Generator backend') parser.add_argument('-s', '--sitemap', type=Path, default=meson_root / 'docs' / 'sitemap.txt', help='Path to the input sitemap.txt') parser.add_argument('-o', '--out', type=Path, required=True, help='Output directory for generated files') parser.add_argument('-i', '--input', type=Path, default=meson_root / 'docs' / 'yaml', help='Input path for the selected loader') @@ -59,6 +60,7 @@ def main() -> int: 'pickle': lambda: GeneratorPickle(refMan, args.out), 'md': lambda: GeneratorMD(refMan, args.out, args.sitemap, args.link_defs, not args.no_modules), 'json': lambda: GeneratorJSON(refMan, args.out, not args.no_modules), + 'man': lambda: GeneratorMan(refMan, args.out, not args.no_modules), } generator = generators[args.generator]() diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 15564d7..82e0a7b 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -53,7 +53,6 @@ index.md Qt4-module.md Qt5-module.md Qt6-module.md - RPM-module.md Rust-module.md Simd-module.md SourceSet-module.md @@ -86,8 +85,8 @@ index.md Using-wraptool.md Wrap-best-practices-and-tips.md Shipping-prebuilt-binaries-as-wraps.md - fallback-wraptool.md Release-notes.md + Release-notes-for-0.61.0.md Release-notes-for-0.60.0.md Release-notes-for-0.59.0.md Release-notes-for-0.58.0.md diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html index b8b6179..c518de5 100644 --- a/docs/theme/extra/templates/navbar_links.html +++ b/docs/theme/extra/templates/navbar_links.html @@ -23,7 +23,6 @@ ("Qt4-module.html","Qt4"), \ ("Qt5-module.html","Qt5"), \ ("Qt6-module.html","Qt6"), \ - ("RPM-module.html","RPM"), \ ("Rust-module.html","Rust"), \ ("Simd-module.html","Simd"), \ ("SourceSet-module.html","SourceSet"), \ diff --git a/docs/yaml/builtins/meson.yaml b/docs/yaml/builtins/meson.yaml index 4f2d24d..00ade2e 100644 --- a/docs/yaml/builtins/meson.yaml +++ b/docs/yaml/builtins/meson.yaml @@ -34,18 +34,17 @@ methods: posargs: script_name: - type: str | file | external_program | exe | custom_tgt | custom_idx + type: str | file | external_program description: | The script to execute. - *(since 0.55.0)* The output of [[configure_file]], [[files]], and [[find_program]] - as well as strings are accepted. + *(since 0.55.0)* The output of [[find_program]] as well as strings are accepted. *(since 0.57.0)* [[@file]] objects and the output of [[configure_file]] may be used. varargs: name: arg - type: str | file | external_program | exe | custom_tgt | custom_idx + type: str | file | external_program since: 0.49.0 description: | Additional arguments @@ -53,8 +52,6 @@ methods: *(since 0.55.0)* The output of [[configure_file]], [[files]], and [[find_program]] as well as strings are accepted. - *(since 0.57.0)* [[@file]] objects and the output of [[configure_file]] may be used. - - name: add_install_script returns: void description: | @@ -89,8 +86,26 @@ methods: shell would. If your script uses Python, `shlex.split()` is the easiest correct way to do this. - posargs_inherit: meson.add_dist_script - varargs_inherit: meson.add_dist_script + posargs: + script_name: + type: str | file | external_program | exe | custom_tgt | custom_idx + description: | + The script to execute. + + *(since 0.55.0)* The output of [[find_program]], [[executable]], + [[custom_target]], as well as strings are accepted. + + *(since 0.57.0)* [[@file]] objects and the output of [[configure_file]] may be used. + + varargs: + name: arg + type: str | file | external_program | exe | custom_tgt | custom_idx + since: 0.49.0 + description: | + Additional arguments + + *(since 0.55.0)* The output of [[find_program]], [[executable]], + [[custom_target]], as well as strings are accepted. kwargs: skip_if_destdir: diff --git a/docs/yaml/objects/cfg_data.yaml b/docs/yaml/objects/cfg_data.yaml index 9a66b73..5cc4b84 100644 --- a/docs/yaml/objects/cfg_data.yaml +++ b/docs/yaml/objects/cfg_data.yaml @@ -41,8 +41,17 @@ methods: type: str description: The name of the variable to set value: - type: bool - description: The value to set as either `1` or `0` + type: bool | int + description: | + The value to set as either `1` or `0` + + Passing numbers was never intended to work, and since 0.62 it has been + deprecated. It will be removed in a future version of Meson. If you + need to pass numbers use the `.set` method. + warnings: + - numeric values < 0 have the surprising behavior of being converted to + [[true]], values > 1 have the more expected but unintentional behavior of + being interpretered as [[true]]. kwargs_inherit: cfg_data.set diff --git a/docs/yaml/objects/dep.yaml b/docs/yaml/objects/dep.yaml index 23092c2..d847690 100644 --- a/docs/yaml/objects/dep.yaml +++ b/docs/yaml/objects/dep.yaml @@ -205,7 +205,7 @@ methods: default_value: type: str - description: The davault value to return when the variable does not exist + description: The default value to return when the variable does not exist pkgconfig_define: type: list[str] diff --git a/docs/yaml/objects/feature.yaml b/docs/yaml/objects/feature.yaml index 5b451e5..b6a754b 100644 --- a/docs/yaml/objects/feature.yaml +++ b/docs/yaml/objects/feature.yaml @@ -54,7 +54,7 @@ methods: if get_option('directx').require(host_machine.system() == 'windows', error_message: 'DirectX only available on Windows').allowed() then src += ['directx.c'] - config.set10('HAVE_DIRECTX', 1) + config.set10('HAVE_DIRECTX', true) endif ``` diff --git a/man/meson.1 b/man/meson.1 index b6fbf69..0443d6e 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "October 2021" "meson 0.60.0" "User Commands" +.TH MESON "1" "January 2022" "meson 0.61.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4b368e7..e43c8c7 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -13,6 +13,7 @@ # limitations under the License. from collections import OrderedDict +from dataclasses import dataclass, InitVar from functools import lru_cache from itertools import chain from pathlib import Path @@ -61,11 +62,11 @@ if T.TYPE_CHECKING: # Assembly files cannot be unitified and neither can LLVM IR files LANGS_CANT_UNITY = ('d', 'fortran', 'vala') +@dataclass(eq=False) class RegenInfo: - def __init__(self, source_dir: str, build_dir: str, depfiles: T.List[str]): - self.source_dir = source_dir - self.build_dir = build_dir - self.depfiles = depfiles + source_dir: str + build_dir: str + depfiles: T.List[str] class TestProtocol(enum.Enum): @@ -97,28 +98,30 @@ class TestProtocol(enum.Enum): return 'tap' +@dataclass(eq=False) class CleanTrees: ''' Directories outputted by custom targets that have to be manually cleaned because on Linux `ninja clean` only deletes empty directories. ''' - def __init__(self, build_dir: str, trees: T.List[str]): - self.build_dir = build_dir - self.trees = trees + build_dir: str + trees: T.List[str] +@dataclass(eq=False) class InstallData: - def __init__(self, source_dir: str, build_dir: str, prefix: str, libdir: str, - strip_bin: T.List[str], install_umask: T.Union[str, int], - mesonintrospect: T.List[str], version: str, - is_cross_build: bool): - # TODO: in python 3.8 or with typing_Extensions install_umask could be: - # `T.Union[T.Literal['preserve'], int]`, which would be more accurate. - self.source_dir = source_dir - self.build_dir = build_dir - self.prefix = prefix - self.libdir = libdir - self.strip_bin = strip_bin - self.install_umask = install_umask + source_dir: str + build_dir: str + prefix: str + libdir: str + strip_bin: T.List[str] + # TODO: in python 3.8 or with typing_Extensions this could be: + # `T.Union[T.Literal['preserve'], int]`, which would be more accurate. + install_umask: T.Union[str, int] + mesonintrospect: T.List[str] + version: str + is_cross_build: bool + + def __post_init__(self) -> None: self.targets: T.List[TargetInstallData] = [] self.headers: T.List[InstallDataBase] = [] self.man: T.List[InstallDataBase] = [] @@ -127,59 +130,52 @@ class InstallData: self.symlinks: T.List[InstallSymlinkData] = [] self.install_scripts: T.List[ExecutableSerialisation] = [] self.install_subdirs: T.List[SubdirInstallData] = [] - self.mesonintrospect = mesonintrospect - self.version = version - self.is_cross_build = is_cross_build +@dataclass(eq=False) class TargetInstallData: - + fname: str + outdir: str + outdir_name: InitVar[str] + aliases: T.Dict[str, str] + strip: bool + install_name_mappings: T.Mapping[str, str] + rpath_dirs_to_remove: T.Set[bytes] + install_rpath: str # TODO: install_mode should just always be a FileMode object + install_mode: T.Optional['FileMode'] + subproject: str + optional: bool = False + tag: T.Optional[str] = None - def __init__(self, fname: str, outdir: str, outdir_name: str, aliases: T.Dict[str, str], - strip: bool, install_name_mappings: T.Mapping[str, str], rpath_dirs_to_remove: T.Set[bytes], - install_rpath: str, install_mode: T.Optional['FileMode'], - subproject: str, optional: bool = False, tag: T.Optional[str] = None): - self.fname = fname - self.outdir = outdir - self.out_name = os.path.join(outdir_name, os.path.basename(fname)) - self.aliases = aliases - self.strip = strip - self.install_name_mappings = install_name_mappings - self.rpath_dirs_to_remove = rpath_dirs_to_remove - self.install_rpath = install_rpath - self.install_mode = install_mode - self.subproject = subproject - self.optional = optional - self.tag = tag + def __post_init__(self, outdir_name: str) -> None: + self.out_name = os.path.join(outdir_name, os.path.basename(self.fname)) +@dataclass(eq=False) class InstallEmptyDir: - def __init__(self, path: str, install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None): - self.path = path - self.install_mode = install_mode - self.subproject = subproject - self.tag = tag + path: str + install_mode: 'FileMode' + subproject: str + tag: T.Optional[str] = None +@dataclass(eq=False) class InstallDataBase: - def __init__(self, path: str, install_path: str, install_path_name: str, - install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None, - data_type: T.Optional[str] = None): - self.path = path - self.install_path = install_path - self.install_path_name = install_path_name - self.install_mode = install_mode - self.subproject = subproject - self.tag = tag - self.data_type = data_type - + path: str + install_path: str + install_path_name: str + install_mode: 'FileMode' + subproject: str + tag: T.Optional[str] = None + data_type: T.Optional[str] = None + +@dataclass(eq=False) class InstallSymlinkData: - def __init__(self, target: str, name: str, install_path: str, - subproject: str, tag: T.Optional[str] = None): - self.target = target - self.name = name - self.install_path = install_path - self.subproject = subproject - self.tag = tag + target: str + name: str + install_path: str + subproject: str + tag: T.Optional[str] = None +# cannot use dataclass here because "exclude" is out of order class SubdirInstallData(InstallDataBase): def __init__(self, path: str, install_path: str, install_path_name: str, install_mode: 'FileMode', exclude: T.Tuple[T.Set[str], T.Set[str]], @@ -187,64 +183,53 @@ class SubdirInstallData(InstallDataBase): super().__init__(path, install_path, install_path_name, install_mode, subproject, tag, data_type) self.exclude = exclude +@dataclass(eq=False) class ExecutableSerialisation: # XXX: should capture and feed default to False, instead of None? - def __init__(self, cmd_args: T.List[str], - env: T.Optional[build.EnvironmentVariables] = None, - exe_wrapper: T.Optional['programs.ExternalProgram'] = None, - workdir: T.Optional[str] = None, - extra_paths: T.Optional[T.List] = None, - capture: T.Optional[bool] = None, - feed: T.Optional[bool] = None, - tag: T.Optional[str] = None, - verbose: bool = False, - ) -> None: - self.cmd_args = cmd_args - self.env = env - if exe_wrapper is not None: - assert isinstance(exe_wrapper, programs.ExternalProgram) - self.exe_wrapper = exe_wrapper - self.workdir = workdir - self.extra_paths = extra_paths - self.capture = capture - self.feed = feed + cmd_args: T.List[str] + env: T.Optional[build.EnvironmentVariables] = None + exe_wrapper: T.Optional['programs.ExternalProgram'] = None + workdir: T.Optional[str] = None + extra_paths: T.Optional[T.List] = None + capture: T.Optional[bool] = None + feed: T.Optional[bool] = None + tag: T.Optional[str] = None + verbose: bool = False + + def __post_init__(self) -> None: + if self.exe_wrapper is not None: + assert isinstance(self.exe_wrapper, programs.ExternalProgram) self.pickled = False self.skip_if_destdir = False - self.verbose = verbose self.subproject = '' - self.tag = tag +@dataclass(eq=False) class TestSerialisation: - def __init__(self, name: str, project: str, suite: T.List[str], fname: T.List[str], - is_cross_built: bool, exe_wrapper: T.Optional[programs.ExternalProgram], - needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str], - env: build.EnvironmentVariables, should_fail: bool, - timeout: T.Optional[int], workdir: T.Optional[str], - extra_paths: T.List[str], protocol: TestProtocol, priority: int, - cmd_is_built: bool, depends: T.List[str], version: str): - self.name = name - self.project_name = project - self.suite = suite - self.fname = fname - self.is_cross_built = is_cross_built - if exe_wrapper is not None: - assert isinstance(exe_wrapper, programs.ExternalProgram) - self.exe_wrapper = exe_wrapper - self.is_parallel = is_parallel - self.cmd_args = cmd_args - self.env = env - self.should_fail = should_fail - self.timeout = timeout - self.workdir = workdir - self.extra_paths = extra_paths - self.protocol = protocol - self.priority = priority - self.needs_exe_wrapper = needs_exe_wrapper - self.cmd_is_built = cmd_is_built - self.depends = depends - self.version = version + name: str + project_name: str + suite: T.List[str] + fname: T.List[str] + is_cross_built: bool + exe_wrapper: T.Optional[programs.ExternalProgram] + needs_exe_wrapper: bool + is_parallel: bool + cmd_args: T.List[str] + env: build.EnvironmentVariables + should_fail: bool + timeout: T.Optional[int] + workdir: T.Optional[str] + extra_paths: T.List[str] + protocol: TestProtocol + priority: int + cmd_is_built: bool + depends: T.List[str] + version: str + + def __post_init__(self) -> None: + if self.exe_wrapper is not None: + assert isinstance(self.exe_wrapper, programs.ExternalProgram) def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']: @@ -708,7 +693,6 @@ class Backend: return False def get_external_rpath_dirs(self, target: build.BuildTarget) -> T.Set[str]: - dirs: T.Set[str] = set() args: T.List[str] = [] for lang in LANGUAGES_USING_LDFLAGS: try: @@ -719,6 +703,11 @@ class Backend: args.extend(e) except Exception: pass + return self.get_rpath_dirs_from_link_args(args) + + @staticmethod + def get_rpath_dirs_from_link_args(args: T.List[str]) -> T.Set[str]: + dirs: T.Set[str] = set() # Match rpath formats: # -Wl,-rpath= # -Wl,-rpath, @@ -751,37 +740,38 @@ class Backend: return dirs @lru_cache(maxsize=None) - def rpaths_for_bundled_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]': - paths: T.List[str] = [] + def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]': + paths: OrderedSet[str] = OrderedSet() for dep in target.external_deps: if not isinstance(dep, (dependencies.ExternalLibrary, dependencies.PkgConfigDependency)): continue - la = dep.link_args - if len(la) != 1 or not os.path.isabs(la[0]): - continue - # The only link argument is an absolute path to a library file. - libpath = la[0] - libdir = os.path.dirname(libpath) - if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): - # No point in adding system paths. - continue - # Don't remove rpaths specified in LDFLAGS. - if libdir in self.get_external_rpath_dirs(target): - continue - # Windows doesn't support rpaths, but we use this function to - # emulate rpaths by setting PATH, so also accept DLLs here - if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']: - continue - if libdir.startswith(self.environment.get_source_dir()): - rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:] - assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute' - paths.append(os.path.join(self.build_to_src, rel_to_src)) - else: - paths.append(libdir) + for libpath in dep.link_args: + # For all link args that are absolute paths to a library file, add RPATH args + if not os.path.isabs(libpath): + continue + libdir = os.path.dirname(libpath) + if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): + # No point in adding system paths. + continue + # Don't remove rpaths specified in LDFLAGS. + if libdir in self.get_external_rpath_dirs(target): + continue + # Windows doesn't support rpaths, but we use this function to + # emulate rpaths by setting PATH, so also accept DLLs here + if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']: + continue + if libdir.startswith(self.environment.get_source_dir()): + rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:] + assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute' + paths.add(os.path.join(self.build_to_src, rel_to_src)) + else: + paths.add(libdir) + # Don't remove rpaths specified by the dependency + paths.difference_update(self.get_rpath_dirs_from_link_args(dep.link_args)) for i in chain(target.link_targets, target.link_whole_targets): if isinstance(i, build.BuildTarget): - paths.extend(self.rpaths_for_bundled_shared_libraries(i, exclude_system)) - return paths + paths.update(self.rpaths_for_non_system_absolute_shared_libraries(i, exclude_system)) + return list(paths) # This may take other types def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex] @@ -794,7 +784,7 @@ class Backend: result = OrderedSet() result.add('meson-out') if isinstance(target, build.BuildTarget): - result.update(self.rpaths_for_bundled_shared_libraries(target)) + result.update(self.rpaths_for_non_system_absolute_shared_libraries(target)) target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result]) return tuple(result) @@ -1068,11 +1058,11 @@ class Backend: tests. """ result: T.Set[str] = set() - prospectives: T.Set[build.Target] = set() + prospectives: T.Set[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = set() if isinstance(target, build.BuildTarget): prospectives.update(target.get_transitive_link_deps()) # External deps - for deppath in self.rpaths_for_bundled_shared_libraries(target, exclude_system=False): + for deppath in self.rpaths_for_non_system_absolute_shared_libraries(target, exclude_system=False): result.add(os.path.normpath(os.path.join(self.environment.get_build_dir(), deppath))) for bdep in extra_bdeps: prospectives.add(bdep) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b6621c9..ed1fe8c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1004,7 +1004,8 @@ class NinjaBackend(backends.Backend): extra_bdeps=target.get_transitive_build_target_deps(), capture=ofilenames[0] if target.capture else None, feed=srcs[0] if target.feed else None, - env=target.env) + env=target.env, + verbose=target.console) if reason: cmd_type = f' (wrapped by meson {reason})' else: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7c873b2..a392f98 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -13,6 +13,7 @@ # limitations under the License. from collections import OrderedDict +from dataclasses import dataclass, field from functools import lru_cache import copy import hashlib @@ -130,22 +131,19 @@ def get_target_macos_dylib_install_name(ld) -> str: class InvalidArguments(MesonException): pass +@dataclass(eq=False) class DependencyOverride(HoldableObject): - def __init__(self, dep: dependencies.Dependency, node: 'BaseNode', explicit: bool = True): - self.dep = dep - self.node = node - self.explicit = explicit + dep: dependencies.Dependency + node: 'BaseNode' + explicit: bool = True +@dataclass(eq=False) class Headers(HoldableObject): - - def __init__(self, sources: T.List[File], install_subdir: T.Optional[str], - custom_install_dir: T.Optional[str], custom_install_mode: 'FileMode', - subproject: str): - self.sources = sources - self.install_subdir = install_subdir - self.custom_install_dir = custom_install_dir - self.custom_install_mode = custom_install_mode - self.subproject = subproject + sources: T.List[File] + install_subdir: T.Optional[str] + custom_install_dir: T.Optional[str] + custom_install_mode: 'FileMode' + subproject: str # TODO: we really don't need any of these methods, but they're preserved to # keep APIs relying on them working. @@ -166,16 +164,13 @@ class Headers(HoldableObject): return self.custom_install_mode +@dataclass(eq=False) class Man(HoldableObject): - - def __init__(self, sources: T.List[File], custom_install_dir: T.Optional[str], - custom_install_mode: 'FileMode', subproject: str, - locale: T.Optional[str]): - self.sources = sources - self.custom_install_dir = custom_install_dir - self.custom_install_mode = custom_install_mode - self.subproject = subproject - self.locale = locale + sources: T.List[File] + custom_install_dir: T.Optional[str] + custom_install_mode: 'FileMode' + subproject: str + locale: T.Optional[str] def get_custom_install_dir(self) -> T.Optional[str]: return self.custom_install_dir @@ -187,40 +182,30 @@ class Man(HoldableObject): return self.sources +@dataclass(eq=False) class EmptyDir(HoldableObject): - - def __init__(self, path: str, install_mode: 'FileMode', subproject: str, - install_tag: T.Optional[str] = None): - self.path = path - self.install_mode = install_mode - self.subproject = subproject - self.install_tag = install_tag + path: str + install_mode: 'FileMode' + subproject: str + install_tag: T.Optional[str] = None +@dataclass(eq=False) class InstallDir(HoldableObject): + source_subdir: str + installable_subdir: str + install_dir: str + install_mode: 'FileMode' + exclude: T.Tuple[T.Set[str], T.Set[str]] + strip_directory: bool + subproject: str + from_source_dir: bool = True + install_tag: T.Optional[str] = None - def __init__(self, source_subdir: str, installable_subdir: str, install_dir: str, - install_mode: 'FileMode', - exclude: T.Tuple[T.Set[str], T.Set[str]], - strip_directory: bool, subproject: str, - from_source_dir: bool = True, - install_tag: T.Optional[str] = None): - self.source_subdir = source_subdir - self.installable_subdir = installable_subdir - self.install_dir = install_dir - self.install_mode = install_mode - self.exclude = exclude - self.strip_directory = strip_directory - self.from_source_dir = from_source_dir - self.subproject = subproject - self.install_tag = install_tag - - +@dataclass(eq=False) class DepManifest: - - def __init__(self, version: str, license: T.List[str]): - self.version = version - self.license = license + version: str + license: T.List[str] def to_json(self) -> T.Dict[str, T.Union[str, T.List[str]]]: return { @@ -229,6 +214,7 @@ class DepManifest: } +# literally everything isn't dataclass stuff class Build: """A class that holds the status of one build including all dependencies and so on. @@ -363,18 +349,17 @@ class Build: return link_args.get(compiler.get_language(), []) +@dataclass(eq=False) class IncludeDirs(HoldableObject): """Internal representation of an include_directories call.""" - def __init__(self, curdir: str, incdirs: T.List[str], is_system: bool, extra_build_dirs: T.Optional[T.List[str]] = None): - self.curdir = curdir - self.incdirs = incdirs - self.is_system = is_system - - # Interpreter has validated that all given directories - # actually exist. - self.extra_build_dirs: T.List[str] = extra_build_dirs or [] + curdir: str + incdirs: T.List[str] + is_system: bool + # Interpreter has validated that all given directories + # actually exist. + extra_build_dirs: T.List[str] = field(default_factory=list) def __repr__(self) -> str: r = '<{} {}/{}>' @@ -404,21 +389,18 @@ class IncludeDirs(HoldableObject): strlist.append(os.path.join(builddir, self.curdir, idir)) return strlist +@dataclass(eq=False) class ExtractedObjects(HoldableObject): ''' Holds a list of sources for which the objects must be extracted ''' - def __init__(self, target: 'BuildTarget', - srclist: T.Optional[T.List[File]] = None, - genlist: T.Optional[T.List['GeneratedTypes']] = None, - objlist: T.Optional[T.List[T.Union[str, 'File', 'ExtractedObjects']]] = None, - recursive: bool = True): - self.target = target - self.recursive = recursive - self.srclist: T.List[File] = srclist if srclist is not None else [] - self.genlist: T.List['GeneratedTypes'] = genlist if genlist is not None else [] - self.objlist: T.Optional[T.List[T.Union[str, 'File', 'ExtractedObjects']]] = \ - objlist if objlist is not None else [] + target: 'BuildTarget' + srclist: T.List[File] = field(default_factory=list) + genlist: T.List['GeneratedTypes'] = field(default_factory=list) + objlist: T.List[T.Union[str, 'File', 'ExtractedObjects']] = field(default_factory=list) + recursive: bool = True + + def __post_init__(self) -> None: if self.target.is_unity: self.check_unity_compatible() @@ -523,23 +505,25 @@ class EnvironmentVariables(HoldableObject): env[name] = method(env, name, values, separator) return env +@dataclass(eq=False) class Target(HoldableObject): # TODO: should Target be an abc.ABCMeta? - def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice): - if has_path_sep(name): + name: str + subdir: str + subproject: str + build_by_default: bool + for_machine: MachineChoice + + def __post_init__(self) -> None: + if has_path_sep(self.name): # Fix failing test 53 when this becomes an error. mlog.warning(textwrap.dedent(f'''\ - Target "{name}" has a path separator in its name. + Target "{self.name}" has a path separator in its name. This is not supported, it can cause unexpected failures and will become a hard error in the future.\ ''')) - self.name = name - self.subdir = subdir - self.subproject = subproject - self.build_by_default = build_by_default - self.for_machine = for_machine self.install = False self.build_always_stale = False self.option_overrides_base: T.Dict[OptionKey, str] = {} @@ -548,6 +532,7 @@ class Target(HoldableObject): if not hasattr(self, 'typename'): raise RuntimeError(f'Target type is not set for target class "{type(self).__name__}". This is a bug') + # dataclass comparators? def __lt__(self, other: object) -> bool: if not isinstance(other, Target): return NotImplemented @@ -1016,11 +1001,11 @@ class BuildTarget(Target): return ExtractedObjects(self, self.sources, self.generated, self.objects, recursive) - def get_all_link_deps(self) -> 'ImmutableListProtocol[Target]': + def get_all_link_deps(self) -> 'ImmutableListProtocol[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]': return self.get_transitive_link_deps() @lru_cache(maxsize=None) - def get_transitive_link_deps(self) -> 'ImmutableListProtocol[Target]': + def get_transitive_link_deps(self) -> 'ImmutableListProtocol[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]': result: T.List[Target] = [] for i in self.link_targets: result += i.get_all_link_deps() @@ -1619,6 +1604,7 @@ class Generator(HoldableObject): def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], arguments: T.List[str], output: T.List[str], + # how2dataclass *, depfile: T.Optional[str] = None, capture: bool = False, @@ -1691,24 +1677,27 @@ class Generator(HoldableObject): return output +@dataclass(eq=False) class GeneratedList(HoldableObject): """The output of generator.process.""" - def __init__(self, generator: Generator, subdir: str, - preserve_path_from: T.Optional[str], - extra_args: T.List[str]): - self.generator = generator - self.name = generator.exe + generator: Generator + subdir: str + preserve_path_from: T.Optional[str] + extra_args: T.List[str] + + def __post_init__(self) -> None: + self.name = self.generator.exe self.depends: T.Set['CustomTarget'] = set() # Things this target depends on (because e.g. a custom target was used as input) - self.subdir = subdir self.infilelist: T.List['File'] = [] self.outfilelist: T.List[str] = [] self.outmap: T.Dict[File, T.List[str]] = {} self.extra_depends = [] # XXX: Doesn't seem to be used? self.depend_files: T.List[File] = [] - self.preserve_path_from = preserve_path_from - self.extra_args: T.List[str] = extra_args if extra_args is not None else [] + + if self.extra_args is None: + self.extra_args: T.List[str] = [] if isinstance(self.generator.exe, programs.ExternalProgram): if not self.generator.exe.found(): @@ -1777,8 +1766,8 @@ class Executable(BuildTarget): self.suffix = 'exe' elif machine.system.startswith('wasm') or machine.system == 'emscripten': self.suffix = 'js' - elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('arm') or - 'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('arm')): + elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('armclang') or + 'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('armclang')): self.suffix = 'axf' elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('ccrx') or 'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('ccrx')): @@ -2588,13 +2577,12 @@ class CustomTarget(Target, CommandBase): return [] def is_internal(self) -> bool: - if not self.should_install(): - return True - for out in self.get_outputs(): - # Can't check if this is a static library, so try to guess - if not out.endswith(('.a', '.lib')): - return False - return True + ''' + Returns True iif this is a not installed static library. + ''' + if len(self.outputs) != 1: + return False + return CustomTargetIndex(self, self.outputs[0]).is_internal() def extract_all_objects_recurse(self) -> T.List[T.Union[str, 'ExtractedObjects']]: return self.get_outputs() @@ -2622,7 +2610,7 @@ class RunTarget(Target, CommandBase): def __init__(self, name: str, command: T.Sequence[T.Union[str, File, BuildTarget, 'CustomTarget', 'CustomTargetIndex', programs.ExternalProgram]], - dependencies: T.Sequence[T.Union[BuildTarget, 'CustomTarget']], + dependencies: T.Sequence[Target], subdir: str, subproject: str, env: T.Optional['EnvironmentVariables'] = None): @@ -2666,7 +2654,7 @@ class RunTarget(Target, CommandBase): return "@run" class AliasTarget(RunTarget): - def __init__(self, name: str, dependencies: T.Sequence[T.Union[BuildTarget, 'CustomTarget']], + def __init__(self, name: str, dependencies: T.Sequence['Target'], subdir: str, subproject: str): super().__init__(name, [], dependencies, subdir, subproject) @@ -2713,6 +2701,7 @@ class Jar(BuildTarget): return ['-cp', os.pathsep.join(cp_paths)] return [] +@dataclass(eq=False) class CustomTargetIndex(HoldableObject): """A special opaque object returned by indexing a CustomTarget. This object @@ -2721,11 +2710,12 @@ class CustomTargetIndex(HoldableObject): the sources. """ - def __init__(self, target: CustomTarget, output: str): + target: CustomTarget + output: str + + def __post_init__(self) -> None: self.typename = 'custom' - self.target = target - self.output = output - self.for_machine = target.for_machine + self.for_machine = self.target.for_machine @property def name(self) -> str: @@ -2764,7 +2754,11 @@ class CustomTargetIndex(HoldableObject): return self.target.should_install() def is_internal(self) -> bool: - return self.target.is_internal() + ''' + Returns True iif this is a not installed static library + ''' + suf = os.path.splitext(self.output)[-1] + return suf in {'.a', '.lib'} and not self.should_install() def extract_all_objects_recurse(self) -> T.List[T.Union[str, 'ExtractedObjects']]: return self.target.extract_all_objects_recurse() @@ -2773,17 +2767,16 @@ class CustomTargetIndex(HoldableObject): return self.target.get_custom_install_dir() class ConfigurationData(HoldableObject): - def __init__(self) -> None: + def __init__(self, initial_values: T.Optional[T.Union[ + T.Dict[str, T.Tuple[T.Union[str, int, bool], T.Optional[str]]], + T.Dict[str, T.Union[str, int, bool]]] + ] = None): super().__init__() - self.values: T.Dict[ - str, - T.Tuple[ - T.Union[str, int, bool], - T.Optional[str] - ] - ] = {} + self.values: T.Dict[str, T.Tuple[T.Union[str, int, bool], T.Optional[str]]] = \ + {k: v if isinstance(v, tuple) else (v, None) for k, v in initial_values.items()} if initial_values else {} + self.used: bool = False - def __repr__(self): + def __repr__(self) -> str: return repr(self.values) def __contains__(self, value: str) -> bool: @@ -2797,45 +2790,41 @@ class ConfigurationData(HoldableObject): # A bit poorly named, but this represents plain data files to copy # during install. +@dataclass(eq=False) class Data(HoldableObject): - def __init__(self, sources: T.List[File], install_dir: str, install_dir_name: str, - install_mode: 'FileMode', subproject: str, - rename: T.List[str] = None, - install_tag: T.Optional[str] = None, - data_type: str = None): - self.sources = sources - self.install_dir = install_dir - self.install_dir_name = install_dir_name - self.install_mode = install_mode - self.install_tag = install_tag - if rename is None: + sources: T.List[File] + install_dir: str + install_dir_name: str + install_mode: 'FileMode' + subproject: str + rename: T.List[str] = None + install_tag: T.Optional[str] = None + data_type: str = None + + def __post_init__(self) -> None: + if self.rename is None: self.rename = [os.path.basename(f.fname) for f in self.sources] - else: - self.rename = rename - self.subproject = subproject - self.data_type = data_type +@dataclass(eq=False) class SymlinkData(HoldableObject): - def __init__(self, target: str, name: str, install_dir: str, - subproject: str, install_tag: T.Optional[str] = None): - self.target = target - if name != os.path.basename(name): - raise InvalidArguments(f'Link name is "{name}", but link names cannot contain path separators. ' + target: str + name: str + install_dir: str + subproject: str + install_tag: T.Optional[str] = None + + def __post_init__(self) -> None: + if self.name != os.path.basename(self.name): + raise InvalidArguments(f'Link name is "{self.name}", but link names cannot contain path separators. ' 'The dir part should be in install_dir.') - self.name = name - self.install_dir = install_dir - self.subproject = subproject - self.install_tag = install_tag +@dataclass(eq=False) class TestSetup: - def __init__(self, exe_wrapper: T.List[str], gdb: bool, - timeout_multiplier: int, env: EnvironmentVariables, - exclude_suites: T.List[str]): - self.exe_wrapper = exe_wrapper - self.gdb = gdb - self.timeout_multiplier = timeout_multiplier - self.env = env - self.exclude_suites = exclude_suites + exe_wrapper: T.List[str] + gdb: bool + timeout_multiplier: int + env: EnvironmentVariables + exclude_suites: T.List[str] def get_sources_string_names(sources, backend): ''' diff --git a/test cases/unit/98 install all targets/bar-custom.txt b/mesonbuild/cmake/data/__init__.py index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/bar-custom.txt +++ b/mesonbuild/cmake/data/__init__.py diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index c2e2d27..0b6d241 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -24,7 +24,7 @@ from .traceparser import CMakeTraceParser, CMakeGeneratorTarget from .tracetargets import resolve_cmake_trace_targets from .. import mlog, mesonlib from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible, OptionKey -from ..mesondata import mesondata +from ..mesondata import DataFile from ..compilers.compilers import assembler_suffixes, lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header from ..programs import ExternalProgram from ..coredata import FORBIDDEN_TARGET_NAMES @@ -816,7 +816,7 @@ class CMakeInterpreter: raise CMakeException('Unable to find CMake') self.trace = CMakeTraceParser(cmake_exe.version(), self.build_dir, permissive=True) - preload_file = mesondata['cmake/data/preload.cmake'].write_to_private(self.env) + preload_file = DataFile('cmake/data/preload.cmake').write_to_private(self.env) toolchain = CMakeToolchain(cmake_exe, self.env, self.for_machine, CMakeExecScope.SUBPROJECT, self.build_dir, preload_file) toolchain_file = toolchain.write() diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 9d4a779..474bf3b 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -183,6 +183,11 @@ class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler): return [] +class ArmLtdClangCCompiler(ClangCCompiler): + + id = 'armltdclang' + + class AppleClangCCompiler(ClangCCompiler): """Handle the differences between Apple Clang and Vanilla Clang. @@ -197,6 +202,9 @@ class AppleClangCCompiler(ClangCCompiler): class EmscriptenCCompiler(EmscriptenMixin, ClangCCompiler): + + id = 'emscripten' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -207,10 +215,13 @@ class EmscriptenCCompiler(EmscriptenMixin, ClangCCompiler): ClangCCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper=exe_wrapper, linker=linker, defines=defines, full_version=full_version) - self.id = 'emscripten' class ArmclangCCompiler(ArmclangCompiler, CCompiler): + ''' + Keil armclang + ''' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -315,6 +326,9 @@ class PGICCompiler(PGICompiler, CCompiler): class NvidiaHPC_CCompiler(PGICompiler, CCompiler): + + id = 'nvidia_hpc' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -322,7 +336,6 @@ class NvidiaHPC_CCompiler(PGICompiler, CCompiler): CCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) PGICompiler.__init__(self) - self.id = 'nvidia_hpc' class ElbrusCCompiler(ElbrusCompiler, CCompiler): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index e0745c2..b652f5a 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -875,7 +875,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta): return None def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return self.linker.build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 8bc013f..749da2f 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -155,7 +155,7 @@ class CPPCompiler(CLikeCompiler, Compiler): } # Currently, remapping is only supported for Clang, Elbrus and GCC - assert self.id in frozenset(['clang', 'lcc', 'gcc', 'emscripten']) + assert self.id in frozenset(['clang', 'lcc', 'gcc', 'emscripten', 'armltdclang']) if cpp_std not in CPP_FALLBACKS: # 'c++03' and 'c++98' don't have fallback types @@ -259,6 +259,11 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): return search_dirs + ['-lstdc++'] +class ArmLtdClangCPPCompiler(ClangCPPCompiler): + + id = 'armltdclang' + + class AppleClangCPPCompiler(ClangCPPCompiler): def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: # We need to apply the search prefix here, as these link arguments may @@ -274,6 +279,9 @@ class AppleClangCPPCompiler(ClangCPPCompiler): class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): + + id = 'emscripten' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -284,7 +292,6 @@ class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): ClangCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper=exe_wrapper, linker=linker, defines=defines, full_version=full_version) - self.id = 'emscripten' def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: args = [] @@ -296,6 +303,10 @@ class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): + ''' + Keil armclang + ''' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -438,6 +449,9 @@ class PGICPPCompiler(PGICompiler, CPPCompiler): class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler): + + id = 'nvidia_hpc' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -446,8 +460,6 @@ class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler): info, exe_wrapper, linker=linker, full_version=full_version) PGICompiler.__init__(self) - self.id = 'nvidia_hpc' - class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler): def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, @@ -687,6 +699,9 @@ class CPP11AsCPP14Mixin(CompilerMixinBase): class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, MSVCCompiler, CPPCompiler): + + id = 'msvc' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', target: str, exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -695,7 +710,6 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) MSVCCompiler.__init__(self, target) - self.id = 'msvc' def get_options(self) -> 'KeyedOptionDictType': cpp_stds = ['none', 'c++11', 'vc++11'] @@ -727,6 +741,9 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi return args class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler): + + id = 'clang-cl' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', target: str, exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -735,7 +752,6 @@ class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, Cl CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) ClangClCompiler.__init__(self, target) - self.id = 'clang-cl' def get_options(self) -> 'KeyedOptionDictType': cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index 7ebb66d..b38f626 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -40,9 +40,8 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): language = 'cs' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo', comp_id: str, runner: T.Optional[str] = None): + info: 'MachineInfo', runner: T.Optional[str] = None): super().__init__(exelist, version, for_machine, info) - self.id = comp_id self.runner = runner @classmethod @@ -121,19 +120,20 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): class MonoCompiler(CsCompiler): + + id = 'mono' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo'): - super().__init__(exelist, version, for_machine, info, 'mono', - runner='mono') + super().__init__(exelist, version, for_machine, info, runner='mono') def rsp_file_syntax(self) -> 'RSPFileSyntax': return RSPFileSyntax.GCC class VisualStudioCsCompiler(CsCompiler): - def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo'): - super().__init__(exelist, version, for_machine, info, 'csc') + + id = 'csc' def get_buildtype_args(self, buildtype: str) -> T.List[str]: res = mono_buildtype_args[buildtype] diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 1f396e4..9662dbc 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -173,6 +173,8 @@ class CudaCompiler(Compiler): # Reverse map -short to --long options. _FLAG_SHORT2LONG_WITHARGS = {v: k for k, v in _FLAG_LONG2SHORT_WITHARGS.items()} + id = 'nvcc' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, exe_wrapper: T.Optional['ExternalProgram'], host_compiler: Compiler, info: 'MachineInfo', @@ -182,7 +184,6 @@ class CudaCompiler(Compiler): self.exe_wrapper = exe_wrapper self.host_compiler = host_compiler self.base_options = host_compiler.base_options - self.id = 'nvcc' self.warn_args = {level: self._to_host_flags(flags) for level, flags in host_compiler.warn_args.items()} @classmethod @@ -706,7 +707,7 @@ class CudaCompiler(Compiler): return self._to_host_flags(self.host_compiler.get_buildtype_linker_args(buildtype), _Phase.LINKER) def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: (rpath_args, rpath_dirs_to_remove) = self.host_compiler.build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 4201d82..bae293e 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -249,7 +249,7 @@ class DmdLikeCompilerMixin(CompilerMixinBase): return self.linker.import_library_args(implibname) def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if self.info.is_windows(): return ([], set()) @@ -681,6 +681,7 @@ class GnuDCompiler(GnuCompiler, DCompiler): # we mostly want DCompiler, but that gives us the Compiler.LINKER_PREFIX instead LINKER_PREFIX = GnuCompiler.LINKER_PREFIX + id = 'gcc' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo', arch: str, *, @@ -692,7 +693,6 @@ class GnuDCompiler(GnuCompiler, DCompiler): exe_wrapper=exe_wrapper, linker=linker, full_version=full_version, is_cross=is_cross) GnuCompiler.__init__(self, {}) - self.id = 'gcc' default_warn_args = ['-Wall', '-Wdeprecated'] self.warn_args = {'0': [], '1': default_warn_args, @@ -760,6 +760,8 @@ def find_ldc_dmd_frontend_version(version_output: T.Optional[str]) -> T.Optional class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): + id = 'llvm' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo', arch: str, *, exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -770,7 +772,6 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): exe_wrapper=exe_wrapper, linker=linker, full_version=full_version, is_cross=is_cross) DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output)) - self.id = 'llvm' self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} def get_colorout_args(self, colortype: str) -> T.List[str]: @@ -824,6 +825,8 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): + id = 'dmd' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo', arch: str, *, exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -834,7 +837,6 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): exe_wrapper=exe_wrapper, linker=linker, full_version=full_version, is_cross=is_cross) DmdLikeCompilerMixin.__init__(self, version) - self.id = 'dmd' self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} def get_colorout_args(self, colortype: str) -> T.List[str]: diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 03c5226..572ec35 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -54,6 +54,7 @@ from .c import ( AppleClangCCompiler, ArmCCompiler, ArmclangCCompiler, + ArmLtdClangCCompiler, ClangCCompiler, ClangClCCompiler, GnuCCompiler, @@ -74,6 +75,7 @@ from .cpp import ( AppleClangCPPCompiler, ArmCPPCompiler, ArmclangCPPCompiler, + ArmLtdClangCPPCompiler, ClangCPPCompiler, ClangClCPPCompiler, GnuCPPCompiler, @@ -97,6 +99,7 @@ from .d import ( from .cuda import CudaCompiler from .fortran import ( FortranCompiler, + ArmLtdFlangFortranCompiler, G95FortranCompiler, GnuFortranCompiler, ElbrusFortranCompiler, @@ -462,6 +465,20 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin ccache + compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker, full_version=full_version) + if 'Arm C/C++/Fortran Compiler' in out: + arm_ver_match = re.search('version (\d+)\.(\d+) \(build number (\d+)\)', out) + arm_ver_major = arm_ver_match.group(1) + arm_ver_minor = arm_ver_match.group(2) + arm_ver_build = arm_ver_match.group(3) + version = '.'.join([arm_ver_major, arm_ver_minor, arm_ver_build]) + if lang == 'c': + cls = ArmLtdClangCCompiler + elif lang == 'cpp': + cls = ArmLtdClangCPPCompiler + linker = guess_nix_linker(env, compiler, cls, for_machine) + return cls( + ccache + compiler, version, for_machine, is_cross, info, + exe_wrap, linker=linker) if 'armclang' in out: # The compiler version is not present in the first line of output, # instead it is present in second line, startswith 'Component:'. @@ -711,6 +728,17 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C compiler, version, for_machine, is_cross, info, exe_wrap, defines, full_version=full_version, linker=linker) + if 'Arm C/C++/Fortran Compiler' in out: + cls = ArmLtdFlangFortranCompiler + arm_ver_match = re.search('version (\d+)\.(\d+) \(build number (\d+)\)', out) + arm_ver_major = arm_ver_match.group(1) + arm_ver_minor = arm_ver_match.group(2) + arm_ver_build = arm_ver_match.group(3) + version = '.'.join([arm_ver_major, arm_ver_minor, arm_ver_build]) + linker = guess_nix_linker(env, compiler, cls, for_machine) + return cls( + ccache + compiler, version, for_machine, is_cross, info, + exe_wrap, linker=linker) if 'G95' in out: cls = G95FortranCompiler linker = guess_nix_linker(env, compiler, cls, for_machine) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 6a4a343..ba2454d 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -260,6 +260,7 @@ class ElbrusFortranCompiler(ElbrusCompiler, FortranCompiler): class G95FortranCompiler(FortranCompiler): LINKER_PREFIX = '-Wl,' + id = 'g95' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -268,7 +269,6 @@ class G95FortranCompiler(FortranCompiler): FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) - self.id = 'g95' default_warn_args = ['-Wall'] self.warn_args = {'0': [], '1': default_warn_args, @@ -286,15 +286,7 @@ class G95FortranCompiler(FortranCompiler): class SunFortranCompiler(FortranCompiler): LINKER_PREFIX = '-Wl,' - - def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, - info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, - linker: T.Optional['DynamicLinker'] = None, - full_version: T.Optional[str] = None): - FortranCompiler.__init__(self, exelist, version, for_machine, - is_cross, info, exe_wrapper, linker=linker, - full_version=full_version) - self.id = 'sun' + id = 'sun' def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: return ['-fpp'] @@ -318,6 +310,7 @@ class SunFortranCompiler(FortranCompiler): class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler): file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp', ) + id = 'intel' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -329,7 +322,6 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler): # FIXME: Add support for OS X and Windows in detect_fortran_compiler so # we are sent the type of compiler IntelGnuLikeCompiler.__init__(self) - self.id = 'intel' default_warn_args = ['-warn', 'general', '-warn', 'truncated_source'] self.warn_args = {'0': [], '1': default_warn_args, @@ -404,6 +396,8 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler): class PathScaleFortranCompiler(FortranCompiler): + id = 'pathscale' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -411,7 +405,6 @@ class PathScaleFortranCompiler(FortranCompiler): FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) - self.id = 'pathscale' default_warn_args = ['-fullwarn'] self.warn_args = {'0': [], '1': default_warn_args, @@ -447,6 +440,8 @@ class PGIFortranCompiler(PGICompiler, FortranCompiler): class NvidiaHPC_FortranCompiler(PGICompiler, FortranCompiler): + id = 'nvidia_hpc' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -456,7 +451,6 @@ class NvidiaHPC_FortranCompiler(PGICompiler, FortranCompiler): full_version=full_version) PGICompiler.__init__(self) - self.id = 'nvidia_hpc' default_warn_args = ['-Minform=inform'] self.warn_args = {'0': [], '1': default_warn_args, @@ -466,6 +460,8 @@ class NvidiaHPC_FortranCompiler(PGICompiler, FortranCompiler): class FlangFortranCompiler(ClangCompiler, FortranCompiler): + id = 'flang' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -474,7 +470,6 @@ class FlangFortranCompiler(ClangCompiler, FortranCompiler): is_cross, info, exe_wrapper, linker=linker, full_version=full_version) ClangCompiler.__init__(self, {}) - self.id = 'flang' default_warn_args = ['-Minform=inform'] self.warn_args = {'0': [], '1': default_warn_args, @@ -494,8 +489,14 @@ class FlangFortranCompiler(ClangCompiler, FortranCompiler): search_dirs.append(f'-L{d}') return search_dirs + ['-lflang', '-lpgmath'] +class ArmLtdFlangFortranCompiler(FlangFortranCompiler): + + id = 'armltdflang' + class Open64FortranCompiler(FortranCompiler): + id = 'open64' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -503,7 +504,6 @@ class Open64FortranCompiler(FortranCompiler): FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) - self.id = 'open64' default_warn_args = ['-fullwarn'] self.warn_args = {'0': [], '1': default_warn_args, @@ -516,6 +516,8 @@ class Open64FortranCompiler(FortranCompiler): class NAGFortranCompiler(FortranCompiler): + id = 'nagfor' + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -523,7 +525,6 @@ class NAGFortranCompiler(FortranCompiler): FortranCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) - self.id = 'nagfor' # Warnings are on by default; -w disables (by category): self.warn_args = { '0': ['-w=all'], diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 4abf998..05c271a 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -30,11 +30,11 @@ if T.TYPE_CHECKING: class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler): language = 'java' + id = 'unknown' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo', full_version: T.Optional[str] = None): super().__init__(exelist, version, for_machine, info, full_version=full_version) - self.id = 'unknown' self.javarunner = 'java' def get_werror_args(self) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py index 4e1898a..5bf862d 100644 --- a/mesonbuild/compilers/mixins/arm.py +++ b/mesonbuild/compilers/mixins/arm.py @@ -74,10 +74,11 @@ class ArmCompiler(Compiler): """Functionality that is common to all ARM family compilers.""" + id = 'arm' + def __init__(self) -> None: if not self.is_cross: raise mesonlib.EnvironmentException('armcc supports only cross-compilation.') - self.id = 'arm' default_warn_args = [] # type: T.List[str] self.warn_args = {'0': [], '1': default_warn_args, @@ -136,6 +137,11 @@ class ArmCompiler(Compiler): class ArmclangCompiler(Compiler): + ''' + This is the Keil armclang. + ''' + + id = 'armclang' def __init__(self) -> None: if not self.is_cross: @@ -145,7 +151,6 @@ class ArmclangCompiler(Compiler): raise mesonlib.EnvironmentException(f'Unsupported Linker {self.linker.exelist}, must be armlink') if not mesonlib.version_compare(self.version, '==' + self.linker.version): raise mesonlib.EnvironmentException('armlink version does not match with compiler version') - self.id = 'armclang' self.base_options = { OptionKey(o) for o in ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', diff --git a/mesonbuild/compilers/mixins/c2000.py b/mesonbuild/compilers/mixins/c2000.py index ab0278e..f614d16 100644 --- a/mesonbuild/compilers/mixins/c2000.py +++ b/mesonbuild/compilers/mixins/c2000.py @@ -55,10 +55,11 @@ c2000_debug_args = { class C2000Compiler(Compiler): + id = 'c2000' + def __init__(self) -> None: if not self.is_cross: raise EnvironmentException('c2000 supports only cross-compilation.') - self.id = 'c2000' self.can_compile_suffixes.add('asm') # Assembly self.can_compile_suffixes.add('cla') # Control Law Accelerator (CLA) diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py index eba4c45..d87769e 100644 --- a/mesonbuild/compilers/mixins/ccrx.py +++ b/mesonbuild/compilers/mixins/ccrx.py @@ -59,10 +59,11 @@ class CcrxCompiler(Compiler): is_cross = True can_compile_suffixes = set() # type: T.Set[str] + id = 'ccrx' + def __init__(self) -> None: if not self.is_cross: raise EnvironmentException('ccrx supports only cross-compilation.') - self.id = 'ccrx' # Assembly self.can_compile_suffixes.add('src') default_warn_args = [] # type: T.List[str] diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 1391297..663a87e 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -45,9 +45,10 @@ clang_optimization_args = { class ClangCompiler(GnuLikeCompiler): + id = 'clang' + def __init__(self, defines: T.Optional[T.Dict[str, str]]): super().__init__() - self.id = 'clang' self.defines = defines or {} self.base_options.update( {OptionKey('b_colorout'), OptionKey('b_lto_threads'), OptionKey('b_lto_mode')}) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 3d2df9b..269ce5c 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -437,7 +437,7 @@ class CLikeCompiler(Compiler): dependencies = [] elif not isinstance(dependencies, collections.abc.Iterable): # TODO: we want to ensure the front end does the listifing here - dependencies = [dependencies] # type: ignore + dependencies = [dependencies] # Collect compiler arguments cargs = self.compiler_args() # type: arglist.CompilerArgs largs = [] # type: T.List[str] diff --git a/mesonbuild/compilers/mixins/compcert.py b/mesonbuild/compilers/mixins/compcert.py index 3211f6a..283c043 100644 --- a/mesonbuild/compilers/mixins/compcert.py +++ b/mesonbuild/compilers/mixins/compcert.py @@ -60,8 +60,9 @@ ccomp_args_to_wul = [ class CompCertCompiler(Compiler): + id = 'ccomp' + def __init__(self) -> None: - self.id = 'ccomp' # Assembly self.can_compile_suffixes.add('s') default_warn_args = [] # type: T.List[str] diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 80fbe12..aac9811 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -32,9 +32,10 @@ class ElbrusCompiler(GnuLikeCompiler): # Elbrus compiler is nearly like GCC, but does not support # PCH, LTO, sanitizers and color output as of version 1.21.x. + id = 'lcc' + def __init__(self) -> None: super().__init__() - self.id = 'lcc' self.base_options = {OptionKey(o) for o in ['b_pgo', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded']} default_warn_args = ['-Wall'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index bc40af4..ea3aab4 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -328,10 +328,10 @@ class GnuCompiler(GnuLikeCompiler): GnuCompiler represents an actual GCC in its many incarnations. Compilers imitating GCC (Clang/Intel) should use the GnuLikeCompiler ABC. """ + id = 'gcc' def __init__(self, defines: T.Optional[T.Dict[str, str]]): super().__init__() - self.id = 'gcc' self.defines = defines or {} self.base_options.update({OptionKey('b_colorout'), OptionKey('b_lto_threads')}) diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index 1417743..2698b39 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -66,6 +66,7 @@ class IntelGnuLikeCompiler(GnuLikeCompiler): '3': ['-O3'], 's': ['-Os'], } + id = 'intel' def __init__(self) -> None: super().__init__() @@ -77,7 +78,6 @@ class IntelGnuLikeCompiler(GnuLikeCompiler): self.base_options = {mesonlib.OptionKey(o) for o in [ 'b_pch', 'b_lundef', 'b_asneeded', 'b_pgo', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_pie']} - self.id = 'intel' self.lang_header = 'none' def get_pch_suffix(self) -> str: @@ -145,9 +145,7 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler): 's': ['/Os'], } - def __init__(self, target: str) -> None: - super().__init__(target) - self.id = 'intel-cl' + id = 'intel-cl' def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: args = super().get_compiler_check_args(mode) diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 144bc70..9513aa5 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -108,7 +108,7 @@ class BasicLinkerIsCompilerMixin(Compiler): raise MesonException("This linker doesn't support soname args") def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 51de8af..2bc7012 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -43,9 +43,10 @@ pgi_buildtype_args = { class PGICompiler(Compiler): + id = 'pgi' + def __init__(self) -> None: self.base_options = {OptionKey('b_pch')} - self.id = 'pgi' default_warn_args = ['-Minform=inform'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index ddd5476..5118e41 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -388,9 +388,7 @@ class MSVCCompiler(VisualStudioLikeCompiler): """Specific to the Microsoft Compilers.""" - def __init__(self, target: str): - super().__init__(target) - self.id = 'msvc' + id = 'msvc' def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]: args = super().get_compile_debugfile_args(rel_obj, pch) @@ -420,9 +418,10 @@ class ClangClCompiler(VisualStudioLikeCompiler): """Specific to Clang-CL.""" + id = 'clang-cl' + def __init__(self, target: str): super().__init__(target) - self.id = 'clang-cl' # Assembly self.can_compile_suffixes.add('s') @@ -439,6 +438,10 @@ class ClangClCompiler(VisualStudioLikeCompiler): def get_pch_base_name(self, header: str) -> str: return header + def get_include_args(self, path: str, is_system: bool) -> T.List[str]: + if path == '': + path = '.' + return ['/clang:-isystem' + path] if is_system else ['-I' + path] def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: if dep.get_include_type() == 'system': @@ -450,4 +453,4 @@ class ClangClCompiler(VisualStudioLikeCompiler): converted += [i] return converted else: - return dep.get_compile_args()
\ No newline at end of file + return dep.get_compile_args() diff --git a/mesonbuild/compilers/mixins/xc16.py b/mesonbuild/compilers/mixins/xc16.py index 77c4690..2433561 100644 --- a/mesonbuild/compilers/mixins/xc16.py +++ b/mesonbuild/compilers/mixins/xc16.py @@ -55,10 +55,11 @@ xc16_debug_args = { class Xc16Compiler(Compiler): + id = 'xc16' + def __init__(self) -> None: if not self.is_cross: raise EnvironmentException('xc16 supports only cross-compilation.') - self.id = 'xc16' # Assembly self.can_compile_suffixes.add('s') default_warn_args = [] # type: T.List[str] diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 8f7e165..399570d 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -44,6 +44,7 @@ class RustCompiler(Compiler): # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX language = 'rust' + id = 'rustc' _WARNING_LEVELS: T.Dict[str, T.List[str]] = { '0': ['-A', 'warnings'], @@ -61,7 +62,6 @@ class RustCompiler(Compiler): is_cross=is_cross, full_version=full_version, linker=linker) self.exe_wrapper = exe_wrapper - self.id = 'rustc' self.base_options.add(OptionKey('b_colorout')) if 'link' in self.linker.id: self.base_options.add(OptionKey('b_vscrt')) @@ -205,11 +205,4 @@ class ClippyRustCompiler(RustCompiler): This just provides us a different id """ - def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - is_cross: bool, info: 'MachineInfo', - exe_wrapper: T.Optional['ExternalProgram'] = None, - full_version: T.Optional[str] = None, - linker: T.Optional['DynamicLinker'] = None): - super().__init__(exelist, version, for_machine, is_cross, info, - exe_wrapper, full_version, linker) - self.id = 'clippy-driver rustc' + id = 'clippy-driver rustc' diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 2d52e21..a2b57b8 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -37,6 +37,7 @@ class SwiftCompiler(Compiler): LINKER_PREFIX = ['-Xlinker'] language = 'swift' + id = 'llvm' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', full_version: T.Optional[str] = None, @@ -45,7 +46,6 @@ class SwiftCompiler(Compiler): is_cross=is_cross, full_version=full_version, linker=linker) self.version = version - self.id = 'llvm' def needs_static_linker(self) -> bool: return True diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index b8144f6..7c3eac0 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -27,12 +27,12 @@ if T.TYPE_CHECKING: class ValaCompiler(Compiler): language = 'vala' + id = 'valac' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo'): super().__init__(exelist, version, for_machine, info, is_cross=is_cross) self.version = version - self.id = 'valac' self.base_options = {OptionKey('b_colorout')} def needs_static_linker(self) -> bool: diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f444cf1..2bb89a2 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -44,7 +44,10 @@ if T.TYPE_CHECKING: CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], str] # Check major_versions_differ() if changing versioning scheme. -version = '0.60.99' +# +# Pip requires that RCs are named like this: '0.1.0.rc1' +# But the corresponding Git tag needs to be '0.1.0rc1' +version = '0.61.99' backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode'] @@ -234,7 +237,7 @@ class UserArrayOption(UserOption[T.List[str]]): elif isinstance(value, list): newvalue = value else: - raise MesonException(f'"{newvalue}" should be a string array, but it is not') + raise MesonException(f'"{value}" should be a string array, but it is not') return newvalue def validate_value(self, value: T.Union[str, T.List[str]], user_input: bool = True) -> T.List[str]: diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 6881a34..b79be42 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -27,9 +27,10 @@ from ..mesonlib import version_compare_many from ..interpreterbase import FeatureDeprecated if T.TYPE_CHECKING: + from .._typing import ImmutableListProtocol from ..compilers.compilers import Compiler from ..environment import Environment - from ..build import BuildTarget, CustomTarget + from ..build import BuildTarget, CustomTarget, IncludeDirs from ..mesonlib import FileOrString @@ -162,7 +163,9 @@ class Dependency(HoldableObject): def get_exe_args(self, compiler: 'Compiler') -> T.List[str]: return [] - def get_pkgconfig_variable(self, variable_name: str, kwargs: T.Dict[str, T.Any]) -> str: + def get_pkgconfig_variable(self, variable_name: str, + define_variable: 'ImmutableListProtocol[str]', + default: T.Optional[str]) -> str: raise DependencyException(f'{self.name!r} is not a pkgconfig dependency') def get_configtool_variable(self, variable_name: str) -> str: @@ -216,9 +219,10 @@ class Dependency(HoldableObject): return new_dep class InternalDependency(Dependency): - def __init__(self, version: str, incdirs: T.List[str], compile_args: T.List[str], - link_args: T.List[str], libraries: T.List['BuildTarget'], - whole_libraries: T.List['BuildTarget'], + def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T.List[str], + link_args: T.List[str], + libraries: T.List[T.Union['BuildTarget', 'CustomTarget']], + whole_libraries: T.List[T.Union['BuildTarget', 'CustomTarget']], sources: T.Sequence[T.Union['FileOrString', 'CustomTarget']], ext_deps: T.List[Dependency], variables: T.Dict[str, T.Any]): super().__init__(DependencyTypeName('internal'), {}) @@ -254,7 +258,9 @@ class InternalDependency(Dependency): return True return any(d.is_built() for d in self.ext_deps) - def get_pkgconfig_variable(self, variable_name: str, kwargs: T.Dict[str, T.Any]) -> str: + def get_pkgconfig_variable(self, variable_name: str, + define_variable: 'ImmutableListProtocol[str]', + default: T.Optional[str]) -> str: raise DependencyException('Method "get_pkgconfig_variable()" is ' 'invalid for an internal dependency') diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index aadf3f8..318bca2 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -648,7 +648,7 @@ class BoostDependency(SystemDependency): try: boost_pc = PkgConfigDependency('boost', self.env, {'required': False}) if boost_pc.found(): - boost_root = boost_pc.get_pkgconfig_variable('prefix', {'default': None}) + boost_root = boost_pc.get_pkgconfig_variable('prefix', [], None) if boost_root: roots += [Path(boost_root)] except DependencyException: diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py index a6898f8..dcbe5df 100644 --- a/mesonbuild/dependencies/cmake.py +++ b/mesonbuild/dependencies/cmake.py @@ -14,9 +14,9 @@ from .base import ExternalDependency, DependencyException, DependencyTypeName from ..mesonlib import is_windows, MesonException, OptionKey, PerMachine, stringlistify, extract_as_list -from ..mesondata import mesondata from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args, CMakeTarget, resolve_cmake_trace_targets, cmake_is_debug from .. import mlog +import importlib.resources from pathlib import Path import functools import re @@ -583,7 +583,7 @@ class CMakeDependency(ExternalDependency): shutil.rmtree(cmake_files.as_posix(), ignore_errors=True) # Insert language parameters into the CMakeLists.txt and write new CMakeLists.txt - cmake_txt = mesondata['dependencies/data/' + cmake_file].data + cmake_txt = importlib.resources.read_text('mesonbuild.dependencies.data', cmake_file, encoding = 'utf-8') # In general, some Fortran CMake find_package() also require C language enabled, # even if nothing from C is directly used. An easy Fortran example that fails diff --git a/test cases/unit/98 install all targets/bar-devel.h b/mesonbuild/dependencies/data/__init__.py index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/bar-devel.h +++ b/mesonbuild/dependencies/data/__init__.py diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py index 78ef9d6..2f1ed0a 100644 --- a/mesonbuild/dependencies/hdf5.py +++ b/mesonbuild/dependencies/hdf5.py @@ -164,7 +164,7 @@ def hdf5_factory(env: 'Environment', for_machine: 'MachineChoice', if PCEXE: # some distros put hdf5-1.2.3.pc with version number in .pc filename. ret = subprocess.run([PCEXE, '--list-all'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - universal_newlines=True) + text=True) if ret.returncode == 0: for pkg in ret.stdout.split('\n'): if pkg.startswith('hdf5'): diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index 8f4c639..e6921e9 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -24,6 +24,7 @@ import typing as T if T.TYPE_CHECKING: from ..environment import Environment + from .._typing import ImmutableListProtocol class PkgConfigDependency(ExternalDependency): # The class's copy of the pkg-config path. Avoids having to search for it @@ -380,18 +381,13 @@ class PkgConfigDependency(ExternalDependency): raise DependencyException(f'Could not generate libs for {self.name}:\n\n{out_raw}') self.link_args, self.raw_link_args = self._search_libs(out, out_raw) - def get_pkgconfig_variable(self, variable_name: str, kwargs: T.Dict[str, T.Union[str, T.List[str]]]) -> str: + def get_pkgconfig_variable(self, variable_name: str, + define_variable: 'ImmutableListProtocol[str]', + default: T.Optional[str]) -> str: options = ['--variable=' + variable_name, self.name] - if 'define_variable' in kwargs: - definition = kwargs.get('define_variable', []) - if not isinstance(definition, list): - raise DependencyException('define_variable takes a list') - - if len(definition) != 2 or not all(isinstance(i, str) for i in definition): - raise DependencyException('define_variable must be made up of 2 strings for VARIABLENAME and VARIABLEVALUE') - - options = ['--define-variable=' + '='.join(definition)] + options + if define_variable: + options = ['--define-variable=' + '='.join(define_variable)] + options ret, out, err = self._call_pkgbin(options) variable = '' @@ -406,9 +402,8 @@ class PkgConfigDependency(ExternalDependency): if not variable: ret, out, _ = self._call_pkgbin(['--print-variables', self.name]) if not re.search(r'^' + variable_name + r'$', out, re.MULTILINE): - if 'default' in kwargs: - assert isinstance(kwargs['default'], str) - variable = kwargs['default'] + if default: + variable = default else: mlog.warning(f"pkgconfig variable '{variable_name}' not defined for dependency {self.name}.") @@ -483,13 +478,8 @@ class PkgConfigDependency(ExternalDependency): default_value: T.Optional[str] = None, pkgconfig_define: T.Optional[T.List[str]] = None) -> T.Union[str, T.List[str]]: if pkgconfig: - kwargs: T.Dict[str, T.Union[str, T.List[str]]] = {} - if default_value is not None: - kwargs['default'] = default_value - if pkgconfig_define is not None: - kwargs['define_variable'] = pkgconfig_define try: - return self.get_pkgconfig_variable(pkgconfig, kwargs) + return self.get_pkgconfig_variable(pkgconfig, pkgconfig_define or [], default_value) except DependencyException: pass if default_value is not None: diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index 9dc928c..a004688 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -181,7 +181,7 @@ class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta) self.is_found = False return if self.private_headers: - qt_inc_dir = mod.get_pkgconfig_variable('includedir', {}) + qt_inc_dir = mod.get_pkgconfig_variable('includedir', [], None) mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m) if not os.path.isdir(mod_private_dir): # At least some versions of homebrew don't seem to set this @@ -203,7 +203,7 @@ class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta) if arg == f'-l{debug_lib_name}' or arg.endswith(f'{debug_lib_name}.lib') or arg.endswith(f'{debug_lib_name}.a'): is_debug = True break - libdir = self.get_pkgconfig_variable('libdir', {}) + libdir = self.get_pkgconfig_variable('libdir', [], None) if not self._link_with_qtmain(is_debug, libdir): self.is_found = False return @@ -211,7 +211,7 @@ class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta) self.bindir = self.get_pkgconfig_host_bins(self) if not self.bindir: # If exec_prefix is not defined, the pkg-config file is broken - prefix = self.get_pkgconfig_variable('exec_prefix', {}) + prefix = self.get_pkgconfig_variable('exec_prefix', [], None) if prefix: self.bindir = os.path.join(prefix, 'bin') @@ -387,7 +387,7 @@ class Qt4PkgConfigDependency(QtPkgConfigDependency): applications = ['moc', 'uic', 'rcc', 'lupdate', 'lrelease'] for application in applications: try: - return os.path.dirname(core.get_pkgconfig_variable(f'{application}_location', {})) + return os.path.dirname(core.get_pkgconfig_variable(f'{application}_location', [], None)) except mesonlib.MesonException: pass return None @@ -400,7 +400,7 @@ class Qt5PkgConfigDependency(QtPkgConfigDependency): @staticmethod def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str: - return core.get_pkgconfig_variable('host_bins', {}) + return core.get_pkgconfig_variable('host_bins', [], None) def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]: return _qt_get_private_includes(mod_inc_dir, module, self.version) @@ -410,7 +410,7 @@ class Qt6PkgConfigDependency(QtPkgConfigDependency): @staticmethod def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str: - return core.get_pkgconfig_variable('host_bins', {}) + return core.get_pkgconfig_variable('host_bins', [], None) def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]: return _qt_get_private_includes(mod_inc_dir, module, self.version) diff --git a/mesonbuild/depfile.py b/mesonbuild/depfile.py index 62cbe81..a8b4588 100644 --- a/mesonbuild/depfile.py +++ b/mesonbuild/depfile.py @@ -11,12 +11,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections +import typing as T -def parse(lines): - rules = [] - targets = [] - deps = [] + +def parse(lines: T.Iterable[str]) -> T.List[T.Tuple[T.List[str], T.List[str]]]: + rules: T.List[T.Tuple[T.List[str], T.List[str]]] = [] + targets: T.List[str] = [] + deps: T.List[str] = [] in_deps = False out = '' for line in lines: @@ -56,12 +57,15 @@ def parse(lines): out += c return rules -Target = collections.namedtuple('Target', ['deps']) +class Target(T.NamedTuple): + + deps: T.Set[str] + class DepFile: - def __init__(self, lines): + def __init__(self, lines: T.Iterable[str]): rules = parse(lines) - depfile = {} + depfile: T.Dict[str, Target] = {} for (targets, deps) in rules: for target in targets: t = depfile.setdefault(target, Target(deps=set())) @@ -69,16 +73,17 @@ class DepFile: t.deps.add(dep) self.depfile = depfile - def get_all_dependencies(self, target, visited=None): - deps = set() + def get_all_dependencies(self, name: str, visited: T.Optional[T.Set[str]] = None) -> T.List[str]: + deps: T.Set[str] = set() if not visited: visited = set() - if target in visited: - return set() - visited.add(target) - target = self.depfile.get(target) + if name in visited: + return [] + visited.add(name) + + target = self.depfile.get(name) if not target: - return set() + return [] deps.update(target.deps) for dep in target.deps: deps.update(self.get_all_dependencies(dep, visited)) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 3a2923b..1b5f728 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from dataclasses import dataclass import subprocess import typing as T from enum import Enum @@ -234,27 +235,15 @@ class Properties: def get(self, key: str, default: T.Optional[T.Union[str, bool, int, T.List[str]]] = None) -> T.Optional[T.Union[str, bool, int, T.List[str]]]: return self.properties.get(key, default) +@dataclass(unsafe_hash=True) class MachineInfo(HoldableObject): - def __init__(self, system: str, cpu_family: str, cpu: str, endian: str): - self.system = system - self.cpu_family = cpu_family - self.cpu = cpu - self.endian = endian - self.is_64_bit = cpu_family in CPU_FAMILIES_64_BIT # type: bool + system: str + cpu_family: str + cpu: str + endian: str - def __eq__(self, other: object) -> bool: - if not isinstance(other, MachineInfo): - return NotImplemented - return \ - self.system == other.system and \ - self.cpu_family == other.cpu_family and \ - self.cpu == other.cpu and \ - self.endian == other.endian - - def __ne__(self, other: object) -> bool: - if not isinstance(other, MachineInfo): - return NotImplemented - return not self.__eq__(other) + def __post_init__(self) -> None: + self.is_64_bit: bool = self.cpu_family in CPU_FAMILIES_64_BIT def __repr__(self) -> str: return f'<MachineInfo: {self.system} {self.cpu_family} ({self.cpu})>' diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py index 2269837..016e4dc 100644 --- a/mesonbuild/interpreter/__init__.py +++ b/mesonbuild/interpreter/__init__.py @@ -28,7 +28,7 @@ __all__ = [ 'CustomTargetIndexHolder', 'MachineHolder', 'Test', - 'ConfigurationDataObject', + 'ConfigurationDataHolder', 'SubprojectHolder', 'DependencyHolder', 'GeneratedListHolder', @@ -46,7 +46,7 @@ from .interpreter import Interpreter, permitted_dependency_kwargs from .compiler import CompilerHolder from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder, CustomTargetIndexHolder, MachineHolder, Test, - ConfigurationDataObject, SubprojectHolder, DependencyHolder, + ConfigurationDataHolder, SubprojectHolder, DependencyHolder, GeneratedListHolder, ExternalProgramHolder, extract_required_kwarg) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d4dbec9..bd71f78 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -43,7 +43,6 @@ from .mesonmain import MesonMain from .dependencyfallbacks import DependencyFallbacksHolder from .interpreterobjects import ( SubprojectHolder, - ConfigurationDataObject, Test, RunProcess, extract_required_kwarg, @@ -433,6 +432,7 @@ class Interpreter(InterpreterBase, HoldableObject): dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder, coredata.UserFeatureOption: OBJ.FeatureOptionHolder, envconfig.MachineInfo: OBJ.MachineHolder, + build.ConfigurationData: OBJ.ConfigurationDataHolder, }) ''' @@ -488,7 +488,7 @@ class Interpreter(InterpreterBase, HoldableObject): elif isinstance(v, Test): self.build.tests.append(v) elif isinstance(v, (int, str, bool, Disabler, ObjectHolder, build.GeneratedList, - ExternalProgram)): + ExternalProgram, build.ConfigurationData)): pass else: raise InterpreterException(f'Module returned a value of unknown type {v!r}.') @@ -829,6 +829,11 @@ external dependencies (including libraries) must go to "dependencies".''') subproject = self.subprojects[subp_name] if required and not subproject.found(): raise InterpreterException(f'Subproject "{subproject.subdir}" required but not found.') + if 'version' in kwargs: + pv = self.build.subprojects[subp_name] + wanted = kwargs['version'] + if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: + raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.') return subproject r = self.environment.wrap_resolver @@ -1015,13 +1020,16 @@ external dependencies (including libraries) must go to "dependencies".''') @typed_pos_args('configuration_data', optargs=[dict]) @noKwargs - def func_configuration_data(self, node: mparser.BaseNode, args: T.Optional[dict], kwargs: 'TYPE_kwargs') -> ConfigurationDataObject: + def func_configuration_data(self, node: mparser.BaseNode, args: T.Tuple[T.Optional[T.Dict[str, T.Any]]], + kwargs: 'TYPE_kwargs') -> build.ConfigurationData: initial_values = args[0] if initial_values is not None: FeatureNew.single_use('configuration_data dictionary', '0.49.0', self.subproject) - else: - initial_values = {} - return ConfigurationDataObject(self.subproject, initial_values) + for k, v in initial_values.items(): + if not isinstance(v, (str, int ,bool)): + raise InvalidArguments( + f'"configuration_data": initial value dictionary key "{k!r}"" must be "str | int | bool", not "{v!r}"') + return build.ConfigurationData(initial_values) def set_backend(self): # The backend is already set when parsing subprojects @@ -1445,9 +1453,10 @@ external dependencies (including libraries) must go to "dependencies".''') progobj = self.notfound_program(args) if isinstance(progobj, ExternalProgram) and not progobj.found(): - mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO')) + if not silent: + mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO')) if required: - m = 'Program {!r} not found' + m = 'Program {!r} not found or not executable' raise InterpreterException(m.format(progobj.get_name())) return progobj @@ -1475,7 +1484,8 @@ external dependencies (including libraries) must go to "dependencies".''') # Only store successful lookups self.store_name_lookups(args) - mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info) + if not silent: + mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info) if isinstance(progobj, build.Executable): progobj.was_returned_by_find_program = True return progobj @@ -2228,8 +2238,12 @@ external dependencies (including libraries) must go to "dependencies".''') conf = kwargs['configuration'] if isinstance(conf, dict): FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject) - conf = ConfigurationDataObject(self.subproject, conf) - elif not isinstance(conf, ConfigurationDataObject): + for k, v in conf.items(): + if not isinstance(v, (str, int ,bool)): + raise InvalidArguments( + f'"configuration_data": initial value dictionary key "{k!r}"" must be "str | int | bool", not "{v!r}"') + conf = build.ConfigurationData(conf) + elif not isinstance(conf, build.ConfigurationData): raise InterpreterException('Argument "configuration" is not of type configuration_data') mlog.log('Configuring', mlog.bold(output), 'using configuration') if len(inputs) > 1: @@ -2238,7 +2252,7 @@ external dependencies (including libraries) must go to "dependencies".''') os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True) file_encoding = kwargs.setdefault('encoding', 'utf-8') missing_variables, confdata_useless = \ - mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf.conf_data, + mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf, fmt, file_encoding) if missing_variables: var_list = ", ".join(map(repr, sorted(missing_variables))) @@ -2254,8 +2268,8 @@ external dependencies (including libraries) must go to "dependencies".''') 'copy a file to the build dir, use the \'copy:\' keyword ' 'argument added in 0.47.0', location=node) else: - mesonlib.dump_conf_header(ofile_abs, conf.conf_data, output_format) - conf.mark_used() + mesonlib.dump_conf_header(ofile_abs, conf, output_format) + conf.used = True elif 'command' in kwargs: if len(inputs) > 1: FeatureNew.single_use('multiple inputs in configure_file()', '0.52.0', self.subproject) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index f078698..9560221 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -16,8 +16,8 @@ from ..backend.backends import TestProtocol from ..interpreterbase import ( ContainerTypeInfo, KwargInfo, MesonOperator, InterpreterObject, MesonInterpreterObject, ObjectHolder, MutableInterpreterObject, - FeatureCheckBase, FeatureNewKwargs, FeatureNew, FeatureDeprecated, - typed_pos_args, typed_kwargs, typed_operator, permittedKwargs, + FeatureCheckBase, FeatureNew, FeatureDeprecated, + typed_pos_args, typed_kwargs, typed_operator, noArgsFlattening, noPosargs, noKwargs, unholder_return, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs, flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode) from ..interpreter.type_checking import NoneType @@ -124,32 +124,25 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.value == 'auto' - @permittedKwargs({'error_message'}) - def require_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption: - if len(args) != 1: - raise InvalidArguments(f'Expected 1 argument, got {len(args)}.') - if not isinstance(args[0], bool): - raise InvalidArguments('boolean argument expected.') - error_message = kwargs.pop('error_message', '') - if error_message and not isinstance(error_message, str): - raise InterpreterException("Error message must be a string.") + @typed_pos_args('feature_option.require', bool) + @typed_kwargs( + 'feature_option.require', + KwargInfo('error_message', (str, NoneType)) + ) + def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> coredata.UserFeatureOption: if args[0]: return copy.deepcopy(self.held_object) - assert isinstance(error_message, str) if self.value == 'enabled': - prefix = f'Feature {self.held_object.name} cannot be enabled' - if error_message: - prefix += ': ' - raise InterpreterException(prefix + error_message) + err_msg = f'Feature {self.held_object.name} cannot be enabled' + if kwargs['error_message']: + err_msg += f': {kwargs["error_message"]}' + raise InterpreterException(err_msg) return self.as_disabled() @noKwargs - def disable_auto_if_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption: - if len(args) != 1: - raise InvalidArguments(f'Expected 1 argument, got {len(args)}.') - if not isinstance(args[0], bool): - raise InvalidArguments('boolean argument expected.') + @typed_pos_args('feature_option.disable_auto_if', bool) + def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption: return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled() @@ -283,11 +276,13 @@ class EnvironmentVariablesHolder(ObjectHolder[build.EnvironmentVariables], Mutab self.held_object.prepend(name, values, kwargs['separator']) -class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject): - def __init__(self, subproject: str, initial_values: T.Optional[T.Dict[str, T.Any]] = None) -> None: - self.used = False # These objects become immutable after use in configure_file. - super().__init__(subproject=subproject) - self.conf_data = build.ConfigurationData() +_CONF_DATA_SET_KWS: KwargInfo[T.Optional[str]] = KwargInfo('description', (str, NoneType)) + + +class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInterpreterObject): + + def __init__(self, obj: build.ConfigurationData, interpreter: 'Interpreter'): + super().__init__(obj, interpreter) self.methods.update({'set': self.set_method, 'set10': self.set10_method, 'set_quoted': self.set_quoted_method, @@ -297,97 +292,70 @@ class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject): 'get_unquoted': self.get_unquoted_method, 'merge_from': self.merge_from_method, }) - if isinstance(initial_values, dict): - for k, v in initial_values.items(): - self.set_method([k, v], {}) - elif initial_values: - raise AssertionError('Unsupported ConfigurationDataObject initial_values') + + def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder': + return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter) def is_used(self) -> bool: - return self.used - - def mark_used(self) -> None: - self.used = True - - def validate_args(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[str, T.Union[str, int, bool], T.Optional[str]]: - if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2: - mlog.deprecation('Passing a list as the single argument to ' - 'configuration_data.set is deprecated. This will ' - 'become a hard error in the future.', - location=self.current_node) - args = args[0] - - if len(args) != 2: - raise InterpreterException("Configuration set requires 2 arguments.") - if self.used: - raise InterpreterException("Can not set values on configuration object that has been used.") - name, val = args - if not isinstance(val, (int, str)): - msg = f'Setting a configuration data value to {val!r} is invalid, ' \ - 'and will fail at configure_file(). If you are using it ' \ - 'just to store some values, please use a dict instead.' - mlog.deprecation(msg, location=self.current_node) - desc = kwargs.get('description', None) - if not isinstance(name, str): - raise InterpreterException("First argument to set must be a string.") - if desc is not None and not isinstance(desc, str): - raise InterpreterException('Description must be a string.') - - # TODO: Remove the cast once we get rid of the deprecation - return name, T.cast(T.Union[str, bool, int], val), desc + return self.held_object.used - @noArgsFlattening - def set_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: - (name, val, desc) = self.validate_args(args, kwargs) - self.conf_data.values[name] = (val, desc) - - def set_quoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: - (name, val, desc) = self.validate_args(args, kwargs) - if not isinstance(val, str): - raise InterpreterException("Second argument to set_quoted must be a string.") - escaped_val = '\\"'.join(val.split('"')) - self.conf_data.values[name] = ('"' + escaped_val + '"', desc) - - def set10_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: - (name, val, desc) = self.validate_args(args, kwargs) - if val: - self.conf_data.values[name] = (1, desc) - else: - self.conf_data.values[name] = (0, desc) + def __check_used(self) -> None: + if self.is_used(): + raise InterpreterException("Can not set values on configuration object that has been used.") - def has_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: - return args[0] in self.conf_data.values + @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.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() + 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() + if isinstance(args[1], int): + mlog.deprecation('configuration_data.set10 with number. the `set10` ' + 'method should only be used with booleans', + location=self.interpreter.current_node) + if args[1] < 0: + mlog.warning('Passing a number that is less than 0 may not have the intended result, ' + 'as meson will treat all non-zero values as true.', + location=self.interpreter.current_node) + self.held_object.values[args[0]] = (int(args[1]), kwargs['description']) + + @typed_pos_args('configuration_data.has', (str, int, bool)) + @noKwargs + def has_method(self, args: T.Tuple[T.Union[str, int, bool]], kwargs: TYPE_kwargs) -> bool: + return args[0] in self.held_object.values @FeatureNew('configuration_data.get()', '0.38.0') - @noArgsFlattening - def get_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: - if len(args) < 1 or len(args) > 2: - raise InterpreterException('Get method takes one or two arguments.') - if not isinstance(args[0], str): - raise InterpreterException('The variable name must be a string.') + @typed_pos_args('configuration_data.get', str, optargs=[(str, int, bool)]) + @noKwargs + def get_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]], + kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: name = args[0] - if name in self.conf_data: - return self.conf_data.get(name)[0] - if len(args) > 1: - # Assertion does not work because setting other values is still - # supported, but deprecated. Use T.cast in the meantime (even though - # this is a lie). - # TODO: Fix this once the deprecation is removed - # assert isinstance(args[1], (int, str, bool)) - return T.cast(T.Union[str, int, bool], args[1]) + if name in self.held_object: + return self.held_object.get(name)[0] + elif args[1] is not None: + return args[1] raise InterpreterException(f'Entry {name} not in configuration data.') @FeatureNew('configuration_data.get_unquoted()', '0.44.0') - def get_unquoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: - if len(args) < 1 or len(args) > 2: - raise InterpreterException('Get method takes one or two arguments.') - if not isinstance(args[0], str): - raise InterpreterException('The variable name must be a string.') + @typed_pos_args('configuration_data.get_unquoted', str, optargs=[(str, int, bool)]) + @noKwargs + def get_unquoted_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]], + kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: name = args[0] - if name in self.conf_data: - val = self.conf_data.get(name)[0] - elif len(args) > 1: - assert isinstance(args[1], (str, int, bool)) + if name in self.held_object: + val = self.held_object.get(name)[0] + elif args[1] is not None: val = args[1] else: raise InterpreterException(f'Entry {name} not in configuration data.') @@ -396,25 +364,22 @@ class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject): return val def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: - return self.conf_data.values[name] + return self.held_object.values[name] @FeatureNew('configuration_data.keys()', '0.57.0') @noPosargs + @noKwargs def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]: return sorted(self.keys()) def keys(self) -> T.List[str]: - return list(self.conf_data.values.keys()) + return list(self.held_object.values.keys()) - def merge_from_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: - if len(args) != 1: - raise InterpreterException('Merge_from takes one positional argument.') - from_object_holder = args[0] - if not isinstance(from_object_holder, ConfigurationDataObject): - raise InterpreterException('Merge_from argument must be a configuration data object.') - from_object = from_object_holder.conf_data - for k, v in from_object.values.items(): - self.conf_data.values[k] = v + @typed_pos_args('configuration_data.merge_from', build.ConfigurationData) + @noKwargs + def merge_from_method(self, args: T.Tuple[build.ConfigurationData], kwargs: TYPE_kwargs) -> None: + from_object = args[0] + self.held_object.values.update(from_object.values) _PARTIAL_DEP_KWARGS = [ @@ -466,71 +431,75 @@ class DependencyHolder(ObjectHolder[Dependency]): def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_name() - @FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0', - 'use Dependency.get_variable(pkgconfig : ...) instead') - @permittedKwargs({'define_variable', 'default'}) - def pkgconfig_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: - args = listify(args) - if len(args) != 1: - raise InterpreterException('get_pkgconfig_variable takes exactly one argument.') - varname = args[0] - if not isinstance(varname, str): - raise InterpreterException('Variable name must be a string.') - return self.held_object.get_pkgconfig_variable(varname, kwargs) + @FeatureDeprecated('dependency.get_pkgconfig_variable', '0.56.0', + 'use dependency.get_variable(pkgconfig : ...) instead') + @typed_pos_args('dependency.get_pkgconfig_variable', str) + @typed_kwargs( + 'dependency.get_pkgconfig_variable', + KwargInfo('default', (str, NoneType)), + KwargInfo( + 'define_variable', + ContainerTypeInfo(list, str, pairs=True), + default=[], + listify=True, + validator=lambda x: 'must be of length 2 or empty' if len(x) not in {0, 2} else None, + ), + ) + def pkgconfig_method(self, args: T.Tuple[str], kwargs: 'kwargs.DependencyPkgConfigVar') -> str: + return self.held_object.get_pkgconfig_variable(args[0], **kwargs) - @FeatureNew('dep.get_configtool_variable', '0.44.0') - @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0', - 'use Dependency.get_variable(configtool : ...) instead') + @FeatureNew('dependency.get_configtool_variable', '0.44.0') + @FeatureDeprecated('dependency.get_configtool_variable', '0.56.0', + 'use dependency.get_variable(configtool : ...) instead') @noKwargs - def configtool_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: - args = listify(args) - if len(args) != 1: - raise InterpreterException('get_configtool_variable takes exactly one argument.') - varname = args[0] - if not isinstance(varname, str): - raise InterpreterException('Variable name must be a string.') - return self.held_object.get_configtool_variable(varname) + @typed_pos_args('dependency.get_config_tool_variable', str) + def configtool_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> str: + return self.held_object.get_configtool_variable(args[0]) - @FeatureNew('dep.partial_dependency', '0.46.0') + @FeatureNew('dependency.partial_dependency', '0.46.0') @noPosargs - @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS) + @typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS) def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency: pdep = self.held_object.get_partial_dependency(**kwargs) return pdep - @FeatureNew('dep.get_variable', '0.51.0') - @typed_pos_args('dep.get_variable', optargs=[str]) - @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'}) - @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal']) - def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> T.Union[str, T.List[str]]: + @FeatureNew('dependency.get_variable', '0.51.0') + @typed_pos_args('dependency.get_variable', optargs=[str]) + @typed_kwargs( + 'dependency.get_variable', + KwargInfo('cmake', (str, NoneType)), + KwargInfo('pkgconfig', (str, NoneType)), + KwargInfo('configtool', (str, NoneType)), + KwargInfo('internal', (str, NoneType), since='0.54.0'), + KwargInfo('default_value', (str, NoneType)), + KwargInfo('pkgconfig_define', ContainerTypeInfo(list, str, pairs=True), default=[], listify=True), + ) + def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: 'kwargs.DependencyGetVariable') -> T.Union[str, T.List[str]]: default_varname = args[0] if default_varname is not None: - FeatureNew('Positional argument to dep.get_variable()', '0.58.0', location=self.current_node).use(self.subproject) - for k in ['cmake', 'pkgconfig', 'configtool', 'internal']: - kwargs.setdefault(k, default_varname) - return self.held_object.get_variable(**kwargs) + FeatureNew('Positional argument to dependency.get_variable()', '0.58.0', location=self.current_node).use(self.subproject) + return self.held_object.get_variable( + cmake=kwargs['cmake'] or default_varname, + pkgconfig=kwargs['pkgconfig'] or default_varname, + configtool=kwargs['configtool'] or default_varname, + internal=kwargs['internal'] or default_varname, + default_value=kwargs['default_value'], + pkgconfig_define=kwargs['pkgconfig_define'], + ) - @FeatureNew('dep.include_type', '0.52.0') + @FeatureNew('dependency.include_type', '0.52.0') @noPosargs @noKwargs def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_include_type() - @FeatureNew('dep.as_system', '0.52.0') - @noKwargs - def as_system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency: - args = listify(args) - new_is_system = 'system' - if len(args) > 1: - raise InterpreterException('as_system takes only one optional value') - if len(args) == 1: - if not isinstance(args[0], str): - raise InterpreterException('as_system takes exactly one string parameter') - new_is_system = args[0] - new_dep = self.held_object.generate_system_dependency(new_is_system) - return new_dep + @FeatureNew('dependency.as_system', '0.52.0') + @noKwargs + @typed_pos_args('dependency.as_system', optargs=[str]) + def as_system_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> Dependency: + return self.held_object.generate_system_dependency(args[0] or 'system') - @FeatureNew('dep.as_link_whole', '0.56.0') + @FeatureNew('dependency.as_link_whole', '0.56.0') @noKwargs @noPosargs def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency: @@ -592,9 +561,9 @@ class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]): def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.held_object.found() - @FeatureNew('dep.partial_dependency', '0.46.0') + @FeatureNew('dependency.partial_dependency', '0.46.0') @noPosargs - @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS) + @typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS) def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency: pdep = self.held_object.get_partial_dependency(**kwargs) return pdep diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 6056329..777db12 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -239,3 +239,30 @@ class RunCommand(TypedDict): check: bool capture: T.Optional[bool] env: build.EnvironmentVariables + + +class FeatureOptionRequire(TypedDict): + + error_message: T.Optional[str] + + +class DependencyPkgConfigVar(TypedDict): + + default: T.Optional[str] + define_variable: T.List[str] + + + +class DependencyGetVariable(TypedDict): + + cmake: T.Optional[str] + pkgconfig: T.Optional[str] + configtool: T.Optional[str] + internal: T.Optional[str] + default_value: T.Optional[str] + pkgconfig_define: T.List[str] + + +class ConfigurationDataSet(TypedDict): + + description: T.Optional[str] diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index e973c5d..be1acc6 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -79,13 +79,19 @@ class MesonMain(MesonInterpreterObject): }) def _find_source_script( - self, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], + self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], args: T.List[str]) -> 'ExecutableSerialisation': largs: T.List[T.Union[str, build.Executable, ExternalProgram]] = [] + if isinstance(prog, (build.Executable, ExternalProgram)): + FeatureNew.single_use(f'Passing executable/found program object to script parameter of {name}', + '0.55.0', self.subproject, location=self.current_node) largs.append(prog) largs.extend(args) return self.interpreter.backend.get_executable_serialisation(largs) + elif isinstance(prog, mesonlib.File): + FeatureNew.single_use(f'Passing file object to script parameter of {name}', + '0.57.0', self.subproject, location=self.current_node) found = self.interpreter.find_program_impl([prog]) largs.append(found) largs.extend(args) @@ -98,7 +104,7 @@ class MesonMain(MesonInterpreterObject): str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram, - ]], allow_built: bool = False) -> T.List[str]: + ]]) -> T.List[str]: script_args = [] # T.List[str] new = False for a in args: @@ -108,8 +114,6 @@ class MesonMain(MesonInterpreterObject): new = True script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir)) elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): - if not allow_built: - raise InterpreterException(f'Arguments to {name} cannot be built') new = True script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()]) @@ -147,12 +151,8 @@ class MesonMain(MesonInterpreterObject): args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, ExternalProgram], T.List[T.Union[str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram]]], kwargs: 'AddInstallScriptKW') -> None: - if isinstance(args[0], mesonlib.File): - FeatureNew.single_use('Passing file object to script parameter of add_install_script', - '0.57.0', self.interpreter.subproject) - - script_args = self._process_script_args('add_install_script', args[1], allow_built=True) - script = self._find_source_script(args[0], script_args) + script_args = self._process_script_args('add_install_script', args[1]) + script = self._find_source_script('add_install_script', args[0], script_args) script.skip_if_destdir = kwargs['skip_if_destdir'] script.tag = kwargs['install_tag'] self.build.install_scripts.append(script) @@ -160,43 +160,37 @@ class MesonMain(MesonInterpreterObject): @typed_pos_args( 'meson.add_postconf_script', (str, mesonlib.File, ExternalProgram), - varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex) + varargs=(str, mesonlib.File, ExternalProgram) ) @noKwargs def add_postconf_script_method( self, args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], - T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]]], + T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: - if isinstance(args[0], mesonlib.File): - FeatureNew.single_use('Passing file object to script parameter of add_postconf_script', - '0.57.0', self.interpreter.subproject) - script_args = self._process_script_args('add_postconf_script', args[1], allow_built=True) - script = self._find_source_script(args[0], script_args) + script_args = self._process_script_args('add_postconf_script', args[1]) + script = self._find_source_script('add_postconf_script', args[0], script_args) self.build.postconf_scripts.append(script) @typed_pos_args( 'meson.add_dist_script', - (str, mesonlib.File, build.Executable, ExternalProgram), - varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex) + (str, mesonlib.File, ExternalProgram), + varargs=(str, mesonlib.File, ExternalProgram) ) @noKwargs def add_dist_script_method( self, - args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, ExternalProgram], - T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]]], + args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], + T.List[T.Union[str, mesonlib.File, ExternalProgram]]], kwargs: 'TYPE_kwargs') -> None: if args[1]: FeatureNew.single_use('Calling "add_dist_script" with multiple arguments', '0.49.0', self.interpreter.subproject) - if isinstance(args[0], mesonlib.File): - FeatureNew.single_use('Passing file object to script parameter of add_dist_script', - '0.57.0', self.interpreter.subproject) if self.interpreter.subproject != '': FeatureNew.single_use('Calling "add_dist_script" in a subproject', '0.58.0', self.interpreter.subproject) - script_args = self._process_script_args('add_dist_script', args[1], allow_built=True) - script = self._find_source_script(args[0], script_args) + script_args = self._process_script_args('add_dist_script', args[1]) + script = self._find_source_script('add_dist_script', args[0], script_args) self.build.dist_scripts.append(script) @noPosargs diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 0f493f2..c6fcaa0 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -19,6 +19,7 @@ from .exceptions import InterpreterException, InvalidArguments from .operator import MesonOperator from ._unholder import _unholder +from dataclasses import dataclass from functools import wraps import abc import itertools @@ -99,10 +100,9 @@ def disablerIfNotFound(f: TV_func) -> TV_func: return ret return T.cast(TV_func, wrapped) +@dataclass(repr=False, eq=False) class permittedKwargs: - - def __init__(self, permitted: T.Set[str]): - self.permitted = permitted # type: T.Set[str] + permitted: T.Set[str] def __call__(self, f: TV_func) -> TV_func: @wraps(f) @@ -575,6 +575,7 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]: return inner +# This cannot be a dataclass due to https://github.com/python/mypy/issues/5374 class FeatureCheckBase(metaclass=abc.ABCMeta): "Base class for feature version checks" @@ -738,6 +739,7 @@ class FeatureDeprecated(FeatureCheckBase): mlog.warning(*args, location=self.location) +# This cannot be a dataclass due to https://github.com/python/mypy/issues/5374 class FeatureCheckKwargsBase(metaclass=abc.ABCMeta): @property diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 5491558..c8489da 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -72,7 +72,7 @@ class StaticLinker: return [] def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) @@ -309,7 +309,7 @@ class AIXArLinker(ArLikeLinker): std_args = ['-csr', '-Xany'] -def prepare_rpaths(raw_rpaths: str, build_dir: str, from_dir: str) -> T.List[str]: +def prepare_rpaths(raw_rpaths: T.Tuple[str, ...], build_dir: str, from_dir: str) -> T.List[str]: # The rpaths we write must be relative if they point to the build dir, # because otherwise they have different length depending on the build # directory. This breaks reproducible builds. @@ -518,7 +518,7 @@ class DynamicLinker(metaclass=abc.ABCMeta): raise MesonException('This linker does not support bitcode bundles') def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) @@ -627,7 +627,7 @@ class GnuLikeDynamicLinkerMixin: return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: m = env.machines[self.for_machine] if m.is_windows() or m.is_cygwin(): @@ -765,7 +765,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): return args def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not rpath_paths and not install_rpath and not build_rpath: return ([], set()) @@ -846,7 +846,7 @@ class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna return [] def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) @@ -924,7 +924,7 @@ class Xc16DynamicLinker(DynamicLinker): return [] def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) @@ -967,7 +967,7 @@ class CompCertDynamicLinker(DynamicLinker): raise MesonException(f'{self.id} does not support shared libraries.') def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: return ([], set()) @@ -1065,7 +1065,7 @@ class NAGDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): id = 'nag' def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not rpath_paths and not install_rpath and not build_rpath: return ([], set()) @@ -1110,7 +1110,7 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): return [] def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not env.machines[self.for_machine].is_windows(): return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set()) @@ -1317,7 +1317,7 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): return ['-z', 'fatal-warnings'] def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if not rpath_paths and not install_rpath and not build_rpath: return ([], set()) @@ -1364,7 +1364,7 @@ class AIXDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): return self._apply_prefix(['-berok']) def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: all_paths = mesonlib.OrderedSet() # type: mesonlib.OrderedSet[str] # install_rpath first, followed by other paths, and the system path last diff --git a/mesonbuild/mesondata.py b/mesonbuild/mesondata.py index 43b7bde..508c041 100644 --- a/mesonbuild/mesondata.py +++ b/mesonbuild/mesondata.py @@ -13,382 +13,35 @@ # limitations under the License. -#### -#### WARNING: This is an automatically generated file! Do not edit! -#### Generated by tools/gen_data.py -#### - - -# TODO: Remember to remove this also from tools/gen_data.py -from pathlib import Path +import importlib.resources +from pathlib import PurePosixPath, Path import typing as T if T.TYPE_CHECKING: from .environment import Environment -###################### -# BEGIN Data section # -###################### - -file_0_data_preload_cmake = '''\ -if(MESON_PS_LOADED) - return() -endif() - -set(MESON_PS_LOADED ON) - -cmake_policy(PUSH) -cmake_policy(SET CMP0054 NEW) # https://cmake.org/cmake/help/latest/policy/CMP0054.html - -# Dummy macros that have a special meaning in the meson code -macro(meson_ps_execute_delayed_calls) -endmacro() - -macro(meson_ps_reload_vars) -endmacro() - -macro(meson_ps_disabled_function) - message(WARNING "The function '${ARGV0}' is disabled in the context of CMake subprojects.\n" - "This should not be an issue but may lead to compilation errors.") -endmacro() - -# Helper macro to inspect the current CMake state -macro(meson_ps_inspect_vars) - set(MESON_PS_CMAKE_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") - set(MESON_PS_CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - meson_ps_execute_delayed_calls() -endmacro() - - -# Override some system functions with custom code and forward the args -# to the original function -macro(add_custom_command) - meson_ps_inspect_vars() - _add_custom_command(${ARGV}) -endmacro() - -macro(add_custom_target) - meson_ps_inspect_vars() - _add_custom_target(${ARGV}) -endmacro() - -macro(set_property) - meson_ps_inspect_vars() - _set_property(${ARGV}) -endmacro() - -function(set_source_files_properties) - set(FILES) - set(I 0) - set(PROPERTIES OFF) - - while(I LESS ARGC) - if(NOT PROPERTIES) - if("${ARGV${I}}" STREQUAL "PROPERTIES") - set(PROPERTIES ON) - else() - list(APPEND FILES "${ARGV${I}}") - endif() - - math(EXPR I "${I} + 1") - else() - set(ID_IDX ${I}) - math(EXPR PROP_IDX "${ID_IDX} + 1") - - set(ID "${ARGV${ID_IDX}}") - set(PROP "${ARGV${PROP_IDX}}") - - set_property(SOURCE ${FILES} PROPERTY "${ID}" "${PROP}") - math(EXPR I "${I} + 2") - endif() - endwhile() -endfunction() - -# Disable some functions that would mess up the CMake meson integration -macro(target_precompile_headers) - meson_ps_disabled_function(target_precompile_headers) -endmacro() - -set(MESON_PS_DELAYED_CALLS add_custom_command;add_custom_target;set_property) -meson_ps_reload_vars() - -cmake_policy(POP) -''' - -file_1_data_CMakeLists_txt = '''\ -# fail noisily if attempt to use this file without setting: -# cmake_minimum_required(VERSION ${CMAKE_VERSION}) -# project(... LANGUAGES ...) - -cmake_policy(SET CMP0000 NEW) - -set(PACKAGE_FOUND FALSE) -set(_packageName "${NAME}") -string(TOUPPER "${_packageName}" PACKAGE_NAME) - -while(TRUE) - if ("${VERSION}" STREQUAL "") - find_package("${NAME}" QUIET COMPONENTS ${COMPS}) - else() - find_package("${NAME}" "${VERSION}" QUIET COMPONENTS ${COMPS}) - endif() - - # ARCHS has to be set via the CMD interface - if(${_packageName}_FOUND OR ${PACKAGE_NAME}_FOUND OR "${ARCHS}" STREQUAL "") - break() - endif() - - list(GET ARCHS 0 CMAKE_LIBRARY_ARCHITECTURE) - list(REMOVE_AT ARCHS 0) -endwhile() - -if(${_packageName}_FOUND OR ${PACKAGE_NAME}_FOUND) - set(PACKAGE_FOUND TRUE) - - # Check the following variables: - # FOO_VERSION - # Foo_VERSION - # FOO_VERSION_STRING - # Foo_VERSION_STRING - if(NOT DEFINED PACKAGE_VERSION) - if(DEFINED ${_packageName}_VERSION) - set(PACKAGE_VERSION "${${_packageName}_VERSION}") - elseif(DEFINED ${PACKAGE_NAME}_VERSION) - set(PACKAGE_VERSION "${${PACKAGE_NAME}_VERSION}") - elseif(DEFINED ${_packageName}_VERSION_STRING) - set(PACKAGE_VERSION "${${_packageName}_VERSION_STRING}") - elseif(DEFINED ${PACKAGE_NAME}_VERSION_STRING) - set(PACKAGE_VERSION "${${PACKAGE_NAME}_VERSION_STRING}") - endif() - endif() - - # Check the following variables: - # FOO_LIBRARIES - # Foo_LIBRARIES - # FOO_LIBS - # Foo_LIBS - set(libs) - if(DEFINED ${_packageName}_LIBRARIES) - set(libs ${_packageName}_LIBRARIES) - elseif(DEFINED ${PACKAGE_NAME}_LIBRARIES) - set(libs ${PACKAGE_NAME}_LIBRARIES) - elseif(DEFINED ${_packageName}_LIBS) - set(libs ${_packageName}_LIBS) - elseif(DEFINED ${PACKAGE_NAME}_LIBS) - set(libs ${PACKAGE_NAME}_LIBS) - endif() - - # Check the following variables: - # FOO_INCLUDE_DIRS - # Foo_INCLUDE_DIRS - # FOO_INCLUDES - # Foo_INCLUDES - # FOO_INCLUDE_DIR - # Foo_INCLUDE_DIR - set(includes) - if(DEFINED ${_packageName}_INCLUDE_DIRS) - set(includes ${_packageName}_INCLUDE_DIRS) - elseif(DEFINED ${PACKAGE_NAME}_INCLUDE_DIRS) - set(includes ${PACKAGE_NAME}_INCLUDE_DIRS) - elseif(DEFINED ${_packageName}_INCLUDES) - set(includes ${_packageName}_INCLUDES) - elseif(DEFINED ${PACKAGE_NAME}_INCLUDES) - set(includes ${PACKAGE_NAME}_INCLUDES) - elseif(DEFINED ${_packageName}_INCLUDE_DIR) - set(includes ${_packageName}_INCLUDE_DIR) - elseif(DEFINED ${PACKAGE_NAME}_INCLUDE_DIR) - set(includes ${PACKAGE_NAME}_INCLUDE_DIR) - endif() - - # Check the following variables: - # FOO_DEFINITIONS - # Foo_DEFINITIONS - set(definitions) - if(DEFINED ${_packageName}_DEFINITIONS) - set(definitions ${_packageName}_DEFINITIONS) - elseif(DEFINED ${PACKAGE_NAME}_DEFINITIONS) - set(definitions ${PACKAGE_NAME}_DEFINITIONS) - endif() - - set(PACKAGE_INCLUDE_DIRS "${${includes}}") - set(PACKAGE_DEFINITIONS "${${definitions}}") - set(PACKAGE_LIBRARIES "${${libs}}") -endif() -''' - -file_2_data_CMakeListsLLVM_txt = '''\ -cmake_minimum_required(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} ) - -set(PACKAGE_FOUND FALSE) - -while(TRUE) - find_package(LLVM REQUIRED CONFIG QUIET) - - # ARCHS has to be set via the CMD interface - if(LLVM_FOUND OR "${ARCHS}" STREQUAL "") - break() - endif() - - list(GET ARCHS 0 CMAKE_LIBRARY_ARCHITECTURE) - list(REMOVE_AT ARCHS 0) -endwhile() - -if(LLVM_FOUND) - set(PACKAGE_FOUND TRUE) - - foreach(mod IN LISTS LLVM_MESON_MODULES) - # Reset variables - set(out_mods) - set(real_mods) - - # Generate a lower and upper case version - string(TOLOWER "${mod}" mod_L) - string(TOUPPER "${mod}" mod_U) - - # Get the mapped components - llvm_map_components_to_libnames(out_mods ${mod} ${mod_L} ${mod_U}) - list(SORT out_mods) - list(REMOVE_DUPLICATES out_mods) - - # Make sure that the modules exist - foreach(i IN LISTS out_mods) - if(TARGET ${i}) - list(APPEND real_mods ${i}) - endif() - endforeach() - - # Set the output variables - set(MESON_LLVM_TARGETS_${mod} ${real_mods}) - foreach(i IN LISTS real_mods) - set(MESON_TARGET_TO_LLVM_${i} ${mod}) - endforeach() - endforeach() - - # Check the following variables: - # LLVM_PACKAGE_VERSION - # LLVM_VERSION - # LLVM_VERSION_STRING - if(NOT DEFINED PACKAGE_VERSION) - if(DEFINED LLVM_PACKAGE_VERSION) - set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") - elseif(DEFINED LLVM_VERSION) - set(PACKAGE_VERSION "${LLVM_VERSION}") - elseif(DEFINED LLVM_VERSION_STRING) - set(PACKAGE_VERSION "${LLVM_VERSION_STRING}") - endif() - endif() - - # Check the following variables: - # LLVM_LIBRARIES - # LLVM_LIBS - set(libs) - if(DEFINED LLVM_LIBRARIES) - set(libs LLVM_LIBRARIES) - elseif(DEFINED LLVM_LIBS) - set(libs LLVM_LIBS) - endif() - - # Check the following variables: - # LLVM_INCLUDE_DIRS - # LLVM_INCLUDES - # LLVM_INCLUDE_DIR - set(includes) - if(DEFINED LLVM_INCLUDE_DIRS) - set(includes LLVM_INCLUDE_DIRS) - elseif(DEFINED LLVM_INCLUDES) - set(includes LLVM_INCLUDES) - elseif(DEFINED LLVM_INCLUDE_DIR) - set(includes LLVM_INCLUDE_DIR) - endif() - - # Check the following variables: - # LLVM_DEFINITIONS - set(definitions) - if(DEFINED LLVM_DEFINITIONS) - set(definitions LLVM_DEFINITIONS) - endif() - - set(PACKAGE_INCLUDE_DIRS "${${includes}}") - set(PACKAGE_DEFINITIONS "${${definitions}}") - set(PACKAGE_LIBRARIES "${${libs}}") -endif() -''' - -file_3_data_CMakePathInfo_txt = '''\ -cmake_minimum_required(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}) - -set(TMP_PATHS_LIST) -list(APPEND TMP_PATHS_LIST ${CMAKE_PREFIX_PATH}) -list(APPEND TMP_PATHS_LIST ${CMAKE_FRAMEWORK_PATH}) -list(APPEND TMP_PATHS_LIST ${CMAKE_APPBUNDLE_PATH}) -list(APPEND TMP_PATHS_LIST $ENV{CMAKE_PREFIX_PATH}) -list(APPEND TMP_PATHS_LIST $ENV{CMAKE_FRAMEWORK_PATH}) -list(APPEND TMP_PATHS_LIST $ENV{CMAKE_APPBUNDLE_PATH}) -list(APPEND TMP_PATHS_LIST ${CMAKE_SYSTEM_PREFIX_PATH}) -list(APPEND TMP_PATHS_LIST ${CMAKE_SYSTEM_FRAMEWORK_PATH}) -list(APPEND TMP_PATHS_LIST ${CMAKE_SYSTEM_APPBUNDLE_PATH}) - -set(LIB_ARCH_LIST) -if(CMAKE_LIBRARY_ARCHITECTURE_REGEX) - file(GLOB implicit_dirs RELATIVE /lib /lib/*-linux-gnu* ) - foreach(dir ${implicit_dirs}) - if("${dir}" MATCHES "${CMAKE_LIBRARY_ARCHITECTURE_REGEX}") - list(APPEND LIB_ARCH_LIST "${dir}") - endif() - endforeach() -endif() - -# "Export" these variables: -set(MESON_ARCH_LIST ${LIB_ARCH_LIST}) -set(MESON_PATHS_LIST ${TMP_PATHS_LIST}) -set(MESON_CMAKE_ROOT ${CMAKE_ROOT}) -set(MESON_CMAKE_SYSROOT ${CMAKE_SYSROOT}) -set(MESON_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}) - -message(STATUS ${TMP_PATHS_LIST}) -''' - - -#################### -# END Data section # -#################### - class DataFile: - def __init__(self, path: Path, sha256sum: str, data: str) -> None: - self.path = path - self.sha256sum = sha256sum - self.data = data + def __init__(self, path: str) -> None: + self.path = PurePosixPath(path) def write_once(self, path: Path) -> None: if not path.exists(): - path.write_text(self.data, encoding='utf-8') + data = importlib.resources.read_text( # [ignore encoding] it's on the next lines, Mr. Lint + ('mesonbuild' / self.path.parent).as_posix().replace('/', '.'), + self.path.name, + encoding='utf-8') + path.write_text(data, encoding='utf-8') def write_to_private(self, env: 'Environment') -> Path: + try: + resource = importlib.resources.files('mesonbuild') / self.path + if isinstance(resource, Path): + return resource + except AttributeError: + # fall through to python 3.7 compatible code + pass + out_file = Path(env.scratch_dir) / 'data' / self.path.name out_file.parent.mkdir(exist_ok=True) self.write_once(out_file) return out_file - - -mesondata = { - 'cmake/data/preload.cmake': DataFile( - Path('cmake/data/preload.cmake'), - 'ce8f30159aab25b92c26c58a219a427d47838bfa0739475221d6c8993b4946e5', - file_0_data_preload_cmake, - ), - 'dependencies/data/CMakeLists.txt': DataFile( - Path('dependencies/data/CMakeLists.txt'), - '4dca24afa13e9311f0598a6ac29690490819bd7d82cfdaa0a2fe5eea3c0fa0d5', - file_1_data_CMakeLists_txt, - ), - 'dependencies/data/CMakeListsLLVM.txt': DataFile( - Path('dependencies/data/CMakeListsLLVM.txt'), - '412cec3315597041a978d018cdaca282dcd47693793540da88ae2f80d0cbd7cd', - file_2_data_CMakeListsLLVM_txt, - ), - 'dependencies/data/CMakePathInfo.txt': DataFile( - Path('dependencies/data/CMakePathInfo.txt'), - '90da8b443982d9c87139b7dc84228eb58cab4315764949637208f25e2bda7db2', - file_3_data_CMakePathInfo_txt, - ), -} diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index f193889..1e3f54e 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -425,7 +425,7 @@ class File(HoldableObject): def suffix(self) -> str: return os.path.splitext(self.fname)[1][1:].lower() - def endswith(self, ending: str) -> bool: + def endswith(self, ending: T.Union[str, T.Tuple[str, ...]]) -> bool: return self.fname.endswith(ending) def split(self, s: str, maxsplit: int = -1) -> T.List[str]: @@ -1319,11 +1319,11 @@ def extract_as_list(dict_object: T.Dict[_T, _U], key: _T, pop: bool = False) -> ''' Extracts all values from given dict_object and listifies them. ''' - fetch = dict_object.get + fetch: T.Callable[[_T], _U] = dict_object.get if pop: fetch = dict_object.pop # If there's only one key, we don't return a list with one element - return listify(fetch(key, []), flatten=True) + return listify(fetch(key) or [], flatten=True) def typeslistify(item: 'T.Union[_T, T.Sequence[_T]]', @@ -1708,9 +1708,7 @@ class OrderedSet(T.MutableSet[_T]): insertion. """ def __init__(self, iterable: T.Optional[T.Iterable[_T]] = None): - # typing.OrderedDict is new in 3.7.2, so we can't use that, but we can - # use MutableMapping, which is fine in this case. - self.__container = collections.OrderedDict() # type: T.MutableMapping[_T, None] + self.__container: T.OrderedDict[_T, None] = collections.OrderedDict() if iterable: self.update(iterable) @@ -1741,12 +1739,10 @@ class OrderedSet(T.MutableSet[_T]): del self.__container[value] def move_to_end(self, value: _T, last: bool = True) -> None: - # Mypy does not know about move_to_end, because it is not part of MutableMapping - self.__container.move_to_end(value, last) # type: ignore + self.__container.move_to_end(value, last) def pop(self, last: bool = True) -> _T: - # Mypy does not know about the last argument, because it is not part of MutableMapping - item, _ = self.__container.popitem(last) # type: ignore + item, _ = self.__container.popitem(last) return item def update(self, iterable: T.Iterable[_T]) -> None: @@ -1756,6 +1752,10 @@ class OrderedSet(T.MutableSet[_T]): def difference(self, set_: T.Union[T.Set[_T], 'OrderedSet[_T]']) -> 'OrderedSet[_T]': return type(self)(e for e in self if e not in set_) + def difference_update(self, iterable: T.Iterable[_T]) -> None: + for item in iterable: + self.discard(item) + def relpath(path: str, start: str) -> str: # On Windows a relative path can't be evaluated for paths on two different # drives (i.e. c:\foo and f:\bar). The only thing left to do is to use the diff --git a/mesonbuild/mesonlib/vsenv.py b/mesonbuild/mesonlib/vsenv.py index 2ba2b90..fcb0c42 100644 --- a/mesonbuild/mesonlib/vsenv.py +++ b/mesonbuild/mesonlib/vsenv.py @@ -26,15 +26,16 @@ def _setup_vsenv(force: bool) -> bool: return False if os.environ.get('OSTYPE') == 'cygwin': return False - if 'Visual Studio' in os.environ['PATH']: - return False - # VSINSTALL is set when running setvars from a Visual Studio installation - # Tested with Visual Studio 2012 and 2017 - if 'VSINSTALLDIR' in os.environ: - return False - # Check explicitly for cl when on Windows - if shutil.which('cl.exe'): - return False + if 'MESON_FORCE_VSENV_FOR_UNITTEST' not in os.environ: + if 'Visual Studio' in os.environ['PATH']: + return False + # VSINSTALL is set when running setvars from a Visual Studio installation + # Tested with Visual Studio 2012 and 2017 + if 'VSINSTALLDIR' in os.environ: + return False + # Check explicitly for cl when on Windows + if shutil.which('cl.exe'): + return False if not force: if shutil.which('cc'): return False @@ -56,6 +57,7 @@ def _setup_vsenv(force: bool) -> bool: '-prerelease', '-requiresAny', '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-requires', 'Microsoft.VisualStudio.Workload.WDExpress', '-products', '*', '-utf8', '-format', @@ -71,6 +73,9 @@ def _setup_vsenv(force: bool) -> bool: bat_path = bat_root / 'VC/Auxiliary/Build/vcvarsx86_arm64.bat' else: bat_path = bat_root / 'VC/Auxiliary/Build/vcvars64.bat' + # if VS is not found try VS Express + if not bat_path.exists(): + bat_path = bat_root / 'VC/Auxiliary/Build/vcvarsx86_amd64.bat' if not bat_path.exists(): raise MesonException(f'Could not find {bat_path}') diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 06f589b..4d873e5 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -117,6 +117,7 @@ class CommandLineParser: return 0 def run(self, args): + pending_python_deprecation_notice = False # If first arg is not a known command, assume user wants to run the setup # command. known_commands = list(self.commands.keys()) + ['-h', '--help'] @@ -130,10 +131,20 @@ class CommandLineParser: args = args[1:] else: parser = self.parser + command = None args = mesonlib.expand_arguments(args) options = parser.parse_args(args) + if command is None: + command = options.command + + # Bump the version here in order to add a pre-exit warning that we are phasing out + # support for old python. If this is already the oldest supported version, then + # this can never be true and does nothing. + if command in ('setup', 'compile', 'test', 'install') and sys.version_info < (3, 7): + pending_python_deprecation_notice = True + try: return options.run_func(options) except MesonException as e: @@ -156,6 +167,9 @@ class CommandLineParser: mlog.exception(e) return 2 finally: + if pending_python_deprecation_notice: + mlog.notice('You are using Python 3.6 which is EOL. Starting with v0.62.0, ' + 'Meson will require Python 3.7 or newer', fatal=False) mlog.shutdown() def run_script_command(script_name, script_args): @@ -193,8 +207,8 @@ def ensure_stdout_accepts_unicode(): sys.stdout.buffer = sys.stdout.raw if hasattr(sys.stdout, 'raw') else sys.stdout def run(original_args, mainfile): - if sys.version_info < (3, 6): - print('Meson works correctly only with python 3.6+.') + if sys.version_info < (3, 7): + print('Meson works correctly only with python 3.7+.') print(f'You have python {sys.version}.') print('Please update your environment') return 1 diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index af847f7..47ebb75 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -25,6 +25,7 @@ import subprocess import sys import typing as T +from . import build from . import environment from .backend.backends import ( InstallData, InstallDataBase, InstallEmptyDir, InstallSymlinkData, @@ -32,7 +33,7 @@ from .backend.backends import ( ) from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version -from .mesonlib import Popen_safe, RealPathAction, is_windows +from .mesonlib import Popen_safe, RealPathAction, is_windows, setup_vsenv from .scripts import depfixer, destdir_join from .scripts.meson_exe import run_exe try: @@ -793,6 +794,8 @@ def run(opts: 'ArgumentType') -> int: if not os.path.exists(os.path.join(opts.wd, datafilename)): sys.exit('Install data not found. Run this command in build directory root.') if not opts.no_rebuild: + b = build.load(opts.wd) + setup_vsenv(b.need_vsenv) if not rebuild_all(opts.wd): sys.exit(-1) os.chdir(opts.wd) diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 60bc09e..bdc9da5 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -206,7 +206,7 @@ def process_markup(args: T.Sequence[TV_Loggable], keep: bool) -> T.List[str]: arr.append(str(arg)) return arr -def force_print(*args: str, nested: str, **kwargs: T.Any) -> None: +def force_print(*args: str, nested: bool, **kwargs: T.Any) -> None: if log_disable_stdout: return iostr = io.StringIO() diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 7eab2cb..8fe61c6 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -80,8 +80,8 @@ class ModuleState: def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True, version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None, - wanted: T.Optional[str] = None) -> 'ExternalProgram': - return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted) + wanted: T.Optional[str] = None, silent: bool = False) -> 'ExternalProgram': + return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent) def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]], workdir: T.Optional[str] = None, @@ -91,8 +91,11 @@ class ModuleState: 'env': env, 'depends': depends, } + # typed_* takes a list, and gives a tuple to func_test. Violating that constraint + # makes the universe (or at least use of this function) implode + real_args = list(args) # TODO: Use interpreter internal API, but we need to go through @typed_kwargs - self._interpreter.func_test(self.current_node, args, kwargs) + self._interpreter.func_test(self.current_node, real_args, kwargs) def get_option(self, name: str, subproject: str = '', machine: MachineChoice = MachineChoice.HOST, diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 67fdd0c..0f325f5 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -20,7 +20,7 @@ from . import ExtensionModule, ModuleReturnValue, ModuleObject from .. import build, mesonlib, mlog, dependencies from ..cmake import SingleTargetOptions, TargetOptions, cmake_defines_to_args -from ..interpreter import ConfigurationDataObject, SubprojectHolder +from ..interpreter import SubprojectHolder from ..interpreterbase import ( FeatureNew, FeatureNewKwargs, @@ -34,7 +34,6 @@ from ..interpreterbase import ( InvalidArguments, InterpreterException, ) -from ..programs import ExternalProgram COMPATIBILITIES = ['AnyNewerVersion', 'SameMajorVersion', 'SameMinorVersion', 'ExactVersion'] @@ -213,6 +212,7 @@ class CmakeModule(ExtensionModule): cmake_detected = False cmake_root = None + @FeatureNew('CMake Module', '0.50.0') def __init__(self, interpreter): super().__init__(interpreter) self.methods.update({ @@ -233,11 +233,11 @@ class CmakeModule(ExtensionModule): return compiler.sizeof('void *', '', env) - def detect_cmake(self): + def detect_cmake(self, state): if self.cmake_detected: return True - cmakebin = ExternalProgram('cmake', silent=False) + cmakebin = state.find_program('cmake', silent=False) if not cmakebin.found(): return False @@ -272,7 +272,7 @@ class CmakeModule(ExtensionModule): if compatibility not in COMPATIBILITIES: raise mesonlib.MesonException('compatibility must be either AnyNewerVersion, SameMajorVersion or ExactVersion.') - if not self.detect_cmake(): + if not self.detect_cmake(state): raise mesonlib.MesonException('Unable to find cmake') pkgroot = pkgroot_name = kwargs.get('install_dir', None) @@ -358,7 +358,7 @@ class CmakeModule(ExtensionModule): if 'configuration' not in kwargs: raise mesonlib.MesonException('"configuration" not specified.') conf = kwargs['configuration'] - if not isinstance(conf, ConfigurationDataObject): + if not isinstance(conf, build.ConfigurationData): raise mesonlib.MesonException('Argument "configuration" is not of type configuration_data') prefix = state.environment.coredata.get_option(mesonlib.OptionKey('prefix')) @@ -372,8 +372,8 @@ class CmakeModule(ExtensionModule): extra = PACKAGE_INIT_EXT.replace('@absInstallDir@', abs_install_dir) extra = extra.replace('@installPrefix@', prefix) - self.create_package_file(ifile_abs, ofile_abs, PACKAGE_RELATIVE_PATH, extra, conf.conf_data) - conf.mark_used() + self.create_package_file(ifile_abs, ofile_abs, PACKAGE_RELATIVE_PATH, extra, conf) + conf.used = True conffile = os.path.normpath(inputfile.relative_name()) if conffile not in self.interpreter.build_def_files: diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py index 60d2885..558ca81 100644 --- a/mesonbuild/modules/dlang.py +++ b/mesonbuild/modules/dlang.py @@ -21,20 +21,21 @@ import os from . import ExtensionModule from .. import dependencies from .. import mlog +from ..interpreterbase import FeatureNew from ..mesonlib import Popen_safe, MesonException -from ..programs import ExternalProgram class DlangModule(ExtensionModule): class_dubbin = None init_dub = False + @FeatureNew('Dlang Module', '0.48.0') def __init__(self, interpreter): super().__init__(interpreter) self.methods.update({ 'generate_dub_file': self.generate_dub_file, }) - def _init_dub(self): + def _init_dub(self, state): if DlangModule.class_dubbin is None: self.dubbin = dependencies.DubDependency.class_dubbin DlangModule.class_dubbin = self.dubbin @@ -42,7 +43,7 @@ class DlangModule(ExtensionModule): self.dubbin = DlangModule.class_dubbin if DlangModule.class_dubbin is None: - self.dubbin = self.check_dub() + self.dubbin = self.check_dub(state) DlangModule.class_dubbin = self.dubbin else: self.dubbin = DlangModule.class_dubbin @@ -53,7 +54,7 @@ class DlangModule(ExtensionModule): def generate_dub_file(self, state, args, kwargs): if not DlangModule.init_dub: - self._init_dub() + self._init_dub(state) if len(args) < 2: raise MesonException('Missing arguments') @@ -109,8 +110,8 @@ class DlangModule(ExtensionModule): p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2] return p.returncode, out.strip() - def check_dub(self): - dubbin = ExternalProgram('dub', silent=True) + def check_dub(self, state): + dubbin = state.find_program('dub', silent=True) if dubbin.found(): try: p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2] diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index ab3aae2..5faee83 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -41,6 +41,7 @@ if T.TYPE_CHECKING: class FSModule(ExtensionModule): + @FeatureNew('Fs Module', '0.53.0') def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) self.methods.update({ diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 326f56b..8e8349b 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -16,6 +16,7 @@ functionality such as gobject-introspection, gresources and gtk-doc''' import copy +import itertools import functools import os import subprocess @@ -29,16 +30,17 @@ from .. import build from .. import interpreter from .. import mesonlib from .. import mlog -from ..build import CustomTarget, CustomTargetIndex, GeneratedList, InvalidArguments +from ..build import BuildTarget, CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments from ..dependencies import Dependency, PkgConfigDependency, InternalDependency from ..interpreter.type_checking import DEPENDS_KW, DEPEND_FILES_KW, INSTALL_KW, NoneType, in_set_validator -from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, FeatureNew, FeatureDeprecated +from ..interpreterbase import noPosargs, noKwargs, FeatureNew, FeatureDeprecated from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo from ..interpreterbase.decorators import typed_pos_args from ..mesonlib import ( MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, ) from ..programs import ExternalProgram, OverrideProgram, EmptyExternalProgram +from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: from typing_extensions import Literal, TypedDict @@ -118,9 +120,9 @@ if T.TYPE_CHECKING: install_dir: T.List[str] check: bool install: bool - gobject_typesfile: T.List[str] - html_assets: T.List[str] - expand_content_files: T.List[str] + gobject_typesfile: T.List[FileOrString] + html_assets: T.List[FileOrString] + expand_content_files: T.List[FileOrString] c_args: T.List[str] include_directories: T.List[T.Union[str, build.IncludeDirs]] dependencies: T.List[T.Union[Dependency, build.SharedLibrary, build.StaticLibrary]] @@ -133,7 +135,7 @@ if T.TYPE_CHECKING: namespace: T.Optional[str] object_manager: bool build_by_default: bool - annotations: T.List[str] + annotations: T.List[T.List[str]] install_header: bool install_dir: T.Optional[str] docbook: T.Optional[str] @@ -152,7 +154,7 @@ if T.TYPE_CHECKING: nostdinc: bool prefix: T.Optional[str] skip_source: bool - sources: T.List[str] + sources: T.List[FileOrString] stdinc: bool valist_marshallers: bool @@ -166,6 +168,36 @@ if T.TYPE_CHECKING: gir_dirs: T.List[str] packages: T.List[T.Union[str, InternalDependency]] + class _MkEnumsCommon(TypedDict): + + sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + install_header: bool + install_dir: T.Optional[str] + identifier_prefix: T.Optional[str] + symbol_prefix: T.Optional[str] + + class MkEnumsSimple(_MkEnumsCommon): + + header_prefix: str + decorator: str + function_prefix: str + body_prefix: str + + class MkEnums(_MkEnumsCommon): + + c_template: T.Optional[FileOrString] + h_template: T.Optional[FileOrString] + comments: T.Optional[str] + eprod: T.Optional[str] + fhead: T.Optional[str] + fprod: T.Optional[str] + ftail: T.Optional[str] + vhead: T.Optional[str] + vprod: T.Optional[str] + vtail: T.Optional[str] + depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] + + # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( 'build_by_default', bool, default=True, @@ -178,6 +210,40 @@ _EXTRA_ARGS_KW: KwargInfo[T.List[str]] = KwargInfo( listify=True, ) +_MK_ENUMS_COMMON_KWS: T.List[KwargInfo] = [ + INSTALL_KW.evolve(name='install_header'), + KwargInfo( + 'sources', + ContainerTypeInfo(list, (str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), + listify=True, + required=True, + ), + KwargInfo('install_dir', (str, NoneType)), + KwargInfo('identifier_prefix', (str, NoneType)), + KwargInfo('symbol_prefix', (str, NoneType)), +] + +def annotations_validator(annotations: T.List[T.Union[str, T.List[str]]]) -> T.Optional[str]: + + """Validate gdbus-codegen annotations argument""" + + badlist = 'must be made up of 3 strings for ELEMENT, KEY, and VALUE' + + if all(isinstance(annot, str) for annot in annotations): + if len(annotations) == 3: + return None + else: + return badlist + elif not all(isinstance(annot, list) for annot in annotations): + for c, annot in enumerate(annotations): + if not isinstance(annot, list): + return f'element {c+1} must be a list' + else: + for c, annot in enumerate(annotations): + if len(annot) != 3 or not all(isinstance(i, str) for i in annot): + return f'element {c+1} {badlist}' + return None + # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it # @@ -190,12 +256,14 @@ native_glib_version = None class GnomeModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) - self.gir_dep = None + self.gir_dep: T.Optional[Dependency] = None + self.giscanner: T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]] = None + self.gicompiler: T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]] = None self.install_glib_compile_schemas = False - self.install_gio_querymodules = [] + self.install_gio_querymodules: T.List[str] = [] self.install_gtk_update_icon_cache = False self.install_update_desktop_database = False - self.devenv = None + self.devenv: T.Optional[build.EnvironmentVariables] = None self.methods.update({ 'post_install': self.post_install, 'compile_resources': self.compile_resources, @@ -243,7 +311,10 @@ class GnomeModule(ExtensionModule): def _get_dep(self, state: 'ModuleState', depname: str, native: bool = False, required: bool = True) -> Dependency: kwargs = {'native': native, 'required': required} - return self.interpreter.func_dependency(state.current_node, [depname], kwargs) + # FIXME: Even if we fix the function, mypy still can't figure out what's + # going on here. And we really dont want to call interpreter + # implementations of meson functions anyway. + return self.interpreter.func_dependency(state.current_node, [depname], kwargs) # type: ignore def _get_native_binary(self, state: 'ModuleState', name: str, depname: str, varname: str, required: bool = True) -> T.Union[ExternalProgram, OverrideProgram, 'build.Executable']: @@ -260,7 +331,7 @@ class GnomeModule(ExtensionModule): # Check if pkgconfig has a variable dep = self._get_dep(state, depname, native=True, required=False) if dep.found() and dep.type_name == 'pkgconfig': - value = dep.get_pkgconfig_variable(varname, {}) + value = dep.get_pkgconfig_variable(varname, [], None) if value: return ExternalProgram(name, [value]) @@ -450,8 +521,9 @@ class GnomeModule(ExtensionModule): rv = [target_c, target_h] return ModuleReturnValue(rv, rv) + @staticmethod def _get_gresource_dependencies( - self, state: 'ModuleState', input_file: str, source_dirs: T.List[str], + state: 'ModuleState', input_file: str, source_dirs: T.List[str], dependencies: T.Sequence[T.Union[mesonlib.File, build.CustomTarget, build.CustomTargetIndex]] ) -> T.Tuple[T.List[mesonlib.FileOrString], T.List[T.Union[build.CustomTarget, build.CustomTargetIndex]], T.List[str]]: @@ -525,17 +597,19 @@ class GnomeModule(ExtensionModule): def _get_link_args(self, state: 'ModuleState', lib: T.Union[build.SharedLibrary, build.StaticLibrary], - depends: T.List[build.BuildTarget], + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']], include_rpath: bool = False, - use_gir_args: bool = False) -> T.List[str]: + use_gir_args: bool = False + ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: link_command: T.List[str] = [] + new_depends = list(depends) # Construct link args if isinstance(lib, build.SharedLibrary): libdir = os.path.join(state.environment.get_build_dir(), state.backend.get_target_dir(lib)) link_command.append('-L' + libdir) if include_rpath: link_command.append('-Wl,-rpath,' + libdir) - depends.append(lib) + new_depends.append(lib) # Needed for the following binutils bug: # https://github.com/mesonbuild/meson/issues/1911 # However, g-ir-scanner does not understand -Wl,-rpath @@ -549,13 +623,17 @@ class GnomeModule(ExtensionModule): link_command.append('--extra-library=' + lib.name) else: link_command.append('-l' + lib.name) - return link_command + return link_command, new_depends def _get_dependencies_flags( - self, deps: T.Sequence[T.Union['Dependency', build.SharedLibrary, build.StaticLibrary]], - state: 'ModuleState', depends: T.List[build.BuildTarget], include_rpath: bool = False, - use_gir_args: bool = False, separate_nodedup: bool = False - ) -> T.Tuple[OrderedSet[str], OrderedSet[str], OrderedSet[str], T.Optional[T.List[str]], OrderedSet[str]]: + self, deps: T.Sequence[T.Union['Dependency', build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]], + state: 'ModuleState', + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']], + include_rpath: bool = False, + use_gir_args: bool = False, + separate_nodedup: bool = False + ) -> T.Tuple[OrderedSet[str], OrderedSet[str], OrderedSet[str], T.Optional[T.List[str]], OrderedSet[str], + T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: cflags: OrderedSet[str] = OrderedSet() internal_ldflags: OrderedSet[str] = OrderedSet() external_ldflags: OrderedSet[str] = OrderedSet() @@ -564,6 +642,7 @@ class GnomeModule(ExtensionModule): external_ldflags_nodedup: T.List[str] = [] gi_includes: OrderedSet[str] = OrderedSet() deps = mesonlib.listify(deps) + depends = list(depends) for dep in deps: if isinstance(dep, Dependency): @@ -576,7 +655,8 @@ class GnomeModule(ExtensionModule): cflags.update(state.get_include_args(dep.include_directories)) for lib in dep.libraries: if isinstance(lib, build.SharedLibrary): - internal_ldflags.update(self._get_link_args(state, lib, depends, include_rpath)) + _ld, depends = self._get_link_args(state, lib, depends, include_rpath) + internal_ldflags.update(_ld) libdepflags = self._get_dependencies_flags(lib.get_external_deps(), state, depends, include_rpath, use_gir_args, True) cflags.update(libdepflags[0]) @@ -640,9 +720,9 @@ class GnomeModule(ExtensionModule): external_ldflags = fix_ldflags(external_ldflags) if not separate_nodedup: external_ldflags.update(external_ldflags_nodedup) - return cflags, internal_ldflags, external_ldflags, None, gi_includes + return cflags, internal_ldflags, external_ldflags, None, gi_includes, depends else: - return cflags, internal_ldflags, external_ldflags, external_ldflags_nodedup, gi_includes + return cflags, internal_ldflags, external_ldflags, external_ldflags_nodedup, gi_includes, depends def _unwrap_gir_target(self, girtarget: T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary], state: 'ModuleState' ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary]: @@ -684,7 +764,8 @@ class GnomeModule(ExtensionModule): return p.returncode == 0 and option in o # May mutate depends and gir_inc_dirs - def _scan_include(self, state: 'ModuleState', includes: T.List[T.Union[str, GirTarget]] + @staticmethod + def _scan_include(state: 'ModuleState', includes: T.List[T.Union[str, GirTarget]] ) -> T.Tuple[T.List[str], T.List[str], T.List[GirTarget]]: ret: T.List[str] = [] gir_inc_dirs: T.List[str] = [] @@ -700,7 +781,8 @@ class GnomeModule(ExtensionModule): return ret, gir_inc_dirs, depends - def _scan_langs(self, state: 'ModuleState', langs: T.Iterable[str]) -> T.List[str]: + @staticmethod + def _scan_langs(state: 'ModuleState', langs: T.Iterable[str]) -> T.List[str]: ret: T.List[str] = [] for lang in langs: @@ -711,7 +793,8 @@ class GnomeModule(ExtensionModule): return ret - def _scan_gir_targets(self, state: 'ModuleState', girtargets: T.List[build.BuildTarget]) -> T.List[T.Union[str, build.Executable]]: + @staticmethod + def _scan_gir_targets(state: 'ModuleState', girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Union[str, build.Executable]]: ret: T.List[T.Union[str, build.Executable]] = [] for girtarget in girtargets: @@ -744,7 +827,8 @@ class GnomeModule(ExtensionModule): return ret - def _get_girtargets_langs_compilers(self, girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Tuple[str, 'Compiler']]: + @staticmethod + def _get_girtargets_langs_compilers(girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Tuple[str, 'Compiler']]: ret: T.List[T.Tuple[str, 'Compiler']] = [] for girtarget in girtargets: for lang, compiler in girtarget.compilers.items(): @@ -755,21 +839,24 @@ class GnomeModule(ExtensionModule): return ret - def _get_gir_targets_deps(self, girtargets: T.Sequence[build.BuildTarget] - ) -> T.List[T.Union[build.Target, Dependency]]: - ret: T.List[T.Union[build.Target, Dependency]] = [] + @staticmethod + def _get_gir_targets_deps(girtargets: T.Sequence[build.BuildTarget] + ) -> T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, Dependency]]: + ret: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, Dependency]] = [] for girtarget in girtargets: ret += girtarget.get_all_link_deps() ret += girtarget.get_external_deps() return ret - def _get_gir_targets_inc_dirs(self, girtargets: T.List[build.BuildTarget]) -> T.List[build.IncludeDirs]: + @staticmethod + def _get_gir_targets_inc_dirs(girtargets: T.Sequence[build.BuildTarget]) -> T.List[build.IncludeDirs]: ret: T.List[build.IncludeDirs] = [] for girtarget in girtargets: ret += girtarget.get_include_dirs() return ret - def _get_langs_compilers_flags(self, state: 'ModuleState', langs_compilers: T.List[T.Tuple[str, 'Compiler']] + @staticmethod + def _get_langs_compilers_flags(state: 'ModuleState', langs_compilers: T.List[T.Tuple[str, 'Compiler']] ) -> T.Tuple[T.List[str], T.List[str], T.List[str]]: cflags: T.List[str] = [] internal_ldflags: T.List[str] = [] @@ -797,8 +884,9 @@ class GnomeModule(ExtensionModule): return cflags, internal_ldflags, external_ldflags - def _make_gir_filelist(self, state: 'ModuleState', srcdir: str, ns: str, - nsversion: str, girtargets: T.List[build.BuildTarget], + @staticmethod + def _make_gir_filelist(state: 'ModuleState', srcdir: str, ns: str, + nsversion: str, girtargets: T.Sequence[build.BuildTarget], libsources: T.Sequence[T.Union[ str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex]] @@ -825,9 +913,14 @@ class GnomeModule(ExtensionModule): return gir_filelist_filename - def _make_gir_target(self, state: 'ModuleState', girfile: str, scan_command: T.List[str], - generated_files: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], - depends: T.List[build.Target], kwargs: T.Dict[str, T.Any]) -> GirTarget: + @staticmethod + def _make_gir_target( + state: 'ModuleState', + girfile: str, + scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], + generated_files: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], + depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes']], + kwargs: T.Dict[str, T.Any]) -> GirTarget: install = kwargs['install_gir'] if install is None: install = kwargs['install'] @@ -851,7 +944,9 @@ class GnomeModule(ExtensionModule): return GirTarget(girfile, state.subdir, state.subproject, scankwargs) - def _make_typelib_target(self, state: 'ModuleState', typelib_output: str, typelib_cmd: T.List[str], + @staticmethod + def _make_typelib_target(state: 'ModuleState', typelib_output: str, + typelib_cmd: T.Sequence[T.Union[str, build.Executable, ExternalProgram, build.CustomTarget]], generated_files: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], kwargs: T.Dict[str, T.Any]) -> TypelibTarget: install = kwargs['install_typelib'] @@ -867,7 +962,7 @@ class GnomeModule(ExtensionModule): typelib_kwargs = { 'input': generated_files, 'output': [typelib_output], - 'command': typelib_cmd, + 'command': list(typelib_cmd), 'install': install, 'install_dir': install_dir, 'install_tag': 'typelib', @@ -876,19 +971,24 @@ class GnomeModule(ExtensionModule): return TypelibTarget(typelib_output, state.subdir, state.subproject, typelib_kwargs) - # May mutate depends - def _gather_typelib_includes_and_update_depends(self, state: 'ModuleState', deps: T.List[Dependency], depends: T.List[build.Target]) -> T.List[str]: + @staticmethod + def _gather_typelib_includes_and_update_depends( + state: 'ModuleState', + deps: T.Sequence[T.Union[Dependency, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]], + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']] + ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: # Need to recursively add deps on GirTarget sources from our # dependencies and also find the include directories needed for the # typelib generation custom target below. typelib_includes: T.List[str] = [] + new_depends = list(depends) for dep in deps: # Add a dependency on each GirTarget listed in dependencies and add # the directory where it will be generated to the typelib includes if isinstance(dep, InternalDependency): for source in dep.sources: if isinstance(source, GirTarget) and source not in depends: - depends.append(source) + new_depends.append(source) subdir = os.path.join(state.environment.get_build_dir(), source.get_subdir()) if subdir not in typelib_includes: @@ -899,10 +999,10 @@ class GnomeModule(ExtensionModule): # FIXME: Store this in the original form from declare_dependency() # so it can be used here directly. elif isinstance(dep, build.SharedLibrary): - for source in dep.generated: - if isinstance(source, GirTarget): + for g_source in dep.generated: + if isinstance(g_source, GirTarget): subdir = os.path.join(state.environment.get_build_dir(), - source.get_subdir()) + g_source.get_subdir()) if subdir not in typelib_includes: typelib_includes.append(subdir) if isinstance(dep, Dependency): @@ -910,9 +1010,10 @@ class GnomeModule(ExtensionModule): assert isinstance(girdir, str), 'for mypy' if girdir and girdir not in typelib_includes: typelib_includes.append(girdir) - return typelib_includes + return typelib_includes, new_depends - def _get_external_args_for_langs(self, state: 'ModuleState', langs: T.Sequence[str]) -> T.List[str]: + @staticmethod + def _get_external_args_for_langs(state: 'ModuleState', langs: T.Sequence[str]) -> T.List[str]: ret: T.List[str] = [] for lang in langs: ret += mesonlib.listify(state.environment.coredata.get_external_args(MachineChoice.HOST, lang)) @@ -977,7 +1078,7 @@ class GnomeModule(ExtensionModule): srcdir = os.path.join(state.environment.get_source_dir(), state.subdir) builddir = os.path.join(state.environment.get_build_dir(), state.subdir) - depends: T.List[T.Union['FileOrString', build.GeneratedTypes, build.Executable, build.SharedLibrary, build.StaticLibrary]] = [] + depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget]] = [] depends.extend(gir_dep.sources) depends.extend(girtargets) @@ -986,11 +1087,11 @@ class GnomeModule(ExtensionModule): deps = self._get_gir_targets_deps(girtargets) deps += kwargs['dependencies'] deps += [gir_dep] - typelib_includes = self._gather_typelib_includes_and_update_depends(state, deps, depends) + typelib_includes, depends = self._gather_typelib_includes_and_update_depends(state, deps, depends) # ldflags will be misinterpreted by gir scanner (showing # spurious dependencies) but building GStreamer fails if they # are not used here. - dep_cflags, dep_internal_ldflags, dep_external_ldflags, _, gi_includes = \ + dep_cflags, dep_internal_ldflags, dep_external_ldflags, _, gi_includes, depends = \ self._get_dependencies_flags(deps, state, depends, use_gir_args=True) scan_cflags = [] scan_cflags += list(self._get_scanner_cflags(cflags)) @@ -1032,7 +1133,7 @@ class GnomeModule(ExtensionModule): scan_command += scan_cflags scan_command += ['--cflags-end'] scan_command += state.get_include_args(inc_dirs) - scan_command += state.get_include_args(list(gi_includes) + gir_inc_dirs + inc_dirs, prefix='--add-include-path=') + scan_command += state.get_include_args(itertools.chain(gi_includes, gir_inc_dirs, inc_dirs), prefix='--add-include-path=') scan_command += list(scan_internal_ldflags) scan_command += self._scan_gir_targets(state, girtargets) scan_command += self._scan_langs(state, [lc[0] for lc in langs_compilers]) @@ -1050,7 +1151,10 @@ class GnomeModule(ExtensionModule): generated_files = [f for f in libsources if isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex))] - scan_target = self._make_gir_target(state, girfile, scan_command, generated_files, depends, kwargs) + scan_target = self._make_gir_target( + state, girfile, scan_command, generated_files, depends, + # We have to cast here because mypy can't figure this out + T.cast(T.Dict[str, T.Any], kwargs)) typelib_output = f'{ns}-{nsversion}.typelib' typelib_cmd = [gicompiler, scan_target, '--output', '@OUTPUT@'] @@ -1059,7 +1163,7 @@ class GnomeModule(ExtensionModule): for incdir in typelib_includes: typelib_cmd += ["--includedir=" + incdir] - typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, generated_files, kwargs) + typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, generated_files, T.cast(T.Dict[str, T.Any], kwargs)) self._devenv_prepend('GI_TYPELIB_PATH', os.path.join(state.environment.get_build_dir(), state.subdir)) @@ -1111,47 +1215,102 @@ class GnomeModule(ExtensionModule): raise MesonException('Yelp requires a list of sources') elif args[1]: mlog.warning('"gnome.yelp" ignores positional sources arguments when the "sources" keyword argument is set') - source_str = '@@'.join(sources) + sources_files = [mesonlib.File.from_source_file(state.environment.source_dir, + os.path.join(state.subdir, 'C'), + s) for s in sources] langs = kwargs['languages'] + if not langs: + langs = read_linguas(os.path.join(state.environment.source_dir, state.subdir)) + + media = kwargs['media'] + symlinks = kwargs['symlink_media'] + targets: T.List[T.Union['build.Target', build.Data, build.SymlinkData]] = [] + potargets: T.List[build.RunTarget] = [] + + itstool = state.find_program('itstool') + msgmerge = state.find_program('msgmerge') + msgfmt = state.find_program('msgfmt') + + install_dir = os.path.join(state.environment.get_datadir(), 'help') + c_install_dir = os.path.join(install_dir, 'C', project_id) + c_data = build.Data(sources_files, c_install_dir, c_install_dir, + mesonlib.FileMode(), state.subproject) + targets.append(c_data) + + media_files: T.List[mesonlib.File] = [] + for m in media: + f = mesonlib.File.from_source_file(state.environment.source_dir, + os.path.join(state.subdir, 'C'), m) + media_files.append(f) + m_install_dir = os.path.join(c_install_dir, os.path.dirname(m)) + m_data = build.Data([f], m_install_dir, m_install_dir, + mesonlib.FileMode(), state.subproject) + targets.append(m_data) + + pot_file = os.path.join('@SOURCE_ROOT@', state.subdir, 'C', project_id + '.pot') + pot_sources = [os.path.join('@SOURCE_ROOT@', state.subdir, 'C', s) for s in sources] + pot_args: T.List[T.Union['ExternalProgram', str]] = [itstool, '-o', pot_file] + pot_args.extend(pot_sources) + pottarget = build.RunTarget(f'help-{project_id}-pot', pot_args, [], + os.path.join(state.subdir, 'C'), state.subproject) + targets.append(pottarget) + + for l in langs: + l_subdir = os.path.join(state.subdir, l) + l_install_dir = os.path.join(install_dir, l, project_id) + + for i, m in enumerate(media): + m_dir = os.path.dirname(m) + m_install_dir = os.path.join(l_install_dir, m_dir) + l_data: T.Union[build.Data, build.SymlinkData] + if symlinks: + link_target = os.path.join(os.path.relpath(c_install_dir, start=m_install_dir), m) + l_data = build.SymlinkData(link_target, os.path.basename(m), + m_install_dir, state.subproject) + else: + try: + m_file = mesonlib.File.from_source_file(state.environment.source_dir, l_subdir, m) + except MesonException: + m_file = media_files[i] + l_data = build.Data([m_file], m_install_dir, m_install_dir, + mesonlib.FileMode(), state.subproject) + targets.append(l_data) + + po_file = l + '.po' + po_args: T.List[T.Union['ExternalProgram', str]] = [ + msgmerge, '-q', '-o', + os.path.join('@SOURCE_ROOT@', l_subdir, po_file), + os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file] + potarget = build.RunTarget(f'help-{project_id}-{l}-update-po', + po_args, [pottarget], l_subdir, state.subproject) + targets.append(potarget) + potargets.append(potarget) + + gmo_file = project_id + '-' + l + '.gmo' + gmo_kwargs = {'command': [msgfmt, '@INPUT@', '-o', '@OUTPUT@'], + 'input': po_file, + 'output': gmo_file, + } + gmotarget = build.CustomTarget(f'help-{project_id}-{l}-gmo', l_subdir, state.subproject, gmo_kwargs) + targets.append(gmotarget) + + merge_kwargs = {'command': [itstool, '-m', os.path.join(l_subdir, gmo_file), + '-o', '@OUTDIR@', '@INPUT@'], + 'input': sources_files, + 'output': sources, + 'depends': gmotarget, + 'install': True, + 'install_dir': l_install_dir, + } + mergetarget = build.CustomTarget(f'help-{project_id}-{l}', l_subdir, state.subproject, merge_kwargs) + targets.append(mergetarget) - script = state.environment.get_build_command() - inscript_args = ['--internal', - 'yelphelper', - 'install', - '--subdir=' + state.subdir, - '--id=' + project_id, - '--installdir=' + os.path.join(state.environment.get_datadir(), 'help'), - '--sources=' + source_str] - if kwargs['symlink_media']: - inscript_args.append('--symlinks=true') - if kwargs['media']: - inscript_args.append('--media=' + '@@'.join(kwargs['media'])) - if langs: - inscript_args.append('--langs=' + '@@'.join(langs)) - inscript = state.backend.get_executable_serialisation(script + inscript_args) - - potargs = state.environment.get_build_command() + [ - '--internal', 'yelphelper', 'pot', - '--subdir=' + state.subdir, - '--id=' + project_id, - '--sources=' + source_str, - ] - pottarget = build.RunTarget('help-' + project_id + '-pot', potargs, - [], state.subdir, state.subproject) - - poargs = state.environment.get_build_command() + [ - '--internal', 'yelphelper', 'update-po', - '--subdir=' + state.subdir, - '--id=' + project_id, - '--sources=' + source_str, - '--langs=' + '@@'.join(langs), - ] - potarget = build.RunTarget('help-' + project_id + '-update-po', poargs, - [], state.subdir, state.subproject) - - rv: T.List[T.Union[build.ExecutableSerialisation, build.RunTarget]] = [inscript, pottarget, potarget] - return ModuleReturnValue(None, rv) + allpotarget = build.AliasTarget(f'help-{project_id}-update-po', potargets, + state.subdir, state.subproject) + targets.append(allpotarget) + + return ModuleReturnValue(None, targets) @typed_pos_args('gnome.gtkdoc', str) @typed_kwargs( @@ -1163,11 +1322,11 @@ class GnomeModule(ExtensionModule): 'dependencies', ContainerTypeInfo(list, (Dependency, build.SharedLibrary, build.StaticLibrary)), listify=True, default=[]), - KwargInfo('expand_content_files', ContainerTypeInfo(list, str), default=[], listify=True), + KwargInfo('expand_content_files', ContainerTypeInfo(list, (str, mesonlib.File)), default=[], listify=True), KwargInfo('fixxref_args', ContainerTypeInfo(list, str), default=[], listify=True), - KwargInfo('gobject_typesfile', ContainerTypeInfo(list, str), default=[], listify=True), + KwargInfo('gobject_typesfile', ContainerTypeInfo(list, (str, mesonlib.File)), default=[], listify=True), KwargInfo('html_args', ContainerTypeInfo(list, str), default=[], listify=True), - KwargInfo('html_assets', ContainerTypeInfo(list, str), default=[], listify=True), + KwargInfo('html_assets', ContainerTypeInfo(list, (str, mesonlib.File)), default=[], listify=True), KwargInfo('ignore_headers', ContainerTypeInfo(list, str), default=[], listify=True), KwargInfo( 'include_directories', @@ -1193,7 +1352,7 @@ class GnomeModule(ExtensionModule): main_xml = kwargs['main_xml'] if main_xml is not None: if main_file is not None: - raise InvalidArguments('gnome.gtkdoc: main_xml and main_xgml are exclusive arguments') + raise InvalidArguments('gnome.gtkdoc: main_xml and main_sgml are exclusive arguments') main_file = main_xml moduleversion = kwargs['module_version'] targetname = modulename + ('-' + moduleversion if moduleversion else '') + '-doc' @@ -1293,7 +1452,8 @@ class GnomeModule(ExtensionModule): def _get_build_args(self, c_args: T.List[str], inc_dirs: T.List[T.Union[str, build.IncludeDirs]], deps: T.List[T.Union[Dependency, build.SharedLibrary, build.StaticLibrary]], - state: 'ModuleState', depends: T.List[build.BuildTarget]) -> T.List[str]: + state: 'ModuleState', + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes']]) -> T.List[str]: args: T.List[str] = [] cflags = c_args.copy() deps_cflags, internal_ldflags, external_ldflags, *_ = \ @@ -1328,7 +1488,7 @@ class GnomeModule(ExtensionModule): def gtkdoc_html_dir(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> str: return os.path.join('share/gtk-doc/html', args[0]) - @typed_pos_args('gnome.gdbus_codegen', str, optargs=[str]) + @typed_pos_args('gnome.gdbus_codegen', str, optargs=[(str, mesonlib.File)]) @typed_kwargs( 'gnome.gdbus_codegen', _BUILD_BY_DEFAULT.evolve(since='0.40.0'), @@ -1338,10 +1498,10 @@ class GnomeModule(ExtensionModule): KwargInfo('namespace', (str, NoneType)), KwargInfo('object_manager', bool, default=False), KwargInfo( - 'annotations', ContainerTypeInfo(list, str), - listify=True, + 'annotations', ContainerTypeInfo(list, (list, str)), default=[], - validator=lambda x: 'must be made up of 3 strings for ELEMENT, KEY, and VALUE' if len(x) != 3 else None + validator=annotations_validator, + convertor=lambda x: [x] if x and isinstance(x[0], str) else x, ), KwargInfo('install_header', bool, default=False, since='0.46.0'), KwargInfo('install_dir', (str, NoneType), since='0.46.0'), @@ -1350,7 +1510,7 @@ class GnomeModule(ExtensionModule): 'autocleanup', str, default='default', since='0.47.0', validator=in_set_validator({'all', 'none', 'objects'})), ) - def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str]], + def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional['FileOrString']], kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] xml_files: T.List['FileOrString'] = [args[1]] if args[1] else [] @@ -1381,9 +1541,9 @@ class GnomeModule(ExtensionModule): build_by_default = kwargs['build_by_default'] # Annotations are a bit ugly in that they are a list of lists of strings... - if kwargs['annotations']: + for annot in kwargs['annotations']: cmd.append('--annotate') - cmd.extend(kwargs['annotations']) + cmd.extend(annot) targets = [] install_header = kwargs['install_header'] @@ -1475,80 +1635,54 @@ class GnomeModule(ExtensionModule): return ModuleReturnValue(targets, targets) - @permittedKwargs({'sources', 'c_template', 'h_template', 'install_header', 'install_dir', - 'comments', 'identifier_prefix', 'symbol_prefix', 'eprod', 'vprod', - 'fhead', 'fprod', 'ftail', 'vhead', 'vtail', 'depends'}) @typed_pos_args('gnome.mkenums', str) - def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: + @typed_kwargs( + 'gnome.mkenums', + *_MK_ENUMS_COMMON_KWS, + DEPENDS_KW, + KwargInfo('c_template', (str, mesonlib.File, NoneType)), + KwargInfo('h_template', (str, mesonlib.File, NoneType)), + KwargInfo('comments', (str, NoneType)), + KwargInfo('eprod', (str, NoneType)), + KwargInfo('fhead', (str, NoneType)), + KwargInfo('fprod', (str, NoneType)), + KwargInfo('ftail', (str, NoneType)), + KwargInfo('vhead', (str, NoneType)), + KwargInfo('vprod', (str, NoneType)), + KwargInfo('vtail', (str, NoneType)), + ) + def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnums') -> ModuleReturnValue: basename = args[0] - if 'sources' not in kwargs: - raise MesonException('Missing keyword argument "sources".') - sources = kwargs.pop('sources') - if isinstance(sources, str): - sources = [sources] - elif not isinstance(sources, list): - raise MesonException( - 'Sources keyword argument must be a string or array.') + c_template = kwargs['c_template'] + if isinstance(c_template, mesonlib.File): + c_template = c_template.absolute_path(state.environment.source_dir, state.environment.build_dir) + h_template = kwargs['h_template'] + if isinstance(h_template, mesonlib.File): + h_template = h_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - cmd = [] + cmd: T.List[str] = [] known_kwargs = ['comments', 'eprod', 'fhead', 'fprod', 'ftail', - 'identifier_prefix', 'symbol_prefix', 'template', + 'identifier_prefix', 'symbol_prefix', 'vhead', 'vprod', 'vtail'] - known_custom_target_kwargs = ['install_dir', 'build_always', - 'depends', 'depend_files'] - c_template = h_template = None - install_header = False - for arg, value in kwargs.items(): - if arg == 'sources': - raise AssertionError("sources should've already been handled") - elif arg == 'c_template': - c_template = value - if isinstance(c_template, mesonlib.File): - c_template = c_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - if 'template' in kwargs: - raise MesonException('Mkenums does not accept both ' - 'c_template and template keyword ' - 'arguments at the same time.') - elif arg == 'h_template': - h_template = value - if isinstance(h_template, mesonlib.File): - h_template = h_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - if 'template' in kwargs: - raise MesonException('Mkenums does not accept both ' - 'h_template and template keyword ' - 'arguments at the same time.') - elif arg == 'install_header': - install_header = value - elif arg in known_kwargs: - cmd += ['--' + arg.replace('_', '-'), value] - elif arg not in known_custom_target_kwargs: - raise MesonException( - f'Mkenums does not take a {arg} keyword argument.') - cmd = [state.find_program(['glib-mkenums', 'mkenums'])] + cmd - custom_kwargs = {} - for arg in known_custom_target_kwargs: - if arg in kwargs: - custom_kwargs[arg] = kwargs[arg] + for arg in known_kwargs: + # mypy can't figure this out + if kwargs[arg]: # type: ignore + cmd += ['--' + arg.replace('_', '-'), kwargs[arg]] # type: ignore - targets = [] + targets: T.List[CustomTarget] = [] + h_target: T.Optional[CustomTarget] = None if h_template is not None: h_output = os.path.basename(os.path.splitext(h_template)[0]) # We always set template as the first element in the source array # so --template consumes it. h_cmd = cmd + ['--template', '@INPUT@'] - h_sources = [h_template] + sources - - # Copy so we don't mutate the arguments for the c_template - h_kwargs = custom_kwargs.copy() - h_kwargs['install'] = install_header - if 'install_dir' not in h_kwargs: - h_kwargs['install_dir'] = \ - state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) - h_target = self._make_mkenum_custom_target(state, h_sources, - h_output, h_cmd, - h_kwargs) + h_sources: T.List[T.Union[FileOrString, 'build.GeneratedTypes']] = [h_template] + h_sources.extend(kwargs['sources']) + h_target = self._make_mkenum_impl( + state, h_sources, h_output, h_cmd, install=kwargs['install_header'], + install_dir=kwargs['install_dir']) targets.append(h_target) if c_template is not None: @@ -1556,110 +1690,86 @@ class GnomeModule(ExtensionModule): # We always set template as the first element in the source array # so --template consumes it. c_cmd = cmd + ['--template', '@INPUT@'] - c_sources = [c_template] + sources - - c_kwargs = custom_kwargs.copy() - # Never install the C file. Complain on bug tracker if you need it. - c_kwargs['install'] = False - c_kwargs['install_dir'] = [] - if h_template is not None: - if 'depends' in custom_kwargs: - c_kwargs['depends'] += [h_target] - else: - c_kwargs['depends'] = h_target - c_target = self._make_mkenum_custom_target(state, c_sources, - c_output, c_cmd, - c_kwargs) + c_sources: T.List[T.Union[FileOrString, 'build.GeneratedTypes']] = [c_template] + c_sources.extend(kwargs['sources']) + + depends = kwargs['depends'].copy() + if h_target is not None: + depends.append(h_target) + c_target = self._make_mkenum_impl( + state, c_sources, c_output, c_cmd, depends=depends) targets.insert(0, c_target) if c_template is None and h_template is None: generic_cmd = cmd + ['@INPUT@'] - custom_kwargs['install'] = install_header - if 'install_dir' not in custom_kwargs: - custom_kwargs['install_dir'] = \ - state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) - target = self._make_mkenum_custom_target(state, sources, basename, - generic_cmd, custom_kwargs) + target = self._make_mkenum_impl( + state, kwargs['sources'], basename, generic_cmd, + install=kwargs['install_header'], + install_dir=kwargs['install_dir']) return ModuleReturnValue(target, [target]) - elif len(targets) == 1: - return ModuleReturnValue(targets[0], [targets[0]]) else: return ModuleReturnValue(targets, targets) @FeatureNew('gnome.mkenums_simple', '0.42.0') @typed_pos_args('gnome.mkenums_simple', str) - def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: + @typed_kwargs( + 'gnome.mkenums_simple', + *_MK_ENUMS_COMMON_KWS, + KwargInfo('header_prefix', str, default=''), + KwargInfo('function_prefix', str, default=''), + KwargInfo('body_prefix', str, default=''), + KwargInfo('decorator', str, default=''), + ) + def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnumsSimple') -> ModuleReturnValue: hdr_filename = f'{args[0]}.h' body_filename = f'{args[0]}.c' - # not really needed, just for sanity checking - forbidden_kwargs = ['c_template', 'h_template', 'eprod', 'fhead', - 'fprod', 'ftail', 'vhead', 'vtail', 'comments'] - for arg in forbidden_kwargs: - if arg in kwargs: - raise MesonException(f'mkenums_simple() does not take a {arg} keyword argument') - - # kwargs to pass as-is from mkenums_simple() to mkenums() - shared_kwargs = ['sources', 'install_header', 'install_dir', - 'identifier_prefix', 'symbol_prefix'] - mkenums_kwargs = {} - for arg in shared_kwargs: - if arg in kwargs: - mkenums_kwargs[arg] = kwargs[arg] - - # .c file generation - c_file_kwargs = copy.deepcopy(mkenums_kwargs) - if 'sources' not in kwargs: - raise MesonException('Missing keyword argument "sources".') - sources = kwargs['sources'] - if isinstance(sources, str): - sources = [sources] - elif not isinstance(sources, list): - raise MesonException( - 'Sources keyword argument must be a string or array.') - - # The `install_header` argument will be used by mkenums() when - # not using template files, so we need to forcibly unset it - # when generating the C source file, otherwise we will end up - # installing it - c_file_kwargs['install_header'] = False - - header_prefix = kwargs.get('header_prefix', '') - decl_decorator = kwargs.get('decorator', '') - func_prefix = kwargs.get('function_prefix', '') - body_prefix = kwargs.get('body_prefix', '') + header_prefix = kwargs['header_prefix'] + decl_decorator = kwargs['decorator'] + func_prefix = kwargs['function_prefix'] + body_prefix = kwargs['body_prefix'] + cmd: T.List[str] = [] + if kwargs['identifier_prefix']: + cmd.extend(['--identifier-prefix', kwargs['identifier_prefix']]) + if kwargs['symbol_prefix']: + cmd.extend(['--symbol-prefix', kwargs['symbol_prefix']]) + + c_cmd = cmd.copy() # Maybe we should write our own template files into the build dir # instead, but that seems like much more work, nice as it would be. fhead = '' if body_prefix != '': fhead += '%s\n' % body_prefix fhead += '#include "%s"\n' % hdr_filename - for hdr in sources: + for hdr in kwargs['sources']: fhead += '#include "{}"\n'.format(os.path.basename(str(hdr))) fhead += textwrap.dedent( ''' #define C_ENUM(v) ((gint) v) #define C_FLAGS(v) ((guint) v) ''') - c_file_kwargs['fhead'] = fhead + c_cmd.extend(['--fhead', fhead]) - c_file_kwargs['fprod'] = textwrap.dedent( + c_cmd.append('--fprod') + c_cmd.append(textwrap.dedent( ''' /* enumerations from "@basename@" */ - ''') + ''')) - c_file_kwargs['vhead'] = textwrap.dedent( + c_cmd.append('--vhead') + c_cmd.append(textwrap.dedent( f''' GType {func_prefix}@enum_name@_get_type (void) {{ static gsize gtype_id = 0; - static const G@Type@Value values[] = {{''') + static const G@Type@Value values[] = {{''')) - c_file_kwargs['vprod'] = ' { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },' + c_cmd.extend(['--vprod', ' { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },']) - c_file_kwargs['vtail'] = textwrap.dedent( + c_cmd.append('--vtail') + c_cmd.append(textwrap.dedent( ''' { 0, NULL, NULL } }; if (g_once_init_enter (>ype_id)) { @@ -1667,55 +1777,72 @@ class GnomeModule(ExtensionModule): g_once_init_leave (>ype_id, new_type); } return (GType) gtype_id; - }''') + }''')) + c_cmd.append('@INPUT@') - rv = self.mkenums(state, [body_filename], c_file_kwargs) - c_file = rv.return_value + c_file = self._make_mkenum_impl(state, kwargs['sources'], body_filename, c_cmd) # .h file generation - h_file_kwargs = copy.deepcopy(mkenums_kwargs) + h_cmd = cmd.copy() - h_file_kwargs['fhead'] = textwrap.dedent( + h_cmd.append('--fhead') + h_cmd.append(textwrap.dedent( f'''#pragma once #include <glib-object.h> {header_prefix} G_BEGIN_DECLS - ''') + ''')) - h_file_kwargs['fprod'] = textwrap.dedent( + h_cmd.append('--fprod') + h_cmd.append(textwrap.dedent( ''' /* enumerations from "@basename@" */ - ''') + ''')) - h_file_kwargs['vhead'] = textwrap.dedent( + h_cmd.append('--vhead') + h_cmd.append(textwrap.dedent( f''' {decl_decorator} GType {func_prefix}@enum_name@_get_type (void); - #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ ({func_prefix}@enum_name@_get_type())''') + #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ ({func_prefix}@enum_name@_get_type())''')) - h_file_kwargs['ftail'] = textwrap.dedent( + h_cmd.append('--ftail') + h_cmd.append(textwrap.dedent( ''' - G_END_DECLS''') + G_END_DECLS''')) + h_cmd.append('@INPUT@') - rv = self.mkenums(state, [hdr_filename], h_file_kwargs) - h_file = rv.return_value + h_file = self._make_mkenum_impl( + state, kwargs['sources'], hdr_filename, h_cmd, + install=kwargs['install_header'], + install_dir=kwargs['install_dir']) return ModuleReturnValue([c_file, h_file], [c_file, h_file]) @staticmethod - def _make_mkenum_custom_target( + def _make_mkenum_impl( state: 'ModuleState', sources: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], - output: str, cmd: T.List[str], kwargs: T.Dict[str, T.Any]) -> build.CustomTarget: + output: str, + cmd: T.List[str], + *, + install: bool = False, + install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, + depends: T.Optional[T.Sequence[T.Union[CustomTarget, CustomTargetIndex, BuildTarget]]] = None + ) -> build.CustomTarget: + real_cmd: T.List[T.Union[str, ExternalProgram]] = [state.find_program(['glib-mkenums', 'mkenums'])] + real_cmd.extend(cmd) custom_kwargs = { 'input': sources, 'output': [output], 'capture': True, - 'command': cmd + 'command': real_cmd, + 'install': install, + 'install_dir': install_dir or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')), + 'depends': list(depends or []), } - custom_kwargs.update(kwargs) return build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs, # https://github.com/mesonbuild/meson/issues/973 absolute_paths=True) @@ -1732,7 +1859,7 @@ class GnomeModule(ExtensionModule): KwargInfo('nostdinc', bool, default=False), KwargInfo('prefix', (str, NoneType)), KwargInfo('skip_source', bool, default=False), - KwargInfo('sources', ContainerTypeInfo(list, str, allow_empty=False), listify=True, required=True), + KwargInfo('sources', ContainerTypeInfo(list, (str, mesonlib.File), allow_empty=False), listify=True, required=True), KwargInfo('stdinc', bool, default=False), KwargInfo('valist_marshallers', bool, default=False), ) @@ -1793,7 +1920,7 @@ class GnomeModule(ExtensionModule): return ModuleReturnValue(rv, rv) def _extract_vapi_packages(self, state: 'ModuleState', packages: T.List[T.Union[InternalDependency, str]], - ) -> T.Tuple[T.List[str], T.List[build.Target], T.List[str], T.List[str], T.List[str]]: + ) -> T.Tuple[T.List[str], T.List[VapiTarget], T.List[str], T.List[str], T.List[str]]: ''' Packages are special because we need to: - Get a list of packages for the .deps file @@ -1803,7 +1930,7 @@ class GnomeModule(ExtensionModule): ''' if not packages: return [], [], [], [], [] - vapi_depends: T.List[build.Target] = [] + vapi_depends: T.List[VapiTarget] = [] vapi_packages: T.List[str] = [] vapi_includes: T.List[str] = [] vapi_args: T.List[str] = [] @@ -1866,7 +1993,7 @@ class GnomeModule(ExtensionModule): KwargInfo('packages', ContainerTypeInfo(list, (str, InternalDependency)), listify=True, default=[]), ) def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenerateVapi') -> ModuleReturnValue: - created_values: T.List[Dependency] = [] + created_values: T.List[T.Union[Dependency, build.Data]] = [] library = args[0] build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) @@ -1874,7 +2001,7 @@ class GnomeModule(ExtensionModule): cmd: T.List[T.Union[str, 'ExternalProgram']] cmd = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] cmd.extend([f'--vapidir={d}' for d in kwargs['vapi_dirs']]) - cmd.extend([f'--metadatdir={d}' for d in kwargs['metadata_dirs']]) + cmd.extend([f'--metadatadir={d}' for d in kwargs['metadata_dirs']]) cmd.extend([f'--girdir={d}' for d in kwargs['gir_dirs']]) cmd += pkg_cmd cmd += ['--metadatadir=' + source_dir] diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 4a0b39e..6c5d347 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -183,7 +183,8 @@ class DependenciesHelper: # lists in case a library is link_with and link_whole at the same time. # See remove_dups() below. self.link_whole_targets.append(t) - self._add_lib_dependencies(t.link_targets, t.link_whole_targets, t.external_deps, public) + if isinstance(t, build.BuildTarget): + self._add_lib_dependencies(t.link_targets, t.link_whole_targets, t.external_deps, public) def add_version_reqs(self, name, version_reqs): if version_reqs: diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 90335a1..2b62ea3 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -79,10 +79,19 @@ class _PythonDependencyBase(_Base): class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase): def __init__(self, name: str, environment: 'Environment', - kwargs: T.Dict[str, T.Any], installation: 'PythonInstallation'): + kwargs: T.Dict[str, T.Any], installation: 'PythonInstallation', + libpc: bool = False): + if libpc: + mlog.debug(f'Searching for {name!r} via pkgconfig lookup in LIBPC') + else: + mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths') + PkgConfigDependency.__init__(self, name, environment, kwargs) _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False)) + if libpc and not self.is_found: + mlog.debug(f'"python-{self.version}" could not be found in LIBPC, this is likely due to a relocated python installation') + class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase): @@ -240,12 +249,15 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice', # If python-X.Y.pc exists in LIBPC, we will try to use it def wrap_in_pythons_pc_dir(name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], installation: 'PythonInstallation') -> 'ExternalDependency': + if not pkg_libdir: + # there is no LIBPC, so we can't search in it + return NotFoundDependency('python', env) + old_pkg_libdir = os.environ.pop('PKG_CONFIG_LIBDIR', None) old_pkg_path = os.environ.pop('PKG_CONFIG_PATH', None) - if pkg_libdir: - os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir + os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir try: - return PythonPkgConfigDependency(name, env, kwargs, installation) + return PythonPkgConfigDependency(name, env, kwargs, installation, True) finally: def set_env(name, value): if value is not None: @@ -255,10 +267,11 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice', set_env('PKG_CONFIG_LIBDIR', old_pkg_libdir) set_env('PKG_CONFIG_PATH', old_pkg_path) - candidates.extend([ - functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation), - functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation) - ]) + candidates.append(functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation)) + # We only need to check both, if a python install has a LIBPC. It might point to the wrong location, + # e.g. relocated / cross compilation, but the presence of LIBPC indicates we should definitely look for something. + if pkg_libdir is not None: + candidates.append(functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation)) if DependencyMethods.SYSTEM in methods: candidates.append(functools.partial(PythonSystemDependency, 'python', env, kwargs, installation)) diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index dc1f7c7..f7600e2 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -16,12 +16,13 @@ import sysconfig from .. import mesonlib from . import ExtensionModule -from ..interpreterbase import noKwargs, permittedKwargs, FeatureDeprecated +from ..interpreterbase import noKwargs, permittedKwargs, FeatureDeprecated, FeatureNew from ..build import known_shmod_kwargs from ..programs import ExternalProgram class Python3Module(ExtensionModule): + @FeatureNew('python3 module', '0.38.0') @FeatureDeprecated('python3 module', '0.48.0') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/mesonbuild/modules/qt6.py b/mesonbuild/modules/qt6.py index d9cd651..3cfe243 100644 --- a/mesonbuild/modules/qt6.py +++ b/mesonbuild/modules/qt6.py @@ -13,10 +13,12 @@ # limitations under the License. from .qt import QtBaseModule +from ..interpreterbase import FeatureNew class Qt6Module(QtBaseModule): + @FeatureNew('Qt6 Module', '0.57.0') def __init__(self, interpreter): QtBaseModule.__init__(self, interpreter, qt_version=6) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py deleted file mode 100644 index 1fae144..0000000 --- a/mesonbuild/modules/rpm.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright 2015 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -'''This module provides helper functions for RPM related -functionality such as generating template RPM spec file.''' - -from .. import build -from .. import compilers -import datetime -from .. import mlog -from . import GirTarget, TypelibTarget -from . import ExtensionModule -from ..interpreterbase import noKwargs - -import os - -class RPMModule(ExtensionModule): - def __init__(self, interpreter): - super().__init__(interpreter) - self.methods.update({ - 'generate_spec_template': self.generate_spec_template, - }) - - @noKwargs - def generate_spec_template(self, state, args, kwargs): - required_compilers = self.__get_required_compilers(state) - proj = state.project_name.replace(' ', '_').replace('\t', '_') - so_installed = False - devel_subpkg = False - files = set() - files_devel = set() - to_delete = set() - for target in state.targets.values(): - if isinstance(target, build.Executable) and target.need_install: - files.add('%%{_bindir}/%s' % target.get_filename()) - elif isinstance(target, build.SharedLibrary) and target.need_install: - files.add('%%{_libdir}/%s' % target.get_filename()) - for alias in target.get_aliases(): - if alias.endswith('.so'): - files_devel.add('%%{_libdir}/%s' % alias) - else: - files.add('%%{_libdir}/%s' % alias) - so_installed = True - elif isinstance(target, build.StaticLibrary) and target.need_install: - to_delete.add('%%{buildroot}%%{_libdir}/%s' % target.get_filename()) - mlog.warning('removing', mlog.bold(target.get_filename()), - 'from package because packaging static libs not recommended') - elif isinstance(target, GirTarget) and target.should_install(): - files_devel.add('%%{_datadir}/gir-1.0/%s' % target.get_filename()[0]) - elif isinstance(target, TypelibTarget) and target.should_install(): - files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0]) - for header in state.headers: - if header.get_install_subdir(): - files_devel.add('%%{_includedir}/%s/' % header.get_install_subdir()) - else: - for hdr_src in header.get_sources(): - files_devel.add('%%{_includedir}/%s' % hdr_src) - for man in state.man: - for man_file in man.get_sources(): - if man.locale: - files.add('%%{_mandir}/%s/man%u/%s.*' % (man.locale, int(man_file.split('.')[-1]), man_file)) - else: - files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file)) - if files_devel: - devel_subpkg = True - - filename = os.path.join(state.environment.get_build_dir(), - '%s.spec' % proj) - with open(filename, 'w+', encoding='utf-8') as fn: - fn.write('Name: %s\n' % proj) - fn.write('Version: # FIXME\n') - fn.write('Release: 1%{?dist}\n') - fn.write('Summary: # FIXME\n') - fn.write('License: # FIXME\n') - fn.write('\n') - fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n') - fn.write('\n') - fn.write('BuildRequires: meson\n') - for compiler in required_compilers: - fn.write('BuildRequires: %s\n' % compiler) - for dep in state.environment.coredata.deps.host: - fn.write('BuildRequires: pkgconfig(%s)\n' % dep[0]) -# ext_libs and ext_progs have been removed from coredata so the following code -# no longer works. It is kept as a reminder of the idea should anyone wish -# to re-implement it. -# -# for lib in state.environment.coredata.ext_libs.values(): -# name = lib.get_name() -# fn.write('BuildRequires: {} # FIXME\n'.format(name)) -# mlog.warning('replace', mlog.bold(name), 'with the real package.', -# 'You can use following command to find package which ' -# 'contains this lib:', -# mlog.bold("dnf provides '*/lib{}.so'".format(name))) -# for prog in state.environment.coredata.ext_progs.values(): -# if not prog.found(): -# fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' % -# prog.get_name()) -# else: -# fn.write('BuildRequires: {}\n'.format(prog.get_path())) - fn.write('\n') - fn.write('%description\n') - fn.write('\n') - if devel_subpkg: - fn.write('%package devel\n') - fn.write('Summary: Development files for %{name}\n') - fn.write('Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}{version}-%{release}\n') - fn.write('\n') - fn.write('%description devel\n') - fn.write('Development files for %{name}.\n') - fn.write('\n') - fn.write('%prep\n') - fn.write('%autosetup\n') - fn.write('\n') - fn.write('%build\n') - fn.write('%meson\n') - fn.write('%meson_build\n') - fn.write('\n') - fn.write('%install\n') - fn.write('%meson_install\n') - if to_delete: - fn.write('rm -vf %s\n' % ' '.join(to_delete)) - fn.write('\n') - fn.write('%check\n') - fn.write('%meson_test\n') - fn.write('\n') - fn.write('%files\n') - for f in files: - fn.write('%s\n' % f) - fn.write('\n') - if devel_subpkg: - fn.write('%files devel\n') - for f in files_devel: - fn.write('%s\n' % f) - fn.write('\n') - if so_installed: - fn.write('%post -p /sbin/ldconfig\n') - fn.write('%postun -p /sbin/ldconfig\n') - fn.write('\n') - fn.write('%changelog\n') - fn.write('* %s meson <meson@example.com> - \n' % - datetime.date.today().strftime('%a %b %d %Y')) - fn.write('- \n') - fn.write('\n') - mlog.log('RPM spec template written to %s.spec.\n' % proj) - - def __get_required_compilers(self, state): - required_compilers = set() - for compiler in state.environment.coredata.compilers.host.values(): - # Elbrus has one 'lcc' package for every compiler - if isinstance(compiler, compilers.GnuCCompiler): - required_compilers.add('gcc') - elif isinstance(compiler, compilers.GnuCPPCompiler): - required_compilers.add('gcc-c++') - elif isinstance(compiler, compilers.ElbrusCCompiler): - required_compilers.add('lcc') - elif isinstance(compiler, compilers.ElbrusCPPCompiler): - required_compilers.add('lcc') - elif isinstance(compiler, compilers.ElbrusFortranCompiler): - required_compilers.add('lcc') - elif isinstance(compiler, compilers.ValaCompiler): - required_compilers.add('vala') - elif isinstance(compiler, compilers.GnuFortranCompiler): - required_compilers.add('gcc-gfortran') - elif isinstance(compiler, compilers.GnuObjCCompiler): - required_compilers.add('gcc-objc') - elif compiler == compilers.GnuObjCPPCompiler: - required_compilers.add('gcc-objc++') - else: - mlog.log('RPM spec file not created, generation not allowed for:', - mlog.bold(compiler.get_id())) - return required_compilers - - -def initialize(*args, **kwargs): - return RPMModule(*args, **kwargs) diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py index ba8b300..515e670 100644 --- a/mesonbuild/modules/sourceset.py +++ b/mesonbuild/modules/sourceset.py @@ -154,8 +154,12 @@ class SourceSet(MutableModuleObject): def _get_from_config_data(key): nonlocal config_cache if key not in config_cache: - args = [key] if strict else [key, False] - config_cache[key] = config_data.get_method(args, {}) + if key in config_data: + config_cache[key] = config_data.get(key)[0] + elif strict: + raise InvalidArguments(f'sourceset.apply: key "{key}" not in passed configuration, and strict set.') + else: + config_cache[key] = False return config_cache[key] files = self.collect(_get_from_config_data, False) diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py index 3339cea..8715a14 100644 --- a/mesonbuild/modules/unstable_simd.py +++ b/mesonbuild/modules/unstable_simd.py @@ -13,6 +13,7 @@ # limitations under the License. from .. import mesonlib, compilers, mlog +from .. import build from . import ExtensionModule @@ -57,8 +58,7 @@ class SimdModule(ExtensionModule): compiler = kwargs['compiler'] if not isinstance(compiler, compilers.compilers.Compiler): raise mesonlib.MesonException('Compiler argument must be a compiler object.') - cdata = self.interpreter.func_configuration_data(None, [], {}) - conf = cdata.conf_data + conf = build.ConfigurationData() for iset in self.isets: if iset not in kwargs: continue @@ -82,7 +82,7 @@ class SimdModule(ExtensionModule): all_lang_args = old_lang_args + args lib_kwargs[langarg_key] = all_lang_args result.append(self.interpreter.func_static_lib(None, [libname], lib_kwargs)) - return [result, cdata] + return [result, conf] def initialize(*args, **kwargs): return SimdModule(*args, **kwargs) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index c470e29..735766e 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from dataclasses import dataclass import re import codecs import textwrap @@ -87,15 +88,15 @@ class BlockParseException(MesonException): TV_TokenTypes = T.TypeVar('TV_TokenTypes', int, str, bool) +@dataclass(eq=False) class Token(T.Generic[TV_TokenTypes]): - def __init__(self, tid: str, filename: str, line_start: int, lineno: int, colno: int, bytespan: T.Tuple[int, int], value: TV_TokenTypes): - self.tid = tid # type: str - self.filename = filename # type: str - self.line_start = line_start # type: int - self.lineno = lineno # type: int - self.colno = colno # type: int - self.bytespan = bytespan # type: T.Tuple[int, int] - self.value = value # type: TV_TokenTypes + tid: str + filename: str + line_start: int + lineno: int + colno: int + bytespan: T.Tuple[int, int] + value: TV_TokenTypes def __eq__(self, other: object) -> bool: if isinstance(other, str): @@ -237,13 +238,19 @@ class Lexer: if not matched: raise ParseException('lexer', self.getline(line_start), lineno, col) +@dataclass(eq=False) class BaseNode: - def __init__(self, lineno: int, colno: int, filename: str, end_lineno: T.Optional[int] = None, end_colno: T.Optional[int] = None): - self.lineno = lineno # type: int - self.colno = colno # type: int - self.filename = filename # type: str - self.end_lineno = end_lineno if end_lineno is not None else self.lineno - self.end_colno = end_colno if end_colno is not None else self.colno + lineno: int + colno: int + filename: str + end_lineno: T.Optional[int] = None + end_colno: T.Optional[int] = None + + def __post_init__(self) -> None: + if self.end_lineno is None: + self.end_lineno = self.lineno + if self.end_colno is None: + self.end_colno = self.colno # Attributes for the visitors self.level = 0 # type: int diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index 67bfca8..4eb3632 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass, InitVar import os, subprocess import argparse import asyncio @@ -94,18 +95,22 @@ class Logger: self.print_progress() +@dataclass(eq=False) class Runner: - def __init__(self, logger: Logger, r: Resolver, wrap: PackageDefinition, repo_dir: str, options: 'Arguments') -> None: + logger: Logger + r: InitVar[Resolver] + wrap: PackageDefinition + repo_dir: str + options: 'Arguments' + + def __post_init__(self, r: Resolver) -> None: # FIXME: Do a copy because Resolver.resolve() is stateful method that # cannot be called from multiple threads. self.wrap_resolver = copy.copy(r) - self.wrap_resolver.dirname = os.path.join(r.subdir_root, wrap.directory) - self.wrap = self.wrap_resolver.wrap = wrap - self.repo_dir = repo_dir - self.options = options - self.run_method: T.Callable[[], bool] = options.subprojects_func.__get__(self) # type: ignore + self.wrap_resolver.dirname = os.path.join(r.subdir_root, self.wrap.directory) + self.wrap_resolver.wrap = self.wrap + self.run_method: T.Callable[[], bool] = self.options.subprojects_func.__get__(self) # type: ignore self.log_queue: T.List[T.Tuple[mlog.TV_LoggableList, T.Any]] = [] - self.logger = logger def log(self, *args: mlog.TV_Loggable, **kwargs: T.Any) -> None: self.log_queue.append((list(args), kwargs)) diff --git a/mesonbuild/scripts/yelphelper.py b/mesonbuild/scripts/yelphelper.py deleted file mode 100644 index 374104b..0000000 --- a/mesonbuild/scripts/yelphelper.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright 2016 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import subprocess -import shutil -import argparse -from .. import mlog -from ..mesonlib import has_path_sep -from . import destdir_join -from .gettext import read_linguas -import typing as T - -parser = argparse.ArgumentParser() -parser.add_argument('command') -parser.add_argument('--id', dest='project_id') -parser.add_argument('--subdir', dest='subdir') -parser.add_argument('--installdir', dest='install_dir') -parser.add_argument('--sources', dest='sources') -parser.add_argument('--media', dest='media', default='') -parser.add_argument('--langs', dest='langs', default='') -parser.add_argument('--symlinks', type=bool, dest='symlinks', default=False) - -def build_pot(srcdir: str, project_id: str, sources: T.List[str]) -> None: - # Must be relative paths - sources = [os.path.join('C', source) for source in sources] - outfile = os.path.join(srcdir, project_id + '.pot') - subprocess.call(['itstool', '-o', outfile] + sources) - -def update_po(srcdir: str, project_id: str, langs: T.List[str]) -> None: - potfile = os.path.join(srcdir, project_id + '.pot') - for lang in langs: - pofile = os.path.join(srcdir, lang, lang + '.po') - subprocess.call(['msgmerge', '-q', '-o', pofile, pofile, potfile]) - -def build_translations(srcdir: str, blddir: str, langs: T.List[str]) -> None: - for lang in langs: - outdir = os.path.join(blddir, lang) - os.makedirs(outdir, exist_ok=True) - subprocess.call([ - 'msgfmt', os.path.join(srcdir, lang, lang + '.po'), - '-o', os.path.join(outdir, lang + '.gmo') - ]) - -def merge_translations(blddir: str, sources: T.List[str], langs: T.List[str]) -> None: - for lang in langs: - subprocess.call([ - 'itstool', '-m', os.path.join(blddir, lang, lang + '.gmo'), - '-o', os.path.join(blddir, lang) - ] + sources) - -def install_help(srcdir: str, blddir: str, sources: T.List[str], media: T.List[str], langs: T.List[str], install_dir: str, destdir: str, project_id: str, symlinks: bool) -> None: - c_install_dir = os.path.join(install_dir, 'C', project_id) - for lang in langs + ['C']: - indir = destdir_join(destdir, os.path.join(install_dir, lang, project_id)) - os.makedirs(indir, exist_ok=True) - for source in sources: - infile = os.path.join(srcdir if lang == 'C' else blddir, lang, source) - outfile = os.path.join(indir, source) - mlog.log(f'Installing {infile} to {outfile}') - shutil.copy2(infile, outfile) - for m in media: - infile = os.path.join(srcdir, lang, m) - outfile = os.path.join(indir, m) - c_infile = os.path.join(srcdir, 'C', m) - if not os.path.exists(infile): - if not os.path.exists(c_infile): - mlog.warning('Media file "%s" did not exist in C directory' % m) - continue - elif symlinks: - srcfile = os.path.join(c_install_dir, m) - mlog.log(f'Symlinking {outfile} to {srcfile}.') - if has_path_sep(m): - os.makedirs(os.path.dirname(outfile), exist_ok=True) - try: - try: - os.symlink(srcfile, outfile) - except FileExistsError: - os.remove(outfile) - os.symlink(srcfile, outfile) - continue - except (NotImplementedError, OSError): - mlog.warning('Symlinking not supported, falling back to copying') - infile = c_infile - else: - # Lang doesn't have media file so copy it over 'C' one - infile = c_infile - mlog.log(f'Installing {infile} to {outfile}') - if has_path_sep(m): - os.makedirs(os.path.dirname(outfile), exist_ok=True) - shutil.copyfile(infile, outfile) - shutil.copystat(infile, outfile) - -def run(args: T.List[str]) -> int: - options = parser.parse_args(args) - langs = options.langs.split('@@') if options.langs else [] - media = options.media.split('@@') if options.media else [] - sources = options.sources.split('@@') - destdir = os.environ.get('DESTDIR', '') - src_subdir = os.path.join(os.environ['MESON_SOURCE_ROOT'], options.subdir) - build_subdir = os.path.join(os.environ['MESON_BUILD_ROOT'], options.subdir) - abs_sources = [os.path.join(src_subdir, 'C', source) for source in sources] - - if not langs: - langs = read_linguas(src_subdir) - - if options.command == 'pot': - build_pot(src_subdir, options.project_id, sources) - elif options.command == 'update-po': - build_pot(src_subdir, options.project_id, sources) - update_po(src_subdir, options.project_id, langs) - elif options.command == 'build': - if langs: - build_translations(src_subdir, build_subdir, langs) - elif options.command == 'install': - install_dir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], options.install_dir) - if langs: - build_translations(src_subdir, build_subdir, langs) - merge_translations(build_subdir, abs_sources, langs) - install_help(src_subdir, build_subdir, sources, media, langs, install_dir, - destdir, options.project_id, options.symlinks) - return 0 diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index f2b59bd..c99e33c 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -14,6 +14,7 @@ from .. import mlog import contextlib +from dataclasses import dataclass import urllib.request import urllib.error import urllib.parse @@ -25,13 +26,14 @@ import stat import subprocess import sys import configparser +import time import typing as T import textwrap from pathlib import Path from . import WrapMode from .. import coredata -from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException +from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree from ..interpreterbase import FeatureNew from .. import mesonlib @@ -203,13 +205,15 @@ def verbose_git(cmd: T.List[str], workingdir: str, check: bool = False) -> bool: except mesonlib.GitException as e: raise WrapException(str(e)) +@dataclass(eq=False) class Resolver: - def __init__(self, source_dir: str, subdir: str, subproject: str = '', wrap_mode: WrapMode = WrapMode.default) -> None: - self.source_dir = source_dir - self.subdir = subdir - self.subproject = subproject - self.wrap_mode = wrap_mode - self.subdir_root = os.path.join(source_dir, subdir) + source_dir: str + subdir: str + subproject: str = '' + wrap_mode: WrapMode = WrapMode.default + + def __post_init__(self) -> None: + self.subdir_root = os.path.join(self.source_dir, self.subdir) self.cachedir = os.path.join(self.subdir_root, 'packagecache') self.wraps = {} # type: T.Dict[str, PackageDefinition] self.provided_deps = {} # type: T.Dict[str, PackageDefinition] @@ -342,7 +346,11 @@ class Resolver: self.get_svn() else: raise WrapException(f'Unknown wrap type {self.wrap.type!r}') - self.apply_patch() + try: + self.apply_patch() + except: + windows_proof_rmtree(self.dirname) + raise # A meson.build or CMakeLists.txt file is required in the directory if method == 'meson' and not os.path.exists(meson_file): @@ -527,12 +535,22 @@ class Resolver: if dhash != expected: raise WrapException(f'Incorrect hash for {what}:\n {expected} expected\n {dhash} actual.') + def get_data_with_backoff(self, urlstring: str) -> T.Tuple[str, str]: + delays = [1, 2, 4, 8, 16] + for d in delays: + try: + return self.get_data(urlstring) + except Exception as e: + mlog.warning(f'failed to download with error: {e}. Trying after a delay...', fatal=False) + time.sleep(d) + return self.get_data(urlstring) + def download(self, what: str, ofname: str, fallback: bool = False) -> None: self.check_can_download() srcurl = self.wrap.get(what + ('_fallback_url' if fallback else '_url')) mlog.log('Downloading', mlog.bold(self.packagename), what, 'from', mlog.bold(srcurl)) try: - dhash, tmpfile = self.get_data(srcurl) + dhash, tmpfile = self.get_data_with_backoff(srcurl) expected = self.wrap.get(what + '_hash').lower() if dhash != expected: os.remove(tmpfile) diff --git a/packaging/createmsi.py b/packaging/createmsi.py index 0139877..d09fa78 100755 --- a/packaging/createmsi.py +++ b/packaging/createmsi.py @@ -125,7 +125,7 @@ class PackageGenerator: self.staging_dirs = ['dist', 'dist2'] self.progfile_dir = 'ProgramFiles64Folder' redist_globs = ['C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Redist\\MSVC\\v*\\MergeModules\\Microsoft_VC142_CRT_x64.msm', - 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\VC\\Redist\\MSVC\\v*\\MergeModules\\Microsoft_VC143_CRT_x64.msm'] + 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Redist\\MSVC\\v*\\MergeModules\\Microsoft_VC143_CRT_x64.msm'] redist_path = None for g in redist_globs: trials = glob(g) diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py index 45096c6..36cb02e 100755 --- a/run_meson_command_tests.py +++ b/run_meson_command_tests.py @@ -70,7 +70,7 @@ class CommandTests(unittest.TestCase): # between CI issue and test bug in that case. Set timeout and fail loud # instead. p = subprocess.run(command, stdout=subprocess.PIPE, - env=os.environ.copy(), universal_newlines=True, + env=os.environ.copy(), text=True, cwd=workdir, timeout=60 * 5) print(p.stdout) if p.returncode != 0: diff --git a/run_mypy.py b/run_mypy.py index 1c2d644..00d490b 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -25,6 +25,7 @@ modules = [ 'mesonbuild/arglist.py', 'mesonbuild/backend/backends.py', # 'mesonbuild/coredata.py', + 'mesonbuild/depfile.py', 'mesonbuild/envconfig.py', 'mesonbuild/interpreter/compiler.py', 'mesonbuild/interpreter/mesonmain.py', @@ -40,6 +41,7 @@ modules = [ 'mesonbuild/mlog.py', 'mesonbuild/msubprojects.py', 'mesonbuild/modules/fs.py', + 'mesonbuild/modules/gnome.py', 'mesonbuild/modules/i18n.py', 'mesonbuild/modules/java.py', 'mesonbuild/modules/keyval.py', diff --git a/run_project_tests.py b/run_project_tests.py index 822cf0b..5fea660 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -926,8 +926,7 @@ def have_d_compiler() -> bool: # that exists but segfaults every time the compiler is run. # Don't know why. Don't know how to fix. Skip in this case. cp = subprocess.run(['dmd', '--version'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + capture_output=True) if cp.stdout == b'': return False return True diff --git a/run_tests.py b/run_tests.py index b6f8884..20edf93 100755 --- a/run_tests.py +++ b/run_tests.py @@ -261,11 +261,10 @@ def ensure_backend_detects_changes(backend: Backend) -> None: time.sleep(1) def run_mtest_inprocess(commandlist: T.List[str]) -> T.Tuple[int, str, str]: - stderr = StringIO() - stdout = StringIO() - with mock.patch.object(sys, 'stdout', stdout), mock.patch.object(sys, 'stderr', stderr): + out = StringIO() + with mock.patch.object(sys, 'stdout', out), mock.patch.object(sys, 'stderr', out): returncode = mtest.run_with_args(commandlist) - return returncode, stdout.getvalue(), stderr.getvalue() + return returncode, stdout.getvalue() def clear_meson_configure_class_caches() -> None: compilers.CCompiler.find_library_cache = {} @@ -23,7 +23,6 @@ classifiers = Operating System :: POSIX :: BSD Operating System :: POSIX :: Linux Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -32,7 +31,7 @@ long_description = Meson is a cross-platform build system designed to be both as [options] packages = find: -python_requires = >= 3.6 +python_requires = >= 3.7 setup_requires = setuptools @@ -49,10 +48,11 @@ typing = [options.package_data] mesonbuild.scripts = cmd_or_ps.ps1 +mesonbuild.cmake.data = * +mesonbuild.dependencies.data = * [options.packages.find] include = mesonbuild, mesonbuild.* -exclude = *.data [tool:pytest] python_classes = @@ -16,9 +16,9 @@ import sys -if sys.version_info < (3, 6): +if sys.version_info < (3, 7): raise SystemExit('ERROR: Tried to install Meson with an unsupported Python version: \n{}' - '\nMeson requires Python 3.6.0 or greater'.format(sys.version)) + '\nMeson requires Python 3.7.0 or greater'.format(sys.version)) from setuptools import setup diff --git a/test cases/common/126 generated llvm ir/meson.build b/test cases/common/126 generated llvm ir/meson.build index f10754a..fddee2b 100644 --- a/test cases/common/126 generated llvm ir/meson.build +++ b/test cases/common/126 generated llvm ir/meson.build @@ -5,7 +5,7 @@ if meson.get_compiler('c').get_id() != 'clang' endif if meson.backend() == 'xcode' - error('MESON_SKIP_TEST: LLMV ir not supported with the Xcode backend. Patches welcome.') + error('MESON_SKIP_TEST: LLVM ir not supported with the Xcode backend. Patches welcome.') endif copy = find_program('copyfile.py') diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build index 416dad7..91a56ff 100644 --- a/test cases/common/14 configure file/meson.build +++ b/test cases/common/14 configure file/meson.build @@ -275,9 +275,6 @@ configure_file( test('configure-file', test_file) -cdata = configuration_data() -cdata.set('invalid_value', ['array']) - # Dictionaries cdata = configuration_data({ diff --git a/test cases/common/187 args flattening/meson.build b/test cases/common/187 args flattening/meson.build index 1dac2f9..61d77e4 100644 --- a/test cases/common/187 args flattening/meson.build +++ b/test cases/common/187 args flattening/meson.build @@ -6,13 +6,6 @@ assert(arr == ['bar', 'baz'], 'get_variable with array fallback is broken') set_variable('arr', ['bar', 'baz']) assert(arr == ['bar', 'baz'], 'set_variable(array) is broken') -conf = configuration_data() -conf.set('foo', ['bar', 'baz']) -assert(conf.get('foo') == ['bar', 'baz'], 'configuration_data.set(array) is broken') - -arr = conf.get('does-not-exist', ['bar', 'baz']) -assert(arr == ['bar', 'baz'], 'configuration_data.get with array fallback is broken') - arr = meson.get_cross_property('does-not-exist', ['bar', 'baz']) assert(arr == ['bar', 'baz'], 'meson.get_cross_property with array fallback is broken') @@ -27,5 +20,6 @@ assert(arr == ['bar', 'baz'], 'meson.get_external_property native:false with arr # Test deprecated behaviour +conf = configuration_data() conf.set(['foo', 'bar']) message(conf.get('foo')) diff --git a/test cases/common/245 dependency fallbacks/meson.build b/test cases/common/246 dependency fallbacks/meson.build index aaabaaa..aaabaaa 100644 --- a/test cases/common/245 dependency fallbacks/meson.build +++ b/test cases/common/246 dependency fallbacks/meson.build diff --git a/test cases/common/245 dependency fallbacks/subprojects/png/meson.build b/test cases/common/246 dependency fallbacks/subprojects/png/meson.build index 5efc60a..5efc60a 100644 --- a/test cases/common/245 dependency fallbacks/subprojects/png/meson.build +++ b/test cases/common/246 dependency fallbacks/subprojects/png/meson.build diff --git a/test cases/common/245 deprecated option/meson.build b/test cases/common/247 deprecated option/meson.build index 5102fd0..5102fd0 100644 --- a/test cases/common/245 deprecated option/meson.build +++ b/test cases/common/247 deprecated option/meson.build diff --git a/test cases/common/245 deprecated option/meson_options.txt b/test cases/common/247 deprecated option/meson_options.txt index 5814531..5814531 100644 --- a/test cases/common/245 deprecated option/meson_options.txt +++ b/test cases/common/247 deprecated option/meson_options.txt diff --git a/test cases/common/245 deprecated option/test.json b/test cases/common/247 deprecated option/test.json index c2f2ca3..c2f2ca3 100644 --- a/test cases/common/245 deprecated option/test.json +++ b/test cases/common/247 deprecated option/test.json diff --git a/test cases/common/246 install_emptydir/meson.build b/test cases/common/248 install_emptydir/meson.build index a5eb046..a5eb046 100644 --- a/test cases/common/246 install_emptydir/meson.build +++ b/test cases/common/248 install_emptydir/meson.build diff --git a/test cases/common/246 install_emptydir/test.json b/test cases/common/248 install_emptydir/test.json index 17abe74..17abe74 100644 --- a/test cases/common/246 install_emptydir/test.json +++ b/test cases/common/248 install_emptydir/test.json diff --git a/test cases/common/247 install_symlink/datafile.dat b/test cases/common/249 install_symlink/datafile.dat index ff3104b..ff3104b 100644 --- a/test cases/common/247 install_symlink/datafile.dat +++ b/test cases/common/249 install_symlink/datafile.dat diff --git a/test cases/common/247 install_symlink/meson.build b/test cases/common/249 install_symlink/meson.build index ae30382..ae30382 100644 --- a/test cases/common/247 install_symlink/meson.build +++ b/test cases/common/249 install_symlink/meson.build diff --git a/test cases/common/247 install_symlink/test.json b/test cases/common/249 install_symlink/test.json index 33aa76e..33aa76e 100644 --- a/test cases/common/247 install_symlink/test.json +++ b/test cases/common/249 install_symlink/test.json diff --git a/test cases/common/250 system include dir/lib/lib.hpp b/test cases/common/250 system include dir/lib/lib.hpp new file mode 100644 index 0000000..a1fbf85 --- /dev/null +++ b/test cases/common/250 system include dir/lib/lib.hpp @@ -0,0 +1,4 @@ +#pragma once + +// This will trigger -Wsign-conversion +inline unsigned convert_to_unsigned(int i) { return i; } diff --git a/test cases/common/250 system include dir/main.cpp b/test cases/common/250 system include dir/main.cpp new file mode 100644 index 0000000..9f83297 --- /dev/null +++ b/test cases/common/250 system include dir/main.cpp @@ -0,0 +1,3 @@ +#include <lib.hpp> + +int main() { return 0; } diff --git a/test cases/common/250 system include dir/meson.build b/test cases/common/250 system include dir/meson.build new file mode 100644 index 0000000..724a8e4 --- /dev/null +++ b/test cases/common/250 system include dir/meson.build @@ -0,0 +1,13 @@ +project('system_include_dir', 'cpp', + version : '0.1', + default_options : 'werror=true', +) + +compiler_id = meson.get_compiler('cpp').get_id() +if not ['gcc', 'clang', 'clang-cl'].contains(compiler_id) + error('MESON_SKIP_TEST: compiler @0@ either doesn\'t support is_system includes or needs to have support for this test added'.format(compiler_id)) +endif + +lib_include_directories = include_directories('lib', is_system: true) +add_project_arguments('-Wsign-conversion', language: 'cpp') +executable('system_include_dir_test', sources: 'main.cpp', include_directories: lib_include_directories) diff --git a/test cases/common/44 pkgconfig-gen/answer.c b/test cases/common/44 pkgconfig-gen/answer.c new file mode 100644 index 0000000..df5f54f --- /dev/null +++ b/test cases/common/44 pkgconfig-gen/answer.c @@ -0,0 +1,3 @@ +int answer_to_life_the_universe_and_everything(void) { + return 42; +} diff --git a/test cases/common/44 pkgconfig-gen/foo.c b/test cases/common/44 pkgconfig-gen/foo.c new file mode 100644 index 0000000..83bb06a --- /dev/null +++ b/test cases/common/44 pkgconfig-gen/foo.c @@ -0,0 +1,7 @@ +#include"simple.h" + +int answer_to_life_the_universe_and_everything (void); + +int simple_function(void) { + return answer_to_life_the_universe_and_everything(); +} diff --git a/test cases/common/44 pkgconfig-gen/meson.build b/test cases/common/44 pkgconfig-gen/meson.build index 87c0767..128205a 100644 --- a/test cases/common/44 pkgconfig-gen/meson.build +++ b/test cases/common/44 pkgconfig-gen/meson.build @@ -18,6 +18,8 @@ if v.version_compare('<0.29') error('MESON_SKIP_TEST: pkg-config version \'' + v + '\' too old') endif +python = import('python').find_installation() +fs = import('fs') pkgg = import('pkgconfig') lib = shared_library('simple', 'simple.c') @@ -40,13 +42,22 @@ test('pkgconfig-validation', pkgconfig, args: ['--validate', 'simple'], env: [ 'PKG_CONFIG_PATH=' + meson.current_build_dir() + '/meson-private' ]) +answerlib = shared_library('answer', 'answer.c') + +pkgg.generate(answerlib, + name : 'libanswer', + description : 'An answer library.', + extra_cflags : ['-DLIBFOO'], +) + # Test that name_prefix='' and name='libfoo' results in '-lfoo' -lib2 = shared_library('libfoo', 'simple.c', +lib2 = shared_library('libfoo', 'foo.c', + link_with: answerlib, name_prefix : '', version : libver) -pkgg.generate( - libraries : lib2, +pkgg.generate(lib2, + libraries : [lib2, answerlib], name : 'libfoo', version : libver, description : 'A foo library.', @@ -124,3 +135,17 @@ if cc.get_id() in ['gcc', 'clang'] description: 'custom target index' ) endif + +# Regression test: A library linking to an uninstalled custom_target static +# library used to crash when generating its pkgconfig file. +# Copy libstat2.a to libstat3.a to have a static library as custom target. +infile = stat2.full_path() +outfile = meson.current_build_dir() / 'libstat3.a' +script = 'import shutil ; shutil.copyfile("@0@", "@1@")'.format(infile, outfile) +ct = custom_target('stat3', + input: stat2, + output: fs.name(outfile), + command: [python, '-c', script], +) +simple6 = library('simple6', link_with: ct) +pkgg.generate(simple6) diff --git a/test cases/common/44 pkgconfig-gen/test.json b/test cases/common/44 pkgconfig-gen/test.json index e741a62..c7bdd43 100644 --- a/test cases/common/44 pkgconfig-gen/test.json +++ b/test cases/common/44 pkgconfig-gen/test.json @@ -3,12 +3,14 @@ {"type": "file", "file": "usr/include/simple.h"}, {"type": "file", "file": "usr/lib/libstat2.a"}, {"type": "file", "file": "usr/lib/pkgconfig/simple.pc"}, + {"type": "file", "file": "usr/lib/pkgconfig/libanswer.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libfoo.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libhello.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libhello_nolib.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/simple2.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/simple3.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/simple5.pc"}, + {"type": "file", "file": "usr/lib/pkgconfig/simple6.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/ct.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/ct0.pc"} ] diff --git a/test cases/failing/101 no glib-compile-resources/test.json b/test cases/failing/101 no glib-compile-resources/test.json index d81b0c0..7f0c02c 100644 --- a/test cases/failing/101 no glib-compile-resources/test.json +++ b/test cases/failing/101 no glib-compile-resources/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/101 no glib-compile-resources/meson.build:8:0: ERROR: Program 'glib-compile-resources' not found" + "line": "test cases/failing/101 no glib-compile-resources/meson.build:8:0: ERROR: Program 'glib-compile-resources' not found or not executable" } ] } diff --git a/test cases/failing/106 feature require.bis/meson.build b/test cases/failing/107 feature require.bis/meson.build index 08c099c..08c099c 100644 --- a/test cases/failing/106 feature require.bis/meson.build +++ b/test cases/failing/107 feature require.bis/meson.build diff --git a/test cases/failing/106 feature require.bis/meson_options.txt b/test cases/failing/107 feature require.bis/meson_options.txt index 5910a87..5910a87 100644 --- a/test cases/failing/106 feature require.bis/meson_options.txt +++ b/test cases/failing/107 feature require.bis/meson_options.txt diff --git a/test cases/failing/106 feature require.bis/test.json b/test cases/failing/107 feature require.bis/test.json index 2583990..2583990 100644 --- a/test cases/failing/106 feature require.bis/test.json +++ b/test cases/failing/107 feature require.bis/test.json diff --git a/test cases/failing/107 no build get_external_property/meson.build b/test cases/failing/108 no build get_external_property/meson.build index 8a4215c..8a4215c 100644 --- a/test cases/failing/107 no build get_external_property/meson.build +++ b/test cases/failing/108 no build get_external_property/meson.build diff --git a/test cases/failing/107 no build get_external_property/test.json b/test cases/failing/108 no build get_external_property/test.json index b95427e..d2827f4 100644 --- a/test cases/failing/107 no build get_external_property/test.json +++ b/test cases/failing/108 no build get_external_property/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/107 no build get_external_property/meson.build:3:0: ERROR: Unknown property for build machine: nonexisting" + "line": "test cases/failing/108 no build get_external_property/meson.build:3:0: ERROR: Unknown property for build machine: nonexisting" } ] } diff --git a/test cases/failing/108 enter subdir twice/meson.build b/test cases/failing/109 enter subdir twice/meson.build index 9343233..9343233 100644 --- a/test cases/failing/108 enter subdir twice/meson.build +++ b/test cases/failing/109 enter subdir twice/meson.build diff --git a/test cases/failing/108 enter subdir twice/sub/meson.build b/test cases/failing/109 enter subdir twice/sub/meson.build index d036a3f..d036a3f 100644 --- a/test cases/failing/108 enter subdir twice/sub/meson.build +++ b/test cases/failing/109 enter subdir twice/sub/meson.build diff --git a/test cases/failing/108 enter subdir twice/test.json b/test cases/failing/109 enter subdir twice/test.json index 0a8e127..f7a526c 100644 --- a/test cases/failing/108 enter subdir twice/test.json +++ b/test cases/failing/109 enter subdir twice/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/108 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited." + "line": "test cases/failing/109 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited." } ] } diff --git a/test cases/failing/109 invalid fstring/meson.build b/test cases/failing/111 invalid fstring/109 invalid fstring/meson.build index dd22f56..dd22f56 100644 --- a/test cases/failing/109 invalid fstring/meson.build +++ b/test cases/failing/111 invalid fstring/109 invalid fstring/meson.build diff --git a/test cases/failing/109 invalid fstring/test.json b/test cases/failing/111 invalid fstring/109 invalid fstring/test.json index 71d8f59..71d8f59 100644 --- a/test cases/failing/109 invalid fstring/test.json +++ b/test cases/failing/111 invalid fstring/109 invalid fstring/test.json diff --git a/test cases/failing/110 invalid fstring/meson.build b/test cases/failing/111 invalid fstring/meson.build index 973df30..973df30 100644 --- a/test cases/failing/110 invalid fstring/meson.build +++ b/test cases/failing/111 invalid fstring/meson.build diff --git a/test cases/failing/110 invalid fstring/test.json b/test cases/failing/111 invalid fstring/test.json index bfd0e2d..89ec40c 100644 --- a/test cases/failing/110 invalid fstring/test.json +++ b/test cases/failing/111 invalid fstring/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/110 invalid fstring/meson.build:3:0: ERROR: Identifier \"foo\" does not name a variable." + "line": "test cases/failing/111 invalid fstring/meson.build:3:0: ERROR: Identifier \"foo\" does not name a variable." } ] } diff --git a/test cases/failing/111 compiler argument checking/meson.build b/test cases/failing/112 compiler argument checking/meson.build index bb1f447..bb1f447 100644 --- a/test cases/failing/111 compiler argument checking/meson.build +++ b/test cases/failing/112 compiler argument checking/meson.build diff --git a/test cases/failing/111 compiler argument checking/test.json b/test cases/failing/112 compiler argument checking/test.json index f41f2d2..f4728db 100644 --- a/test cases/failing/111 compiler argument checking/test.json +++ b/test cases/failing/112 compiler argument checking/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/111 compiler argument checking/meson.build:4:0: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\"" + "line": "test cases/failing/112 compiler argument checking/meson.build:4:0: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\"" } ] } diff --git a/test cases/failing/112 empty fallback/meson.build b/test cases/failing/113 empty fallback/meson.build index f4eb5fe..f4eb5fe 100644 --- a/test cases/failing/112 empty fallback/meson.build +++ b/test cases/failing/113 empty fallback/meson.build diff --git a/test cases/failing/112 empty fallback/subprojects/foo/meson.build b/test cases/failing/113 empty fallback/subprojects/foo/meson.build index c9e134b..c9e134b 100644 --- a/test cases/failing/112 empty fallback/subprojects/foo/meson.build +++ b/test cases/failing/113 empty fallback/subprojects/foo/meson.build diff --git a/test cases/failing/112 empty fallback/test.json b/test cases/failing/113 empty fallback/test.json index 89520ef..c8ed212 100644 --- a/test cases/failing/112 empty fallback/test.json +++ b/test cases/failing/113 empty fallback/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/112 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found, tried pkgconfig and cmake" + "line": "test cases/failing/113 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found, tried pkgconfig and cmake" } ] } diff --git a/test cases/failing/113 cmake executable dependency/meson.build b/test cases/failing/114 cmake executable dependency/meson.build index bfb03ef..bfb03ef 100644 --- a/test cases/failing/113 cmake executable dependency/meson.build +++ b/test cases/failing/114 cmake executable dependency/meson.build diff --git a/test cases/failing/113 cmake executable dependency/subprojects/cmlib/CMakeLists.txt b/test cases/failing/114 cmake executable dependency/subprojects/cmlib/CMakeLists.txt index 0067879..0067879 100644 --- a/test cases/failing/113 cmake executable dependency/subprojects/cmlib/CMakeLists.txt +++ b/test cases/failing/114 cmake executable dependency/subprojects/cmlib/CMakeLists.txt diff --git a/test cases/failing/113 cmake executable dependency/subprojects/cmlib/main.c b/test cases/failing/114 cmake executable dependency/subprojects/cmlib/main.c index 9b6bdc2..9b6bdc2 100644 --- a/test cases/failing/113 cmake executable dependency/subprojects/cmlib/main.c +++ b/test cases/failing/114 cmake executable dependency/subprojects/cmlib/main.c diff --git a/test cases/failing/113 cmake executable dependency/test.json b/test cases/failing/114 cmake executable dependency/test.json index 1cb4a0f..82306dd 100644 --- a/test cases/failing/113 cmake executable dependency/test.json +++ b/test cases/failing/114 cmake executable dependency/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/113 cmake executable dependency/meson.build:9:0: ERROR: main is an executable and does not support the dependency() method. Use target() instead." + "line": "test cases/failing/114 cmake executable dependency/meson.build:9:0: ERROR: main is an executable and does not support the dependency() method. Use target() instead." } ] } diff --git a/test cases/failing/114 allow_fallback with fallback/meson.build b/test cases/failing/115 allow_fallback with fallback/meson.build index 2874e42..2874e42 100644 --- a/test cases/failing/114 allow_fallback with fallback/meson.build +++ b/test cases/failing/115 allow_fallback with fallback/meson.build diff --git a/test cases/failing/114 allow_fallback with fallback/test.json b/test cases/failing/115 allow_fallback with fallback/test.json index 1e5712e..6bdcc51 100644 --- a/test cases/failing/114 allow_fallback with fallback/test.json +++ b/test cases/failing/115 allow_fallback with fallback/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/114 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive" + "line": "test cases/failing/115 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive" } ] } diff --git a/test cases/failing/115 nonsensical bindgen/meson.build b/test cases/failing/116 nonsensical bindgen/meson.build index 6995f67..6995f67 100644 --- a/test cases/failing/115 nonsensical bindgen/meson.build +++ b/test cases/failing/116 nonsensical bindgen/meson.build diff --git a/test cases/failing/115 nonsensical bindgen/src/header.h b/test cases/failing/116 nonsensical bindgen/src/header.h index 750621f..750621f 100644 --- a/test cases/failing/115 nonsensical bindgen/src/header.h +++ b/test cases/failing/116 nonsensical bindgen/src/header.h diff --git a/test cases/failing/115 nonsensical bindgen/src/source.c b/test cases/failing/116 nonsensical bindgen/src/source.c index d652d28..d652d28 100644 --- a/test cases/failing/115 nonsensical bindgen/src/source.c +++ b/test cases/failing/116 nonsensical bindgen/src/source.c diff --git a/test cases/failing/115 nonsensical bindgen/test.json b/test cases/failing/116 nonsensical bindgen/test.json index d9249b2..b0a38e8 100644 --- a/test cases/failing/115 nonsensical bindgen/test.json +++ b/test cases/failing/116 nonsensical bindgen/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/115 nonsensical bindgen/meson.build:17:24: ERROR: bindgen source file must be a C header, not an object or build target" + "line": "test cases/failing/116 nonsensical bindgen/meson.build:17:24: ERROR: bindgen source file must be a C header, not an object or build target" } ] } diff --git a/test cases/failing/116 run_target in test/meson.build b/test cases/failing/117 run_target in test/meson.build index db7cb30..db7cb30 100644 --- a/test cases/failing/116 run_target in test/meson.build +++ b/test cases/failing/117 run_target in test/meson.build diff --git a/test cases/failing/116 run_target in test/test.json b/test cases/failing/117 run_target in test/test.json index 961c1e4..ad24367 100644 --- a/test cases/failing/116 run_target in test/test.json +++ b/test cases/failing/117 run_target in test/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/116 run_target in test/meson.build:4:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex]" + "line": "test cases/failing/117 run_target in test/meson.build:4:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex]" } ] } diff --git a/test cases/failing/116 run_target in test/trivial.c b/test cases/failing/117 run_target in test/trivial.c index 96612d4..96612d4 100644 --- a/test cases/failing/116 run_target in test/trivial.c +++ b/test cases/failing/117 run_target in test/trivial.c diff --git a/test cases/failing/117 run_target in add_install_script/meson.build b/test cases/failing/118 run_target in add_install_script/meson.build index 9d37a39..9d37a39 100644 --- a/test cases/failing/117 run_target in add_install_script/meson.build +++ b/test cases/failing/118 run_target in add_install_script/meson.build diff --git a/test cases/failing/117 run_target in add_install_script/test.json b/test cases/failing/118 run_target in add_install_script/test.json index fec1941..a09b1a3 100644 --- a/test cases/failing/117 run_target in add_install_script/test.json +++ b/test cases/failing/118 run_target in add_install_script/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/117 run_target in add_install_script/meson.build:4:6: ERROR: meson.add_install_script argument 2 was of type \"RunTarget\" but should have been one of: \"str\", \"File\", \"BuildTarget\", \"CustomTarget\", \"CustomTargetIndex\", \"ExternalProgram\"" + "line": "test cases/failing/118 run_target in add_install_script/meson.build:4:6: ERROR: meson.add_install_script argument 2 was of type \"RunTarget\" but should have been one of: \"str\", \"File\", \"BuildTarget\", \"CustomTarget\", \"CustomTargetIndex\", \"ExternalProgram\"" } ] } diff --git a/test cases/failing/117 run_target in add_install_script/trivial.c b/test cases/failing/118 run_target in add_install_script/trivial.c index 1b14571..1b14571 100644 --- a/test cases/failing/117 run_target in add_install_script/trivial.c +++ b/test cases/failing/118 run_target in add_install_script/trivial.c diff --git a/test cases/failing/118 pathsep in install_symlink/meson.build b/test cases/failing/119 pathsep in install_symlink/meson.build index cce82c2..cce82c2 100644 --- a/test cases/failing/118 pathsep in install_symlink/meson.build +++ b/test cases/failing/119 pathsep in install_symlink/meson.build diff --git a/test cases/failing/118 pathsep in install_symlink/test.json b/test cases/failing/119 pathsep in install_symlink/test.json index e3f3a4a..9df546a 100644 --- a/test cases/failing/118 pathsep in install_symlink/test.json +++ b/test cases/failing/119 pathsep in install_symlink/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/118 pathsep in install_symlink/meson.build:3:0: ERROR: Link name is \"foo/bar\", but link names cannot contain path separators. The dir part should be in install_dir." + "line": "test cases/failing/119 pathsep in install_symlink/meson.build:3:0: ERROR: Link name is \"foo/bar\", but link names cannot contain path separators. The dir part should be in install_dir." } ] } diff --git a/test cases/failing/120 subproject version conflict/meson.build b/test cases/failing/120 subproject version conflict/meson.build new file mode 100644 index 0000000..ffbcc13 --- /dev/null +++ b/test cases/failing/120 subproject version conflict/meson.build @@ -0,0 +1,4 @@ +project('120 subproject version conflict') + +A_dep = subproject('A').get_variable('A_dep') +B_dep = subproject('B', version: '1').get_variable('B_dep') diff --git a/test cases/failing/120 subproject version conflict/subprojects/A/meson.build b/test cases/failing/120 subproject version conflict/subprojects/A/meson.build new file mode 100644 index 0000000..7da4df0 --- /dev/null +++ b/test cases/failing/120 subproject version conflict/subprojects/A/meson.build @@ -0,0 +1,4 @@ +project('A') + +B_dep = subproject('B').get_variable('B_dep') +A_dep = declare_dependency(dependencies: B_dep) diff --git a/test cases/failing/120 subproject version conflict/subprojects/B/meson.build b/test cases/failing/120 subproject version conflict/subprojects/B/meson.build new file mode 100644 index 0000000..0ead934 --- /dev/null +++ b/test cases/failing/120 subproject version conflict/subprojects/B/meson.build @@ -0,0 +1,3 @@ +project('B', version: '100') + +B_dep = declare_dependency() diff --git a/test cases/failing/120 subproject version conflict/test.json b/test cases/failing/120 subproject version conflict/test.json new file mode 100644 index 0000000..5d445e5 --- /dev/null +++ b/test cases/failing/120 subproject version conflict/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/120 subproject version conflict/meson.build:4:0: ERROR: Subproject B version is 100 but 1 required." + } + ] +} diff --git a/test cases/unit/98 install all targets/bar-notag.txt b/test cases/frameworks/10 gtk-doc/doc/foobar1/baz.jpg index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/bar-notag.txt +++ b/test cases/frameworks/10 gtk-doc/doc/foobar1/baz.jpg diff --git a/test cases/unit/98 install all targets/foo.in b/test cases/frameworks/10 gtk-doc/doc/foobar1/baz.png.in index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/foo.in +++ b/test cases/frameworks/10 gtk-doc/doc/foobar1/baz.png.in diff --git a/test cases/frameworks/10 gtk-doc/doc/foobar1/meson.build b/test cases/frameworks/10 gtk-doc/doc/foobar1/meson.build index f4b3724..2af9670 100644 --- a/test cases/frameworks/10 gtk-doc/doc/foobar1/meson.build +++ b/test cases/frameworks/10 gtk-doc/doc/foobar1/meson.build @@ -1,9 +1,15 @@ +png = configure_file(input: 'baz.png.in', + output: 'baz.png', + copy: true) + gnome.gtkdoc('foobar', src_dir : [inc, '.'], main_sgml : 'foobar-docs.sgml', content_files : [docbook, version_xml], dependencies: foo_dep, + html_assets: ['baz.jpg', png], # Manually written types file for regression test: # https://github.com/mesonbuild/meson/issues/8744 gobject_typesfile: 'foobar.types', - install : true) + install : true, + check: false) diff --git a/test cases/frameworks/10 gtk-doc/doc/foobar2/meson.build b/test cases/frameworks/10 gtk-doc/doc/foobar2/meson.build index 0b2faa0..5f860ef 100644 --- a/test cases/frameworks/10 gtk-doc/doc/foobar2/meson.build +++ b/test cases/frameworks/10 gtk-doc/doc/foobar2/meson.build @@ -1,6 +1,13 @@ +types = configure_file(input: '../foobar1/foobar.types', + output: 'foobar.types', + copy: true +) + gnome.gtkdoc('foobar2', src_dir : inc, main_sgml : 'foobar-docs.sgml', content_files : [docbook, version_xml], + gobject_typesfile: types, + dependencies: foo_dep, install : true, install_dir : 'foobar2') diff --git a/test cases/frameworks/10 gtk-doc/test.json b/test cases/frameworks/10 gtk-doc/test.json index edade39..f2805d3 100644 --- a/test cases/frameworks/10 gtk-doc/test.json +++ b/test cases/frameworks/10 gtk-doc/test.json @@ -2,6 +2,8 @@ "installed": [ {"type": "file", "file": "usr/include/foo-version.h"}, {"type": "file", "file": "usr/share/gtk-doc/html/foobar/BAR.html"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foobar/baz.jpg"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foobar/baz.png"}, {"type": "file", "file": "usr/share/gtk-doc/html/foobar/foobar.devhelp2"}, {"type": "file", "file": "usr/share/gtk-doc/html/foobar/foobar.html"}, {"type": "file", "file": "usr/share/gtk-doc/html/foobar/FooObj.html"}, diff --git a/test cases/frameworks/13 yelp/help/C/index2.page b/test cases/frameworks/13 yelp/help/C/index2.page new file mode 100644 index 0000000..14b6b51 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/C/index2.page @@ -0,0 +1,8 @@ +<page xmlns="http://projectmallard.org/1.0/" + xmlns:its="http://www.w3.org/2005/11/its" + type="guide" + id="index2"> + <title> + Hello! + </title> +</page> diff --git a/test cases/frameworks/13 yelp/help/C/index3.page b/test cases/frameworks/13 yelp/help/C/index3.page new file mode 100644 index 0000000..c0c21c1 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/C/index3.page @@ -0,0 +1,8 @@ +<page xmlns="http://projectmallard.org/1.0/" + xmlns:its="http://www.w3.org/2005/11/its" + type="guide" + id="index3"> + <title> + Hello! + </title> +</page> diff --git a/test cases/frameworks/13 yelp/help/meson.build b/test cases/frameworks/13 yelp/help/meson.build index c8edd61..d6dbe10 100644 --- a/test cases/frameworks/13 yelp/help/meson.build +++ b/test cases/frameworks/13 yelp/help/meson.build @@ -8,14 +8,14 @@ gnome.yelp('meson', ) gnome.yelp('meson-symlink', - sources: 'index.page', + sources: 'index2.page', media: 'media/test.txt', symlink_media: true, languages: ['de', 'es'], ) gnome.yelp('meson-linguas', - sources: 'index.page', + sources: 'index3.page', media: 'media/test.txt', symlink_media: false, ) diff --git a/test cases/frameworks/13 yelp/test.json b/test cases/frameworks/13 yelp/test.json index ffe75cb..22e34d2 100644 --- a/test cases/frameworks/13 yelp/test.json +++ b/test cases/frameworks/13 yelp/test.json @@ -6,17 +6,17 @@ {"type": "file", "file": "usr/share/help/es/meson/media/test.txt"}, {"type": "file", "file": "usr/share/help/de/meson/index.page"}, {"type": "file", "file": "usr/share/help/de/meson/media/test.txt"}, - {"type": "file", "file": "usr/share/help/C/meson-symlink/index.page"}, + {"type": "file", "file": "usr/share/help/C/meson-symlink/index2.page"}, {"type": "file", "file": "usr/share/help/C/meson-symlink/media/test.txt"}, + {"type": "file", "file": "usr/share/help/es/meson-symlink/index2.page"}, {"type": "file", "file": "usr/share/help/es/meson-symlink/media/test.txt"}, - {"type": "file", "file": "usr/share/help/es/meson-symlink/index.page"}, - {"type": "file", "file": "usr/share/help/de/meson-symlink/index.page"}, + {"type": "file", "file": "usr/share/help/de/meson-symlink/index2.page"}, {"type": "file", "file": "usr/share/help/de/meson-symlink/media/test.txt"}, - {"type": "file", "file": "usr/share/help/C/meson-linguas/index.page"}, + {"type": "file", "file": "usr/share/help/C/meson-linguas/index3.page"}, {"type": "file", "file": "usr/share/help/C/meson-linguas/media/test.txt"}, + {"type": "file", "file": "usr/share/help/es/meson-linguas/index3.page"}, {"type": "file", "file": "usr/share/help/es/meson-linguas/media/test.txt"}, - {"type": "file", "file": "usr/share/help/es/meson-linguas/index.page"}, - {"type": "file", "file": "usr/share/help/de/meson-linguas/index.page"}, + {"type": "file", "file": "usr/share/help/de/meson-linguas/index3.page"}, {"type": "file", "file": "usr/share/help/de/meson-linguas/media/test.txt"} ], "skip_on_jobname": ["azure", "cygwin", "macos", "msys2"] diff --git a/test cases/frameworks/7 gnome/gdbus/data/com.example.Sample.xml b/test cases/frameworks/7 gnome/gdbus/data/com.example.Sample.xml index 9ece885..d7adc30 100644 --- a/test cases/frameworks/7 gnome/gdbus/data/com.example.Sample.xml +++ b/test cases/frameworks/7 gnome/gdbus/data/com.example.Sample.xml @@ -6,5 +6,9 @@ <arg direction="in" type="s" name="name"/> <arg direction="out" type="s" name="greeting"/> </method> + <method name="Bye"> + <arg direction="in" type="s" name="name"/> + <arg direction="out" type="s" name="greeting"/> + </method> </interface> </node> diff --git a/test cases/frameworks/7 gnome/gdbus/meson.build b/test cases/frameworks/7 gnome/gdbus/meson.build index 2de172f..a786d24 100644 --- a/test cases/frameworks/7 gnome/gdbus/meson.build +++ b/test cases/frameworks/7 gnome/gdbus/meson.build @@ -10,12 +10,29 @@ assert(gdbus_src.length() == 2, 'expected 2 targets') assert(gdbus_src[0].full_path().endswith('.c'), 'expected 1 c source file') assert(gdbus_src[1].full_path().endswith('.h'), 'expected 1 c header file') +sample_xml = configure_file(input: 'data/com.example.Sample.xml', + output: 'com.example.Sample.xml', + copy: true) + +gdbus_src = gnome.gdbus_codegen('generated-gdbus-no-docbook-files-posarg', + sample_xml, + interface_prefix : 'com.example.', + namespace : 'Sample', + annotations : [ + ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'] + ], +) +assert(gdbus_src.length() == 2, 'expected 2 targets') +assert(gdbus_src[0].full_path().endswith('.c'), 'expected 1 c source file') +assert(gdbus_src[1].full_path().endswith('.h'), 'expected 1 c header file') + gdbus_src = gnome.gdbus_codegen('generated-gdbus', sources : files('data/com.example.Sample.xml'), interface_prefix : 'com.example.', namespace : 'Sample', annotations : [ - ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'] + ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'], + ['com.example.Bye()', 'org.freedesktop.DBus.Deprecated', 'true'], ], docbook : 'generated-gdbus-doc', install_header : true, diff --git a/test cases/frameworks/7 gnome/genmarshal/main.c b/test cases/frameworks/7 gnome/genmarshal/main.c.in index 83b08af..8e3ca7a 100644 --- a/test cases/frameworks/7 gnome/genmarshal/main.c +++ b/test cases/frameworks/7 gnome/genmarshal/main.c.in @@ -1,7 +1,7 @@ -#include<stdio.h> -#include<stdlib.h> -#include<glib-object.h> -#include"marshaller.h" +#include <stdio.h> +#include <stdlib.h> +#include <glib-object.h> +#include @MARSHALLER_HEADER@ static int singleton = 42; diff --git a/test cases/frameworks/7 gnome/genmarshal/meson.build b/test cases/frameworks/7 gnome/genmarshal/meson.build index d7189f5..7686b4b 100644 --- a/test cases/frameworks/7 gnome/genmarshal/meson.build +++ b/test cases/frameworks/7 gnome/genmarshal/meson.build @@ -1,12 +1,51 @@ -marshallers = gnome.genmarshal('marshaller', -sources : 'marshaller.list', -install_header : true, -install_dir : get_option('includedir'), -extra_args : ['-UG_ENABLE_DEBUG', '--prototypes']) - -marshaller_c = marshallers[0] -marshaller_h = marshallers[1] - -genmarshalexe = executable('genmarshalprog', 'main.c', marshaller_c, marshaller_h, -dependencies : gobj) -test('genmarshal test', genmarshalexe) +m_list = configure_file(input: 'marshaller.list', + output: 'm.list', + copy: true) + +idx = 0 +mlists = ['marshaller.list', files('marshaller.list'), m_list] + +foreach mlist : mlists + marshallers = gnome.genmarshal('marshaller-@0@'.format(idx), + sources : mlist, + install_header : true, + install_dir : get_option('includedir') / 'subdir-@0@'.format(idx), + extra_args : ['-UG_ENABLE_DEBUG', '--prototypes']) + + marshaller_c = marshallers[0] + marshaller_h = marshallers[1] + + cdata = configuration_data() + cdata.set_quoted('MARSHALLER_HEADER', 'marshaller-@0@.h'.format(idx)) + + main_c = configure_file(input: 'main.c.in', + output: 'main-@0@.c'.format(idx), + configuration: cdata) + + genmarshalexe = executable('genmarshalprog-@0@'.format(idx), + main_c, marshaller_c, marshaller_h, + dependencies : gobj) + test('genmarshal test @0@'.format(idx), genmarshalexe) + idx += 1 +endforeach + +foreach mlist : mlists + marshallers = gnome.genmarshal('marshaller-@0@'.format(idx), + sources : [mlist], + install_header : true, + install_dir : get_option('includedir') / 'subdir-@0@'.format(idx), + extra_args : ['-UG_ENABLE_DEBUG', '--prototypes']) + + marshaller_c = marshallers[0] + marshaller_h = marshallers[1] + + main_c = configure_file(input: 'main.c.in', + output: 'main-@0@.c'.format(idx), + configuration: cdata) + + genmarshalexe = executable('genmarshalprog-@0@'.format(idx), + main_c, marshaller_c, marshaller_h, + dependencies : gobj) + test('genmarshal test @0@'.format(idx), genmarshalexe) + idx += 1 +endforeach diff --git a/test cases/frameworks/7 gnome/test.json b/test cases/frameworks/7 gnome/test.json index d243cff..0d17384 100644 --- a/test cases/frameworks/7 gnome/test.json +++ b/test cases/frameworks/7 gnome/test.json @@ -4,7 +4,12 @@ {"type": "file", "file": "usr/include/enums2.h"}, {"type": "file", "file": "usr/include/enums3.h"}, {"type": "file", "file": "usr/include/enums5.h"}, - {"type": "file", "file": "usr/include/marshaller.h"}, + {"type": "file", "file": "usr/include/subdir-0/marshaller-0.h"}, + {"type": "file", "file": "usr/include/subdir-1/marshaller-1.h"}, + {"type": "file", "file": "usr/include/subdir-2/marshaller-2.h"}, + {"type": "file", "file": "usr/include/subdir-3/marshaller-3.h"}, + {"type": "file", "file": "usr/include/subdir-4/marshaller-4.h"}, + {"type": "file", "file": "usr/include/subdir-5/marshaller-5.h"}, {"type": "expr", "file": "usr/lib/?libgir_lib.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libgir_lib.dll.a"}, {"type": "expr", "file": "usr/lib/?libgir_lib2.so"}, diff --git a/test cases/unit/99 custom target name/file.txt.in b/test cases/unit/100 custom target name/file.txt.in index e69de29..e69de29 100644 --- a/test cases/unit/99 custom target name/file.txt.in +++ b/test cases/unit/100 custom target name/file.txt.in diff --git a/test cases/unit/99 custom target name/meson.build b/test cases/unit/100 custom target name/meson.build index 8d148a8..8d148a8 100644 --- a/test cases/unit/99 custom target name/meson.build +++ b/test cases/unit/100 custom target name/meson.build diff --git a/test cases/unit/99 custom target name/subdir/meson.build b/test cases/unit/100 custom target name/subdir/meson.build index 785a7b3..785a7b3 100644 --- a/test cases/unit/99 custom target name/subdir/meson.build +++ b/test cases/unit/100 custom target name/subdir/meson.build diff --git a/test cases/unit/99 relative find program/foo.py b/test cases/unit/101 relative find program/foo.py index 21239b7..21239b7 100755 --- a/test cases/unit/99 relative find program/foo.py +++ b/test cases/unit/101 relative find program/foo.py diff --git a/test cases/unit/99 relative find program/meson.build b/test cases/unit/101 relative find program/meson.build index 5745d8a..5745d8a 100644 --- a/test cases/unit/99 relative find program/meson.build +++ b/test cases/unit/101 relative find program/meson.build diff --git a/test cases/unit/99 relative find program/subdir/meson.build b/test cases/unit/101 relative find program/subdir/meson.build index 475f5f5..475f5f5 100644 --- a/test cases/unit/99 relative find program/subdir/meson.build +++ b/test cases/unit/101 relative find program/subdir/meson.build diff --git a/test cases/unit/100 rlib linkage/lib2.rs b/test cases/unit/102 rlib linkage/lib2.rs index 3487bc5..3487bc5 100644 --- a/test cases/unit/100 rlib linkage/lib2.rs +++ b/test cases/unit/102 rlib linkage/lib2.rs diff --git a/test cases/unit/100 rlib linkage/main.rs b/test cases/unit/102 rlib linkage/main.rs index d0f82e4..d0f82e4 100644 --- a/test cases/unit/100 rlib linkage/main.rs +++ b/test cases/unit/102 rlib linkage/main.rs diff --git a/test cases/unit/100 rlib linkage/meson.build b/test cases/unit/102 rlib linkage/meson.build index 2d15b2a..2d15b2a 100644 --- a/test cases/unit/100 rlib linkage/meson.build +++ b/test cases/unit/102 rlib linkage/meson.build diff --git a/test cases/unit/101 python without pkgconfig/meson.build b/test cases/unit/103 python without pkgconfig/meson.build index b3a0c42..b3a0c42 100644 --- a/test cases/unit/101 python without pkgconfig/meson.build +++ b/test cases/unit/103 python without pkgconfig/meson.build diff --git a/test cases/unit/97 link full name/.gitignore b/test cases/unit/98 link full name/.gitignore index 8129601..8129601 100644 --- a/test cases/unit/97 link full name/.gitignore +++ b/test cases/unit/98 link full name/.gitignore diff --git a/test cases/unit/97 link full name/libtestprovider/meson.build b/test cases/unit/98 link full name/libtestprovider/meson.build index 128c213..128c213 100644 --- a/test cases/unit/97 link full name/libtestprovider/meson.build +++ b/test cases/unit/98 link full name/libtestprovider/meson.build diff --git a/test cases/unit/97 link full name/libtestprovider/provider.c b/test cases/unit/98 link full name/libtestprovider/provider.c index 5e79966..5e79966 100644 --- a/test cases/unit/97 link full name/libtestprovider/provider.c +++ b/test cases/unit/98 link full name/libtestprovider/provider.c diff --git a/test cases/unit/97 link full name/proguser/meson.build b/test cases/unit/98 link full name/proguser/meson.build index 5be5bc9..5be5bc9 100644 --- a/test cases/unit/97 link full name/proguser/meson.build +++ b/test cases/unit/98 link full name/proguser/meson.build diff --git a/test cases/unit/97 link full name/proguser/receiver.c b/test cases/unit/98 link full name/proguser/receiver.c index 65e9d8e..65e9d8e 100644 --- a/test cases/unit/97 link full name/proguser/receiver.c +++ b/test cases/unit/98 link full name/proguser/receiver.c diff --git a/test cases/unit/98 install all targets/foo1-devel.h b/test cases/unit/99 install all targets/bar-custom.txt index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/foo1-devel.h +++ b/test cases/unit/99 install all targets/bar-custom.txt diff --git a/test cases/unit/98 install all targets/subdir/bar2-devel.h b/test cases/unit/99 install all targets/bar-devel.h index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/subdir/bar2-devel.h +++ b/test cases/unit/99 install all targets/bar-devel.h diff --git a/test cases/unit/98 install all targets/subdir/foo2.in b/test cases/unit/99 install all targets/bar-notag.txt index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/subdir/foo2.in +++ b/test cases/unit/99 install all targets/bar-notag.txt diff --git a/test cases/unit/98 install all targets/custom_files/data.txt b/test cases/unit/99 install all targets/custom_files/data.txt index 557db03..557db03 100644 --- a/test cases/unit/98 install all targets/custom_files/data.txt +++ b/test cases/unit/99 install all targets/custom_files/data.txt diff --git a/test cases/unit/98 install all targets/subdir/foo3-devel.h b/test cases/unit/99 install all targets/foo.in index e69de29..e69de29 100644 --- a/test cases/unit/98 install all targets/subdir/foo3-devel.h +++ b/test cases/unit/99 install all targets/foo.in diff --git a/test cases/unit/99 install all targets/foo1-devel.h b/test cases/unit/99 install all targets/foo1-devel.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/unit/99 install all targets/foo1-devel.h diff --git a/test cases/unit/98 install all targets/lib.c b/test cases/unit/99 install all targets/lib.c index 2ea9c7d..2ea9c7d 100644 --- a/test cases/unit/98 install all targets/lib.c +++ b/test cases/unit/99 install all targets/lib.c diff --git a/test cases/unit/98 install all targets/main.c b/test cases/unit/99 install all targets/main.c index 0fb4389..0fb4389 100644 --- a/test cases/unit/98 install all targets/main.c +++ b/test cases/unit/99 install all targets/main.c diff --git a/test cases/unit/98 install all targets/meson.build b/test cases/unit/99 install all targets/meson.build index 94bd1fe..94bd1fe 100644 --- a/test cases/unit/98 install all targets/meson.build +++ b/test cases/unit/99 install all targets/meson.build diff --git a/test cases/unit/98 install all targets/script.py b/test cases/unit/99 install all targets/script.py index c5f3be9..c5f3be9 100644 --- a/test cases/unit/98 install all targets/script.py +++ b/test cases/unit/99 install all targets/script.py diff --git a/test cases/unit/99 install all targets/subdir/bar2-devel.h b/test cases/unit/99 install all targets/subdir/bar2-devel.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/unit/99 install all targets/subdir/bar2-devel.h diff --git a/test cases/unit/99 install all targets/subdir/foo2.in b/test cases/unit/99 install all targets/subdir/foo2.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/unit/99 install all targets/subdir/foo2.in diff --git a/test cases/unit/99 install all targets/subdir/foo3-devel.h b/test cases/unit/99 install all targets/subdir/foo3-devel.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/unit/99 install all targets/subdir/foo3-devel.h diff --git a/test cases/unit/98 install all targets/subdir/lib.c b/test cases/unit/99 install all targets/subdir/lib.c index 2ea9c7d..2ea9c7d 100644 --- a/test cases/unit/98 install all targets/subdir/lib.c +++ b/test cases/unit/99 install all targets/subdir/lib.c diff --git a/test cases/unit/98 install all targets/subdir/main.c b/test cases/unit/99 install all targets/subdir/main.c index 0fb4389..0fb4389 100644 --- a/test cases/unit/98 install all targets/subdir/main.c +++ b/test cases/unit/99 install all targets/subdir/main.c diff --git a/test cases/unit/98 install all targets/subdir/meson.build b/test cases/unit/99 install all targets/subdir/meson.build index 53c796a..53c796a 100644 --- a/test cases/unit/98 install all targets/subdir/meson.build +++ b/test cases/unit/99 install all targets/subdir/meson.build diff --git a/test cases/unit/98 install all targets/subdir/script.py b/test cases/unit/99 install all targets/subdir/script.py index c5f3be9..c5f3be9 100644 --- a/test cases/unit/98 install all targets/subdir/script.py +++ b/test cases/unit/99 install all targets/subdir/script.py diff --git a/test cases/vala/11 generated vapi/libfoo/foo.metadata b/test cases/vala/11 generated vapi/libfoo/foo.metadata new file mode 100644 index 0000000..e208fe3 --- /dev/null +++ b/test cases/vala/11 generated vapi/libfoo/foo.metadata @@ -0,0 +1 @@ +Foo.bar nullable diff --git a/test cases/vala/11 generated vapi/libfoo/meson.build b/test cases/vala/11 generated vapi/libfoo/meson.build index 9dc14ce..ee425f2 100644 --- a/test cases/vala/11 generated vapi/libfoo/meson.build +++ b/test cases/vala/11 generated vapi/libfoo/meson.build @@ -24,7 +24,14 @@ libfoo_gir = gnome.generate_gir(libfoo, ], ) +configure_file( + input: 'foo.metadata', + output: 'Foo-@0@.metadata'.format(libfoo_api_ver), + copy: true +) + libfoo_vapi = gnome.generate_vapi('foo-' + libfoo_api_ver, + metadata_dirs: meson.current_build_dir(), sources: libfoo_gir[0], install: true, ) diff --git a/tools/dircondenser.py b/tools/dircondenser.py index 2a726df..fa299e9 100755 --- a/tools/dircondenser.py +++ b/tools/dircondenser.py @@ -81,6 +81,8 @@ def condense(dirname: str) -> None: os.chdir(curdir) replace_source('run_unittests.py', replacements) replace_source('run_project_tests.py', replacements) + for f in glob('unittests/*.py'): + replace_source(f, replacements) if __name__ == '__main__': if len(sys.argv) != 1: diff --git a/tools/gen_data.py b/tools/gen_data.py deleted file mode 100755 index b1c62e0..0000000 --- a/tools/gen_data.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Daniel Mensinger - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import hashlib -import textwrap -import re -from pathlib import Path -from datetime import datetime -import typing as T - -class DataFile: - file_counter = 0 - - def __init__(self, path: Path, root: Path): - self.path = path - self.id = self.path.relative_to(root) - self.data_str = f'file_{DataFile.file_counter}_data_' + re.sub('[^a-zA-Z0-9]', '_', self.path.name) - DataFile.file_counter += 1 - - b = self.path.read_bytes() - self.data = b.decode() - self.sha256sum = hashlib.sha256(b).hexdigest() - - def __repr__(self) -> str: - return f'<{type(self).__name__}: [{self.sha256sum}] {self.id}>' - -def main() -> int: - root_dir = Path(__file__).resolve().parents[1] - mesonbuild_dir = root_dir / 'mesonbuild' - out_file = mesonbuild_dir / 'mesondata.py' - - data_dirs = sorted(mesonbuild_dir.glob('**/data')) - - data_files: T.List[DataFile] = [] - - for d in data_dirs: - for p in sorted(d.iterdir()): - data_files += [DataFile(p, mesonbuild_dir)] - - print(f'Found {len(data_files)} data files') - - # Generate the data script - data = '' - - data += textwrap.dedent(f'''\ - # Copyright {datetime.today().year} The Meson development team - - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at - - # http://www.apache.org/licenses/LICENSE-2.0 - - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - - - #### - #### WARNING: This is an automatically generated file! Do not edit! - #### Generated by {Path(__file__).resolve().relative_to(root_dir)} - #### - - - # TODO: Remember to remove this also from tools/gen_data.py - from pathlib import Path - import typing as T - - if T.TYPE_CHECKING: - from .environment import Environment - - ###################### - # BEGIN Data section # - ###################### - - ''') - - for i in data_files: - data += f"{i.data_str} = '''\\\n{i.data}'''\n\n" - - data += textwrap.dedent(f''' - #################### - # END Data section # - #################### - - class DataFile: - def __init__(self, path: Path, sha256sum: str, data: str) -> None: - self.path = path - self.sha256sum = sha256sum - self.data = data - - def write_once(self, path: Path) -> None: - if not path.exists(): - path.write_text(self.data, encoding='utf-8') - - def write_to_private(self, env: 'Environment') -> Path: - out_file = Path(env.scratch_dir) / 'data' / self.path.name - out_file.parent.mkdir(exist_ok=True) - self.write_once(out_file) - return out_file - - - mesondata = {{ - ''') - - for i in data_files: - data += textwrap.indent(textwrap.dedent(f"""\ - '{i.id}': DataFile( - Path('{i.id}'), - '{i.sha256sum}', - {i.data_str}, - ), - """), ' ') - - data += textwrap.dedent('''\ - } - ''') - - print(f'Updating {out_file}') - out_file.write_text(data, encoding='utf-8') - return 0 - -if __name__ == '__main__': - sys.exit(main()) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index daa1385..51ca7a0 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1592,10 +1592,10 @@ class AllPlatformTests(BasePlatformTests): os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir env = get_fake_env(testdir, self.builddir, self.prefix) kwargs = {'required': True, 'silent': True} - foo_dep = PkgConfigDependency('libfoo', env, kwargs) + foo_dep = PkgConfigDependency('libanswer', env, kwargs) # Ensure link_args are properly quoted libdir = PurePath(prefix) / PurePath(libdir) - link_args = ['-L' + libdir.as_posix(), '-lfoo'] + link_args = ['-L' + libdir.as_posix(), '-lanswer'] self.assertEqual(foo_dep.get_link_args(), link_args) # Ensure include args are properly quoted incdir = PurePath(prefix) / PurePath('include') @@ -2306,7 +2306,6 @@ class AllPlatformTests(BasePlatformTests): self.assertEqual(f.read().strip(), b'/* #undef FOO_BAR */') with open(os.path.join(self.builddir, 'nosubst-nocopy2.txt'), 'rb') as f: self.assertEqual(f.read().strip(), b'') - self.assertRegex(out, r"DEPRECATION:.*\['array'\] is invalid.*dict") def test_dirs(self): with tempfile.TemporaryDirectory() as containing: @@ -3186,11 +3185,6 @@ class AllPlatformTests(BasePlatformTests): expected_lines = expected.split('\n')[1:] out_start = out.find(expected_lines[0]) out_lines = out[out_start:].split('\n')[:len(expected_lines)] - if sys.version_info < (3, 7, 0): - # Dictionary order is not stable in Python <3.7, so sort the lines - # while comparing - expected_lines = sorted(expected_lines) - out_lines = sorted(out_lines) for e, o in zip(expected_lines, out_lines): if e.startswith(' external dep'): self.assertRegex(o, r'^ external dep : (YES [0-9.]*|NO)$') @@ -3830,7 +3824,7 @@ class AllPlatformTests(BasePlatformTests): self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') self.init(testdir) self.build() @@ -3952,7 +3946,7 @@ class AllPlatformTests(BasePlatformTests): do_install(None, expected_all, 2) def test_introspect_install_plan(self): - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json') self.init(testdir) self.assertPathExists(introfile) @@ -4139,7 +4133,7 @@ class AllPlatformTests(BasePlatformTests): }} ''') - testdir = os.path.join(self.unit_test_dir, '100 rlib linkage') + testdir = os.path.join(self.unit_test_dir, '102 rlib linkage') gen_file = os.path.join(testdir, 'lib.rs') with open(gen_file, 'w') as f: f.write(template.format(0)) @@ -4159,7 +4153,7 @@ class AllPlatformTests(BasePlatformTests): self.assertIn('exit status 39', cm.exception.stdout) def test_custom_target_name(self): - testdir = os.path.join(self.unit_test_dir, '99 custom target name') + testdir = os.path.join(self.unit_test_dir, '100 custom target name') self.init(testdir) out = self.build() if self.backend is Backend.ninja: diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index bd2f902..cfc78ce 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -160,7 +160,7 @@ class BasePlatformTests(TestCase): p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, encoding='utf-8', - universal_newlines=True, cwd=workdir, timeout=60 * 5) + text=True, cwd=workdir, timeout=60 * 5) print(p.stdout) if p.returncode != 0: if 'MESON_SKIP_TEST' in p.stdout: @@ -249,10 +249,10 @@ class BasePlatformTests(TestCase): def run_tests(self, *, inprocess=False, override_envvars=None): if not inprocess: - self._run(self.test_command, workdir=self.builddir, override_envvars=override_envvars) + return self._run(self.test_command, workdir=self.builddir, override_envvars=override_envvars) else: with mock.patch.dict(os.environ, override_envvars): - run_mtest_inprocess(['-C', self.builddir]) + return run_mtest_inprocess(['-C', self.builddir])[1] def install(self, *, use_destdir=True, override_envvars=None): if self.backend is not Backend.ninja: @@ -263,7 +263,7 @@ class BasePlatformTests(TestCase): override_envvars = destdir else: override_envvars.update(destdir) - self._run(self.install_command, workdir=self.builddir, override_envvars=override_envvars) + return self._run(self.install_command, workdir=self.builddir, override_envvars=override_envvars) def uninstall(self, *, override_envvars=None): self._run(self.uninstall_command, workdir=self.builddir, override_envvars=override_envvars) diff --git a/unittests/datatests.py b/unittests/datatests.py index e440243..b623f1f 100644 --- a/unittests/datatests.py +++ b/unittests/datatests.py @@ -13,12 +13,9 @@ # limitations under the License. import re -import textwrap import unittest -import hashlib from itertools import chain from pathlib import Path -import typing as T import mesonbuild.mlog import mesonbuild.depfile @@ -237,36 +234,3 @@ class DataTests(unittest.TestCase): interp = Interpreter(FakeBuild(env), mock=True) astint = AstInterpreter('.', '', '') self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys())) - - def test_mesondata_is_up_to_date(self): - from mesonbuild.mesondata import mesondata - err_msg = textwrap.dedent(''' - - ########################################################### - ### mesonbuild.mesondata is not up-to-date ### - ### Please regenerate it by running tools/gen_data.py ### - ########################################################### - - ''') - - root_dir = Path(__file__).parents[1] - - mesonbuild_dir = root_dir / 'mesonbuild' - - data_dirs = mesonbuild_dir.glob('**/data') - data_files = [] # type: T.List[T.Tuple(str, str)] - - for i in data_dirs: - for p in i.iterdir(): - data_files += [(p.relative_to(mesonbuild_dir).as_posix(), hashlib.sha256(p.read_bytes()).hexdigest())] - - current_files = set(mesondata.keys()) - scanned_files = {x[0] for x in data_files} - - self.assertSetEqual(current_files, scanned_files, err_msg + 'Data files were added or removed\n') - errors = [] - for i in data_files: - if mesondata[i[0]].sha256sum != i[1]: - errors += [i[0]] - - self.assertListEqual(errors, [], err_msg + 'Files were changed') diff --git a/unittests/helpers.py b/unittests/helpers.py index b759dba..bf06bdc 100644 --- a/unittests/helpers.py +++ b/unittests/helpers.py @@ -5,11 +5,13 @@ import unittest import functools import re import typing as T +from pathlib import Path from contextlib import contextmanager from mesonbuild.compilers import detect_c_compiler, compiler_from_language from mesonbuild.mesonlib import ( - MachineChoice, is_osx, is_cygwin, EnvironmentException, OptionKey, MachineChoice + MachineChoice, is_osx, is_cygwin, EnvironmentException, OptionKey, MachineChoice, + OrderedSet ) from run_tests import get_fake_env @@ -170,3 +172,15 @@ def get_rpath(fname: str) -> T.Optional[str]: # don't check for, so clear those final = ':'.join([e for e in raw.split(':') if not e.startswith('/nix')]) return final + +def get_path_without_cmd(cmd: str, path: str) -> str: + pathsep = os.pathsep + paths = OrderedSet([Path(p).resolve() for p in path.split(pathsep)]) + while True: + full_path = shutil.which(cmd, path=path) + if full_path is None: + break + dirname = Path(full_path).resolve().parent + paths.discard(dirname) + path = pathsep.join([str(p) for p in paths]) + return path diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 8ff0f8e..90db4ca 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -148,18 +148,18 @@ class LinuxlikeTests(BasePlatformTests): self.assertTrue(foo_dep.found()) self.assertEqual(foo_dep.get_version(), '1.0') self.assertIn('-lfoo', foo_dep.get_link_args()) - self.assertEqual(foo_dep.get_pkgconfig_variable('foo', {}), 'bar') - self.assertPathEqual(foo_dep.get_pkgconfig_variable('datadir', {}), '/usr/data') + self.assertEqual(foo_dep.get_pkgconfig_variable('foo', [], None), 'bar') + self.assertPathEqual(foo_dep.get_pkgconfig_variable('datadir', [], None), '/usr/data') libhello_nolib = PkgConfigDependency('libhello_nolib', env, kwargs) self.assertTrue(libhello_nolib.found()) self.assertEqual(libhello_nolib.get_link_args(), []) self.assertEqual(libhello_nolib.get_compile_args(), []) - self.assertEqual(libhello_nolib.get_pkgconfig_variable('foo', {}), 'bar') - self.assertEqual(libhello_nolib.get_pkgconfig_variable('prefix', {}), self.prefix) + self.assertEqual(libhello_nolib.get_pkgconfig_variable('foo', [], None), 'bar') + self.assertEqual(libhello_nolib.get_pkgconfig_variable('prefix', [], None), self.prefix) if version_compare(libhello_nolib.check_pkgconfig(libhello_nolib.pkgbin),">=0.29.1"): - self.assertEqual(libhello_nolib.get_pkgconfig_variable('escaped_var', {}), r'hello\ world') - self.assertEqual(libhello_nolib.get_pkgconfig_variable('unescaped_var', {}), 'hello world') + self.assertEqual(libhello_nolib.get_pkgconfig_variable('escaped_var', [], None), r'hello\ world') + self.assertEqual(libhello_nolib.get_pkgconfig_variable('unescaped_var', [], None), 'hello world') cc = detect_c_compiler(env, MachineChoice.HOST) if cc.get_id() in {'gcc', 'clang'}: @@ -1334,7 +1334,7 @@ class LinuxlikeTests(BasePlatformTests): see: https://github.com/mesonbuild/meson/issues/9000 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a ''' - testdir = os.path.join(self.unit_test_dir, '97 link full name','libtestprovider') + testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider') oldprefix = self.prefix # install into installdir without using DESTDIR installdir = self.installdir @@ -1347,7 +1347,7 @@ class LinuxlikeTests(BasePlatformTests): self.new_builddir() env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir), 'PKG_CONFIG_PATH': os.path.join(installdir, self.libdir, 'pkgconfig')} - testdir = os.path.join(self.unit_test_dir, '97 link full name','proguser') + testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser') self.init(testdir,override_envvars=env) # test for link with full path @@ -1717,7 +1717,7 @@ class LinuxlikeTests(BasePlatformTests): p = subprocess.run([ar, 't', outlib], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - universal_newlines=True, timeout=1) + text=True, timeout=1) obj_files = p.stdout.strip().split('\n') self.assertEqual(len(obj_files), 1) self.assertTrue(obj_files[0].endswith('-prelink.o')) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 25f3b1a..c5e5233 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -32,7 +32,7 @@ class PlatformAgnosticTests(BasePlatformTests): Tests that find_program() with a relative path does not find the program in current workdir. ''' - testdir = os.path.join(self.unit_test_dir, '99 relative find program') + testdir = os.path.join(self.unit_test_dir, '101 relative find program') self.init(testdir, workdir=testdir) def test_invalid_option_names(self): @@ -68,5 +68,5 @@ class PlatformAgnosticTests(BasePlatformTests): interp.process(fname) def test_python_dependency_without_pkgconfig(self): - testdir = os.path.join(self.unit_test_dir, '101 python without pkgconfig') + testdir = os.path.join(self.unit_test_dir, '103 python without pkgconfig') self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'}) diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 1e6a398..161ae9d 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -32,8 +32,7 @@ class RewriterTests(BasePlatformTests): if isinstance(args, str): args = [args] command = self.rewrite_command + ['--verbose', '--skip', '--sourcedir', directory] + args - p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, timeout=60) + p = subprocess.run(command, capture_output=True, text=True, timeout=60) print('STDOUT:') print(p.stdout) print('STDERR:') diff --git a/unittests/windowstests.py b/unittests/windowstests.py index bd75c74..d3ad932 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -373,3 +373,29 @@ class WindowsTests(BasePlatformTests): raise SkipTest('Not using MSVC') self.init(testdir, extra_args=['-Dtest-failure=true']) self.assertRaises(subprocess.CalledProcessError, self.build) + + @unittest.skipIf(is_cygwin(), "Needs visual studio") + def test_vsenv_option(self): + if self.backend is not Backend.ninja: + raise SkipTest('Only ninja backend is valid for test') + env = os.environ.copy() + env['MESON_FORCE_VSENV_FOR_UNITTEST'] = '1' + # Remove ninja from PATH to ensure that the one provided by Visual + # Studio is picked, as a regression test for + # https://github.com/mesonbuild/meson/issues/9774 + env['PATH'] = get_path_without_cmd('ninja', env['PATH']) + testdir = os.path.join(self.common_test_dir, '1 trivial') + out = self.init(testdir, extra_args=['--vsenv'], override_envvars=env) + self.assertIn('Activating VS', out) + self.assertRegex(out, 'Visual Studio environment is needed to run Ninja') + # All these directly call ninja with the full path, so we need to patch + # it out to use meson subcommands + with mock.patch.object(self, 'build_command', self.meson_command + ['compile']): + out = self.build(override_envvars=env) + self.assertIn('Activating VS', out) + with mock.patch.object(self, 'test_command', self.meson_command + ['test']): + out = self.run_tests(override_envvars=env) + self.assertIn('Activating VS', out) + with mock.patch.object(self, 'install_command', self.meson_command + ['install']): + out = self.install(override_envvars=env) + self.assertIn('Activating VS', out) |