aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/codecov.yml11
-rw-r--r--.github/workflows/cygwin.yml18
-rw-r--r--.github/workflows/file_format.yml1
-rw-r--r--.github/workflows/images.yml15
-rw-r--r--.github/workflows/lint.yml4
-rw-r--r--.github/workflows/macos.yml34
-rw-r--r--.github/workflows/msys2.yml18
-rw-r--r--.github/workflows/nonnative.yml22
-rw-r--r--.github/workflows/os_comp.yml26
-rw-r--r--.github/workflows/unusedargs_missingreturn.yml30
-rw-r--r--.gitignore1
-rw-r--r--.mailmap5
-rw-r--r--.mypy.ini4
-rw-r--r--azure-pipelines.yml25
-rwxr-xr-xci/ciimage/arch/install.sh5
-rw-r--r--ci/ciimage/cuda-cross/image.json8
-rwxr-xr-xci/ciimage/cuda-cross/install.sh38
-rwxr-xr-xci/ciimage/fedora/install.sh11
-rw-r--r--ci/ciimage/gentoo/image.json2
-rwxr-xr-xci/ciimage/gentoo/install.sh71
-rwxr-xr-xci/ciimage/opensuse/install.sh13
-rwxr-xr-xci/ciimage/ubuntu-rolling/install.sh6
-rwxr-xr-xci/combine_cov.sh10
-rw-r--r--ci/coverage.ps114
-rw-r--r--ci/run.ps16
-rw-r--r--ci/usercustomize.py5
-rw-r--r--cross/cuda-cross.json5
-rw-r--r--cross/cuda-cross.txt17
-rw-r--r--cross/ubuntu-armhf.txt1
-rw-r--r--cross/xc32.txt17
-rw-r--r--data/macros.meson4
-rw-r--r--data/syntax-highlighting/vim/syntax/meson.vim4
-rw-r--r--docs/extensions/refman_links.py35
-rw-r--r--docs/markdown/Adding-new-projects-to-wrapdb.md72
-rw-r--r--docs/markdown/Build-options.md4
-rw-r--r--docs/markdown/Builtin-options.md88
-rw-r--r--docs/markdown/CMake-module.md4
-rw-r--r--docs/markdown/Codegen-module.md144
-rw-r--r--docs/markdown/Commands.md6
-rw-r--r--docs/markdown/Creating-releases.md2
-rw-r--r--docs/markdown/Cross-compilation.md24
-rw-r--r--docs/markdown/Dependencies.md3
-rw-r--r--docs/markdown/Design-rationale.md10
-rw-r--r--docs/markdown/Fs-module.md15
-rw-r--r--docs/markdown/Getting-meson_zh.md2
-rw-r--r--docs/markdown/Gnome-module.md7
-rw-r--r--docs/markdown/Installing.md1
-rw-r--r--docs/markdown/Overview.md10
-rw-r--r--docs/markdown/Pkgconfig-module.md8
-rw-r--r--docs/markdown/Qt6-module.md2
-rw-r--r--docs/markdown/Reference-tables.md8
-rw-r--r--docs/markdown/Release-notes-for-0.38.0.md2
-rw-r--r--docs/markdown/Release-notes-for-1.10.0.md289
-rw-r--r--docs/markdown/Release-notes-for-1.9.0.md138
-rw-r--r--docs/markdown/Rewriter.md19
-rw-r--r--docs/markdown/Rust-module.md2
-rw-r--r--docs/markdown/Rust.md21
-rw-r--r--docs/markdown/Snippets-module.md111
-rw-r--r--docs/markdown/Syntax.md2
-rw-r--r--docs/markdown/Users.md188
-rw-r--r--docs/markdown/Vala.md49
-rw-r--r--docs/markdown/Wrap-dependency-system-manual.md29
-rw-r--r--docs/markdown/Yaml-RefMan.md6
-rw-r--r--docs/markdown/_Sidebar.md1
-rw-r--r--docs/markdown/i18n-module.md7
-rw-r--r--docs/markdown/index.md4
-rw-r--r--docs/markdown/snippets/update_xc32_for_v5.md4
-rw-r--r--docs/meson.build4
-rw-r--r--docs/refman/generatormd.py9
-rw-r--r--docs/refman/model.py3
-rw-r--r--docs/refman/templates/meson.vim.mustache4
-rw-r--r--docs/sitemap.txt4
-rw-r--r--docs/theme/extra/templates/navbar_links.html2
-rw-r--r--docs/yaml/builtins/meson.yaml10
-rw-r--r--docs/yaml/elementary/array.yml74
-rw-r--r--docs/yaml/elementary/dict.yml11
-rw-r--r--docs/yaml/elementary/list.yml42
-rw-r--r--docs/yaml/elementary/str.yml8
-rw-r--r--docs/yaml/functions/_build_target_base.yaml96
-rw-r--r--docs/yaml/functions/add_global_arguments.yaml4
-rw-r--r--docs/yaml/functions/add_test_setup.yaml10
-rw-r--r--docs/yaml/functions/benchmark.yaml12
-rw-r--r--docs/yaml/functions/build_target.yaml6
-rw-r--r--docs/yaml/functions/configure_file.yaml26
-rw-r--r--docs/yaml/functions/custom_target.yaml37
-rw-r--r--docs/yaml/functions/debug.yaml4
-rw-r--r--docs/yaml/functions/declare_dependency.yaml28
-rw-r--r--docs/yaml/functions/dependency.yaml19
-rw-r--r--docs/yaml/functions/environment.yaml4
-rw-r--r--docs/yaml/functions/executable.yaml17
-rw-r--r--docs/yaml/functions/files.yaml2
-rw-r--r--docs/yaml/functions/find_program.yaml10
-rw-r--r--docs/yaml/functions/generator.yaml10
-rw-r--r--docs/yaml/functions/get_option.yaml7
-rw-r--r--docs/yaml/functions/install_data.yaml13
-rw-r--r--docs/yaml/functions/install_emptydir.yaml2
-rw-r--r--docs/yaml/functions/install_headers.yaml6
-rw-r--r--docs/yaml/functions/install_man.yaml2
-rw-r--r--docs/yaml/functions/install_subdir.yaml10
-rw-r--r--docs/yaml/functions/library.yaml8
-rw-r--r--docs/yaml/functions/message.yaml4
-rw-r--r--docs/yaml/functions/project.yaml22
-rw-r--r--docs/yaml/functions/run_command.yaml2
-rw-r--r--docs/yaml/functions/run_target.yaml12
-rw-r--r--docs/yaml/functions/shared_library.yaml12
-rw-r--r--docs/yaml/functions/structured_sources.yaml2
-rw-r--r--docs/yaml/functions/subdir.yaml2
-rw-r--r--docs/yaml/functions/subproject.yaml2
-rw-r--r--docs/yaml/functions/summary.yaml14
-rw-r--r--docs/yaml/functions/vcs_tag.yaml4
-rw-r--r--docs/yaml/objects/build_tgt.yaml24
-rw-r--r--docs/yaml/objects/cfg_data.yaml2
-rw-r--r--docs/yaml/objects/compiler.yaml54
-rw-r--r--docs/yaml/objects/custom_tgt.yaml4
-rw-r--r--docs/yaml/objects/dep.yaml7
-rw-r--r--docs/yaml/objects/external_program.yaml5
-rw-r--r--docs/yaml/objects/generator.yaml10
-rw-r--r--man/meson.16
-rw-r--r--mesonbuild/ast/interpreter.py730
-rw-r--r--mesonbuild/ast/introspection.py224
-rw-r--r--mesonbuild/ast/printer.py52
-rw-r--r--mesonbuild/backend/backends.py364
-rw-r--r--mesonbuild/backend/ninjabackend.py642
-rw-r--r--mesonbuild/backend/vs2010backend.py193
-rw-r--r--mesonbuild/backend/vs2012backend.py7
-rw-r--r--mesonbuild/backend/vs2013backend.py7
-rw-r--r--mesonbuild/backend/vs2015backend.py6
-rw-r--r--mesonbuild/backend/vs2017backend.py7
-rw-r--r--mesonbuild/backend/vs2019backend.py7
-rw-r--r--mesonbuild/backend/vs2022backend.py7
-rw-r--r--mesonbuild/backend/vs2026backend.py55
-rw-r--r--mesonbuild/backend/xcodebackend.py115
-rw-r--r--mesonbuild/build.py999
-rw-r--r--mesonbuild/cargo/__init__.py5
-rw-r--r--mesonbuild/cargo/builder.py6
-rw-r--r--mesonbuild/cargo/cfg.py167
-rw-r--r--mesonbuild/cargo/interpreter.py1118
-rw-r--r--mesonbuild/cargo/manifest.py756
-rw-r--r--mesonbuild/cargo/raw.py204
-rw-r--r--mesonbuild/cargo/toml.py49
-rw-r--r--mesonbuild/cargo/version.py14
-rw-r--r--mesonbuild/cmake/common.py1
-rw-r--r--mesonbuild/cmake/generator.py2
-rw-r--r--mesonbuild/cmake/interpreter.py41
-rw-r--r--mesonbuild/cmake/toolchain.py19
-rw-r--r--mesonbuild/cmake/tracetargets.py12
-rw-r--r--mesonbuild/cmdline.py182
-rw-r--r--mesonbuild/compilers/__init__.py2
-rw-r--r--mesonbuild/compilers/asm.py94
-rw-r--r--mesonbuild/compilers/c.py257
-rw-r--r--mesonbuild/compilers/c_function_attributes.py9
-rw-r--r--mesonbuild/compilers/compilers.py279
-rw-r--r--mesonbuild/compilers/cpp.py366
-rw-r--r--mesonbuild/compilers/cs.py13
-rw-r--r--mesonbuild/compilers/cuda.py98
-rw-r--r--mesonbuild/compilers/cython.py11
-rw-r--r--mesonbuild/compilers/d.py89
-rw-r--r--mesonbuild/compilers/detect.py389
-rw-r--r--mesonbuild/compilers/fortran.py232
-rw-r--r--mesonbuild/compilers/java.py9
-rw-r--r--mesonbuild/compilers/mixins/apple.py24
-rw-r--r--mesonbuild/compilers/mixins/arm.py3
-rw-r--r--mesonbuild/compilers/mixins/ccrx.py3
-rw-r--r--mesonbuild/compilers/mixins/clang.py83
-rw-r--r--mesonbuild/compilers/mixins/clike.py374
-rw-r--r--mesonbuild/compilers/mixins/compcert.py3
-rw-r--r--mesonbuild/compilers/mixins/elbrus.py13
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py11
-rw-r--r--mesonbuild/compilers/mixins/gnu.py104
-rw-r--r--mesonbuild/compilers/mixins/intel.py7
-rw-r--r--mesonbuild/compilers/mixins/islinker.py23
-rw-r--r--mesonbuild/compilers/mixins/microchip.py (renamed from mesonbuild/compilers/mixins/xc16.py)97
-rw-r--r--mesonbuild/compilers/mixins/pgi.py11
-rw-r--r--mesonbuild/compilers/mixins/ti.py7
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py60
-rw-r--r--mesonbuild/compilers/objc.py33
-rw-r--r--mesonbuild/compilers/objcpp.py34
-rw-r--r--mesonbuild/compilers/rust.py277
-rw-r--r--mesonbuild/compilers/swift.py44
-rw-r--r--mesonbuild/compilers/vala.py49
-rw-r--r--mesonbuild/coredata.py316
-rw-r--r--mesonbuild/dependencies/__init__.py4
-rw-r--r--mesonbuild/dependencies/base.py129
-rw-r--r--mesonbuild/dependencies/boost.py42
-rw-r--r--mesonbuild/dependencies/cmake.py28
-rw-r--r--mesonbuild/dependencies/coarrays.py9
-rw-r--r--mesonbuild/dependencies/configtool.py12
-rw-r--r--mesonbuild/dependencies/cuda.py116
-rw-r--r--mesonbuild/dependencies/detect.py56
-rw-r--r--mesonbuild/dependencies/dev.py77
-rw-r--r--mesonbuild/dependencies/dub.py7
-rw-r--r--mesonbuild/dependencies/factory.py18
-rw-r--r--mesonbuild/dependencies/framework.py7
-rw-r--r--mesonbuild/dependencies/hdf5.py12
-rw-r--r--mesonbuild/dependencies/misc.py133
-rw-r--r--mesonbuild/dependencies/mpi.py9
-rw-r--r--mesonbuild/dependencies/pkgconfig.py53
-rw-r--r--mesonbuild/dependencies/platform.py7
-rw-r--r--mesonbuild/dependencies/python.py314
-rw-r--r--mesonbuild/dependencies/qt.py34
-rw-r--r--mesonbuild/dependencies/scalapack.py18
-rw-r--r--mesonbuild/dependencies/ui.py80
-rw-r--r--mesonbuild/envconfig.py298
-rw-r--r--mesonbuild/environment.py669
-rw-r--r--mesonbuild/interpreter/__init__.py7
-rw-r--r--mesonbuild/interpreter/compiler.py168
-rw-r--r--mesonbuild/interpreter/dependencyfallbacks.py87
-rw-r--r--mesonbuild/interpreter/interpreter.py432
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py269
-rw-r--r--mesonbuild/interpreter/kwargs.py63
-rw-r--r--mesonbuild/interpreter/mesonmain.py99
-rw-r--r--mesonbuild/interpreter/primitives/array.py65
-rw-r--r--mesonbuild/interpreter/primitives/boolean.py28
-rw-r--r--mesonbuild/interpreter/primitives/dict.py56
-rw-r--r--mesonbuild/interpreter/primitives/integer.py53
-rw-r--r--mesonbuild/interpreter/primitives/range.py7
-rw-r--r--mesonbuild/interpreter/primitives/string.py112
-rw-r--r--mesonbuild/interpreter/type_checking.py242
-rw-r--r--mesonbuild/interpreterbase/__init__.py6
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py123
-rw-r--r--mesonbuild/interpreterbase/decorators.py13
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py41
-rw-r--r--mesonbuild/interpreterbase/operator.py5
-rw-r--r--mesonbuild/linkers/detect.py48
-rw-r--r--mesonbuild/linkers/linkers.py429
-rw-r--r--mesonbuild/machinefile.py20
-rw-r--r--mesonbuild/mcompile.py2
-rw-r--r--mesonbuild/mconf.py66
-rw-r--r--mesonbuild/mdevenv.py57
-rw-r--r--mesonbuild/mdist.py18
-rw-r--r--mesonbuild/mesonlib.py12
-rw-r--r--mesonbuild/mesonmain.py4
-rw-r--r--mesonbuild/mformat.py127
-rw-r--r--mesonbuild/minit.py2
-rw-r--r--mesonbuild/minstall.py14
-rw-r--r--mesonbuild/mintro.py133
-rw-r--r--mesonbuild/mlog.py14
-rw-r--r--mesonbuild/modules/__init__.py8
-rw-r--r--mesonbuild/modules/_qt.py77
-rw-r--r--mesonbuild/modules/cmake.py31
-rw-r--r--mesonbuild/modules/codegen.py445
-rw-r--r--mesonbuild/modules/cuda.py205
-rw-r--r--mesonbuild/modules/dlang.py74
-rw-r--r--mesonbuild/modules/external_project.py13
-rw-r--r--mesonbuild/modules/fs.py121
-rw-r--r--mesonbuild/modules/gnome.py137
-rw-r--r--mesonbuild/modules/hotdoc.py32
-rw-r--r--mesonbuild/modules/i18n.py26
-rw-r--r--mesonbuild/modules/pkgconfig.py62
-rw-r--r--mesonbuild/modules/python.py88
-rw-r--r--mesonbuild/modules/python3.py2
-rw-r--r--mesonbuild/modules/rust.py59
-rw-r--r--mesonbuild/modules/simd.py2
-rw-r--r--mesonbuild/modules/snippets.py104
-rw-r--r--mesonbuild/modules/windows.py14
-rw-r--r--mesonbuild/mparser.py250
-rw-r--r--mesonbuild/msetup.py81
-rwxr-xr-xmesonbuild/msubprojects.py22
-rw-r--r--mesonbuild/mtest.py86
-rw-r--r--mesonbuild/optinterpreter.py2
-rw-r--r--mesonbuild/options.py596
-rw-r--r--mesonbuild/programs.py7
-rw-r--r--mesonbuild/rewriter.py847
-rw-r--r--mesonbuild/scripts/clangformat.py2
-rw-r--r--mesonbuild/scripts/clangtidy.py6
-rw-r--r--mesonbuild/scripts/clippy.py2
-rw-r--r--mesonbuild/scripts/coverage.py6
-rw-r--r--mesonbuild/scripts/depfixer.py79
-rwxr-xr-xmesonbuild/scripts/env2mfile.py115
-rw-r--r--mesonbuild/scripts/externalproject.py10
-rw-r--r--mesonbuild/scripts/pickle_env.py8
-rw-r--r--mesonbuild/scripts/run_tool.py30
-rw-r--r--mesonbuild/scripts/rustdoc.py17
-rw-r--r--mesonbuild/scripts/scanbuild.py9
-rw-r--r--mesonbuild/scripts/symbolextractor.py4
-rw-r--r--mesonbuild/scripts/vcstagger.py4
-rw-r--r--mesonbuild/templates/cpptemplates.py18
-rw-r--r--mesonbuild/templates/cstemplates.py10
-rw-r--r--mesonbuild/templates/ctemplates.py16
-rw-r--r--mesonbuild/templates/cudatemplates.py10
-rw-r--r--mesonbuild/templates/dlangtemplates.py9
-rw-r--r--mesonbuild/templates/fortrantemplates.py10
-rw-r--r--mesonbuild/templates/javatemplates.py10
-rw-r--r--mesonbuild/templates/objcpptemplates.py16
-rw-r--r--mesonbuild/templates/objctemplates.py16
-rw-r--r--mesonbuild/templates/rusttemplates.py10
-rw-r--r--mesonbuild/templates/sampleimpl.py55
-rw-r--r--mesonbuild/templates/valatemplates.py10
-rw-r--r--mesonbuild/tooldetect.py248
-rw-r--r--mesonbuild/utils/platform.py105
-rw-r--r--mesonbuild/utils/posix.py32
-rw-r--r--mesonbuild/utils/universal.py225
-rw-r--r--mesonbuild/utils/vsenv.py58
-rw-r--r--mesonbuild/utils/win32.py29
-rw-r--r--mesonbuild/wrap/wrap.py175
-rw-r--r--mesonbuild/wrap/wraptool.py42
-rwxr-xr-xpackaging/builddist.py32
-rwxr-xr-xpackaging/builddist.sh19
-rwxr-xr-xpackaging/mpackage.py17
-rwxr-xr-xrun_meson_command_tests.py31
-rwxr-xr-xrun_mypy.py25
-rwxr-xr-xrun_project_tests.py27
-rwxr-xr-xrun_shell_checks.py70
-rwxr-xr-xrun_tests.py11
-rwxr-xr-xrun_unittests.py67
-rw-r--r--test cases/cmake/13 system includes/main2.cpp5
-rw-r--r--test cases/cmake/13 system includes/meson.build4
-rw-r--r--test cases/cmake/2 advanced/meson.build18
-rw-r--r--test cases/common/104 has arg/meson.build19
-rw-r--r--test cases/common/109 custom target capture/meson.build10
-rw-r--r--test cases/common/109 custom target capture/test.json3
-rw-r--r--test cases/common/117 shared module/meson.build8
-rw-r--r--test cases/common/117 shared module/test.json5
-rw-r--r--test cases/common/14 configure file/CMakeLists.txt10
-rw-r--r--test cases/common/14 configure file/config7.h.in17
-rw-r--r--test cases/common/14 configure file/meson.build7
-rw-r--r--test cases/common/14 configure file/prog7.c12
-rw-r--r--test cases/common/14 configure file/test.json3
-rwxr-xr-xtest cases/common/182 find override/broken.py3
-rw-r--r--test cases/common/182 find override/meson.build10
-rw-r--r--test cases/common/188 dict/meson.build12
-rw-r--r--test cases/common/197 function attributes/meson.build29
-rw-r--r--test cases/common/22 object extraction/meson.build93
-rw-r--r--test cases/common/22 object extraction/subprojects/sub/meson.build4
-rw-r--r--test cases/common/22 object extraction/subprojects/sub/source.c5
-rw-r--r--test cases/common/220 fs module/meson.build17
-rw-r--r--test cases/common/247 deprecated option/test.json30
-rw-r--r--test cases/common/26 find program/meson.build20
-rw-r--r--test cases/common/280 pkgconfig-gen/meson.build5
-rw-r--r--test cases/common/281 subproj options/meson.build5
-rw-r--r--test cases/common/281 subproj options/subprojects/sub/meson.build4
-rw-r--r--test cases/common/281 subproj options/subprojects/sub/meson_options.txt1
-rw-r--r--test cases/common/281 subproj options/subprojects/sub2/meson.build2
-rw-r--r--test cases/common/281 subproj options/subprojects/sub2/meson_options.txt1
-rw-r--r--test cases/common/282 test args and depends in path/libs/a/lib_a.c5
-rw-r--r--test cases/common/282 test args and depends in path/libs/a/lib_a.def3
-rw-r--r--test cases/common/282 test args and depends in path/libs/a/meson.build5
-rw-r--r--test cases/common/282 test args and depends in path/libs/b/lib_b.c5
-rw-r--r--test cases/common/282 test args and depends in path/libs/b/lib_b.def3
-rw-r--r--test cases/common/282 test args and depends in path/libs/b/meson.build5
-rw-r--r--test cases/common/282 test args and depends in path/libs/meson.build2
-rw-r--r--test cases/common/282 test args and depends in path/meson.build19
-rw-r--r--test cases/common/282 test args and depends in path/test.c67
-rw-r--r--test cases/common/283 wrap override/meson.build8
-rw-r--r--test cases/common/283 wrap override/subprojects/sub/meson.build7
-rw-r--r--test cases/common/283 wrap override/subprojects/sub/subprojects/subsub.wrap5
-rw-r--r--test cases/common/283 wrap override/subprojects/subsub/meson.build5
-rw-r--r--test cases/common/284 pkgconfig subproject/meson.build13
-rw-r--r--test cases/common/284 pkgconfig subproject/simple.c6
-rw-r--r--test cases/common/284 pkgconfig subproject/simple.h6
-rw-r--r--test cases/common/284 pkgconfig subproject/subprojects/simple2/exports.def2
-rw-r--r--test cases/common/284 pkgconfig subproject/subprojects/simple2/meson.build9
-rw-r--r--test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.c5
-rw-r--r--test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.h6
-rw-r--r--test cases/common/284 pkgconfig subproject/test.json15
-rw-r--r--test cases/common/285 atomic/a.c (renamed from test cases/failing/76 override exe config/foo.c)0
-rw-r--r--test cases/common/285 atomic/meson.build23
-rw-r--r--test cases/common/286 importstd/meson.build26
-rw-r--r--test cases/common/286 importstd/useistd.cpp6
-rw-r--r--test cases/common/32 has header/meson.build11
-rw-r--r--test cases/common/35 string operations/meson.build6
-rw-r--r--test cases/common/40 options/meson.build3
-rw-r--r--test cases/common/42 subproject/meson.build4
-rw-r--r--test cases/common/42 subproject/meson.options1
-rw-r--r--test cases/common/42 subproject/subprojects/subunused/meson.build1
-rw-r--r--test cases/common/42 subproject/subprojects/subunused/meson.options1
-rw-r--r--test cases/common/42 subproject/test.json9
-rw-r--r--test cases/common/43 subproject options/meson.build2
-rw-r--r--test cases/common/43 subproject options/subprojects/subproject/meson.build9
-rw-r--r--test cases/common/44 pkgconfig-gen/meson.build8
-rw-r--r--test cases/common/44 pkgconfig-gen/test.json1
-rw-r--r--test cases/common/56 array methods/meson.build19
-rw-r--r--test cases/common/98 subproject subdir/meson.build2
-rw-r--r--test cases/common/98 subproject subdir/subprojects/sub_static/meson.build1
-rw-r--r--test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt1
-rw-r--r--test cases/csharp/4 external dep/meson.build6
-rw-r--r--test cases/cuda/10 cuda dependency/modules/meson.build2
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build2
-rw-r--r--test cases/cuda/12 cuda dependency (mixed)/meson.build2
-rw-r--r--test cases/cython/2 generated sources/meson.build9
-rw-r--r--test cases/failing/100 compiler no lang/test.json7
-rw-r--r--test cases/failing/100 no fallback/meson.build (renamed from test cases/failing/101 no fallback/meson.build)0
-rw-r--r--test cases/failing/100 no fallback/subprojects/foob/meson.build (renamed from test cases/failing/101 no fallback/subprojects/foob/meson.build)0
-rw-r--r--test cases/failing/100 no fallback/test.json (renamed from test cases/failing/101 no fallback/test.json)0
-rw-r--r--test cases/failing/101 feature require/meson.build (renamed from test cases/failing/102 feature require/meson.build)0
-rw-r--r--test cases/failing/101 feature require/meson_options.txt (renamed from test cases/failing/102 feature require/meson_options.txt)0
-rw-r--r--test cases/failing/101 feature require/test.json (renamed from test cases/failing/102 feature require/test.json)0
-rw-r--r--test cases/failing/102 feature require.bis/meson.build (renamed from test cases/failing/103 feature require.bis/meson.build)0
-rw-r--r--test cases/failing/102 feature require.bis/meson_options.txt (renamed from test cases/failing/103 feature require.bis/meson_options.txt)0
-rw-r--r--test cases/failing/102 feature require.bis/test.json (renamed from test cases/failing/103 feature require.bis/test.json)0
-rw-r--r--test cases/failing/103 no build get_external_property/meson.build (renamed from test cases/failing/104 no build get_external_property/meson.build)0
-rw-r--r--test cases/failing/103 no build get_external_property/test.json (renamed from test cases/failing/104 no build get_external_property/test.json)2
-rw-r--r--test cases/failing/104 enter subdir twice/meson.build (renamed from test cases/failing/105 enter subdir twice/meson.build)0
-rw-r--r--test cases/failing/104 enter subdir twice/sub/meson.build (renamed from test cases/failing/105 enter subdir twice/sub/meson.build)0
-rw-r--r--test cases/failing/104 enter subdir twice/test.json (renamed from test cases/failing/105 enter subdir twice/test.json)2
-rw-r--r--test cases/failing/105 invalid fstring/109 invalid fstring/meson.build (renamed from test cases/failing/106 invalid fstring/109 invalid fstring/meson.build)0
-rw-r--r--test cases/failing/105 invalid fstring/109 invalid fstring/test.json (renamed from test cases/failing/106 invalid fstring/109 invalid fstring/test.json)0
-rw-r--r--test cases/failing/105 invalid fstring/meson.build (renamed from test cases/failing/106 invalid fstring/meson.build)0
-rw-r--r--test cases/failing/105 invalid fstring/test.json (renamed from test cases/failing/106 invalid fstring/test.json)2
-rw-r--r--test cases/failing/106 compiler argument checking/meson.build (renamed from test cases/failing/107 compiler argument checking/meson.build)0
-rw-r--r--test cases/failing/106 compiler argument checking/test.json (renamed from test cases/failing/107 compiler argument checking/test.json)2
-rw-r--r--test cases/failing/107 empty fallback/meson.build (renamed from test cases/failing/108 empty fallback/meson.build)0
-rw-r--r--test cases/failing/107 empty fallback/subprojects/foo/meson.build (renamed from test cases/failing/108 empty fallback/subprojects/foo/meson.build)0
-rw-r--r--test cases/failing/107 empty fallback/test.json (renamed from test cases/failing/108 empty fallback/test.json)2
-rw-r--r--test cases/failing/108 cmake executable dependency/meson.build (renamed from test cases/failing/109 cmake executable dependency/meson.build)0
-rw-r--r--test cases/failing/108 cmake executable dependency/subprojects/cmlib/CMakeLists.txt (renamed from test cases/failing/109 cmake executable dependency/subprojects/cmlib/CMakeLists.txt)0
-rw-r--r--test cases/failing/108 cmake executable dependency/subprojects/cmlib/main.c (renamed from test cases/failing/109 cmake executable dependency/subprojects/cmlib/main.c)0
-rw-r--r--test cases/failing/108 cmake executable dependency/test.json (renamed from test cases/failing/109 cmake executable dependency/test.json)2
-rw-r--r--test cases/failing/109 allow_fallback with fallback/meson.build (renamed from test cases/failing/110 allow_fallback with fallback/meson.build)0
-rw-r--r--test cases/failing/109 allow_fallback with fallback/test.json (renamed from test cases/failing/110 allow_fallback with fallback/test.json)2
-rw-r--r--test cases/failing/110 nonsensical bindgen/meson.build (renamed from test cases/failing/111 nonsensical bindgen/meson.build)2
-rw-r--r--test cases/failing/110 nonsensical bindgen/src/header.h (renamed from test cases/failing/111 nonsensical bindgen/src/header.h)2
-rw-r--r--test cases/failing/110 nonsensical bindgen/src/source.c (renamed from test cases/failing/111 nonsensical bindgen/src/source.c)2
-rw-r--r--test cases/failing/110 nonsensical bindgen/test.json (renamed from test cases/failing/111 nonsensical bindgen/test.json)2
-rw-r--r--test cases/failing/111 run_target in test/meson.build (renamed from test cases/failing/112 run_target in test/meson.build)0
-rw-r--r--test cases/failing/111 run_target in test/test.json (renamed from test cases/failing/112 run_target in test/test.json)2
-rw-r--r--test cases/failing/111 run_target in test/trivial.c (renamed from test cases/failing/112 run_target in test/trivial.c)0
-rw-r--r--test cases/failing/112 run_target in add_install_script/meson.build (renamed from test cases/failing/113 run_target in add_install_script/meson.build)0
-rw-r--r--test cases/failing/112 run_target in add_install_script/test.json (renamed from test cases/failing/113 run_target in add_install_script/test.json)2
-rw-r--r--test cases/failing/112 run_target in add_install_script/trivial.c (renamed from test cases/failing/113 run_target in add_install_script/trivial.c)0
-rw-r--r--test cases/failing/113 pathsep in install_symlink/meson.build (renamed from test cases/failing/114 pathsep in install_symlink/meson.build)0
-rw-r--r--test cases/failing/113 pathsep in install_symlink/test.json (renamed from test cases/failing/114 pathsep in install_symlink/test.json)2
-rw-r--r--test cases/failing/114 subproject version conflict/meson.build (renamed from test cases/failing/115 subproject version conflict/meson.build)0
-rw-r--r--test cases/failing/114 subproject version conflict/subprojects/A/meson.build (renamed from test cases/failing/115 subproject version conflict/subprojects/A/meson.build)0
-rw-r--r--test cases/failing/114 subproject version conflict/subprojects/B/meson.build (renamed from test cases/failing/115 subproject version conflict/subprojects/B/meson.build)0
-rw-r--r--test cases/failing/114 subproject version conflict/test.json (renamed from test cases/failing/115 subproject version conflict/test.json)2
-rw-r--r--test cases/failing/115 structured source empty string/main.rs (renamed from test cases/failing/116 structured source empty string/main.rs)0
-rw-r--r--test cases/failing/115 structured source empty string/meson.build (renamed from test cases/failing/116 structured source empty string/meson.build)0
-rw-r--r--test cases/failing/115 structured source empty string/test.json (renamed from test cases/failing/116 structured source empty string/test.json)2
-rw-r--r--test cases/failing/116 structured_sources conflicts/main.rs (renamed from test cases/failing/117 structured_sources conflicts/main.rs)0
-rw-r--r--test cases/failing/116 structured_sources conflicts/meson.build (renamed from test cases/failing/117 structured_sources conflicts/meson.build)0
-rw-r--r--test cases/failing/116 structured_sources conflicts/test.json (renamed from test cases/failing/117 structured_sources conflicts/test.json)2
-rw-r--r--test cases/failing/117 missing compiler/meson.build (renamed from test cases/failing/118 missing compiler/meson.build)0
-rw-r--r--test cases/failing/117 missing compiler/subprojects/sub/main.c (renamed from test cases/failing/118 missing compiler/subprojects/sub/main.c)0
-rw-r--r--test cases/failing/117 missing compiler/subprojects/sub/meson.build (renamed from test cases/failing/118 missing compiler/subprojects/sub/meson.build)0
-rw-r--r--test cases/failing/117 missing compiler/test.json (renamed from test cases/failing/118 missing compiler/test.json)2
-rw-r--r--test cases/failing/118 cmake subproject error/meson.build (renamed from test cases/failing/119 cmake subproject error/meson.build)0
-rw-r--r--test cases/failing/118 cmake subproject error/subprojects/cmlib/CMakeLists.txt (renamed from test cases/failing/119 cmake subproject error/subprojects/cmlib/CMakeLists.txt)0
-rw-r--r--test cases/failing/118 cmake subproject error/test.json (renamed from test cases/failing/119 cmake subproject error/test.json)2
-rw-r--r--test cases/failing/119 pkgconfig not relocatable outside prefix/meson.build (renamed from test cases/failing/120 pkgconfig not relocatable outside prefix/meson.build)0
-rw-r--r--test cases/failing/119 pkgconfig not relocatable outside prefix/test.json (renamed from test cases/failing/120 pkgconfig not relocatable outside prefix/test.json)2
-rw-r--r--test cases/failing/120 subproject sandbox violation/meson.build (renamed from test cases/failing/121 subproject sandbox violation/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/meson_options.txt (renamed from test cases/failing/121 subproject sandbox violation/meson_options.txt)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj1/file.txt (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj1/file.txt)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj1/meson.build (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj1/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj1/nested/meson.build (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj1/nested/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj2/file.txt (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj2/file.txt)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj2/meson.build (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj2/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj2/nested/meson.build (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj2/nested/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj3/file.txt (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj3/file.txt)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/subprojects/subproj3/meson.build (renamed from test cases/failing/121 subproject sandbox violation/subprojects/subproj3/meson.build)0
-rw-r--r--test cases/failing/120 subproject sandbox violation/test.json (renamed from test cases/failing/121 subproject sandbox violation/test.json)2
-rw-r--r--test cases/failing/121 override and add_project_dependency/inc/lib.h (renamed from test cases/failing/122 override and add_project_dependency/inc/lib.h)0
-rw-r--r--test cases/failing/121 override and add_project_dependency/lib.c (renamed from test cases/failing/122 override and add_project_dependency/lib.c)0
-rw-r--r--test cases/failing/121 override and add_project_dependency/meson.build (renamed from test cases/failing/122 override and add_project_dependency/meson.build)0
-rw-r--r--test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build (renamed from test cases/failing/122 override and add_project_dependency/subprojects/a/meson.build)0
-rw-r--r--test cases/failing/121 override and add_project_dependency/subprojects/a/prog.c (renamed from test cases/failing/122 override and add_project_dependency/subprojects/a/prog.c)0
-rw-r--r--test cases/failing/121 override and add_project_dependency/test.json (renamed from test cases/failing/122 override and add_project_dependency/test.json)2
-rw-r--r--test cases/failing/122 targets before add_project_dependency/inc/lib.h (renamed from test cases/failing/123 targets before add_project_dependency/inc/lib.h)0
-rw-r--r--test cases/failing/122 targets before add_project_dependency/lib.c (renamed from test cases/failing/123 targets before add_project_dependency/lib.c)0
-rw-r--r--test cases/failing/122 targets before add_project_dependency/meson.build (renamed from test cases/failing/123 targets before add_project_dependency/meson.build)0
-rw-r--r--test cases/failing/122 targets before add_project_dependency/test.json (renamed from test cases/failing/123 targets before add_project_dependency/test.json)2
-rw-r--r--test cases/failing/123 extract from unity/meson.build (renamed from test cases/failing/124 extract from unity/meson.build)0
-rw-r--r--test cases/failing/123 extract from unity/src1.c (renamed from test cases/failing/124 extract from unity/src1.c)0
-rw-r--r--test cases/failing/123 extract from unity/src2.c (renamed from test cases/failing/124 extract from unity/src2.c)0
-rw-r--r--test cases/failing/123 extract from unity/test.json (renamed from test cases/failing/124 extract from unity/test.json)2
-rw-r--r--test cases/failing/124 subproject object as a dependency/main.c (renamed from test cases/failing/125 subproject object as a dependency/main.c)0
-rw-r--r--test cases/failing/124 subproject object as a dependency/meson.build (renamed from test cases/failing/125 subproject object as a dependency/meson.build)0
-rw-r--r--test cases/failing/124 subproject object as a dependency/subprojects/sub/meson.build (renamed from test cases/failing/125 subproject object as a dependency/subprojects/sub/meson.build)0
-rw-r--r--test cases/failing/124 subproject object as a dependency/test.json (renamed from test cases/failing/125 subproject object as a dependency/test.json)2
-rw-r--r--test cases/failing/125 generator host binary/exe.c (renamed from test cases/failing/126 generator host binary/exe.c)0
-rw-r--r--test cases/failing/125 generator host binary/lib.in (renamed from test cases/failing/126 generator host binary/lib.in)0
-rw-r--r--test cases/failing/125 generator host binary/meson.build (renamed from test cases/failing/126 generator host binary/meson.build)0
-rw-r--r--test cases/failing/125 generator host binary/test.json (renamed from test cases/failing/126 generator host binary/test.json)0
-rw-r--r--test cases/failing/126 invalid ast/meson.build (renamed from test cases/failing/127 invalid ast/meson.build)0
-rw-r--r--test cases/failing/126 invalid ast/test.json (renamed from test cases/failing/127 invalid ast/test.json)2
-rw-r--r--test cases/failing/127 invalid project function/meson.build (renamed from test cases/failing/128 invalid project function/meson.build)0
-rw-r--r--test cases/failing/127 invalid project function/test.json (renamed from test cases/failing/128 invalid project function/test.json)2
-rw-r--r--test cases/failing/128 utf8 with bom/meson.build (renamed from test cases/failing/129 utf8 with bom/meson.build)0
-rw-r--r--test cases/failing/128 utf8 with bom/test.json (renamed from test cases/failing/129 utf8 with bom/test.json)2
-rw-r--r--test cases/failing/129 utf8 with bom subdir/meson.build (renamed from test cases/failing/130 utf8 with bom subdir/meson.build)0
-rw-r--r--test cases/failing/129 utf8 with bom subdir/subdir/meson.build (renamed from test cases/failing/130 utf8 with bom subdir/subdir/meson.build)0
-rw-r--r--test cases/failing/129 utf8 with bom subdir/test.json (renamed from test cases/failing/130 utf8 with bom subdir/test.json)2
-rw-r--r--test cases/failing/130 utf8 with bom options/meson.build (renamed from test cases/failing/131 utf8 with bom options/meson.build)0
-rw-r--r--test cases/failing/130 utf8 with bom options/meson.options (renamed from test cases/failing/131 utf8 with bom options/meson.options)0
-rw-r--r--test cases/failing/130 utf8 with bom options/test.json (renamed from test cases/failing/131 utf8 with bom options/test.json)2
-rw-r--r--test cases/failing/131 module use inside project decl/meson.build (renamed from test cases/failing/132 module use inside project decl/meson.build)0
-rw-r--r--test cases/failing/131 module use inside project decl/test.json (renamed from test cases/failing/132 module use inside project decl/test.json)2
-rw-r--r--test cases/failing/132 dub missing dependency/dub.json (renamed from test cases/failing/133 dub missing dependency/dub.json)0
-rw-r--r--test cases/failing/132 dub missing dependency/dub.selections.json (renamed from test cases/failing/133 dub missing dependency/dub.selections.json)0
-rw-r--r--test cases/failing/132 dub missing dependency/meson.build (renamed from test cases/failing/133 dub missing dependency/meson.build)0
-rw-r--r--test cases/failing/132 dub missing dependency/source/app.d (renamed from test cases/failing/133 dub missing dependency/source/app.d)0
-rw-r--r--test cases/failing/132 dub missing dependency/test.json (renamed from test cases/failing/133 dub missing dependency/test.json)0
-rw-r--r--test cases/failing/133 java sources in non jar target/Test.java (renamed from test cases/failing/134 java sources in non jar target/Test.java)0
-rw-r--r--test cases/failing/133 java sources in non jar target/meson.build (renamed from test cases/failing/134 java sources in non jar target/meson.build)0
-rw-r--r--test cases/failing/133 java sources in non jar target/test.json (renamed from test cases/failing/134 java sources in non jar target/test.json)2
-rw-r--r--test cases/failing/134 rust link_language/f.rs1
-rw-r--r--test cases/failing/134 rust link_language/meson.build10
-rw-r--r--test cases/failing/134 rust link_language/test.json8
-rw-r--r--test cases/failing/135 invalid build_subdir/existing-dir/config.h.in5
-rw-r--r--test cases/failing/135 invalid build_subdir/meson.build24
-rw-r--r--test cases/failing/135 invalid build_subdir/test.json7
-rw-r--r--test cases/failing/16 extract from subproject/main.c5
-rw-r--r--test cases/failing/16 extract from subproject/meson.build9
-rw-r--r--test cases/failing/16 extract from subproject/subprojects/sub_project/meson.build3
-rw-r--r--test cases/failing/16 extract from subproject/subprojects/sub_project/sub_lib.c3
-rw-r--r--test cases/failing/16 extract from subproject/test.json7
-rw-r--r--test cases/failing/16 same target/file.c (renamed from test cases/failing/17 same target/file.c)0
-rw-r--r--test cases/failing/16 same target/meson.build (renamed from test cases/failing/17 same target/meson.build)0
-rw-r--r--test cases/failing/16 same target/test.json (renamed from test cases/failing/17 same target/test.json)2
-rw-r--r--test cases/failing/17 wrong plusassign/meson.build (renamed from test cases/failing/18 wrong plusassign/meson.build)0
-rw-r--r--test cases/failing/17 wrong plusassign/test.json (renamed from test cases/failing/18 wrong plusassign/test.json)2
-rw-r--r--test cases/failing/18 target clash/clash.c (renamed from test cases/failing/19 target clash/clash.c)0
-rw-r--r--test cases/failing/18 target clash/meson.build (renamed from test cases/failing/19 target clash/meson.build)0
-rw-r--r--test cases/failing/18 target clash/test.json (renamed from test cases/failing/19 target clash/test.json)0
-rw-r--r--test cases/failing/19 version/meson.build (renamed from test cases/failing/20 version/meson.build)0
-rw-r--r--test cases/failing/19 version/test.json (renamed from test cases/failing/20 version/test.json)2
-rw-r--r--test cases/failing/20 subver/meson.build (renamed from test cases/failing/21 subver/meson.build)0
-rw-r--r--test cases/failing/20 subver/subprojects/foo/meson.build (renamed from test cases/failing/21 subver/subprojects/foo/meson.build)0
-rw-r--r--test cases/failing/20 subver/test.json (renamed from test cases/failing/21 subver/test.json)2
-rw-r--r--test cases/failing/21 assert/meson.build (renamed from test cases/failing/22 assert/meson.build)0
-rw-r--r--test cases/failing/21 assert/test.json (renamed from test cases/failing/22 assert/test.json)2
-rw-r--r--test cases/failing/22 rel testdir/meson.build (renamed from test cases/failing/23 rel testdir/meson.build)0
-rw-r--r--test cases/failing/22 rel testdir/simple.c (renamed from test cases/failing/23 rel testdir/simple.c)0
-rw-r--r--test cases/failing/22 rel testdir/test.json (renamed from test cases/failing/23 rel testdir/test.json)2
-rw-r--r--test cases/failing/23 int conversion/meson.build (renamed from test cases/failing/24 int conversion/meson.build)0
-rw-r--r--test cases/failing/23 int conversion/test.json (renamed from test cases/failing/24 int conversion/test.json)2
-rw-r--r--test cases/failing/24 badlang/meson.build (renamed from test cases/failing/25 badlang/meson.build)0
-rw-r--r--test cases/failing/24 badlang/test.json (renamed from test cases/failing/25 badlang/test.json)2
-rw-r--r--test cases/failing/25 output subdir/foo.in (renamed from test cases/failing/26 output subdir/foo.in)0
-rw-r--r--test cases/failing/25 output subdir/meson.build (renamed from test cases/failing/26 output subdir/meson.build)0
-rw-r--r--test cases/failing/25 output subdir/subdir/dummy.txt (renamed from test cases/failing/26 output subdir/subdir/dummy.txt)0
-rw-r--r--test cases/failing/25 output subdir/test.json (renamed from test cases/failing/26 output subdir/test.json)2
-rw-r--r--test cases/failing/26 noprog use/meson.build (renamed from test cases/failing/27 noprog use/meson.build)0
-rw-r--r--test cases/failing/26 noprog use/test.json (renamed from test cases/failing/27 noprog use/test.json)2
-rw-r--r--test cases/failing/27 no crossprop/meson.build (renamed from test cases/failing/28 no crossprop/meson.build)0
-rw-r--r--test cases/failing/27 no crossprop/test.json (renamed from test cases/failing/28 no crossprop/test.json)2
-rw-r--r--test cases/failing/28 nested ternary/meson.build (renamed from test cases/failing/29 nested ternary/meson.build)0
-rw-r--r--test cases/failing/28 nested ternary/test.json (renamed from test cases/failing/29 nested ternary/test.json)2
-rw-r--r--test cases/failing/29 invalid man extension/foo.a1 (renamed from test cases/failing/30 invalid man extension/foo.a1)0
-rw-r--r--test cases/failing/29 invalid man extension/meson.build (renamed from test cases/failing/30 invalid man extension/meson.build)0
-rw-r--r--test cases/failing/29 invalid man extension/test.json (renamed from test cases/failing/30 invalid man extension/test.json)2
-rw-r--r--test cases/failing/30 no man extension/foo (renamed from test cases/failing/31 no man extension/foo)0
-rw-r--r--test cases/failing/30 no man extension/meson.build (renamed from test cases/failing/31 no man extension/meson.build)0
-rw-r--r--test cases/failing/30 no man extension/test.json (renamed from test cases/failing/31 no man extension/test.json)2
-rw-r--r--test cases/failing/31 exe static shared/meson.build (renamed from test cases/failing/32 exe static shared/meson.build)0
-rw-r--r--test cases/failing/31 exe static shared/prog.c (renamed from test cases/failing/32 exe static shared/prog.c)0
-rw-r--r--test cases/failing/31 exe static shared/shlib2.c (renamed from test cases/failing/32 exe static shared/shlib2.c)0
-rw-r--r--test cases/failing/31 exe static shared/stat.c (renamed from test cases/failing/32 exe static shared/stat.c)0
-rw-r--r--test cases/failing/31 exe static shared/test.json (renamed from test cases/failing/32 exe static shared/test.json)2
-rw-r--r--test cases/failing/32 non-root subproject/meson.build (renamed from test cases/failing/33 non-root subproject/meson.build)0
-rw-r--r--test cases/failing/32 non-root subproject/some/meson.build (renamed from test cases/failing/33 non-root subproject/some/meson.build)0
-rw-r--r--test cases/failing/32 non-root subproject/test.json7
-rw-r--r--test cases/failing/33 dependency not-required then required/meson.build (renamed from test cases/failing/34 dependency not-required then required/meson.build)0
-rw-r--r--test cases/failing/33 dependency not-required then required/test.json (renamed from test cases/failing/34 dependency not-required then required/test.json)0
-rw-r--r--test cases/failing/33 non-root subproject/test.json7
-rw-r--r--test cases/failing/34 project argument after target/exe.c (renamed from test cases/failing/35 project argument after target/exe.c)0
-rw-r--r--test cases/failing/34 project argument after target/meson.build (renamed from test cases/failing/35 project argument after target/meson.build)0
-rw-r--r--test cases/failing/34 project argument after target/test.json (renamed from test cases/failing/35 project argument after target/test.json)2
-rw-r--r--test cases/failing/35 pkgconfig dependency impossible conditions/meson.build (renamed from test cases/failing/36 pkgconfig dependency impossible conditions/meson.build)0
-rw-r--r--test cases/failing/35 pkgconfig dependency impossible conditions/test.json (renamed from test cases/failing/36 pkgconfig dependency impossible conditions/test.json)2
-rw-r--r--test cases/failing/36 has function external dependency/meson.build (renamed from test cases/failing/37 has function external dependency/meson.build)0
-rw-r--r--test cases/failing/36 has function external dependency/mylib.c (renamed from test cases/failing/37 has function external dependency/mylib.c)0
-rw-r--r--test cases/failing/36 has function external dependency/test.json (renamed from test cases/failing/37 has function external dependency/test.json)2
-rw-r--r--test cases/failing/37 prefix absolute/meson.build (renamed from test cases/failing/38 prefix absolute/meson.build)0
-rw-r--r--test cases/failing/37 prefix absolute/test.json (renamed from test cases/failing/38 prefix absolute/test.json)2
-rw-r--r--test cases/failing/38 kwarg assign/dummy.c (renamed from test cases/failing/39 kwarg assign/dummy.c)0
-rw-r--r--test cases/failing/38 kwarg assign/meson.build (renamed from test cases/failing/39 kwarg assign/meson.build)0
-rw-r--r--test cases/failing/38 kwarg assign/prog.c (renamed from test cases/failing/39 kwarg assign/prog.c)0
-rw-r--r--test cases/failing/38 kwarg assign/test.json (renamed from test cases/failing/39 kwarg assign/test.json)2
-rw-r--r--test cases/failing/39 custom target plainname many inputs/1.txt (renamed from test cases/failing/40 custom target plainname many inputs/1.txt)0
-rw-r--r--test cases/failing/39 custom target plainname many inputs/2.txt (renamed from test cases/failing/40 custom target plainname many inputs/2.txt)0
-rw-r--r--test cases/failing/39 custom target plainname many inputs/catfiles.py (renamed from test cases/failing/40 custom target plainname many inputs/catfiles.py)0
-rw-r--r--test cases/failing/39 custom target plainname many inputs/meson.build (renamed from test cases/failing/40 custom target plainname many inputs/meson.build)0
-rw-r--r--test cases/failing/39 custom target plainname many inputs/test.json (renamed from test cases/failing/40 custom target plainname many inputs/test.json)2
-rwxr-xr-xtest cases/failing/40 custom target outputs not matching install_dirs/generator.py (renamed from test cases/failing/41 custom target outputs not matching install_dirs/generator.py)0
-rw-r--r--test cases/failing/40 custom target outputs not matching install_dirs/meson.build (renamed from test cases/failing/41 custom target outputs not matching install_dirs/meson.build)0
-rw-r--r--test cases/failing/40 custom target outputs not matching install_dirs/test.json (renamed from test cases/failing/41 custom target outputs not matching install_dirs/test.json)0
-rw-r--r--test cases/failing/41 project name colon/meson.build (renamed from test cases/failing/42 project name colon/meson.build)0
-rw-r--r--test cases/failing/41 project name colon/test.json (renamed from test cases/failing/42 project name colon/test.json)2
-rw-r--r--test cases/failing/42 abs subdir/bob/meson.build (renamed from test cases/failing/43 abs subdir/bob/meson.build)0
-rw-r--r--test cases/failing/42 abs subdir/meson.build (renamed from test cases/failing/43 abs subdir/meson.build)0
-rw-r--r--test cases/failing/42 abs subdir/test.json (renamed from test cases/failing/43 abs subdir/test.json)2
-rw-r--r--test cases/failing/43 abspath to srcdir/meson.build (renamed from test cases/failing/44 abspath to srcdir/meson.build)0
-rw-r--r--test cases/failing/43 abspath to srcdir/test.json (renamed from test cases/failing/44 abspath to srcdir/test.json)2
-rw-r--r--test cases/failing/44 pkgconfig variables reserved/meson.build (renamed from test cases/failing/45 pkgconfig variables reserved/meson.build)0
-rw-r--r--test cases/failing/44 pkgconfig variables reserved/simple.c (renamed from test cases/failing/45 pkgconfig variables reserved/simple.c)0
-rw-r--r--test cases/failing/44 pkgconfig variables reserved/simple.h (renamed from test cases/failing/45 pkgconfig variables reserved/simple.h)0
-rw-r--r--test cases/failing/44 pkgconfig variables reserved/test.json (renamed from test cases/failing/45 pkgconfig variables reserved/test.json)2
-rw-r--r--test cases/failing/45 pkgconfig variables zero length/meson.build (renamed from test cases/failing/46 pkgconfig variables zero length/meson.build)0
-rw-r--r--test cases/failing/45 pkgconfig variables zero length/simple.c (renamed from test cases/failing/46 pkgconfig variables zero length/simple.c)0
-rw-r--r--test cases/failing/45 pkgconfig variables zero length/simple.h (renamed from test cases/failing/46 pkgconfig variables zero length/simple.h)0
-rw-r--r--test cases/failing/45 pkgconfig variables zero length/test.json (renamed from test cases/failing/46 pkgconfig variables zero length/test.json)2
-rw-r--r--test cases/failing/46 pkgconfig variables not key value/meson.build (renamed from test cases/failing/47 pkgconfig variables not key value/meson.build)0
-rw-r--r--test cases/failing/46 pkgconfig variables not key value/simple.c (renamed from test cases/failing/47 pkgconfig variables not key value/simple.c)0
-rw-r--r--test cases/failing/46 pkgconfig variables not key value/simple.h (renamed from test cases/failing/47 pkgconfig variables not key value/simple.h)0
-rw-r--r--test cases/failing/46 pkgconfig variables not key value/test.json (renamed from test cases/failing/47 pkgconfig variables not key value/test.json)2
-rw-r--r--test cases/failing/47 executable comparison/meson.build (renamed from test cases/failing/48 executable comparison/meson.build)0
-rw-r--r--test cases/failing/47 executable comparison/prog.c (renamed from test cases/failing/48 executable comparison/prog.c)0
-rw-r--r--test cases/failing/47 executable comparison/test.json (renamed from test cases/failing/48 executable comparison/test.json)2
-rw-r--r--test cases/failing/48 inconsistent comparison/meson.build (renamed from test cases/failing/49 inconsistent comparison/meson.build)0
-rw-r--r--test cases/failing/48 inconsistent comparison/test.json (renamed from test cases/failing/49 inconsistent comparison/test.json)2
-rw-r--r--test cases/failing/49 slashname/meson.build (renamed from test cases/failing/50 slashname/meson.build)0
-rw-r--r--test cases/failing/49 slashname/sub/meson.build (renamed from test cases/failing/50 slashname/sub/meson.build)0
-rw-r--r--test cases/failing/49 slashname/sub/prog.c (renamed from test cases/failing/50 slashname/sub/prog.c)0
-rw-r--r--test cases/failing/49 slashname/test.json (renamed from test cases/failing/50 slashname/test.json)2
-rw-r--r--test cases/failing/50 reserved meson prefix/meson-foo/meson.build (renamed from test cases/failing/51 reserved meson prefix/meson-foo/meson.build)0
-rw-r--r--test cases/failing/50 reserved meson prefix/meson.build (renamed from test cases/failing/51 reserved meson prefix/meson.build)0
-rw-r--r--test cases/failing/50 reserved meson prefix/test.json (renamed from test cases/failing/51 reserved meson prefix/test.json)2
-rw-r--r--test cases/failing/51 or on new line/meson.build (renamed from test cases/failing/52 or on new line/meson.build)0
-rw-r--r--test cases/failing/51 or on new line/meson_options.txt (renamed from test cases/failing/52 or on new line/meson_options.txt)0
-rw-r--r--test cases/failing/51 or on new line/test.json (renamed from test cases/failing/52 or on new line/test.json)2
-rw-r--r--test cases/failing/52 link with executable/meson.build (renamed from test cases/failing/53 link with executable/meson.build)0
-rw-r--r--test cases/failing/52 link with executable/module.c (renamed from test cases/failing/53 link with executable/module.c)0
-rw-r--r--test cases/failing/52 link with executable/prog.c (renamed from test cases/failing/53 link with executable/prog.c)0
-rw-r--r--test cases/failing/52 link with executable/test.json (renamed from test cases/failing/53 link with executable/test.json)2
-rw-r--r--test cases/failing/53 assign custom target index/meson.build (renamed from test cases/failing/54 assign custom target index/meson.build)0
-rw-r--r--test cases/failing/53 assign custom target index/test.json (renamed from test cases/failing/54 assign custom target index/test.json)2
-rw-r--r--test cases/failing/54 getoption prefix/meson.build (renamed from test cases/failing/55 getoption prefix/meson.build)0
-rw-r--r--test cases/failing/54 getoption prefix/subprojects/abc/meson.build (renamed from test cases/failing/55 getoption prefix/subprojects/abc/meson.build)0
-rw-r--r--test cases/failing/54 getoption prefix/subprojects/abc/meson_options.txt (renamed from test cases/failing/55 getoption prefix/subprojects/abc/meson_options.txt)0
-rw-r--r--test cases/failing/54 getoption prefix/test.json (renamed from test cases/failing/55 getoption prefix/test.json)2
-rw-r--r--test cases/failing/55 bad option argument/meson.build (renamed from test cases/failing/56 bad option argument/meson.build)0
-rw-r--r--test cases/failing/55 bad option argument/meson_options.txt (renamed from test cases/failing/56 bad option argument/meson_options.txt)0
-rw-r--r--test cases/failing/55 bad option argument/test.json (renamed from test cases/failing/56 bad option argument/test.json)2
-rw-r--r--test cases/failing/56 subproj filegrab/meson.build (renamed from test cases/failing/57 subproj filegrab/meson.build)0
-rw-r--r--test cases/failing/56 subproj filegrab/prog.c (renamed from test cases/failing/57 subproj filegrab/prog.c)0
-rw-r--r--test cases/failing/56 subproj filegrab/subprojects/a/meson.build (renamed from test cases/failing/57 subproj filegrab/subprojects/a/meson.build)0
-rw-r--r--test cases/failing/56 subproj filegrab/test.json (renamed from test cases/failing/57 subproj filegrab/test.json)2
-rw-r--r--test cases/failing/57 grab subproj/meson.build (renamed from test cases/failing/58 grab subproj/meson.build)0
-rw-r--r--test cases/failing/57 grab subproj/subprojects/foo/meson.build (renamed from test cases/failing/58 grab subproj/subprojects/foo/meson.build)0
-rw-r--r--test cases/failing/57 grab subproj/subprojects/foo/sub.c (renamed from test cases/failing/58 grab subproj/subprojects/foo/sub.c)0
-rw-r--r--test cases/failing/57 grab subproj/test.json (renamed from test cases/failing/58 grab subproj/test.json)2
-rw-r--r--test cases/failing/58 grab sibling/meson.build (renamed from test cases/failing/59 grab sibling/meson.build)0
-rw-r--r--test cases/failing/58 grab sibling/subprojects/a/meson.build (renamed from test cases/failing/59 grab sibling/subprojects/a/meson.build)0
-rw-r--r--test cases/failing/58 grab sibling/subprojects/b/meson.build (renamed from test cases/failing/59 grab sibling/subprojects/b/meson.build)0
-rw-r--r--test cases/failing/58 grab sibling/subprojects/b/sneaky.c (renamed from test cases/failing/59 grab sibling/subprojects/b/sneaky.c)0
-rw-r--r--test cases/failing/58 grab sibling/test.json (renamed from test cases/failing/59 grab sibling/test.json)2
-rw-r--r--test cases/failing/59 string as link target/meson.build (renamed from test cases/failing/60 string as link target/meson.build)0
-rw-r--r--test cases/failing/59 string as link target/prog.c (renamed from test cases/failing/60 string as link target/prog.c)0
-rw-r--r--test cases/failing/59 string as link target/test.json (renamed from test cases/failing/60 string as link target/test.json)2
-rw-r--r--test cases/failing/60 dependency not-found and required/meson.build (renamed from test cases/failing/61 dependency not-found and required/meson.build)0
-rw-r--r--test cases/failing/60 dependency not-found and required/test.json (renamed from test cases/failing/61 dependency not-found and required/test.json)2
-rw-r--r--test cases/failing/61 subproj different versions/main.c (renamed from test cases/failing/62 subproj different versions/main.c)0
-rw-r--r--test cases/failing/61 subproj different versions/meson.build (renamed from test cases/failing/62 subproj different versions/meson.build)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/a/a.c (renamed from test cases/failing/62 subproj different versions/subprojects/a/a.c)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/a/a.h (renamed from test cases/failing/62 subproj different versions/subprojects/a/a.h)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/a/meson.build (renamed from test cases/failing/62 subproj different versions/subprojects/a/meson.build)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/b/b.c (renamed from test cases/failing/62 subproj different versions/subprojects/b/b.c)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/b/b.h (renamed from test cases/failing/62 subproj different versions/subprojects/b/b.h)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/b/meson.build (renamed from test cases/failing/62 subproj different versions/subprojects/b/meson.build)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/c/c.h (renamed from test cases/failing/62 subproj different versions/subprojects/c/c.h)0
-rw-r--r--test cases/failing/61 subproj different versions/subprojects/c/meson.build (renamed from test cases/failing/62 subproj different versions/subprojects/c/meson.build)0
-rw-r--r--test cases/failing/61 subproj different versions/test.json (renamed from test cases/failing/62 subproj different versions/test.json)2
-rw-r--r--test cases/failing/62 wrong boost module/meson.build (renamed from test cases/failing/63 wrong boost module/meson.build)0
-rw-r--r--test cases/failing/62 wrong boost module/test.json (renamed from test cases/failing/63 wrong boost module/test.json)2
-rw-r--r--test cases/failing/63 install_data rename bad size/file1.txt (renamed from test cases/failing/64 install_data rename bad size/file1.txt)0
-rw-r--r--test cases/failing/63 install_data rename bad size/file2.txt (renamed from test cases/failing/64 install_data rename bad size/file2.txt)0
-rw-r--r--test cases/failing/63 install_data rename bad size/meson.build (renamed from test cases/failing/64 install_data rename bad size/meson.build)0
-rw-r--r--test cases/failing/63 install_data rename bad size/test.json (renamed from test cases/failing/64 install_data rename bad size/test.json)2
-rw-r--r--test cases/failing/64 skip only subdir/meson.build (renamed from test cases/failing/65 skip only subdir/meson.build)0
-rw-r--r--test cases/failing/64 skip only subdir/subdir/meson.build (renamed from test cases/failing/65 skip only subdir/subdir/meson.build)0
-rw-r--r--test cases/failing/64 skip only subdir/test.json (renamed from test cases/failing/65 skip only subdir/test.json)2
-rw-r--r--test cases/failing/65 dual override/meson.build (renamed from test cases/failing/66 dual override/meson.build)0
-rw-r--r--test cases/failing/65 dual override/overrides.py (renamed from test cases/failing/66 dual override/overrides.py)0
-rw-r--r--test cases/failing/65 dual override/test.json (renamed from test cases/failing/66 dual override/test.json)2
-rw-r--r--test cases/failing/66 override used/meson.build (renamed from test cases/failing/67 override used/meson.build)0
-rwxr-xr-xtest cases/failing/66 override used/other.py (renamed from test cases/failing/67 override used/other.py)0
-rwxr-xr-xtest cases/failing/66 override used/something.py (renamed from test cases/failing/67 override used/something.py)0
-rw-r--r--test cases/failing/66 override used/test.json (renamed from test cases/failing/67 override used/test.json)2
-rw-r--r--test cases/failing/67 run_command unclean exit/meson.build (renamed from test cases/failing/68 run_command unclean exit/meson.build)0
-rwxr-xr-xtest cases/failing/67 run_command unclean exit/returncode.py (renamed from test cases/failing/68 run_command unclean exit/returncode.py)0
-rw-r--r--test cases/failing/67 run_command unclean exit/test.json (renamed from test cases/failing/68 run_command unclean exit/test.json)2
-rw-r--r--test cases/failing/68 int literal leading zero/meson.build (renamed from test cases/failing/69 int literal leading zero/meson.build)0
-rw-r--r--test cases/failing/68 int literal leading zero/test.json (renamed from test cases/failing/69 int literal leading zero/test.json)2
-rw-r--r--test cases/failing/69 configuration immutable/input (renamed from test cases/failing/70 configuration immutable/input)0
-rw-r--r--test cases/failing/69 configuration immutable/meson.build (renamed from test cases/failing/70 configuration immutable/meson.build)0
-rw-r--r--test cases/failing/69 configuration immutable/test.json (renamed from test cases/failing/70 configuration immutable/test.json)2
-rw-r--r--test cases/failing/70 link with shared module on osx/meson.build (renamed from test cases/failing/71 link with shared module on osx/meson.build)0
-rw-r--r--test cases/failing/70 link with shared module on osx/module.c (renamed from test cases/failing/71 link with shared module on osx/module.c)0
-rw-r--r--test cases/failing/70 link with shared module on osx/prog.c (renamed from test cases/failing/71 link with shared module on osx/prog.c)0
-rw-r--r--test cases/failing/70 link with shared module on osx/test.json (renamed from test cases/failing/71 link with shared module on osx/test.json)2
-rw-r--r--test cases/failing/71 non-ascii in ascii encoded configure file/config9.h.in (renamed from test cases/failing/72 non-ascii in ascii encoded configure file/config9.h.in)0
-rw-r--r--test cases/failing/71 non-ascii in ascii encoded configure file/meson.build (renamed from test cases/failing/72 non-ascii in ascii encoded configure file/meson.build)0
-rw-r--r--test cases/failing/71 non-ascii in ascii encoded configure file/test.json (renamed from test cases/failing/72 non-ascii in ascii encoded configure file/test.json)2
-rw-r--r--test cases/failing/72 subproj dependency not-found and required/meson.build (renamed from test cases/failing/73 subproj dependency not-found and required/meson.build)0
-rw-r--r--test cases/failing/72 subproj dependency not-found and required/test.json7
-rw-r--r--test cases/failing/73 subproj dependency not-found and required/test.json7
-rw-r--r--test cases/failing/73 unfound run/meson.build (renamed from test cases/failing/74 unfound run/meson.build)0
-rw-r--r--test cases/failing/73 unfound run/test.json (renamed from test cases/failing/74 unfound run/test.json)2
-rw-r--r--test cases/failing/74 framework dependency with version/meson.build (renamed from test cases/failing/75 framework dependency with version/meson.build)0
-rw-r--r--test cases/failing/74 framework dependency with version/test.json (renamed from test cases/failing/75 framework dependency with version/test.json)2
-rw-r--r--test cases/failing/75 override exe config/foo.c3
-rw-r--r--test cases/failing/75 override exe config/meson.build (renamed from test cases/failing/76 override exe config/meson.build)0
-rw-r--r--test cases/failing/75 override exe config/test.json (renamed from test cases/failing/76 override exe config/test.json)2
-rw-r--r--test cases/failing/76 gl dependency with version/meson.build (renamed from test cases/failing/77 gl dependency with version/meson.build)0
-rw-r--r--test cases/failing/76 gl dependency with version/test.json (renamed from test cases/failing/77 gl dependency with version/test.json)2
-rw-r--r--test cases/failing/77 threads dependency with version/meson.build (renamed from test cases/failing/78 threads dependency with version/meson.build)0
-rw-r--r--test cases/failing/77 threads dependency with version/test.json (renamed from test cases/failing/78 threads dependency with version/test.json)2
-rw-r--r--test cases/failing/78 gtest dependency with version/meson.build (renamed from test cases/failing/79 gtest dependency with version/meson.build)0
-rw-r--r--test cases/failing/78 gtest dependency with version/test.json (renamed from test cases/failing/79 gtest dependency with version/test.json)2
-rw-r--r--test cases/failing/79 dub library/meson.build (renamed from test cases/failing/80 dub library/meson.build)0
-rw-r--r--test cases/failing/79 dub library/test.json (renamed from test cases/failing/80 dub library/test.json)2
-rw-r--r--test cases/failing/80 dub executable/meson.build (renamed from test cases/failing/81 dub executable/meson.build)0
-rw-r--r--test cases/failing/80 dub executable/test.json (renamed from test cases/failing/81 dub executable/test.json)2
-rw-r--r--test cases/failing/81 dub compiler/meson.build (renamed from test cases/failing/82 dub compiler/meson.build)0
-rw-r--r--test cases/failing/81 dub compiler/test.json (renamed from test cases/failing/82 dub compiler/test.json)2
-rw-r--r--test cases/failing/82 subproj not-found dep/meson.build (renamed from test cases/failing/83 subproj not-found dep/meson.build)0
-rw-r--r--test cases/failing/82 subproj not-found dep/subprojects/somesubproj/meson.build (renamed from test cases/failing/83 subproj not-found dep/subprojects/somesubproj/meson.build)0
-rw-r--r--test cases/failing/82 subproj not-found dep/test.json (renamed from test cases/failing/83 subproj not-found dep/test.json)2
-rw-r--r--test cases/failing/83 invalid configure file/input (renamed from test cases/failing/84 invalid configure file/input)0
-rw-r--r--test cases/failing/83 invalid configure file/meson.build (renamed from test cases/failing/84 invalid configure file/meson.build)0
-rw-r--r--test cases/failing/83 invalid configure file/test.json (renamed from test cases/failing/84 invalid configure file/test.json)2
-rw-r--r--test cases/failing/84 kwarg dupe/meson.build (renamed from test cases/failing/85 kwarg dupe/meson.build)0
-rw-r--r--test cases/failing/84 kwarg dupe/prog.c (renamed from test cases/failing/85 kwarg dupe/prog.c)0
-rw-r--r--test cases/failing/84 kwarg dupe/test.json (renamed from test cases/failing/85 kwarg dupe/test.json)2
-rw-r--r--test cases/failing/85 missing pch file/meson.build (renamed from test cases/failing/86 missing pch file/meson.build)0
-rw-r--r--test cases/failing/85 missing pch file/prog.c (renamed from test cases/failing/86 missing pch file/prog.c)0
-rw-r--r--test cases/failing/85 missing pch file/test.json8
-rw-r--r--test cases/failing/86 missing pch file/test.json8
-rw-r--r--test cases/failing/86 pch source different folder/include/pch.h (renamed from test cases/failing/87 pch source different folder/include/pch.h)0
-rw-r--r--test cases/failing/86 pch source different folder/meson.build (renamed from test cases/failing/87 pch source different folder/meson.build)0
-rw-r--r--test cases/failing/86 pch source different folder/prog.c (renamed from test cases/failing/87 pch source different folder/prog.c)0
-rw-r--r--test cases/failing/86 pch source different folder/src/pch.c (renamed from test cases/failing/87 pch source different folder/src/pch.c)0
-rw-r--r--test cases/failing/86 pch source different folder/test.json7
-rw-r--r--test cases/failing/87 pch source different folder/test.json7
-rw-r--r--test cases/failing/87 unknown config tool/meson.build (renamed from test cases/failing/88 unknown config tool/meson.build)0
-rw-r--r--test cases/failing/87 unknown config tool/test.json (renamed from test cases/failing/88 unknown config tool/test.json)2
-rw-r--r--test cases/failing/88 custom target install data/Info.plist.cpp (renamed from test cases/failing/89 custom target install data/Info.plist.cpp)0
-rw-r--r--test cases/failing/88 custom target install data/meson.build (renamed from test cases/failing/89 custom target install data/meson.build)0
-rw-r--r--test cases/failing/88 custom target install data/preproc.py (renamed from test cases/failing/89 custom target install data/preproc.py)0
-rw-r--r--test cases/failing/88 custom target install data/test.json (renamed from test cases/failing/89 custom target install data/test.json)2
-rw-r--r--test cases/failing/89 add dict non string key/meson.build (renamed from test cases/failing/90 add dict non string key/meson.build)0
-rw-r--r--test cases/failing/89 add dict non string key/test.json (renamed from test cases/failing/90 add dict non string key/test.json)2
-rw-r--r--test cases/failing/90 add dict duplicate keys/meson.build (renamed from test cases/failing/91 add dict duplicate keys/meson.build)0
-rw-r--r--test cases/failing/90 add dict duplicate keys/test.json (renamed from test cases/failing/91 add dict duplicate keys/test.json)2
-rw-r--r--test cases/failing/91 no host get_external_property/meson.build (renamed from test cases/failing/92 no host get_external_property/meson.build)0
-rw-r--r--test cases/failing/91 no host get_external_property/test.json (renamed from test cases/failing/92 no host get_external_property/test.json)2
-rw-r--r--test cases/failing/92 no native compiler/main.c (renamed from test cases/failing/93 no native compiler/main.c)0
-rw-r--r--test cases/failing/92 no native compiler/meson.build (renamed from test cases/failing/93 no native compiler/meson.build)0
-rw-r--r--test cases/failing/92 no native compiler/test.json (renamed from test cases/failing/93 no native compiler/test.json)2
-rw-r--r--test cases/failing/93 subdir parse error/meson.build (renamed from test cases/failing/94 subdir parse error/meson.build)0
-rw-r--r--test cases/failing/93 subdir parse error/subdir/meson.build (renamed from test cases/failing/94 subdir parse error/subdir/meson.build)0
-rw-r--r--test cases/failing/93 subdir parse error/test.json (renamed from test cases/failing/94 subdir parse error/test.json)2
-rw-r--r--test cases/failing/94 invalid option file/meson.build (renamed from test cases/failing/95 invalid option file/meson.build)0
-rw-r--r--test cases/failing/94 invalid option file/meson_options.txt (renamed from test cases/failing/95 invalid option file/meson_options.txt)0
-rw-r--r--test cases/failing/94 invalid option file/test.json7
-rw-r--r--test cases/failing/95 invalid option file/test.json7
-rw-r--r--test cases/failing/95 no lang/main.c (renamed from test cases/failing/96 no lang/main.c)0
-rw-r--r--test cases/failing/95 no lang/meson.build (renamed from test cases/failing/96 no lang/meson.build)0
-rw-r--r--test cases/failing/95 no lang/test.json (renamed from test cases/failing/96 no lang/test.json)2
-rw-r--r--test cases/failing/96 no glib-compile-resources/meson.build (renamed from test cases/failing/97 no glib-compile-resources/meson.build)0
-rw-r--r--test cases/failing/96 no glib-compile-resources/test.json (renamed from test cases/failing/97 no glib-compile-resources/test.json)2
-rw-r--r--test cases/failing/96 no glib-compile-resources/trivial.gresource.xml (renamed from test cases/failing/97 no glib-compile-resources/trivial.gresource.xml)0
-rw-r--r--test cases/failing/97 number in combo/meson.build (renamed from test cases/failing/98 number in combo/meson.build)0
-rw-r--r--test cases/failing/97 number in combo/nativefile.ini (renamed from test cases/failing/98 number in combo/nativefile.ini)0
-rw-r--r--test cases/failing/97 number in combo/test.json (renamed from test cases/failing/98 number in combo/test.json)2
-rw-r--r--test cases/failing/98 bool in combo/meson.build (renamed from test cases/failing/99 bool in combo/meson.build)0
-rw-r--r--test cases/failing/98 bool in combo/meson_options.txt (renamed from test cases/failing/99 bool in combo/meson_options.txt)0
-rw-r--r--test cases/failing/98 bool in combo/nativefile.ini (renamed from test cases/failing/99 bool in combo/nativefile.ini)0
-rw-r--r--test cases/failing/98 bool in combo/test.json (renamed from test cases/failing/99 bool in combo/test.json)2
-rw-r--r--test cases/failing/99 compiler no lang/meson.build (renamed from test cases/failing/100 compiler no lang/meson.build)0
-rw-r--r--test cases/failing/99 compiler no lang/test.json7
-rw-r--r--test cases/format/1 default/indentation.meson28
-rw-r--r--test cases/format/5 transform/default.expected.meson28
-rw-r--r--test cases/format/5 transform/muon.expected.meson28
-rw-r--r--test cases/format/5 transform/options.expected.meson41
-rw-r--r--test cases/format/5 transform/source.meson40
-rw-r--r--test cases/fortran/23 preprocess/main.f9012
-rw-r--r--test cases/fortran/23 preprocess/meson.build11
-rw-r--r--test cases/frameworks/15 llvm/test.json6
-rw-r--r--test cases/frameworks/17 mpi/test.json6
-rw-r--r--test cases/frameworks/18 vulkan/meson.build4
-rw-r--r--test cases/frameworks/18 vulkan/meson.options6
-rw-r--r--test cases/frameworks/18 vulkan/test.json11
-rw-r--r--test cases/frameworks/25 hdf5/meson.build5
-rw-r--r--test cases/frameworks/25 hdf5/test.json2
-rw-r--r--test cases/frameworks/38 gettext extractor/meson.build4
-rw-r--r--test cases/frameworks/38 gettext extractor/src/lib3/foo.c (renamed from test cases/unit/126 test slice/test.py)0
-rw-r--r--test cases/frameworks/38 gettext extractor/src/lib3/meson.build4
-rw-r--r--test cases/frameworks/38 gettext extractor/src/meson.build1
-rw-r--r--test cases/frameworks/38 gettext extractor/test.json2
-rw-r--r--test cases/frameworks/7 gnome/gdbus/meson.build17
-rw-r--r--test cases/frameworks/7 gnome/meson.build2
-rw-r--r--test cases/frameworks/8 flex/meson.build37
-rw-r--r--test cases/native/9 override with exe/subprojects/sub/meson.build10
-rwxr-xr-xtest cases/python/11 script path/gen7
-rw-r--r--test cases/python/11 script path/meson.build19
-rwxr-xr-xtest cases/python/11 script path/run.py7
-rw-r--r--test cases/python/9 extmodule limited api/meson.build4
-rw-r--r--test cases/rewrite/1 basic/addSrc.json38
-rw-r--r--test cases/rewrite/1 basic/addTgt.json2
-rw-r--r--test cases/rewrite/1 basic/expected_dag.txt129
-rw-r--r--test cases/rewrite/1 basic/info.json20
-rw-r--r--test cases/rewrite/1 basic/meson.build16
-rw-r--r--test cases/rewrite/1 basic/rmSrc.json40
-rw-r--r--test cases/rewrite/1 basic/rmTgt.json5
-rw-r--r--test cases/rewrite/10 duplicate globals/info.json8
-rw-r--r--test cases/rewrite/10 duplicate globals/meson.build5
-rw-r--r--test cases/rewrite/3 kwargs/add.json3
-rw-r--r--test cases/rewrite/3 kwargs/set.json3
-rw-r--r--test cases/rewrite/8 kwargs dict/info.json14
-rw-r--r--test cases/rewrite/8 kwargs dict/meson.build10
-rw-r--r--test cases/rewrite/9 tricky dataflow/addSrc.json77
-rw-r--r--test cases/rewrite/9 tricky dataflow/info.json37
-rw-r--r--test cases/rewrite/9 tricky dataflow/meson.build41
-rw-r--r--test cases/rust/1 basic/meson.build7
-rw-r--r--test cases/rust/1 basic/test.json4
-rw-r--r--test cases/rust/12 bindgen/cpp/meson.build2
-rw-r--r--test cases/rust/12 bindgen/dependencies/internal_dep.h2
-rw-r--r--test cases/rust/12 bindgen/dependencies/internal_main.rs2
-rw-r--r--test cases/rust/12 bindgen/gen/gen_header.py (renamed from test cases/rust/12 bindgen/src/gen_header.py)2
-rw-r--r--test cases/rust/12 bindgen/gen/meson.build13
-rw-r--r--test cases/rust/12 bindgen/include/other.h2
-rw-r--r--test cases/rust/12 bindgen/meson.build17
-rw-r--r--test cases/rust/12 bindgen/src/cpp.rs2
-rw-r--r--test cases/rust/12 bindgen/src/global.rs2
-rw-r--r--test cases/rust/12 bindgen/src/header.h2
-rw-r--r--test cases/rust/12 bindgen/src/header3.h2
-rw-r--r--test cases/rust/12 bindgen/src/main.rs2
-rw-r--r--test cases/rust/12 bindgen/src/main2.rs2
-rw-r--r--test cases/rust/12 bindgen/src/main3.rs2
-rw-r--r--test cases/rust/12 bindgen/src/source.c2
-rw-r--r--test cases/rust/13 external c dependencies/foo.h3
-rw-r--r--test cases/rust/13 external c dependencies/meson.build11
-rw-r--r--test cases/rust/14 external libm/meson.build4
-rw-r--r--test cases/rust/19 structured sources/empty.file0
-rw-r--r--test cases/rust/19 structured sources/meson.build2
-rw-r--r--test cases/rust/19 structured sources/src2/meson.build4
-rw-r--r--test cases/rust/2 sharedlib/meson.build4
-rw-r--r--test cases/rust/2 sharedlib/test.json2
-rw-r--r--test cases/rust/20 rust and cpp/meson.build2
-rw-r--r--test cases/rust/22 cargo subproject/meson.build2
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml3
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap3
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml12
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs5
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build3
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs.wrap2
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/Cargo.toml7
-rw-r--r--test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/lib.rs8
-rw-r--r--test cases/rust/27 objects/lib1-dylib.rs10
-rw-r--r--test cases/rust/27 objects/meson.build14
-rw-r--r--test cases/rust/28 mixed/hello.rs6
-rw-r--r--test cases/rust/28 mixed/main.cc5
-rw-r--r--test cases/rust/28 mixed/meson.build12
-rw-r--r--test cases/rust/29 self-contained/lib.rs4
-rw-r--r--test cases/rust/29 self-contained/main.c6
-rw-r--r--test cases/rust/29 self-contained/meson.build17
-rw-r--r--test cases/rust/29 self-contained/test.json6
-rw-r--r--test cases/rust/3 staticlib/meson.build3
-rw-r--r--test cases/rust/3 staticlib/prog2.rs5
-rw-r--r--test cases/rust/3 staticlib/stuff2.rs14
-rw-r--r--test cases/rust/30 rlib link subdir/cdep/f.c1
-rw-r--r--test cases/rust/30 rlib link subdir/cdep/meson.build1
-rw-r--r--test cases/rust/30 rlib link subdir/f-sys.rs3
-rw-r--r--test cases/rust/30 rlib link subdir/main.rs1
-rw-r--r--test cases/rust/30 rlib link subdir/meson.build21
-rw-r--r--test cases/rust/31 both machines/lib.rs3
-rw-r--r--test cases/rust/31 both machines/meson.build36
-rw-r--r--test cases/rust/31 both machines/test.rs5
-rw-r--r--test cases/rust/32 cargo workspace/meson.build13
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo.wrap5
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/Cargo.toml15
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/src/foo/Cargo.toml10
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/src/foo/src/lib.rs6
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/src/lib.rs3
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/src/member1/Cargo.toml7
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/src/member1/src/lib.rs5
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1-rs.wrap6
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/Cargo.toml9
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/src/lib.rs5
-rw-r--r--test cases/rust/32 cargo workspace/subprojects/member2.wrap2
-rw-r--r--test cases/rust/32 cargo workspace/test_foo_1.c5
-rw-r--r--test cases/rust/32 cargo workspace/test_foo_1.rs5
-rw-r--r--test cases/rust/33 cargo multiple versions/meson.build3
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs.wrap2
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/Cargo.toml6
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/lib.rs3
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs.wrap2
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/Cargo.toml6
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/lib.rs3
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/main.wrap2
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/main/Cargo.toml9
-rw-r--r--test cases/rust/33 cargo multiple versions/subprojects/main/lib.rs6
-rw-r--r--test cases/rust/4 polyglot/meson.build6
-rw-r--r--test cases/rust/4 polyglot/stuff.rs2
-rw-r--r--test cases/rust/4 polyglot/test.json25
-rw-r--r--test cases/rust/9 unit tests/meson.build6
-rw-r--r--test cases/snippets/1 symbol visibility header/main-static-only.c3
-rw-r--r--test cases/snippets/1 symbol visibility header/main.c3
-rw-r--r--test cases/snippets/1 symbol visibility header/meson.build13
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/meson.build5
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.c3
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.h3
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.c3
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.h3
-rw-r--r--test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/meson.build39
-rw-r--r--test cases/snippets/1 symbol visibility header/test.json15
-rw-r--r--test cases/swift/11 mixed cpp/main.swift6
-rw-r--r--test cases/swift/11 mixed cpp/meson.build12
-rw-r--r--test cases/swift/11 mixed cpp/mylib.cpp22
-rw-r--r--test cases/swift/11 mixed cpp/mylib.h13
-rw-r--r--test cases/swift/12 c std passthrough/header.h10
-rw-r--r--test cases/swift/12 c std passthrough/main.swift3
-rw-r--r--test cases/swift/12 c std passthrough/meson.build3
-rw-r--r--test cases/swift/13 mixed objcpp/main.swift2
-rw-r--r--test cases/swift/13 mixed objcpp/meson.build12
-rw-r--r--test cases/swift/13 mixed objcpp/mylib.h17
-rw-r--r--test cases/swift/13 mixed objcpp/mylib.mm29
-rw-r--r--test cases/swift/14 single-file library/main.swift3
-rw-r--r--test cases/swift/14 single-file library/meson.build4
-rw-r--r--test cases/swift/14 single-file library/singlefile.swift1
-rw-r--r--test cases/swift/15 main in single-file library/main.swift3
-rw-r--r--test cases/swift/15 main in single-file library/meson.build4
-rw-r--r--test cases/swift/15 main in single-file library/module.modulemap3
-rw-r--r--test cases/swift/15 main in single-file library/program.c5
-rw-r--r--test cases/swift/15 main in single-file library/program.h1
-rw-r--r--test cases/swift/16 main in multi-file library/main.swift4
-rw-r--r--test cases/swift/16 main in multi-file library/meson.build4
-rw-r--r--test cases/swift/16 main in multi-file library/module.modulemap3
-rw-r--r--test cases/swift/16 main in multi-file library/more.swift3
-rw-r--r--test cases/swift/16 main in multi-file library/program.c5
-rw-r--r--test cases/swift/16 main in multi-file library/program.h1
-rw-r--r--test cases/swift/8 extra args/lib.swift3
-rw-r--r--test cases/swift/8 extra args/main.swift1
-rw-r--r--test cases/swift/8 extra args/meson.build4
-rw-r--r--test cases/unit/116 empty project/expected_mods.json5
-rw-r--r--test cases/unit/120 rewrite/meson.build11
-rw-r--r--test cases/unit/126 python extension/foo.c31
-rw-r--r--test cases/unit/126 python extension/meson.build20
-rw-r--r--test cases/unit/127 pkgsubproj/meson.build (renamed from test cases/unit/125 pkgsubproj/meson.build)0
-rw-r--r--test cases/unit/127 pkgsubproj/subprojects/sub/meson.build (renamed from test cases/unit/125 pkgsubproj/subprojects/sub/meson.build)0
-rw-r--r--test cases/unit/128 test slice/meson.build (renamed from test cases/unit/126 test slice/meson.build)0
-rw-r--r--test cases/unit/128 test slice/test.py0
-rw-r--r--test cases/unit/129 sanitizers/meson.build (renamed from test cases/unit/127 sanitizers/meson.build)0
-rw-r--r--test cases/unit/130 long opt vs D/meson.build (renamed from test cases/unit/128 long opt vs D/meson.build)0
-rw-r--r--test cases/unit/130 long opt vs D/meson_options.txt (renamed from test cases/unit/128 long opt vs D/meson_options.txt)0
-rw-r--r--test cases/unit/131 vala internal glib/lib.vala3
-rw-r--r--test cases/unit/131 vala internal glib/meson.build21
-rw-r--r--test cases/unit/131 vala internal glib/meson.options1
-rw-r--r--test cases/unit/132 custom target index test/meson.build16
-rw-r--r--test cases/unit/56 introspection/meson.build15
-rw-r--r--test cases/unit/58 introspect buildoptions/meson.build8
-rw-r--r--test cases/unit/69 cross/crossfile.in3
-rw-r--r--test cases/unit/69 cross/meson.build11
-rw-r--r--test cases/unit/69 cross/nativefile.in2
-rw-r--r--test cases/unit/92 new subproject in configured project/meson.build2
-rw-r--r--test cases/unit/92 new subproject in configured project/meson_options.txt2
-rw-r--r--test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c2
-rw-r--r--test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build2
-rw-r--r--test cases/vala/31 generated ui file subdirectory/meson.build22
-rw-r--r--test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in6
-rw-r--r--test cases/vala/31 generated ui file subdirectory/subdir/meson.build13
-rw-r--r--test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml6
-rw-r--r--test cases/vala/31 generated ui file subdirectory/test.vala7
-rw-r--r--test cases/vala/32 valaless vapigen/clib.c5
-rw-r--r--test cases/vala/32 valaless vapigen/clib.h3
-rw-r--r--test cases/vala/32 valaless vapigen/meson.build34
-rw-r--r--test cases/vala/32 valaless vapigen/test_clib.c9
-rw-r--r--test cases/vala/9 gir/meson.build7
-rw-r--r--test cases/warning/8 meson.options/meson.build (renamed from test cases/warning/9 meson.options/meson.build)0
-rw-r--r--test cases/warning/8 meson.options/meson.options (renamed from test cases/warning/9 meson.options/meson.options)0
-rw-r--r--test cases/warning/8 meson.options/subprojects/no-warn/meson.build (renamed from test cases/warning/9 meson.options/subprojects/no-warn/meson.build)0
-rw-r--r--test cases/warning/8 meson.options/subprojects/no-warn/meson.options (renamed from test cases/warning/9 meson.options/subprojects/no-warn/meson.options)0
-rw-r--r--test cases/warning/8 meson.options/subprojects/no-warn/meson_options.txt (renamed from test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt)0
-rw-r--r--test cases/warning/8 meson.options/test.json (renamed from test cases/warning/9 meson.options/test.json)0
-rw-r--r--test cases/warning/9 target with no sources/meson.build (renamed from test cases/warning/8 target with no sources/meson.build)0
-rw-r--r--test cases/warning/9 target with no sources/test.json (renamed from test cases/warning/8 target with no sources/test.json)0
-rw-r--r--test cases/windows/21 masm/meson.build4
-rw-r--r--test cases/windows/25 embed manifest/DPIAware.manifest9
-rw-r--r--test cases/windows/25 embed manifest/meson.build11
-rw-r--r--test cases/windows/25 embed manifest/prog.c3
-rwxr-xr-xtools/dircondenser.py1
-rwxr-xr-xtools/run_with_cov.py42
-rw-r--r--unittests/__init__.py20
-rw-r--r--unittests/allplatformstests.py720
-rw-r--r--unittests/cargotests.py480
-rw-r--r--unittests/datatests.py10
-rw-r--r--unittests/failuretests.py18
-rw-r--r--unittests/internaltests.py121
-rw-r--r--unittests/linuxliketests.py76
-rw-r--r--unittests/machinefiletests.py10
-rw-r--r--unittests/optiontests.py317
-rw-r--r--unittests/platformagnostictests.py52
-rw-r--r--unittests/rewritetests.py198
-rw-r--r--unittests/windowstests.py15
987 files changed, 17778 insertions, 9169 deletions
diff --git a/.github/codecov.yml b/.github/codecov.yml
deleted file mode 100644
index fa7b82a..0000000
--- a/.github/codecov.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-coverage:
- status:
- project:
- default:
- informational: true
- patch:
- default:
- informational: true
-comment: false
-github_checks:
- annotations: false
diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml
index d641b18..2ba1ff2 100644
--- a/.github/workflows/cygwin.yml
+++ b/.github/workflows/cygwin.yml
@@ -87,7 +87,7 @@ jobs:
- name: Run pip
run: |
export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32
- python3 -m pip --disable-pip-version-check install gcovr fastjsonschema pefile pytest pytest-subtests pytest-xdist coverage
+ python3 -m pip --disable-pip-version-check install gcovr fastjsonschema pefile pytest pytest-subtests pytest-xdist
shell: C:\cygwin\bin\bash.exe --noprofile --norc -o igncr -eo pipefail '{0}'
- uses: actions/cache/save@v4
@@ -99,7 +99,7 @@ jobs:
- name: Run tests
run: |
export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32
- python3 ./tools/run_with_cov.py run_tests.py --backend=ninja
+ python3 ./run_tests.py --backend=ninja
env:
# Cygwin's static boost installation is broken (some static library
# variants such as boost_thread are not present)
@@ -112,17 +112,3 @@ jobs:
path: meson-test-run.*
# test log should be saved on failure
if: ${{ !cancelled() }}
-
- - name: Aggregate coverage reports
- run: |
- export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32
- ./ci/combine_cov.sh
- shell: C:\cygwin\bin\bash.exe --noprofile --norc -o igncr -eo pipefail '{0}'
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "${{ matrix.NAME }}"
- fail_ci_if_error: false
- verbose: true
diff --git a/.github/workflows/file_format.yml b/.github/workflows/file_format.yml
index d61d634..a8d4ce2 100644
--- a/.github/workflows/file_format.yml
+++ b/.github/workflows/file_format.yml
@@ -18,3 +18,4 @@ jobs:
with:
python-version: '3.x'
- run: python3 ./run_format_tests.py
+ - run: python3 ./run_shell_checks.py
diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml
index d20f7e5..369c91e 100644
--- a/.github/workflows/images.yml
+++ b/.github/workflows/images.yml
@@ -37,13 +37,14 @@ jobs:
fail-fast: false
matrix:
cfg:
- - { name: Arch Linux, id: arch }
- - { name: CUDA (on Arch), id: cuda }
- - { name: Fedora, id: fedora }
- - { name: Gentoo, id: gentoo }
- - { name: OpenSUSE, id: opensuse }
- - { name: Ubuntu Bionic, id: bionic }
- - { name: Ubuntu Rolling, id: ubuntu-rolling }
+ - { name: Arch Linux, id: arch }
+ - { name: CUDA (on Arch), id: cuda }
+ - { name: CUDA Cross (on Ubuntu Jammy), id: cuda-cross }
+ - { name: Fedora, id: fedora }
+ - { name: Gentoo, id: gentoo }
+ - { name: OpenSUSE, id: opensuse }
+ - { name: Ubuntu Bionic, id: bionic }
+ - { name: Ubuntu Rolling, id: ubuntu-rolling }
steps:
# Need v3 because of bionic
- uses: actions/checkout@v3
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5588034..7161b61 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
- python-version: '3.x'
+ python-version: '3.13'
- run: python -m pip install pylint
- run: pylint --output-format colorized mesonbuild
@@ -53,7 +53,7 @@ jobs:
with:
python-version: '3.x'
# Pin mypy to version 1.8, so we retain the ability to lint for Python 3.7
- - run: python -m pip install "mypy==1.8" coverage strictyaml types-PyYAML types-tqdm types-chevron
+ - run: python -m pip install "mypy==1.8" strictyaml truststore types-PyYAML types-tqdm types-chevron
- run: python run_mypy.py --allver
env:
PYTHONUNBUFFERED: 1
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 2d2ea39..3afb4ba 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -35,7 +35,7 @@ jobs:
- run: |
export PATH="$HOME/Library/Python/3.9/bin:$PATH"
/usr/bin/python3 -m pip install --upgrade pip
- /usr/bin/python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema coverage
+ /usr/bin/python3 -m pip install pytest pytest-xdist pytest-subtests fastjsonschema
- run: brew install pkg-config ninja llvm qt@5
- env:
CPPFLAGS: "-I/opt/homebrew/include"
@@ -48,20 +48,7 @@ jobs:
export SDKROOT="$(xcodebuild -version -sdk macosx Path)"
export PATH="$HOME/Library/Python/3.9/bin:$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:$PATH"
export PKG_CONFIG_PATH="/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/Current/lib/pkgconfig:/opt/homebrew/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH"
- /usr/bin/python3 ./tools/run_with_cov.py ./run_unittests.py
-
- - name: Aggregate coverage reports
- run: |
- export PATH="$HOME/Library/Python/3.9/bin:$PATH"
- ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "appleclang [unit tests]"
- fail_ci_if_error: false
- verbose: true
+ /usr/bin/python3 ./run_unittests.py
project-tests-appleclang:
@@ -98,9 +85,7 @@ jobs:
# https://github.com/actions/setup-python/issues/58
- run: brew install pkg-config ninja llvm qt@5 boost ldc hdf5 openmpi lapack scalapack sdl2 boost-python3 gtk-doc zstd ncurses objfw libomp
- run: |
- python3 -m pip install --upgrade setuptools
- python3 -m pip install --upgrade pip
- python3 -m pip install cython coverage
+ python3 -m pip install cython
- env:
CPPFLAGS: "-I/opt/homebrew/include"
LDFLAGS: "-L/opt/homebrew/lib"
@@ -114,18 +99,7 @@ jobs:
# We need this to avoid objfw test failures.
export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/ncurses/bin:$PATH:/opt/homebrew/opt/llvm/bin"
export PKG_CONFIG_PATH="/opt/homebrew/opt/qt@5/lib/pkgconfig:/opt/homebrew/opt/lapack/lib/pkgconfig:/opt/homebrew/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH"
- ./tools/run_with_cov.py ./run_project_tests.py --backend=ninja
-
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "appleclang [project tests; unity=${{ matrix.unity }}]"
- fail_ci_if_error: false
- verbose: true
+ ./run_project_tests.py --backend=ninja
Qt4macos:
# This job only works on Intel Macs, because OpenSSL 1.0 doesn't build on
diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml
index 9101e6b..b926d18 100644
--- a/.github/workflows/msys2.yml
+++ b/.github/workflows/msys2.yml
@@ -29,7 +29,7 @@ permissions:
jobs:
test:
- runs-on: windows-2019
+ runs-on: windows-2022
name: ${{ matrix.NAME }}
strategy:
fail-fast: false
@@ -85,11 +85,12 @@ jobs:
mingw-w64-${{ matrix.MSYS2_ARCH }}-python-pip
mingw-w64-${{ matrix.MSYS2_ARCH }}-python-fastjsonschema
mingw-w64-${{ matrix.MSYS2_ARCH }}-objfw
+ mingw-w64-${{ matrix.MSYS2_ARCH }}-llvm
mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.TOOLCHAIN }}
- name: Install dependencies
run: |
- python3 -m pip --disable-pip-version-check install gcovr pefile pytest pytest-subtests pytest-xdist coverage
+ python3 -m pip --disable-pip-version-check install gcovr pefile pytest pytest-subtests pytest-xdist
- name: Install pypy3 on x86_64
run: |
@@ -124,20 +125,9 @@ jobs:
pacman --noconfirm --needed -S mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.MSYS2_CURSES }}
fi
- MSYSTEM= python3 ./tools/run_with_cov.py run_tests.py --backend=ninja
+ MSYSTEM= python3 ./run_tests.py --backend=ninja
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.NAME }}
path: meson-test-run.*
-
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "${{ matrix.NAME }}"
- fail_ci_if_error: false
- verbose: true
diff --git a/.github/workflows/nonnative.yml b/.github/workflows/nonnative.yml
index 2712d10..c616f51 100644
--- a/.github/workflows/nonnative.yml
+++ b/.github/workflows/nonnative.yml
@@ -36,18 +36,16 @@ jobs:
- run: |
apt-get -y purge clang gcc gdc
apt-get -y autoremove
- python3 -m pip install coverage
- uses: actions/checkout@v4
- name: Run tests
- run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./tools/run_with_cov.py ./run_tests.py $CI_ARGS --cross ubuntu-armhf.json --cross-only'
+ run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS --cross ubuntu-armhf.json --cross-only'
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "Ubuntu nonnative"
- fail_ci_if_error: false
- verbose: true
+ cross-cuda:
+ runs-on: ubuntu-latest
+ container: mesonbuild/cuda-cross:latest
+ env:
+ MESON_CI_JOBNAME: cuda-cross-${{ github.job }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Run tests
+ run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS --cross cuda-cross.json --cross-only'
diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml
index 0912a75..4b9b7a4 100644
--- a/.github/workflows/os_comp.yml
+++ b/.github/workflows/os_comp.yml
@@ -72,18 +72,7 @@ jobs:
source /ci/env_vars.sh
cd $GITHUB_WORKSPACE
- ./tools/run_with_cov.py ./run_tests.py $CI_ARGS
-
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "OS Comp [${{ matrix.cfg.name }}]"
- fail_ci_if_error: false
- verbose: true
+ ./run_tests.py $CI_ARGS
pypy:
name: 'Arch / PyPy'
@@ -172,15 +161,4 @@ jobs:
update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix
update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix
- ./tools/run_with_cov.py ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS
-
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "Ubuntu [${{ matrix.cfg.CC }} ${{ matrix.cfg.RUN_TESTS_ARGS }} ${{ matrix.cfg.MESON_ARGS }}]"
- fail_ci_if_error: false
- verbose: true
+ ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS
diff --git a/.github/workflows/unusedargs_missingreturn.yml b/.github/workflows/unusedargs_missingreturn.yml
index d6f1246..4367ce5 100644
--- a/.github/workflows/unusedargs_missingreturn.yml
+++ b/.github/workflows/unusedargs_missingreturn.yml
@@ -52,22 +52,10 @@ jobs:
run: |
sudo apt update -yq
sudo apt install -yq --no-install-recommends g++ gfortran ninja-build gobjc gobjc++
- python -m pip install coverage
- - run: ./tools/run_with_cov.py run_project_tests.py --only cmake common fortran platform-linux "objective c" "objective c++"
+ - run: ./run_project_tests.py --only cmake common fortran platform-linux "objective c" "objective c++"
env:
MESON_CI_JOBNAME: linux-ubuntu-gcc-werror
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "UnusedMissingReturn"
- fail_ci_if_error: false
- verbose: true
-
windows:
runs-on: windows-latest
steps:
@@ -76,23 +64,11 @@ jobs:
with:
python-version: '3.x'
- - run: pip install ninja pefile coverage
+ - run: pip install ninja pefile
- - run: python ./tools/run_with_cov.py run_project_tests.py --only platform-windows
+ - run: python ./run_project_tests.py --only platform-windows
env:
CC: gcc
CXX: g++
FC: gfortran
MESON_CI_JOBNAME: msys2-gcc-werror
-
- - name: Aggregate coverage reports
- run: ./ci/combine_cov.sh
- shell: C:\msys64\usr\bin\bash.exe --noprofile --norc -o igncr -eo pipefail '{0}'
-
- - name: Upload coverage report
- uses: codecov/codecov-action@v3
- with:
- files: .coverage/coverage.xml
- name: "UnusedMissingReturn Windows"
- fail_ci_if_error: false
- verbose: true
diff --git a/.gitignore b/.gitignore
index 8ff5e78..e04aa0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@ __pycache__
*~
*.swp
packagecache
+.wraplock
/MANIFEST
/build
/dist
diff --git a/.mailmap b/.mailmap
index a616d10..f692dae 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,7 +1,8 @@
Alexandre Foley <Alexandre.foley@usherbrooke.ca> AlexandreFoley <alexandre.foley@usherbrooke.ca>
Igor Gnatenko <i.gnatenko.brain@gmail.com> Igor Gnatenko <ignatenko@redhat.com>
-Jussi Pakkanen <jpakkane@gmail.com> Jussi Pakkanen <jpakkane@brash.local>
-Jussi Pakkanen <jpakkane@gmail.com> jpakkane <jpakkane@gmail.com>
+Jussi Pakkanen <jussi.pakkanen@mailbox.org> Jussi Pakkanen <jpakkane@brash.local>
+Jussi Pakkanen <jussi.pakkanen@mailbox.org> Jussi Pakkanen <jpakkane@gmail.com>
+Jussi Pakkanen <jussi.pakkanen@mailbox.org> jpakkane <jpakkane@gmail.com>
Liam Beguin <liambeguin@gmail.com> Liam Beguin <lvb@xiphos.com>
Nirbheek Chauhan <nirbheek@centricular.com> Nirbheek Chauhan <nirbheek.chauhan@gmail.com>
Nicolas Schneider <nioncode+git@gmail.com> Nicolas Schneider <nioncode+github@gmail.com>
diff --git a/.mypy.ini b/.mypy.ini
index d00944b..936b1ac 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -22,3 +22,7 @@ check_untyped_defs = True
# disallow_any_explicit = True
# disallow_any_generics = True
# disallow_subclassing_any = True
+
+# future default behaviors
+allow_redefinition_new = True
+local_partial_types = True
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index ea511f3..4488648 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -54,28 +54,28 @@ variables:
jobs:
-- job: vs2019
+- job: vs2022
timeoutInMinutes: 120
pool:
- vmImage: windows-2019
+ vmImage: windows-2022
strategy:
matrix:
- vc2019x64ninja:
+ vc2022x64ninja:
arch: x64
- compiler: msvc2019
+ compiler: msvc2022
backend: ninja
ifort: true
- vc2019x64vs:
+ vc2022x64vs:
arch: x64
- compiler: msvc2019
- backend: vs2019
+ compiler: msvc2022
+ backend: vs2022
# mysteriously, several tests fail because vs cannot find
# executables such as cmd.exe ???
ifort: false
- vc2019arm64ninjacross:
+ vc2022arm64ninjacross:
arch: arm64
- compiler: msvc2019
+ compiler: msvc2022
backend: ninja
extraargs: --cross arm64cl.txt --cross-only
# ifort doesn't support arm64
@@ -105,7 +105,7 @@ jobs:
displayName: insert ifort into environment
inputs:
filename: ci/intel-scripts/activate_windows.bat
- arguments: vs2019
+ arguments: vs2022
modifyEnvironment: True
condition: eq(variables.ifort, 'true')
- task: PowerShell@2
@@ -114,8 +114,3 @@ jobs:
filePath: .\ci\run.ps1
env:
MESON_CI_JOBNAME: azure-$(System.JobName)
- - task: PowerShell@2
- displayName: Gathering coverage report
- inputs:
- targetType: 'filePath'
- filePath: .\ci\coverage.ps1
diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh
index de43cb2..402bb04 100755
--- a/ci/ciimage/arch/install.sh
+++ b/ci/ciimage/arch/install.sh
@@ -9,12 +9,13 @@ source /ci/common.sh
pkgs=(
python python-pip pypy3
ninja make git sudo fakeroot autoconf automake patch
- libelf gcc gcc-fortran gcc-objc vala rust bison flex cython go dlang-dmd
+ libelf gcc gcc-fortran gcc-objc vala rust byacc flex cython go dlang-dmd
mono boost qt5-base gtkmm3 gtest gmock protobuf gobject-introspection
itstool glib2-devel gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz
- doxygen vulkan-headers vulkan-icd-loader vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools
+ doxygen vulkan-headers vulkan-icd-loader vulkan-validation-layers openssh mercurial gtk-sharp-3 qt5-tools
libwmf cmake netcdf-fortran openmpi nasm gnustep-base gettext
python-lxml hotdoc rust-bindgen qt6-base qt6-tools qt6-declarative wayland wayland-protocols
+ intel-oneapi-mkl
# cuda
)
diff --git a/ci/ciimage/cuda-cross/image.json b/ci/ciimage/cuda-cross/image.json
new file mode 100644
index 0000000..062322e
--- /dev/null
+++ b/ci/ciimage/cuda-cross/image.json
@@ -0,0 +1,8 @@
+{
+ "base_image": "ubuntu:22.04",
+ "args": ["--only", "cuda", "--cross", "cuda-cross.json"],
+ "env": {
+ "CI": "1",
+ "MESON_CI_JOBNAME": "linux-cuda-cross"
+ }
+}
diff --git a/ci/ciimage/cuda-cross/install.sh b/ci/ciimage/cuda-cross/install.sh
new file mode 100755
index 0000000..6b5fe7f
--- /dev/null
+++ b/ci/ciimage/cuda-cross/install.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -e
+
+source /ci/common.sh
+
+export DEBIAN_FRONTEND=noninteractive
+export LANG='C.UTF-8'
+
+apt-get -y update
+apt-get -y upgrade
+apt-get -y install wget
+
+# Cuda repo + keyring.
+wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
+apt-get -y install ./cuda-keyring_1.1-1_all.deb
+
+# Cuda cross repo.
+echo "deb [signed-by=/usr/share/keyrings/cuda-archive-keyring.gpg] https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/cross-linux-aarch64/ /" \
+ > /etc/apt/sources.list.d/cuda-ubuntu2204-cross-linux-aarch64.list
+apt-get -y update
+
+pkgs=(
+ clang cmake crossbuild-essential-arm64 cuda-cross-aarch64
+ cuda-nvcc-12-9 git libglib2.0-dev ninja-build pkg-config python3-pip
+)
+
+apt-get -y install "${pkgs[@]}"
+
+install_minimal_python_packages
+
+# Tests need nvcc in PATH in order to run cuda tests.
+echo "export PATH=\$PATH:/usr/local/cuda/bin" >> /ci/env_vars.sh
+
+# cleanup
+apt-get -y clean
+apt-get -y autoclean
+rm cuda-keyring_1.1-1_all.deb
diff --git a/ci/ciimage/fedora/install.sh b/ci/ciimage/fedora/install.sh
index aa87655..62952dc 100755
--- a/ci/ciimage/fedora/install.sh
+++ b/ci/ciimage/fedora/install.sh
@@ -12,11 +12,13 @@ pkgs=(
boost-python3-devel
itstool gtk3-devel java-latest-openjdk-devel gtk-doc llvm-devel clang-devel SDL2-devel graphviz-devel zlib zlib-devel zlib-static
#hdf5-openmpi-devel hdf5-devel netcdf-openmpi-devel netcdf-devel netcdf-fortran-openmpi-devel netcdf-fortran-devel scalapack-openmpi-devel
- doxygen vulkan-devel vulkan-validation-layers-devel openssh lksctp-tools-devel objfw mercurial gtk-sharp2-devel libpcap-devel gpgme-devel
+ doxygen vulkan-devel vulkan-validation-layers-devel openssh lksctp-tools-devel objfw mercurial gtk-sharp3-devel libpcap-devel gpgme-devel
qt5-qtbase-devel qt5-qttools-devel qt5-linguist qt5-qtbase-private-devel
qt6-qtdeclarative-devel qt6-qtbase-devel qt6-qttools-devel qt6-linguist qt6-qtbase-private-devel
libwmf-devel valgrind cmake openmpi-devel nasm gnustep-base-devel gettext-devel ncurses-devel
libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel libgcrypt-devel wayland-devel wayland-protocols-devel
+ # HACK: remove npm once we switch back to hotdoc sdist
+ nodejs-npm
)
# Sys update
@@ -24,7 +26,12 @@ dnf -y upgrade
# Install deps
dnf -y install "${pkgs[@]}"
-install_python_packages hotdoc
+# HACK: build hotdoc from git repo since current sdist is broken on modern compilers
+# change back to 'hotdoc' once it's fixed
+install_python_packages git+https://github.com/hotdoc/hotdoc
+
+# HACK: uninstall npm after building hotdoc, remove when we remove npm
+dnf -y remove nodejs-npm
# Cleanup
dnf -y clean all
diff --git a/ci/ciimage/gentoo/image.json b/ci/ciimage/gentoo/image.json
index e59eee5..f7ecd64 100644
--- a/ci/ciimage/gentoo/image.json
+++ b/ci/ciimage/gentoo/image.json
@@ -1,5 +1,5 @@
{
- "base_image": "gentoo/stage3:desktop",
+ "base_image": "gentoo/stage3:latest",
"env": {
"CI": "1",
"MESON_CI_JOBNAME": "linux-gentoo-gcc",
diff --git a/ci/ciimage/gentoo/install.sh b/ci/ciimage/gentoo/install.sh
index 30b0299..c1e15ef 100755
--- a/ci/ciimage/gentoo/install.sh
+++ b/ci/ciimage/gentoo/install.sh
@@ -23,12 +23,19 @@ pkgs_stable=(
dev-util/bindgen
dev-libs/elfutils
+ dev-libs/protobuf
+
+ # modules
dev-util/gdbus-codegen
+ dev-util/glib-utils
dev-libs/gobject-introspection
dev-util/itstool
- dev-libs/protobuf
+ dev-util/wayland-scanner
+ dev-libs/wayland-protocols
+ dev-libs/wayland
# custom deps
+ dev-libs/boost
net-libs/libpcap
dev-util/gtk-doc
media-libs/libwmf
@@ -38,14 +45,22 @@ pkgs_stable=(
dev-cpp/gtest
sci-libs/hdf5
dev-qt/linguist-tools
- sys-devel/llvm
+ dev-qt/qtwidgets:5
+ llvm-core/llvm
+ dev-qt/qtdeclarative:6
dev-qt/qttools
+ net-print/cups
+ dev-util/vulkan-headers
+ media-libs/vulkan-loader
# misc
app-admin/sudo
app-text/doxygen
sys-devel/bison
+ sys-devel/reflex
sys-devel/gettext
+ # needed by vala
+ x11-libs/gtk+
# TODO: vulkan-validation-layers
# TODO: cuda
@@ -58,21 +73,14 @@ pkgs_stable=(
#dev-libs/wayland
#dev-libs/wayland-protocols
#dev-python/pypy3
- #dev-qt/qtbase:6
- #dev-qt/qtcore:5
- #dev-qt/qttools:6
#dev-vcs/mercurial
#gnustep-base/gnustep-base
#media-gfx/graphviz
#sci-libs/netcdf-fortran
- #sys-devel/clang
+ #llvm-core/clang
#x11-libs/gtk+:3
)
pkgs_latest=(
- # ~arch boost needed for py3.12 for now (needs 1.84)
- dev-build/b2
- dev-libs/boost
-
dev-build/autoconf
dev-build/automake
@@ -94,7 +102,6 @@ printf "%s\n" ${pkgs_latest[@]} >> /etc/portage/package.accept_keywords/meson
cat /etc/portage/package.accept_keywords/meson
cat <<-EOF > /etc/portage/package.accept_keywords/misc
- dev-lang/python-exec
dev-lang/python
EOF
@@ -102,13 +109,21 @@ mkdir /etc/portage/binrepos.conf || true
mkdir /etc/portage/profile || true
cat <<-EOF > /etc/portage/package.use/ci
dev-cpp/gtkmm X
+ media-libs/libglvnd X
+ media-libs/freetype harfbuzz
+ x11-libs/cairo X
+ x11-libs/libxkbcommon X
dev-lang/rust clippy rustfmt
dev-lang/rust-bin clippy rustfmt
dev-libs/boost python
+ sci-libs/hdf5 cxx
+
+ # slimmed binpkg, nomesa
+ media-libs/libsdl2 -opengl -wayland -alsa -dbus -gles2 -udev -vulkan
# Some of these settings are needed just to get the binpkg but
# aren't negative to have anyway
- sys-devel/gcc ada d
+ sys-devel/gcc ada d jit
>=sys-devel/gcc-13 ada objc objc++
sys-devel/gcc pgo lto
@@ -133,17 +148,20 @@ cat <<-EOF >> /etc/portage/make.conf
FEATURES="\${FEATURES} -ipc-sandbox -network-sandbox -pid-sandbox"
EOF
-# TODO: Enable all Pythons / add multiple jobs with diff. Python impls?
+# Maybe we could enable all Pythons / add multiple jobs with diff. Python impls?
#echo '*/* PYTHON_TARGETS: python3_10 python3_11 python3_12' >> /etc/portage/package.use/python
-echo '*/* PYTHON_TARGETS: python3_12' >> /etc/portage/package.use/python
-cat <<-EOF >> /etc/portage/profile/use.mask
--python_targets_python3_12
--python_single_target_python3_12
-EOF
-cat <<-EOF >> /etc/portage/profile/use.stable.mask
--python_targets_python3_12
--python_single_target_python3_12
-EOF
+
+# The below is for cases where we want non-default Python (either to get
+# better coverage from something older, or something newer)
+#echo '*/* PYTHON_TARGETS: python3_12' >> /etc/portage/package.use/python
+#cat <<-EOF >> /etc/portage/profile/use.mask
+#-python_targets_python3_12
+#-python_single_target_python3_12
+#EOF
+#cat <<-EOF >> /etc/portage/profile/use.stable.mask
+#-python_targets_python3_12
+#-python_single_target_python3_12
+#EOF
echo 'dev-lang/python ensurepip' >> /etc/portage/package.use/python
@@ -164,6 +182,15 @@ python3 -m pip install "${base_python_pkgs[@]}"
echo "source /etc/profile" >> /ci/env_vars.sh
+# For inexplicable reasons, Gentoo only packages valac as valac-$(version) so
+# no software can locate it. Parse the installed version out of portage and
+# export it to meson.
+VALA_VER=$(portageq best_version / dev-lang/vala)
+VALA_VER=${VALA_VER#dev-lang/vala-}
+VALA_VER=${VALA_VER%.*}
+echo "export VALAC=/usr/bin/valac-${VALA_VER}" >> /ci/env_vars.sh
+echo "export VAPIGEN=/usr/bin/vapigen-${VALA_VER}" >> /ci/env_vars.sh
+
# Cleanup to avoid including large contents in the docker image.
# We don't need cache files that are side artifacts of installing packages.
# We also don't need the gentoo tree -- the official docker image doesn't
diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh
index 7a76071..7adb3fd 100755
--- a/ci/ciimage/opensuse/install.sh
+++ b/ci/ciimage/opensuse/install.sh
@@ -16,9 +16,11 @@ pkgs=(
qt6-declarative-devel qt6-base-devel qt6-tools qt6-tools-linguist qt6-declarative-tools qt6-core-private-devel
libwmf-devel valgrind cmake nasm gnustep-base-devel gettext-tools gettext-runtime gettext-csharp ncurses-devel
libxml2-devel libxslt-devel libyaml-devel glib2-devel json-glib-devel
- boost-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_system-devel
- libboost_test-devel libboost_log-devel libboost_regex-devel
+ boost-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel
+ libboost_headers-devel libboost_test-devel libboost_log-devel libboost_regex-devel
libboost_python3-devel libboost_regex-devel
+ # HACK: remove npm once we switch back to hotdoc sdist
+ npm
)
# Sys update
@@ -27,7 +29,12 @@ zypper --non-interactive update
# Install deps
zypper install -y "${pkgs[@]}"
-install_python_packages hotdoc
+# HACK: build hotdoc from git repo since current sdist is broken on modern compilers
+# change back to 'hotdoc' once it's fixed
+install_python_packages git+https://github.com/hotdoc/hotdoc
+
+# HACK: uninstall npm after building hotdoc, remove when we remove npm
+zypper remove -y -u npm
echo 'export PKG_CONFIG_PATH="/usr/lib64/mpi/gcc/openmpi3/lib64/pkgconfig:$PKG_CONFIG_PATH"' >> /ci/env_vars.sh
diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh
index 1c0891c..3460be4 100755
--- a/ci/ciimage/ubuntu-rolling/install.sh
+++ b/ci/ciimage/ubuntu-rolling/install.sh
@@ -43,7 +43,9 @@ eatmydata apt-get -y build-dep meson
eatmydata apt-get -y install "${pkgs[@]}"
eatmydata apt-get -y install --no-install-recommends wine-stable # Wine is special
-install_python_packages hotdoc
+# HACK: build hotdoc from git repo since current sdist is broken on modern compilers
+# change back to 'hotdoc' once it's fixed
+install_python_packages git+https://github.com/hotdoc/hotdoc
# Lower ulimit before running dub, otherwise there's a very high chance it will OOM.
# See: https://github.com/dlang/phobos/pull/9048 and https://github.com/dlang/phobos/pull/8990
@@ -69,7 +71,7 @@ rustup target add arm-unknown-linux-gnueabihf
# Use the GitHub API to get the latest release information
LATEST_RELEASE=$(wget -qO- "https://api.github.com/repos/ziglang/zig/releases/latest")
ZIGVER=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
-ZIG_BASE="zig-linux-x86_64-$ZIGVER"
+ZIG_BASE="zig-x86_64-linux-$ZIGVER"
wget "https://ziglang.org/download/$ZIGVER/$ZIG_BASE.tar.xz"
tar xf "$ZIG_BASE.tar.xz"
rm -rf "$ZIG_BASE.tar.xz"
diff --git a/ci/combine_cov.sh b/ci/combine_cov.sh
deleted file mode 100755
index 99a503b..0000000
--- a/ci/combine_cov.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-echo "Combining coverage reports..."
-coverage combine
-
-echo "Generating XML report..."
-coverage xml
-
-echo "Printing report"
-coverage report
diff --git a/ci/coverage.ps1 b/ci/coverage.ps1
deleted file mode 100644
index ebd7cd4..0000000
--- a/ci/coverage.ps1
+++ /dev/null
@@ -1,14 +0,0 @@
-echo ""
-echo ""
-echo "=== Gathering coverage report ==="
-echo ""
-
-python3 -m coverage combine
-python3 -m coverage xml
-python3 -m coverage report
-
-# Currently codecov.py does not handle Azure, use this fork of a fork to get it
-# working without requiring a token
-git clone https://github.com/mensinda/codecov-python
-python3 -m pip install --ignore-installed ./codecov-python
-python3 -m codecov -f .coverage/coverage.xml -n "VS$env:compiler $env:arch $env:backend" -c $env:SOURCE_VERSION
diff --git a/ci/run.ps1 b/ci/run.ps1
index d781cf7..db684ac 100644
--- a/ci/run.ps1
+++ b/ci/run.ps1
@@ -49,7 +49,7 @@ function DownloadFile([String] $Source, [String] $Destination) {
if (($env:backend -eq 'ninja') -and ($env:arch -ne 'arm64')) { $dmd = $true } else { $dmd = $false }
-DownloadFile -Source https://github.com/mesonbuild/cidata/releases/download/ci5/ci_data.zip -Destination $env:AGENT_WORKFOLDER\ci_data.zip
+DownloadFile -Source https://github.com/mesonbuild/cidata/releases/download/ci7/ci_data.zip -Destination $env:AGENT_WORKFOLDER\ci_data.zip
echo "Extracting ci_data.zip"
Expand-Archive $env:AGENT_WORKFOLDER\ci_data.zip -DestinationPath $env:AGENT_WORKFOLDER\ci_data
& "$env:AGENT_WORKFOLDER\ci_data\install.ps1" -Arch $env:arch -Compiler $env:compiler -Boost $true -DMD $dmd
@@ -92,7 +92,7 @@ python --version
# Needed for running unit tests in parallel.
echo ""
-python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist pytest-subtests fastjsonschema coverage
+python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist pytest-subtests fastjsonschema
# Needed for running the Cython tests
python -m pip --disable-pip-version-check install cython
@@ -102,6 +102,6 @@ echo "=== Start running tests ==="
# Starting from VS2019 Powershell(?) will fail the test run
# if it prints anything to stderr. Python's test runner
# does that by default so we need to forward it.
-cmd /c "python 2>&1 ./tools/run_with_cov.py run_tests.py --backend $env:backend $env:extraargs"
+cmd /c "python 2>&1 run_tests.py --backend $env:backend $env:extraargs"
exit $LastExitCode
diff --git a/ci/usercustomize.py b/ci/usercustomize.py
deleted file mode 100644
index d72c6ad..0000000
--- a/ci/usercustomize.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-# Copyright 2021 The Meson development team
-
-import coverage
-coverage.process_startup()
diff --git a/cross/cuda-cross.json b/cross/cuda-cross.json
new file mode 100644
index 0000000..f2d0086
--- /dev/null
+++ b/cross/cuda-cross.json
@@ -0,0 +1,5 @@
+{
+ "file": "cuda-cross.txt",
+ "tests": ["cuda"],
+ "env": {}
+}
diff --git a/cross/cuda-cross.txt b/cross/cuda-cross.txt
new file mode 100644
index 0000000..7e81463
--- /dev/null
+++ b/cross/cuda-cross.txt
@@ -0,0 +1,17 @@
+[binaries]
+c = ['/usr/bin/aarch64-linux-gnu-gcc']
+cpp = ['/usr/bin/aarch64-linux-gnu-g++']
+cuda = ['/usr/local/cuda/bin/nvcc']
+ar = '/usr/bin/aarch64-linux-gnu-ar'
+strip = '/usr/bin/aarch64-linux-gnu-strip'
+ld = '/usr/bin/aarch64-linux-gnu-ld'
+
+[host_machine]
+system = 'linux'
+cpu_family = 'aarch64'
+cpu = 'aarch64'
+endian = 'little'
+
+[built-in options]
+cuda_link_args = ['-lstdc++']
+cuda_ccbindir = '/usr/bin/aarch64-linux-gnu-gcc'
diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt
index 6409e39..97a1c21 100644
--- a/cross/ubuntu-armhf.txt
+++ b/cross/ubuntu-armhf.txt
@@ -4,6 +4,7 @@
c = ['/usr/bin/arm-linux-gnueabihf-gcc']
cpp = ['/usr/bin/arm-linux-gnueabihf-g++']
rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7']
+rustdoc = ['rustdoc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7']
ar = '/usr/arm-linux-gnueabihf/bin/ar'
strip = '/usr/arm-linux-gnueabihf/bin/strip'
pkg-config = '/usr/bin/arm-linux-gnueabihf-pkg-config'
diff --git a/cross/xc32.txt b/cross/xc32.txt
new file mode 100644
index 0000000..dd3d026
--- /dev/null
+++ b/cross/xc32.txt
@@ -0,0 +1,17 @@
+# This file assumes that path to the Microchip XC32 toolchain is added
+# to the environment(PATH) variable, so that Meson can find XC32 while building.
+
+[binaries]
+c = 'xc32-gcc'
+cpp = 'xc32-g++'
+ar = 'xc32-ar'
+bin2hex = 'xc32-bin2hex'
+
+[host_machine]
+system = 'baremetal'
+cpu_family = 'pic32'
+cpu = 'pic32'
+endian = 'little'
+
+[properties]
+needs_exe_wrapper = true
diff --git a/data/macros.meson b/data/macros.meson
index 5977410..c2d1adc 100644
--- a/data/macros.meson
+++ b/data/macros.meson
@@ -29,14 +29,14 @@
%{shrink:%{__meson} compile \
-C %{_vpath_builddir} \
-j %{_smp_build_ncpus} \
- %{?__meson_verbose:--verbose} \
+ %[ 0%{?__meson_verbose} ? "--verbose" : "" ] \
%{nil}}
%meson_install \
%{shrink:DESTDIR=%{buildroot} %{__meson} install \
-C %{_vpath_builddir} \
--no-rebuild \
- %{!?__meson_verbose:--quiet} \
+ %[ ! 0%{?__meson_verbose} ? "--quiet" : "" ] \
%{nil}}
%meson_test \
diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim
index a1679e0..905e9fe 100644
--- a/data/syntax-highlighting/vim/syntax/meson.vim
+++ b/data/syntax-highlighting/vim/syntax/meson.vim
@@ -27,7 +27,7 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-" http://mesonbuild.com/Syntax.html
+" https://mesonbuild.com/Syntax.html
syn keyword mesonConditional elif else if endif
syn keyword mesonRepeat foreach endforeach
syn keyword mesonOperator and not or in
@@ -54,7 +54,7 @@ syn match mesonEscape "\\N{\a\+\%(\s\a\+\)*}" contained
syn match mesonEscape "\\$"
" Meson only supports integer numbers
-" http://mesonbuild.com/Syntax.html#numbers
+" https://mesonbuild.com/Syntax.html#numbers
syn match mesonNumber "\<\d\+\>"
syn match mesonNumber "\<0x\x\+\>"
syn match mesonNumber "\<0o\o\+\>"
diff --git a/docs/extensions/refman_links.py b/docs/extensions/refman_links.py
index 5c22a0a..865668b 100644
--- a/docs/extensions/refman_links.py
+++ b/docs/extensions/refman_links.py
@@ -1,11 +1,13 @@
from pathlib import Path
from json import loads
+import os
import re
from hotdoc.core.exceptions import HotdocSourceException
from hotdoc.core.extension import Extension
from hotdoc.core.tree import Page
from hotdoc.core.project import Project
+from hotdoc.core.symbols import *
from hotdoc.run_hotdoc import Application
from hotdoc.core.formatter import Formatter
from hotdoc.utils.loggable import Logger, warn, info
@@ -52,6 +54,35 @@ class RefmanLinksExtension(Extension):
with valid links to the correct URL. To reference objects / types use the
[[@object]] syntax.
'''
+ for key, value in self._data.items():
+ path = os.path.relpath(value, self.app.config.get_invoke_dir()).split('#')[0]
+ if path == page.link.ref:
+ if key.startswith('@'):
+ res = self.create_symbol(
+ ClassSymbol,
+ display_name=key[1:],
+ filename=path,
+ unique_name=key)
+ res.link = Link(value, res.display_name, res.unique_name)
+ elif '.' in key:
+ res = self.create_symbol(
+ MethodSymbol,
+ parameters=[],
+ display_name=key.split('.')[-1],
+ parent_name=f'@{key.split(".")[-2]}',
+ filename=path,
+ unique_name=key)
+ res.link = Link(value, key, res.unique_name)
+ else:
+ res = self.create_symbol(
+ FunctionSymbol,
+ parameters=[],
+ display_name=key,
+ filename=path,
+ unique_name=key)
+ res.link = Link(value, res.display_name, res.unique_name)
+ page.symbols.append(res)
+
link_regex = re.compile(r'(\[\[#?@?([ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\.)*[ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\]\])(.)?', re.MULTILINE)
for m in link_regex.finditer(page.formatted_contents):
i = m.group(1)
@@ -103,6 +134,10 @@ class RefmanLinksExtension(Extension):
ext.formatter.formatting_page_signal.connect(self._formatting_page_cb)
info('Meson refman extension LOADED')
+ def create_symbol(self, *args, **kwargs):
+ kwargs['language'] = 'meson'
+ return super(RefmanLinksExtension, self).create_symbol(*args, **kwargs)
+
@staticmethod
def get_dependencies() -> T.List[T.Type[Extension]]:
return [] # In case this extension has dependencies on other extensions
diff --git a/docs/markdown/Adding-new-projects-to-wrapdb.md b/docs/markdown/Adding-new-projects-to-wrapdb.md
index 7c28b49..e690a8b 100644
--- a/docs/markdown/Adding-new-projects-to-wrapdb.md
+++ b/docs/markdown/Adding-new-projects-to-wrapdb.md
@@ -51,6 +51,37 @@ If the project name is too generic or ambiguous (e.g. `benchmark`),
consider using `organization-project` naming format (e.g.
`google-benchmark`).
+## Overriding dependencies in the submitted project
+
+Ideally the project you submit should make a call to `meson.override_dependency`
+for each dependency you would like to expose, with the first argument
+matching the pkg-config file name. This abstracts away the
+need to know and keep track of the variable names downstream.
+
+For instance, the Apache Arrow project exposes multiple dependencies like
+its base `arrow` library, along with an `arrow-compute` library. The
+project generates `arrow.pc` and `arrow-compute.pc` files for pkg-config
+respectively, so internally the project also calls:
+
+```meson
+arrow_dep = declare_dependency(...)
+meson.override_dependency('arrow', arrow_dep)
+
+arrow_compute_dep = declare_dependency(...)
+meson.override_dependency('arrow-compute', arrow_compute_dep)
+```
+Note that `meson.override_dependency` was introduced in Meson version
+0.54.0. If your project supports older versions of Meson, you should
+add a condition to only call that function in versions where it is
+available:
+
+```meson
+if meson.version().version_compare('>=0.54.0')
+ meson.override_dependency('arrow', arrow_dep)
+ meson.override_dependency('arrow-compute', arrow_compute_dep)
+endif
+```
+
## How to contribute a new wrap
If the project already uses Meson build system, then only a wrap file
@@ -83,22 +114,41 @@ shasum -a 256 path/to/libfoo-1.0.0.tar.gz
Next you need to add the entries that define what dependencies the
current project provides. This is important, as it is what makes
-Meson's automatic dependency resolver work. It is done by adding a
-`provide` entry at the end of the `upstream.wrap` file. Using the Ogg
-library as an example, this is what it would look like:
+Meson's automatic dependency resolver work.
+
+Assuming the project that you are creating
+a wrap file for has called `meson.override_dependency`, then you
+can declare those overridden dependencies in the `provide` section
+of the wrap file:
+
+```ini
+[provide]
+dependency_names = arrow, arrow_compute
+```
+
+In the case that you do not control the upstream Meson configuration
+and it does not already make a call to `meson.override_dependency`,
+then you can still expose dependency variables in the wrap file, using
+a syntax like:
```ini
[provide]
-ogg = ogg_dep
+arrow = arrow_dep
+arrow_compute = arrow_compute_dep
```
-The `ogg` part on the left refers to the dependency name, which should
-be the same as its Pkg-Config name. `ogg_dep` on the right refers to
-the variable in the build definitions that provides the dependency.
-Most commonly it holds the result of a `declare_dependency` call. If a
-variable of that name is not defined, Meson will exit with a hard
-error. For further details see [the main Wrap
-manual](Wrap-dependency-system-manual.md).
+The `arrow` and `arrow_compute` parts on the left refer to the dependency
+names, which should be the same as their Pkg-Config name. `arrow_dep` and
+`arrow_compute_dep` on the right refer to the variables in the build
+definition that provide the dependencies. Most commonly, they hold the
+result of a `declare_dependency` call. If a variable of that name is
+not defined, Meson will exit with a hard error. For further details see
+[the main Wrap manual](Wrap-dependency-system-manual.md).
+
+However, it is strongly advised in such cases to request that the upstream
+repository use `meson.override_dependency` for its next release, so that
+the variable names chosen in the upstream configuration file can be
+decoupled from the wrap file contents.
Now you can create the build files, if the upstream project does not
contain any, and work on them until the project builds correctly.
diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md
index 190705d..ee12f55 100644
--- a/docs/markdown/Build-options.md
+++ b/docs/markdown/Build-options.md
@@ -244,6 +244,10 @@ project which also has an option called `some_option`, then calling
`yield` is `false`, `get_option` returns the value of the subproject's
option.
+*Since 1.8.0* `-Dsub:some_option=anothervalue`, when used with a
+yielding option, sets the value separately from the option
+it yields to.
+
## Built-in build options
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index faf7a60..ee576c3 100644
--- a/docs/markdown/Builtin-options.md
+++ b/docs/markdown/Builtin-options.md
@@ -72,31 +72,33 @@ Options that are labeled "per machine" in the table are set per
machine. See the [specifying options per
machine](#specifying-options-per-machine) section for details.
-| Option | Default value | Description | Is per machine | Is per subproject |
-| -------------------------------------- | ------------- | ----------- | -------------- | ----------------- |
+| Option | Default value | Description | Is per machine | Per subproject (since) |
+| -------------------------------------- | ------------- | ----------- | -------------- | ---------------------- |
| auto_features {enabled, disabled, auto} | auto | Override value of all 'auto' features | no | no |
| backend {ninja, vs,<br>vs2010, vs2012, vs2013, vs2015, vs2017, vs2019, vs2022, xcode, none} | ninja | Backend to use | no | no |
| genvslite {vs2022} | vs2022 | Setup multi-buildtype ninja build directories and Visual Studio solution | no | no |
-| buildtype {plain, debug,<br>debugoptimized, release, minsize, custom} | debug | Build type to use | no | no |
-| debug | true | Enable debug symbols and other information | no | no |
-| default_both_libraries {shared, static, auto} | shared | Default library type for both_libraries | no | no |
-| default_library {shared, static, both} | shared | Default library type | no | yes |
+| buildtype {plain, debug,<br>debugoptimized, release, minsize, custom} | debug | Build type to use | no | 1.8.0 |
+| debug | true | Enable debug symbols and other information | no | 1.8.0 |
+| default_both_libraries {shared, static, auto} | shared | Default library type for both_libraries | no | 1.8.0 |
+| default_library {shared, static, both} | shared | Default library type | no | 0.54.0 |
| errorlogs | true | Whether to print the logs from failing tests. | no | no |
| install_umask {preserve, 0000-0777} | 022 | Default umask to apply on permissions of installed files | no | no |
| layout {mirror,flat} | mirror | Build directory layout | no | no |
-| optimization {plain, 0, g, 1, 2, 3, s} | 0 | Optimization level | no | no |
+| namingscheme {platform, classic} | classic | Library naming scheme to use | no | 1.10.0 |
+| optimization {plain, 0, g, 1, 2, 3, s} | 0 | Optimization level | no | 1.8.0 |
| pkg_config_path {OS separated path} | '' | Additional paths for pkg-config to search before builtin paths | yes | no |
| prefer_static | false | Whether to try static linking before shared linking | no | no |
| cmake_prefix_path | [] | Additional prefixes for cmake to search before builtin paths | yes | no |
| stdsplit | true | Split stdout and stderr in test logs | no | no |
-| strip | false | Strip targets on install | no | no |
-| unity {on, off, subprojects} | off | Unity build | no | no |
-| unity_size {>=2} | 4 | Unity file block size | no | no |
-| warning_level {0, 1, 2, 3, everything} | 1 | Set the warning level. From 0 = compiler default to everything = highest | no | yes |
-| werror | false | Treat warnings as errors | no | yes |
+| strip | false | Strip targets on install | no | 1.8.0 |
+| unity {on, off, subprojects} | off | Unity build | no | 1.8.0 |
+| unity_size {>=2} | 4 | Unity file block size | no | 1.8.0 |
+| warning_level {0, 1, 2, 3, everything} | 1 | Set the warning level. From 0 = compiler default to everything = highest | no | 0.56.0 |
+| werror | false | Treat warnings as errors | no | 0.54.0 |
| wrap_mode {default, nofallback,<br>nodownload, forcefallback, nopromote} | default | Wrap mode to use | no | no |
| force_fallback_for | [] | Force fallback for those dependencies | no | no |
| vsenv | false | Activate Visual Studio environment | no | no |
+| os2_emxomf | false | Use OMF format on OS/2 | no | no |
(For the Rust language only, `warning_level=0` disables all warnings).
@@ -150,6 +152,10 @@ the two-way mapping:
All other combinations of `debug` and `optimization` set `buildtype` to `'custom'`.
+Note that `-Ddebug=false` does not cause the compiler preprocessor macro
+`NDEBUG` to be defined.
+The macro can be defined using the base option `b_ndebug`, described below.
+
#### Details for `warning_level`
Exact flags per warning level is compiler specific, but there is an approximate
@@ -198,6 +204,16 @@ When `default_both_libraries` is 'auto', passing a [[@both_libs]] dependency
in [[both_libraries]] will link the static dependency with the static lib,
and the shared dependency with the shared lib.
+#### Details for `os2_emxomf`
+
+The `--os2-emxomf` argument is supported since `1.10.0`, `-Dos2_emxomf=true`
+syntax is supported since `1.10.0`.
+
+Setting the `os2_emxomf` option to `true` forces to use emxomf toolchains in
+order to generate OMF files instead of aout toolchains.
+
+`os2_emxomf` is `false` by default.
+
## Base options
These are set in the same way as universal options, either by
@@ -298,7 +314,10 @@ or compiler being used:
| cpp_rtti | true | true, false | Whether to enable RTTI (runtime type identification) |
| cpp_thread_count | 4 | integer value ≥ 0 | Number of threads to use with emcc when using threads |
| cpp_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against |
+| cpp_importstd | false | true or false | Whether to use `import std` |
| fortran_std | none | [none, legacy, f95, f2003, f2008, f2018] | Fortran language standard to use |
+| rust_dynamic_std | false | true, false | Whether to link dynamically to the Rust standard library *(Added in 1.9.0)* |
+| rust_nightly | auto | enabled, disabled, auto | Nightly Rust compiler (enabled=required, disabled=don't use nightly feature, auto=use nightly feature if available) *(Added in 1.10.0)* |
| cuda_ccbindir | | filesystem path | CUDA non-default toolchain directory to use (-ccbin) *(Added in 0.57.1)* |
The default values of `c_winlibs` and `cpp_winlibs` are in
@@ -366,11 +385,10 @@ allowing differences in behavior to crop out.
## Specifying options per subproject
-Since *0.54.0* `default_library` and `werror` built-in options can be
-defined per subproject. This is useful, for example, when building
-shared libraries in the main project and statically linking a subproject,
-or when the main project must build with no warnings but some subprojects
-cannot.
+Several built-in options and all compiler options can be defined per subproject.
+This is useful, for example, when building shared libraries in the main project
+and statically linking a subproject, or when the main project must build
+with no warnings but some subprojects cannot.
Most of the time, this would be used either in the parent project by
setting subproject's default_options (e.g. `subproject('foo',
@@ -378,12 +396,35 @@ default_options: 'default_library=static')`), or by the user through the
command line: `-Dfoo:default_library=static`.
The value is overridden in this order:
-- Value from parent project
-- Value from subproject's default_options if set
-- Value from subproject() default_options if set
-- Value from command line if set
-
-Since *0.56.0* `warning_level` can also be defined per subproject.
+- `opt=value` from parent project's `default_options`
+- `opt=value` from subproject's `default_options`
+- `opt=value` from machine file
+- `opt=value` from command line
+- `subp:opt=value` from parent project's default options
+- `opt=value` from `subproject()` `default_options`
+- `subp:opt=value` from machine file
+- `subp:opt=value` from command line
+
+### Old behavior
+
+Between *0.54.0* and *1.7.x* only a few options could be defined per subproject:
+* `default_library` and `werror` since *0.54.0*;
+* `warning_level` since *0.56.0*;
+* compiler options since *0.63.0*
+
+The value was overridden in this order:
+
+- `opt=value` from parent project's `default_options`
+- `opt=value` from machine file
+- `opt=value` from command line
+- `opt=value` from subproject's `default_options`
+- `subp:opt=value` from parent project's default options
+- `opt=value` from `subproject()` `default_options`
+- `subp:opt=value` from machine file
+- `subp:opt=value` from command line
+
+In other word, the subproject's `default_options` had a *higher* priority
+than `opt=value` from machine file or command line.
## Module options
@@ -422,6 +463,7 @@ install prefix. For example: if the install prefix is `/usr` and the
| platlibdir | | Directory path | Directory for site-specific, platform-specific files (Since 0.60.0) |
| purelibdir | | Directory path | Directory for site-specific, non-platform-specific files (Since 0.60.0) |
| allow_limited_api | true | true, false | Disables project-wide use of the Python Limited API (Since 1.3.0) |
+| build_config | | File path | Specifies the Python build configuration file (PEP 739) (Since 1.10.0) |
*Since 0.60.0* The `python.platlibdir` and `python.purelibdir` options are used
by the python module methods `python.install_sources()` and
diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md
index f8275c9..982fa35 100644
--- a/docs/markdown/CMake-module.md
+++ b/docs/markdown/CMake-module.md
@@ -138,8 +138,8 @@ and supports the following methods:
`include_type` kwarg *(new in 0.56.0)* controls the include type of the
returned dependency object similar to the same kwarg in the
[[dependency]] function.
- - `include_directories(target)` returns a Meson [[@inc]]
- object for the specified target. Using this method is not necessary
+ - `include_directories(target)` returns an array of Meson [[@inc]]
+ objects for the specified target. Using this method is not necessary
if the dependency object is used.
- `target(target)` returns the raw build target.
- `target_type(target)` returns the type of the target as a string
diff --git a/docs/markdown/Codegen-module.md b/docs/markdown/Codegen-module.md
new file mode 100644
index 0000000..0f6d524
--- /dev/null
+++ b/docs/markdown/Codegen-module.md
@@ -0,0 +1,144 @@
+---
+short-description: Common Code Generators Module
+authors:
+ - name: Dylan Baker
+ email: dylan@pnwbakers.com
+ years: [2024, 2025]
+...
+
+# Codegen Module
+
+*(New in 1.10.0)*
+
+This module provides wrappers around common code generators, such as flex/lex and yacc/bison. This purpose of this is to make it easier and more pleasant to use *common* code generators in a mesonic way.
+
+## Functions
+
+### lex()
+
+```meson
+lex_gen = codegen.lex(implementations : ['flex', 'reflex'], flex_version : ['>= 2.6', '< 3'], reflex_version : '!= 1.4.2')
+```
+
+This function provides fine grained control over what implementation(s) and version(s) of lex are acceptable for a given project (These are set per-subproject). It returns a [new object](#lexgenerator), which can be used to generate code.
+
+It accepts the following keyword arguments:
+
+ - `implementations`: a string array of acceptable implementations to use. May include: `lex`, `flex`, `reflex`, or `win_flex`.
+ - `lex_version`: a string array of version constraints to apply to the `lex` binary
+ - `flex_version`: a string array of version constraints to apply to the `flex` binary
+ - `reflex_version`: a string array of version constraints to apply to the `relex` binary
+ - `win_flex_version`: a string array of version constraints to apply to the `win_flex` binary
+ - `required`: A boolean or feature option
+ - `disabler`: Return a disabler if not found
+ - `native`: Is this generator for the host or build machine?
+
+### yacc()
+
+```meson
+yacc = codegen.yacc(implementations : ['bison', 'win_bison'])
+```
+
+This function provides fine grained controls over which implementation(s) and version(s) of the parser generator to use.
+
+Accepts the following keyword arguments:
+
+ - `implementations`: a string array of acceptable implementations to use. May include: `yacc`, `byacc`, `bison`, or `win_bison`.
+ - `yacc_version`: a string array of version constraints to apply to the `yacc` binary
+ - `byacc_version`: a string array of version constraints to apply to the `byacc` binary
+ - `bison_version`: a string array of version constraints to apply to the `bison` binary
+ - `win_bison_version`: a string array of version constraints to apply to the `win_bison` binary
+ - `required`: A boolean or feature option
+ - `disabler`: Return a disabler if not found
+ - `native`: Is this generator for the host or build machine?
+
+## Returned Objects
+
+### LexGenerator
+
+#### lex.implementation
+
+```meson
+lex = codegen.lex()
+impl = lex.implementation()
+```
+
+Returns the string name of the lex implementation chosen. May be one of:
+
+- lex
+- flex
+- reflex
+- win_flex
+
+#### lex.generate
+
+```meson
+lex = codegen.lex()
+lex.generate('lexer.l')
+```
+
+This function wraps flex, lex, reflex (but not RE/flex), and win_flex (on Windows). When using win_flex it will automatically add the `--wincompat` argument.
+
+This requires an input file, which may be a string, File, or generated source. It additionally takes the following options keyword arguments:
+
+- `args`: An array of extra arguments to pass the lexer
+- `plainname`: If set to true then `@PLAINNAME@` will be used as the source base, otherwise `@BASENAME@`.
+- `source`: the name of the source output. If this is unset Meson will use `{base}.{ext}` with an extension of `cpp` if the input has an extension of `.ll`, or `c` otherwise, with base being determined by the `plainname` argument.
+- `header`: The optional output name for a header file. If this is unset no header is added
+- `table`: The optional output name for a table file. If this is unset no table will be generated
+
+The outputs will be in the form `source [header] [table]`, which means those can be accessed by indexing the output of the `lex` call:
+
+```meson
+lex = codegen.lex()
+l1 = lex.generate('lexer.l', header : '@BASENAME@.h', table : '@BASENAME@.tab.h')
+headers = [l1[1], l1[2]] # [header, table]
+
+l2 = lex.generate('lexer.l', table : '@BASENAME@.tab.h')
+table = l2[1]
+```
+
+### YaccGenerator
+
+#### yacc.implementation
+
+```meson
+yacc = codegen.yacc()
+impl = yacc.implementation()
+```
+
+Returns the string name of the yacc implementation chosen. May be one of:
+
+- yacc
+- bison
+- byacc
+- win_bison
+
+#### yacc.generate
+
+```meson
+yacc = codegen.yacc()
+yacc.generate('parser.y')
+```
+
+This function wraps bison, yacc, byacc, and win_bison (on Windows), and attempts to abstract away the differences between them
+
+This requires an input file, which may be a string, File, or generated source. It additionally takes the following options keyword arguments:
+
+- `version`: Version constraints on the lexer
+- `args`: An array of extra arguments to pass the lexer
+- `plainname`: If set to true then `@PLAINNAME@` will be used as the source base, otherwise `@BASENAME@`.
+- `source`: the name of the source output. If this is unset Meson will use `{base}.{ext}` with an extension of `cpp` if the input has an extension of `.yy` or `c` otherwise, with base being determined by the `plainname` argument.
+- `header`: the name of the header output. If this is unset Meson will use `{base}.{ext}` with an extension of `hpp` if the input has an extension of `.yy` or `h` otherwise, with base being determined by the `plainname` argument.
+- `locations`: The name of the locations file, if one is generated. Due to the way yacc works this must be duplicated in the file and in the command.
+
+The outputs will be in the form `source header [locations]`, which means those can be accessed by indexing the output of the `yacc` call:
+
+```meson
+yacc = codegen.yacc()
+p1 = yacc.generate('parser.y', header : '@BASENAME@.h', locations : 'locations.h')
+headers = [p1[1], p1[2]] # [header, locations]
+
+p2 = yacc.generate('parser.yy', locations : 'locations.hpp')
+locations = p2[1]
+```
diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md
index 247f2d7..339dd93 100644
--- a/docs/markdown/Commands.md
+++ b/docs/markdown/Commands.md
@@ -493,6 +493,12 @@ or `--check-only` option).
input instead of reading it from a file. This cannot be used with `--recursive`
or `--inline` arguments.
+*Since 1.9.0* Using `-` as source file with `--editor-config` now requires
+`--source-file-path` argument to ensure consistent results.
+
+*Since 1.10.0* When `--check-diff` is specified, instead of silently exiting
+with an error code, `meson format` will print a diff of the formatting changes.
+
#### Differences with `muon fmt`
diff --git a/docs/markdown/Creating-releases.md b/docs/markdown/Creating-releases.md
index 44c127e..7b97aec 100644
--- a/docs/markdown/Creating-releases.md
+++ b/docs/markdown/Creating-releases.md
@@ -104,7 +104,7 @@ following example.
`meson.build`:
```meson
project('tig', 'c',
- version : run_command('version.sh', 'get-vcs').stdout.strip())
+ version : run_command('version.sh', 'get-vcs').stdout().strip())
meson.add_dist_script('version.sh', 'set-dist', meson.project_version())
```
diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md
index 64196f3..65daa22 100644
--- a/docs/markdown/Cross-compilation.md
+++ b/docs/markdown/Cross-compilation.md
@@ -139,6 +139,22 @@ of a wrapper, these lines are all you need to write. Meson will
automatically use the given wrapper when it needs to run host
binaries. This happens e.g. when running the project's test suite.
+Note that `exe_wrapper` in the cross file is handled separately
+from the `exe_wrapper` argument in
+[`add_test_setup`](Reference-manual_functions.md#add_test_setup_exe_wrapper)
+and [`meson test --wrapper`](Unit-tests.md#other-test-options)
+command line argument. Meson must have `exe_wrapper` specified in the
+cross file or else it will skip tests that attempt to run cross
+compiled binaries. Only the cross file `exe_wrapper` value will be
+stored in the `MESON_EXE_WRAPPER` environment variable. If another
+wrapper is given in the test setup with `exe_wrapper` or as a
+`meson test --wrapper` command line argument, then meson will prepend
+the additional wrapper before the cross file wrapper like the
+following command:
+```
+[prepend_wrapper] <cross_file_wrapper> <exe_binary> <args...>
+```
+
### Properties
In addition to the properties allowed in [all machine
@@ -173,12 +189,12 @@ remember to specify the args as an array and not as a single string
*Since 0.52.0* The `sys_root` property may point to the root of the
host system path (the system that will run the compiled binaries).
-This is used internally by Meson to set the PKG_CONFIG_SYSROOT_DIR
+This is used internally by Meson to set the `PKG_CONFIG_SYSROOT_DIR`
environment variable for pkg-config. If this is unset the host system
is assumed to share a root with the build system.
-*Since 0.54.0* The pkg_config_libdir property may point to a list of
-path used internally by Meson to set the PKG_CONFIG_LIBDIR environment
+*Since 0.54.0* The `pkg_config_libdir` property may point to a list of
+path used internally by Meson to set the `PKG_CONFIG_LIBDIR` environment
variable for pkg-config. This prevents pkg-config from searching cross
dependencies in system directories.
@@ -357,7 +373,7 @@ myvar = meson.get_external_property('somekey')
As of version 0.44.0 Meson supports loading cross files from system
locations (except on Windows). This will be
-$XDG_DATA_DIRS/meson/cross, or if XDG_DATA_DIRS is undefined, then
+`$XDG_DATA_DIRS/meson/cross`, or if `XDG_DATA_DIRS` is undefined, then
/usr/local/share/meson/cross and /usr/share/meson/cross will be tried
in that order, for system wide cross files. User local files can be
put in $XDG_DATA_HOME/meson/cross, or ~/.local/share/meson/cross if
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md
index f7bf901..4d94d58 100644
--- a/docs/markdown/Dependencies.md
+++ b/docs/markdown/Dependencies.md
@@ -388,6 +388,9 @@ additional toolkit libraries that need to be explicitly linked to. If the
CUDA Toolkit cannot be found in the default paths on your system, you can
set the path using `CUDA_PATH` explicitly.
+Cuda does not honor the `prefer_static` option, and will link statically unless
+the `static` keyword argument is set to `false`.
+
## CUPS
`method` may be `auto`, `config-tool`, `pkg-config`, `cmake` or `extraframework`.
diff --git a/docs/markdown/Design-rationale.md b/docs/markdown/Design-rationale.md
index 4133979..c520773 100644
--- a/docs/markdown/Design-rationale.md
+++ b/docs/markdown/Design-rationale.md
@@ -34,9 +34,9 @@ may not work. In some cases the executable file is a binary whereas at
other times it is a wrapper shell script that invokes the real binary
which resides in a hidden subdirectory. GDB invocation fails if the
binary is a script but succeeds if it is not. The user has to remember
-the type of each one of his executables (which is an implementation
-detail of the build system) just to be able to debug them. Several
-other such pain points can be found in [this blog
+the type of each executable (which is an implementation detail of the
+build system) just to be able to debug them. Several other such pain
+points can be found in [this blog
post](http://voices.canonical.com/jussi.pakkanen/2011/09/13/autotools/).
Given these idiosyncrasies it is no wonder that most people don't want
@@ -132,7 +132,7 @@ and so on.
Sometimes you just have to compile files with only given compiler
flags and no others, or install files in weird places. The system must
-allow the user to do this if he really wants to.
+allow the user to do this.
Overview of the solution
--
@@ -151,7 +151,7 @@ passing around compiler flags and linker flags. In the proposed system
the user just declares that a given build target uses a given external
dependency. The build system then takes care of passing all flags and
settings to their proper locations. This means that the user can focus
-on his own code rather than marshalling command line arguments from
+on their own code rather than marshalling command line arguments from
one place to another.
A DSL is more work than the approach taken by SCons, which is to
diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md
index 7ba4832..91c706e 100644
--- a/docs/markdown/Fs-module.md
+++ b/docs/markdown/Fs-module.md
@@ -206,13 +206,26 @@ fs.name('foo/bar/baz.dll.a') # baz.dll.a
*since 0.54.0*
Returns the last component of the path, dropping the last part of the
-suffix
+suffix.
```meson
fs.stem('foo/bar/baz.dll') # baz
fs.stem('foo/bar/baz.dll.a') # baz.dll
```
+### suffix
+
+*since 1.9.0*
+
+Returns the last dot-separated portion of the final component of the path
+including the dot, if any.
+
+```meson
+fs.suffix('foo/bar/baz.dll') # .dll
+fs.suffix('foo/bar/baz.dll.a') # .a
+fs.suffix('foo/bar') # (empty)
+```
+
### read
- `read(path, encoding: 'utf-8')` *(since 0.57.0)*:
return a [string](Syntax.md#strings) with the contents of the given `path`.
diff --git a/docs/markdown/Getting-meson_zh.md b/docs/markdown/Getting-meson_zh.md
index 4a4cb34..da0bdd5 100644
--- a/docs/markdown/Getting-meson_zh.md
+++ b/docs/markdown/Getting-meson_zh.md
@@ -1,6 +1,6 @@
# 获取Meson
-Meson基于Python3运行,要求Python版本3.5以上。 如果你的操作系统提供包管理器, 你应该用包管理器安装meson;如果没有包管理器,你应该在[Python主页]下载合适的Python3。相关请参阅[特殊平台的安装特例](#特殊平台的安装特例).
+Meson基于Python3运行,要求Python版本3.7以上。 如果你的操作系统提供包管理器, 你应该用包管理器安装python;如果没有包管理器,你应该在[Python主页]下载合适的Python3。相关请参阅[特殊平台的安装特例](#特殊平台的安装特例).
## 下载Meson
diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md
index e8953ef..84bcc61 100644
--- a/docs/markdown/Gnome-module.md
+++ b/docs/markdown/Gnome-module.md
@@ -280,6 +280,8 @@ one XML file.
* `object_manager`: *(Added 0.40.0)* if true generates object manager code
* `annotations`: *(Added 0.43.0)* list of lists of 3 strings for the annotation for `'ELEMENT', 'KEY', 'VALUE'`
* `docbook`: *(Added 0.43.0)* prefix to generate `'PREFIX'-NAME.xml` docbooks
+* `rst`: *(Added 1.9.0)* prefix to generate `'PREFIX'-NAME.rst` reStructuredTexts
+* `markdown`: *(Added 1.9.0)* prefix to generate `'PREFIX'-NAME.md` markdowns
* `build_by_default`: causes, when set to true, to have this target be
built by default, that is, when invoking plain `meson compile`, the default
value is true for all built target types
@@ -289,8 +291,9 @@ one XML file.
Starting *0.46.0*, this function returns a list of at least two custom
targets (in order): one for the source code and one for the header.
-The list will contain a third custom target for the generated docbook
-files if that keyword argument is passed.
+The list can then contain other custom targets for the generated documentation
+files depending if the keyword argument is passed (in order): the docbook
+target, the reStructuredText target and the markdown target.
Earlier versions return a single custom target representing all the
outputs. Generally, you should just add this list of targets to a top
diff --git a/docs/markdown/Installing.md b/docs/markdown/Installing.md
index 0a7ca9f..5c34eba 100644
--- a/docs/markdown/Installing.md
+++ b/docs/markdown/Installing.md
@@ -170,6 +170,7 @@ time, please help extending the list of well known categories.
* `pkgconfig.generate()`,
* `gnome.generate_gir()` - `.gir` file,
* `gnome.generate_vapi()` - `.vapi` file (*Since 0.64.0*),
+ * `gnome.generate_vapi()` - `.deps` file (*Since 1.9.1*),
* Files installed into `libdir` and with `.a` or `.pc` extension,
* File installed into `includedir`,
* Generated header files installed with `gnome.compile_resources()`,
diff --git a/docs/markdown/Overview.md b/docs/markdown/Overview.md
index 7bee937..66758fd 100644
--- a/docs/markdown/Overview.md
+++ b/docs/markdown/Overview.md
@@ -6,11 +6,11 @@ short-description: Overview of the Meson build system
Meson is a build system that is designed to be as user-friendly as
possible without sacrificing performance. The main tool for this is a
-custom language that the user uses to describe the structure of his
-build. The main design goals of this language has been simplicity,
-clarity and conciseness. Much inspiration was drawn from the Python
-programming language, which is considered very readable, even to
-people who have not programmed in Python before.
+custom language used to describe the structure of the build. The main
+design goals of this language have been simplicity, clarity and
+conciseness. Much inspiration was drawn from the Python programming
+language, which is considered very readable, even to people who have
+not programmed in Python before.
Another main idea has been to provide first class support for modern
programming tools and best practices. These include features as varied
diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md
index 80882cb..eb41026 100644
--- a/docs/markdown/Pkgconfig-module.md
+++ b/docs/markdown/Pkgconfig-module.md
@@ -44,9 +44,12 @@ keyword arguments.
`${PREFIX}/include/foobar-1`, the correct value for this argument
would be `foobar-1`
- `requires` list of strings, pkgconfig-dependencies or libraries that
- `pkgconfig.generate()` was used on to put in the `Requires` field
+ `pkgconfig.generate()` was used on to put in the `Requires` field.
+ *Since 1.9.0* internal dependencies are supported if `pkgconfig.generate()`
+ was used on the underlying library.
- `requires_private` the same as `requires` but for the `Requires.private` field
- `url` a string with a url for the library
+- `license` (*Since 1.9.0*) a string with a SPDX license to add to the generated file.
- `variables` a list of strings with custom variables to add to the
generated file. The strings must be in the form `name=value` and may
reference other pkgconfig variables,
@@ -90,6 +93,9 @@ application. That will cause pkg-config to prefer those
builddir. This is an experimental feature provided on a best-effort
basis, it might not work in all use-cases.
+*Since 1.9.0* you can specify a license identifier. To use the current project
+licence, simply use `license: meson.project_license()` as argument to `generate()`.
+
### Implicit dependencies
The exact rules followed to find dependencies that are implicitly
diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md
index ebe42a5..972a6a9 100644
--- a/docs/markdown/Qt6-module.md
+++ b/docs/markdown/Qt6-module.md
@@ -134,7 +134,7 @@ lrelease, it takes no positional arguments, and the following keyword arguments:
- `qresource` string: rcc source file to extract ts_files from; cannot be used
with ts_files kwarg.
- `rcc_extra_arguments` string[]: any additional arguments to `rcc` (optional),
- when used with `qresource.
+ when used with `qresource`.
Returns either: a list of custom targets for the compiled
translations, or, if using a `qresource` file, a single custom target
diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md
index a5d2785..4a3bf28 100644
--- a/docs/markdown/Reference-tables.md
+++ b/docs/markdown/Reference-tables.md
@@ -42,6 +42,7 @@ These are return values of the `get_id` (Compiler family) and
| ti | Texas Instruments C/C++ Compiler | |
| valac | Vala compiler | |
| xc16 | Microchip XC16 C compiler | |
+| xc32-gcc | Microchip XC32 C/C++ compiler | gcc |
| cython | The Cython compiler | |
| nasm | The NASM compiler (Since 0.64.0) | |
| yasm | The YASM compiler (Since 0.64.0) | |
@@ -58,6 +59,7 @@ These are return values of the `get_linker_id` method in a compiler object.
| Value | Linker family |
| ----- | --------------- |
| ld.bfd | The GNU linker |
+| ld.eld | Qualcomm's embedded linker |
| ld.gold | The GNU gold linker |
| ld.lld | The LLVM linker, with the GNU interface |
| ld.mold | The fast MOLD linker |
@@ -72,6 +74,7 @@ These are return values of the `get_linker_id` method in a compiler object.
| optlink | optlink (used with DMD) |
| rlink | The Renesas linker, used with CCrx only |
| xc16-ar | The Microchip linker, used with XC16 only |
+| ld.xc32 | The Microchip linker, used with XC32 only |
| ar2000 | The Texas Instruments linker, used with C2000 only |
| ti-ar | The Texas Instruments linker |
| ar6000 | The Texas Instruments linker, used with C6000 only |
@@ -125,6 +128,7 @@ set in the cross file.
| msp430 | 16 bit MSP430 processor |
| parisc | HP PA-RISC processor |
| pic24 | 16 bit Microchip PIC24 |
+| pic32 | 32 bit Microchip PIC32 |
| ppc | 32 bit PPC processors |
| ppc64 | 64 bit PPC processors |
| riscv32 | 32 bit RISC-V Open ISA |
@@ -171,6 +175,7 @@ These are provided by the `.system()` method call.
| openbsd | |
| windows | Native Windows (not Cygwin or MSYS2) |
| sunos | illumos and Solaris |
+| os/2 | OS/2 |
Any string not listed above is not guaranteed to remain stable in
future releases.
@@ -292,6 +297,7 @@ which are supported by GCC, Clang, and other compilers.
| const |
| constructor |
| constructor_priority |
+| counted_by⁸ |
| deprecated |
| destructor |
| error |
@@ -347,6 +353,8 @@ which are supported by GCC, Clang, and other compilers.
⁷ *New in 1.5.0*
+⁸ *New in 1.10.0*
+
### MSVC __declspec
These values are supported using the MSVC style `__declspec` annotation,
diff --git a/docs/markdown/Release-notes-for-0.38.0.md b/docs/markdown/Release-notes-for-0.38.0.md
index 5aaca0a..7be99ab 100644
--- a/docs/markdown/Release-notes-for-0.38.0.md
+++ b/docs/markdown/Release-notes-for-0.38.0.md
@@ -106,7 +106,7 @@ the check is now ~40% faster.
## Array indexing now supports fallback values
The second argument to the array
-[[list.get]] function is now returned
+[[array.get]] function is now returned
if the specified index could not be found
```meson
diff --git a/docs/markdown/Release-notes-for-1.10.0.md b/docs/markdown/Release-notes-for-1.10.0.md
new file mode 100644
index 0000000..aa4d133
--- /dev/null
+++ b/docs/markdown/Release-notes-for-1.10.0.md
@@ -0,0 +1,289 @@
+---
+title: Release 1.10.0
+short-description: Release notes for 1.10.0
+...
+
+# New features
+
+Meson 1.10.0 was released on 08 December 2025
+## Support for the `counted_by` attribute
+
+`compiler.has_function_attribute()` now supports for the new `counted_by`
+attribute.
+
+## Added a `values()` method for dictionaries
+
+Mesons built-in [[@dict]] type now supports the [[dict.values]] method
+to retrieve the dictionary values as an array, analogous to the
+[[dict.keys]] method.
+
+```meson
+dict = { 'b': 'world', 'a': 'hello' }
+
+[[#dict.keys]] # Returns ['a', 'b']
+[[#dict.values]] # Returns ['hello', 'world']
+```
+
+## Add cmd_array method to ExternalProgram
+
+Added a new `cmd_array()` method to the `ExternalProgram` object that returns
+an array containing the command(s) for the program. This is particularly useful
+in cases like pyInstaller where the Python command is `meson.exe runpython`,
+and the full path should not be used but rather the command array.
+
+The method returns a list of strings representing the complete command needed
+to execute the external program, which may differ from just the full path
+returned by `full_path()` in cases where wrapper commands or interpreters are
+involved.
+
+## Microchip XC32 compiler support
+
+The Microchip XC32 compiler is now supported.
+
+## Added OS/2 support
+
+Meson now supports OS/2 system. Especially, `shortname` kwarg and
+`os2_emxomf` builtin option are introduced.
+
+`shortname` is used to specify a short DLL name fitting to a 8.3 rule.
+
+```meson
+lib = library('foo_library',
+ ...
+ shortname: 'foo',
+ ...
+)
+```
+
+This will generate `foo.dll` not `foo_library.dll` on OS/2. If
+`shortname` is not used, `foo_libr.dll` which is truncated up to 8
+characters is generated.
+
+`os2_emxomf` is used to generate OMF files with OMF tool-chains.
+
+```
+meson setup --os2-emxomf builddir
+```
+
+This will generate OMF object files and `.lib` library files. If
+`--os2-emxomf` is not used, AOUT object files and `.a` library files are
+generated.
+
+## Android cross file generator
+
+The `env2mfile` command now supports a `--android` argument. When
+specified, it tells the cross file generator to create cross files for
+all Android toolchains located on the current machines.
+
+This typically creates many files, so the `-o` argument specifies the
+output directory. A typical use case goes like this:
+
+```
+meson env2mfile --android -o androidcross
+meson setup --cross-file \
+ androidcross/android-29.0.14033849-android35-aarch64-cross.txt \
+ builddir
+```
+
+## Array `.slice()` method
+
+Arrays now have a `.slice()` method which allows for subsetting of arrays.
+
+## `-Db_vscrt` on clang
+
+`-Db_vscrt` will now link the appropriate runtime library, and set
+the appropriate preprocessor symbols, also when the compiler is clang.
+This is only supported when using `link.exe` or `lld-link.exe` as the
+linker.
+
+## Added `build_subdir` arg to various targets
+
+`custom_target()`, `build_target()` and `configure_file()` now support
+the `build_subdir` argument. This directs Meson to place the build
+result within the specified sub-directory path of the build directory.
+
+```meson
+configure_file(input : files('config.h.in'),
+ output : 'config.h',
+ build_subdir : 'config-subdir',
+ install_dir : 'share/appdir',
+ configuration : conf)
+```
+
+This places the build result, `config.h`, in a sub-directory named
+`config-subdir`, creating it if necessary. To prevent collisions
+within the build directory, `build_subdir` is not allowed to match a
+file or directory in the source directory nor contain '..' to refer to
+the parent of the build directory. `build_subdir` does not affect the
+install directory path at all; `config.h` will be installed as
+`share/appdir/config.h`. `build_subdir` may contain multiple levels of
+directory names.
+
+This allows construction of files within the build system that have
+any required trailing path name components as well as building
+multiple files with the same basename from the same source directory.
+
+## Support for Cargo workspaces
+
+When parsing `Cargo.toml` files, Meson now recognizes workspaces
+and will process all the required members and any requested optional
+members of the workspace.
+
+For the time being it is recommended to regroup all Cargo dependencies inside a
+single workspace invoked from the main Meson project. When invoking multiple
+different Cargo subprojects from Meson, feature resolution of common
+dependencies might be wrong.
+
+## Experimental Codegen module
+
+A new module wrapping some common code generators has been added. Currently it supports lex/flex and yacc/bison.
+
+## Methods from compiler object now accept strings for include_directories
+
+The various [[@compiler]] methods with a `include_directories` keyword argument
+now accept strings or array of strings, in addition to [[@inc]] objects
+generated from [[include_directories]] function, as it was already the case for
+[[build_target]] family of functions.
+
+## `meson format` has a new `--check-diff` option
+
+When using `meson format --check-only` to verify formatting in CI, it would
+previously silently exit with an error code if the code was not formatted
+correctly.
+
+A new `--check-diff` option has been added which will instead print a diff of
+the required changes and then exit with an error code.
+
+## `-Db_thinlto_cache` now supported for GCC
+
+`-Db_thinlto_cache` is now supported for GCC 15's incremental LTO, as is
+`-Db_thinlto_cache_dir`.
+
+## Using `meson.get_compiler()` to get a language from another project is marked broken
+
+Meson currently will return a compiler instance from the `meson.get_compiler()`
+call, if that language has been initialized in any project. This can result in
+situations where a project can only work as a subproject, or if a dependency is
+provided by a subproject rather than by a pre-built dependency.
+
+## Experimental C++ import std support
+
+**Note**: this feature is experimental and not guaranteed to be
+ backwards compatible or even exist at all in future Meson releases.
+
+Meson now supports `import std`, a new, modular way of using the C++
+standard library. This support is enabled with the new `cpp_importstd`
+option. It defaults to `false`, but you can set it to `true` either
+globally or per-target using `override_options` in the usual way.
+
+The implementation has many limitations. The biggest one is that the
+same module file is used on _all_ targets. That means you can not mix
+multiple different C++ standards versions as the compiled module file
+can only be used with the same compiler options as were used to build
+it. This feature only works with the Ninja backend.
+
+As `import std` is a major new feature in compilers, expect to
+encounter toolchain issues when using it. For an example [see
+here](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122614).
+
+## Common `Cargo.lock` for all Cargo subprojects
+
+Whenever Meson finds a `Cargo.lock` file in the toplevel directory
+of the project, it will use it to resolve the versions of Cargo
+subprojects in preference to per-subproject `Cargo.lock` files.
+Per-subproject lock files are only used if the invoking project
+did not have a `Cargo.lock` file itself.
+
+If you wish to experiment with Cargo subprojects, it is recommended
+to use `cargo` to set up `Cargo.lock` and `Cargo.toml` files,
+encompassing all Rust targets, in the toplevel source directory.
+Cargo subprojects remain unstable and subject to change.
+
+## Add a configure log in meson-logs
+
+Add a second log file `meson-setup.txt` which contains the configure logs
+displayed on stdout during the meson `setup` stage.
+It allows user to navigate through the setup logs without searching in the terminal
+or the extensive information of `meson-log.txt`.
+
+## Added new `namingscheme` option
+
+Traditionally Meson has named output targets so that they don't clash
+with each other. This has meant, among other things, that on Windows
+Meson uses a nonstandard `.a` suffix for static libraries because both
+static libraries and import libraries have the suffix `.lib`.
+
+There is now an option `namingscheme` that can be set to
+`platform`. This new platform native naming scheme that replicates
+what Rust does. That is, shared libraries on Windows get a suffix
+`.dll`, static libraries get `.lib` and import libraries have the name
+`.dll.lib`.
+
+We expect to change the default value of this option to `platform` in
+a future major version. Until that happens we reserve the right to
+alter how `platform` actually names its output files.
+
+## Rewriter improvements
+
+The [rewriter](Rewriter.md) added support for writing the `project()`
+`license_files` argument and for reading dict-valued kwargs.
+
+It also removed the unused but mandatory `value` arguments to the
+`default-options delete` and `kwargs delete` CLI subcommands. To allow
+scripts to continue supporting previous releases, these arguments are
+still accepted (with a warning) if they're all equal to the empty string.
+
+## Passing `-C default-linker-libraries` to rustc
+
+When calling rustc, Meson now passes the `-C default-linker-libraries` option.
+While rustc passes the necessary libraries for Rust programs, they are rarely
+enough for mixed Rust/C programs.
+
+## `rustc` will receive `-C embed-bitcode=no` and `-C lto` command line options
+
+With this release, Meson passes command line arguments to `rustc` to
+enable LTO when the `b_lto` built-in option is set to true. As an
+optimization, Meson also passes `-C embed-bitcode=no` when LTO is
+disabled; note that this is incompatible with passing `-C lto` via
+`rust_args`.
+
+## New method to handle GNU and Windows symbol visibility for C/C++/ObjC/ObjC++
+
+Defining public API of a cross platform C/C++/ObjC/ObjC++ library is often
+painful and requires copying macro snippets into every projects, typically using
+`__declspec(dllexport)`, `__declspec(dllimport)` or
+`__attribute__((visibility("default")))`.
+
+Meson can now generate a header file that defines exactly what's needed for
+all supported platforms:
+[`snippets.symbol_visibility_header()`](Snippets-module.md#symbol_visibility_header).
+
+## Vala BuildTarget dependency enhancements
+
+A BuildTarget that has Vala sources can now get a File dependency for its
+generated header, vapi, and gir files.
+
+```meson
+lib = library('foo', 'foo.vala')
+lib_h = lib.vala_header()
+lib_s = static_lib('static', 'static.c', lib_h)
+
+lib_vapi = lib.vala_vapi()
+
+custom_target(
+ 'foo-typelib',
+ command : ['g-ir-compiler', '--output', '@OUTPUT@', '@INPUT@'],
+ input : lib.vala_gir(),
+ output : 'Foo-1.0.typelib'
+)
+```
+
+`static.c` will not start compilation until `lib.h` is generated.
+
+## `i18n.xgettext` now accepts CustomTarget and CustomTargetIndex as sources
+
+Previously, [[@custom_tgt]] were accepted but silently ignored, and
+[[@custom_idx]] were not accepted.
+
+Now, they both can be used, and the generated outputs will be scanned to extract
+translation strings.
diff --git a/docs/markdown/Release-notes-for-1.9.0.md b/docs/markdown/Release-notes-for-1.9.0.md
new file mode 100644
index 0000000..aad105f
--- /dev/null
+++ b/docs/markdown/Release-notes-for-1.9.0.md
@@ -0,0 +1,138 @@
+---
+title: Release 1.9.0
+short-description: Release notes for 1.9.0
+...
+
+# New features
+
+Meson 1.9.0 was released on 24 August 2025
+## Array `.flatten()` method
+
+Arrays now have a `.flatten()` method, which turns nested arrays into a single
+flat array. This provides the same effect that Meson often does to arrays
+internally, such as when passed to most function arguments.
+
+## `clang-tidy`'s auto-generated targets correctly select source files
+
+In previous versions, the target would run `clang-tidy` on _every_ C-like source files (.c, .h, .cpp, .hpp). It did not work correctly because some files, especially headers, are not intended to be consumed as is.
+
+It will now run only on source files participating in targets.
+
+## Added Qualcomm's embedded linker, eld
+
+Qualcomm recently open-sourced their embedded linker.
+https://github.com/qualcomm/eld
+
+Meson users can now use this linker.
+
+## Added suffix function to the FS module
+
+The basename and stem were already available. For completeness, expose also the
+suffix.
+
+## Support response files for custom targets
+
+When using the Ninja backend, Meson can now pass arguments to supported tools
+through response files.
+
+In this release it's enabled only for the Gnome module, fixing calling
+`gnome.mkenums()` with a large set of files on Windows (requires
+Glib 2.59 or higher).
+
+## meson format now has a --source-file-path argument when reading from stdin
+
+This argument is mandatory to mix stdin reading with the use of editor config.
+It allows to know where to look for the .editorconfig, and to use the right
+section of .editorconfig based on the parsed file name.
+
+## Added license keyword to pkgconfig.generate
+
+When specified, it will add a `License:` attribute to the generated .pc file.
+
+## pkgconfig.generate supports internal dependencies in `requires`
+
+Internal dependencies can now be specified to `requires` if
+pkgconfig.generate was called on the underlying library.
+
+## New experimental option `rust_dynamic_std`
+
+A new option `rust_dynamic_std` can be used to link Rust programs so
+that they use a dynamic library for the Rust `libstd`.
+
+Right now, `staticlib` crates cannot be produced if `rust_dynamic_std` is
+true, but this may change in the future.
+
+## Rust and non-Rust sources in the same target
+
+Meson now supports creating a single target with Rust and non Rust
+sources mixed together. In this case, if specified, `link_language`
+must be set to `rust`.
+
+## Explicitly setting Swift module name is now supported
+
+It is now possible to set the Swift module name for a target via the
+*swift_module_name* target kwarg, overriding the default inferred from the
+target name.
+
+```meson
+lib = library('foo', 'foo.swift', swift_module_name: 'Foo')
+```
+
+## Top-level statement handling in Swift libraries
+
+The Swift compiler normally treats modules with a single source
+file (and files named main.swift) to run top-level code at program
+start. This emits a main symbol which is usually undesirable in a
+library target. Meson now automatically passes the *-parse-as-library*
+flag to the Swift compiler in case of single-file library targets to
+disable this behavior unless the source file is called main.swift.
+
+## Swift compiler receives select C family compiler options
+
+Meson now passes select few C family (C/C++/Obj-C/Obj-C++) compiler
+options to the Swift compiler, notably *-std=*, in order to improve
+the compatibility of C code as interpreted by the C compiler and the
+Swift compiler.
+
+NB: This does not include any of the options set in the target's
+c_flags.
+
+## Swift/C++ interoperability is now supported
+
+It is now possible to create Swift executables that can link to C++ or
+Objective-C++ libraries. To enable this feature, set the target kwarg
+_swift\_interoperability\_mode_ to 'cpp'.
+
+To import C++ code, specify a bridging header in the Swift target's
+sources, or use another way such as adding a directory containing a
+Clang module map to its include path.
+
+Note: Enabling C++ interoperability in a library target is a breaking
+change. Swift libraries that enable it need their consumers to enable
+it as well, as per [the Swift documentation][1].
+
+Swift 5.9 is required to use this feature. Xcode 15 is required if the
+Xcode backend is used.
+
+```meson
+lib = static_library('mylib', 'mylib.cpp')
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
+```
+
+[1]: https://www.swift.org/documentation/cxx-interop/project-build-setup/#vending-packages-that-enable-c-interoperability
+
+## Support for MASM in Visual Studio backends
+
+Previously, assembling `.masm` files with Microsoft's Macro Assembler is only
+available on the Ninja backend. This now also works on Visual Studio backends.
+
+Note that building ARM64EC code using `ml64.exe` is currently unimplemented in
+both of the backends. If you need mixing x64 and Arm64 in your project, please
+file an issue on GitHub.
+
+## Limited support for WrapDB v1
+
+WrapDB v1 has been discontinued for several years, Meson will now print a
+deprecation warning if a v1 URL is still being used. Wraps can be updated to
+latest version using `meson wrap update` command.
+
diff --git a/docs/markdown/Rewriter.md b/docs/markdown/Rewriter.md
index 82f8635..546666f 100644
--- a/docs/markdown/Rewriter.md
+++ b/docs/markdown/Rewriter.md
@@ -94,7 +94,8 @@ It is also possible to set kwargs of specific functions with the
rewriter. The general command for setting or removing kwargs is:
```bash
-meson rewrite kwargs {set/delete} <function type> <function ID> <key1> <value1> <key2> <value2> ...
+meson rewrite kwargs set <function type> <function ID> <key1> <value1> <key2> <value2> ...
+meson rewrite kwargs delete <function type> <function ID> <key1> <key2> ...
```
For instance, setting the project version can be achieved with this command:
@@ -116,14 +117,23 @@ converted to `/` by msys bash but in order to keep usage
shell-agnostic, the rewrite command also allows `//` as the function
ID such that it will work in both msys bash and other shells.
+*Before 1.10.0*, the `delete` command expected `<key> <value>` pairs as
+in `set`; the `<value>` was ignored. For backward compatibility, Meson
+accepts this syntax with a warning if all `<value>`s are the empty string.
+
### Setting the project default options
For setting and deleting default options, use the following command:
```bash
-meson rewrite default-options {set/delete} <opt1> <value1> <opt2> <value2> ...
+meson rewrite default-options set <opt1> <value1> <opt2> <value2> ...
+meson rewrite default-options delete <opt1> <opt2> ...
```
+*Before 1.10.0*, the `delete` command expected `<opt> <value>` pairs as
+in `set`; the `<value>` was ignored. For backward compatibility, Meson
+accepts this syntax with a warning if all `<value>`s are the empty string.
+
## Limitations
Rewriting a Meson file is not guaranteed to keep the indentation of
@@ -229,6 +239,9 @@ The format for the type `target` is defined as follows:
}
```
+For operation `delete`, the values of the `options` can be anything
+(including `null`).
+
### Default options modification format
The format for the type `default_options` is defined as follows:
@@ -246,7 +259,7 @@ The format for the type `default_options` is defined as follows:
```
For operation `delete`, the values of the `options` can be anything
-(including `null`)
+(including `null`).
## Extracting information
diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md
index 35eaf39..5ce0fdc 100644
--- a/docs/markdown/Rust-module.md
+++ b/docs/markdown/Rust-module.md
@@ -137,7 +137,7 @@ were never turned on by Meson.
```ini
[properties]
-bindgen_clang_arguments = ['-target', 'x86_64-linux-gnu']
+bindgen_clang_arguments = ['--target', 'x86_64-linux-gnu']
```
### proc_macro()
diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md
index 08580cd..67bbdec 100644
--- a/docs/markdown/Rust.md
+++ b/docs/markdown/Rust.md
@@ -27,14 +27,20 @@ feature is stabilized.
## Mixing Rust and non-Rust sources
-Meson currently does not support creating a single target with Rust and non Rust
-sources mixed together, therefore one must compile multiple libraries and link
-them.
+*(Since 1.9.0)* Rust supports mixed targets, but only supports using
+`rustc` as the linker for such targets. If you need to use a non-Rust
+linker, or support Meson < 1.9.0, see below.
+
+Until Meson 1.9.0, Meson did not support creating a single target with
+Rust and non Rust sources mixed together. One had to compile a separate
+Rust `static_library` or `shared_library`, and link it into the C build
+target (e.g., a library or an executable).
```meson
rust_lib = static_library(
'rust_lib',
sources : 'lib.rs',
+ rust_abi: 'c',
...
)
@@ -44,7 +50,6 @@ c_lib = static_library(
link_with : rust_lib,
)
```
-This is an implementation detail of Meson, and is subject to change in the future.
## Mixing Generated and Static sources
@@ -93,3 +98,11 @@ target is a proc macro or dylib, or it depends on a dylib, in which case [`-C
prefer-dynamic`](https://doc.rust-lang.org/rustc/codegen-options/index.html#prefer-dynamic)
will be passed to the Rust compiler, and the standard libraries will be
dynamically linked.
+
+## Multiple targets for the same crate name
+
+For library targets that have `rust_abi: 'rust'`, the crate name is derived from the
+target name. First, dashes, spaces and dots are replaced with underscores. Second,
+*since 1.10.0* anything after the first `+` is dropped. This allows creating multiple
+targets for the same crate name, for example when the same crate is built multiple
+times with different features, or for both the build and the host machine.
diff --git a/docs/markdown/Snippets-module.md b/docs/markdown/Snippets-module.md
new file mode 100644
index 0000000..15d5e9f
--- /dev/null
+++ b/docs/markdown/Snippets-module.md
@@ -0,0 +1,111 @@
+---
+short-description: Code snippets module
+...
+
+# Snippets module
+
+*(new in 1.10.0)*
+
+This module provides helpers to generate commonly useful code snippets.
+
+## Functions
+
+### symbol_visibility_header()
+
+```meson
+snippets.symbol_visibility_header(header_name,
+ namespace: str
+ api: str
+ compilation: str
+ static_compilation: str
+ static_only: bool
+)
+```
+
+Generate a header file that defines macros to be used to mark all public APIs
+of a library. Depending on the platform, this will typically use
+`__declspec(dllexport)`, `__declspec(dllimport)` or
+`__attribute__((visibility("default")))`. It is compatible with C, C++,
+ObjC and ObjC++ languages. The content of the header is static regardless
+of the compiler used.
+
+The first positional argument is the name of the header file to be generated.
+It also takes the following keyword arguments:
+
+- `namespace`: Prefix for generated macros, defaults to the current project name.
+ It will be converted to upper case with all non-alphanumeric characters replaced
+ by an underscore `_`. It is only used for `api`, `compilation` and
+ `static_compilation` default values.
+- `api`: Name of the macro used to mark public APIs. Defaults to `<NAMESPACE>_API`.
+- `compilation`: Name of a macro defined only when compiling the library.
+ Defaults to `<NAMESPACE>_COMPILATION`.
+- `static_compilation`: Name of a macro defined only when compiling or using
+ static library. Defaults to `<NAMESPACE>_STATIC_COMPILATION`.
+- `static_only`: If set to true, `<NAMESPACE>_STATIC_COMPILATION` is defined
+ inside the generated header. In that case the header can only be used for
+ building a static library. By default it is `true` when `default_library=static`,
+ and `false` otherwise. [See below for more information](#static_library)
+
+Projects that define multiple shared libraries should typically have
+one header per library, with a different namespace.
+
+The generated header file should be installed using `install_headers()`.
+
+`meson.build`:
+```meson
+project('mylib', 'c')
+subdir('mylib')
+```
+
+`mylib/meson.build`:
+```meson
+snippets = import('snippets')
+apiheader = snippets.symbol_visibility_header('apiconfig.h')
+install_headers(apiheader, 'lib.h', subdir: 'mylib')
+lib = library('mylib', 'lib.c',
+ gnu_symbol_visibility: 'hidden',
+ c_args: ['-DMYLIB_COMPILATION'],
+)
+```
+
+`mylib/lib.h`
+```c
+#include <mylib/apiconfig.h>
+MYLIB_API int do_stuff();
+```
+
+`mylib/lib.c`
+```c
+#include "lib.h"
+int do_stuff() {
+ return 0;
+}
+```
+
+#### Static library
+
+When building both static and shared libraries on Windows (`default_library=both`),
+`-D<NAMESPACE>_STATIC_COMPILATION` must be defined only for the static library,
+using `c_static_args`. This causes Meson to compile the library twice.
+
+```meson
+if host_system == 'windows'
+ static_arg = ['-DMYLIB_STATIC_COMPILATION']
+else
+ static_arg = []
+endif
+lib = library('mylib', 'lib.c',
+ gnu_symbol_visibility: 'hidden',
+ c_args: ['-DMYLIB_COMPILATION'],
+ c_static_args: static_arg
+)
+```
+
+`-D<NAMESPACE>_STATIC_COMPILATION` C-flag must be defined when compiling
+applications that use the library API. It typically should be defined in
+`declare_dependency(..., compile_args: [])` and
+`pkgconfig.generate(..., extra_cflags: [])`.
+
+Note that when building both shared and static libraries on Windows,
+applications cannot currently rely on `pkg-config` to define this macro.
+See https://github.com/mesonbuild/meson/pull/14829.
diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md
index 05f5038..f4a2e17 100644
--- a/docs/markdown/Syntax.md
+++ b/docs/markdown/Syntax.md
@@ -566,7 +566,7 @@ executable('exe1', 'foo.c', 'bar.c', 'foobar.c')
Because of an internal implementation detail, the following syntax
is currently also supported, even though the first argument of
-[[executable]] is a single [[@str]] and not a [[@list]]:
+[[executable]] is a single [[@str]] and not a [[@array]]:
```meson
# WARNING: This example is only valid because of an internal
diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md
index a515b24..5b9821b 100644
--- a/docs/markdown/Users.md
+++ b/docs/markdown/Users.md
@@ -2,7 +2,7 @@
title: Users
...
-# Notable projects using Meson
+# Notable projects and organizations using Meson
If you're aware of a notable project that uses Meson, please
[file a pull-request](https://github.com/mesonbuild/meson/edit/master/docs/markdown/Users.md)
@@ -11,181 +11,47 @@ for it. For other projects using Meson, you may be interested in this
Some additional projects are listed in the [`meson` GitHub
topic](https://github.com/topics/meson).
- - [2048.cpp](https://github.com/plibither8/2048.cpp), a fully featured terminal version of the game "2048" written in C++
- - [aawordsearch](https://github.com/theimpossibleastronaut/aawordsearch), generate wordsearch puzzles using random words in different languages
- - [Adwaita Manager](https://github.com/AdwCustomizerTeam/AdwCustomizer), change the look of Adwaita, with ease
- - [Aravis](https://github.com/AravisProject/aravis), a glib/gobject based library for video acquisition using Genicam cameras
- - [Akira](https://github.com/akiraux/Akira), a native Linux app for UI and UX design built in Vala and Gtk
- - [AQEMU](https://github.com/tobimensch/aqemu), a Qt GUI for QEMU virtual machines, since version 0.9.3
- - [Arduino sample project](https://github.com/jpakkane/mesonarduino)
- - [Asteria](https://github.com/lhmouse/asteria), another scripting language
- - [Audacious](https://github.com/audacious-media-player), a lightweight and versatile audio player
- - [bolt](https://gitlab.freedesktop.org/bolt/bolt), userspace daemon to enable security levels for Thunderbolt™ 3 on Linux
- - [bsdutils](https://github.com/dcantrell/bsdutils), alternative to GNU coreutils using software from FreeBSD
- - [Bubblewrap](https://github.com/containers/bubblewrap), unprivileged sandboxing tool
- - [Budgie Desktop](https://github.com/budgie-desktop/budgie-desktop), a desktop environment built on GNOME technologies
- - [Bzip2](https://gitlab.com/federicomenaquintero/bzip2), the bzip2 compressor/decompressor
- - [Cage](https://github.com/Hjdskes/cage), a Wayland kiosk
- - [canfigger](https://github.com/andy5995/canfigger), simple configuration file parser library
- - [casync](https://github.com/systemd/casync), Content-Addressable Data Synchronization Tool
- - [cglm](https://github.com/recp/cglm), a highly optimized graphics math library for C
- - [cinnamon-desktop](https://github.com/linuxmint/cinnamon-desktop), the cinnamon desktop library
- - [Cozy](https://github.com/geigi/cozy), a modern audio book player for Linux and macOS using GTK+ 3
- - [Criterion](https://github.com/Snaipe/Criterion), extensible cross-platform C and C++ unit testing framework
- - [dav1d](https://code.videolan.org/videolan/dav1d), an AV1 decoder
- - [dbus-broker](https://github.com/bus1/dbus-broker), Linux D-Bus Message Broker
- - [DOSBox Staging](https://github.com/dosbox-staging/dosbox-staging), DOS/x86 emulator
- - [DPDK](http://dpdk.org/browse/dpdk), Data Plane Development Kit, a set of libraries and drivers for fast packet processing
- - [DXVK](https://github.com/doitsujin/dxvk), a Vulkan-based Direct3D 11 implementation for Linux using Wine
+## [Xorg](https://www.x.org)
+
+ - [Xserver](https://gitlab.freedesktop.org/xorg/xserver), the X.org display server
+
+## [Gnome](https://www.gnome.org)
+
+ - [GTK](https://gitlab.gnome.org/GNOME/gtk), the multi-platform toolkit used by GNOME
+ - [GLib](https://gitlab.gnome.org/GNOME/glib), cross-platform C library used by GTK+ and GStreamer
+
+## [Enlightenment](https://www.enlightenment.org/)
+
- [EFL](https://www.enlightenment.org/about-efl), multiplatform set of libraries, used by the Enlightenment windows manager and other projects
- - [Enlightenment](https://www.enlightenment.org/), windows manager, compositor and minimal desktop for Linux
- - [elementary OS](https://github.com/elementary/), Linux desktop oriented distribution
- - [Emeus](https://github.com/ebassi/emeus), constraint based layout manager for GTK+
- - [Entangle](https://entangle-photo.org/), tethered camera control and capture desktop application
- - [ESP8266 Arduino sample project](https://github.com/trilader/arduino-esp8266-meson), sample project for using the ESP8266 Arduino port with Meson
- - [FeedReader](https://github.com/jangernert/FeedReader), a modern desktop application designed to complement existing web-based RSS accounts
- - [Flecs](https://github.com/SanderMertens/flecs), a Fast and Lightweight ECS (Entity Component System) C library
- - [Foliate](https://github.com/johnfactotum/foliate), a simple and modern GTK eBook reader, built with GJS and Epub.js
- - [Fractal](https://wiki.gnome.org/Apps/Fractal/), a Matrix messaging client for GNOME
- - [Frida](https://github.com/frida/frida-core), a dynamic binary instrumentation toolkit
- - [fwupd](https://github.com/hughsie/fwupd), a simple daemon to allow session software to update firmware
- - [GameMode](https://github.com/FeralInteractive/gamemode), a daemon/lib combo for Linux that allows games to request a set of optimisations be temporarily applied to the host OS
- - [Geary](https://wiki.gnome.org/Apps/Geary), an email application built around conversations, for the GNOME 3 desktop
+
+## [Elementary OS](https://github.com/elementary/)
+
+## [cinnamon-desktop](https://github.com/linuxmint/cinnamon-desktop)
+
+ - [Nemo](https://github.com/linuxmint/nemo), the file manager for the Cinnamon desktop environment
+
+## [Budgie Desktop](https://github.com/budgie-desktop/budgie-desktop)
+
+## Other Notable projects
+
+ - [FreeType](https://freetype.org/), widely used open source font rendering engine
- [GIMP](https://gitlab.gnome.org/GNOME/gimp), an image manipulation program (master branch)
- - [Git](https://git-scm.com/), ["the information manager from hell"](https://github.com/git/git/commit/e83c5163316f89bfbde7d9ab23ca2e25604af290)
- - [GLib](https://gitlab.gnome.org/GNOME/glib), cross-platform C library used by GTK+ and GStreamer
- - [Glorytun](https://github.com/angt/glorytun), a multipath UDP tunnel
- - [GNOME Boxes](https://gitlab.gnome.org/GNOME/gnome-boxes), a GNOME hypervisor
- - [GNOME Builder](https://gitlab.gnome.org/GNOME/gnome-builder), an IDE for the GNOME platform
- - [GNOME MPV](https://github.com/gnome-mpv/gnome-mpv), GNOME frontend to the mpv video player
- - [GNOME Recipes](https://gitlab.gnome.org/GNOME/recipes), application for cooking recipes
- - [GNOME Software](https://gitlab.gnome.org/GNOME/gnome-software), an app store for GNOME
- - [GNOME Twitch](https://github.com/vinszent/gnome-twitch), an app for viewing Twitch streams on GNOME desktop
- - [GNOME Usage](https://gitlab.gnome.org/GNOME/gnome-usage), a GNOME application for visualizing system resources
- - [GNOME Web](https://gitlab.gnome.org/GNOME/epiphany), a browser for a simple, clean, beautiful view of the web
- - [GNU FriBidi](https://github.com/fribidi/fribidi), the open source implementation of the Unicode Bidirectional Algorithm
- - [Graphene](https://ebassi.github.io/graphene/), a thin type library for graphics
- - [Grilo](https://git.gnome.org/browse/grilo) and [Grilo plugins](https://git.gnome.org/browse/grilo-plugins), the Grilo multimedia framework
- [GStreamer](https://gitlab.freedesktop.org/gstreamer/gstreamer), multimedia framework
- - [GTK+](https://gitlab.gnome.org/GNOME/gtk), the multi-platform toolkit used by GNOME
- - [GtkDApp](https://gitlab.com/csoriano/GtkDApp), an application template for developing Flatpak apps with Gtk+ and D
- - [GVfs](https://git.gnome.org/browse/gvfs/), a userspace virtual filesystem designed to work with the I/O abstraction of GIO
- - [Hardcode-Tray](https://github.com/bil-elmoussaoui/Hardcode-Tray), fixes hardcoded tray icons in Linux
+ - [Git](https://git-scm.com/), ["the information manager from hell"](https://github.com/git/git/commit/e83c5163316f89bfbde7d9ab23ca2e25604af290)
- [HarfBuzz](https://github.com/harfbuzz/harfbuzz), a text shaping engine
- - [HelenOS](http://helenos.org), a portable microkernel-based multiserver operating system
- [HexChat](https://github.com/hexchat/hexchat), a cross-platform IRC client in C
- - [IGT](https://gitlab.freedesktop.org/drm/igt-gpu-tools), Linux kernel graphics driver test suite
- - [i3](https://i3wm.org), improved tiling window manager
- - [inih](https://github.com/benhoyt/inih) (INI Not Invented Here), a small and simple .INI file parser written in C
- - [Irssi](https://github.com/irssi/irssi), a terminal chat client in C
- - [iSH](https://github.com/tbodt/ish), Linux shell for iOS
- - [Janet](https://github.com/janet-lang/janet), a functional and imperative programming language and bytecode interpreter
- - [json](https://github.com/nlohmann/json), JSON for Modern C++
- - [JsonCpp](https://github.com/open-source-parsers/jsoncpp), a C++ library for interacting with JSON
- - [Json-glib](https://gitlab.gnome.org/GNOME/json-glib), GLib-based JSON manipulation library
- - [Kiwix libraries](https://github.com/kiwix/kiwix-lib)
- - [Knot Resolver](https://gitlab.labs.nic.cz/knot/knot-resolver), Full caching DNS resolver implementation
- - [Ksh](https://github.com/att/ast), a Korn Shell
- - [Lc0](https://github.com/LeelaChessZero/lc0), LeelaChessZero is a UCI-compliant chess engine designed to play chess via neural network
- - [Le](https://github.com/kirushyk/le), machine learning framework
- - [libcamera](https://git.linuxtv.org/libcamera.git/), a library to handle complex cameras on Linux, ChromeOS and Android
- - [Libdrm](https://gitlab.freedesktop.org/mesa/drm), a library for abstracting DRM kernel interfaces
- - [libdwarf](https://www.prevanders.net/dwarf.html), a multiplatform DWARF parser library
- - [libeconf](https://github.com/openSUSE/libeconf), Enhanced config file parsing library, which merges config files placed in several locations into one
- - [Libepoxy](https://github.com/anholt/libepoxy/), a library for handling OpenGL function pointer management
- - [libfuse](https://github.com/libfuse/libfuse), the reference implementation of the Linux FUSE (Filesystem in Userspace) interface
- - [Libgit2-glib](https://git.gnome.org/browse/libgit2-glib), a GLib wrapper for libgit2
- - [libglvnd](https://gitlab.freedesktop.org/glvnd/libglvnd), Vendor neutral OpenGL dispatch library for Unix-like OSes
- - [Libhttpseverywhere](https://git.gnome.org/browse/libhttpseverywhere), a library to enable httpseverywhere on any desktop app
- - [libmodulemd](https://github.com/fedora-modularity/libmodulemd), a GObject Introspected library for managing [Fedora Project](https://getfedora.org/) module metadata
- - [Libosmscout](https://github.com/Framstag/libosmscout), a C++ library for offline map rendering, routing and location
-lookup based on OpenStreetMap data
- - [libratbag](https://github.com/libratbag/libratbag), provides a DBus daemon to configure input devices, mainly gaming mice
- - [libspng](https://github.com/randy408/libspng), a C library for reading and writing Portable Network Graphics (PNG)
-format files
- - [libSRTP](https://github.com/cisco/libsrtp) (from Cisco Systems), a library for SRTP (Secure Realtime Transport Protocol)
- - [libui](https://github.com/andlabs/libui), a simple and portable (but not inflexible) GUI library in C that uses the native GUI technologies of each platform it supports
- - [Libva](https://github.com/intel/libva), an implementation for the VA (VIdeo Acceleration) API
- - [libvips](https://github.com/libvips/libvips), a fast image processing library with low memory needs
- - [Libvirt](https://libvirt.org), a toolkit to manage virtualization platforms
- - [Libzim](https://github.com/openzim/libzim), the reference implementation for the ZIM file format
- - [Linux PAM](https://github.com/linux-pam/linux-pam), The Pluggable Authentication Modules project for Linux
- [LXC](https://github.com/lxc/lxc), Linux container runtime
- - [Marker](https://github.com/fabiocolacio/Marker), a GTK-3 markdown editor
- - [mcfgthread](https://github.com/lhmouse/mcfgthread), cornerstone library for C++11 threading on mingw-w64
+ - [Linux PAM](https://github.com/linux-pam/linux-pam), The Pluggable Authentication Modules project for Linux
- [Mesa](https://mesa3d.org/), an open source graphics driver project
- - [Miniz](https://github.com/richgel999/miniz), a zlib replacement library
- - [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
- - [Nautilus](https://gitlab.gnome.org/GNOME/nautilus), the GNOME file manager
- - [Nemo](https://github.com/linuxmint/nemo), the file manager for the Cinnamon desktop environment
- - [netatalk](https://netatalk.io/), a free and open source AFP file server for Mac and Apple II
- - [NetPanzer](https://github.com/netpanzer/netpanzer), a 2D online multiplayer tactical warfare game designed for fast action combat
- [NumPy](https://numpy.org/), a Python package for scientific computing
- - [nvme-cli](https://github.com/linux-nvme/nvme-cli), NVMe management command line interface
- - [oomd](https://github.com/facebookincubator/oomd), a userspace Out-Of-Memory (OOM) killer for Linux systems
- [OpenH264](https://github.com/cisco/openh264), open source H.264 codec
- - [OpenHMD](https://github.com/OpenHMD/OpenHMD), a free and open source API and drivers for immersive technology, such as head mounted displays with built in head tracking
- [OpenRC](https://github.com/OpenRC/openrc), an init system for Unix-like operating systems
- - [OpenTitan](https://github.com/lowRISC/opentitan), an open source silicon Root of Trust (RoT) project
- - [Orc](https://gitlab.freedesktop.org/gstreamer/orc), the Optimized Inner Loop Runtime Compiler
- - [OTS](https://github.com/khaledhosny/ots), the OpenType Sanitizer, parses and serializes OpenType files (OTF, TTF) and WOFF and WOFF2 font files, validating and sanitizing them as it goes. Used by Chromium and Firefox
- - [Outlier](https://github.com/kerolasa/outlier), a small Hello World style Meson example project
- - [p11-kit](https://github.com/p11-glue/p11-kit), PKCS#11 module aggregator
- [Pacman](https://gitlab.archlinux.org/pacman/pacman.git), a package manager for Arch Linux
- - [Pango](https://git.gnome.org/browse/pango/), an Internationalized text layout and rendering library
- - [Parzip](https://github.com/jpakkane/parzip), a multithreaded reimplementation of Zip
- - [Peek](https://github.com/phw/peek), simple animated GIF screen recorder with an easy to use interface
- [PicoLibc](https://github.com/keith-packard/picolibc), a standard C library for small embedded systems with limited RAM
- [PipeWire](https://github.com/PipeWire/pipewire), a framework for video and audio for containerized applications
- - [Pistache](https://github.com/pistacheio/pistache), a high performance REST toolkit written in C++
- - [Pithos](https://github.com/pithos/pithos), a Pandora Radio client
- - [Pitivi](https://github.com/pitivi/pitivi/), a nonlinear video editor
- - [Planner](https://github.com/alainm23/planner), task manager with Todoist support designed for GNU/Linux
- - [Playerctl](https://github.com/acrisci/playerctl), mpris command-line controller and library for spotify, vlc, audacious, bmp, cmus, and others
- - [Polari](https://gitlab.gnome.org/GNOME/polari), an IRC client
- [PostgreSQL](https://www.postgresql.org/), an advanced open source relational database
- - [qboot](https://github.com/bonzini/qboot), a minimal x86 firmware for booting Linux kernels
- [QEMU](https://qemu.org), a processor emulator and virtualizer
- - [radare2](https://github.com/radare/radare2), unix-like reverse engineering framework and commandline tools (not the default)
- - [refivar](https://github.com/nvinson/refivar), A reimplementation of efivar in Rust
- - [Rizin](https://rizin.re), Free and Open Source Reverse Engineering Framework
- - [rmw](https://theimpossibleastronaut.com/rmw-website/), safe-remove utility for the command line
- - [RxDock](https://gitlab.com/rxdock/rxdock), a protein-ligand docking software designed for high throughput virtual screening (fork of rDock)
- [SciPy](https://scipy.org/), an open-source software for mathematics, science, and engineering
- - [scrcpy](https://github.com/Genymobile/scrcpy), a cross platform application that provides display and control of Android devices connected on USB or over TCP/IP
- - [Sequeler](https://github.com/Alecaddd/sequeler), a friendly SQL client for Linux, built with Vala and Gtk
- - [Siril](https://gitlab.com/free-astro/siril), an image processing software for amateur astronomy
- - [slapt-get](https://github.com/jaos/slapt-get), an APT like system for Slackware package management
- - [Spot](https://github.com/xou816/spot), Rust based Spotify client for the GNOME desktop
- - [SSHFS](https://github.com/libfuse/sshfs), allows you to mount a remote filesystem using SFTP
- - [sway](https://github.com/swaywm/sway), i3-compatible Wayland compositor
- - [Sysprof](https://git.gnome.org/browse/sysprof), a profiling tool
- [systemd](https://github.com/systemd/systemd), the init system
- - [szl](https://github.com/dimkr/szl), a lightweight, embeddable scripting language
- - [Taisei Project](https://taisei-project.org/), an open-source Touhou Project clone and fangame
- - [Terminology](https://github.com/billiob/terminology), a terminal emulator based on the Enlightenment Foundation Libraries
- - [ThorVG](https://www.thorvg.org/), vector-based scenes and animations library
- - [Tilix](https://github.com/gnunn1/tilix), a tiling terminal emulator for Linux using GTK+ 3
- - [Tizonia](https://github.com/tizonia/tizonia-openmax-il), a command-line cloud music player for Linux with support for Spotify, Google Play Music, YouTube, SoundCloud, TuneIn, Plex servers and Chromecast devices
- - [Fossil Logic](https://github.com/fossillogic), Fossil Logic is a cutting-edge software development company specializing in C/C++, Python, programming, Android development using Kotlin, and SQL solutions
- - [UFJF-MLTK](https://github.com/mateus558/UFJF-Machine-Learning-Toolkit), A C++ cross-platform framework for machine learning algorithms development and testing
- - [Vala Language Server](https://github.com/benwaffle/vala-language-server), code intelligence engine for the Vala and Genie programming languages
- - [Valum](https://github.com/valum-framework/valum), a micro web framework written in Vala
- - [Venom](https://github.com/naxuroqa/Venom), a modern Tox client for the GNU/Linux desktop
- - [vkQuake](https://github.com/Novum/vkQuake), a port of id Software's Quake using Vulkan instead of OpenGL for rendering
- - [VMAF](https://github.com/Netflix/vmaf) (by Netflix), a perceptual video quality assessment based on multi-method fusion
- - [Wayland](https://gitlab.freedesktop.org/wayland/wayland) and [Weston](https://gitlab.freedesktop.org/wayland/weston), a next generation display server
- - [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots), a modular Wayland compositor library
- - [xi-gtk](https://github.com/eyelash/xi-gtk), a GTK+ front-end for the Xi editor
- - [Xorg](https://gitlab.freedesktop.org/xorg/xserver), the X.org display server (not the default yet)
- - [X Test Suite](https://gitlab.freedesktop.org/xorg/test/xts), The X.org test suite
- - [zathura](https://github.com/pwmt/zathura), a highly customizable and functional document viewer based on the
-girara user interface library and several document libraries
- - [Zrythm](https://git.sr.ht/~alextee/zrythm), a cross-platform digital audio workstation written in C using GTK4
- - [ZStandard](https://github.com/facebook/zstd/commit/4dca56ed832c6a88108a2484a8f8ff63d8d76d91), a compression algorithm developed at Facebook (not used by default)
-
-Note that a more up-to-date list of GNOME projects that use Meson can
-be found
-[here](https://wiki.gnome.org/Initiatives/GnomeGoals/MesonPorting).
+ - [Wayland](https://gitlab.freedesktop.org/wayland/wayland), common display protocol
diff --git a/docs/markdown/Vala.md b/docs/markdown/Vala.md
index fdc4fba..ff7a7c7 100644
--- a/docs/markdown/Vala.md
+++ b/docs/markdown/Vala.md
@@ -304,6 +304,40 @@ In this example, the second and third elements of the `install_dir`
array indicate the destination with `true` to use default directories
(i.e. `include` and `share/vala/vapi`).
+### Depending on C header
+
+*(since 1.10.0)*
+
+Given the previous example,
+
+```meson
+foo_lib = shared_library(...)
+foo_h = foo_lib.vala_header()
+```
+
+This header can now be used like any other generated header to create an
+order-only dependency.
+
+
+### Depending on VAPI header
+
+*(since 1.10.0)*
+
+Given the previous example,
+
+```meson
+foo_lib = shared_library(...)
+foo_vapi = foo_lib.vala_vapi()
+```
+
+### Depending on generated GIR
+
+*(since 1.10.0)*
+
+```meson
+foo_lib = shared_library(..., vala_gir : 'foo.gir')
+foo_gir = foo_lib.vala_gir()
+```
### GObject Introspection and language bindings
@@ -339,6 +373,21 @@ directory (i.e. `share/gir-1.0` for GIRs). The fourth element in the
To then generate a typelib file use a custom target with the
`g-ir-compiler` program and a dependency on the library:
+*Since Meson 1.10*, use the `.vala_gir()` method to get a handle to the generated `.gir` file:
+
+```meson
+g_ir_compiler = find_program('g-ir-compiler')
+custom_target('foo typelib', command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
+ input: foo_lib.vala_gir(),
+ output: 'Foo-1.0.typelib',
+ install: true,
+ install_dir: get_option('libdir') / 'girepository-1.0')
+```
+
+
+*Before Meson 1.10*, calculating the path to the input is required, as is adding a
+manual dependency to the vala target:
+
```meson
g_ir_compiler = find_program('g-ir-compiler')
custom_target('foo typelib', command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md
index 73358e7..4a7250d 100644
--- a/docs/markdown/Wrap-dependency-system-manual.md
+++ b/docs/markdown/Wrap-dependency-system-manual.md
@@ -322,8 +322,8 @@ foo-bar-1.0 = foo_bar_dep
**Note**: This is experimental and has no backwards or forwards compatibility guarantees.
See [Meson's rules on mixing build systems](Mixing-build-systems.md).
-Cargo subprojects automatically override the `<package_name>-<version>-rs` dependency
-name:
+Cargo subprojects automatically call `override_dependency` with the name
+`<package_name>-<version>-<suffix>`, where every part is defeined as follows:
- `package_name` is defined in `[package] name = ...` section of the `Cargo.toml`.
- `version` is the API version deduced from `[package] version = ...` as follow:
* `x.y.z` -> 'x'
@@ -331,9 +331,9 @@ name:
* `0.0.x` -> '0'
It allows to make different dependencies for incompatible versions of the same
crate.
-- `-rs` suffix is added to distinguish from regular system dependencies, for
- example `gstreamer-1.0` is a system pkg-config dependency and `gstreamer-0.22-rs`
- is a Cargo dependency.
+- the suffix is `-rs` for `rlib` and `dylib` crate types. The suffix is added to
+ distinguish Rust crates from C-ABI dependencies; for example `gstreamer-1.0`
+ is a system pkg-config dependency and `gstreamer-0.22-rs` is a Cargo dependency.
That means the `.wrap` file should have `dependency_names = foo-1-rs` in their
`[provide]` section when `Cargo.toml` has package name `foo` and version `1.2`.
@@ -361,10 +361,21 @@ Some naming conventions need to be respected:
This is typically used as `extra_args += ['--cfg', 'foo']`.
- The `extra_deps` variable is pre-defined and can be used to add extra dependencies.
This is typically used as `extra_deps += dependency('foo')`.
-
-Since *1.5.0* Cargo wraps can also be provided with `Cargo.lock` file at the root
-of (sub)project source tree. Meson will automatically load that file and convert
-it into a series of wraps definitions.
+- The `features` variable is pre-defined and contains the list of features enabled
+ on this crate.
+
+Since *1.5.0* Cargo wraps can also be provided with `Cargo.lock` file at the
+root of (sub)project source tree. Meson will automatically load that file, looking
+for crates found on `crates.io` or in a git repository, and will convert those
+crates into a series of wraps definitions. Since *1.11.0* the overlay directory
+(`patch_directory`) is automatically detected, using the same directory as the
+dependency name for `crates.io` URLs, and the final component of the URL
+(possibly with the `.git` suffix removed) for "git" URLs.
+
+Since *1.10.0* Workspace Cargo.toml are supported. For the time being it is
+recommended to regroup all Cargo dependencies inside a single workspace invoked
+from the main Meson project. When invoking multiple different Cargo subprojects
+from Meson, feature resolution of common dependencies might be wrong.
## Using wrapped projects
diff --git a/docs/markdown/Yaml-RefMan.md b/docs/markdown/Yaml-RefMan.md
index 2872791..cce844b 100644
--- a/docs/markdown/Yaml-RefMan.md
+++ b/docs/markdown/Yaml-RefMan.md
@@ -150,9 +150,9 @@ optargs:
...
varargs:
- name: Some name # [required]
- type: str | list[str | int] # [required]
- description: Some helpful text # [required]
+ name: Some name # [required]
+ type: str | array[str | int] # [required]
+ description: Some helpful text # [required]
since: 0.42.0
deprecated: 100.99.0
min_varargs: 1
diff --git a/docs/markdown/_Sidebar.md b/docs/markdown/_Sidebar.md
index ce73d5a..eb6afb6 100644
--- a/docs/markdown/_Sidebar.md
+++ b/docs/markdown/_Sidebar.md
@@ -9,6 +9,7 @@
### [Modules](Module-reference.md)
+* [codegen](Codegen-module.md)
* [gnome](Gnome-module.md)
* [i18n](i18n-module.md)
* [pkgconfig](Pkgconfig-module.md)
diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md
index da6fce7..767e0c8 100644
--- a/docs/markdown/i18n-module.md
+++ b/docs/markdown/i18n-module.md
@@ -83,14 +83,14 @@ i18n.xgettext(name, sources..., args: [...], recursive: false)
```
Invokes the `xgettext` program on given sources, to generate a `.pot` file.
-This function is to be used when the `gettext` function workflow it not suitable
+This function is to be used when the `gettext` function workflow is not suitable
for your project. For example, it can be used to produce separate `.pot` files
for each executable.
Positional arguments are the following:
* name `str`: the name of the resulting pot file.
-* sources `list[str|File|build_tgt|custom_tgt]`:
+* sources `array[str|File|build_tgt|custom_tgt|custom_idx]`:
source files or targets. May be a list of `string`, `File`, [[@build_tgt]],
or [[@custom_tgt]] returned from other calls to this function.
@@ -120,4 +120,7 @@ included to generate the final pot file. Therefore, adding a dependency to
source target will automatically add the translations of that dependency to the
needed translations for that source target.
+*New in 1.10.0* sources can be result of [[@custom_tgt]] or [[@custom_idx]].
+Before 1.10.0, custom targets were silently ignored.
+
*Added 1.8.0*
diff --git a/docs/markdown/index.md b/docs/markdown/index.md
index cbb7901..ea168c2 100644
--- a/docs/markdown/index.md
+++ b/docs/markdown/index.md
@@ -51,9 +51,7 @@ 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/).
-Other methods of communication include the [mailing
-list](https://groups.google.com/forum/#!forum/mesonbuild) (hosted by
-Google Groups) and the
+For general discussion, please use the
[Discussions](https://github.com/mesonbuild/meson/discussions) section
of the Meson GitHub repository.
diff --git a/docs/markdown/snippets/update_xc32_for_v5.md b/docs/markdown/snippets/update_xc32_for_v5.md
new file mode 100644
index 0000000..1263948
--- /dev/null
+++ b/docs/markdown/snippets/update_xc32_for_v5.md
@@ -0,0 +1,4 @@
+## XC32 support now aware of v5.00 features
+
+XC32 features introduced in v5.00 can now be used. This includes
+support for LTO auto and the C2x and CPP23 standards.
diff --git a/docs/meson.build b/docs/meson.build
index c476b59..f79f5f9 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -135,6 +135,8 @@ documentation = hotdoc.generate_doc(meson.project_name(),
extra_extension: meson.current_source_dir() / 'extensions' / 'refman_links.py',
refman_data_file: refman_md[1],
fatal_warnings: true,
+ devhelp_activate: true,
+ devhelp_online: 'https://mesonbuild.com/',
)
run_target('upload',
@@ -146,4 +148,4 @@ run_target('upload',
depends: documentation,
)
-test('validate_links', find_program('./validatelinks.py'), args: meson.current_source_dir() / 'markdown' / 'Users.md')
+test('validate_links', find_program('./validatelinks.py'), args: meson.current_source_dir() / 'markdown' / 'Users.md', timeout: 65)
diff --git a/docs/refman/generatormd.py b/docs/refman/generatormd.py
index 854712d..ebf57fa 100644
--- a/docs/refman/generatormd.py
+++ b/docs/refman/generatormd.py
@@ -44,6 +44,7 @@ _OBJ_ID_MAP = {
ObjectType.ELEMENTARY: 'elementary',
ObjectType.BUILTIN: 'builtin',
ObjectType.MODULE: 'module',
+ ObjectType.FUNCTIONS: 'functions',
ObjectType.RETURNED: 'returned',
}
@@ -299,7 +300,7 @@ class GeneratorMD(GeneratorBase):
def _write_functions(self) -> None:
data = {'functions': [self._gen_func_or_method(x) for x in self.functions]}
- self._write_template(data, 'root.functions')
+ self._write_template(data, f'root.{_OBJ_ID_MAP[ObjectType.FUNCTIONS]}')
def _root_refman_docs(self) -> None:
def gen_obj_links(objs: T.List[Object]) -> T.List[T.Dict[str, str]]:
@@ -330,6 +331,7 @@ class GeneratorMD(GeneratorBase):
self._write_template(data, 'root')
self._write_template({**dummy, 'name': 'Elementary types'}, f'root.{_OBJ_ID_MAP[ObjectType.ELEMENTARY]}', 'dummy')
self._write_template({**dummy, 'name': 'Builtin objects'}, f'root.{_OBJ_ID_MAP[ObjectType.BUILTIN]}', 'dummy')
+ self._write_functions()
self._write_template({**dummy, 'name': 'Returned objects'}, f'root.{_OBJ_ID_MAP[ObjectType.RETURNED]}', 'dummy')
if self.enable_modules:
@@ -339,7 +341,8 @@ class GeneratorMD(GeneratorBase):
def generate(self) -> None:
mlog.log('Generating markdown files...')
with mlog.nested():
- self._write_functions()
+ for fun in self.functions:
+ self._write_template(self._gen_func_or_method(fun), f'root.{_OBJ_ID_MAP[ObjectType.FUNCTIONS]}.{fun.name}', 'func')
for obj in self.objects:
if not self.enable_modules and (obj.obj_type == ObjectType.MODULE or obj.defined_by_module is not None):
continue
@@ -383,7 +386,7 @@ class GeneratorMD(GeneratorBase):
data[f'{obj.name}.{m.name}'] = f'{obj_file}#{obj.name}{m.name}'
# Functions
- funcs_file = self._gen_filename('root.functions', extension='html')
+ funcs_file = self._gen_filename(f'root.{_OBJ_ID_MAP[ObjectType.FUNCTIONS]}', extension='html')
for fn in self.functions:
data[fn.name] = f'{funcs_file}#{fn.name}'
diff --git a/docs/refman/model.py b/docs/refman/model.py
index 51d4ca2..1360d89 100644
--- a/docs/refman/model.py
+++ b/docs/refman/model.py
@@ -78,7 +78,8 @@ class ObjectType(Enum):
ELEMENTARY = 0
BUILTIN = 1
MODULE = 2
- RETURNED = 3
+ FUNCTIONS = 3
+ RETURNED = 4
@dataclass
class Object(NamedObject, FetureCheck):
diff --git a/docs/refman/templates/meson.vim.mustache b/docs/refman/templates/meson.vim.mustache
index d8f009e..a0c0513 100644
--- a/docs/refman/templates/meson.vim.mustache
+++ b/docs/refman/templates/meson.vim.mustache
@@ -27,7 +27,7 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-" http://mesonbuild.com/Syntax.html
+" https://mesonbuild.com/Syntax.html
syn keyword mesonConditional elif else if endif
syn keyword mesonRepeat foreach endforeach
syn keyword mesonOperator and not or in
@@ -54,7 +54,7 @@ syn match mesonEscape "\\N{\a\+\%(\s\a\+\)*}" contained
syn match mesonEscape "\\$"
" Meson only supports integer numbers
-" http://mesonbuild.com/Syntax.html#numbers
+" https://mesonbuild.com/Syntax.html#numbers
syn match mesonNumber "\<\d\+\>"
syn match mesonNumber "\<0x\x\+\>"
syn match mesonNumber "\<0o\o\+\>"
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index fd37185..a71e954 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -38,6 +38,7 @@ index.md
Code-formatting.md
Modules.md
CMake-module.md
+ Codegen-module.md
Cuda-module.md
Dlang-module.md
External-Project-module.md
@@ -55,6 +56,7 @@ index.md
Qt6-module.md
Rust-module.md
Simd-module.md
+ Snippets-module.md
SourceSet-module.md
Windows-module.md
i18n-module.md
@@ -88,6 +90,8 @@ index.md
Wrap-best-practices-and-tips.md
Shipping-prebuilt-binaries-as-wraps.md
Release-notes.md
+ Release-notes-for-1.10.0.md
+ Release-notes-for-1.9.0.md
Release-notes-for-1.8.0.md
Release-notes-for-1.7.0.md
Release-notes-for-1.6.0.md
diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html
index 65a21a2..ac4a6fe 100644
--- a/docs/theme/extra/templates/navbar_links.html
+++ b/docs/theme/extra/templates/navbar_links.html
@@ -7,6 +7,7 @@
<ul class="dropdown-menu" id="modules-menu">
@for tup in [ \
("CMake-module.html","CMake"), \
+ ("Codegen-module.html","Codegen"), \
("Cuda-module.html","CUDA"), \
("Dlang-module.html","Dlang"), \
("External-Project-module.html","External Project"), \
@@ -25,6 +26,7 @@
("Qt6-module.html","Qt6"), \
("Rust-module.html","Rust"), \
("Simd-module.html","Simd"), \
+ ("Snippets-module.html","Snippets"), \
("SourceSet-module.html","SourceSet"), \
("Wayland-module.html","Wayland"), \
("Windows-module.html","Windows")]:
diff --git a/docs/yaml/builtins/meson.yaml b/docs/yaml/builtins/meson.yaml
index 516e41c..eac8368 100644
--- a/docs/yaml/builtins/meson.yaml
+++ b/docs/yaml/builtins/meson.yaml
@@ -139,7 +139,7 @@ methods:
to install only a subset of the files.
By default the script has no install tag which means it is not being run when
`meson install --tags` argument is specified.
-
+
dry_run:
type: bool
since: 1.1.0
@@ -447,12 +447,12 @@ methods:
description: Returns the version string specified in [[project]] function call.
- name: project_license
- returns: list[str]
+ returns: array[str]
since: 0.45.0
description: Returns the array of licenses specified in [[project]] function call.
- name: project_license_files
- returns: list[file]
+ returns: array[file]
since: 1.1.0
description: Returns the array of license files specified in the [[project]] function call.
@@ -498,10 +498,10 @@ methods:
posargs:
env:
- type: env | str | list[str] | dict[str] | dict[list[str]]
+ type: env | str | array[str] | dict[str] | dict[array[str]]
description: |
The [[@env]] object to add.
- Since *0.62.0* list of strings is allowed in dictionary values. In that
+ Since *0.62.0* array of strings is allowed in dictionary values. In that
case values are joined using the separator.
kwargs:
separator:
diff --git a/docs/yaml/elementary/array.yml b/docs/yaml/elementary/array.yml
new file mode 100644
index 0000000..7183d1a
--- /dev/null
+++ b/docs/yaml/elementary/array.yml
@@ -0,0 +1,74 @@
+name: array
+long_name: Array
+description: An array of elements. See [arrays](Syntax.md#arrays).
+is_container: true
+
+methods:
+- name: contains
+ returns: bool
+ description: |
+ Returns `true` if the array contains the object
+ given as argument, `false` otherwise
+
+ arg_flattening: false
+
+ posargs:
+ item:
+ type: any
+ description: The item to check
+
+- name: get
+ returns: any
+ description: |
+ returns the object at the given index,
+ negative indices count from the back of the array, indexing out of
+ bounds returns the `fallback` value *(since 0.38.0)* or, if it is
+ not specified, causes a fatal error
+
+ arg_flattening: false
+
+ posargs:
+ index:
+ type: int
+ description: Index of the array position to query. Negative values start at the end of the array
+
+ optargs:
+ fallback:
+ type: any
+ description: Fallback value that is returned if the index is out of range.
+
+- name: slice
+ returns: array[any]
+ since: 1.10.0
+ description: |
+ Return a selection of the elements of the array starting at index `start`
+ and continuing with `step` size jumps until `stop`. Negative indices count
+ from the back of the array. The step size cannot be zero, but may be
+ negative. If it is negative, `start` and `stop` default to the end and
+ beginning of the array, respectively. If `step` is positive, `start`
+ defaults to 0 and `stop` defaults to the length of the array. Either both
+ or none of `start` and `stop` must be provided to prevent ambiguity.
+
+ optargs:
+ start:
+ type: int
+ description: The lower bound of the slice
+
+ stop:
+ type: int
+ description: The upper bound of the slice
+
+ kwargs:
+ step:
+ type: int
+ default: 1
+ description: The step size
+
+- name: length
+ returns: int
+ description: Returns the current size of the array.
+
+- name: flatten
+ returns: array[any]
+ since: 1.9.0
+ description: Returns a flattened copy of the array, with all nested arrays removed.
diff --git a/docs/yaml/elementary/dict.yml b/docs/yaml/elementary/dict.yml
index 19263df..525fbee 100644
--- a/docs/yaml/elementary/dict.yml
+++ b/docs/yaml/elementary/dict.yml
@@ -44,5 +44,12 @@ methods:
description: Fallback value that is returned if the key is not in the [[@dict]].
- name: keys
- returns: list[str]
- description: Returns an array of keys in the dictionary.
+ returns: array[str]
+ description: Returns an array of keys in the dictionary, sorted in ascending order.
+
+- name: values
+ returns: array[any]
+ since: 1.10.0
+ description: |
+ Returns an array of values in the dictionary, sorted by the
+ corresponding keys in ascending order.
diff --git a/docs/yaml/elementary/list.yml b/docs/yaml/elementary/list.yml
deleted file mode 100644
index 1ffb6d2..0000000
--- a/docs/yaml/elementary/list.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: list
-long_name: List
-description: An array of elements. See [arrays](Syntax.md#arrays).
-is_container: true
-
-methods:
-- name: contains
- returns: bool
- description: |
- Returns `true` if the array contains the object
- given as argument, `false` otherwise
-
- arg_flattening: false
-
- posargs:
- item:
- type: any
- description: The item to check
-
-- name: get
- returns: any
- description: |
- returns the object at the given index,
- negative indices count from the back of the array, indexing out of
- bounds returns the `fallback` value *(since 0.38.0)* or, if it is
- not specified, causes a fatal error
-
- arg_flattening: false
-
- posargs:
- index:
- type: int
- description: Index of the list position to query. Negative values start at the end of the list
-
- optargs:
- fallback:
- type: any
- description: Fallback value that is returned if the index is out of range.
-
-- name: length
- returns: int
- description: Returns the current size of the array / list.
diff --git a/docs/yaml/elementary/str.yml b/docs/yaml/elementary/str.yml
index 44aa742..c2b6c35 100644
--- a/docs/yaml/elementary/str.yml
+++ b/docs/yaml/elementary/str.yml
@@ -203,7 +203,7 @@ methods:
# str.split
- name: split
- returns: list[str]
+ returns: array[str]
description: |
Splits the string at the specified character
(or whitespace if not set) and returns the parts in an
@@ -224,7 +224,7 @@ methods:
description: Specifies the character / substring where to split the string.
- name: splitlines
- returns: list[str]
+ returns: array[str]
since: 1.2.0
description: |
Splits the string into an array of lines.
@@ -250,7 +250,7 @@ methods:
returns: str
description: |
The opposite of split,
- for example `'.'.join(['a', 'b', 'c']` yields `'a.b.c'`.
+ for example `'.'.join(['a', 'b', 'c'])` yields `'a.b.c'`.
example: |
```meson
@@ -270,7 +270,7 @@ methods:
The strings to join with the current string.
Before Meson *0.60.0* this function only accepts a single positional
- argument of the type [[list[str]]].
+ argument of the type [[array[str]]].
# str.underscorify
- name: underscorify
diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml
index 1721b29..1129533 100644
--- a/docs/yaml/functions/_build_target_base.yaml
+++ b/docs/yaml/functions/_build_target_base.yaml
@@ -43,13 +43,13 @@ kwargs:
description: precompiled header file to use for the given language
<lang>_args:
- type: list[str]
+ type: array[str]
description: |
compiler flags to use for the given language;
eg: `cpp_args` for C++
vala_args:
- type: list[str | file]
+ type: array[str | file]
description: |
Compiler flags for Vala. Unlike other languages this may contain Files
@@ -74,7 +74,7 @@ kwargs:
but which will be removed on install
dependencies:
- type: list[dep]
+ type: array[dep]
description: |
one or more dependency objects
created with
@@ -83,10 +83,12 @@ kwargs:
(for deps built by the project)
extra_files:
- type: str | file | custom_tgt | custom_idx
+ type: str | file
description: |
Not used for the build itself but are shown as source files in IDEs
- that group files by targets (such as Visual Studio)
+ that group files by targets (such as Visual Studio).
+
+ These may only be static sources.
gui_app:
type: bool
@@ -98,7 +100,7 @@ kwargs:
0.56.0, use `win_subsystem` instead.
link_args:
- type: list[str]
+ type: array[str]
description: |
Flags to use during linking. You can use UNIX-style
flags here for all platforms.
@@ -125,7 +127,7 @@ kwargs:
*(broken until 0.55.0)*
link_whole:
- type: list[lib | custom_tgt | custom_idx]
+ type: array[lib | custom_tgt | custom_idx]
since: 0.40.0
description: |
Links all contents of the given static libraries whether they are used or
@@ -133,18 +135,18 @@ kwargs:
'/WHOLEARCHIVE' MSVC linker option. This allows the linked target to
re-export symbols from all objects in the static libraries.
- *(since 0.41.0)* If passed a list that list will be flattened.
+ *(since 0.41.0)* If passed an array that array will be flattened.
*(since 0.51.0)* This argument also accepts outputs produced by
custom targets. The user must ensure that the output is a library in
the correct format.
link_with:
- type: list[lib | custom_tgt | custom_idx]
+ type: array[lib | custom_tgt | custom_idx]
description: |
One or more shared or static libraries
- (built by this project) that this target should be linked with. *(since 0.41.0)* If passed a
- list this list will be flattened. *(since 0.51.0)* The arguments can also be custom targets.
+ (built by this project) that this target should be linked with. *(since 0.41.0)* If passed an
+ array that array will be flattened. *(since 0.51.0)* The arguments can also be custom targets.
In this case Meson will assume that merely adding the output file in the linker command
line is sufficient to make linking work. If this is not sufficient,
then the build system writer must write all other steps manually.
@@ -156,7 +158,7 @@ kwargs:
description: Controls whether Meson adds the current source and build directories to the include path
include_directories:
- type: list[inc | str]
+ type: array[inc | str]
description: |
one or more objects created with the [[include_directories]] function,
or *(since 0.50.0)* strings, which will be transparently expanded to include directory objects
@@ -175,7 +177,7 @@ kwargs:
something like this: `install_dir : get_option('libdir') / 'projectname-1.0'`.
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
Specify the file mode in symbolic format
@@ -198,7 +200,7 @@ kwargs:
(but *not* before that). On Windows, this argument has no effect.
objects:
- type: list[extracted_obj | file | str]
+ type: array[extracted_obj | file | str]
description: |
List of object files that should be linked in this target.
@@ -208,7 +210,7 @@ kwargs:
object files had to be placed in `sources`.
name_prefix:
- type: str | list[void]
+ type: str | array[void]
description: |
The string that will be used as the prefix for the
target output filename by overriding the default (only used for
@@ -219,7 +221,7 @@ kwargs:
Set this to `[]`, or omit the keyword argument for the default behaviour.
name_suffix:
- type: str | list[void]
+ type: str | array[void]
description: |
The string that will be used as the extension for the
target by overriding the default. By default on Windows this is
@@ -235,7 +237,7 @@ kwargs:
Set this to `[]`, or omit the keyword argument for the default behaviour.
override_options:
- type: list[str] | dict[str | bool | int | list[str]]
+ type: array[str] | dict[str | bool | int | array[str]]
since: 0.40.0
description: |
takes an array of strings in the same format as `project`'s `default_options`
@@ -253,10 +255,12 @@ kwargs:
`default`, `internal`, `hidden`, `protected` or `inlineshidden`, which
is the same as `hidden` but also includes things like C++ implicit
constructors as specified in the GCC manual. Ignored on compilers that
- do not support GNU visibility arguments.
+ do not support GNU visibility arguments. See also
+ [`snippets.symbol_visibility_header()`](Snippets-module.md#symbol_visibility_header)
+ method to help with defining public API.
d_import_dirs:
- type: list[inc | str]
+ type: array[inc | str]
since: 0.62.0
description: |
the directories to add to the string search path (i.e. `-J` switch for DMD).
@@ -268,11 +272,11 @@ kwargs:
description: When set to true, the D modules are compiled in debug mode.
d_module_versions:
- type: list[str | int]
+ type: array[str | int]
description: List of module version identifiers set when compiling D sources.
d_debug:
- type: list[str]
+ type: array[str]
description: |
The [D version identifiers](https://dlang.org/spec/version.html#version) to add
during the compilation of D source files.
@@ -323,8 +327,50 @@ kwargs:
type: dict[str]
since: 1.2.0
description: |
- On rust targets this provides a map of library names to the crate name
- with which it would be available inside the rust code.
+ On rust targets this allows giving custom names to the crates that are
+ linked into the target. For example, passing a dependency map of
+ `{ 'gtk4': 'gtk' }` has the same effect of writing `gtk.package = "gtk4"`
+ in the `dependencies` section of a Cargo.toml file, or
+ `extern crate gtk4 as gtk` inside Rust code.
+
+ *Since 1.10.0*, the keys can be either crate names or target names.
+
+ vala_header:
+ type: str
+ description: |
+ On Vala targets, this provides a way to override the name of the generated
+ C compatible header for targets that generate them.
- This allows renaming similar to the dependency renaming feature of cargo
- or `extern crate foo as bar` inside rust code.
+ If it is not set the default will be calculated from the target's name.
+
+ vala_vapi:
+ type: str
+ description: |
+ On Vala targets, this provides a way to override the name of the generated
+ VAPI file.
+
+ If it is not set the default will be calculated from the target's name.
+
+ vala_gir:
+ type: str
+ description: |
+ If set, generates a GIR file with the given name. If this is unset then
+ no GIR file will be generated.
+
+ build_subdir:
+ type: str
+ since: 1.10.0
+ description:
+ Places the build results in a subdirectory of the given name
+ rather than directly into the build directory. This does not
+ affect the install directory, which uses install_dir.
+
+ This allows inserting a directory name into the build path,
+ either when needed to use the build result while building other
+ targets or as a way to support multiple targets with the same
+ basename by using unique build_subdir values for each one.
+
+ To prevent collisions within the build directory, build_subdir
+ is not allowed to match a file or directory in the source
+ directory, nor contain '..' to refer to the parent of the build
+ directory.
diff --git a/docs/yaml/functions/add_global_arguments.yaml b/docs/yaml/functions/add_global_arguments.yaml
index 3b26d10..25e4eef 100644
--- a/docs/yaml/functions/add_global_arguments.yaml
+++ b/docs/yaml/functions/add_global_arguments.yaml
@@ -15,11 +15,11 @@ varargs:
kwargs:
language:
- type: list[str]
+ type: array[str]
required: true
description: |
Specifies the language(s) that the arguments should be
- applied to. If a list of languages is given, the arguments are added
+ applied to. If an array of languages is given, the arguments are added
to each of the corresponding compiler command lines. Note that there
is no way to remove an argument set in this way. If you have an
argument that is only used in a subset of targets, you have to specify
diff --git a/docs/yaml/functions/add_test_setup.yaml b/docs/yaml/functions/add_test_setup.yaml
index 3d666c7..a29b137 100644
--- a/docs/yaml/functions/add_test_setup.yaml
+++ b/docs/yaml/functions/add_test_setup.yaml
@@ -18,7 +18,7 @@ posargs:
kwargs:
env:
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
description: |
environment variables to set
, such as `['NAME1=value1', 'NAME2=value2']`,
@@ -26,7 +26,7 @@ kwargs:
environment juggling. *(Since 0.52.0)* A dictionary is also accepted.
exe_wrapper:
- type: list[str | external_program]
+ type: array[str | external_program]
description: The command or script followed by the arguments to it
gdb:
@@ -52,9 +52,9 @@ kwargs:
without the `--setup` option.
exclude_suites:
- type: list[str]
+ type: array[str]
since: 0.57.0
description:
- A list of test suites that should be excluded when using this setup.
+ An array of test suites that should be excluded when using this setup.
Suites specified in the `--suite` option
- to `meson test` will always run, overriding `add_test_setup` if necessary. \ No newline at end of file
+ to `meson test` will always run, overriding `add_test_setup` if necessary.
diff --git a/docs/yaml/functions/benchmark.yaml b/docs/yaml/functions/benchmark.yaml
index 7a555a4..fe69f28 100644
--- a/docs/yaml/functions/benchmark.yaml
+++ b/docs/yaml/functions/benchmark.yaml
@@ -28,11 +28,11 @@ posargs:
kwargs:
args:
- type: list[str | file | tgt | external_program]
+ type: array[str | file | tgt | external_program]
description: Arguments to pass to the executable
env:
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
description: |
environment variables to set, such as `['NAME1=value1',
'NAME2=value2']`, or an [[@env]] object which allows more sophisticated
@@ -46,11 +46,11 @@ kwargs:
executable returns a non-zero return value (i.e. reports an error)
suite:
- type: str | list[str]
+ type: str | array[str]
description: |
- `'label'` (or list of labels `['label1', 'label2']`)
+ `'label'` (or array of labels `['label1', 'label2']`)
attached to this test. The suite name is qualified by a (sub)project
- name resulting in `(sub)project_name:label`. In the case of a list
+ name resulting in `(sub)project_name:label`. In the case of an array
of strings, the suite names will be `(sub)project_name:label1`,
`(sub)project_name:label2`, etc.
@@ -70,7 +70,7 @@ kwargs:
for the test
depends:
- type: list[build_tgt | custom_tgt]
+ type: array[build_tgt | custom_tgt]
since: 0.46.0
description: |
specifies that this test depends on the specified
diff --git a/docs/yaml/functions/build_target.yaml b/docs/yaml/functions/build_target.yaml
index a56fe75..f883e87 100644
--- a/docs/yaml/functions/build_target.yaml
+++ b/docs/yaml/functions/build_target.yaml
@@ -26,9 +26,9 @@ description: |
build_target(<arguments and keyword arguments>, target_type : 'executable')
```
- The lists for the kwargs (such as `sources`, `objects`, and `dependencies`) are
- always flattened, which means you can freely nest and add lists while
- creating the final list.
+ The arrays for the kwargs (such as `sources`, `objects`, and `dependencies`) are
+ always flattened, which means you can freely nest and add arrays while
+ creating the final array.
The returned object also has methods that are documented in [[@build_tgt]].
diff --git a/docs/yaml/functions/configure_file.yaml b/docs/yaml/functions/configure_file.yaml
index 20b96aa..32cb559 100644
--- a/docs/yaml/functions/configure_file.yaml
+++ b/docs/yaml/functions/configure_file.yaml
@@ -12,10 +12,12 @@ description: |
A dictionary can be passed instead of a
[[@cfg_data]] object.
- When a list of strings is passed to the `command:` keyword argument,
+ When an array of strings is passed to the `command:` keyword argument,
it takes any source or configured file as the `input:` and assumes
that the `output:` is produced when the specified command is run.
+ You can install the outputted file with the `install_dir:` kwarg, see below.
+
*(since 0.47.0)* When the `copy:` keyword argument is set to `true`,
this function will copy the file provided in `input:` to a file in the
build directory with the name `output:` in the current directory.
@@ -34,7 +36,7 @@ kwargs:
file specified as `output`.
command:
- type: list[str | file]
+ type: array[str | file]
description: |
As explained above, if specified, Meson does not create
the file itself but rather runs the specified command, which allows
@@ -103,7 +105,7 @@ kwargs:
string, the file is not installed.
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
Specify the file mode in symbolic format
@@ -153,3 +155,21 @@ kwargs:
description: |
When specified, macro guards will be used instead of '#pragma once'. The
macro guard name will be the specified name.
+
+ build_subdir:
+ type: str
+ since: 1.10.0
+ description:
+ Places the build results in a subdirectory of the given name
+ rather than directly into the build directory. This does not
+ affect the install directory, which uses install_dir.
+
+ This allows inserting a directory name into the build path,
+ either when needed to use the build result while building other
+ targets or as a way to support multiple targets with the same
+ basename by using unique build_subdir values for each one.
+
+ To prevent collisions within the build directory, build_subdir
+ is not allowed to match a file or directory in the source
+ directory, nor contain '..' to refer to the parent of the build
+ directory.
diff --git a/docs/yaml/functions/custom_target.yaml b/docs/yaml/functions/custom_target.yaml
index 585d260..5836bb7 100644
--- a/docs/yaml/functions/custom_target.yaml
+++ b/docs/yaml/functions/custom_target.yaml
@@ -12,10 +12,16 @@ description: |
custom_target('foo', output: 'file.txt', ...)
```
+ The files passed to `output:` cannot contain path separators. See the
+ [manual on custom build targets](Custom-build-targets.md#details-on-command-invocation) for
+ an explanation on where output files should be placed.
+
+ You can install the outputted files with the `install_dir:` kwarg, see below.
+
*Since 0.60.0* the name argument is optional and defaults to the basename of the first
output (`file.txt` in the example above).
- The list of strings passed to the `command` keyword argument accept
+ The array of strings passed to the `command` keyword argument accept
the following special string substitutions:
- `@INPUT@`: the full path to the input passed to `input`. If more than
@@ -103,7 +109,7 @@ kwargs:
There are some compilers that can't be told to write
their output to a file but instead write it to standard output. When
this argument is set to true, Meson captures `stdout` and writes it
- to the target file. Note that your command argument list may not
+ to the target file. Note that your command argument array may not
contain `@OUTPUT@` when capture mode is active.
console:
@@ -118,7 +124,7 @@ kwargs:
serializing all targets in this pool.
command:
- type: list[str | file | exe | external_program]
+ type: array[str | file | exe | external_program]
description: |
Command to run to create outputs from inputs. The command
may be strings or the return value of functions that return file-like
@@ -132,7 +138,7 @@ kwargs:
-arg2'` as the latter will *not* work.
depend_files:
- type: list[str | file]
+ type: array[str | file]
description: |
files ([[@str]],
[[@file]], or the return value of [[configure_file]] that
@@ -140,7 +146,7 @@ kwargs:
argument. Useful for adding regen dependencies.
depends:
- type: list[build_tgt | custom_tgt | custom_idx]
+ type: array[build_tgt | custom_tgt | custom_idx]
description: |
Specifies that this target depends on the specified
target(s), even though it does not take any of them as a command
@@ -162,8 +168,8 @@ kwargs:
are also accepted.
input:
- type: list[str | file]
- description: List of source files. *(since 0.41.0)* the list is flattened.
+ type: array[str | file]
+ description: Array of source files. *(since 0.41.0)* the array is flattened.
install:
type: bool
@@ -171,7 +177,7 @@ kwargs:
the install step (see `install_dir` for details).
install_dir:
- type: str | list[str | bool]
+ type: str | array[str | bool]
description: |
If only one install_dir is provided, all outputs are installed there.
*Since 0.40.0* Allows you to specify the installation directory for each
@@ -195,17 +201,17 @@ kwargs:
This would install `second.file` to `otherdir` and not install `first.file`.
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
The file mode and optionally the owner/uid and group/gid.
See the `install_mode` kwarg of [[install_data]] for more information.
install_tag:
- type: list[str]
+ type: array[str]
since: 0.60.0
description: |
- A list of strings, one per output, used by the `meson install --tags` command
+ An array of strings, one per output, used by the `meson install --tags` command
to install only a subset of the files.
By default all outputs have no install tag which means they are not being
@@ -214,12 +220,13 @@ kwargs:
outputs that have no tag or are not installed.
output:
- type: list[str]
- description: List of output files.
+ type: array[str]
+ description: |
+ Array of output files. These cannot contain path separators.
env:
since: 0.57.0
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
description: |
environment variables to set, such as
`{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`,
@@ -234,4 +241,4 @@ kwargs:
There are some compilers that can't be told to read
their input from a file and instead read it from standard input. When this
argument is set to `true`, Meson feeds the input file to `stdin`. Note that
- your argument list may not contain `@INPUT@` when feed mode is active.
+ your argument array may not contain `@INPUT@` when feed mode is active.
diff --git a/docs/yaml/functions/debug.yaml b/docs/yaml/functions/debug.yaml
index a64bd92..4a4508c 100644
--- a/docs/yaml/functions/debug.yaml
+++ b/docs/yaml/functions/debug.yaml
@@ -5,10 +5,10 @@ description: Write the argument string to the meson build log.
posargs:
message:
- type: str | int | bool | list[str | int | bool] | dict[str | int | bool]
+ type: str | int | bool | array[str | int | bool] | dict[str | int | bool]
description: The message to print
varargs:
name: msg
- type: str | int | bool | list[str | int | bool] | dict[str | int | bool]
+ type: str | int | bool | array[str | int | bool] | dict[str | int | bool]
description: Additional parameters will be separated by spaces
diff --git a/docs/yaml/functions/declare_dependency.yaml b/docs/yaml/functions/declare_dependency.yaml
index 848082d..15d1606 100644
--- a/docs/yaml/functions/declare_dependency.yaml
+++ b/docs/yaml/functions/declare_dependency.yaml
@@ -12,41 +12,41 @@ description: |
kwargs:
compile_args:
- type: list[str]
+ type: array[str]
description: Compile arguments to use.
dependencies:
- type: list[dep]
+ type: array[dep]
description: Other dependencies needed to use this dependency.
include_directories:
- type: list[inc | str]
+ type: array[inc | str]
description: |
the directories to add to header search path,
must be [[@inc]] objects or *(since 0.50.0)* plain strings.
link_args:
- type: list[str]
+ type: array[str]
description: Link arguments to use.
link_with:
- type: list[lib]
+ type: array[lib]
description: Libraries to link against.
link_whole:
- type: list[lib]
+ type: array[lib]
since: 0.46.0
description: Libraries to link fully, same as [[executable]].
sources:
- type: list[str | file | custom_tgt | custom_idx | generated_list]
+ type: array[str | file | custom_tgt | custom_idx | generated_list]
description: |
sources to add to targets
(or generated header files
that should be built before sources including them are built)
extra_files:
- type: list[str | file]
+ type: array[str | file]
since: 1.2.0
description: |
extra files to add to targets.
@@ -59,31 +59,31 @@ kwargs:
such as `1.2.3`. Defaults to the project version.
variables:
- type: dict[str] | list[str]
+ type: dict[str] | array[str]
since: 0.54.0
description: |
a dictionary of arbitrary strings,
this is meant to be used
in subprojects where special variables would be provided via cmake or
- pkg-config. *since 0.56.0* it can also be a list of `'key=value'` strings.
+ pkg-config. *since 0.56.0* it can also be an array of `'key=value'` strings.
d_module_versions:
- type: str | int | list[str | int]
+ type: str | int | array[str | int]
since: 0.62.0
description: |
The [D version identifiers](https://dlang.org/spec/version.html#version) to add
during the compilation of D source files.
d_import_dirs:
- type: list[inc | str]
+ type: array[inc | str]
since: 0.62.0
description: |
the directories to add to the string search path (i.e. `-J` switch for DMD).
Must be [[@inc]] objects or plain strings.
objects:
- type: list[extracted_obj]
+ type: array[extracted_obj]
since: 1.1.0
description: |
- a list of object files, to be linked directly into the targets that use the
+ an array of object files, to be linked directly into the targets that use the
dependency.
diff --git a/docs/yaml/functions/dependency.yaml b/docs/yaml/functions/dependency.yaml
index a19deab..c337769 100644
--- a/docs/yaml/functions/dependency.yaml
+++ b/docs/yaml/functions/dependency.yaml
@@ -78,7 +78,7 @@ varargs:
kwargs:
default_options:
- type: list[str] | dict[str | bool | int | list[str]]
+ type: array[str] | dict[str | bool | int | array[str]]
since: 0.38.0
description: |
An array of default option values
@@ -103,7 +103,7 @@ kwargs:
for more details.
fallback:
- type: list[str] | str
+ type: array[str] | str
description: |
Manually specifies a subproject fallback
to use in case the dependency is not found in the system.
@@ -120,7 +120,7 @@ kwargs:
in this case the subproject must use
`meson.override_dependency('dependency_name', subproj_dep)`
to specify the dependency object used in the superproject.
- If the value is an empty list, it has the same effect as
+ If the value is an empty array it has the same effect as
`allow_fallback: false`.
language:
@@ -174,21 +174,26 @@ kwargs:
libraries instead of dynamic ones (note that this is not supported
by all dependency backends)
+ Leaving this value unset will result in an implementation defined default
+ value. For most dependencies this means the value of the `prefer_static`
+ option, but some custom dependencies have their own method for determining
+ the most useful default option.
+
*Since 0.60.0* it also sets `default_library` option accordingly on the fallback
subproject if it was not set explicitly in `default_options` keyword argument.
- *Since 0.63.0* when the `prefer_static` option is set to `true` the default
- value is `true` otherwise the default value is `false`.
+ *Since 0.63.0* when the `prefer_static` option is used to calculate the
+ default value.
version:
- type: list[str] | str
+ type: array[str] | str
since: 0.37.0
description: |
Specifies the required version,
a string containing a
comparison operator followed by the version string, examples include
`>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching.
- You can also specify multiple restrictions by passing a list to this
+ You can also specify multiple restrictions by passing an array to this
keyword argument, such as: `['>=3.14.0', '<=4.1.0']`.
These requirements are never met if the version is unknown.
diff --git a/docs/yaml/functions/environment.yaml b/docs/yaml/functions/environment.yaml
index 4c18ffc..1f6e198 100644
--- a/docs/yaml/functions/environment.yaml
+++ b/docs/yaml/functions/environment.yaml
@@ -7,12 +7,12 @@ arg_flattening: false
optargs:
env:
- type: str | list[str] | dict[str] | dict[list[str]]
+ type: str | array[str] | dict[str] | dict[array[str]]
since: 0.52.0
description: |
If provided, each key/value pair is added into the [[@env]] object
as if [[env.set]] method was called for each of them.
- Since *0.62.0* list of strings is allowed in dictionary values. In that
+ Since *0.62.0* arrays of strings are allowed in dictionary values. In that
case values are joined using the separator.
kwargs:
diff --git a/docs/yaml/functions/executable.yaml b/docs/yaml/functions/executable.yaml
index df71b79..c819a38 100644
--- a/docs/yaml/functions/executable.yaml
+++ b/docs/yaml/functions/executable.yaml
@@ -4,9 +4,9 @@ description: |
Creates a new executable. The first argument specifies its name and
the remaining positional arguments define the input files to use.
- The lists for the kwargs (such as `sources`, `objects`, and `dependencies`) are
- always flattened, which means you can freely nest and add lists while
- creating the final list.
+ The arrays for the kwargs (such as `sources`, `objects`, and `dependencies`) are
+ always flattened, which means you can freely nest and add arrays while
+ creating the final array.
The returned object also has methods that are documented in [[@exe]].
@@ -37,15 +37,18 @@ kwargs:
since: 0.45.0
description: |
when set to true causes the target's symbols to be
- dynamically exported, allowing modules built using the
- [[shared_module]] function to refer to functions,
- variables and other symbols defined in the executable itself. Implies
- the `implib` argument.
+ dynamically exported, allowing modules built using the
+ [[shared_module]] function to refer to functions,
+ variables and other symbols defined in the executable itself.
implib:
type: bool | str
since: 0.42.0
description: |
+ When set to a string, that will be the name of a generated import library.
+
+ *Since 1.10.0* passing a boolean value is deprecated.
+
When set to true, an import library is generated for the
executable (the name of the import library is based on *exe_name*).
Alternatively, when set to a string, that gives the base name for
diff --git a/docs/yaml/functions/files.yaml b/docs/yaml/functions/files.yaml
index ca72745..4d701e1 100644
--- a/docs/yaml/functions/files.yaml
+++ b/docs/yaml/functions/files.yaml
@@ -1,5 +1,5 @@
name: files
-returns: list[file]
+returns: array[file]
description: |
This command takes the strings given to it in arguments and returns
corresponding File objects that you can use as sources for build
diff --git a/docs/yaml/functions/find_program.yaml b/docs/yaml/functions/find_program.yaml
index 1899941..110f5df 100644
--- a/docs/yaml/functions/find_program.yaml
+++ b/docs/yaml/functions/find_program.yaml
@@ -24,7 +24,7 @@ description: |
(because the command invocator will reject the command otherwise) and
Unixes (if the script file does not have the executable bit set).
Hence, you *must not* manually add the interpreter while using this
- script as part of a list of commands. Since *0.50.0* if the "python3"
+ script as part of an array of commands. Since *0.50.0* if the "python3"
program is requested and it is not found in the system, Meson will return
its current interpreter.
@@ -98,7 +98,7 @@ kwargs:
instead of a not-found object.
version:
- type: str | list[str]
+ type: str | array[str]
since: 0.52.0
description: |
Specifies the required version, see
@@ -117,12 +117,12 @@ kwargs:
If this is unspecified, `program_name --version` will be used.
dirs:
- type: list[str]
+ type: array[str]
since: 0.53.0
- description: extra list of absolute paths where to look for program names.
+ description: extra array of absolute paths where to look for program names.
default_options:
- type: list[str] | dict[str | bool | int | list[str]]
+ type: array[str] | dict[str | bool | int | array[str]]
since: 1.3.0
description: |
An array of default option values
diff --git a/docs/yaml/functions/generator.yaml b/docs/yaml/functions/generator.yaml
index 6079d30..c505394 100644
--- a/docs/yaml/functions/generator.yaml
+++ b/docs/yaml/functions/generator.yaml
@@ -45,12 +45,12 @@ posargs:
kwargs:
arguments:
- type: list[str]
- description: A list of template strings that will be the command line arguments passed to the executable.
+ type: array[str]
+ description: An array of template strings that will be the command line arguments passed to the executable.
depends:
# Not sure why this is not just `target`
- type: list[build_tgt | custom_tgt | custom_idx]
+ type: array[build_tgt | custom_tgt | custom_idx]
since: 0.51.0
description: |
An array of build targets that must be built before
@@ -68,9 +68,9 @@ kwargs:
recompilation,
output:
- type: list[str]
+ type: array[str]
description: |
- Template string (or list of template strings) defining
+ Template string (or array of template strings) defining
how an output file name is (or multiple output names are) generated
from a single source file name.
diff --git a/docs/yaml/functions/get_option.yaml b/docs/yaml/functions/get_option.yaml
index 0934758..e23e380 100644
--- a/docs/yaml/functions/get_option.yaml
+++ b/docs/yaml/functions/get_option.yaml
@@ -1,5 +1,5 @@
name: get_option
-returns: str | int | bool | feature | list[str | int | bool]
+returns: str | int | bool | feature | array[str | int | bool]
description: |
Obtains the value of the [project build option](Build-options.md)
specified in the positional argument.
@@ -20,6 +20,11 @@ description: |
See [`feature` options](Build-options.md#features)
documentation for more details.
+ For options that are [specified
+ per-machine](Builtin-options.md#specifying-options-per-machine)
+ `get_option()` retrieves the value of the option for the
+ build machine if the argument starts with `build.`.
+
posargs:
option_name:
type: str
diff --git a/docs/yaml/functions/install_data.yaml b/docs/yaml/functions/install_data.yaml
index 9ed09a7..516ca14 100644
--- a/docs/yaml/functions/install_data.yaml
+++ b/docs/yaml/functions/install_data.yaml
@@ -2,6 +2,9 @@ name: install_data
returns: void
description: |
Installs files from the source tree that are listed as positional arguments.
+ Please note that this can only install static files from the source tree.
+ Generated files are installed via the `install_dir:` kwarg on the respective
+ generators, such as `custom_target()` or `configure_file()`.
See [Installing](Installing.md) for more examples.
@@ -25,7 +28,7 @@ kwargs:
If omitted, the directory defaults to `{datadir}/{projectname}` *(since 0.45.0)*.
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.38.0
description: |
specify the file mode in symbolic format and
@@ -58,16 +61,16 @@ kwargs:
This is equivalent to GNU Automake's `nobase` option.
rename:
- type: list[str]
+ type: array[str]
since: 0.46.0
description: |
- If specified renames each source file into corresponding file from `rename` list.
+ If specified renames each source file into corresponding file from `rename` array.
Nested paths are allowed and they are
- joined with `install_dir`. Length of `rename` list must be equal to
+ joined with `install_dir`. Length of `rename` array must be equal to
the number of sources.
sources:
- type: list[file | str]
+ type: array[file | str]
description: Additional files to install.
follow_symlinks:
diff --git a/docs/yaml/functions/install_emptydir.yaml b/docs/yaml/functions/install_emptydir.yaml
index df84f60..4f77f59 100644
--- a/docs/yaml/functions/install_emptydir.yaml
+++ b/docs/yaml/functions/install_emptydir.yaml
@@ -16,7 +16,7 @@ varargs:
kwargs:
install_mode:
- type: list[str | int]
+ type: array[str | int]
description: |
Specify the file mode in symbolic format and optionally the owner/uid and
group/gid for the created directory.
diff --git a/docs/yaml/functions/install_headers.yaml b/docs/yaml/functions/install_headers.yaml
index 0ac4fc5..42f6462 100644
--- a/docs/yaml/functions/install_headers.yaml
+++ b/docs/yaml/functions/install_headers.yaml
@@ -9,6 +9,10 @@ description: |
argument. As an example if this has the value `myproj` then the
headers would be installed to `/{prefix}/include/myproj`.
+ Please note that this can only install static files from the source tree.
+ Generated files are installed via the `install_dir:` kwarg on the respective
+ generators, such as `custom_target()` or `configure_file().
+
example: |
For example, this will install `common.h` and `kola.h` into
`/{prefix}/include`:
@@ -57,7 +61,7 @@ kwargs:
Incompatible with the `install_dir` kwarg.
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
Specify the file mode in symbolic format
diff --git a/docs/yaml/functions/install_man.yaml b/docs/yaml/functions/install_man.yaml
index 8d9ba60..1deef59 100644
--- a/docs/yaml/functions/install_man.yaml
+++ b/docs/yaml/functions/install_man.yaml
@@ -20,7 +20,7 @@ warnings:
kwargs:
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
Specify the file mode in symbolic format
diff --git a/docs/yaml/functions/install_subdir.yaml b/docs/yaml/functions/install_subdir.yaml
index 19abee3..3420f5e 100644
--- a/docs/yaml/functions/install_subdir.yaml
+++ b/docs/yaml/functions/install_subdir.yaml
@@ -66,7 +66,7 @@ posargs:
kwargs:
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 0.47.0
description: |
Specify the file mode in symbolic format
@@ -83,16 +83,16 @@ kwargs:
tag which means they are not being installed when `--tags` argument is specified.
exclude_files:
- type: list[str]
+ type: array[str]
description: |
- A list of file names that should not be installed.
+ An array of file names that should not be installed.
Names are interpreted as paths relative to the `subdir_name` location.
exclude_directories:
- type: list[str]
+ type: array[str]
since: 0.47.0
description: |
- A list of directory names that should not be installed.
+ An array of directory names that should not be installed.
Names are interpreted as paths relative to the `subdir_name` location.
install_dir:
diff --git a/docs/yaml/functions/library.yaml b/docs/yaml/functions/library.yaml
index 1d406f1..4a4611c 100644
--- a/docs/yaml/functions/library.yaml
+++ b/docs/yaml/functions/library.yaml
@@ -40,13 +40,13 @@ kwargs:
type being build.
<lang>_static_args:
- type: list[str]
+ type: array[str]
since: 1.3.0
description:
Arguments that are only passed to a static library
vala_static_args:
- type: list[str | file]
+ type: array[str | file]
since: 1.3.0
description:
Arguments that are only passed to a static library
@@ -54,13 +54,13 @@ kwargs:
Like `vala_args`, [[files]] is allowed in addition to string
<lang>_shared_args:
- type: list[str]
+ type: array[str]
since: 1.3.0
description:
Arguments that are only passed to a shared library
vala_shared_args:
- type: list[str | file]
+ type: array[str | file]
since: 1.3.0
description:
Arguments that are only passed to a shared library
diff --git a/docs/yaml/functions/message.yaml b/docs/yaml/functions/message.yaml
index e480457..339dfc4 100644
--- a/docs/yaml/functions/message.yaml
+++ b/docs/yaml/functions/message.yaml
@@ -6,11 +6,11 @@ arg_flattening: false
posargs:
text:
- type: str | int | bool | list[str | int | bool] | dict[str | int | bool]
+ type: str | int | bool | array[str | int | bool] | dict[str | int | bool]
description: The message to print.
varargs:
name: more_text
since: 0.54.0
- type: str | int | bool | list[str | int | bool] | dict[str | int | bool]
+ type: str | int | bool | array[str | int | bool] | dict[str | int | bool]
description: Additional text that will be printed separated by spaces.
diff --git a/docs/yaml/functions/project.yaml b/docs/yaml/functions/project.yaml
index 5be8cac..0db8c03 100644
--- a/docs/yaml/functions/project.yaml
+++ b/docs/yaml/functions/project.yaml
@@ -13,9 +13,9 @@ description: |
would probably want to use the name _libfoobar_ instead of _The Foobar
Library_.
- It may be followed by the list of programming languages that the project uses.
+ It may be followed by the array of programming languages that the project uses.
- *(since 0.40.0)* The list of languages is optional.
+ *(since 0.40.0)* The array of languages is optional.
These languages may be used both for `native: false` (the default)
(host machine) targets and for `native: true` (build machine) targets.
@@ -24,7 +24,7 @@ description: |
Supported values for languages are `c`, `cpp` (for `C++`), `cuda`,
`cython`, `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`),
- `vala` and `rust`.
+ `swift`, `nasm`, `masm`, `linearasm`, `vala` and `rust`.
posargs:
project_name:
@@ -38,22 +38,26 @@ varargs:
kwargs:
default_options:
- type: list[str] | dict[str | bool | int | list[str]]
+ type: array[str] | dict[str | bool | int | array[str]]
description: |
Accepts strings in the form `key=value`
which have the same format as options to `meson configure`.
For example to set the default project type you would
set this: `default_options : ['buildtype=debugoptimized']`. Note
that these settings are only used when running Meson for the first
- time. Global options such as `buildtype` can only be specified in
- the master project, settings in subprojects are ignored. Project
- specific options are used normally even in subprojects.
+ time.
Note that some options can override the default behavior;
for example, using `c_args` here means that the `CFLAGS`
environment variable is not used. Consider using
[[add_project_arguments()]] instead.
+ Also note that not all options are taken into account when
+ building as a subproject, and the exact set of options
+ that are per-subproject has increased over time; for more
+ information, see [core options](Builtin-options.md#core-options)
+ and [compiler options](Builtin-options.md#compiler-options).
+
*(since 1.2.0)*: A dictionary may now be passed.
version:
@@ -72,7 +76,7 @@ kwargs:
Usually something like `>=0.28.0`.
license:
- type: str | list[str]
+ type: str | array[str]
description: |
Takes a string or array of strings describing the license(s) the code is under.
@@ -94,7 +98,7 @@ kwargs:
value in your Meson build files with `meson.project_license()`.
license_files:
- type: str | list[str]
+ type: str | array[str]
since: 1.1.0
description: |
Takes a string or array of strings with the paths to the license file(s)
diff --git a/docs/yaml/functions/run_command.yaml b/docs/yaml/functions/run_command.yaml
index 5803f82..1c9cc53 100644
--- a/docs/yaml/functions/run_command.yaml
+++ b/docs/yaml/functions/run_command.yaml
@@ -40,7 +40,7 @@ kwargs:
empty string.
env:
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
since: 0.50.0
description: |
environment variables to set,
diff --git a/docs/yaml/functions/run_target.yaml b/docs/yaml/functions/run_target.yaml
index 3ede1c9..bf670d8 100644
--- a/docs/yaml/functions/run_target.yaml
+++ b/docs/yaml/functions/run_target.yaml
@@ -31,25 +31,25 @@ posargs:
kwargs:
command:
- type: list[exe| external_program | custom_tgt | file | str]
+ type: array[exe| external_program | custom_tgt | file | str]
description: |
- A list containing the command to run and the arguments
- to pass to it. Each list item may be a string or a target. For
+ An array containing the command to run and the arguments
+ to pass to it. Each array element may be a string or a target. For
instance, passing the return value of [[executable]]
as the first item will run that executable, or passing a string as
the first item will find that command in `PATH` and run it.
depends:
- type: list[build_tgt | custom_tgt | custom_idx]
+ type: array[build_tgt | custom_tgt | custom_idx]
description: |
- A list of targets that this target depends on but which
+ An array of targets that this target depends on but which
are not listed in the command array (because, for example, the
script does file globbing internally, custom_idx was not possible
as a type between 0.60 and 1.4.0).
env:
since: 0.57.0
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
description: |
environment variables to set, such as
`{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`,
diff --git a/docs/yaml/functions/shared_library.yaml b/docs/yaml/functions/shared_library.yaml
index f633aca..c3ea77a 100644
--- a/docs/yaml/functions/shared_library.yaml
+++ b/docs/yaml/functions/shared_library.yaml
@@ -29,13 +29,13 @@ kwargs:
`soversion` is not defined, it is set to `3`.
darwin_versions:
- type: str | int | list[str]
+ type: str | int | array[str]
since: 0.48.0
description: |
Defines the `compatibility version` and `current version` for the dylib on macOS.
- If a list is specified, it must be
+ If an array is specified, it must be
either zero, one, or two elements. If only one element is specified
- or if it's not a list, the specified value will be used for setting
+ or if it's not an array the specified value will be used for setting
both compatibility version and current version. If unspecified, the
`soversion` will be used as per the aforementioned rules.
@@ -54,3 +54,9 @@ kwargs:
Set the specific ABI to compile (when compiling rust).
- 'rust' (default): Create a "dylib" crate.
- 'c': Create a "cdylib" crate.
+
+ shortname:
+ type: str
+ since: 1.10.0
+ description: |
+ A string specifying a DLL name fitting to 8.3 limit on OS/2 of this shared library.
diff --git a/docs/yaml/functions/structured_sources.yaml b/docs/yaml/functions/structured_sources.yaml
index a5f0a83..0dbf585 100644
--- a/docs/yaml/functions/structured_sources.yaml
+++ b/docs/yaml/functions/structured_sources.yaml
@@ -10,7 +10,7 @@ description: |
posargs:
root:
- type: list[str | file | custom_tgt | custom_idx | generated_list]
+ type: array[str | file | custom_tgt | custom_idx | generated_list]
description: Sources to put at the root of the generated structure
optargs:
diff --git a/docs/yaml/functions/subdir.yaml b/docs/yaml/functions/subdir.yaml
index 428563b..96e17ba 100644
--- a/docs/yaml/functions/subdir.yaml
+++ b/docs/yaml/functions/subdir.yaml
@@ -21,6 +21,6 @@ posargs:
kwargs:
if_found:
- type: list[dep]
+ type: array[dep]
since: 0.44.0
description: Only enter the subdir if all [[dep.found]] methods return `true`.
diff --git a/docs/yaml/functions/subproject.yaml b/docs/yaml/functions/subproject.yaml
index bccac79..508837c 100644
--- a/docs/yaml/functions/subproject.yaml
+++ b/docs/yaml/functions/subproject.yaml
@@ -42,7 +42,7 @@ posargs:
kwargs:
default_options:
- type: list[str] | dict[str | bool | int | list[str]]
+ type: array[str] | dict[str | bool | int | array[str]]
since: 0.37.0
description: |
An array of default option values
diff --git a/docs/yaml/functions/summary.yaml b/docs/yaml/functions/summary.yaml
index cf63dcd..b531282 100644
--- a/docs/yaml/functions/summary.yaml
+++ b/docs/yaml/functions/summary.yaml
@@ -16,7 +16,7 @@ description: |
- an integer, boolean or string
- *since 0.57.0* an external program or a dependency
- *since 0.58.0* a feature option
- - a list of those.
+ - an array of those.
Instead of calling summary as `summary(key, value)`, it is also possible to
directly pass a dictionary to the [[summary]] function, as seen in the example
@@ -38,7 +38,7 @@ example: |
summary({'Some boolean': false,
'Another boolean': true,
'Some string': 'Hello World',
- 'A list': ['string', 1, true],
+ 'An array': ['string', 1, true],
}, section: 'Configuration')
```
@@ -56,7 +56,7 @@ example: |
Some boolean : False
Another boolean: True
Some string : Hello World
- A list : string
+ An array : string
1
True
```
@@ -65,7 +65,7 @@ arg_flattening: false
posargs:
key_or_dict:
- type: str | dict[str | bool | int | dep | external_program | list[str | bool | int | dep | external_program]]
+ type: str | dict[str | bool | int | dep | external_program | array[str | bool | int | dep | external_program]]
description: |
The name of the new entry, or a dict containing multiple entries. If a
dict is passed it is equivalent to calling summary() once for each
@@ -74,7 +74,7 @@ posargs:
optargs:
value:
- type: str | bool | int | dep | external_program | list[str | bool | int | dep | external_program]
+ type: str | bool | int | dep | external_program | array[str | bool | int | dep | external_program]
description: |
The value to print for the `key`. Only valid if `key_or_dict` is a str.
@@ -92,5 +92,5 @@ kwargs:
type: str
since: 0.54.0
description: |
- The separator to use when printing list values in this summary. If no
- separator is given, each list item will be printed on its own line.
+ The separator to use when printing array values in this summary. If no
+ separator is given, each array item will be printed on its own line.
diff --git a/docs/yaml/functions/vcs_tag.yaml b/docs/yaml/functions/vcs_tag.yaml
index 3a35684..bf223e7 100644
--- a/docs/yaml/functions/vcs_tag.yaml
+++ b/docs/yaml/functions/vcs_tag.yaml
@@ -22,7 +22,7 @@ description: |
kwargs:
command:
- type: list[exe | external_program | custom_tgt | file | str]
+ type: array[exe | external_program | custom_tgt | file | str]
description: |
The command to execute, see [[custom_target]] for details
on how this command must be specified.
@@ -71,7 +71,7 @@ kwargs:
The subdirectory to install the generated file to (e.g. `share/myproject`).
install_mode:
- type: list[str | int]
+ type: array[str | int]
since: 1.7.0
description: |
Specify the file mode in symbolic format
diff --git a/docs/yaml/objects/build_tgt.yaml b/docs/yaml/objects/build_tgt.yaml
index 73b9b5d..3fed56c 100644
--- a/docs/yaml/objects/build_tgt.yaml
+++ b/docs/yaml/objects/build_tgt.yaml
@@ -77,3 +77,27 @@ methods:
objects feature compatible with [[@external_program]] objects. This
simplifies use-cases where an executable is used instead of
an [[@external_program]].
+
+- name: vala_header
+ returns: file
+ since: 1.10.0
+ description: |
+ Returns a [[@file]] object pointing to a C compatible header generated by
+ the vala compiler, if this target does not generate a vala header then it is
+ an error to call this method.
+
+- name: vala_vapi
+ returns: file
+ since: 1.10.0
+ description: |
+ Returns a [[@file]] object pointing to a VAPI header generated by the vala
+ compiler, if this target does not generate a VAPI file then it is an error
+ to call this method.
+
+- name: vala_gir
+ returns: file
+ since: 1.10.0
+ description: |
+ Returns a [[@file]] object pointing to a GIR file generated by the vala
+ compiler, if this target does not generate a GIR file then it is an error
+ to call this method.
diff --git a/docs/yaml/objects/cfg_data.yaml b/docs/yaml/objects/cfg_data.yaml
index 03abb17..36f9755 100644
--- a/docs/yaml/objects/cfg_data.yaml
+++ b/docs/yaml/objects/cfg_data.yaml
@@ -114,7 +114,7 @@ methods:
description: The name of the variable to query
- name: keys
- returns: list[str]
+ returns: array[str]
since: 0.57.0
description: |
Returns an array of keys of
diff --git a/docs/yaml/objects/compiler.yaml b/docs/yaml/objects/compiler.yaml
index 43831d2..c676180 100644
--- a/docs/yaml/objects/compiler.yaml
+++ b/docs/yaml/objects/compiler.yaml
@@ -45,9 +45,9 @@ methods:
description: You have found a bug if you can see this!
kwargs:
args:
- type: list[str]
+ type: array[str]
description: |
- Used to pass a list of compiler arguments.
+ Used to pass an array of compiler arguments.
Defining include paths for headers not in the default include path
via `-Isome/path/to/header` is generally supported, however, usually not
recommended.
@@ -61,16 +61,19 @@ methods:
description: You have found a bug if you can see this!
kwargs:
include_directories:
- type: inc | list[inc]
+ type: array[inc | str]
since: 0.38.0
- description: Extra directories for header searches.
+ description: |
+ Extra directories for header searches, created with the
+ [[include_directories]] function.
+ *(Since 1.10.0)* Strings are also accepted.
- name: _dependencies
returns: void
description: You have found a bug if you can see this!
kwargs:
dependencies:
- type: dep | list[dep]
+ type: dep | array[dep]
description: Additionally dependencies required for compiling and / or linking.
- name: _prefix
@@ -78,7 +81,7 @@ methods:
description: You have found a bug if you can see this!
kwargs:
prefix:
- type: str | list[str]
+ type: str | array[str]
description: |
Used to add `#include`s and other things that are required
for the symbol to be declared. Since 1.0.0 an array is accepted
@@ -184,7 +187,7 @@ methods:
description: Returns the compiler's version number as a string.
- name: cmd_array
- returns: list[str]
+ returns: array[str]
description: Returns an array containing the command(s) for the compiler.
@@ -441,10 +444,10 @@ methods:
*(since 0.47.0)* The value of a `feature` option can also be passed here.
has_headers:
- type: list[str]
+ type: array[str]
since: 0.50.0
description: |
- List of headers that must be found as well.
+ An array of headers that must be found as well.
This check is equivalent to checking each header with a
[[compiler.has_header]] call.
@@ -468,7 +471,7 @@ methods:
description: If `true`, this method will return a [[@disabler]] on a failed check.
dirs:
- type: list[str]
+ type: array[str]
description: |
Additional directories to search in.
@@ -478,19 +481,20 @@ methods:
# does not work, since all _common kwargs need to be prefixed `header_` here
# kwargs_inherit: compiler._common
header_args:
- type: list[str]
+ type: array[str]
since: 0.51.0
description: |
When the `has_headers` kwarg is also used, this argument is passed to
[[compiler.has_header]] as `args`.
header_include_directories:
- type: inc | list[inc]
+ type: array[inc | str]
since: 0.51.0
description: |
When the `has_headers` kwarg is also used, this argument is passed to
[[compiler.has_header]] as `include_directories`.
+ *(Since 1.10.0)* Strings are also accepted.
header_dependencies:
- type: dep | list[dep]
+ type: dep | array[dep]
since: 0.51.0
description: |
When the `has_headers` kwarg is also used, this argument is passed to
@@ -539,7 +543,7 @@ methods:
- compiler._required
- name: get_supported_arguments
- returns: list[str]
+ returns: array[str]
since: 0.43.0
varargs_inherit: compiler.has_multi_arguments
description: |
@@ -558,11 +562,11 @@ methods:
- `'require'`: Abort if at least one argument is not supported
- name: first_supported_argument
- returns: list[str]
+ returns: array[str]
since: 0.43.0
varargs_inherit: compiler.has_multi_arguments
description: |
- Given a list of strings, returns a single-element list containing the first
+ Given an array of strings, returns a single-element array containing the first
argument that passes the [[compiler.has_argument]] test or an empty array if
none pass.
@@ -601,7 +605,7 @@ methods:
- compiler._required
- name: get_supported_link_arguments
- returns: list[str]
+ returns: array[str]
since: 0.46.0
varargs_inherit: compiler.has_multi_link_arguments
description: |
@@ -621,11 +625,11 @@ methods:
# - `'require'`: Abort if at least one argument is not supported
- name: first_supported_link_argument
- returns: list[str]
+ returns: array[str]
since: 0.46.0
varargs_inherit: compiler.has_multi_link_arguments
description: |
- Given a list of strings, returns the first argument that passes the
+ Given an array of strings, returns the first argument that passes the
[[compiler.has_link_argument]] test or an empty array if none pass.
- name: has_function_attribute
@@ -645,7 +649,7 @@ methods:
- compiler._required
- name: get_supported_function_attributes
- returns: list[str]
+ returns: array[str]
since: 0.48.0
description: |
Returns an array containing any names that are supported GCC style attributes.
@@ -666,10 +670,10 @@ methods:
operating systems.
- name: preprocess
- returns: list[custom_idx]
+ returns: array[custom_idx]
since: 0.64.0
description: |
- Preprocess a list of source files but do not compile them. The preprocessor
+ Preprocess an array of source files but do not compile them. The preprocessor
will receive the same arguments (include directories, defines, etc) as with
normal compilation. That includes for example args added with
`add_project_arguments()`, or on the command line with `-Dc_args=-DFOO`.
@@ -694,15 +698,15 @@ methods:
the source filename and `@BASENAME@` is replaced by the source filename
without its extension.
compile_args:
- type: list[str]
+ type: array[str]
description: |
Extra flags to pass to the preprocessor
dependencies:
- type: dep | list[dep]
+ type: dep | array[dep]
description: Additionally dependencies required.
since: 1.1.0
depends:
- type: list[build_tgt | custom_tgt]
+ type: array[build_tgt | custom_tgt]
description: |
Specifies that this target depends on the specified
target(s). These targets should be built before starting
diff --git a/docs/yaml/objects/custom_tgt.yaml b/docs/yaml/objects/custom_tgt.yaml
index 5102ab9..60e067c 100644
--- a/docs/yaml/objects/custom_tgt.yaml
+++ b/docs/yaml/objects/custom_tgt.yaml
@@ -25,9 +25,9 @@ methods:
custom target's output argument.
- name: to_list
- returns: list[custom_idx]
+ returns: array[custom_idx]
since: 0.54.0
description: |
- Returns a list of opaque objects that references this target,
+ Returns an array of opaque objects that references this target,
and can be used as a source in other targets. This can be used to
iterate outputs with `foreach` loop.
diff --git a/docs/yaml/objects/dep.yaml b/docs/yaml/objects/dep.yaml
index ffd19f7..fdf5fe5 100644
--- a/docs/yaml/objects/dep.yaml
+++ b/docs/yaml/objects/dep.yaml
@@ -34,11 +34,11 @@ methods:
kwargs:
define_variable:
- type: list[str]
+ type: array[str]
since: 0.44.0
description: |
You can also redefine a
- variable by passing a list to this kwarg
+ variable by passing an array to this kwarg
that can affect the retrieved variable: `['prefix', '/'])`.
*(Since 1.3.0)* Multiple variables can be specified in pairs.
@@ -224,7 +224,7 @@ methods:
description: The default value to return when the variable does not exist
pkgconfig_define:
- type: list[str]
+ type: array[str]
description: See [[dep.get_pkgconfig_variable]]
- name: as_static
@@ -250,4 +250,3 @@ methods:
recursive:
type: bool
description: If true, this is recursively applied to dependencies
- \ No newline at end of file
diff --git a/docs/yaml/objects/external_program.yaml b/docs/yaml/objects/external_program.yaml
index 4c24497..db5d39f 100644
--- a/docs/yaml/objects/external_program.yaml
+++ b/docs/yaml/objects/external_program.yaml
@@ -56,3 +56,8 @@ methods:
```meson
run_command(find_program('foo'), 'arg1', 'arg2')
```
+
+- name: cmd_array
+ returns: array[str]
+ description: Returns an array containing the command(s) for the program.
+ since: 1.10.0
diff --git a/docs/yaml/objects/generator.yaml b/docs/yaml/objects/generator.yaml
index fbef95f..3dbcdd5 100644
--- a/docs/yaml/objects/generator.yaml
+++ b/docs/yaml/objects/generator.yaml
@@ -9,20 +9,20 @@ methods:
- name: process
returns: generated_list
description: |
- Takes a list of files, causes them to be processed and returns an object containing the result
+ Takes an array of files, causes them to be processed and returns an object containing the result
which can then, for example, be passed into a build target definition.
varargs:
name: source
min_varargs: 1
type: str | file | custom_tgt | custom_idx | generated_list
- description: List of sources to process.
+ description: sources to process.
kwargs:
extra_args:
- type: list[str]
+ type: array[str]
description: |
- If present, will be used to replace an entry `@EXTRA_ARGS@` in the argument list.
+ If present, will be used to replace an entry `@EXTRA_ARGS@` in the argument array.
preserve_path_from:
type: str
@@ -36,7 +36,7 @@ methods:
directory}/one.out`.
env:
- type: env | list[str] | dict[str]
+ type: env | array[str] | dict[str]
since: 1.3.0
description: |
environment variables to set, such as
diff --git a/man/meson.1 b/man/meson.1
index b78cf96..f529784 100644
--- a/man/meson.1
+++ b/man/meson.1
@@ -1,4 +1,4 @@
-.TH MESON "1" "April 2025" "meson 1.8.0" "User Commands"
+.TH MESON "1" "December 2025" "meson 1.10.0" "User Commands"
.SH NAME
meson - a high productivity build system
.SH DESCRIPTION
@@ -679,6 +679,8 @@ could not rebuild the required targets.
.SH SEE ALSO
-http://mesonbuild.com/
+https://mesonbuild.com/
https://wrapdb.mesonbuild.com/
+
+.MR meson-reference 3
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index cd8156a..d3afb4d 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -8,8 +8,12 @@ from __future__ import annotations
import os
import sys
import typing as T
+from collections import defaultdict
+from dataclasses import dataclass
+import itertools
+from pathlib import Path
-from .. import mparser, mesonlib
+from .. import mparser, mesonlib, mlog
from .. import environment
from ..interpreterbase import (
@@ -20,8 +24,14 @@ from ..interpreterbase import (
ContinueRequest,
Disabler,
default_resolve_key,
+ is_disabled,
+ UnknownValue,
+ UndefinedVariable,
+ InterpreterObject,
)
+from ..interpreterbase.helpers import flatten
+
from ..interpreter import (
StringHolder,
BooleanHolder,
@@ -36,19 +46,21 @@ from ..mparser import (
ArrayNode,
AssignmentNode,
BaseNode,
- ElementaryNode,
EmptyNode,
IdNode,
MethodNode,
NotNode,
PlusAssignmentNode,
TernaryNode,
+ SymbolNode,
+ Token,
+ FunctionNode,
)
if T.TYPE_CHECKING:
from .visitor import AstVisitor
from ..interpreter import Interpreter
- from ..interpreterbase import SubProject, TYPE_nkwargs, TYPE_var
+ from ..interpreterbase import SubProject, TYPE_var, TYPE_nvar
from ..mparser import (
AndNode,
ComparisonNode,
@@ -60,38 +72,128 @@ if T.TYPE_CHECKING:
UMinusNode,
)
-class DontCareObject(MesonInterpreterObject):
- pass
-
-class MockExecutable(MesonInterpreterObject):
- pass
-
-class MockStaticLibrary(MesonInterpreterObject):
- pass
-
-class MockSharedLibrary(MesonInterpreterObject):
- pass
-
-class MockCustomTarget(MesonInterpreterObject):
- pass
-
-class MockRunTarget(MesonInterpreterObject):
- pass
-
-ADD_SOURCE = 0
-REMOVE_SOURCE = 1
-
_T = T.TypeVar('_T')
_V = T.TypeVar('_V')
+def _symbol(val: str) -> SymbolNode:
+ return SymbolNode(Token('', '', 0, 0, 0, (0, 0), val))
+
+# `IntrospectionFile` is to the `IntrospectionInterpreter` what `File` is to the normal `Interpreter`.
+@dataclass
+class IntrospectionFile:
+ subdir: str
+ rel: str
+
+ def to_abs_path(self, root_dir: Path) -> Path:
+ return (root_dir / self.subdir / self.rel).resolve()
+
+ def __hash__(self) -> int:
+ return hash((self.__class__.__name__, self.subdir, self.rel))
+
+# `IntrospectionDependency` is to the `IntrospectionInterpreter` what `Dependency` is to the normal `Interpreter`.
+@dataclass
+class IntrospectionDependency(MesonInterpreterObject):
+ name: T.Union[str, UnknownValue]
+ required: T.Union[bool, UnknownValue]
+ version: T.Union[T.List[str], UnknownValue]
+ has_fallback: bool
+ conditional: bool
+ node: FunctionNode
+
+# `IntrospectionBuildTarget` is to the `IntrospectionInterpreter` what `BuildTarget` is to the normal `Interpreter`.
+@dataclass
+class IntrospectionBuildTarget(MesonInterpreterObject):
+ name: str
+ machine: str
+ id: str
+ typename: str
+ defined_in: str
+ subdir: str
+ build_by_default: T.Union[bool, UnknownValue]
+ installed: T.Union[bool, UnknownValue]
+ outputs: T.List[str]
+ source_nodes: T.List[BaseNode]
+ extra_files: BaseNode
+ kwargs: T.Dict[str, TYPE_var]
+ node: FunctionNode
+
+def is_ignored_edge(src: T.Union[BaseNode, UnknownValue]) -> bool:
+ return (isinstance(src, FunctionNode) and src.func_name.value not in {'files', 'get_variable'}) or isinstance(src, MethodNode)
+
+class DataflowDAG:
+ src_to_tgts: T.DefaultDict[T.Union[BaseNode, UnknownValue], T.Set[T.Union[BaseNode, UnknownValue]]]
+ tgt_to_srcs: T.DefaultDict[T.Union[BaseNode, UnknownValue], T.Set[T.Union[BaseNode, UnknownValue]]]
+
+ def __init__(self) -> None:
+ self.src_to_tgts = defaultdict(set)
+ self.tgt_to_srcs = defaultdict(set)
+
+ def add_edge(self, source: T.Union[BaseNode, UnknownValue], target: T.Union[BaseNode, UnknownValue]) -> None:
+ self.src_to_tgts[source].add(target)
+ self.tgt_to_srcs[target].add(source)
+
+ # Returns all nodes in the DAG that are reachable from a node in `srcs`.
+ # In other words, A node `a` is part of the returned set exactly if data
+ # from `srcs` flows into `a`, directly or indirectly.
+ # Certain edges are ignored.
+ def reachable(self, srcs: T.Set[T.Union[BaseNode, UnknownValue]], reverse: bool) -> T.Set[T.Union[BaseNode, UnknownValue]]:
+ reachable = srcs.copy()
+ active = srcs.copy()
+ while active:
+ new: T.Set[T.Union[BaseNode, UnknownValue]] = set()
+ if reverse:
+ for tgt in active:
+ new.update(src for src in self.tgt_to_srcs[tgt] if not is_ignored_edge(src))
+ else:
+ for src in active:
+ if is_ignored_edge(src):
+ continue
+ new.update(tgt for tgt in self.src_to_tgts[src])
+ reachable.update(new)
+ active = new
+ return reachable
+
+ # Returns all paths from src to target.
+ # Certain edges are ignored.
+ def find_all_paths(self, src: T.Union[BaseNode, UnknownValue], target: T.Union[BaseNode, UnknownValue]) -> T.List[T.List[T.Union[BaseNode, UnknownValue]]]:
+ queue = [(src, [src])]
+ paths = []
+ while queue:
+ cur, path = queue.pop()
+ if cur == target:
+ paths.append(path)
+ if is_ignored_edge(cur):
+ continue
+ queue.extend((tgt, path + [tgt]) for tgt in self.src_to_tgts[cur])
+ return paths
class AstInterpreter(InterpreterBase):
def __init__(self, source_root: str, subdir: str, subproject: SubProject, subproject_dir: str, env: environment.Environment, visitors: T.Optional[T.List[AstVisitor]] = None):
super().__init__(source_root, subdir, subproject, subproject_dir, env)
self.visitors = visitors if visitors is not None else []
- self.assignments: T.Dict[str, BaseNode] = {}
- self.assign_vals: T.Dict[str, T.Any] = {}
- self.reverse_assignment: T.Dict[str, BaseNode] = {}
+ self.nesting: T.List[int] = []
+ self.cur_assignments: T.DefaultDict[str, T.List[T.Tuple[T.List[int], T.Union[BaseNode, UnknownValue]]]] = defaultdict(list)
+ self.all_assignment_nodes: T.DefaultDict[str, T.List[AssignmentNode]] = defaultdict(list)
+ # dataflow_dag is an acyclic directed graph that contains an edge
+ # from one instance of `BaseNode` to another instance of `BaseNode` if
+ # data flows directly from one to the other. Example: If meson.build
+ # contains this:
+ # var = 'foo' + '123'
+ # executable(var, 'src.c')
+ # var = 'bar'
+ # dataflow_dag will contain an edge from the IdNode corresponding to
+ # 'var' in line 2 to the ArithmeticNode corresponding to 'foo' + '123'.
+ # This graph is crucial for e.g. node_to_runtime_value because we have
+ # to know that 'var' in line2 is 'foo123' and not 'bar'.
+ self.dataflow_dag = DataflowDAG()
+ self.funcvals: T.Dict[BaseNode, T.Any] = {}
+ self.tainted = False
+ self.predefined_vars = {
+ 'meson': UnknownValue(),
+ 'host_machine': UnknownValue(),
+ 'build_machine': UnknownValue(),
+ 'target_machine': UnknownValue()
+ }
self.funcs.update({'project': self.func_do_nothing,
'test': self.func_do_nothing,
'benchmark': self.func_do_nothing,
@@ -124,7 +226,7 @@ class AstInterpreter(InterpreterBase):
'vcs_tag': self.func_do_nothing,
'add_languages': self.func_do_nothing,
'declare_dependency': self.func_do_nothing,
- 'files': self.func_do_nothing,
+ 'files': self.func_files,
'executable': self.func_do_nothing,
'static_library': self.func_do_nothing,
'shared_library': self.func_do_nothing,
@@ -133,9 +235,9 @@ class AstInterpreter(InterpreterBase):
'custom_target': self.func_do_nothing,
'run_target': self.func_do_nothing,
'subdir': self.func_subdir,
- 'set_variable': self.func_do_nothing,
- 'get_variable': self.func_do_nothing,
- 'unset_variable': self.func_do_nothing,
+ 'set_variable': self.func_set_variable,
+ 'get_variable': self.func_get_variable,
+ 'unset_variable': self.func_unset_variable,
'is_disabler': self.func_do_nothing,
'is_variable': self.func_do_nothing,
'disabler': self.func_do_nothing,
@@ -153,14 +255,14 @@ class AstInterpreter(InterpreterBase):
'debug': self.func_do_nothing,
})
- def _unholder_args(self, args: _T, kwargs: _V) -> T.Tuple[_T, _V]:
+ def _unholder_args(self, args: T.Any, kwargs: T.Any) -> T.Tuple[T.Any, T.Any]:
return args, kwargs
- def _holderify(self, res: _T) -> _T:
+ def _holderify(self, res: T.Any) -> T.Any:
return res
- def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> bool:
- return True
+ def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> UnknownValue:
+ return UnknownValue()
def load_root_meson_file(self) -> None:
super().load_root_meson_file()
@@ -182,24 +284,50 @@ class AstInterpreter(InterpreterBase):
buildfilename = os.path.join(subdir, environment.build_filename)
sys.stderr.write(f'Unable to find build file {buildfilename} --> Skipping\n')
- def method_call(self, node: BaseNode) -> bool:
- return True
+ def inner_method_call(self, obj: BaseNode, method_name: str, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Any:
+ for arg in itertools.chain(args, kwargs.values()):
+ if isinstance(arg, UnknownValue):
+ return UnknownValue()
+
+ if isinstance(obj, str):
+ result = StringHolder(obj, T.cast('Interpreter', self)).method_call(method_name, args, kwargs)
+ elif isinstance(obj, bool):
+ result = BooleanHolder(obj, T.cast('Interpreter', self)).method_call(method_name, args, kwargs)
+ elif isinstance(obj, int):
+ result = IntegerHolder(obj, T.cast('Interpreter', self)).method_call(method_name, args, kwargs)
+ elif isinstance(obj, list):
+ result = ArrayHolder(obj, T.cast('Interpreter', self)).method_call(method_name, args, kwargs)
+ elif isinstance(obj, dict):
+ result = DictHolder(obj, T.cast('Interpreter', self)).method_call(method_name, args, kwargs)
+ else:
+ return UnknownValue()
+ return result
- def evaluate_fstring(self, node: mparser.StringNode) -> str:
- assert isinstance(node, mparser.StringNode)
- return node.value
+ def method_call(self, node: mparser.MethodNode) -> None:
+ invocable = node.source_object
+ self.evaluate_statement(invocable)
+ obj = self.node_to_runtime_value(invocable)
+ method_name = node.name.value
+ (args, kwargs) = self.reduce_arguments(node.args)
+ if is_disabled(args, kwargs):
+ res = Disabler()
+ else:
+ res = self.inner_method_call(obj, method_name, args, kwargs)
+ self.funcvals[node] = res
- def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> TYPE_var:
- return self.reduce_arguments(cur.args)[0]
+ def evaluate_fstring(self, node: mparser.StringNode) -> None:
+ pass
+
+ def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> None:
+ for arg in cur.args.arguments:
+ self.evaluate_statement(arg)
- def evaluate_arithmeticstatement(self, cur: ArithmeticNode) -> int:
+ def evaluate_arithmeticstatement(self, cur: ArithmeticNode) -> None:
self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right)
- return 0
- def evaluate_uminusstatement(self, cur: UMinusNode) -> int:
+ def evaluate_uminusstatement(self, cur: UMinusNode) -> None:
self.evaluate_statement(cur.value)
- return 0
def evaluate_ternary(self, node: TernaryNode) -> None:
assert isinstance(node, TernaryNode)
@@ -207,42 +335,27 @@ class AstInterpreter(InterpreterBase):
self.evaluate_statement(node.trueblock)
self.evaluate_statement(node.falseblock)
- def evaluate_dictstatement(self, node: mparser.DictNode) -> TYPE_nkwargs:
- def resolve_key(node: mparser.BaseNode) -> str:
- if isinstance(node, mparser.StringNode):
- return node.value
- return '__AST_UNKNOWN__'
- arguments, kwargs = self.reduce_arguments(node.args, key_resolver=resolve_key)
- assert not arguments
- self.argument_depth += 1
- for key, value in kwargs.items():
- if isinstance(key, BaseNode):
- self.evaluate_statement(key)
- self.argument_depth -= 1
- return {}
+ def evaluate_dictstatement(self, node: mparser.DictNode) -> None:
+ for k, v in node.args.kwargs.items():
+ self.evaluate_statement(k)
+ self.evaluate_statement(v)
- def evaluate_plusassign(self, node: PlusAssignmentNode) -> None:
- assert isinstance(node, PlusAssignmentNode)
- # Cheat by doing a reassignment
- self.assignments[node.var_name.value] = node.value # Save a reference to the value node
- if node.value.ast_id:
- self.reverse_assignment[node.value.ast_id] = node
- self.assign_vals[node.var_name.value] = self.evaluate_statement(node.value)
-
- def evaluate_indexing(self, node: IndexNode) -> int:
- return 0
-
- def unknown_function_called(self, func_name: str) -> None:
- pass
+ def evaluate_indexing(self, node: IndexNode) -> None:
+ self.evaluate_statement(node.iobject)
+ self.evaluate_statement(node.index)
def reduce_arguments(
self,
args: mparser.ArgumentNode,
key_resolver: T.Callable[[mparser.BaseNode], str] = default_resolve_key,
duplicate_key_error: T.Optional[str] = None,
- ) -> T.Tuple[T.List[TYPE_var], TYPE_nkwargs]:
+ ) -> T.Tuple[T.List[T.Any], T.Any]:
+ for arg in args.arguments:
+ self.evaluate_statement(arg)
+ for value in args.kwargs.values():
+ self.evaluate_statement(value)
if isinstance(args, ArgumentNode):
- kwargs: T.Dict[str, TYPE_var] = {}
+ kwargs = {}
for key, val in args.kwargs.items():
kwargs[key_resolver(key)] = val
if args.incorrect_order():
@@ -251,158 +364,417 @@ class AstInterpreter(InterpreterBase):
else:
return self.flatten_args(args), {}
- def evaluate_comparison(self, node: ComparisonNode) -> bool:
+ def evaluate_comparison(self, node: ComparisonNode) -> None:
self.evaluate_statement(node.left)
self.evaluate_statement(node.right)
- return False
- def evaluate_andstatement(self, cur: AndNode) -> bool:
+ def evaluate_andstatement(self, cur: AndNode) -> None:
self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right)
- return False
- def evaluate_orstatement(self, cur: OrNode) -> bool:
+ def evaluate_orstatement(self, cur: OrNode) -> None:
self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right)
- return False
- def evaluate_notstatement(self, cur: NotNode) -> bool:
+ def evaluate_notstatement(self, cur: NotNode) -> None:
self.evaluate_statement(cur.value)
- return False
+
+ def find_potential_writes(self, node: BaseNode) -> T.Set[str]:
+ if isinstance(node, mparser.ForeachClauseNode):
+ return {el.value for el in node.varnames} | self.find_potential_writes(node.block)
+ elif isinstance(node, mparser.CodeBlockNode):
+ ret = set()
+ for line in node.lines:
+ ret.update(self.find_potential_writes(line))
+ return ret
+ elif isinstance(node, (AssignmentNode, PlusAssignmentNode)):
+ return set([node.var_name.value]) | self.find_potential_writes(node.value)
+ elif isinstance(node, IdNode):
+ return set()
+ elif isinstance(node, ArrayNode):
+ ret = set()
+ for arg in node.args.arguments:
+ ret.update(self.find_potential_writes(arg))
+ return ret
+ elif isinstance(node, mparser.DictNode):
+ ret = set()
+ for k, v in node.args.kwargs.items():
+ ret.update(self.find_potential_writes(k))
+ ret.update(self.find_potential_writes(v))
+ return ret
+ elif isinstance(node, FunctionNode):
+ ret = set()
+ for arg in node.args.arguments:
+ ret.update(self.find_potential_writes(arg))
+ for arg in node.args.kwargs.values():
+ ret.update(self.find_potential_writes(arg))
+ return ret
+ elif isinstance(node, MethodNode):
+ ret = self.find_potential_writes(node.source_object)
+ for arg in node.args.arguments:
+ ret.update(self.find_potential_writes(arg))
+ for arg in node.args.kwargs.values():
+ ret.update(self.find_potential_writes(arg))
+ return ret
+ elif isinstance(node, ArithmeticNode):
+ return self.find_potential_writes(node.left) | self.find_potential_writes(node.right)
+ elif isinstance(node, (mparser.NumberNode, mparser.StringNode, mparser.BreakNode, mparser.BooleanNode, mparser.ContinueNode)):
+ return set()
+ elif isinstance(node, mparser.IfClauseNode):
+ if isinstance(node.elseblock, EmptyNode):
+ ret = set()
+ else:
+ ret = self.find_potential_writes(node.elseblock.block)
+ for i in node.ifs:
+ ret.update(self.find_potential_writes(i))
+ return ret
+ elif isinstance(node, mparser.IndexNode):
+ return self.find_potential_writes(node.iobject) | self.find_potential_writes(node.index)
+ elif isinstance(node, mparser.IfNode):
+ return self.find_potential_writes(node.condition) | self.find_potential_writes(node.block)
+ elif isinstance(node, (mparser.ComparisonNode, mparser.OrNode, mparser.AndNode)):
+ return self.find_potential_writes(node.left) | self.find_potential_writes(node.right)
+ elif isinstance(node, mparser.NotNode):
+ return self.find_potential_writes(node.value)
+ elif isinstance(node, mparser.TernaryNode):
+ return self.find_potential_writes(node.condition) | self.find_potential_writes(node.trueblock) | self.find_potential_writes(node.falseblock)
+ elif isinstance(node, mparser.UMinusNode):
+ return self.find_potential_writes(node.value)
+ elif isinstance(node, mparser.ParenthesizedNode):
+ return self.find_potential_writes(node.inner)
+ raise mesonlib.MesonBugException('Unhandled node type')
def evaluate_foreach(self, node: ForeachClauseNode) -> None:
+ asses = self.find_potential_writes(node)
+ for ass in asses:
+ self.cur_assignments[ass].append((self.nesting.copy(), UnknownValue()))
try:
self.evaluate_codeblock(node.block)
except ContinueRequest:
pass
except BreakRequest:
pass
+ for ass in asses:
+ self.cur_assignments[ass].append((self.nesting.copy(), UnknownValue())) # In case the foreach loops 0 times.
def evaluate_if(self, node: IfClauseNode) -> None:
+ self.nesting.append(0)
for i in node.ifs:
self.evaluate_codeblock(i.block)
+ self.nesting[-1] += 1
if not isinstance(node.elseblock, EmptyNode):
self.evaluate_codeblock(node.elseblock.block)
-
- def get_variable(self, varname: str) -> int:
- return 0
-
- def assignment(self, node: AssignmentNode) -> None:
- assert isinstance(node, AssignmentNode)
- self.assignments[node.var_name.value] = node.value # Save a reference to the value node
- if node.value.ast_id:
- self.reverse_assignment[node.value.ast_id] = node
- self.assign_vals[node.var_name.value] = self.evaluate_statement(node.value) # Evaluate the value just in case
-
- def resolve_node(self, node: BaseNode, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.Optional[T.Any]:
- def quick_resolve(n: BaseNode, loop_detect: T.Optional[T.List[str]] = None) -> T.Any:
- if loop_detect is None:
- loop_detect = []
- if isinstance(n, IdNode):
- assert isinstance(n.value, str)
- if n.value in loop_detect or n.value not in self.assignments:
- return []
- return quick_resolve(self.assignments[n.value], loop_detect = loop_detect + [n.value])
- elif isinstance(n, ElementaryNode):
- return n.value
+ self.nesting.pop()
+ for var_name in self.cur_assignments:
+ potential_values = []
+ oldval = self.get_cur_value_if_defined(var_name)
+ if not isinstance(oldval, UndefinedVariable):
+ potential_values.append(oldval)
+ for nesting, value in self.cur_assignments[var_name]:
+ if len(nesting) > len(self.nesting):
+ potential_values.append(value)
+ self.cur_assignments[var_name] = [(nesting, v) for (nesting, v) in self.cur_assignments[var_name] if len(nesting) <= len(self.nesting)]
+ if len(potential_values) > 1 or (len(potential_values) > 0 and isinstance(oldval, UndefinedVariable)):
+ uv = UnknownValue()
+ for pv in potential_values:
+ self.dataflow_dag.add_edge(pv, uv)
+ self.cur_assignments[var_name].append((self.nesting.copy(), uv))
+
+ def func_files(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Any:
+ ret: T.List[T.Union[IntrospectionFile, UnknownValue]] = []
+ for arg in args:
+ if isinstance(arg, str):
+ ret.append(IntrospectionFile(self.subdir, arg))
+ elif isinstance(arg, UnknownValue):
+ ret.append(UnknownValue())
else:
- return n
-
- if id_loop_detect is None:
- id_loop_detect = []
- result = None
-
- if not isinstance(node, BaseNode):
- return None
-
- assert node.ast_id
- if node.ast_id in id_loop_detect:
- return None # Loop detected
- id_loop_detect += [node.ast_id]
-
- # Try to evaluate the value of the node
- if isinstance(node, IdNode):
- result = quick_resolve(node)
-
- elif isinstance(node, ElementaryNode):
- result = node.value
-
- elif isinstance(node, NotNode):
- result = self.resolve_node(node.value, include_unknown_args, id_loop_detect)
- if isinstance(result, bool):
- result = not result
-
+ raise TypeError
+ return ret
+
+ def get_cur_value_if_defined(self, var_name: str) -> T.Union[BaseNode, UnknownValue, UndefinedVariable]:
+ if var_name in self.predefined_vars:
+ return self.predefined_vars[var_name]
+ ret: T.Union[BaseNode, UnknownValue, UndefinedVariable] = UndefinedVariable()
+ for nesting, value in reversed(self.cur_assignments[var_name]):
+ if len(self.nesting) >= len(nesting) and self.nesting[:len(nesting)] == nesting:
+ ret = value
+ break
+ if isinstance(ret, UndefinedVariable) and self.tainted:
+ return UnknownValue()
+ return ret
+
+ def get_cur_value(self, var_name: str) -> T.Union[BaseNode, UnknownValue]:
+ ret = self.get_cur_value_if_defined(var_name)
+ if isinstance(ret, UndefinedVariable):
+ path = mlog.get_relative_path(Path(self.current_node.filename), Path(os.getcwd()))
+ mlog.warning(f"{path}:{self.current_node.lineno}:{self.current_node.colno} will always crash if executed, since a variable named `{var_name}` is not defined")
+ # We could add more advanced analysis of code referencing undefined
+ # variables, but it is probably not worth the effort and the
+ # complexity. So we do the simplest thing, returning an
+ # UnknownValue.
+ return UnknownValue()
+ return ret
+
+ # The function `node_to_runtime_value` takes a node of the ast as an
+ # argument and tries to return the same thing that would be passed to e.g.
+ # `func_message` if you put `message(node)` in your `meson.build` file and
+ # run `meson setup`. If this is not possible, `UnknownValue()` is returned.
+ # There are 3 Reasons why this is sometimes impossible:
+ # 1. Because the meson rewriter is imperfect and has not implemented everything yet
+ # 2. Because the value is different on different machines, example:
+ # ```meson
+ # node = somedep.found()
+ # message(node)
+ # ```
+ # will print `true` on some machines and `false` on others, so
+ # `node_to_runtime_value` does not know whether to return `true` or
+ # `false` and will return `UnknownValue()`.
+ # 3. Here:
+ # ```meson
+ # foreach x : [1, 2]
+ # node = x
+ # message(node)
+ # endforeach
+ # ```
+ # `node_to_runtime_value` does not know whether to return `1` or `2` and
+ # will return `UnknownValue()`.
+ #
+ # If you have something like
+ # ```
+ # node = [123, somedep.found()]
+ # ```
+ # `node_to_runtime_value` will return `[123, UnknownValue()]`.
+ def node_to_runtime_value(self, node: T.Union[UnknownValue, BaseNode, TYPE_var]) -> T.Any:
+ if isinstance(node, (mparser.StringNode, mparser.BooleanNode, mparser.NumberNode)):
+ return node.value
+ elif isinstance(node, mparser.StringNode):
+ if node.is_fstring:
+ return UnknownValue()
+ else:
+ return node.value
+ elif isinstance(node, list):
+ return [self.node_to_runtime_value(x) for x in node]
elif isinstance(node, ArrayNode):
- result = node.args.arguments.copy()
+ return [self.node_to_runtime_value(x) for x in node.args.arguments]
+ elif isinstance(node, mparser.DictNode):
+ return {self.node_to_runtime_value(k): self.node_to_runtime_value(v) for k, v in node.args.kwargs.items()}
+ elif isinstance(node, IdNode):
+ assert len(self.dataflow_dag.tgt_to_srcs[node]) == 1
+ val = next(iter(self.dataflow_dag.tgt_to_srcs[node]))
+ return self.node_to_runtime_value(val)
+ elif isinstance(node, (MethodNode, FunctionNode)):
+ funcval = self.funcvals[node]
+ if isinstance(funcval, (dict, str)):
+ return funcval
+ else:
+ return self.node_to_runtime_value(funcval)
+ elif isinstance(node, ArithmeticNode):
+ left = self.node_to_runtime_value(node.left)
+ right = self.node_to_runtime_value(node.right)
+ if isinstance(left, list) and isinstance(right, UnknownValue):
+ return left + [right]
+ if isinstance(right, list) and isinstance(left, UnknownValue):
+ return [left] + right
+ if isinstance(left, UnknownValue) or isinstance(right, UnknownValue):
+ return UnknownValue()
+ if node.operation == '+':
+ if isinstance(left, dict) and isinstance(right, dict):
+ ret = left.copy()
+ for k, v in right.items():
+ ret[k] = v
+ return ret
+ if isinstance(left, list):
+ if not isinstance(right, list):
+ right = [right]
+ return left + right
+ return left + right
+ elif node.operation == '-':
+ return left - right
+ elif node.operation == '*':
+ return left * right
+ elif node.operation == '/':
+ if isinstance(left, int) and isinstance(right, int):
+ return left // right
+ elif isinstance(left, str) and isinstance(right, str):
+ return os.path.join(left, right).replace('\\', '/')
+ elif node.operation == '%':
+ if isinstance(left, int) and isinstance(right, int):
+ return left % right
+ elif isinstance(node, (UnknownValue, IntrospectionBuildTarget, IntrospectionFile, IntrospectionDependency, str, bool, int)):
+ return node
+ elif isinstance(node, mparser.IndexNode):
+ iobject = self.node_to_runtime_value(node.iobject)
+ index = self.node_to_runtime_value(node.index)
+ if isinstance(iobject, UnknownValue) or isinstance(index, UnknownValue):
+ return UnknownValue()
+ return iobject[index]
+ elif isinstance(node, mparser.ComparisonNode):
+ left = self.node_to_runtime_value(node.left)
+ right = self.node_to_runtime_value(node.right)
+ if isinstance(left, UnknownValue) or isinstance(right, UnknownValue):
+ return UnknownValue()
+ if node.ctype == '==':
+ return left == right
+ elif node.ctype == '!=':
+ return left != right
+ elif node.ctype == 'in':
+ return left in right
+ elif node.ctype == 'not in':
+ return left not in right
+ elif isinstance(node, mparser.TernaryNode):
+ cond = self.node_to_runtime_value(node.condition)
+ if isinstance(cond, UnknownValue):
+ return UnknownValue()
+ if cond is True:
+ return self.node_to_runtime_value(node.trueblock)
+ if cond is False:
+ return self.node_to_runtime_value(node.falseblock)
+ elif isinstance(node, mparser.OrNode):
+ left = self.node_to_runtime_value(node.left)
+ right = self.node_to_runtime_value(node.right)
+ if isinstance(left, UnknownValue) or isinstance(right, UnknownValue):
+ return UnknownValue()
+ return left or right
+ elif isinstance(node, mparser.AndNode):
+ left = self.node_to_runtime_value(node.left)
+ right = self.node_to_runtime_value(node.right)
+ if isinstance(left, UnknownValue) or isinstance(right, UnknownValue):
+ return UnknownValue()
+ return left and right
+ elif isinstance(node, mparser.UMinusNode):
+ val = self.node_to_runtime_value(node.value)
+ if isinstance(val, UnknownValue):
+ return val
+ if isinstance(val, (int, float)):
+ return -val
+ elif isinstance(node, mparser.NotNode):
+ val = self.node_to_runtime_value(node.value)
+ if isinstance(val, UnknownValue):
+ return val
+ if isinstance(val, bool):
+ return not val
+ elif isinstance(node, mparser.ParenthesizedNode):
+ return self.node_to_runtime_value(node.inner)
+ raise mesonlib.MesonBugException('Unhandled node type')
- elif isinstance(node, ArgumentNode):
- result = node.arguments.copy()
+ def assignment(self, node: AssignmentNode) -> None:
+ assert isinstance(node, AssignmentNode)
+ self.evaluate_statement(node.value)
+ self.cur_assignments[node.var_name.value].append((self.nesting.copy(), node.value))
+ self.all_assignment_nodes[node.var_name.value].append(node)
- elif isinstance(node, ArithmeticNode):
- if node.operation != 'add':
- return None # Only handle string and array concats
- l = self.resolve_node(node.left, include_unknown_args, id_loop_detect)
- r = self.resolve_node(node.right, include_unknown_args, id_loop_detect)
- if isinstance(l, str) and isinstance(r, str):
- result = l + r # String concatenation detected
+ def evaluate_plusassign(self, node: PlusAssignmentNode) -> None:
+ assert isinstance(node, PlusAssignmentNode)
+ self.evaluate_statement(node.value)
+ lhs = self.get_cur_value(node.var_name.value)
+ newval: T.Union[UnknownValue, ArithmeticNode]
+ if isinstance(lhs, UnknownValue):
+ newval = UnknownValue()
+ else:
+ newval = mparser.ArithmeticNode(operation='+', left=lhs, operator=_symbol('+'), right=node.value)
+ self.cur_assignments[node.var_name.value].append((self.nesting.copy(), newval))
+ self.all_assignment_nodes[node.var_name.value].append(node)
+
+ self.dataflow_dag.add_edge(lhs, newval)
+ self.dataflow_dag.add_edge(node.value, newval)
+
+ def func_set_variable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
+ assert isinstance(node, FunctionNode)
+ if bool(node.args.kwargs):
+ raise InvalidArguments('set_variable accepts no keyword arguments')
+ if len(node.args.arguments) != 2:
+ raise InvalidArguments('set_variable requires exactly two positional arguments')
+ var_name = args[0]
+ value = node.args.arguments[1]
+ if isinstance(var_name, UnknownValue):
+ self.evaluate_statement(value)
+ self.tainted = True
+ return
+ assert isinstance(var_name, str)
+ equiv = AssignmentNode(var_name=IdNode(Token('', '', 0, 0, 0, (0, 0), var_name)), value=value, operator=_symbol('='))
+ equiv.ast_id = str(id(equiv))
+ self.evaluate_statement(equiv)
+
+ def func_get_variable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Any:
+ assert isinstance(node, FunctionNode)
+ var_name = args[0]
+ if isinstance(var_name, UnknownValue):
+ val: T.Union[UnknownValue, BaseNode] = UnknownValue()
+ else:
+ assert isinstance(var_name, str)
+ val = self.get_cur_value(var_name)
+ self.dataflow_dag.add_edge(val, node)
+ return val
+
+ def func_unset_variable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
+ assert isinstance(node, FunctionNode)
+ if bool(node.args.kwargs):
+ raise InvalidArguments('unset_variable accepts no keyword arguments')
+ if len(node.args.arguments) != 1:
+ raise InvalidArguments('unset_variable requires exactly one positional arguments')
+ var_name = args[0]
+ assert isinstance(var_name, str)
+ self.cur_assignments[var_name].append((self.nesting.copy(), node))
+
+ def nodes_to_pretty_filelist(self, root_path: Path, subdir: str, nodes: T.List[BaseNode]) -> T.List[T.Union[str, UnknownValue]]:
+ def src_to_abs(src: T.Union[str, IntrospectionFile, UnknownValue]) -> T.Union[str, UnknownValue]:
+ if isinstance(src, str):
+ return os.path.normpath(os.path.join(root_path, subdir, src))
+ elif isinstance(src, IntrospectionFile):
+ return str(src.to_abs_path(root_path))
+ elif isinstance(src, UnknownValue):
+ return src
else:
- result = self.flatten_args(l, include_unknown_args, id_loop_detect) + self.flatten_args(r, include_unknown_args, id_loop_detect)
-
- elif isinstance(node, MethodNode):
- src = quick_resolve(node.source_object)
- margs = self.flatten_args(node.args.arguments, include_unknown_args, id_loop_detect)
- mkwargs: T.Dict[str, TYPE_var] = {}
- method_name = node.name.value
- try:
- if isinstance(src, str):
- result = StringHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, bool):
- result = BooleanHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, int):
- result = IntegerHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, list):
- result = ArrayHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, dict):
- result = DictHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- except mesonlib.MesonException:
- return None
-
- # Ensure that the result is fully resolved (no more nodes)
- if isinstance(result, BaseNode):
- result = self.resolve_node(result, include_unknown_args, id_loop_detect)
- elif isinstance(result, list):
- new_res: T.List[TYPE_var] = []
- for i in result:
- if isinstance(i, BaseNode):
- resolved = self.resolve_node(i, include_unknown_args, id_loop_detect)
- if resolved is not None:
- new_res += self.flatten_args(resolved, include_unknown_args, id_loop_detect)
- else:
- new_res += [i]
- result = new_res
+ raise TypeError
- return result
+ rtvals: T.List[T.Any] = flatten([self.node_to_runtime_value(sn) for sn in nodes])
+ return [src_to_abs(x) for x in rtvals]
- def flatten_args(self, args_raw: T.Union[TYPE_var, T.Sequence[TYPE_var]], include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.List[TYPE_var]:
+ def flatten_args(self, args_raw: T.Union[TYPE_nvar, T.Sequence[TYPE_nvar]], include_unknown_args: bool = False) -> T.List[TYPE_var]:
# Make sure we are always dealing with lists
if isinstance(args_raw, list):
args = args_raw
else:
args = [args_raw]
- flattened_args: T.List[TYPE_var] = []
+ # BaseNode resolves to Any. :/
+ flattened_args: T.List[T.Union[TYPE_var, T.Any]] = []
# Resolve the contents of args
for i in args:
if isinstance(i, BaseNode):
- resolved = self.resolve_node(i, include_unknown_args, id_loop_detect)
+ resolved = self.node_to_runtime_value(i)
if resolved is not None:
if not isinstance(resolved, list):
resolved = [resolved]
flattened_args += resolved
- elif isinstance(i, (str, bool, int, float)) or include_unknown_args:
+ elif isinstance(i, (str, bool, int, float, UnknownValue, IntrospectionFile)) or include_unknown_args:
flattened_args += [i]
+ else:
+ raise NotImplementedError
return flattened_args
def evaluate_testcase(self, node: TestCaseClauseNode) -> Disabler | None:
return Disabler(subproject=self.subproject)
+
+ def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[InterpreterObject]:
+ if hasattr(cur, 'args'):
+ for arg in cur.args.arguments:
+ self.dataflow_dag.add_edge(arg, cur)
+ for k, v in cur.args.kwargs.items():
+ self.dataflow_dag.add_edge(v, cur)
+ for attr in ['source_object', 'left', 'right', 'items', 'iobject', 'index', 'condition']:
+ if hasattr(cur, attr):
+ assert isinstance(getattr(cur, attr), mparser.BaseNode)
+ self.dataflow_dag.add_edge(getattr(cur, attr), cur)
+ if isinstance(cur, mparser.IdNode):
+ self.dataflow_dag.add_edge(self.get_cur_value(cur.value), cur)
+ return None
+ else:
+ return super().evaluate_statement(cur)
+
+ def function_call(self, node: mparser.FunctionNode) -> T.Any:
+ ret = super().function_call(node)
+ if ret is not None:
+ self.funcvals[node] = ret
+ return ret
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index 4eb3fec..360edae 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -6,22 +6,20 @@
# or an interpreter-based tool
from __future__ import annotations
-import copy
import os
import typing as T
from .. import compilers, environment, mesonlib, options
-from .. import coredata as cdata
from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..compilers import detect_compiler_for
-from ..interpreterbase import InvalidArguments, SubProject
+from ..interpreterbase import InvalidArguments, SubProject, UnknownValue
from ..mesonlib import MachineChoice
from ..options import OptionKey
-from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
-from .interpreter import AstInterpreter
+from ..mparser import BaseNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
+from .interpreter import AstInterpreter, IntrospectionBuildTarget, IntrospectionDependency
if T.TYPE_CHECKING:
- from ..build import BuildTarget
+ from ..build import BuildTarget, BuildTargetKeywordArguments
from ..interpreterbase import TYPE_var
from .visitor import AstVisitor
@@ -44,8 +42,11 @@ class IntrospectionHelper:
return NotImplemented
class IntrospectionInterpreter(AstInterpreter):
- # Interpreter to detect the options without a build directory
- # Most of the code is stolen from interpreter.Interpreter
+ # If you run `meson setup ...` the `Interpreter`-class walks over the AST.
+ # If you run `meson rewrite ...` and `meson introspect meson.build ...`,
+ # the `AstInterpreter`-class walks over the AST.
+ # Works without a build directory.
+ # Most of the code is stolen from interpreter.Interpreter .
def __init__(self,
source_root: str,
subdir: str,
@@ -61,11 +62,10 @@ class IntrospectionInterpreter(AstInterpreter):
self.cross_file = cross_file
self.backend = backend
- self.default_options = {OptionKey('backend'): self.backend}
self.project_data: T.Dict[str, T.Any] = {}
- self.targets: T.List[T.Dict[str, T.Any]] = []
- self.dependencies: T.List[T.Dict[str, T.Any]] = []
- self.project_node: BaseNode = None
+ self.targets: T.List[IntrospectionBuildTarget] = []
+ self.dependencies: T.List[IntrospectionDependency] = []
+ self.project_node: FunctionNode = None
self.funcs.update({
'add_languages': self.func_add_languages,
@@ -83,6 +83,7 @@ class IntrospectionInterpreter(AstInterpreter):
def func_project(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
if self.project_node:
raise InvalidArguments('Second call to project()')
+ assert isinstance(node, FunctionNode)
self.project_node = node
if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
@@ -114,25 +115,6 @@ class IntrospectionInterpreter(AstInterpreter):
self._load_option_file()
- def_opts = self.flatten_args(kwargs.get('default_options', []))
- _project_default_options = mesonlib.stringlistify(def_opts)
- string_dict = cdata.create_options_dict(_project_default_options, self.subproject)
- self.project_default_options = {OptionKey(s): v for s, v in string_dict.items()}
- self.default_options.update(self.project_default_options)
- if self.environment.first_invocation or (self.subproject != '' and self.subproject not in self.coredata.initialized_subprojects):
- if self.subproject == '':
- self.coredata.optstore.initialize_from_top_level_project_call(
- T.cast('T.Dict[T.Union[OptionKey, str], str]', string_dict),
- {}, # TODO: not handled by this Interpreter.
- self.environment.options)
- else:
- self.coredata.optstore.initialize_from_subproject_call(
- self.subproject,
- {}, # TODO: this isn't handled by the introspection interpreter...
- T.cast('T.Dict[T.Union[OptionKey, str], str]', string_dict),
- {}) # TODO: this isn't handled by the introspection interpreter...
- self.coredata.initialized_subprojects.add(self.subproject)
-
if not self.is_subproject() and 'subproject_dir' in kwargs:
spdirname = kwargs['subproject_dir']
if isinstance(spdirname, StringNode):
@@ -146,10 +128,8 @@ class IntrospectionInterpreter(AstInterpreter):
if os.path.isdir(os.path.join(subprojects_dir, i)):
self.do_subproject(SubProject(i))
- self.coredata.init_backend_options(self.backend)
- options = {k: v for k, v in self.environment.options.items() if self.environment.coredata.optstore.is_backend_option(k)}
+ self.environment.init_backend_options(self.backend)
- self.coredata.set_options(options)
self._add_languages(proj_langs, True, MachineChoice.HOST)
self._add_languages(proj_langs, True, MachineChoice.BUILD)
@@ -164,10 +144,10 @@ class IntrospectionInterpreter(AstInterpreter):
except (mesonlib.MesonException, RuntimeError):
pass
- def func_add_languages(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
+ def func_add_languages(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> UnknownValue:
kwargs = self.flatten_kwargs(kwargs)
required = kwargs.get('required', True)
- assert isinstance(required, (bool, options.UserFeatureOption)), 'for mypy'
+ assert isinstance(required, (bool, options.UserFeatureOption, UnknownValue)), 'for mypy'
if isinstance(required, options.UserFeatureOption):
required = required.is_enabled()
if 'native' in kwargs:
@@ -176,8 +156,9 @@ class IntrospectionInterpreter(AstInterpreter):
else:
for for_machine in [MachineChoice.BUILD, MachineChoice.HOST]:
self._add_languages(args, required, for_machine)
+ return UnknownValue()
- def _add_languages(self, raw_langs: T.List[TYPE_var], required: bool, for_machine: MachineChoice) -> None:
+ def _add_languages(self, raw_langs: T.List[TYPE_var], required: T.Union[bool, UnknownValue], for_machine: MachineChoice) -> None:
langs: T.List[str] = []
for l in self.flatten_args(raw_langs):
if isinstance(l, str):
@@ -192,48 +173,49 @@ class IntrospectionInterpreter(AstInterpreter):
comp = detect_compiler_for(self.environment, lang, for_machine, True, self.subproject)
except mesonlib.MesonException:
# do we even care about introspecting this language?
- if required:
+ if isinstance(required, UnknownValue) or required:
raise
else:
continue
- if self.subproject:
- options = {}
- for k in comp.get_options():
- v = copy.copy(self.coredata.optstore.get_value_object(k))
- k = k.evolve(subproject=self.subproject)
- options[k] = v
- self.coredata.add_compiler_options(options, lang, for_machine, self.environment, self.subproject)
-
- def func_dependency(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
+ if comp:
+ self.coredata.process_compiler_options(lang, comp, self.subproject)
+
+ def func_dependency(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[IntrospectionDependency]:
+ assert isinstance(node, FunctionNode)
args = self.flatten_args(args)
kwargs = self.flatten_kwargs(kwargs)
if not args:
- return
+ return None
name = args[0]
+ assert isinstance(name, (str, UnknownValue))
has_fallback = 'fallback' in kwargs
required = kwargs.get('required', True)
version = kwargs.get('version', [])
if not isinstance(version, list):
version = [version]
- if isinstance(required, ElementaryNode):
- required = required.value
- if not isinstance(required, bool):
- required = False
- self.dependencies += [{
- 'name': name,
- 'required': required,
- 'version': version,
- 'has_fallback': has_fallback,
- 'conditional': node.condition_level > 0,
- 'node': node
- }]
-
- def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dict[str, TYPE_var], targetclass: T.Type[BuildTarget]) -> T.Optional[T.Dict[str, T.Any]]:
+ if any(isinstance(el, UnknownValue) for el in version):
+ version = UnknownValue()
+ else:
+ assert all(isinstance(el, str) for el in version)
+ version = T.cast(T.List[str], version)
+ assert isinstance(required, (bool, UnknownValue))
+ newdep = IntrospectionDependency(
+ name=name,
+ required=required,
+ version=version,
+ has_fallback=has_fallback,
+ conditional=node.condition_level > 0,
+ node=node)
+ self.dependencies += [newdep]
+ return newdep
+
+ def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dict[str, TYPE_var], targetclass: T.Type[BuildTarget]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
+ assert isinstance(node, FunctionNode)
args = self.flatten_args(args)
if not args or not isinstance(args[0], str):
- return None
+ return UnknownValue()
name = args[0]
- srcqueue = [node]
+ srcqueue: T.List[BaseNode] = [node]
extra_queue = []
# Process the sources BEFORE flattening the kwargs, to preserve the original nodes
@@ -245,45 +227,26 @@ class IntrospectionInterpreter(AstInterpreter):
kwargs = self.flatten_kwargs(kwargs_raw, True)
- def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[BaseNode]:
- res: T.List[BaseNode] = []
- while inqueue:
- curr = inqueue.pop(0)
- arg_node = None
- assert isinstance(curr, BaseNode)
- if isinstance(curr, FunctionNode):
- arg_node = curr.args
- elif isinstance(curr, ArrayNode):
- arg_node = curr.args
- elif isinstance(curr, IdNode):
- # Try to resolve the ID and append the node to the queue
- assert isinstance(curr.value, str)
- var_name = curr.value
- if var_name in self.assignments:
- tmp_node = self.assignments[var_name]
- if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)):
- inqueue += [tmp_node]
- elif isinstance(curr, ArithmeticNode):
- inqueue += [curr.left, curr.right]
- if arg_node is None:
- continue
- arg_nodes = arg_node.arguments.copy()
- # Pop the first element if the function is a build target function
- if isinstance(curr, FunctionNode) and curr.func_name.value in BUILD_TARGET_FUNCTIONS:
- arg_nodes.pop(0)
- elementary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))]
- inqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))]
- if elementary_nodes:
- res += [curr]
- return res
-
- source_nodes = traverse_nodes(srcqueue)
- extraf_nodes = traverse_nodes(extra_queue)
+ oldlen = len(node.args.arguments)
+ source_nodes = node.args.arguments[1:]
+ for k, v in node.args.kwargs.items():
+ assert isinstance(k, IdNode)
+ if k.value == 'sources':
+ source_nodes.append(v)
+ assert oldlen == len(node.args.arguments)
+
+ extraf_nodes = None
+ for k, v in node.args.kwargs.items():
+ assert isinstance(k, IdNode)
+ if k.value == 'extra_files':
+ assert extraf_nodes is None
+ extraf_nodes = v
# Make sure nothing can crash when creating the build class
- kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in {'install', 'build_by_default', 'build_always'}}
- kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in kwargs_reduced.items()}
- kwargs_reduced = {k: v for k, v in kwargs_reduced.items() if not isinstance(v, BaseNode)}
+ _kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in {'install', 'build_by_default', 'build_always', 'name_prefix'}}
+ _kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in _kwargs_reduced.items()}
+ _kwargs_reduced = {k: v for k, v in _kwargs_reduced.items() if not isinstance(v, (BaseNode, UnknownValue))}
+ kwargs_reduced = T.cast('BuildTargetKeywordArguments', _kwargs_reduced)
for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST
objects: T.List[T.Any] = []
empty_sources: T.List[T.Any] = []
@@ -293,27 +256,34 @@ class IntrospectionInterpreter(AstInterpreter):
self.environment, self.coredata.compilers[for_machine], kwargs_reduced)
target.process_compilers_late()
- new_target = {
- 'name': target.get_basename(),
- 'machine': target.for_machine.get_lower_case_name(),
- 'id': target.get_id(),
- 'type': target.get_typename(),
- 'defined_in': os.path.normpath(os.path.join(self.source_root, self.subdir, environment.build_filename)),
- 'subdir': self.subdir,
- 'build_by_default': target.build_by_default,
- 'installed': target.should_install(),
- 'outputs': target.get_outputs(),
- 'sources': source_nodes,
- 'extra_files': extraf_nodes,
- 'kwargs': kwargs,
- 'node': node,
- }
+ build_by_default: T.Union[UnknownValue, bool] = target.build_by_default
+ if 'build_by_default' in kwargs and isinstance(kwargs['build_by_default'], UnknownValue):
+ build_by_default = kwargs['build_by_default']
+
+ install: T.Union[UnknownValue, bool] = target.should_install()
+ if 'install' in kwargs and isinstance(kwargs['install'], UnknownValue):
+ install = kwargs['install']
+
+ new_target = IntrospectionBuildTarget(
+ name=target.get_basename(),
+ machine=target.for_machine.get_lower_case_name(),
+ id=target.get_id(),
+ typename=target.get_typename(),
+ defined_in=os.path.normpath(os.path.join(self.source_root, self.subdir, environment.build_filename)),
+ subdir=self.subdir,
+ build_by_default=build_by_default,
+ installed=install,
+ outputs=target.get_outputs(),
+ source_nodes=source_nodes,
+ extra_files=extraf_nodes,
+ kwargs=kwargs,
+ node=node)
self.targets += [new_target]
return new_target
- def build_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
- default_library = self.coredata.optstore.get_value_for(OptionKey('default_library'))
+ def build_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
+ default_library = self.coredata.optstore.get_value_for(OptionKey('default_library', subproject=self.subproject))
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibrary)
elif default_library == 'static':
@@ -322,28 +292,28 @@ class IntrospectionInterpreter(AstInterpreter):
return self.build_target(node, args, kwargs, SharedLibrary)
return None
- def func_executable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_executable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, Executable)
- def func_static_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_static_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, StaticLibrary)
- def func_shared_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_shared_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedLibrary)
- def func_both_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_both_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedLibrary)
- def func_shared_module(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_shared_module(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedModule)
- def func_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_library(node, args, kwargs)
- def func_jar(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_jar(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, Jar)
- def func_build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Optional[T.Dict[str, T.Any]]:
+ def func_build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
if 'target_type' not in kwargs:
return None
target_type = kwargs.pop('target_type')
@@ -395,7 +365,7 @@ class IntrospectionInterpreter(AstInterpreter):
flattened_kwargs = {}
for key, val in kwargs.items():
if isinstance(val, BaseNode):
- resolved = self.resolve_node(val, include_unknown_args)
+ resolved = self.node_to_runtime_value(val)
if resolved is not None:
flattened_kwargs[key] = resolved
elif isinstance(val, (str, bool, int, float)) or include_unknown_args:
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py
index 4ce3b3f..024b62b 100644
--- a/mesonbuild/ast/printer.py
+++ b/mesonbuild/ast/printer.py
@@ -7,12 +7,46 @@ from __future__ import annotations
from .. import mparser
from .visitor import AstVisitor, FullAstVisitor
+from ..mesonlib import MesonBugException
import re
import typing as T
+# Also known as "order of operations" or "binding power".
+# This is the counterpart to Parser.e1, Parser.e2, Parser.e3, Parser.e4, Parser.e5, Parser.e6, Parser.e7, Parser.e8, Parser.e9, Parser.e10
+def precedence_level(node: mparser.BaseNode) -> int:
+ if isinstance(node, (mparser.PlusAssignmentNode, mparser.AssignmentNode, mparser.TernaryNode)):
+ return 1
+ elif isinstance(node, mparser.OrNode):
+ return 2
+ elif isinstance(node, mparser.AndNode):
+ return 3
+ elif isinstance(node, mparser.ComparisonNode):
+ return 4
+ elif isinstance(node, mparser.ArithmeticNode):
+ if node.operation in {'+', '-'}:
+ return 5
+ elif node.operation in {'%', '*', '/'}:
+ return 6
+ elif isinstance(node, (mparser.NotNode, mparser.UMinusNode)):
+ return 7
+ elif isinstance(node, mparser.FunctionNode):
+ return 8
+ elif isinstance(node, (mparser.ArrayNode, mparser.DictNode)):
+ return 9
+ elif isinstance(node, (mparser.BooleanNode, mparser.IdNode, mparser.NumberNode, mparser.StringNode, mparser.EmptyNode)):
+ return 10
+ elif isinstance(node, mparser.ParenthesizedNode):
+ # Parenthesize have the highest binding power, but since the AstPrinter
+ # ignores ParanthesizedNode, the binding power of the inner node is
+ # relevant.
+ return precedence_level(node.inner)
+ raise MesonBugException('Unhandled node type')
+
class AstPrinter(AstVisitor):
+ escape_trans: T.Dict[int, str] = str.maketrans({'\\': '\\\\', "'": "\'"})
+
def __init__(self, indent: int = 2, arg_newline_cutoff: int = 5, update_ast_line_nos: bool = False):
self.result = ''
self.indent = indent
@@ -57,7 +91,7 @@ class AstPrinter(AstVisitor):
node.lineno = self.curr_line or node.lineno
def escape(self, val: str) -> str:
- return val.replace('\\', '\\\\').replace("'", "\'")
+ return val.translate(self.escape_trans)
def visit_StringNode(self, node: mparser.StringNode) -> None:
assert isinstance(node.value, str)
@@ -104,15 +138,25 @@ class AstPrinter(AstVisitor):
def visit_ComparisonNode(self, node: mparser.ComparisonNode) -> None:
node.left.accept(self)
- self.append_padded(node.ctype if node.ctype != 'notin' else 'not in', node)
+ self.append_padded(node.ctype, node)
node.lineno = self.curr_line or node.lineno
node.right.accept(self)
+ def maybe_parentheses(self, outer: mparser.BaseNode, inner: mparser.BaseNode, parens: bool) -> None:
+ if parens:
+ self.append('(', inner)
+ inner.accept(self)
+ if parens:
+ self.append(')', inner)
+
def visit_ArithmeticNode(self, node: mparser.ArithmeticNode) -> None:
- node.left.accept(self)
+ prec = precedence_level(node)
+ prec_left = precedence_level(node.left)
+ prec_right = precedence_level(node.right)
+ self.maybe_parentheses(node, node.left, prec > prec_left)
self.append_padded(node.operator.value, node)
node.lineno = self.curr_line or node.lineno
- node.right.accept(self)
+ self.maybe_parentheses(node, node.right, prec > prec_right or (prec == prec_right and node.operation in {'sub', 'div', 'mod'}))
def visit_NotNode(self, node: mparser.NotNode) -> None:
node.lineno = self.curr_line or node.lineno
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 3dfa2fb..dddcf67 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -24,12 +24,13 @@ from .. import dependencies
from .. import programs
from .. import mesonlib
from .. import mlog
-from ..compilers import LANGUAGES_USING_LDFLAGS, detect, lang_suffixes
+from .. import compilers
+from ..compilers import detect, lang_suffixes
from ..mesonlib import (
File, MachineChoice, MesonException, MesonBugException, OrderedSet,
ExecutableSerialisation, EnvironmentException,
classify_unity_sources, get_compiler_for_source,
- is_parent_path,
+ get_rsp_threshold, unique_list
)
from ..options import OptionKey
@@ -39,14 +40,14 @@ if T.TYPE_CHECKING:
from ..arglist import CompilerArgs
from ..compilers import Compiler
from ..environment import Environment
- from ..interpreter import Interpreter, Test
+ from ..interpreter import Test
from ..linkers.linkers import StaticLinker
from ..mesonlib import FileMode, FileOrString
from ..options import ElementaryOptionValues
from typing_extensions import TypedDict, NotRequired
- _ALL_SOURCES_TYPE = T.List[T.Union[File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
+ _ALL_SOURCES_TYPE = T.List[T.Union[File, build.GeneratedTypes]]
class TargetIntrospectionData(TypedDict):
@@ -61,7 +62,7 @@ if T.TYPE_CHECKING:
# Languages that can mix with C or C++ but don't support unity builds yet
# because the syntax we use for unity builds is specific to C/++/ObjC/++.
# Assembly files cannot be unitified and neither can LLVM IR files
-LANGS_CANT_UNITY = ('d', 'fortran', 'vala')
+LANGS_CANT_UNITY = ('d', 'fortran', 'vala', 'rust')
@dataclass(eq=False)
class RegenInfo:
@@ -177,7 +178,6 @@ class InstallSymlinkData:
install_path: str
subproject: str
tag: T.Optional[str] = None
- allow_missing: bool = False
# cannot use dataclass here because "exclude" is out of order
class SubdirInstallData(InstallDataBase):
@@ -218,47 +218,50 @@ class TestSerialisation:
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']:
+def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']:
if backend == 'ninja':
from . import ninjabackend
- return ninjabackend.NinjaBackend(build, interpreter)
+ return ninjabackend.NinjaBackend(build)
elif backend == 'vs':
from . import vs2010backend
- return vs2010backend.autodetect_vs_version(build, interpreter)
+ return vs2010backend.autodetect_vs_version(build)
elif backend == 'vs2010':
from . import vs2010backend
- return vs2010backend.Vs2010Backend(build, interpreter)
+ return vs2010backend.Vs2010Backend(build)
elif backend == 'vs2012':
from . import vs2012backend
- return vs2012backend.Vs2012Backend(build, interpreter)
+ return vs2012backend.Vs2012Backend(build)
elif backend == 'vs2013':
from . import vs2013backend
- return vs2013backend.Vs2013Backend(build, interpreter)
+ return vs2013backend.Vs2013Backend(build)
elif backend == 'vs2015':
from . import vs2015backend
- return vs2015backend.Vs2015Backend(build, interpreter)
+ return vs2015backend.Vs2015Backend(build)
elif backend == 'vs2017':
from . import vs2017backend
- return vs2017backend.Vs2017Backend(build, interpreter)
+ return vs2017backend.Vs2017Backend(build)
elif backend == 'vs2019':
from . import vs2019backend
- return vs2019backend.Vs2019Backend(build, interpreter)
+ return vs2019backend.Vs2019Backend(build)
elif backend == 'vs2022':
from . import vs2022backend
- return vs2022backend.Vs2022Backend(build, interpreter)
+ return vs2022backend.Vs2022Backend(build)
+ elif backend == 'vs2026':
+ from . import vs2026backend
+ return vs2026backend.Vs2026Backend(build)
elif backend == 'xcode':
from . import xcodebackend
- return xcodebackend.XCodeBackend(build, interpreter)
+ return xcodebackend.XCodeBackend(build)
elif backend == 'none':
from . import nonebackend
- return nonebackend.NoneBackend(build, interpreter)
+ return nonebackend.NoneBackend(build)
return None
-def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']:
+def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']:
if genvsname == 'vs2022':
from . import vs2022backend
- return vs2022backend.Vs2022Backend(build, interpreter, gen_lite = True)
+ return vs2022backend.Vs2022Backend(build, gen_lite = True)
return None
# This class contains the basic functionality that is needed by all backends.
@@ -268,14 +271,13 @@ class Backend:
environment: T.Optional['Environment']
name = '<UNKNOWN>'
- def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional['Interpreter']):
+ def __init__(self, build: T.Optional[build.Build]):
# Make it possible to construct a dummy backend
# This is used for introspection without a build directory
if build is None:
self.environment = None
return
self.build = build
- self.interpreter = interpreter
self.environment = build.environment
self.processed_targets: T.Set[str] = set()
self.build_dir = self.environment.get_build_dir()
@@ -296,7 +298,7 @@ class Backend:
def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) -> T.Optional[T.Dict]:
raise RuntimeError(f'generate is not implemented in {type(self).__name__}')
- def get_target_filename(self, t: T.Union[build.Target, build.CustomTargetIndex], *, warn_multi_output: bool = True) -> str:
+ def get_target_filename(self, t: build.AnyTargetType, *, warn_multi_output: bool = True) -> str:
if isinstance(t, build.CustomTarget):
if warn_multi_output and len(t.get_outputs()) != 1:
mlog.warning(f'custom_target {t.name!r} has more than one output! '
@@ -309,7 +311,7 @@ class Backend:
filename = t.get_filename()
return os.path.join(self.get_target_dir(t), filename)
- def get_target_filename_abs(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
+ def get_target_filename_abs(self, target: build.AnyTargetType) -> str:
return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
def get_target_debug_filename(self, target: build.BuildTarget) -> T.Optional[str]:
@@ -337,14 +339,14 @@ class Backend:
def get_build_dir_include_args(self, target: build.BuildTarget, compiler: 'Compiler', *, absolute_path: bool = False) -> T.List[str]:
if absolute_path:
- curdir = os.path.join(self.build_dir, target.get_subdir())
+ curdir = os.path.join(self.build_dir, target.get_builddir())
else:
- curdir = target.get_subdir()
+ curdir = target.get_builddir()
if curdir == '':
curdir = '.'
return compiler.get_include_args(curdir, False)
- def get_target_filename_for_linking(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> T.Optional[str]:
+ def get_target_filename_for_linking(self, target: build.AnyTargetType) -> T.Optional[str]:
# On some platforms (msvc for instance), the file that is used for
# dynamic linking is not the same as the dynamic library itself. This
# file is called an import library, and we want to link against that.
@@ -369,17 +371,23 @@ class Backend:
raise AssertionError(f'BUG: Tried to link to {target!r} which is not linkable')
@lru_cache(maxsize=None)
- def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
+ def get_target_dir(self, target: build.AnyTargetType) -> str:
if isinstance(target, build.RunTarget):
# this produces no output, only a dummy top-level name
dirname = ''
elif self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror':
- dirname = target.get_subdir()
+ dirname = target.get_builddir()
else:
dirname = 'meson-out'
+ build_subdir = target.get_build_subdir()
+ if build_subdir:
+ dirname = os.path.join(dirname, build_subdir)
return dirname
- def get_target_dir_relative_to(self, t: build.Target, o: build.Target) -> str:
+ def get_target_dir_relative_to(self,
+ t: T.Union[build.Target, build.CustomTargetIndex],
+ o: T.Union[build.Target, build.CustomTargetIndex],
+ ) -> str:
'''Get a target dir relative to another target's directory'''
target_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))
othert_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(o))
@@ -392,16 +400,16 @@ class Backend:
return os.path.join(self.build_to_src, target_dir)
return self.build_to_src
- def get_target_private_dir(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]) -> str:
+ def get_target_private_dir(self, target: build.BuildTargetTypes) -> str:
return os.path.join(self.get_target_filename(target, warn_multi_output=False) + '.p')
- def get_target_private_dir_abs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]) -> str:
+ def get_target_private_dir_abs(self, target: build.BuildTargetTypes) -> str:
return os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target))
@lru_cache(maxsize=None)
def get_target_generated_dir(
- self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex],
- gensrc: T.Union[build.CustomTarget, build.CustomTargetIndex, build.GeneratedList],
+ self, target: build.BuildTargetTypes,
+ gensrc: build.GeneratedTypes,
src: str) -> str:
"""
Takes a BuildTarget, a generator source (CustomTarget or GeneratedList),
@@ -415,7 +423,7 @@ class Backend:
# target that the GeneratedList is used in
return os.path.join(self.get_target_private_dir(target), src)
- def get_unity_source_file(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex],
+ def get_unity_source_file(self, target: build.BuildTargetTypes,
suffix: str, number: int) -> mesonlib.File:
# There is a potential conflict here, but it is unlikely that
# anyone both enables unity builds and has a file called foo-unity.cpp.
@@ -472,11 +480,11 @@ class Backend:
def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = ''
) -> T.Tuple[T.List[str], T.List[build.BuildTargetTypes]]:
obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root)
- return list(dict.fromkeys(obj_list)), deps
+ return unique_list(obj_list), deps
def determine_ext_objs(self, objects: build.ExtractedObjects) -> T.List[str]:
obj_list, _ = self._flatten_object_list(objects.target, [objects], '')
- return list(dict.fromkeys(obj_list))
+ return unique_list(obj_list)
def _flatten_object_list(self, target: build.BuildTarget,
objects: T.Sequence[T.Union[str, 'File', build.ExtractedObjects]],
@@ -486,7 +494,7 @@ class Backend:
for obj in objects:
if isinstance(obj, str):
o = os.path.join(proj_dir_to_build_root,
- self.build_to_src, target.get_subdir(), obj)
+ self.build_to_src, target.get_builddir(), obj)
obj_list.append(o)
elif isinstance(obj, mesonlib.File):
if obj.is_built:
@@ -533,6 +541,7 @@ class Backend:
capture: T.Optional[str] = None,
feed: T.Optional[str] = None,
env: T.Optional[mesonlib.EnvironmentVariables] = None,
+ can_use_rsp_file: bool = False,
tag: T.Optional[str] = None,
verbose: bool = False,
installdir_map: T.Optional[T.Dict[str, str]] = None) -> 'ExecutableSerialisation':
@@ -563,9 +572,7 @@ class Backend:
cmd_args: T.List[str] = []
for c in raw_cmd_args:
if isinstance(c, programs.ExternalProgram):
- p = c.get_path()
- assert isinstance(p, str)
- cmd_args.append(p)
+ cmd_args += c.get_command()
elif isinstance(c, (build.BuildTarget, build.CustomTarget)):
cmd_args.append(self.get_target_filename_abs(c))
elif isinstance(c, mesonlib.File):
@@ -589,11 +596,26 @@ class Backend:
else:
if exe_cmd[0].endswith('.jar'):
exe_cmd = ['java', '-jar'] + exe_cmd
- elif exe_cmd[0].endswith('.exe') and not (mesonlib.is_windows() or mesonlib.is_cygwin() or mesonlib.is_wsl()):
+ elif exe_cmd[0].endswith('.exe') and not (mesonlib.is_windows() or mesonlib.is_cygwin() or mesonlib.is_wsl() or machine.is_os2()):
exe_cmd = ['mono'] + exe_cmd
exe_wrapper = None
workdir = workdir or self.environment.get_build_dir()
+
+ # Must include separators as well
+ needs_rsp_file = can_use_rsp_file and sum(len(i) + 1 for i in cmd_args) >= get_rsp_threshold()
+
+ if needs_rsp_file:
+ hasher = hashlib.sha1()
+ args = ' '.join(mesonlib.quote_arg(arg) for arg in cmd_args)
+ hasher.update(args.encode(encoding='utf-8', errors='ignore'))
+ digest = hasher.hexdigest()
+ scratch_file = f'meson_rsp_{digest}.rsp'
+ rsp_file = os.path.join(self.environment.get_scratch_dir(), scratch_file)
+ with open(rsp_file, 'w', encoding='utf-8', newline='\n') as f:
+ f.write(args)
+ cmd_args = [f'@{rsp_file}']
+
return ExecutableSerialisation(exe_cmd + cmd_args, env,
exe_wrapper, workdir,
extra_paths, capture, feed, tag, verbose, installdir_map)
@@ -606,6 +628,7 @@ class Backend:
feed: T.Optional[str] = None,
force_serialize: bool = False,
env: T.Optional[mesonlib.EnvironmentVariables] = None,
+ can_use_rsp_file: bool = False,
verbose: bool = False) -> T.Tuple[T.List[str], str]:
'''
Serialize an executable for running with a generator or a custom target
@@ -613,7 +636,7 @@ class Backend:
cmd: T.List[T.Union[str, mesonlib.File, build.BuildTarget, build.CustomTarget, programs.ExternalProgram]] = []
cmd.append(exe)
cmd.extend(cmd_args)
- es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env, verbose=verbose)
+ es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env, can_use_rsp_file, verbose=verbose)
reasons: T.List[str] = []
if es.extra_paths:
reasons.append('to set PATH')
@@ -653,6 +676,9 @@ class Backend:
envlist.append(f'{k}={v}')
return ['env'] + envlist + es.cmd_args, ', '.join(reasons)
+ if any(a.startswith('@') for a in es.cmd_args):
+ reasons.append('because command is too long')
+
if not force_serialize:
if not capture and not feed:
return es.cmd_args, ''
@@ -715,118 +741,6 @@ class Backend:
return l, stdlib_args
@staticmethod
- def _libdir_is_system(libdir: str, compilers: T.Mapping[str, 'Compiler'], env: 'Environment') -> bool:
- libdir = os.path.normpath(libdir)
- for cc in compilers.values():
- if libdir in cc.get_library_dirs(env):
- return True
- return False
-
- def get_external_rpath_dirs(self, target: build.BuildTarget) -> T.Set[str]:
- args: T.List[str] = []
- for lang in LANGUAGES_USING_LDFLAGS:
- try:
- e = self.environment.coredata.get_external_link_args(target.for_machine, lang)
- if isinstance(e, str):
- args.append(e)
- else:
- 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,
- rpath_regex = re.compile(r'-Wl,-rpath[=,]([^,]+)')
- # Match solaris style compat runpath formats:
- # -Wl,-R
- # -Wl,-R,
- runpath_regex = re.compile(r'-Wl,-R[,]?([^,]+)')
- # Match symbols formats:
- # -Wl,--just-symbols=
- # -Wl,--just-symbols,
- symbols_regex = re.compile(r'-Wl,--just-symbols[=,]([^,]+)')
- for arg in args:
- rpath_match = rpath_regex.match(arg)
- if rpath_match:
- for dir in rpath_match.group(1).split(':'):
- dirs.add(dir)
- runpath_match = runpath_regex.match(arg)
- if runpath_match:
- for dir in runpath_match.group(1).split(':'):
- # The symbols arg is an rpath if the path is a directory
- if Path(dir).is_dir():
- dirs.add(dir)
- symbols_match = symbols_regex.match(arg)
- if symbols_match:
- for dir in symbols_match.group(1).split(':'):
- # Prevent usage of --just-symbols to specify rpath
- if Path(dir).is_dir():
- raise MesonException(f'Invalid arg for --just-symbols, {dir} is a directory.')
- return dirs
-
- @lru_cache(maxsize=None)
- def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]':
- paths: OrderedSet[str] = OrderedSet()
- srcdir = self.environment.get_source_dir()
-
- for dep in target.external_deps:
- if dep.type_name not in {'library', 'pkgconfig', 'cmake'}:
- continue
- 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
- # .dll is there for mingw gcc
- # .so's may be extended with version information, e.g. libxyz.so.1.2.3
- if not (
- os.path.splitext(libpath)[1] in {'.dll', '.lib', '.so', '.dylib'}
- or re.match(r'.+\.so(\.|$)', os.path.basename(libpath))
- ):
- continue
-
- if is_parent_path(srcdir, libdir):
- rel_to_src = libdir[len(srcdir) + 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.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]
- ) -> T.Tuple[str, ...]:
- result: OrderedSet[str]
- if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror':
- # Need a copy here
- result = OrderedSet(target.get_link_dep_subdirs())
- else:
- result = OrderedSet()
- result.add('meson-out')
- if isinstance(target, build.BuildTarget):
- 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)
-
- @staticmethod
@lru_cache(maxsize=None)
def canonicalize_filename(fname: str) -> str:
if os.path.altsep is not None:
@@ -905,18 +819,19 @@ class Backend:
# Filter out headers and all non-source files
sources: T.List['FileOrString'] = []
for s in raw_sources:
- if self.environment.is_source(s):
+ if compilers.is_source(s):
sources.append(s)
- elif self.environment.is_object(s):
+ elif compilers.is_object(s):
result.append(s.relative_name())
# MSVC generate an object file for PCH
if extobj.pch and self.target_uses_pch(extobj.target):
for lang, pch in extobj.target.pch.items():
- compiler = extobj.target.compilers[lang]
- if compiler.get_argument_syntax() == 'msvc':
- objname = self.get_msvc_pch_objname(lang, pch)
- result.append(os.path.join(targetdir, objname))
+ if pch:
+ compiler = extobj.target.compilers[lang]
+ if compiler.get_argument_syntax() == 'msvc':
+ objname = self.get_msvc_pch_objname(lang, pch)
+ result.append(os.path.join(targetdir, objname))
# extobj could contain only objects and no sources
if not sources:
@@ -951,13 +866,13 @@ class Backend:
args: T.List[str] = []
pchpath = self.get_target_private_dir(target)
includeargs = compiler.get_include_args(pchpath, False)
- p = target.get_pch(compiler.get_language())
+ p = target.pch.get(compiler.get_language())
if p:
args += compiler.get_pch_use_args(pchpath, p[0])
return includeargs + args
- def get_msvc_pch_objname(self, lang: str, pch: T.List[str]) -> str:
- if len(pch) == 1:
+ def get_msvc_pch_objname(self, lang: str, pch: T.Tuple[str, T.Optional[str]]) -> str:
+ if pch[1] is None:
# Same name as in create_msvc_pch_implementation() below.
return f'meson_pch-{lang}.obj'
return os.path.splitext(pch[1])[0] + '.obj'
@@ -1023,8 +938,8 @@ class Backend:
commands += compiler.get_werror_args()
# Add compile args for c_* or cpp_* build options set on the
# command-line or default_options inside project().
- commands += compiler.get_option_compile_args(target, self.environment, target.subproject)
- commands += compiler.get_option_std_args(target, self.environment, target.subproject)
+ commands += compiler.get_option_compile_args(target, target.subproject)
+ commands += compiler.get_option_std_args(target, target.subproject)
optimization = self.get_target_option(target, 'optimization')
assert isinstance(optimization, str), 'for mypy'
@@ -1067,11 +982,6 @@ class Backend:
if compiler.language == 'vala':
if dep.type_name == 'pkgconfig':
assert isinstance(dep, dependencies.ExternalDependency)
- if dep.name == 'glib-2.0' and dep.version_reqs is not None:
- for req in dep.version_reqs:
- if req.startswith(('>=', '==')):
- commands += ['--target-glib', req[2:]]
- break
commands += ['--pkg', dep.name]
elif isinstance(dep, dependencies.ExternalLibrary):
commands += dep.get_link_args('vala')
@@ -1083,6 +993,32 @@ class Backend:
commands += dep.get_exe_args(compiler)
# For 'automagic' deps: Boost and GTest. Also dependency('threads').
# pkg-config puts the thread flags itself via `Cflags:`
+ if compiler.language == 'vala':
+ # Vala wants to know the minimum glib version
+ for dep in target.added_deps:
+ if dep.name == 'glib-2.0':
+ if dep.type_name == 'pkgconfig':
+ assert isinstance(dep, dependencies.ExternalDependency)
+ if dep.version_reqs is not None:
+ for req in dep.version_reqs:
+ if req.startswith(('>=', '==')):
+ commands += ['--target-glib', req[2:].strip()]
+ break
+ elif isinstance(dep, dependencies.InternalDependency) and dep.version is not None:
+ glib_version = dep.version.split('.')
+ if len(glib_version) != 3:
+ mlog.warning(f'GLib version has unexpected format: {dep.version}')
+ break
+ try:
+ # If GLib version is a development version, downgrade
+ # --target-glib to the previous version, as valac will
+ # complain about non-even minor versions
+ glib_version[1] = str((int(glib_version[1]) // 2) * 2)
+ except ValueError:
+ mlog.warning(f'GLib version has unexpected format: {dep.version}')
+ break
+ commands += ['--target-glib', f'{glib_version[0]}.{glib_version[1]}']
+
# Fortran requires extra include directives.
if compiler.language == 'fortran':
for lt in chain(target.link_targets, target.link_whole_targets):
@@ -1127,8 +1063,8 @@ class Backend:
# Get program and library dirs from all target compilers
if isinstance(target, build.BuildTarget):
for cc in target.compilers.values():
- paths.update(cc.get_program_dirs(self.environment))
- paths.update(cc.get_library_dirs(self.environment))
+ paths.update(cc.get_program_dirs())
+ paths.update(cc.get_library_dirs())
return list(paths)
@staticmethod
@@ -1188,8 +1124,8 @@ class Backend:
return results
def determine_windows_extra_paths(
- self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, programs.ExternalProgram, mesonlib.File, str],
- extra_bdeps: T.Sequence[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]) -> T.List[str]:
+ self, target: T.Union[build.BuildTargetTypes, programs.ExternalProgram, mesonlib.File, str],
+ extra_bdeps: T.Sequence[build.BuildTargetTypes]) -> T.List[str]:
"""On Windows there is no such thing as an rpath.
We must determine all locations of DLLs that this exe
@@ -1255,15 +1191,12 @@ class Backend:
exe_wrapper = self.environment.get_exe_wrapper()
machine = self.environment.machines[exe.for_machine]
if machine.is_windows() or machine.is_cygwin():
- extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = []
+ extra_bdeps: T.List[build.BuildTargetTypes] = []
if isinstance(exe, build.CustomTarget):
extra_bdeps = list(exe.get_transitive_build_target_deps())
+ extra_bdeps.extend(t.depends)
+ extra_bdeps.extend(a for a in t.cmd_args if isinstance(a, build.BuildTarget))
extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps)
- for a in t.cmd_args:
- if isinstance(a, build.BuildTarget):
- for p in self.determine_windows_extra_paths(a, []):
- if p not in extra_paths:
- extra_paths.append(p)
else:
extra_paths = []
@@ -1289,8 +1222,12 @@ class Backend:
else:
raise MesonException('Bad object in test command.')
+ # set LD_LIBRARY_PATH for
+ # a) dependencies, as relying on rpath is not very safe:
+ # https://github.com/mesonbuild/meson/pull/11119
+ # b) depends and targets passed via args.
t_env = copy.deepcopy(t.env)
- if not machine.is_windows() and not machine.is_cygwin() and not machine.is_darwin():
+ if not machine.is_windows() and not machine.is_cygwin():
ld_lib_path_libs: T.Set[build.SharedLibrary] = set()
for d in depends:
if isinstance(d, build.BuildTarget):
@@ -1299,10 +1236,12 @@ class Backend:
ld_lib_path_libs.add(l)
env_build_dir = self.environment.get_build_dir()
- ld_lib_path: T.Set[str] = set(os.path.join(env_build_dir, l.get_subdir()) for l in ld_lib_path_libs)
+ ld_lib_path: T.Set[str] = set(os.path.join(env_build_dir, l.get_builddir()) for l in ld_lib_path_libs)
if ld_lib_path:
t_env.prepend('LD_LIBRARY_PATH', list(ld_lib_path), ':')
+ if machine.is_darwin():
+ t_env.prepend('DYLD_LIBRARY_PATH', list(ld_lib_path), ':')
ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross,
exe_wrapper, self.environment.need_exe_wrapper(),
@@ -1320,7 +1259,7 @@ class Backend:
def write_test_serialisation(self, tests: T.List['Test'], datafile: T.BinaryIO) -> None:
pickle.dump(self.create_test_serialisation(tests), datafile)
- def construct_target_rel_paths(self, t: T.Union[build.Target, build.CustomTargetIndex], workdir: T.Optional[str]) -> T.List[str]:
+ def construct_target_rel_paths(self, t: build.AnyTargetType, workdir: T.Optional[str]) -> T.List[str]:
target_dir = self.get_target_dir(t)
# ensure that test executables can be run when passed as arguments
if isinstance(t, build.Executable) and workdir is None:
@@ -1371,7 +1310,7 @@ class Backend:
'''List of all files whose alteration means that the build
definition needs to be regenerated.'''
deps = OrderedSet([str(Path(self.build_to_src) / df)
- for df in self.interpreter.get_build_def_files()])
+ for df in self.build.def_files])
if self.environment.is_cross_build():
deps.update(self.environment.coredata.cross_files)
deps.update(self.environment.coredata.config_files)
@@ -1442,27 +1381,31 @@ class Backend:
result[name] = b
return result
- def get_testlike_targets(self, benchmark: bool = False) -> T.OrderedDict[str, T.Union[build.BuildTarget, build.CustomTarget]]:
- result: T.OrderedDict[str, T.Union[build.BuildTarget, build.CustomTarget]] = OrderedDict()
+ def get_testlike_targets(self, benchmark: bool = False) -> T.Iterable[T.Union[build.BuildTarget, build.CustomTarget]]:
targets = self.build.get_benchmarks() if benchmark else self.build.get_tests()
for t in targets:
exe = t.exe
- if isinstance(exe, (build.CustomTarget, build.BuildTarget)):
- result[exe.get_id()] = exe
+ if isinstance(exe, build.CustomTargetIndex):
+ yield exe.target
+ elif isinstance(exe, (build.CustomTarget, build.BuildTarget)):
+ yield exe
for arg in t.cmd_args:
- if not isinstance(arg, (build.CustomTarget, build.BuildTarget)):
- continue
- result[arg.get_id()] = arg
+ if isinstance(arg, build.CustomTargetIndex):
+ yield arg.target
+ elif isinstance(arg, (build.CustomTarget, build.BuildTarget)):
+ yield arg
for dep in t.depends:
assert isinstance(dep, (build.CustomTarget, build.BuildTarget, build.CustomTargetIndex))
- result[dep.get_id()] = dep
- return result
+ if isinstance(dep, build.CustomTargetIndex):
+ yield dep.target
+ else:
+ yield dep
@lru_cache(maxsize=None)
def get_custom_target_provided_by_generated_source(self, generated_source: build.CustomTarget) -> 'ImmutableListProtocol[str]':
libs: T.List[str] = []
for f in generated_source.get_outputs():
- if self.environment.is_library(f):
+ if compilers.is_library(f):
libs.append(os.path.join(self.get_target_dir(generated_source), f))
return libs
@@ -1520,7 +1463,7 @@ class Backend:
deps.append(os.path.join(self.build_to_src, target.subdir, i))
return deps
- def get_custom_target_output_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
+ def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str:
# The XCode backend is special. A target foo/bar does
# not go to ${BUILDDIR}/foo/bar but instead to
# ${BUILDDIR}/${BUILDTYPE}/foo/bar.
@@ -1562,7 +1505,7 @@ class Backend:
def eval_custom_target_command(
self, target: build.CustomTarget, absolute_outputs: bool = False) -> \
- T.Tuple[T.List[str], T.List[str], T.List[str]]:
+ T.Tuple[T.List[str], T.List[str], T.List[str | programs.ExternalProgram]]:
# We want the outputs to be absolute only when using the VS backend
# XXX: Maybe allow the vs backend to use relative paths too?
source_root = self.build_to_src
@@ -1575,7 +1518,7 @@ class Backend:
outputs = [os.path.join(outdir, i) for i in target.get_outputs()]
inputs = self.get_custom_target_sources(target)
# Evaluate the command list
- cmd: T.List[str] = []
+ cmd: T.List[str | programs.ExternalProgram] = []
for i in target.command:
if isinstance(i, build.BuildTarget):
cmd += self.build_target_to_cmd_array(i)
@@ -1611,6 +1554,9 @@ class Backend:
if not target.absolute_paths:
pdir = self.get_target_private_dir(target)
i = i.replace('@PRIVATE_DIR@', pdir)
+ elif isinstance(i, programs.ExternalProgram):
+ # Let it pass and be extended elsewhere
+ pass
else:
raise RuntimeError(f'Argument {i} is of unknown type {type(i)}')
cmd.append(i)
@@ -1635,9 +1581,18 @@ class Backend:
# fixed.
#
# https://github.com/mesonbuild/meson/pull/737
- cmd = [i.replace('\\', '/') for i in cmd]
+ cmd = [i.replace('\\', '/') if isinstance(i, str) else i for i in cmd]
return inputs, outputs, cmd
+ def transform_link_args(self, target: build.BuildTarget, args: list[str]) -> list[str]:
+ resolved_args = []
+ for i in args:
+ if '@PRIVATE_DIR@' in i:
+ pdir = self.get_target_private_dir(target)
+ i = i.replace('@PRIVATE_DIR@', pdir)
+ resolved_args.append(i)
+ return resolved_args
+
def get_introspect_command(self) -> str:
return ' '.join(shlex.quote(x) for x in self.environment.get_build_command() + ['introspect'])
@@ -1777,7 +1732,7 @@ class Backend:
for alias, to, tag in t.get_aliases():
alias = os.path.join(first_outdir, alias)
- s = InstallSymlinkData(to, alias, first_outdir, t.subproject, tag, allow_missing=True)
+ s = InstallSymlinkData(to, alias, first_outdir, t.subproject, tag)
d.symlinks.append(s)
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)):
@@ -1996,6 +1951,8 @@ class Backend:
compiler += [j]
elif isinstance(j, (build.BuildTarget, build.CustomTarget)):
compiler += j.get_outputs()
+ elif isinstance(j, programs.ExternalProgram):
+ compiler += j.get_command()
else:
raise RuntimeError(f'Type "{type(j).__name__}" is not supported in get_introspection_data. This is a bug')
@@ -2079,7 +2036,7 @@ class Backend:
compiler: 'Compiler',
sources: _ALL_SOURCES_TYPE,
output_templ: str,
- depends: T.Optional[T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]] = None,
+ depends: T.Optional[T.List[build.BuildTargetTypes]] = None,
) -> build.GeneratedList:
'''
Some backends don't support custom compilers. This is a convenience
@@ -2088,10 +2045,11 @@ class Backend:
exe = programs.ExternalProgram(compiler.get_exe())
args = compiler.get_exe_args()
commands = self.compiler_to_generator_args(target, compiler)
- generator = build.Generator(exe, args + commands.to_native(),
+ generator = build.Generator(self.environment,
+ exe, args + commands.to_native(),
[output_templ], depfile='@PLAINNAME@.d',
depends=depends)
- return generator.process_files(sources, self.interpreter)
+ return generator.process_files(sources)
def compile_target_to_generator(self, target: build.CompileTarget) -> build.GeneratedList:
all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated)
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index d7de987..086d195 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -20,16 +20,17 @@ import typing as T
from . import backends
from .. import modules
-from .. import environment, mesonlib
+from .. import mesonlib
from .. import build
from .. import mlog
from .. import compilers
+from .. import tooldetect
from ..arglist import CompilerArgs
-from ..compilers import Compiler
+from ..compilers import Compiler, is_library
from ..linkers import ArLikeLinker, RSPFileSyntax
from ..mesonlib import (
File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine,
- ProgressBar, quote_arg
+ ProgressBar, quote_arg, unique_list
)
from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path
from ..options import OptionKey
@@ -41,7 +42,6 @@ if T.TYPE_CHECKING:
from .._typing import ImmutableListProtocol
from ..build import ExtractedObjects, LibTypes
- from ..interpreter import Interpreter
from ..linkers.linkers import DynamicLinker, StaticLinker
from ..compilers.cs import CsCompiler
from ..compilers.fortran import FortranCompiler
@@ -477,10 +477,16 @@ class RustCrate:
return ret
+@dataclass
+class ImportStdInfo:
+ gen_target: NinjaBuildElement
+ gen_module_file: str
+ gen_objects: T.List[str]
+
class NinjaBackend(backends.Backend):
- def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[build.Build]):
+ super().__init__(build)
self.name = 'ninja'
self.ninja_filename = 'build.ninja'
self.fortran_deps: T.Dict[str, T.Dict[str, File]] = {}
@@ -500,11 +506,7 @@ class NinjaBackend(backends.Backend):
# - https://github.com/mesonbuild/meson/pull/9453
# - https://github.com/mesonbuild/meson/issues/9479#issuecomment-953485040
self.allow_thin_archives = PerMachine[bool](True, True)
- if self.environment:
- for for_machine in MachineChoice:
- if 'cuda' in self.environment.coredata.compilers[for_machine]:
- mlog.debug('cuda enabled globally, disabling thin archives for {}, since nvcc/nvlink cannot handle thin archives natively'.format(for_machine))
- self.allow_thin_archives[for_machine] = False
+ self.import_std: T.Optional[ImportStdInfo] = None
def create_phony_target(self, dummy_outfile: str, rulename: str, phony_infilename: str) -> NinjaBuildElement:
'''
@@ -523,7 +525,7 @@ class NinjaBackend(backends.Backend):
return NinjaBuildElement(self.all_outputs, to_name, rulename, phony_infilename)
- def detect_vs_dep_prefix(self, tempfilename):
+ def detect_vs_dep_prefix(self, tempfilename: str) -> T.TextIO:
'''VS writes its dependency in a locale dependent format.
Detect the search prefix to use.'''
# TODO don't hard-code host
@@ -573,7 +575,7 @@ class NinjaBackend(backends.Backend):
# \MyDir\include\stdio.h.
matchre = re.compile(rb"^(.*\s)([a-zA-Z]:[\\/]|[\\\/]).*stdio.h$")
- def detect_prefix(out):
+ def detect_prefix(out: bytes) -> T.TextIO:
for line in re.split(rb'\r?\n', out):
match = matchre.match(line)
if match:
@@ -595,7 +597,13 @@ class NinjaBackend(backends.Backend):
# We don't yet have a use case where we'd expect to make use of this,
# so no harm in catching and reporting something unexpected.
raise MesonBugException('We do not expect the ninja backend to be given a valid \'vslite_ctx\'')
- ninja = environment.detect_ninja_command_and_version(log=True)
+ if self.environment:
+ for for_machine in MachineChoice:
+ if 'cuda' in self.environment.coredata.compilers[for_machine]:
+ mlog.debug('cuda enabled globally, disabling thin archives for {}, since nvcc/nvlink cannot handle thin archives natively'.format(for_machine))
+ self.allow_thin_archives[for_machine] = False
+
+ ninja = tooldetect.detect_ninja_command_and_version(log=True)
if self.environment.coredata.optstore.get_value_for(OptionKey('vsenv')):
builddir = Path(self.environment.get_build_dir())
try:
@@ -656,7 +664,7 @@ class NinjaBackend(backends.Backend):
key = OptionKey('b_coverage')
if key in self.environment.coredata.optstore and\
self.environment.coredata.optstore.get_value_for('b_coverage'):
- gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe = environment.find_coverage_tools(self.environment.coredata)
+ gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe = tooldetect.find_coverage_tools(self.environment.coredata)
mlog.debug(f'Using {gcovr_exe} ({gcovr_version}), {lcov_exe} and {llvm_cov_exe} for code coverage')
if gcovr_exe or (lcov_exe and genhtml_exe):
self.add_build_comment(NinjaComment('Coverage rules'))
@@ -746,9 +754,9 @@ class NinjaBackend(backends.Backend):
if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
continue
for src in genlist.get_outputs():
- if self.environment.is_header(src):
+ if compilers.is_header(src):
header_deps.append(self.get_target_generated_dir(target, genlist, src))
- if 'vala' in target.compilers and not isinstance(target, build.Executable):
+ if target.vala_header:
vala_header = File.from_built_file(self.get_target_dir(target), target.vala_header)
header_deps.append(vala_header)
# Recurse and find generated headers
@@ -785,11 +793,11 @@ class NinjaBackend(backends.Backend):
srcs[f] = s
return srcs
- def get_target_source_can_unity(self, target, source):
+ def get_target_source_can_unity(self, target, source: FileOrString) -> bool:
if isinstance(source, File):
source = source.fname
- if self.environment.is_llvm_ir(source) or \
- self.environment.is_assembly(source):
+ if compilers.is_llvm_ir(source) or \
+ compilers.is_assembly(source):
return False
suffix = os.path.splitext(source)[1][1:].lower()
for lang in backends.LANGS_CANT_UNITY:
@@ -870,13 +878,15 @@ class NinjaBackend(backends.Backend):
}
tgt[lnk_hash] = lnk_block
- def generate_target(self, target) -> None:
+ def generate_target(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.RunTarget]) -> None:
if isinstance(target, build.BuildTarget):
os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
if isinstance(target, build.CustomTarget):
self.generate_custom_target(target)
+ return
if isinstance(target, build.RunTarget):
self.generate_run_target(target)
+ return
compiled_sources: T.List[str] = []
source2object: T.Dict[str, str] = {}
name = target.get_id()
@@ -890,14 +900,14 @@ class NinjaBackend(backends.Backend):
self.generate_shlib_aliases(target, self.get_target_dir(target))
+ # Generate rules for GeneratedLists
+ self.generate_generator_list_rules(target)
+
# If target uses a language that cannot link to C objects,
# just generate for that language and return.
if isinstance(target, build.Jar):
self.generate_jar_target(target)
return
- if target.uses_rust():
- self.generate_rust_target(target)
- return
if 'cs' in target.compilers:
self.generate_cs_target(target)
return
@@ -921,7 +931,7 @@ class NinjaBackend(backends.Backend):
# a language that is handled below, such as C or C++
transpiled_sources: T.List[str]
- if 'vala' in target.compilers:
+ if target.uses_vala():
# Sources consumed by valac are filtered out. These only contain
# C/C++ sources, objects, generated libs, and unknown sources now.
target_sources, generated_sources, \
@@ -934,8 +944,6 @@ class NinjaBackend(backends.Backend):
generated_sources = self.get_target_generated_sources(target)
transpiled_sources = []
self.scan_fortran_module_outputs(target)
- # Generate rules for GeneratedLists
- self.generate_generator_list_rules(target)
# Generate rules for building the remaining source files in this target
outname = self.get_target_filename(target)
@@ -964,16 +972,16 @@ class NinjaBackend(backends.Backend):
generated_source_files: T.List[File] = []
for rel_src in generated_sources.keys():
raw_src = File.from_built_relative(rel_src)
- if self.environment.is_source(rel_src):
+ if compilers.is_source(rel_src):
if is_unity and self.get_target_source_can_unity(target, rel_src):
unity_deps.append(raw_src)
abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
unity_src.append(abs_src)
else:
generated_source_files.append(raw_src)
- elif self.environment.is_object(rel_src):
+ elif compilers.is_object(rel_src):
obj_list.append(rel_src)
- elif self.environment.is_library(rel_src) or modules.is_module_library(rel_src):
+ elif compilers.is_library(rel_src) or modules.is_module_library(rel_src):
pass
elif is_compile_target:
generated_source_files.append(raw_src)
@@ -991,7 +999,9 @@ class NinjaBackend(backends.Backend):
# this target. We create the Ninja build file elements for this here
# because we need `header_deps` to be fully generated in the above loop.
for src in generated_source_files:
- if self.environment.is_llvm_ir(src):
+ if not compilers.is_separate_compile(src):
+ continue
+ if compilers.is_llvm_ir(src):
o, s = self.generate_llvm_ir_compile(target, src)
else:
o, s = self.generate_single_compile(target, src, True, order_deps=header_deps)
@@ -1039,7 +1049,7 @@ class NinjaBackend(backends.Backend):
# compile we get precise dependency info from dep files.
# This should work in all cases. If it does not, then just
# move them from orderdeps to proper deps.
- if self.environment.is_header(src):
+ if compilers.is_header(src):
header_deps.append(raw_src)
else:
transpiled_source_files.append(raw_src)
@@ -1049,21 +1059,24 @@ class NinjaBackend(backends.Backend):
# Generate compile targets for all the preexisting sources for this target
for src in target_sources.values():
- if not self.environment.is_header(src) or is_compile_target:
- if self.environment.is_llvm_ir(src):
- o, s = self.generate_llvm_ir_compile(target, src)
- obj_list.append(o)
- elif is_unity and self.get_target_source_can_unity(target, src):
- abs_src = os.path.join(self.environment.get_build_dir(),
- src.rel_to_builddir(self.build_to_src))
- unity_src.append(abs_src)
- else:
- o, s = self.generate_single_compile(target, src, False, [],
- header_deps + d_generated_deps + fortran_order_deps,
- fortran_inc_args)
- obj_list.append(o)
- compiled_sources.append(s)
- source2object[s] = o
+ if not compilers.is_separate_compile(src):
+ continue
+ if compilers.is_header(src) and not is_compile_target:
+ continue
+ if compilers.is_llvm_ir(src):
+ o, s = self.generate_llvm_ir_compile(target, src)
+ obj_list.append(o)
+ elif is_unity and self.get_target_source_can_unity(target, src):
+ abs_src = os.path.join(self.environment.get_build_dir(),
+ src.rel_to_builddir(self.build_to_src))
+ unity_src.append(abs_src)
+ else:
+ o, s = self.generate_single_compile(target, src, False, [],
+ header_deps + d_generated_deps + fortran_order_deps,
+ fortran_inc_args)
+ obj_list.append(o)
+ compiled_sources.append(s)
+ source2object[s] = o
if is_unity:
for src in self.generate_unity_files(target, unity_src):
@@ -1083,8 +1096,14 @@ class NinjaBackend(backends.Backend):
final_obj_list = self.generate_prelink(target, obj_list)
else:
final_obj_list = obj_list
- elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args)
+
self.generate_dependency_scan_target(target, compiled_sources, source2object, fortran_order_deps)
+
+ if target.uses_rust():
+ self.generate_rust_target(target, outname, final_obj_list, fortran_order_deps)
+ return
+
+ elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args)
self.add_build(elem)
#In AIX, we archive shared libraries. If the instance is a shared library, we add a command to archive the shared library
#object and create the build element.
@@ -1189,7 +1208,7 @@ class NinjaBackend(backends.Backend):
if isinstance(s, build.GeneratedList):
self.generate_genlist_for_target(s, target)
- def unwrap_dep_list(self, target):
+ def unwrap_dep_list(self, target: T.Union[build.CustomTarget, build.RunTarget]) -> T.List[str]:
deps = []
for i in target.get_dependencies():
# FIXME, should not grab element at zero but rather expand all.
@@ -1223,6 +1242,7 @@ class NinjaBackend(backends.Backend):
capture=ofilenames[0] if target.capture else None,
feed=srcs[0] if target.feed else None,
env=target.env,
+ can_use_rsp_file=target.rspable,
verbose=target.console)
if reason:
cmd_type = f' (wrapped by meson {reason})'
@@ -1242,7 +1262,7 @@ class NinjaBackend(backends.Backend):
self.add_build(elem)
self.processed_targets.add(target.get_id())
- def build_run_target_name(self, target) -> str:
+ def build_run_target_name(self, target: build.RunTarget) -> str:
if target.subproject != '':
subproject_prefix = f'{target.subproject}@@'
else:
@@ -1554,7 +1574,6 @@ class NinjaBackend(backends.Backend):
elem.add_item('ARGS', commands)
self.add_build(elem)
- self.generate_generator_list_rules(target)
self.create_target_source_introspection(target, compiler, commands, rel_srcs, generated_rel_srcs)
def determine_java_compile_args(self, target, compiler) -> T.List[str]:
@@ -1572,7 +1591,7 @@ class NinjaBackend(backends.Backend):
args += ['-sourcepath', sourcepath]
return args
- def generate_java_compile(self, srcs, target, compiler, args):
+ def generate_java_compile(self, srcs, target, compiler, args) -> str:
deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets]
generated_sources = self.get_target_generated_sources(target)
for rel_src in generated_sources.keys():
@@ -1603,25 +1622,24 @@ class NinjaBackend(backends.Backend):
description = 'Creating JAR $out'
self.add_rule(NinjaRule(rule, command, [], description))
- def determine_dep_vapis(self, target) -> T.List[str]:
+ def determine_dep_vapis(self, target: build.BuildTarget) -> T.List[str]:
"""
Peek into the sources of BuildTargets we're linking with, and if any of
them was built with Vala, assume that it also generated a .vapi file of
the same name as the BuildTarget and return the path to it relative to
the build directory.
"""
- result = OrderedSet()
+ result: OrderedSet[str] = OrderedSet()
for dep in itertools.chain(target.link_targets, target.link_whole_targets):
- if not dep.is_linkable_target():
+ if not (isinstance(dep, build.BuildTarget) and dep.is_linkable_target()):
continue
for i in dep.sources:
- if hasattr(i, 'fname'):
- i = i.fname
if i.split('.')[-1] in compilers.lang_suffixes['vala']:
- vapiname = dep.vala_vapi
- fullname = os.path.join(self.get_target_dir(dep), vapiname)
- result.add(fullname)
- break
+ if dep.vala_vapi is not None:
+ vapiname = dep.vala_vapi
+ fullname = os.path.join(self.get_target_dir(dep), vapiname)
+ result.add(fullname)
+ break
return list(result)
def split_vala_sources(self, t: build.BuildTarget) -> \
@@ -1740,31 +1758,36 @@ class NinjaBackend(backends.Backend):
# Library name
args += ['--library', target.name]
# Outputted header
- hname = os.path.join(self.get_target_dir(target), target.vala_header)
- args += ['--header', hname]
- if self.is_unity(target):
- # Without this the declarations will get duplicated in the .c
- # files and cause a build failure when all of them are
- # #include-d in one .c file.
- # https://github.com/mesonbuild/meson/issues/1969
- args += ['--use-header']
- valac_outputs.append(hname)
+ if target.vala_header is not None:
+ hname = os.path.join(self.get_target_dir(target), target.vala_header)
+ args += ['--header', hname]
+ if self.is_unity(target):
+ # Without this the declarations will get duplicated in the .c
+ # files and cause a build failure when all of them are
+ # #include-d in one .c file.
+ # https://github.com/mesonbuild/meson/issues/1969
+ args += ['--use-header']
+ valac_outputs.append(hname)
# Outputted vapi file
- vapiname = os.path.join(self.get_target_dir(target), target.vala_vapi)
- # Force valac to write the vapi and gir files in the target build dir.
- # Without this, it will write it inside c_out_dir
- args += ['--vapi', os.path.join('..', target.vala_vapi)]
- valac_outputs.append(vapiname)
+ if target.vala_vapi is not None:
+ vapiname = os.path.join(self.get_target_dir(target), target.vala_vapi)
+ # Force valac to write the vapi and gir files in the target build dir.
+ # Without this, it will write it inside c_out_dir
+ args += ['--vapi', os.path.join('..', target.vala_vapi)]
+ valac_outputs.append(vapiname)
# Install header and vapi to default locations if user requests this
if len(target.install_dir) > 1 and target.install_dir[1] is True:
target.install_dir[1] = self.environment.get_includedir()
if len(target.install_dir) > 2 and target.install_dir[2] is True:
target.install_dir[2] = os.path.join(self.environment.get_datadir(), 'vala', 'vapi')
# Generate GIR if requested
- if isinstance(target.vala_gir, str):
+ if target.vala_gir is not None:
girname = os.path.join(self.get_target_dir(target), target.vala_gir)
args += ['--gir', os.path.join('..', target.vala_gir)]
valac_outputs.append(girname)
+ shared_target = target.get('shared')
+ if isinstance(shared_target, build.SharedLibrary):
+ args += ['--shared-library', shared_target.get_filename()]
# Install GIR to default location if requested by user
if len(target.install_dir) > 3 and target.install_dir[3] is True:
target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0')
@@ -1775,7 +1798,7 @@ class NinjaBackend(backends.Backend):
gres_xml, = self.get_custom_target_sources(gensrc)
args += ['--gresources=' + gres_xml]
for source_dir in gensrc.source_dirs:
- gres_dirs += [os.path.join(self.get_target_dir(gensrc), source_dir)]
+ gres_dirs += [source_dir]
# Ensure that resources are built before vala sources
# This is required since vala code using [GtkTemplate] effectively depends on .ui files
# GResourceHeaderTarget is not suitable due to lacking depfile
@@ -1791,6 +1814,8 @@ class NinjaBackend(backends.Backend):
self.compiler_to_rule_name(valac),
all_files + dependency_vapis)
element.add_item('ARGS', args)
+ depfile = valac.depfile_for_object(os.path.join(self.get_target_dir(target), target.name))
+ element.add_item('DEPFILE', depfile)
element.add_dep(extra_dep_files)
self.add_build(element)
self.create_target_source_introspection(target, valac, args, all_files, [])
@@ -1810,8 +1835,8 @@ class NinjaBackend(backends.Backend):
args += cython.get_always_args()
args += cython.get_debug_args(self.get_target_option(target, 'debug'))
args += cython.get_optimization_args(self.get_target_option(target, 'optimization'))
- args += cython.get_option_compile_args(target, self.environment, target.subproject)
- args += cython.get_option_std_args(target, self.environment, target.subproject)
+ args += cython.get_option_compile_args(target, target.subproject)
+ args += cython.get_option_std_args(target, target.subproject)
args += self.build.get_global_args(cython, target.for_machine)
args += self.build.get_project_args(cython, target.subproject, target.for_machine)
args += target.get_extra_args('cython')
@@ -1857,9 +1882,9 @@ class NinjaBackend(backends.Backend):
generated_sources[ssrc] = mesonlib.File.from_built_file(gen.get_subdir(), ssrc)
# Following logic in L883-900 where we determine whether to add generated source
# as a header(order-only) dep to the .so compilation rule
- if not self.environment.is_source(ssrc) and \
- not self.environment.is_object(ssrc) and \
- not self.environment.is_library(ssrc) and \
+ if not compilers.is_source(ssrc) and \
+ not compilers.is_object(ssrc) and \
+ not compilers.is_library(ssrc) and \
not modules.is_module_library(ssrc):
header_deps.append(ssrc)
for source in pyx_sources:
@@ -1877,24 +1902,28 @@ class NinjaBackend(backends.Backend):
elem.add_orderdep(instr)
self.add_build(elem)
- def __generate_sources_structure(self, root: Path, structured_sources: build.StructuredSources) -> T.Tuple[T.List[str], T.Optional[str]]:
+ def __generate_sources_structure(self, root: Path, structured_sources: build.StructuredSources,
+ main_file_ext: T.Union[str, T.Tuple[str, ...]] = tuple(),
+ ) -> T.Tuple[T.List[str], T.Optional[str]]:
first_file: T.Optional[str] = None
orderdeps: T.List[str] = []
for path, files in structured_sources.sources.items():
for file in files:
if isinstance(file, File):
out = root / path / Path(file.fname).name
- orderdeps.append(str(out))
self._generate_copy_target(file, out)
- if first_file is None:
- first_file = str(out)
+ out_s = str(out)
+ orderdeps.append(out_s)
+ if first_file is None and out_s.endswith(main_file_ext):
+ first_file = out_s
else:
for f in file.get_outputs():
out = root / path / f
- orderdeps.append(str(out))
+ out_s = str(out)
+ orderdeps.append(out_s)
self._generate_copy_target(str(Path(file.subdir) / f), out)
- if first_file is None:
- first_file = str(out)
+ if first_file is None and out_s.endswith(main_file_ext):
+ first_file = out_s
return orderdeps, first_file
def _add_rust_project_entry(self, name: str, main_rust_file: str, args: CompilerArgs,
@@ -1914,7 +1943,7 @@ class NinjaBackend(backends.Backend):
crate = RustCrate(
len(self.rust_crates),
- name,
+ self._get_rust_crate_name(name),
main_rust_file,
crate_type,
target_name,
@@ -1928,11 +1957,20 @@ class NinjaBackend(backends.Backend):
self.rust_crates[name] = crate
- def _get_rust_dependency_name(self, target: build.BuildTarget, dependency: LibTypes) -> str:
- # Convert crate names with dashes to underscores by default like
- # cargo does as dashes can't be used as parts of identifiers
- # in Rust
- return target.rust_dependency_map.get(dependency.name, dependency.name).replace('-', '_')
+ @staticmethod
+ def _get_rust_crate_name(target_name: str) -> str:
+ # Rustc replaces - with _. spaces or dots are not allowed, so we replace them with underscores
+ # Also +SUFFIX is dropped, which can be used to distinguish host from build crates
+ crate_name = target_name.replace('-', '_').replace(' ', '_').replace('.', '_')
+ return crate_name.split('+', 1)[0]
+
+ @staticmethod
+ def _get_rust_dependency_name(target: build.BuildTarget, dependency: LibTypes) -> str:
+ crate_name_raw = target.rust_dependency_map.get(dependency.name, None)
+ if crate_name_raw is None:
+ dependency_crate_name = NinjaBackend._get_rust_crate_name(dependency.name)
+ crate_name_raw = target.rust_dependency_map.get(dependency_crate_name, dependency.name)
+ return NinjaBackend._get_rust_crate_name(crate_name_raw)
def generate_rust_sources(self, target: build.BuildTarget) -> T.Tuple[T.List[str], str]:
orderdeps: T.List[str] = []
@@ -1940,23 +1978,33 @@ class NinjaBackend(backends.Backend):
# Rust compiler takes only the main file as input and
# figures out what other files are needed via import
# statements and magic.
- main_rust_file = None
+ main_rust_file: T.Optional[str] = None
if target.structured_sources:
if target.structured_sources.needs_copy():
_ods, main_rust_file = self.__generate_sources_structure(Path(
- self.get_target_private_dir(target)) / 'structured', target.structured_sources)
+ self.get_target_private_dir(target)) / 'structured', target.structured_sources, '.rs')
+ if main_rust_file is None:
+ raise MesonException('Could not find a rust file to treat as the main file for ', target.name)
else:
# The only way to get here is to have only files in the "root"
# positional argument, which are all generated into the same
# directory
- g = target.structured_sources.first_file()
-
- if isinstance(g, File):
- main_rust_file = g.rel_to_builddir(self.build_to_src)
- elif isinstance(g, GeneratedList):
- main_rust_file = os.path.join(self.get_target_private_dir(target), g.get_outputs()[0])
- else:
- main_rust_file = os.path.join(g.get_subdir(), g.get_outputs()[0])
+ for g in target.structured_sources.sources['']:
+ if isinstance(g, File):
+ if g.endswith('.rs'):
+ main_rust_file = g.rel_to_builddir(self.build_to_src)
+ elif isinstance(g, GeneratedList):
+ for h in g.get_outputs():
+ if h.endswith('.rs'):
+ main_rust_file = os.path.join(self.get_target_private_dir(target), h)
+ break
+ else:
+ for h in g.get_outputs():
+ if h.endswith('.rs'):
+ main_rust_file = os.path.join(g.get_subdir(), h)
+ break
+ if main_rust_file is not None:
+ break
_ods = []
for f in target.structured_sources.as_list():
@@ -1967,9 +2015,10 @@ class NinjaBackend(backends.Backend):
for s in f.get_outputs()])
self.all_structured_sources.update(_ods)
orderdeps.extend(_ods)
+ return orderdeps, main_rust_file
for i in target.get_sources():
- if main_rust_file is None:
+ if main_rust_file is None and i.endswith('.rs'):
main_rust_file = i.rel_to_builddir(self.build_to_src)
for g in target.get_generated_sources():
for i in g.get_outputs():
@@ -1977,7 +2026,7 @@ class NinjaBackend(backends.Backend):
fname = os.path.join(self.get_target_private_dir(target), i)
else:
fname = os.path.join(g.get_subdir(), i)
- if main_rust_file is None:
+ if main_rust_file is None and fname.endswith('.rs'):
main_rust_file = fname
orderdeps.append(fname)
@@ -1992,12 +2041,12 @@ class NinjaBackend(backends.Backend):
args.extend(['--crate-type', src_crate_type])
# If we're dynamically linking, add those arguments
- if target.rust_crate_type in {'bin', 'dylib'}:
+ if target.rust_crate_type in {'bin', 'dylib', 'cdylib'}:
args.extend(rustc.get_linker_always_args())
+ args += compilers.get_base_link_args(target, rustc, self.environment)
args += self.generate_basic_compiler_args(target, rustc)
- # Rustc replaces - with _. spaces or dots are not allowed, so we replace them with underscores
- args += ['--crate-name', target.name.replace('-', '_').replace(' ', '_').replace('.', '_')]
+ args += ['--crate-name', self._get_rust_crate_name(target.name)]
if depfile:
args += rustc.get_dependency_gen_args(target_name, depfile)
args += rustc.get_output_args(target_name)
@@ -2005,48 +2054,46 @@ class NinjaBackend(backends.Backend):
args += target.get_extra_args('rust')
return args
- def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler) -> T.Tuple[T.List[str], T.List[str], T.List[RustDep], T.List[str]]:
+ def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler,
+ obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[RustDep], T.List[str]]:
deps: T.List[str] = []
project_deps: T.List[RustDep] = []
args: T.List[str] = []
- # Rustc always use non-debug Windows runtime. Inject the one selected
- # by Meson options instead.
- # https://github.com/rust-lang/rust/issues/39016
- if not isinstance(target, build.StaticLibrary):
- try:
- buildtype = self.get_target_option(target, 'buildtype')
- crt = self.get_target_option(target, 'b_vscrt')
- args += rustc.get_crt_link_args(crt, buildtype)
- except (KeyError, AttributeError):
- pass
-
- if mesonlib.version_compare(rustc.version, '>= 1.67.0'):
- verbatim = '+verbatim'
- else:
- verbatim = ''
-
- def _link_library(libname: str, static: bool, bundle: bool = False):
+ def _link_library(libname: str, static: bool, bundle: bool = False) -> None:
+ orig_libname = libname
type_ = 'static' if static else 'dylib'
modifiers = []
+ # Except with -Clink-arg, search is limited to the -L search paths
+ dir_, libname = os.path.split(libname)
+ linkdirs.add(dir_)
if not bundle and static:
modifiers.append('-bundle')
- if verbatim:
- modifiers.append(verbatim)
+ if rustc.has_verbatim():
+ modifiers.append('+verbatim')
+ else:
+ libname = rustc.lib_file_to_l_arg(libname)
+ if libname is None:
+ raise MesonException(f"rustc does not implement '-l{type_}:+verbatim'; cannot link to '{orig_libname}' due to nonstandard name")
+
if modifiers:
type_ += ':' + ','.join(modifiers)
args.append(f'-l{type_}={libname}')
- objs, od = self.flatten_object_list(target)
- for o in objs:
+ for o in obj_list:
args.append(f'-Clink-arg={o}')
deps.append(o)
- fortran_order_deps = self.get_fortran_order_deps(od)
linkdirs = mesonlib.OrderedSet()
external_deps = target.external_deps.copy()
target_deps = target.get_dependencies()
for d in target_deps:
+ # rlibs only store -l flags, not -L; help out rustc and always
+ # add the -L flag, in case it's needed to find non-bundled
+ # dependencies of an rlib. At this point we don't have
+ # information on whether this is a direct dependency (which
+ # might use -Clink-arg= below) or an indirect one, so always
+ # add to linkdirs.
linkdirs.add(d.subdir)
deps.append(self.get_dependency_filename(d))
if isinstance(d, build.StaticLibrary):
@@ -2079,8 +2126,7 @@ class NinjaBackend(backends.Backend):
link_whole = d in target.link_whole_targets
if isinstance(target, build.StaticLibrary) or (isinstance(target, build.Executable) and rustc.get_crt_static()):
static = isinstance(d, build.StaticLibrary)
- libname = os.path.basename(lib) if verbatim else d.name
- _link_library(libname, static, bundle=link_whole)
+ _link_library(lib, static, bundle=link_whole)
elif link_whole:
link_whole_args = rustc.linker.get_link_whole_for([lib])
args += [f'-Clink-arg={a}' for a in link_whole_args]
@@ -2089,22 +2135,19 @@ class NinjaBackend(backends.Backend):
for e in external_deps:
for a in e.get_link_args():
- if a in rustc.native_static_libs:
- # Exclude link args that rustc already add by default
- pass
- elif a.startswith('-L'):
+ if a.startswith('-L'):
args.append(a)
- elif a.endswith(('.dll', '.so', '.dylib', '.a', '.lib')) and isinstance(target, build.StaticLibrary):
- dir_, lib = os.path.split(a)
+ continue
+ elif is_library(a):
+ if isinstance(target, build.StaticLibrary):
+ static = a.endswith(('.a', '.lib'))
+ _link_library(a, static)
+ continue
+
+ dir_, _ = os.path.split(a)
linkdirs.add(dir_)
- if not verbatim:
- lib, ext = os.path.splitext(lib)
- if lib.startswith('lib'):
- lib = lib[3:]
- static = a.endswith(('.a', '.lib'))
- _link_library(lib, static)
- else:
- args.append(f'-Clink-arg={a}')
+
+ args.append(f'-Clink-arg={a}')
for d in linkdirs:
d = d or '.'
@@ -2119,40 +2162,44 @@ class NinjaBackend(backends.Backend):
and dep.rust_crate_type == 'dylib'
for dep in target_deps)
- if target.rust_crate_type in {'dylib', 'proc-macro'} or has_rust_shared_deps:
- # add prefer-dynamic if any of the Rust libraries we link
+ if target.rust_crate_type in {'dylib', 'proc-macro'}:
+ # also add prefer-dynamic if any of the Rust libraries we link
# against are dynamic or this is a dynamic library itself,
# otherwise we'll end up with multiple implementations of libstd.
+ has_rust_shared_deps = True
+ elif self.get_target_option(target, 'rust_dynamic_std'):
+ if target.rust_crate_type == 'staticlib':
+ # staticlib crates always include a copy of the Rust libstd,
+ # therefore it is not possible to also link it dynamically.
+ # The options to avoid this (-Z staticlib-allow-rdylib-deps and
+ # -Z staticlib-prefer-dynamic) are not yet stable; alternatively,
+ # one could use "--emit obj" (implemented in the pull request at
+ # https://github.com/mesonbuild/meson/pull/11213) or "--emit rlib"
+ # (officially not recommended for linking with C programs).
+ raise MesonException('rust_dynamic_std does not support staticlib crates yet')
+ # want libstd as a shared dep
+ has_rust_shared_deps = True
+
+ if has_rust_shared_deps:
args += ['-C', 'prefer-dynamic']
-
- if isinstance(target, build.SharedLibrary) or has_shared_deps:
+ if has_shared_deps or has_rust_shared_deps:
args += self.get_build_rpath_args(target, rustc)
- return deps, fortran_order_deps, project_deps, args
-
- def generate_rust_target(self, target: build.BuildTarget) -> None:
- rustc = T.cast('RustCompiler', target.compilers['rust'])
- self.generate_generator_list_rules(target)
-
- for i in target.get_sources():
- if not rustc.can_compile(i):
- raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')
- for g in target.get_generated_sources():
- for i in g.get_outputs():
- if not rustc.can_compile(i):
- raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')
+ return deps, project_deps, args
+ def generate_rust_target(self, target: build.BuildTarget, target_name: str, obj_list: T.List[str],
+ fortran_order_deps: T.List[str]) -> None:
orderdeps, main_rust_file = self.generate_rust_sources(target)
- target_name = self.get_target_filename(target)
if main_rust_file is None:
raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report')
+ rustc = T.cast('RustCompiler', target.compilers['rust'])
args = rustc.compiler_args()
depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d')
args += self.get_rust_compiler_args(target, rustc, target.rust_crate_type, depfile)
- deps, fortran_order_deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc)
+ deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc, obj_list)
args += deps_args
proc_macro_dylib_path = None
@@ -2184,10 +2231,14 @@ class NinjaBackend(backends.Backend):
if target.doctests:
assert target.doctests.target is not None
- rustdoc = rustc.get_rustdoc(self.environment)
+ rustdoc = rustc.get_rustdoc()
args = rustdoc.get_exe_args()
args += self.get_rust_compiler_args(target.doctests.target, rustdoc, target.rust_crate_type)
- _, _, _, deps_args = self.get_rust_compiler_deps_and_args(target.doctests.target, rustdoc)
+ o, _ = self.flatten_object_list(target.doctests.target)
+ obj_list = unique_list(obj_list + o)
+ # Rustc does not add files in the obj_list to Rust rlibs,
+ # and is added by Meson to all of the dependencies, including here.
+ _, _, deps_args = self.get_rust_compiler_deps_and_args(target.doctests.target, rustdoc, obj_list)
args += deps_args
target.doctests.cmd_args = args.to_native() + [main_rust_file] + target.doctests.cmd_args
@@ -2207,21 +2258,18 @@ class NinjaBackend(backends.Backend):
def compiler_to_pch_rule_name(cls, compiler: Compiler) -> str:
return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine, 'PCH')
- def swift_module_file_name(self, target):
+ def swift_module_file_name(self, target) -> str:
return os.path.join(self.get_target_private_dir(target),
- self.target_swift_modulename(target) + '.swiftmodule')
-
- def target_swift_modulename(self, target):
- return target.name
+ target.swift_module_name + '.swiftmodule')
- def determine_swift_dep_modules(self, target):
+ def determine_swift_dep_modules(self, target) -> T.List[str]:
result = []
for l in target.link_targets:
if self.is_swift_target(l):
result.append(self.swift_module_file_name(l))
return result
- def get_swift_link_deps(self, target):
+ def get_swift_link_deps(self, target) -> T.List[str]:
result = []
for l in target.link_targets:
result.append(self.get_target_filename(l))
@@ -2239,19 +2287,33 @@ class NinjaBackend(backends.Backend):
return srcs, others
def generate_swift_target(self, target) -> None:
- module_name = self.target_swift_modulename(target)
+ module_name = target.swift_module_name
swiftc = target.compilers['swift']
abssrc = []
relsrc = []
abs_headers = []
header_imports = []
+
+ if not target.uses_swift_cpp_interop():
+ cpp_targets = [t for t in target.link_targets if t.uses_swift_cpp_interop()]
+ if cpp_targets != []:
+ target_word = 'targets' if len(cpp_targets) > 1 else 'target'
+ first = ', '.join(repr(t.name) for t in cpp_targets[:-1])
+ and_word = ' and ' if len(cpp_targets) > 1 else ''
+ last = repr(cpp_targets[-1].name)
+ enable_word = 'enable' if len(cpp_targets) > 1 else 'enables'
+ raise MesonException('Swift target {0} links against {1} {2}{3}{4} which {5} C++ interoperability. '
+ 'This requires {0} to also have it enabled. '
+ 'Add "swift_interoperability_mode: \'cpp\'" to the definition of {0}.'
+ .format(repr(target.name), target_word, first, and_word, last, enable_word))
+
for i in target.get_sources():
if swiftc.can_compile(i):
rels = i.rel_to_builddir(self.build_to_src)
abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), rels))
relsrc.append(rels)
abssrc.append(abss)
- elif self.environment.is_header(i):
+ elif compilers.is_header(i):
relh = i.rel_to_builddir(self.build_to_src)
absh = os.path.normpath(os.path.join(self.environment.get_build_dir(), relh))
abs_headers.append(absh)
@@ -2261,6 +2323,16 @@ class NinjaBackend(backends.Backend):
os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
compile_args = self.generate_basic_compiler_args(target, swiftc)
compile_args += swiftc.get_module_args(module_name)
+ compile_args += swiftc.get_cxx_interoperability_args(target)
+ compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine)
+ compile_args += self.build.get_global_args(swiftc, target.for_machine)
+ if isinstance(target, (build.StaticLibrary, build.SharedLibrary)):
+ # swiftc treats modules with a single source file, and the main.swift file in multi-source file modules
+ # as top-level code. This is undesirable in library targets since it emits a main function. Add the
+ # -parse-as-library option as necessary to prevent emitting the main function while keeping files explicitly
+ # named main.swift treated as the entrypoint of the module in case this is desired.
+ if len(abssrc) == 1 and os.path.basename(abssrc[0]) != 'main.swift':
+ compile_args += swiftc.get_library_args()
for i in reversed(target.get_include_dirs()):
basedir = i.get_curdir()
for d in i.get_incdirs():
@@ -2419,6 +2491,13 @@ class NinjaBackend(backends.Backend):
args = []
options = {}
self.add_rule(NinjaRule(rule, cmdlist, args, description, **options, extra=None))
+ if self.environment.machines[for_machine].is_os2() and complist:
+ rule = 'IMPORTLIB{}'.format(self.get_rule_suffix(for_machine))
+ description = 'Generating import library $out'
+ command = ['emximp']
+ args = ['-o', '$out', '$in']
+ options = {}
+ self.add_rule(NinjaRule(rule, command, args, description, **options, extra=None))
args = self.environment.get_build_command() + \
['--internal',
@@ -2450,9 +2529,19 @@ class NinjaBackend(backends.Backend):
def generate_vala_compile_rules(self, compiler) -> None:
rule = self.compiler_to_rule_name(compiler)
- command = compiler.get_exelist() + ['$ARGS', '$in']
+ command = compiler.get_exelist()
description = 'Compiling Vala source $in'
- self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1'))
+
+ depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
+ depfile = '$DEPFILE' if depargs else None
+ depstyle = 'gcc' if depargs else None
+
+ args = depargs + ['$ARGS', '$in']
+
+ self.add_rule(NinjaRule(rule, command + args, [], description,
+ depfile=depfile,
+ deps=depstyle,
+ extra='restat = 1'))
def generate_cython_compile_rules(self, compiler: 'Compiler') -> None:
rule = self.compiler_to_rule_name(compiler)
@@ -2544,7 +2633,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
command = compiler.get_exelist()
args = ['$ARGS'] + depargs + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + ['-cm', '$in']
description = 'Compiling to C object $in'
- if compiler.get_argument_syntax() == 'msvc':
+ if compiler.get_depfile_format() == 'msvc':
deps = 'msvc'
depfile = None
else:
@@ -2602,7 +2691,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
command = compiler.get_exelist()
args = ['$ARGS'] + depargs + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + compiler.get_compile_only_args() + ['$in']
description = f'Compiling {compiler.get_display_language()} object $out'
- if compiler.get_argument_syntax() == 'msvc':
+ if compiler.get_depfile_format() == 'msvc':
deps = 'msvc'
depfile = None
else:
@@ -2628,7 +2717,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
else:
command = compiler.get_exelist() + ['$ARGS'] + depargs + output + compiler.get_compile_only_args() + ['$in']
description = 'Precompiling header $in'
- if compiler.get_argument_syntax() == 'msvc':
+ if compiler.get_depfile_format() == 'msvc':
deps = 'msvc'
depfile = None
else:
@@ -2680,7 +2769,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
continue
self.generate_genlist_for_target(genlist, target)
- def replace_paths(self, target, args, override_subdir=None):
+ def replace_paths(self, target, args: T.List[str], override_subdir=None) -> T.List[str]:
if override_subdir:
source_target_dir = os.path.join(self.build_to_src, override_subdir)
else:
@@ -2822,12 +2911,12 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
mod_files = _scan_fortran_file_deps(src, srcdir, dirname, tdeps, compiler)
return mod_files
- def get_no_stdlib_link_args(self, target, linker):
+ def get_no_stdlib_link_args(self, target, linker) -> T.List[str]:
if hasattr(linker, 'language') and linker.language in self.build.stdlibs[target.for_machine]:
return linker.get_no_stdlib_link_args()
return []
- def get_compile_debugfile_args(self, compiler, target, objfile):
+ def get_compile_debugfile_args(self, compiler, target, objfile) -> T.List[str]:
# The way MSVC uses PDB files is documented exactly nowhere so
# the following is what we have been able to decipher via
# reverse engineering.
@@ -2896,7 +2985,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return linker.get_link_debugfile_args(filename)
return []
- def generate_llvm_ir_compile(self, target, src: FileOrString):
+ def generate_llvm_ir_compile(self, target, src: FileOrString) -> T.Tuple[str, str]:
compiler = get_compiler_for_source(target.compilers.values(), src)
commands = compiler.compiler_args()
# Compiler args for compiling this target
@@ -3110,11 +3199,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# Add MSVC debug file generation compile flags: /Fd /FS
commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
- # PCH handling
- if self.target_uses_pch(target):
- pchlist = target.get_pch(compiler.language)
+ # PCH handling. We only support PCH for C and C++
+ if compiler.language in {'c', 'cpp'} and target.has_pch() and self.target_uses_pch(target):
+ pchlist = target.pch[compiler.language]
else:
- pchlist = []
+ pchlist = None
if not pchlist:
pch_dep = []
elif compiler.id == 'intel':
@@ -3127,9 +3216,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# If TASKING compiler family is used and MIL linking is enabled for the target,
# then compilation rule name is a special one to output MIL files
# instead of object files for .c files
- key = OptionKey('b_lto')
if compiler.get_id() == 'tasking':
- if ((isinstance(target, build.StaticLibrary) and target.prelink) or target.get_option(key)) and src.rsplit('.', 1)[1] in compilers.lang_suffixes['c']:
+ target_lto = self.get_target_option(target, OptionKey('b_lto', machine=target.for_machine, subproject=target.subproject))
+ if ((isinstance(target, build.StaticLibrary) and target.prelink) or target_lto) and src.rsplit('.', 1)[1] in compilers.lang_suffixes['c']:
compiler_name = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine)
else:
compiler_name = self.compiler_to_rule_name(compiler)
@@ -3159,6 +3248,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
rel_obj)
self.add_build(depelem)
commands += compiler.get_module_outdir_args(self.get_target_private_dir(target))
+
+ # C++ import std is complicated enough to get its own method.
+ istd_args, istd_dep = self.handle_cpp_import_std(target, compiler)
+ commands.extend(istd_args)
+ header_deps += istd_dep
if extra_args is not None:
commands.extend(extra_args)
@@ -3208,6 +3302,58 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
assert isinstance(rel_src, str)
return (rel_obj, rel_src.replace('\\', '/'))
+ def target_uses_import_std(self, target: build.BuildTarget) -> bool:
+ if 'cpp' not in target.compilers:
+ return False
+ if 'cpp_importstd' not in self.environment.coredata.optstore:
+ return False
+ if self.environment.coredata.get_option_for_target(target, 'cpp_importstd') == 'false':
+ return False
+ return True
+
+ def handle_cpp_import_std(self, target: build.BuildTarget, compiler):
+ istd_args = []
+ istd_dep = []
+ if not self.target_uses_import_std(target):
+ return istd_args, istd_dep
+ mlog.warning('Import std support is experimental and might break compatibility in the future.')
+ # At the time of writing, all three major compilers work
+ # wildly differently. Keep this isolated here until things
+ # consolidate.
+ if compiler.id == 'gcc':
+ if self.import_std is None:
+ mod_file = 'gcm.cache/std.gcm'
+ mod_obj_file = 'std.o'
+ elem = NinjaBuildElement(self.all_outputs, [mod_file, mod_obj_file], 'CUSTOM_COMMAND', [])
+ compile_args = compiler.get_option_compile_args(target, self.environment)
+ compile_args += compiler.get_option_std_args(target, self.environment)
+ compile_args += ['-c', '-fmodules', '-fsearch-include-path', 'bits/std.cc']
+ elem.add_item('COMMAND', compiler.exelist + compile_args)
+ self.add_build(elem)
+ self.import_std = ImportStdInfo(elem, mod_file, [mod_obj_file])
+ istd_args = ['-fmodules']
+ istd_dep = [File(True, '', self.import_std.gen_module_file)]
+ return istd_args, istd_dep
+ elif compiler.id == 'msvc':
+ if self.import_std is None:
+ mod_file = 'std.ifc'
+ mod_obj_file = 'std.obj'
+ in_file = Path(os.environ['VCToolsInstallDir']) / 'modules/std.ixx'
+ if not in_file.is_file():
+ raise SystemExit('VS std import header could not be located.')
+ in_file_str = str(in_file)
+ elem = NinjaBuildElement(self.all_outputs, [mod_file, mod_obj_file], 'CUSTOM_COMMAND', [in_file_str])
+ compile_args = compiler.get_option_compile_args(target, self.environment)
+ compile_args += compiler.get_option_std_args(target, self.environment)
+ compile_args += ['/nologo', '/c', '/O2', in_file_str]
+ elem.add_item('COMMAND', compiler.exelist + compile_args)
+ self.add_build(elem)
+ self.import_std = ImportStdInfo(elem, mod_file, [mod_obj_file])
+ istd_dep = [File(True, '', self.import_std.gen_module_file)]
+ return istd_args, istd_dep
+ else:
+ raise MesonException(f'Import std not supported on compiler {compiler.id} yet.')
+
def add_dependency_scanner_entries_to_element(self, target: build.BuildTarget, compiler, element, src) -> None:
if not self.should_use_dyndeps_for_target(target):
return
@@ -3226,7 +3372,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
priv = self.get_target_private_dir(target)
return os.path.join(priv, 'depscan.json'), os.path.join(priv, 'depscan.dd')
- def add_header_deps(self, target, ninja_element, header_deps):
+ def add_header_deps(self, target, ninja_element, header_deps) -> None:
for d in header_deps:
if isinstance(d, File):
d = d.rel_to_builddir(self.build_to_src)
@@ -3258,7 +3404,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
for lt in itertools.chain(target.link_targets, target.link_whole_targets)
]
- def generate_msvc_pch_command(self, target, compiler, pch):
+ def generate_msvc_pch_command(self, target, compiler, pch: T.Tuple[str, T.Optional[str]]):
header = pch[0]
pchname = compiler.get_pch_name(header)
dst = os.path.join(self.get_target_private_dir(target), pchname)
@@ -3266,7 +3412,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
commands = []
commands += self.generate_basic_compiler_args(target, compiler)
- if len(pch) == 1:
+ if pch[1] is None:
# Auto generate PCH.
source = self.create_msvc_pch_implementation(target, compiler.get_language(), pch[0])
pch_header_dir = os.path.dirname(os.path.join(self.build_to_src, target.get_source_subdir(), header))
@@ -3285,7 +3431,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return commands, dep, dst, link_objects, source
- def generate_gcc_pch_command(self, target, compiler, pch):
+ def generate_gcc_pch_command(self, target, compiler, pch: str):
commands = self._generate_single_compile(target, compiler)
if pch.split('.')[-1] == 'h' and compiler.language == 'cpp':
# Explicitly compile pch headers as C++. If Clang is invoked in C++ mode, it actually warns if
@@ -3296,24 +3442,20 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
dep = dst + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [] # Gcc does not create an object file during pch generation.
- def generate_mwcc_pch_command(self, target, compiler, pch):
+ def generate_mwcc_pch_command(self, target, compiler, pch: str):
commands = self._generate_single_compile(target, compiler)
dst = os.path.join(self.get_target_private_dir(target),
os.path.basename(pch) + '.' + compiler.get_pch_suffix())
dep = os.path.splitext(dst)[0] + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [] # mwcc compilers do not create an object file during pch generation.
- def generate_pch(self, target, header_deps=None):
+ def generate_pch(self, target: build.BuildTarget, header_deps=None):
header_deps = header_deps if header_deps is not None else []
pch_objects = []
for lang in ['c', 'cpp']:
- pch = target.get_pch(lang)
+ pch = target.pch[lang]
if not pch:
continue
- if not has_path_sep(pch[0]) or not has_path_sep(pch[-1]):
- msg = f'Precompiled header of {target.get_basename()!r} must not be in the same ' \
- 'directory as source, please put it in a subdirectory.'
- raise InvalidArguments(msg)
compiler: Compiler = target.compilers[lang]
if compiler.get_argument_syntax() == 'msvc':
(commands, dep, dst, objs, src) = self.generate_msvc_pch_command(target, compiler, pch)
@@ -3341,13 +3483,18 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.all_pch[compiler.id].update(objs + [dst])
return pch_objects
- def get_target_shsym_filename(self, target):
+ def get_target_shsym_filename(self, target) -> str:
# Always name the .symbols file after the primary build output because it always exists
targetdir = self.get_target_private_dir(target)
return os.path.join(targetdir, target.get_filename() + '.symbols')
def generate_shsym(self, target) -> None:
- target_file = self.get_target_filename(target)
+ # On OS/2, an import library is generated after linking a DLL, so
+ # if a DLL is used as a target, import library is not generated.
+ if self.environment.machines[target.for_machine].is_os2():
+ target_file = self.get_target_filename_for_linking(target)
+ else:
+ target_file = self.get_target_filename(target)
if isinstance(target, build.SharedLibrary) and target.aix_so_archive:
if self.environment.machines[target.for_machine].is_aix():
linker, stdlib_args = target.get_clink_dynamic_linker_and_stdlibs()
@@ -3365,20 +3512,20 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
def get_import_filename(self, target) -> str:
return os.path.join(self.get_target_dir(target), target.import_filename)
- def get_target_type_link_args(self, target, linker):
- commands = []
+ def get_target_type_link_args(self, target: build.BuildTarget, linker: Compiler):
+ commands: T.List[str] = []
if isinstance(target, build.Executable):
# Currently only used with the Swift compiler to add '-emit-executable'
commands += linker.get_std_exe_link_args()
# If export_dynamic, add the appropriate linker arguments
if target.export_dynamic:
- commands += linker.gen_export_dynamic_link_args(self.environment)
+ commands += linker.gen_export_dynamic_link_args()
# If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio)
if target.import_filename:
commands += linker.gen_import_library_args(self.get_import_filename(target))
if target.pie:
commands += linker.get_pie_link_args()
- if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
+ if target.vs_module_defs:
commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
elif isinstance(target, build.SharedLibrary):
if isinstance(target, build.SharedModule):
@@ -3390,10 +3537,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
if not isinstance(target, build.SharedModule) or target.force_soname:
# Add -Wl,-soname arguments on Linux, -install_name on OS X
commands += linker.get_soname_args(
- self.environment, target.prefix, target.name, target.suffix,
+ target.prefix, target.name, target.suffix,
target.soversion, target.darwin_versions)
- # This is only visited when building for Windows using either GCC or Visual Studio
- if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
+ if target.vs_module_defs:
commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
# This is only visited when building for Windows using either GCC or Visual Studio
if target.import_filename:
@@ -3491,15 +3637,15 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
mlog.warning("Generated linker command has '-l' argument without following library name")
break
libs.add(lib)
- elif os.path.isabs(item) and self.environment.is_library(item) and os.path.isfile(item):
+ elif os.path.isabs(item) and compilers.is_library(item) and os.path.isfile(item):
absolute_libs.append(item)
guessed_dependencies = []
# TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker
try:
- static_patterns = linker.get_library_naming(self.environment, LibType.STATIC, strict=True)
- shared_patterns = linker.get_library_naming(self.environment, LibType.SHARED, strict=True)
- search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
+ static_patterns = linker.get_library_naming(LibType.STATIC, strict=True)
+ shared_patterns = linker.get_library_naming(LibType.SHARED, strict=True)
+ search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs())
for libname in libs:
# be conservative and record most likely shared and static resolution, because we don't know exactly
# which one the linker will prefer
@@ -3517,7 +3663,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return guessed_dependencies + absolute_libs
- def generate_prelink(self, target, obj_list):
+ def generate_prelink(self, target, obj_list) -> T.List[str]:
assert isinstance(target, build.StaticLibrary)
prelink_name = os.path.join(self.get_target_private_dir(target), target.name + '-prelink.o')
elem = NinjaBuildElement(self.all_outputs, [prelink_name], 'CUSTOM_COMMAND', obj_list)
@@ -3548,12 +3694,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
else:
target_slashname_workaround_dir = self.get_target_dir(target)
(rpath_args, target.rpath_dirs_to_remove) = (
- linker.build_rpath_args(self.environment,
- self.environment.get_build_dir(),
+ linker.build_rpath_args(self.environment.get_build_dir(),
target_slashname_workaround_dir,
- self.determine_rpath_dirs(target),
- target.build_rpath,
- target.install_rpath))
+ target))
return rpath_args
def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None):
@@ -3566,6 +3709,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
linker_base = linker.get_language() # Fixme.
if isinstance(target, build.SharedLibrary):
self.generate_shsym(target)
+ if self.environment.machines[target.for_machine].is_os2():
+ target_file = self.get_target_filename(target)
+ import_name = self.get_import_filename(target)
+ elem = NinjaBuildElement(self.all_outputs, import_name, 'IMPORTLIB', target_file)
+ self.add_build(elem)
crstr = self.get_rule_suffix(target.for_machine)
linker_rule = linker_base + '_LINKER' + crstr
# Create an empty commands list, and start adding link arguments from
@@ -3581,11 +3729,12 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# options passed on the command-line, in default_options, etc.
# These have the lowest priority.
if isinstance(target, build.StaticLibrary):
- commands += linker.get_base_link_args(target, linker, self.environment)
+ base_link_args = linker.get_base_link_args(target, linker, self.environment)
else:
- commands += compilers.get_base_link_args(target,
- linker,
- self.environment)
+ base_link_args = compilers.get_base_link_args(target,
+ linker,
+ self.environment)
+ commands += self.transform_link_args(target, base_link_args)
# Add -nostdlib if needed; can't be overridden
commands += self.get_no_stdlib_link_args(target, linker)
# Add things like /NOLOGO; usually can't be overridden
@@ -3672,11 +3821,13 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
#
# We shouldn't check whether we are making a static library, because
# in the LTO case we do use a real compiler here.
- commands += linker.get_option_link_args(target, self.environment)
+ commands += linker.get_option_link_args(target)
dep_targets = []
dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal))
+ obj_list += self.get_import_std_object(target)
+
# Add libraries generated by custom targets
custom_target_libraries = self.get_custom_target_provided_libraries(target)
commands += extra_args
@@ -3688,7 +3839,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs)
elem.add_dep(dep_targets + custom_target_libraries)
if linker.get_id() == 'tasking':
- if len([x for x in dep_targets + custom_target_libraries if x.endswith('.ma')]) > 0 and not target.get_option(OptionKey('b_lto')):
+ if len([x for x in dep_targets + custom_target_libraries if x.endswith('.ma')]) > 0 and not self.get_target_option(target, OptionKey('b_lto', target.subproject, target.for_machine)):
raise MesonException(f'Tried to link the target named \'{target.name}\' with a MIL archive without LTO enabled! This causes the compiler to ignore the archive.')
# Compiler args must be included in TI C28x linker commands.
@@ -3705,9 +3856,17 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.create_target_linker_introspection(target, linker, commands)
return elem
- def get_dependency_filename(self, t):
+ def get_import_std_object(self, target: build.BuildTarget) -> T.List[File]:
+ if not self.target_uses_import_std(target):
+ return []
+ return self.import_std.gen_objects
+
+ def get_dependency_filename(self, t) -> str:
if isinstance(t, build.SharedLibrary):
- return self.get_target_shsym_filename(t)
+ if t.uses_rust() and t.rust_crate_type == 'proc-macro':
+ return self.get_target_filename(t)
+ else:
+ return self.get_target_shsym_filename(t)
elif isinstance(t, mesonlib.File):
if t.is_built:
return t.relative_name()
@@ -3756,9 +3915,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
gcda_elem.add_item('description', 'Deleting gcda files')
self.add_build(gcda_elem)
- def get_user_option_args(self, shut_up_pylint: bool = True) -> T.List[str]:
- if shut_up_pylint:
- return []
+ def get_user_option_args(self) -> T.List[str]:
cmds = []
for k, v in self.environment.coredata.optstore.items():
if self.environment.coredata.optstore.is_project_option(k):
@@ -3806,7 +3963,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.add_build(elem)
def generate_scanbuild(self) -> None:
- if not environment.detect_scanbuild():
+ if not tooldetect.detect_scanbuild():
return
if 'scan-build' in self.all_outputs:
return
@@ -3824,8 +3981,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
if extra_arg:
target_name += f'-{extra_arg}'
extra_args.append(f'--{extra_arg}')
- colorout = self.environment.coredata.optstore.get_value('b_colorout') \
+ colorout = self.environment.coredata.optstore.get_value_for('b_colorout') \
if OptionKey('b_colorout') in self.environment.coredata.optstore else 'always'
+ assert isinstance(colorout, str), 'for mypy'
extra_args.extend(['--color', colorout])
if not os.path.exists(os.path.join(self.environment.source_dir, '.clang-' + name)) and \
not os.path.exists(os.path.join(self.environment.source_dir, '_clang-' + name)):
@@ -3846,16 +4004,16 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.add_build(elem)
def generate_clangformat(self) -> None:
- if not environment.detect_clangformat():
+ if not tooldetect.detect_clangformat():
return
self.generate_clangtool('format')
self.generate_clangtool('format', 'check')
def generate_clangtidy(self) -> None:
- if not environment.detect_clangtidy():
+ if not tooldetect.detect_clangtidy():
return
self.generate_clangtool('tidy', need_pch=True)
- if not environment.detect_clangapply():
+ if not tooldetect.detect_clangapply():
return
self.generate_clangtool('tidy', 'fix', need_pch=True)
@@ -3890,11 +4048,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
def generate_ending(self) -> None:
for targ, deps in [
- ('all', self.get_build_by_default_targets()),
+ ('all', self.get_build_by_default_targets().values()),
('meson-test-prereq', self.get_testlike_targets()),
('meson-benchmark-prereq', self.get_testlike_targets(True))]:
targetlist = []
- for t in deps.values():
+ for t in deps:
# Add the first output of each target to the 'all' target so that
# they are all built
#Add archive file if shared library in AIX for build all.
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 283f9f0..cc7ce1a 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -28,11 +28,10 @@ from .. import coredata
if T.TYPE_CHECKING:
from ..arglist import CompilerArgs
- from ..interpreter import Interpreter
Project = T.Tuple[str, Path, str, MachineChoice]
-def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]) -> backends.Backend:
+def autodetect_vs_version(build: T.Optional[build.Build]) -> backends.Backend:
vs_version = os.getenv('VisualStudioVersion', None)
vs_install_dir = os.getenv('VSINSTALLDIR', None)
if not vs_install_dir:
@@ -42,27 +41,31 @@ def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optiona
# vcvarsall.bat doesn't set it, so also use VSINSTALLDIR
if vs_version == '11.0' or 'Visual Studio 11' in vs_install_dir:
from mesonbuild.backend.vs2012backend import Vs2012Backend
- return Vs2012Backend(build, interpreter)
+ return Vs2012Backend(build)
if vs_version == '12.0' or 'Visual Studio 12' in vs_install_dir:
from mesonbuild.backend.vs2013backend import Vs2013Backend
- return Vs2013Backend(build, interpreter)
+ return Vs2013Backend(build)
if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir:
from mesonbuild.backend.vs2015backend import Vs2015Backend
- return Vs2015Backend(build, interpreter)
+ return Vs2015Backend(build)
if vs_version == '15.0' or 'Visual Studio 17' in vs_install_dir or \
'Visual Studio\\2017' in vs_install_dir:
from mesonbuild.backend.vs2017backend import Vs2017Backend
- return Vs2017Backend(build, interpreter)
+ return Vs2017Backend(build)
if vs_version == '16.0' or 'Visual Studio 19' in vs_install_dir or \
'Visual Studio\\2019' in vs_install_dir:
from mesonbuild.backend.vs2019backend import Vs2019Backend
- return Vs2019Backend(build, interpreter)
+ return Vs2019Backend(build)
if vs_version == '17.0' or 'Visual Studio 22' in vs_install_dir or \
'Visual Studio\\2022' in vs_install_dir:
from mesonbuild.backend.vs2022backend import Vs2022Backend
- return Vs2022Backend(build, interpreter)
+ return Vs2022Backend(build)
+ if vs_version == '18.0' or 'Visual Studio 26' in vs_install_dir or \
+ 'Visual Studio\\2026' in vs_install_dir:
+ from mesonbuild.backend.vs2026backend import Vs2026Backend
+ return Vs2026Backend(build)
if 'Visual Studio 10.0' in vs_install_dir:
- return Vs2010Backend(build, interpreter)
+ return Vs2010Backend(build)
raise MesonException('Could not detect Visual Studio using VisualStudioVersion: {!r} or VSINSTALLDIR: {!r}!\n'
'Please specify the exact backend to use.'.format(vs_version, vs_install_dir))
@@ -135,8 +138,8 @@ class Vs2010Backend(backends.Backend):
name = 'vs2010'
- def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter], gen_lite: bool = False):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False):
+ super().__init__(build)
self.project_file_version = '10.0.30319.1'
self.sln_file_version = '11.00'
self.sln_version_comment = '2010'
@@ -147,10 +150,13 @@ class Vs2010Backend(backends.Backend):
self.handled_target_deps = {}
self.gen_lite = gen_lite # Synonymous with generating the simpler makefile-style multi-config projects that invoke 'meson compile' builds, avoiding native MSBuild complications
+ def detect_toolset(self) -> None:
+ pass
+
def get_target_private_dir(self, target):
return os.path.join(self.get_target_dir(target), target.get_id())
- def generate_genlist_for_target(self, genlist: T.Union[build.GeneratedList, build.CustomTarget, build.CustomTargetIndex], target: build.BuildTarget, parent_node: ET.Element, generator_output_files: T.List[str], custom_target_include_dirs: T.List[str], custom_target_output_files: T.List[str]) -> None:
+ def generate_genlist_for_target(self, genlist: build.GeneratedTypes, target: build.BuildTarget, parent_node: ET.Element, generator_output_files: T.List[str], custom_target_include_dirs: T.List[str], custom_target_output_files: T.List[str]) -> None:
if isinstance(genlist, build.GeneratedList):
for x in genlist.depends:
self.generate_genlist_for_target(x, target, parent_node, [], [], [])
@@ -227,6 +233,7 @@ class Vs2010Backend(backends.Backend):
# Check for (currently) unexpected capture arg use cases -
if capture:
raise MesonBugException('We do not expect any vs backend to generate with \'capture = True\'')
+ self.detect_toolset()
host_machine = self.environment.machines.host.cpu_family
if host_machine in {'64', 'x86_64'}:
# amd64 or x86_64
@@ -326,7 +333,7 @@ class Vs2010Backend(backends.Backend):
result[o.target.get_id()] = o.target
return result.items()
- def get_target_deps(self, t: T.Dict[T.Any, T.Union[build.Target, build.CustomTargetIndex]], recursive=False):
+ def get_target_deps(self, t: T.Dict[T.Any, build.AnyTargetType], recursive=False):
all_deps: T.Dict[str, build.Target] = {}
for target in t.values():
if isinstance(target, build.CustomTargetIndex):
@@ -413,7 +420,8 @@ class Vs2010Backend(backends.Backend):
def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> None:
default_projlist = self.get_build_by_default_targets()
- default_projlist.update(self.get_testlike_targets())
+ for t in self.get_testlike_targets():
+ default_projlist[t.get_id()] = t
sln_filename_tmp = sln_filename + '~'
# Note using the utf-8 BOM requires the blank line, otherwise Visual Studio Version Selector fails.
# Without the BOM, VSVS fails if there is a blank line.
@@ -534,7 +542,7 @@ class Vs2010Backend(backends.Backend):
replace_if_different(sln_filename, sln_filename_tmp)
def generate_projects(self, vslite_ctx: dict = None) -> T.List[Project]:
- startup_project = self.environment.coredata.optstore.get_value('backend_startup_project')
+ startup_project = self.environment.coredata.optstore.get_value_for('backend_startup_project')
projlist: T.List[Project] = []
startup_idx = 0
for (i, (name, target)) in enumerate(self.build.targets.items()):
@@ -566,16 +574,16 @@ class Vs2010Backend(backends.Backend):
objects = []
languages = []
for i in srclist:
- if self.environment.is_header(i):
+ if compilers.is_header(i):
headers.append(i)
- elif self.environment.is_object(i):
+ elif compilers.is_object(i):
objects.append(i)
- elif self.environment.is_source(i):
+ elif compilers.is_source(i):
sources.append(i)
lang = self.lang_from_source_file(i)
if lang not in languages:
languages.append(lang)
- elif self.environment.is_library(i):
+ elif compilers.is_library(i):
pass
else:
# Everything that is not an object or source file is considered a header.
@@ -619,7 +627,8 @@ class Vs2010Backend(backends.Backend):
conftype='Utility',
target_ext=None,
target_platform=None,
- gen_manifest=True) -> T.Tuple[ET.Element, ET.Element]:
+ gen_manifest=True,
+ masm_type: T.Optional[T.Literal['masm', 'marmasm']] = None) -> T.Tuple[ET.Element, ET.Element]:
root = ET.Element('Project', {'DefaultTargets': "Build",
'ToolsVersion': '4.0',
'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'})
@@ -657,6 +666,13 @@ class Vs2010Backend(backends.Backend):
# "The build tools for v142 (Platform Toolset = 'v142') cannot be found. ... please install v142 build tools."
# This is extremely unhelpful and misleading since the v14x build tools ARE installed.
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props')
+ ext_settings_grp = ET.SubElement(root, 'ImportGroup', Label='ExtensionSettings')
+ if masm_type:
+ ET.SubElement(
+ ext_settings_grp,
+ 'Import',
+ Project=rf'$(VCTargetsPath)\BuildCustomizations\{masm_type}.props',
+ )
# This attribute makes sure project names are displayed as expected in solution files even when their project file names differ
pname = ET.SubElement(globalgroup, 'ProjectName')
@@ -692,9 +708,11 @@ class Vs2010Backend(backends.Backend):
if target_ext:
ET.SubElement(direlem, 'TargetExt').text = target_ext
- ET.SubElement(direlem, 'EmbedManifest').text = 'false'
- if not gen_manifest:
- ET.SubElement(direlem, 'GenerateManifest').text = 'false'
+ # Fix weird mt.exe error:
+ # mt.exe is trying to compile a non-existent .generated.manifest file and link it
+ # with the target. This does not happen without masm props.
+ ET.SubElement(direlem, 'EmbedManifest').text = 'true' if masm_type or gen_manifest == 'embed' else 'false'
+ ET.SubElement(direlem, 'GenerateManifest').text = 'true' if gen_manifest else 'false'
return (root, type_config)
@@ -775,12 +793,19 @@ class Vs2010Backend(backends.Backend):
platform = self.build_platform
else:
platform = self.platform
+
+ masm = self.get_masm_type(target)
+
(root, type_config) = self.create_basic_project(target.name,
temp_dir=target.get_id(),
guid=guid,
target_platform=platform,
- gen_manifest=self.get_gen_manifest(target))
+ gen_manifest=self.get_gen_manifest(target),
+ masm_type=masm)
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
+ ext_tgt_grp = ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets')
+ if masm:
+ ET.SubElement(ext_tgt_grp, 'Import', Project=rf'$(VCTargetsPath)\BuildCustomizations\{masm}.targets')
target.generated = [self.compile_target_to_generator(target)]
target.sources = []
self.generate_custom_generator_commands(target, root)
@@ -795,25 +820,31 @@ class Vs2010Backend(backends.Backend):
return 'c'
if ext in compilers.cpp_suffixes:
return 'cpp'
+ if ext in compilers.lang_suffixes['masm']:
+ return 'masm'
raise MesonException(f'Could not guess language from source file {src}.')
- def add_pch(self, pch_sources, lang, inc_cl):
+ def add_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]],
+ lang, inc_cl) -> None:
if lang in pch_sources:
self.use_pch(pch_sources, lang, inc_cl)
- def create_pch(self, pch_sources, lang, inc_cl):
+ def create_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]],
+ lang, inc_cl) -> None:
pch = ET.SubElement(inc_cl, 'PrecompiledHeader')
pch.text = 'Create'
self.add_pch_files(pch_sources, lang, inc_cl)
- def use_pch(self, pch_sources, lang, inc_cl):
+ def use_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]],
+ lang, inc_cl) -> None:
pch = ET.SubElement(inc_cl, 'PrecompiledHeader')
pch.text = 'Use'
header = self.add_pch_files(pch_sources, lang, inc_cl)
pch_include = ET.SubElement(inc_cl, 'ForcedIncludeFiles')
pch_include.text = header + ';%(ForcedIncludeFiles)'
- def add_pch_files(self, pch_sources, lang, inc_cl):
+ def add_pch_files(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]],
+ lang, inc_cl) -> str:
header = os.path.basename(pch_sources[lang][0])
pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile')
# When USING PCHs, MSVC will not do the regular include
@@ -842,6 +873,8 @@ class Vs2010Backend(backends.Backend):
# FIXME add args as needed.
if entry[1:].startswith('fsanitize'):
return True
+ if entry[1:] in frozenset(['Zi', 'Z7', 'ZI']):
+ return True
return entry[1:].startswith('M')
def add_additional_options(self, lang, parent_node, file_args):
@@ -956,13 +989,13 @@ class Vs2010Backend(backends.Backend):
other.append(arg)
return lpaths, libs, other
- def _get_cl_compiler(self, target):
+ def _get_cl_compiler(self, target: build.BuildTarget):
for lang, c in target.compilers.items():
if lang in {'c', 'cpp'}:
return c
- # No source files, only objects, but we still need a compiler, so
+ # No C/C++ source files, only objects/assembly source, but we still need a compiler, so
# return a found compiler
- if len(target.objects) > 0:
+ if len(target.objects) > 0 or len(target.sources) > 0:
for lang, c in self.environment.coredata.compilers[target.for_machine].items():
if lang in {'c', 'cpp'}:
return c
@@ -999,9 +1032,9 @@ class Vs2010Backend(backends.Backend):
file_args[l] += compilers.get_base_compile_args(
target, comp, self.environment)
file_args[l] += comp.get_option_compile_args(
- target, self.environment, target.subproject)
+ target, target.subproject)
file_args[l] += comp.get_option_std_args(
- target, self.environment, target.subproject)
+ target, target.subproject)
# Add compile args added using add_project_arguments()
for l, args in self.build.projects_args[target.for_machine].get(target.subproject, {}).items():
@@ -1119,11 +1152,10 @@ class Vs2010Backend(backends.Backend):
return (target_args, file_args), (target_defines, file_defines), (target_inc_dirs, file_inc_dirs)
- @staticmethod
- def get_build_args(compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]:
+ def get_build_args(self, target: build.BuildTarget, compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]:
build_args = compiler.get_optimization_args(optimization_level)
build_args += compiler.get_debug_args(debug)
- build_args += compiler.sanitizer_compile_args(sanitize)
+ build_args += compiler.sanitizer_compile_args(target, sanitize)
return build_args
@@ -1325,11 +1357,11 @@ class Vs2010Backend(backends.Backend):
if '/fsanitize=address' in build_args:
ET.SubElement(type_config, 'EnableASAN').text = 'true'
# Debug format
- if '/ZI' in build_args:
+ if '/ZI' in build_args or '-ZI' in build_args:
ET.SubElement(clconf, 'DebugInformationFormat').text = 'EditAndContinue'
- elif '/Zi' in build_args:
+ elif '/Zi' in build_args or '-Zi' in build_args:
ET.SubElement(clconf, 'DebugInformationFormat').text = 'ProgramDatabase'
- elif '/Z7' in build_args:
+ elif '/Z7' in build_args or '-Z7' in build_args:
ET.SubElement(clconf, 'DebugInformationFormat').text = 'OldStyle'
else:
ET.SubElement(clconf, 'DebugInformationFormat').text = 'None'
@@ -1438,7 +1470,7 @@ class Vs2010Backend(backends.Backend):
# to be after all internal and external libraries so that unresolved
# symbols from those can be found here. This is needed when the
# *_winlibs that we want to link to are static mingw64 libraries.
- extra_link_args += compiler.get_option_link_args(target, self.environment, target.subproject)
+ extra_link_args += compiler.get_option_link_args(target, target.subproject)
(additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native())
# Add more libraries to be linked if needed
@@ -1457,13 +1489,13 @@ class Vs2010Backend(backends.Backend):
# Unfortunately, we can't use self.object_filename_from_source()
for gen in l.genlist:
for src in gen.get_outputs():
- if self.environment.is_source(src):
+ if compilers.is_source(src):
path = self.get_target_generated_dir(t, gen, src)
gen_src_ext = '.' + os.path.splitext(path)[1][1:]
extra_link_args.append(path[:-len(gen_src_ext)] + '.obj')
for src in l.srclist:
- if self.environment.is_source(src):
+ if compilers.is_source(src):
target_private_dir = self.relpath(self.get_target_private_dir(t),
self.get_target_dir(t))
rel_obj = self.object_filename_from_source(t, compiler, src, target_private_dir)
@@ -1493,8 +1525,9 @@ class Vs2010Backend(backends.Backend):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))
if len(extra_link_args) > 0:
- extra_link_args.append('%(AdditionalOptions)')
- ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
+ args = [self.escape_additional_option(arg) for arg in extra_link_args]
+ args.append('%(AdditionalOptions)')
+ ET.SubElement(link, "AdditionalOptions").text = ' '.join(args)
if len(additional_libpaths) > 0:
additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
@@ -1551,7 +1584,7 @@ class Vs2010Backend(backends.Backend):
# once a build/compile has generated these sources.
#
# This modifies the paths in 'gen_files' in place, as opposed to returning a new list of modified paths.
- def relocate_generated_file_paths_to_concrete_build_dir(self, gen_files: T.List[str], target: T.Union[build.Target, build.CustomTargetIndex]) -> None:
+ def relocate_generated_file_paths_to_concrete_build_dir(self, gen_files: T.List[str], target: build.AnyTargetType) -> None:
(_, build_dir_tail) = os.path.split(self.src_to_build)
meson_build_dir_for_buildtype = build_dir_tail[:-2] + coredata.get_genvs_default_buildtype_list()[0] # Get the first buildtype suffixed dir (i.e. '[builddir]_debug') from '[builddir]_vs'
# Relative path from this .vcxproj to the directory containing the set of '..._[debug/debugoptimized/release]' setup meson build dirs.
@@ -1607,6 +1640,8 @@ class Vs2010Backend(backends.Backend):
else:
platform = self.platform
+ masm = self.get_masm_type(target)
+
tfilename = os.path.splitext(target.get_filename())
(root, type_config) = self.create_basic_project(tfilename[0],
@@ -1615,7 +1650,8 @@ class Vs2010Backend(backends.Backend):
conftype=conftype,
target_ext=tfilename[1],
target_platform=platform,
- gen_manifest=self.get_gen_manifest(target))
+ gen_manifest=self.get_gen_manifest(target),
+ masm_type=masm)
generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(
target, root)
@@ -1625,7 +1661,7 @@ class Vs2010Backend(backends.Backend):
gen_hdrs += custom_hdrs
compiler = self._get_cl_compiler(target)
- build_args = Vs2010Backend.get_build_args(compiler, self.optimization, self.debug, self.sanitize)
+ build_args = self.get_build_args(target, compiler, self.optimization, self.debug, self.sanitize)
assert isinstance(target, (build.Executable, build.SharedLibrary, build.StaticLibrary, build.SharedModule)), 'for mypy'
# Prefix to use to access the build root from the vcxproj dir
@@ -1658,25 +1694,25 @@ class Vs2010Backend(backends.Backend):
else:
return False
- pch_sources = {}
+ pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]] = {}
if self.target_uses_pch(target):
for lang in ['c', 'cpp']:
- pch = target.get_pch(lang)
+ pch = target.pch[lang]
if not pch:
continue
if compiler.id == 'msvc':
- if len(pch) == 1:
+ if pch[1] is None:
# Auto generate PCH.
src = os.path.join(proj_to_build_root, self.create_msvc_pch_implementation(target, lang, pch[0]))
pch_header_dir = os.path.dirname(os.path.join(proj_to_src_dir, pch[0]))
else:
src = os.path.join(proj_to_src_dir, pch[1])
pch_header_dir = None
- pch_sources[lang] = [pch[0], src, lang, pch_header_dir]
+ pch_sources[lang] = (pch[0], src, lang, pch_header_dir)
else:
# I don't know whether its relevant but let's handle other compilers
# used with a vs backend
- pch_sources[lang] = [pch[0], None, lang, None]
+ pch_sources[lang] = (pch[0], None, lang, None)
previous_includes = []
if len(headers) + len(gen_hdrs) + len(target.extra_files) + len(pch_sources) > 0:
@@ -1719,12 +1755,17 @@ class Vs2010Backend(backends.Backend):
for s in sources:
relpath = os.path.join(proj_to_build_root, s.rel_to_builddir(self.build_to_src))
if path_normalize_add(relpath, previous_sources):
- inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
+ lang = Vs2010Backend.lang_from_source_file(s)
+ if lang == 'masm' and masm:
+ inc_cl = ET.SubElement(inc_src, masm.upper(), Include=relpath)
+ else:
+ inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
+
if self.gen_lite:
self.add_project_nmake_defs_incs_and_opts(inc_cl, relpath, defs_paths_opts_per_lang_and_buildtype, platform)
else:
- lang = Vs2010Backend.lang_from_source_file(s)
- self.add_pch(pch_sources, lang, inc_cl)
+ if lang != 'masm':
+ self.add_pch(pch_sources, lang, inc_cl)
self.add_additional_options(lang, inc_cl, file_args)
self.add_preprocessor_defines(lang, inc_cl, file_defines)
self.add_include_dirs(lang, inc_cl, file_inc_dirs)
@@ -1732,12 +1773,17 @@ class Vs2010Backend(backends.Backend):
self.object_filename_from_source(target, compiler, s)
for s in gen_src:
if path_normalize_add(s, previous_sources):
- inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s)
+ lang = Vs2010Backend.lang_from_source_file(s)
+ if lang == 'masm' and masm:
+ inc_cl = ET.SubElement(inc_src, masm.upper(), Include=s)
+ else:
+ inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s)
+
if self.gen_lite:
self.add_project_nmake_defs_incs_and_opts(inc_cl, s, defs_paths_opts_per_lang_and_buildtype, platform)
else:
- lang = Vs2010Backend.lang_from_source_file(s)
- self.add_pch(pch_sources, lang, inc_cl)
+ if lang != 'masm':
+ self.add_pch(pch_sources, lang, inc_cl)
self.add_additional_options(lang, inc_cl, file_args)
self.add_preprocessor_defines(lang, inc_cl, file_defines)
self.add_include_dirs(lang, inc_cl, file_inc_dirs)
@@ -1786,6 +1832,9 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(inc_objs, 'Object', Include=s)
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
+ ext_tgt_grp = ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets')
+ if masm:
+ ET.SubElement(ext_tgt_grp, 'Import', Project=rf'$(VCTargetsPath)\BuildCustomizations\{masm}.targets')
self.add_regen_dependency(root)
if not self.gen_lite:
# Injecting further target dependencies into this vcxproj implies and forces a Visual Studio BUILD dependency,
@@ -2096,7 +2145,8 @@ class Vs2010Backend(backends.Backend):
pass
# Returns if a target generates a manifest or not.
- def get_gen_manifest(self, target):
+ # Returns 'embed' if the generated manifest is embedded.
+ def get_gen_manifest(self, target: T.Optional[build.BuildTarget]):
if not isinstance(target, build.BuildTarget):
return True
@@ -2113,6 +2163,31 @@ class Vs2010Backend(backends.Backend):
arg = arg.upper()
if arg == '/MANIFEST:NO':
return False
+ if arg.startswith('/MANIFEST:EMBED'):
+ return 'embed'
if arg == '/MANIFEST' or arg.startswith('/MANIFEST:'):
break
return True
+
+ # FIXME: add a way to distinguish between arm64ec+marmasm (written in ARM assembly)
+ # and arm64ec+masm (written in x64 assembly).
+ #
+ # For now, assume it's the native ones. (same behavior as ninja backend)
+ def get_masm_type(self, target: build.BuildTarget):
+ if not isinstance(target, build.BuildTarget):
+ return None
+
+ if 'masm' not in target.compilers:
+ return None
+
+ if target.for_machine == MachineChoice.BUILD:
+ platform = self.build_platform
+ elif target.for_machine == MachineChoice.HOST:
+ platform = self.platform
+ else:
+ return None
+
+ if platform in {'ARM', 'arm64', 'arm64ec'}:
+ return 'marmasm'
+ else:
+ return 'masm'
diff --git a/mesonbuild/backend/vs2012backend.py b/mesonbuild/backend/vs2012backend.py
index 307964b..5ed31d9 100644
--- a/mesonbuild/backend/vs2012backend.py
+++ b/mesonbuild/backend/vs2012backend.py
@@ -10,17 +10,18 @@ from ..mesonlib import MesonException
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2012Backend(Vs2010Backend):
name = 'vs2012'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[Build]):
+ super().__init__(build)
self.vs_version = '2012'
self.sln_file_version = '12.00'
self.sln_version_comment = '2012'
+
+ def detect_toolset(self) -> None:
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2013backend.py b/mesonbuild/backend/vs2013backend.py
index ae0b68b..c53e64c 100644
--- a/mesonbuild/backend/vs2013backend.py
+++ b/mesonbuild/backend/vs2013backend.py
@@ -9,17 +9,18 @@ import typing as T
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2013Backend(Vs2010Backend):
name = 'vs2013'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[Build]):
+ super().__init__(build)
self.vs_version = '2013'
self.sln_file_version = '12.00'
self.sln_version_comment = '2013'
+
+ def detect_toolset(self) -> None:
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py
index 4c515cc..f5a468f 100644
--- a/mesonbuild/backend/vs2015backend.py
+++ b/mesonbuild/backend/vs2015backend.py
@@ -10,17 +10,17 @@ from ..mesonlib import MesonException
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2015Backend(Vs2010Backend):
name = 'vs2015'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[Build]):
self.vs_version = '2015'
self.sln_file_version = '12.00'
self.sln_version_comment = '14'
+
+ def detect_toolset(self) -> None:
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py
index 393544f..bf6c337 100644
--- a/mesonbuild/backend/vs2017backend.py
+++ b/mesonbuild/backend/vs2017backend.py
@@ -12,18 +12,19 @@ from ..mesonlib import MesonException
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2017Backend(Vs2010Backend):
name = 'vs2017'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[Build]):
+ super().__init__(build)
self.vs_version = '2017'
self.sln_file_version = '12.00'
self.sln_version_comment = '15'
+
+ def detect_toolset(self) -> None:
# We assume that host == build
if self.environment is not None:
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2019backend.py b/mesonbuild/backend/vs2019backend.py
index 4d6e226..bf86a47 100644
--- a/mesonbuild/backend/vs2019backend.py
+++ b/mesonbuild/backend/vs2019backend.py
@@ -11,17 +11,18 @@ from .vs2010backend import Vs2010Backend
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2019Backend(Vs2010Backend):
name = 'vs2019'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[Build]):
+ super().__init__(build)
self.sln_file_version = '12.00'
self.sln_version_comment = 'Version 16'
+
+ def detect_toolset(self) -> None:
if self.environment is not None:
comps = self.environment.coredata.compilers.host
if comps and all(c.id == 'clang-cl' for c in comps.values()):
diff --git a/mesonbuild/backend/vs2022backend.py b/mesonbuild/backend/vs2022backend.py
index 27e0438..d7de4be 100644
--- a/mesonbuild/backend/vs2022backend.py
+++ b/mesonbuild/backend/vs2022backend.py
@@ -11,17 +11,18 @@ from .vs2010backend import Vs2010Backend
if T.TYPE_CHECKING:
from ..build import Build
- from ..interpreter import Interpreter
class Vs2022Backend(Vs2010Backend):
name = 'vs2022'
- def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter], gen_lite: bool = False):
- super().__init__(build, interpreter, gen_lite=gen_lite)
+ def __init__(self, build: T.Optional[Build], gen_lite: bool = False):
+ super().__init__(build, gen_lite=gen_lite)
self.sln_file_version = '12.00'
self.sln_version_comment = 'Version 17'
+
+ def detect_toolset(self) -> None:
if self.environment is not None:
comps = self.environment.coredata.compilers.host
if comps and all(c.id == 'clang-cl' for c in comps.values()):
diff --git a/mesonbuild/backend/vs2026backend.py b/mesonbuild/backend/vs2026backend.py
new file mode 100644
index 0000000..ba24d8a
--- /dev/null
+++ b/mesonbuild/backend/vs2026backend.py
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2025 The Meson development team
+
+from __future__ import annotations
+
+import os
+import typing as T
+import xml.etree.ElementTree as ET
+
+from .vs2010backend import Vs2010Backend
+
+if T.TYPE_CHECKING:
+ from ..build import Build
+
+
+class Vs2026Backend(Vs2010Backend):
+
+ name = 'vs2026'
+
+ def __init__(self, build: T.Optional[Build], gen_lite: bool = False):
+ super().__init__(build, gen_lite=gen_lite)
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = 'Version 18'
+
+ def detect_toolset(self) -> None:
+ if self.environment is not None:
+ comps = self.environment.coredata.compilers.host
+ if comps and all(c.id == 'clang-cl' for c in comps.values()):
+ self.platform_toolset = 'ClangCL'
+ elif comps and all(c.id == 'intel-cl' for c in comps.values()):
+ c = list(comps.values())[0]
+ if c.version.startswith('19'):
+ self.platform_toolset = 'Intel C++ Compiler 19.0'
+ # We don't have support for versions older than 2022 right now.
+ if not self.platform_toolset:
+ self.platform_toolset = 'v145'
+ self.vs_version = '2026'
+ # WindowsSDKVersion should be set by command prompt.
+ sdk_version = os.environ.get('WindowsSDKVersion', None)
+ if sdk_version:
+ self.windows_target_platform_version = sdk_version.rstrip('\\')
+
+ def generate_debug_information(self, link):
+ # valid values for vs2026 is 'false', 'true', 'DebugFastLink', 'DebugFull'
+ ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull'
+
+ def generate_lang_standard_info(self, file_args, clconf):
+ if 'cpp' in file_args:
+ optargs = [x for x in file_args['cpp'] if x.startswith('/std:c++')]
+ if optargs:
+ ET.SubElement(clconf, 'LanguageStandard').text = optargs[0].replace("/std:c++", "stdcpp")
+ if 'c' in file_args:
+ optargs = [x for x in file_args['c'] if x.startswith('/std:c')]
+ if optargs:
+ ET.SubElement(clconf, 'LanguageStandard_C').text = optargs[0].replace("/std:c", "stdc")
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 587404a..b4f43f3 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -8,6 +8,7 @@ import typing as T
from . import backends
from .. import build
+from .. import compilers
from .. import mesonlib
from .. import mlog
from ..arglist import CompilerArgs
@@ -17,7 +18,6 @@ from ..options import OptionKey
if T.TYPE_CHECKING:
from ..build import BuildTarget
from ..compilers import Compiler
- from ..interpreter import Interpreter
INDENT = '\t'
XCODETYPEMAP = {'c': 'sourcecode.c.c',
@@ -176,6 +176,15 @@ class PbxDict:
self.keys.add(key)
self.items.append(item)
+ def get_item(self, key: str) -> PbxDictItem:
+ assert key in self.keys
+ for item in self.items:
+ if not isinstance(item, PbxDictItem):
+ continue
+ if item.key == key:
+ return item
+ return None
+
def has_item(self, key: str) -> bool:
return key in self.keys
@@ -227,8 +236,8 @@ class XCodeBackend(backends.Backend):
name = 'xcode'
- def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
- super().__init__(build, interpreter)
+ def __init__(self, build: T.Optional[build.Build]):
+ super().__init__(build)
self.project_uid = self.environment.coredata.lang_guids['default'].replace('-', '')[:24]
self.buildtype = T.cast('str', self.environment.coredata.optstore.get_value_for(OptionKey('buildtype')))
self.project_conflist = self.gen_id()
@@ -260,7 +269,7 @@ class XCodeBackend(backends.Backend):
# that is used in two targets gets a total of four unique ID numbers.
self.fileref_ids = {}
- def write_pbxfile(self, top_level_dict, ofilename) -> None:
+ def write_pbxfile(self, top_level_dict: PbxDict, ofilename: str) -> None:
tmpname = ofilename + '.tmp'
with open(tmpname, 'w', encoding='utf-8') as ofile:
ofile.write('// !$*UTF8*$!\n')
@@ -271,12 +280,12 @@ class XCodeBackend(backends.Backend):
return str(uuid.uuid4()).upper().replace('-', '')[:24]
@functools.lru_cache(maxsize=None)
- def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
+ def get_target_dir(self, target: build.AnyTargetType) -> str:
dirname = os.path.join(target.get_subdir(), T.cast('str', self.environment.coredata.optstore.get_value_for(OptionKey('buildtype'))))
#os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
return dirname
- def get_custom_target_output_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
+ def get_custom_target_output_dir(self, target: build.AnyTargetType) -> str:
dirname = target.get_subdir()
os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
return dirname
@@ -396,10 +405,23 @@ class XCodeBackend(backends.Backend):
def generate_filemap(self) -> None:
self.filemap = {} # Key is source file relative to src root.
+ self.foldermap = {}
self.target_filemap = {}
for name, t in self.build_targets.items():
for s in t.sources:
if isinstance(s, mesonlib.File):
+ if '/' in s.fname:
+ # From the top level down, add the folders containing the source file.
+ folder = os.path.split(os.path.dirname(s.fname))
+ while folder:
+ fpath = os.path.join(*folder)
+ # Multiple targets might use the same folders, so store their targets with them.
+ # Otherwise, folders and their source files will appear in the wrong places in Xcode.
+ if (fpath, t) not in self.foldermap:
+ self.foldermap[(fpath, t)] = self.gen_id()
+ else:
+ break
+ folder = folder[:-1]
s = os.path.join(s.subdir, s.fname)
self.filemap[s] = self.gen_id()
for o in t.objects:
@@ -469,7 +491,7 @@ class XCodeBackend(backends.Backend):
self.build_rules[name] = languages
def generate_custom_target_map(self) -> None:
- self.shell_targets = {}
+ self.shell_targets: T.Dict[T.Union[str, T.Tuple[str, int]], str] = {}
self.custom_target_output_buildfile = {}
self.custom_target_output_fileref = {}
for tname, t in self.custom_targets.items():
@@ -502,7 +524,8 @@ class XCodeBackend(backends.Backend):
self.gen_single_target_map(genlist, tname, t, generator_id)
generator_id += 1
- def gen_single_target_map(self, genlist, tname, t, generator_id) -> None:
+ def gen_single_target_map(self, genlist: build.GeneratedList, tname: str,
+ t: build.AnyTargetType, generator_id: int) -> None:
k = (tname, generator_id)
assert k not in self.shell_targets
self.shell_targets[k] = self.gen_id()
@@ -532,7 +555,7 @@ class XCodeBackend(backends.Backend):
self.native_frameworks_fileref[f] = self.gen_id()
def generate_target_dependency_map(self) -> None:
- self.target_dependency_map = {}
+ self.target_dependency_map: T.Dict[T.Union[str, T.Tuple[str, str]], str] = {}
for tname, t in self.build_targets.items():
for target in t.link_targets:
if isinstance(target, build.CustomTargetIndex):
@@ -606,7 +629,7 @@ class XCodeBackend(backends.Backend):
self.fileref_ids[k] = self.gen_id()
def generate_build_file_maps(self) -> None:
- for buildfile in self.interpreter.get_build_def_files():
+ for buildfile in self.build.def_files:
assert isinstance(buildfile, str)
self.buildfile_ids[buildfile] = self.gen_id()
self.fileref_ids[buildfile] = self.gen_id()
@@ -767,7 +790,7 @@ class XCodeBackend(backends.Backend):
self.create_generator_shellphase(objects_dict, tname, generator_id)
generator_id += 1
- def create_generator_shellphase(self, objects_dict, tname, generator_id) -> None:
+ def create_generator_shellphase(self, objects_dict: PbxDict, tname: str, generator_id: int) -> None:
file_ids = self.generator_buildfile_ids[(tname, generator_id)]
ref_ids = self.generator_fileref_ids[(tname, generator_id)]
assert len(ref_ids) == len(file_ids)
@@ -1002,7 +1025,7 @@ class XCodeBackend(backends.Backend):
custom_dict.add_item('sourceTree', 'SOURCE_ROOT')
objects_dict.add_item(self.custom_target_output_fileref[o], custom_dict)
- for buildfile in self.interpreter.get_build_def_files():
+ for buildfile in self.build.def_files:
basename = os.path.split(buildfile)[1]
buildfile_dict = PbxDict()
typestr = self.get_xcodetype(buildfile)
@@ -1052,6 +1075,24 @@ class XCodeBackend(backends.Backend):
main_children.add_item(frameworks_id, 'Frameworks')
main_dict.add_item('sourceTree', '<group>')
+ # Define each folder as a group in Xcode. That way, it can build the file tree correctly.
+ # This must be done before the project tree group is generated, as source files are added during that phase.
+ for (path, target), id in self.foldermap.items():
+ folder_dict = PbxDict()
+ objects_dict.add_item(id, folder_dict, path)
+ folder_dict.add_item('isa', 'PBXGroup')
+ folder_children = PbxArray()
+ folder_dict.add_item('children', folder_children)
+ folder_dict.add_item('name', '"{}"'.format(path.rsplit('/', 1)[-1]))
+ folder_dict.add_item('path', f'"{path}"')
+ folder_dict.add_item('sourceTree', 'SOURCE_ROOT')
+
+ # Add any detected subdirectories (not declared as subdir()) here, but only one level higher.
+ # Example: In "root", add "root/sub", but not "root/sub/subtwo".
+ for path_dep, target_dep in self.foldermap:
+ if path_dep.startswith(path) and path_dep.split('/', 1)[0] == path.split('/', 1)[0] and path_dep != path and path_dep.count('/') == path.count('/') + 1 and target == target_dep:
+ folder_children.add_item(self.foldermap[(path_dep, target)], path_dep)
+
self.add_projecttree(objects_dict, projecttree_id)
resource_dict = PbxDict()
@@ -1117,10 +1158,11 @@ class XCodeBackend(backends.Backend):
product_dict.add_item('name', 'Products')
product_dict.add_item('sourceTree', '<group>')
- def write_group_target_entry(self, objects_dict, t):
+ def write_group_target_entry(self, objects_dict: PbxDict, t) -> str:
tid = t.get_id()
group_id = self.gen_id()
target_dict = PbxDict()
+ folder_ids = set()
objects_dict.add_item(group_id, target_dict, tid)
target_dict.add_item('isa', 'PBXGroup')
target_children = PbxArray()
@@ -1130,6 +1172,18 @@ class XCodeBackend(backends.Backend):
source_files_dict = PbxDict()
for s in t.sources:
if isinstance(s, mesonlib.File):
+ # If the file is in a folder, add it to the group representing that folder.
+ if '/' in s.fname:
+ folder = '/'.join(s.fname.split('/')[:-1])
+ folder_dict = objects_dict.get_item(self.foldermap[(folder, t)]).value.get_item('children').value
+ temp = os.path.join(s.subdir, s.fname)
+ folder_dict.add_item(self.fileref_ids[(tid, temp)], temp)
+ if self.foldermap[(folder, t)] in folder_ids:
+ continue
+ if len(folder.split('/')) == 1:
+ target_children.add_item(self.foldermap[(folder, t)], folder)
+ folder_ids.add(self.foldermap[(folder, t)])
+ continue
s = os.path.join(s.subdir, s.fname)
elif isinstance(s, str):
s = os.path.join(t.subdir, s)
@@ -1157,7 +1211,7 @@ class XCodeBackend(backends.Backend):
source_files_dict.add_item('sourceTree', '<group>')
return group_id
- def add_projecttree(self, objects_dict, projecttree_id) -> None:
+ def add_projecttree(self, objects_dict: PbxDict, projecttree_id: str) -> None:
root_dict = PbxDict()
objects_dict.add_item(projecttree_id, root_dict, "Root of project tree")
root_dict.add_item('isa', 'PBXGroup')
@@ -1169,7 +1223,7 @@ class XCodeBackend(backends.Backend):
project_tree = self.generate_project_tree()
self.write_tree(objects_dict, project_tree, target_children, '')
- def write_tree(self, objects_dict, tree_node, children_array, current_subdir) -> None:
+ def write_tree(self, objects_dict: PbxDict, tree_node: FileTreeEntry, children_array: PbxArray, current_subdir: str) -> None:
for subdir_name, subdir_node in tree_node.subdirs.items():
subdir_dict = PbxDict()
subdir_children = PbxArray()
@@ -1385,7 +1439,8 @@ class XCodeBackend(backends.Backend):
self.generate_single_generator_phase(tname, t, genlist, generator_id, objects_dict)
generator_id += 1
- def generate_single_generator_phase(self, tname, t, genlist, generator_id, objects_dict) -> None:
+ def generate_single_generator_phase(self, tname: str, t: build.AnyTargetType,
+ genlist: build.GeneratedList, generator_id: int, objects_dict: PbxDict) -> None:
# TODO: this should be rewritten to use the meson wrapper, like the other generators do
# Currently it doesn't handle a host binary that requires an exe wrapper correctly.
generator = genlist.get_generator()
@@ -1458,7 +1513,7 @@ class XCodeBackend(backends.Backend):
phase_dict.add_item('files', file_arr)
for s in self.build_targets[name].sources:
s = os.path.join(s.subdir, s.fname)
- if not self.environment.is_header(s):
+ if not compilers.is_header(s):
file_arr.add_item(self.buildfile_ids[(name, s)], os.path.join(self.environment.get_source_dir(), s))
generator_id = 0
for gt in t.generated:
@@ -1564,7 +1619,7 @@ class XCodeBackend(backends.Backend):
settings_dict.add_item('SDKROOT', 'macosx')
bt_dict.add_item('name', buildtype)
- def determine_internal_dep_link_args(self, target, buildtype):
+ def determine_internal_dep_link_args(self, target: build.BuildTarget, buildtype: str) -> T.Tuple[T.List[str], bool]:
links_dylib = False
dep_libs = []
for l in target.link_targets:
@@ -1589,13 +1644,14 @@ class XCodeBackend(backends.Backend):
links_dylib = links_dylib or sub_links_dylib
return (dep_libs, links_dylib)
- def generate_single_build_target(self, objects_dict, target_name, target) -> None:
+ def generate_single_build_target(self, objects_dict: PbxDict, target_name: str, target: build.BuildTarget) -> None:
for buildtype in self.buildtypes:
- dep_libs = []
+ dep_libs: T.List[str] = []
links_dylib = False
headerdirs = []
bridging_header = ""
is_swift = self.is_swift_target(target)
+ langs = set()
for d in target.include_dirs:
for sd in d.incdirs:
cd = os.path.join(d.curdir, sd)
@@ -1606,7 +1662,7 @@ class XCodeBackend(backends.Backend):
# Swift can import declarations from C-based code using bridging headers.
# There can only be one header, and it must be included as a source file.
for i in target.get_sources():
- if self.environment.is_header(i) and is_swift:
+ if compilers.is_header(i) and is_swift:
relh = i.rel_to_builddir(self.build_to_src)
bridging_header = os.path.normpath(os.path.join(self.environment.get_build_dir(), relh))
break
@@ -1678,7 +1734,7 @@ class XCodeBackend(backends.Backend):
ldargs += linker.get_std_shared_lib_link_args()
ldstr = ' '.join(ldargs)
valid = self.buildconfmap[target_name][buildtype]
- langargs = {}
+ langargs: T.Dict[str, T.List[str]] = {}
for lang in self.environment.coredata.compilers[target.for_machine]:
if lang not in LANGNAMEMAP:
continue
@@ -1687,8 +1743,8 @@ class XCodeBackend(backends.Backend):
continue
# Start with warning args
warn_args = compiler.get_warn_args(self.get_target_option(target, 'warning_level'))
- std_args = compiler.get_option_compile_args(target, self.environment, target.subproject)
- std_args += compiler.get_option_std_args(target, self.environment, target.subproject)
+ std_args = compiler.get_option_compile_args(target, target.subproject)
+ std_args += compiler.get_option_std_args(target, target.subproject)
# Add compile args added using add_project_arguments()
pargs = self.build.projects_args[target.for_machine].get(target.subproject, {}).get(lang, [])
# Add compile args added using add_global_arguments()
@@ -1715,6 +1771,7 @@ class XCodeBackend(backends.Backend):
lang = 'c'
elif lang == 'objcpp':
lang = 'cpp'
+ langs.add(lang)
langname = LANGNAMEMAP[lang]
langargs.setdefault(langname, [])
langargs[langname] = cargs + cti_args + args
@@ -1745,9 +1802,7 @@ class XCodeBackend(backends.Backend):
# Xcode uses GCC_PREFIX_HEADER which only allows one file per target/executable. Precompiling various header files and
# applying a particular pch to each source file will require custom scripts (as a build phase) and build flags per each
# file. Since Xcode itself already discourages precompiled headers in favor of modules we don't try much harder here.
- pchs = target.get_pch('c') + target.get_pch('cpp') + target.get_pch('objc') + target.get_pch('objcpp')
- # Make sure to use headers (other backends require implementation files like *.c *.cpp, etc; these should not be used here)
- pchs = [pch for pch in pchs if pch.endswith('.h') or pch.endswith('.hh') or pch.endswith('hpp')]
+ pchs = [t[0] for t in [target.pch['c'], target.pch['cpp']] if t is not None]
if pchs:
if len(pchs) > 1:
mlog.warning(f'Unsupported Xcode configuration: More than 1 precompiled header found "{pchs!s}". Target "{target.name}" might not compile correctly.')
@@ -1776,6 +1831,8 @@ class XCodeBackend(backends.Backend):
settings_dict.add_item('SECTORDER_FLAGS', '')
if is_swift and bridging_header:
settings_dict.add_item('SWIFT_OBJC_BRIDGING_HEADER', bridging_header)
+ if self.objversion >= 60 and target.uses_swift_cpp_interop():
+ settings_dict.add_item('SWIFT_OBJC_INTEROP_MODE', 'objcxx')
settings_dict.add_item('BUILD_DIR', symroot)
settings_dict.add_item('OBJROOT', f'{symroot}/build')
sysheader_arr = PbxArray()
@@ -1789,7 +1846,7 @@ class XCodeBackend(backends.Backend):
warn_array.add_item('"$(inherited)"')
bt_dict.add_item('name', buildtype)
- def normalize_header_search_paths(self, header_dirs) -> PbxArray:
+ def normalize_header_search_paths(self, header_dirs: T.List[str]) -> PbxArray:
header_arr = PbxArray()
for i in header_dirs:
np = os.path.normpath(i)
@@ -1798,7 +1855,7 @@ class XCodeBackend(backends.Backend):
header_arr.add_item(item)
return header_arr
- def add_otherargs(self, settings_dict, langargs):
+ def add_otherargs(self, settings_dict: PbxDict, langargs: T.Dict[str, T.List[str]]) -> None:
for langname, args in langargs.items():
if args:
quoted_args = []
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 7320b88..906f552 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -22,23 +22,23 @@ from . import programs
from .mesonlib import (
HoldableObject, SecondLevelHolder,
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
- extract_as_list, typeslistify, stringlistify, classify_unity_sources,
+ extract_as_list, typeslistify, classify_unity_sources,
get_filenames_templates_dict, substitute_values, has_path_sep,
- is_parent_path, PerMachineDefaultable,
+ is_parent_path, relpath, PerMachineDefaultable,
MesonBugException, EnvironmentVariables, pickle_load, lazy_property,
)
from .options import OptionKey
from .compilers import (
is_header, is_object, is_source, clink_langs, sort_clink, all_languages,
- is_known_suffix, detect_static_linker
+ is_known_suffix, detect_static_linker, LANGUAGES_USING_LDFLAGS
)
from .interpreterbase import FeatureNew, FeatureDeprecated
if T.TYPE_CHECKING:
- from typing_extensions import Literal, TypedDict
+ from typing_extensions import Literal, TypeAlias, TypedDict
- from . import environment
+ from .environment import Environment
from ._typing import ImmutableListProtocol
from .backend.backends import Backend
from .compilers import Compiler
@@ -47,13 +47,14 @@ if T.TYPE_CHECKING:
from .interpreterbase import SubProject
from .linkers.linkers import StaticLinker
from .mesonlib import ExecutableSerialisation, FileMode, FileOrString
- from .modules import ModuleState
from .mparser import BaseNode
- GeneratedTypes = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList']
- LibTypes = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex']
- BuildTargetTypes = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex']
- ObjectTypes = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes']
+ GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList']
+ LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex']
+ BuildTargetTypes: TypeAlias = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex']
+ ObjectTypes: TypeAlias = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes']
+ AnyTargetType: TypeAlias = T.Union['Target', 'CustomTargetIndex']
+ RustCrateType: TypeAlias = Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro']
class DFeatures(TypedDict):
@@ -62,6 +63,89 @@ if T.TYPE_CHECKING:
import_dirs: T.List[IncludeDirs]
versions: T.List[T.Union[str, int]]
+ class BuildTargetKeywordArguments(TypedDict, total=False):
+
+ # The use of Sequence[str] here is intentional to get the generally
+ # undesirable behavior of Sequence[str]
+
+ build_by_default: bool
+ build_rpath: str
+ c_pch: T.Optional[T.Tuple[str, T.Optional[str]]]
+ cpp_pch: T.Optional[T.Tuple[str, T.Optional[str]]]
+ d_debug: T.List[T.Union[str, int]]
+ d_import_dirs: T.List[IncludeDirs]
+ d_module_versions: T.List[T.Union[str, int]]
+ d_unittest: bool
+ dependencies: T.List[dependencies.Dependency]
+ extra_files: T.List[File]
+ gnu_symbol_visibility: Literal['default', 'internal', 'hidden', 'protected', 'inlineshidden', '']
+ implicit_include_directories: bool
+ include_directories: T.List[IncludeDirs]
+ install: bool
+ install_dir: T.List[T.Union[str, Literal[False]]]
+ install_mode: FileMode
+ install_rpath: str
+ install_tag: T.List[T.Optional[str]]
+ language_args: T.DefaultDict[str, T.List[str]]
+ link_args: T.List[str]
+ link_depends: T.List[T.Union[str, File, CustomTarget, CustomTargetIndex]]
+ link_language: str
+ link_whole: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]
+ link_with: T.List[BuildTargetTypes]
+ name_prefix: T.Optional[str]
+ name_suffix: T.Optional[str]
+ native: MachineChoice
+ override_options: T.Dict[OptionKey, str]
+ resources: T.List[str]
+ swift_interoperability_mode: Literal['c', 'cpp']
+ swift_module_name: str
+ rust_crate_type: RustCrateType
+ rust_dependency_map: T.Dict[str, str]
+ vala_gir: T.Optional[str]
+ vala_header: T.Optional[str]
+ vala_vapi: T.Optional[str]
+ win_subsystem: str
+
+ _allow_no_sources: bool
+
+ class ExecutableKeywordArguments(BuildTargetKeywordArguments, total=False):
+
+ android_exe_type: T.Optional[Literal['application', 'executable']]
+ implib: T.Optional[str]
+ export_dynamic: bool
+ pie: bool
+ vs_module_defs: T.Union[str, File, CustomTarget, CustomTargetIndex]
+
+ class SharedModuleKeywordArguments(BuildTargetKeywordArguments, total=False):
+
+ vs_module_defs: T.Union[str, File, CustomTarget, CustomTargetIndex]
+
+ class SharedLibraryKeywordArguments(SharedModuleKeywordArguments, total=False):
+
+ version: str
+ soversion: str
+ darwin_versions: T.Tuple[str, str]
+ shortname: str
+
+ class StaticLibraryKeywordArguments(BuildTargetKeywordArguments, total=False):
+
+ pic: bool
+ prelink: bool
+
+DEFAULT_STATIC_LIBRARY_NAMES: T.Mapping[str, T.Tuple[str, str]] = {
+ 'unix': ('lib', 'a'),
+ 'windows': ('', 'lib'),
+ 'darwin': ('lib', 'a'),
+ 'cygwin': ('lib', 'a'),
+}
+
+DEFAULT_SHARED_LIBRARY_NAMES: T.Mapping[str, T.Tuple[str, str, str]] = {
+ 'unix': ('lib', 'so', ''),
+ 'windows': ('', 'dll', 'dll.lib'),
+ 'darwin': ('lib', 'dylib', ''),
+ 'cygwin': ('cyg', 'dll', 'dll.a'),
+}
+
pch_kwargs = {'c_pch', 'cpp_pch'}
lang_arg_kwargs = {f'{lang}_args' for lang in all_languages}
@@ -75,10 +159,12 @@ lang_arg_kwargs |= {
vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'}
rust_kwargs = {'rust_crate_type', 'rust_dependency_map'}
cs_kwargs = {'resources', 'cs_args'}
+swift_kwargs = {'swift_interoperability_mode', 'swift_module_name'}
buildtarget_kwargs = {
'build_by_default',
'build_rpath',
+ 'build_subdir',
'dependencies',
'extra_files',
'gui_app',
@@ -110,10 +196,11 @@ known_build_target_kwargs = (
pch_kwargs |
vala_kwargs |
rust_kwargs |
- cs_kwargs)
+ cs_kwargs |
+ swift_kwargs)
known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs', 'android_exe_type'}
-known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi'}
+known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi', 'shortname'}
known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'}
known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'}
known_jar_kwargs = known_exe_kwargs | {'main_class', 'java_resources'}
@@ -244,12 +331,13 @@ class Build:
all dependencies and so on.
"""
- def __init__(self, environment: environment.Environment):
+ def __init__(self, environment: Environment):
self.version = coredata.version
+ self._def_files: T.Optional[T.List[str]] = None
self.project_name = 'name of master project'
self.project_version: T.Optional[str] = None
self.environment = environment
- self.projects = {}
+ self.projects: T.Dict[str, str] = {}
self.targets: 'T.OrderedDict[str, T.Union[CustomTarget, BuildTarget]]' = OrderedDict()
self.targetnames: T.Set[T.Tuple[str, str]] = set() # Set of executable names and their subdir
self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {})
@@ -275,7 +363,7 @@ class Build:
self.stdlibs = PerMachine({}, {})
self.test_setups: T.Dict[str, TestSetup] = {}
self.test_setup_default_name = None
- self.find_overrides: T.Dict[str, T.Union['Executable', programs.ExternalProgram, programs.OverrideProgram]] = {}
+ self.find_overrides: T.Dict[str, T.Union['OverrideExecutable', programs.ExternalProgram, programs.OverrideProgram]] = {}
self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for.
# If we are doing a cross build we need two caches, if we're doing a
@@ -289,14 +377,26 @@ class Build:
Needed for tracking whether a modules options needs to be exposed to the user.
"""
- def get_build_targets(self):
+ @property
+ def def_files(self) -> T.List[str]:
+ if self._def_files is None:
+ raise MesonBugException('build.def_files has not been set yet')
+ return self._def_files
+
+ @def_files.setter
+ def def_files(self, value: T.List[str]):
+ if self._def_files is not None:
+ raise MesonBugException('build.def_files already set')
+ self._def_files = value
+
+ def get_build_targets(self) -> OrderedDict[str, BuildTarget]:
build_targets = OrderedDict()
for name, t in self.targets.items():
if isinstance(t, BuildTarget):
build_targets[name] = t
return build_targets
- def get_custom_targets(self):
+ def get_custom_targets(self) -> OrderedDict[str, CustomTarget]:
custom_targets = OrderedDict()
for name, t in self.targets.items():
if isinstance(t, CustomTarget):
@@ -320,7 +420,7 @@ class Build:
if self.static_linker[compiler.for_machine] is None and compiler.needs_static_linker():
self.static_linker[compiler.for_machine] = detect_static_linker(self.environment, compiler)
- def get_project(self):
+ def get_project(self) -> T.Dict[str, str]:
return self.projects['']
def get_subproject_dir(self):
@@ -477,7 +577,7 @@ class StructuredSources(HoldableObject):
represent the required filesystem layout.
"""
- sources: T.DefaultDict[str, T.List[T.Union[File, CustomTarget, CustomTargetIndex, GeneratedList]]] = field(
+ sources: T.DefaultDict[str, T.List[T.Union[File, GeneratedTypes]]] = field(
default_factory=lambda: defaultdict(list))
def __add__(self, other: StructuredSources) -> StructuredSources:
@@ -489,14 +589,7 @@ class StructuredSources(HoldableObject):
def __bool__(self) -> bool:
return bool(self.sources)
- def first_file(self) -> T.Union[File, CustomTarget, CustomTargetIndex, GeneratedList]:
- """Get the first source in the root
-
- :return: The first source in the root
- """
- return self.sources[''][0]
-
- def as_list(self) -> T.List[T.Union[File, CustomTarget, CustomTargetIndex, GeneratedList]]:
+ def as_list(self) -> T.List[T.Union[File, GeneratedTypes]]:
return list(itertools.chain.from_iterable(self.sources.values()))
def needs_copy(self) -> bool:
@@ -524,10 +617,11 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
subproject: 'SubProject'
build_by_default: bool
for_machine: MachineChoice
- environment: environment.Environment
+ environment: Environment
install: bool = False
build_always_stale: bool = False
extra_files: T.List[File] = field(default_factory=list)
+ build_subdir: str = ''
@abc.abstractproperty
def typename(self) -> str:
@@ -545,6 +639,9 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
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.builddir = self.subdir
+ if self.build_subdir:
+ self.builddir = os.path.join(self.subdir, self.build_subdir)
# dataclass comparators?
def __lt__(self, other: object) -> bool:
@@ -605,6 +702,12 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
def get_typename(self) -> str:
return self.typename
+ def get_build_subdir(self) -> str:
+ return self.build_subdir
+
+ def get_builddir(self) -> str:
+ return self.builddir
+
@staticmethod
def _get_id_hash(target_id: str) -> str:
# We don't really need cryptographic security here.
@@ -640,45 +743,14 @@ class Target(HoldableObject, metaclass=abc.ABCMeta):
if getattr(self, 'name_suffix_set', False):
name += '.' + self.suffix
return self.construct_id_from_path(
- self.subdir, name, self.type_suffix())
+ self.builddir, name, self.type_suffix())
def get_id(self) -> str:
return self.id
- def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None:
- if 'build_by_default' in kwargs:
- self.build_by_default = kwargs['build_by_default']
- if not isinstance(self.build_by_default, bool):
- raise InvalidArguments('build_by_default must be a boolean value.')
-
- if not self.build_by_default and kwargs.get('install', False):
- # For backward compatibility, if build_by_default is not explicitly
- # set, use the value of 'install' if it's enabled.
- self.build_by_default = True
-
- self.raw_overrides = self.parse_overrides(kwargs)
-
def get_override(self, name: str) -> T.Optional[str]:
return self.raw_overrides.get(name, None)
- @staticmethod
- def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[str, str]:
- opts = kwargs.get('override_options', [])
-
- # In this case we have an already parsed and ready to go dictionary
- # provided by typed_kwargs
- if isinstance(opts, dict):
- return T.cast('T.Dict[OptionKey, str]', opts)
-
- result: T.Dict[str, str] = {}
- overrides = stringlistify(opts)
- for o in overrides:
- if '=' not in o:
- raise InvalidArguments('Overrides must be of form "key=value"')
- k, v = o.split('=', 1)
- result[k] = v
- return result
-
def is_linkable_target(self) -> bool:
return False
@@ -692,6 +764,7 @@ class BuildTarget(Target):
known_kwargs = known_build_target_kwargs
install_dir: T.List[T.Union[str, Literal[False]]]
+ rust_crate_type: RustCrateType
# This set contains all the languages a linker can link natively
# without extra flags. For instance, nvcc (cuda) can link C++
@@ -711,10 +784,10 @@ class BuildTarget(Target):
sources: T.List['SourceOutputs'],
structured_sources: T.Optional[StructuredSources],
objects: T.List[ObjectTypes],
- environment: environment.Environment,
+ environment: Environment,
compilers: T.Dict[str, 'Compiler'],
- kwargs: T.Dict[str, T.Any]):
- super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False))
+ kwargs: BuildTargetKeywordArguments):
+ super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False), build_subdir=kwargs.get('build_subdir', ''))
self.all_compilers = compilers
self.compilers: OrderedDict[str, Compiler] = OrderedDict()
self.objects: T.List[ObjectTypes] = []
@@ -725,7 +798,7 @@ class BuildTarget(Target):
self.link_targets: T.List[LibTypes] = []
self.link_whole_targets: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]] = []
self.depend_files: T.List[File] = []
- self.link_depends = []
+ self.link_depends: T.List[T.Union[File, BuildTargetTypes]] = []
self.added_deps = set()
self.name_prefix_set = False
self.name_suffix_set = False
@@ -736,7 +809,7 @@ class BuildTarget(Target):
# The list of all files outputted by this target. Useful in cases such
# as Vala which generates .vapi and .h besides the compiled output.
self.outputs = [self.filename]
- self.pch: T.Dict[str, T.List[str]] = {}
+ self.pch: T.Dict[str, T.Optional[T.Tuple[str, T.Optional[str]]]] = {}
self.extra_args: T.DefaultDict[str, T.List[str]] = kwargs.get('language_args', defaultdict(list))
self.sources: T.List[File] = []
# If the same source is defined multiple times, use it only once.
@@ -754,6 +827,9 @@ class BuildTarget(Target):
self.both_lib: T.Optional[T.Union[StaticLibrary, SharedLibrary]] = None
# Track build_rpath entries so we can remove them at install time
self.rpath_dirs_to_remove: T.Set[bytes] = set()
+ self.vala_header: T.Optional[str] = None
+ self.vala_vapi: T.Optional[str] = None
+ self.vala_gir: T.Optional[str] = None
self.process_sourcelist(sources)
# Objects can be:
# 1. Preexisting objects provided by the user with the `objects:` kwarg
@@ -773,9 +849,10 @@ class BuildTarget(Target):
self.link_whole_targets.clear()
self.link(link_targets)
self.link_whole(link_whole_targets)
+ self._set_vala_args(kwargs)
- if not any([self.sources, self.generated, self.objects, self.link_whole_targets, self.structured_sources,
- kwargs.pop('_allow_no_sources', False)]):
+ if not any([[src for src in self.sources if not is_header(src)], self.generated, self.objects,
+ self.link_whole_targets, self.structured_sources, kwargs.pop('_allow_no_sources', False)]):
mlog.warning(f'Build target {name} has no sources. '
'This was never supposed to be allowed but did because of a bug, '
'support will be removed in a future release of Meson')
@@ -783,22 +860,41 @@ class BuildTarget(Target):
self.validate_install()
self.check_module_linking()
+ def _set_vala_args(self, kwargs: BuildTargetKeywordArguments) -> None:
+ if self.uses_vala():
+ self.vala_header = kwargs.get('vala_header') or self.name + '.h'
+ self.vala_vapi = kwargs.get('vala_vapi') or self.name + '.vapi'
+ self.vala_gir = kwargs.get('vala_gir')
+
def post_init(self) -> None:
''' Initialisations and checks requiring the final list of compilers to be known
'''
self.validate_sources()
- if self.structured_sources and any([self.sources, self.generated]):
- raise MesonException('cannot mix structured sources and unstructured sources')
- if self.structured_sources and 'rust' not in self.compilers:
- raise MesonException('structured sources are only supported in Rust targets')
if self.uses_rust():
+ if self.link_language and self.link_language != 'rust':
+ raise MesonException('cannot build Rust sources with a different link_language')
+ if self.structured_sources:
+ # TODO: the interpreter should be able to generate a better error message?
+ if any((s.endswith('.rs') for s in self.sources)) or \
+ any(any((s.endswith('.rs') for s in g.get_outputs())) for g in self.generated):
+ raise MesonException('cannot mix Rust structured sources and unstructured sources')
+
# relocation-model=pic is rustc's default and Meson does not
# currently have a way to disable PIC.
self.pic = True
- if 'vala' in self.compilers and self.is_linkable_target():
- self.outputs += [self.vala_header, self.vala_vapi]
- self.install_tag += ['devel', 'devel']
- if self.vala_gir:
+ self.pie = True
+ else:
+ if self.structured_sources:
+ raise MesonException('structured sources are only supported in Rust targets')
+
+ if self.is_linkable_target():
+ if self.vala_header is not None:
+ self.outputs.append(self.vala_header)
+ self.install_tag.append('devel')
+ if self.vala_vapi is not None:
+ self.outputs.append(self.vala_vapi)
+ self.install_tag.append('devel')
+ if self.vala_gir is not None:
self.outputs.append(self.vala_gir)
self.install_tag.append('devel')
@@ -816,12 +912,12 @@ class BuildTarget(Target):
else:
mlog.warning('Installing target build for the build machine. This will fail in a cross build.')
- def check_unknown_kwargs(self, kwargs):
+ def check_unknown_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None:
# Override this method in derived classes that have more
# keywords.
self.check_unknown_kwargs_int(kwargs, self.known_kwargs)
- def check_unknown_kwargs_int(self, kwargs, known_kwargs):
+ def check_unknown_kwargs_int(self, kwargs: BuildTargetKeywordArguments, known_kwargs: T.Set[str]) -> None:
unknowns = []
for k in kwargs:
if k == 'language_args':
@@ -889,6 +985,11 @@ class BuildTarget(Target):
# did user override clink_langs for this target?
link_langs = [self.link_language] if self.link_language else clink_langs
+ if self.link_language:
+ if self.link_language not in self.all_compilers:
+ m = f'Target {self.name} requires {self.link_language} compiler not part of the project'
+ raise MesonException(m)
+
# If this library is linked against another library we need to consider
# the languages of those libraries as well.
if self.link_targets or self.link_whole_targets:
@@ -896,6 +997,10 @@ class BuildTarget(Target):
if isinstance(t, (CustomTarget, CustomTargetIndex)):
continue # We can't know anything about these.
for name, compiler in t.compilers.items():
+ if name == 'rust':
+ # Rust is always linked through a C-ABI target, so do not add
+ # the compiler here
+ continue
if name in link_langs and name not in self.compilers:
self.compilers[name] = compiler
@@ -903,7 +1008,7 @@ class BuildTarget(Target):
# No source files or parent targets, target consists of only object
# files of unknown origin. Just add the first clink compiler
# that we have and hope that it can link these objects
- for lang in link_langs:
+ for lang in reversed(link_langs):
if lang in self.all_compilers:
self.compilers[lang] = self.all_compilers[lang]
break
@@ -981,7 +1086,7 @@ class BuildTarget(Target):
self.compilers[lang] = compiler
break
else:
- if is_known_suffix(s):
+ if is_known_suffix(s) and not is_header(s):
path = pathlib.Path(str(s)).as_posix()
m = f'No {self.for_machine.get_lower_case_name()} machine compiler for {path!r}'
raise MesonException(m)
@@ -1011,7 +1116,7 @@ class BuildTarget(Target):
langs = ', '.join(self.compilers.keys())
raise InvalidArguments(f'Cannot mix those languages into a target: {langs}')
- def process_link_depends(self, sources):
+ def process_link_depends(self, sources: T.Iterable[T.Union[str, File, BuildTargetTypes]]) -> None:
"""Process the link_depends keyword argument.
This is designed to handle strings, Files, and the output of Custom
@@ -1020,19 +1125,14 @@ class BuildTarget(Target):
generated twice, since the output needs to be passed to the ld_args and
link_depends.
"""
- sources = listify(sources)
for s in sources:
if isinstance(s, File):
self.link_depends.append(s)
elif isinstance(s, str):
self.link_depends.append(
File.from_source_file(self.environment.source_dir, self.subdir, s))
- elif hasattr(s, 'get_outputs'):
- self.link_depends.append(s)
else:
- raise InvalidArguments(
- 'Link_depends arguments must be strings, Files, '
- 'or a Custom Target, or lists thereof.')
+ self.link_depends.append(s)
def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects:
sources_set = set(self.sources)
@@ -1074,20 +1174,23 @@ class BuildTarget(Target):
at link time, see get_dependencies() for that.
"""
result: OrderedSet[BuildTargetTypes] = OrderedSet()
+ nonresults: T.Set[BuildTargetTypes] = set()
stack: T.Deque[BuildTargetTypes] = deque()
stack.appendleft(self)
while stack:
t = stack.pop()
- if t in result:
+ if t in result or t in nonresults:
continue
if isinstance(t, CustomTargetIndex):
stack.appendleft(t.target)
continue
if isinstance(t, SharedLibrary):
result.add(t)
+ else:
+ nonresults.add(t)
if isinstance(t, BuildTarget):
- stack.extendleft(t.link_targets)
- stack.extendleft(t.link_whole_targets)
+ stack.extendleft((t2 for t2 in t.link_targets if t2 not in nonresults))
+ stack.extendleft((t2 for t2 in t.link_whole_targets if t2 not in nonresults))
return list(result)
@lru_cache(maxsize=None)
@@ -1155,22 +1258,23 @@ class BuildTarget(Target):
def get_custom_install_mode(self) -> T.Optional['FileMode']:
return self.install_mode
- def process_kwargs(self, kwargs):
- self.process_kwargs_base(kwargs)
+ def process_kwargs(self, kwargs: BuildTargetKeywordArguments) -> None:
self.original_kwargs = kwargs
- self.add_pch('c', extract_as_list(kwargs, 'c_pch'))
- self.add_pch('cpp', extract_as_list(kwargs, 'cpp_pch'))
+ if 'build_by_default' in kwargs:
+ self.build_by_default = kwargs['build_by_default']
+
+ if not self.build_by_default and kwargs.get('install', False):
+ # For backward compatibility, if build_by_default is not explicitly
+ # set, use the value of 'install' if it's enabled.
+ self.build_by_default = True
+
+ self.raw_overrides = kwargs.get('override_options', {})
- if not isinstance(self, Executable) or kwargs.get('export_dynamic', False):
- self.vala_header = kwargs.get('vala_header', self.name + '.h')
- self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi')
- self.vala_gir = kwargs.get('vala_gir', None)
+ self.pch['c'] = kwargs.get('c_pch')
+ self.pch['cpp'] = kwargs.get('cpp_pch')
- self.link_args = extract_as_list(kwargs, 'link_args')
- for i in self.link_args:
- if not isinstance(i, str):
- raise InvalidArguments('Link_args arguments must be strings.')
+ self.link_args = kwargs.get('link_args', [])
for l in self.link_args:
if '-Wl,-rpath' in l or l.startswith('-rpath'):
mlog.warning(textwrap.dedent('''\
@@ -1191,94 +1295,48 @@ class BuildTarget(Target):
self.install_dir = typeslistify(kwargs.get('install_dir', []),
(str, bool))
self.install_mode = kwargs.get('install_mode', None)
- self.install_tag = stringlistify(kwargs.get('install_tag', [None]))
- if not isinstance(self, Executable):
- # build_target will always populate these as `None`, which is fine
- if kwargs.get('gui_app') is not None:
- raise InvalidArguments('Argument gui_app can only be used on executables.')
- if kwargs.get('win_subsystem') is not None:
- raise InvalidArguments('Argument win_subsystem can only be used on executables.')
- extra_files = extract_as_list(kwargs, 'extra_files')
- for i in extra_files:
- assert isinstance(i, File)
- if i in self.extra_files:
- continue
- trial = os.path.join(self.environment.get_source_dir(), i.subdir, i.fname)
- if not os.path.isfile(trial):
- raise InvalidArguments(f'Tried to add non-existing extra file {i}.')
- self.extra_files.append(i)
+ self.install_tag: T.List[T.Optional[str]] = kwargs.get('install_tag') or [None]
+ self.extra_files = kwargs.get('extra_files', [])
self.install_rpath: str = kwargs.get('install_rpath', '')
- if not isinstance(self.install_rpath, str):
- raise InvalidArguments('Install_rpath is not a string.')
self.build_rpath = kwargs.get('build_rpath', '')
- if not isinstance(self.build_rpath, str):
- raise InvalidArguments('Build_rpath is not a string.')
- resources = extract_as_list(kwargs, 'resources')
- for r in resources:
- if not isinstance(r, str):
- raise InvalidArguments('Resource argument is not a string.')
- trial = os.path.join(self.environment.get_source_dir(), self.subdir, r)
- if not os.path.isfile(trial):
- raise InvalidArguments(f'Tried to add non-existing resource {r}.')
- self.resources = resources
- if kwargs.get('name_prefix') is not None:
- name_prefix = kwargs['name_prefix']
- if isinstance(name_prefix, list):
- if name_prefix:
- raise InvalidArguments('name_prefix array must be empty to signify default.')
- else:
- if not isinstance(name_prefix, str):
- raise InvalidArguments('name_prefix must be a string.')
- self.prefix = name_prefix
- self.name_prefix_set = True
- if kwargs.get('name_suffix') is not None:
- name_suffix = kwargs['name_suffix']
- if isinstance(name_suffix, list):
- if name_suffix:
- raise InvalidArguments('name_suffix array must be empty to signify default.')
- else:
- if not isinstance(name_suffix, str):
- raise InvalidArguments('name_suffix must be a string.')
- if name_suffix == '':
- raise InvalidArguments('name_suffix should not be an empty string. '
- 'If you want meson to use the default behaviour '
- 'for each platform pass `[]` (empty array)')
- self.suffix = name_suffix
- self.name_suffix_set = True
- if isinstance(self, StaticLibrary):
- # You can't disable PIC on OS X. The compiler ignores -fno-PIC.
- # PIC is always on for Windows (all code is position-independent
- # since library loading is done differently)
- m = self.environment.machines[self.for_machine]
- if m.is_darwin() or m.is_windows():
- self.pic = True
- else:
- self.pic = self._extract_pic_pie(kwargs, 'pic', 'b_staticpic')
- if isinstance(self, Executable) or (isinstance(self, StaticLibrary) and not self.pic):
- # Executables must be PIE on Android
- if self.environment.machines[self.for_machine].is_android():
- self.pie = True
- else:
- self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie')
+ self.resources = kwargs.get('resources', [])
+ name_prefix = kwargs.get('name_prefix')
+ if name_prefix is not None:
+ self.prefix = name_prefix
+ self.name_prefix_set = True
+ name_suffix = kwargs.get('name_suffix')
+ if name_suffix is not None:
+ self.suffix = name_suffix
+ self.name_suffix_set = True
self.implicit_include_directories = kwargs.get('implicit_include_directories', True)
- if not isinstance(self.implicit_include_directories, bool):
- raise InvalidArguments('Implicit_include_directories must be a boolean.')
self.gnu_symbol_visibility = kwargs.get('gnu_symbol_visibility', '')
- if not isinstance(self.gnu_symbol_visibility, str):
- raise InvalidArguments('GNU symbol visibility must be a string.')
- if self.gnu_symbol_visibility != '':
- permitted = ['default', 'internal', 'hidden', 'protected', 'inlineshidden']
- if self.gnu_symbol_visibility not in permitted:
- raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.gnu_symbol_visibility, ', '.join(permitted)))
-
- rust_dependency_map = kwargs.get('rust_dependency_map', {})
- if not isinstance(rust_dependency_map, dict):
- raise InvalidArguments(f'Invalid rust_dependency_map "{rust_dependency_map}": must be a dictionary.')
- if any(not isinstance(v, str) for v in rust_dependency_map.values()):
- raise InvalidArguments(f'Invalid rust_dependency_map "{rust_dependency_map}": must be a dictionary with string values.')
- self.rust_dependency_map = rust_dependency_map
-
- def _extract_pic_pie(self, kwargs: T.Dict[str, T.Any], arg: str, option: str) -> bool:
+ self.rust_dependency_map = kwargs.get('rust_dependency_map', {})
+
+ self.swift_interoperability_mode = kwargs.get('swift_interoperability_mode', 'c')
+ self.swift_module_name = kwargs.get('swift_module_name') or self.name
+
+ @T.overload
+ def _extract_pic_pie(self, kwargs: StaticLibraryKeywordArguments, arg: Literal['pic'],
+ option: Literal['b_staticpic']) -> bool: ...
+
+ @T.overload
+ def _extract_pic_pie(self, kwargs: T.Union[StaticLibraryKeywordArguments, ExecutableKeywordArguments],
+ arg: Literal['pie'], option: Literal['b_pie']) -> bool: ...
+
+ def _extract_pic_pie(self, kwargs: T.Union[StaticLibraryKeywordArguments, ExecutableKeywordArguments],
+ arg: Literal['pic', 'pie'], option: Literal['b_staticpic', 'b_pie']) -> bool:
+ # You can't disable PIC on OS X. The compiler ignores -fno-PIC.
+ # PIC is always on for Windows (all code is position-independent
+ # since library loading is done differently)
+ m = self.environment.machines[self.for_machine]
+ assert m is not None, 'for mypy'
+ if arg == 'pic' and (m.is_darwin() or m.is_windows()):
+ return True
+
+ # Executables must be PIE on Android
+ if arg == 'pie' and m.is_android():
+ return True
+
# Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags
all_flags = self.extra_args['c'] + self.extra_args['cpp']
if '-f' + arg.lower() in all_flags or '-f' + arg.upper() in all_flags:
@@ -1287,15 +1345,12 @@ class BuildTarget(Target):
k = OptionKey(option)
if kwargs.get(arg) is not None:
- val = T.cast('bool', kwargs[arg])
+ return kwargs[arg]
elif k in self.environment.coredata.optstore:
- val = self.environment.coredata.optstore.get_value_for(k.name, k.subproject)
- else:
- val = False
-
- if not isinstance(val, bool):
- raise InvalidArguments(f'Argument {arg} to {self.name!r} must be boolean')
- return val
+ val = self.environment.coredata.get_option_for_target(self, k)
+ assert isinstance(val, bool), 'for mypy'
+ return val
+ return False
def get_filename(self) -> str:
return self.filename
@@ -1363,10 +1418,7 @@ class BuildTarget(Target):
return self.install
def has_pch(self) -> bool:
- return bool(self.pch)
-
- def get_pch(self, language: str) -> T.List[str]:
- return self.pch.get(language, [])
+ return any(x is not None for x in self.pch.values())
def get_include_dirs(self) -> T.List['IncludeDirs']:
return self.include_dirs
@@ -1375,6 +1427,10 @@ class BuildTarget(Target):
deps = listify(deps)
for dep in deps:
if dep in self.added_deps:
+ # Prefer to add dependencies to added_deps which have a name
+ if dep.is_named():
+ self.added_deps.remove(dep)
+ self.added_deps.add(dep)
continue
if isinstance(dep, dependencies.InternalDependency):
@@ -1533,37 +1589,6 @@ class BuildTarget(Target):
else:
mlog.warning(msg + ' This will fail in cross build.')
- def add_pch(self, language: str, pchlist: T.List[str]) -> None:
- if not pchlist:
- return
- elif len(pchlist) == 1:
- if not is_header(pchlist[0]):
- raise InvalidArguments(f'PCH argument {pchlist[0]} is not a header.')
- elif len(pchlist) == 2:
- if is_header(pchlist[0]):
- if not is_source(pchlist[1]):
- raise InvalidArguments('PCH definition must contain one header and at most one source.')
- elif is_source(pchlist[0]):
- if not is_header(pchlist[1]):
- raise InvalidArguments('PCH definition must contain one header and at most one source.')
- pchlist = [pchlist[1], pchlist[0]]
- else:
- raise InvalidArguments(f'PCH argument {pchlist[0]} is of unknown type.')
-
- if os.path.dirname(pchlist[0]) != os.path.dirname(pchlist[1]):
- raise InvalidArguments('PCH files must be stored in the same folder.')
-
- FeatureDeprecated.single_use('PCH source files', '0.50.0', self.subproject,
- 'Only a single header file should be used.')
- elif len(pchlist) > 2:
- raise InvalidArguments('PCH definition may have a maximum of 2 files.')
- for f in pchlist:
- if not isinstance(f, str):
- raise MesonException('PCH arguments must be strings.')
- if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, f)):
- raise MesonException(f'File {f} does not exist.')
- self.pch[language] = pchlist
-
def add_include_dirs(self, args: T.Sequence['IncludeDirs'], set_is_system: T.Optional[str] = None) -> None:
ids: T.List['IncludeDirs'] = []
for a in args:
@@ -1603,12 +1628,15 @@ class BuildTarget(Target):
if isinstance(link_target, (CustomTarget, CustomTargetIndex)):
continue
for language in link_target.compilers:
+ if language == 'rust' and not link_target.uses_rust_abi():
+ # All Rust dependencies must go through a C-ABI dependency, so ignore it
+ continue
if language not in langs:
langs.append(language)
return langs
- def get_prelinker(self):
+ def get_prelinker(self) -> Compiler:
if self.link_language:
comp = self.all_compilers[self.link_language]
return comp
@@ -1637,7 +1665,7 @@ class BuildTarget(Target):
# If the user set the link_language, just return that.
if self.link_language:
comp = self.all_compilers[self.link_language]
- return comp, comp.language_stdlib_only_link_flags(self.environment)
+ return comp, comp.language_stdlib_only_link_flags()
# Since dependencies could come from subprojects, they could have
# languages we don't have in self.all_compilers. Use the global list of
@@ -1667,7 +1695,7 @@ class BuildTarget(Target):
for l in clink_langs:
try:
comp = self.all_compilers[l]
- return comp, comp.language_stdlib_only_link_flags(self.environment)
+ return comp, comp.language_stdlib_only_link_flags()
except KeyError:
pass
@@ -1682,7 +1710,7 @@ class BuildTarget(Target):
# We need to use all_compilers here because
# get_langs_used_by_deps could return a language from a
# subproject
- stdlib_args.extend(all_compilers[dl].language_stdlib_only_link_flags(self.environment))
+ stdlib_args.extend(all_compilers[dl].language_stdlib_only_link_flags())
return stdlib_args
def uses_rust(self) -> bool:
@@ -1694,6 +1722,12 @@ class BuildTarget(Target):
def uses_fortran(self) -> bool:
return 'fortran' in self.compilers
+ def uses_vala(self) -> bool:
+ return 'vala' in self.compilers
+
+ def uses_swift_cpp_interop(self) -> bool:
+ return self.swift_interoperability_mode == 'cpp' and 'swift' in self.compilers
+
def get_using_msvc(self) -> bool:
'''
Check if the dynamic linker is MSVC. Used by Executable, StaticLibrary,
@@ -1744,11 +1778,11 @@ class BuildTarget(Target):
'use shared_library() with `override_options: [\'b_lundef=false\']` instead.')
link_target.force_soname = True
- def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None:
- if kwargs.get('vs_module_defs') is None:
+ def process_vs_module_defs_kw(self, kwargs: ExecutableKeywordArguments) -> None:
+ path = kwargs.get('vs_module_defs')
+ if path is None:
return
- path: T.Union[str, File, CustomTarget, CustomTargetIndex] = kwargs['vs_module_defs']
if isinstance(path, str):
if os.path.isabs(path):
self.vs_module_defs = File.from_absolute_file(path)
@@ -1757,16 +1791,12 @@ class BuildTarget(Target):
elif isinstance(path, File):
# When passing a generated file.
self.vs_module_defs = path
- elif isinstance(path, (CustomTarget, CustomTargetIndex)):
+ else:
# When passing output of a Custom Target
self.vs_module_defs = File.from_built_file(path.get_subdir(), path.get_filename())
- else:
- raise InvalidArguments(
- 'vs_module_defs must be either a string, '
- 'a file object, a Custom Target, or a Custom Target Index')
- self.process_link_depends(path)
+ self.process_link_depends([path])
- def extract_targets_as_list(self, kwargs: T.Dict[str, T.Union[LibTypes, T.Sequence[LibTypes]]], key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]:
+ def extract_targets_as_list(self, kwargs: BuildTargetKeywordArguments, key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]:
bl_type = self.environment.coredata.optstore.get_value_for(OptionKey('default_both_libraries'))
if bl_type == 'auto':
if isinstance(self, StaticLibrary):
@@ -1788,6 +1818,132 @@ class BuildTarget(Target):
"""Base case used by BothLibraries"""
return self
+ def determine_rpath_dirs(self) -> T.Tuple[str, ...]:
+ result: OrderedSet[str]
+ if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror':
+ # Need a copy here
+ result = OrderedSet(self.get_link_dep_subdirs())
+ else:
+ result = OrderedSet()
+ result.add('meson-out')
+ result.update(self.rpaths_for_non_system_absolute_shared_libraries())
+ self.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result])
+ return tuple(result)
+
+ @lru_cache(maxsize=None)
+ def rpaths_for_non_system_absolute_shared_libraries(self, exclude_system: bool = True) -> ImmutableListProtocol[str]:
+ paths: OrderedSet[str] = OrderedSet()
+ srcdir = self.environment.get_source_dir()
+
+ system_dirs = set()
+ if exclude_system:
+ for cc in self.compilers.values():
+ system_dirs.update(cc.get_library_dirs())
+
+ external_rpaths = self.get_external_rpath_dirs()
+ build_to_src = relpath(self.environment.get_source_dir(),
+ self.environment.get_build_dir())
+
+ for dep in self.external_deps:
+ if dep.type_name not in {'library', 'pkgconfig', 'cmake'}:
+ continue
+ for libpath in dep.link_args:
+ if libpath.startswith('-'):
+ continue
+ # For all link args that are absolute paths to a library file, add RPATH args
+ if not os.path.isabs(libpath):
+ continue
+ libdir, libname = os.path.split(libpath)
+ # Windows doesn't support rpaths, but we use this function to
+ # emulate rpaths by setting PATH
+ # .dll is there for mingw gcc
+ # .so's may be extended with version information, e.g. libxyz.so.1.2.3
+ if not (
+ libname.endswith(('.dll', '.lib', '.so', '.dylib'))
+ or '.so.' in libname
+ ):
+ continue
+
+ # Don't remove rpaths specified in LDFLAGS.
+ if libdir in external_rpaths:
+ continue
+ if system_dirs and os.path.normpath(libdir) in system_dirs:
+ # No point in adding system paths.
+ continue
+
+ if is_parent_path(srcdir, libdir):
+ rel_to_src = libdir[len(srcdir) + 1:]
+ assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute'
+ paths.add(os.path.join(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 itertools.chain(self.link_targets, self.link_whole_targets):
+ if isinstance(i, BuildTarget):
+ paths.update(i.rpaths_for_non_system_absolute_shared_libraries(exclude_system))
+ return list(paths)
+
+ def get_external_rpath_dirs(self) -> T.Set[str]:
+ args: T.List[str] = []
+ for lang in LANGUAGES_USING_LDFLAGS:
+ try:
+ args += self.environment.coredata.get_external_link_args(self.for_machine, lang)
+ except KeyError:
+ pass
+ return self.get_rpath_dirs_from_link_args(args)
+
+ # Match rpath formats:
+ # -Wl,-rpath=
+ # -Wl,-rpath,
+ _rpath_regex = re.compile(r'-Wl,-rpath[=,]([^,]+)')
+ # Match solaris style compat runpath formats:
+ # -Wl,-R
+ # -Wl,-R,
+ _runpath_regex = re.compile(r'-Wl,-R[,]?([^,]+)')
+ # Match symbols formats:
+ # -Wl,--just-symbols=
+ # -Wl,--just-symbols,
+ _symbols_regex = re.compile(r'-Wl,--just-symbols[=,]([^,]+)')
+
+ @classmethod
+ def get_rpath_dirs_from_link_args(cls, args: T.List[str]) -> T.Set[str]:
+ dirs: T.Set[str] = set()
+
+ for arg in args:
+ if not arg.startswith('-Wl,'):
+ continue
+
+ rpath_match = cls._rpath_regex.match(arg)
+ if rpath_match:
+ for dir in rpath_match.group(1).split(':'):
+ dirs.add(dir)
+ runpath_match = cls._runpath_regex.match(arg)
+ if runpath_match:
+ for dir in runpath_match.group(1).split(':'):
+ # The symbols arg is an rpath if the path is a directory
+ if os.path.isdir(dir):
+ dirs.add(dir)
+ symbols_match = cls._symbols_regex.match(arg)
+ if symbols_match:
+ for dir in symbols_match.group(1).split(':'):
+ # Prevent usage of --just-symbols to specify rpath
+ if os.path.isdir(dir):
+ raise MesonException(f'Invalid arg for --just-symbols, {dir} is a directory.')
+ return dirs
+
+ def get_platform_scheme_name(self) -> str:
+ m = self.environment.machines[self.for_machine]
+ if m.is_cygwin():
+ return 'cygwin'
+ elif m.is_windows():
+ return 'windows'
+ elif m.is_darwin():
+ return 'darwin'
+ else:
+ return 'unix'
+
+
class FileInTargetPrivateDir:
"""Represents a file with the path '/path/to/build/target_private_dir/fname'.
target_private_dir is the return value of get_target_private_dir which is e.g. 'subdir/target.p'.
@@ -1823,19 +1979,21 @@ class FileMaybeInTargetPrivateDir:
return self.fname
class Generator(HoldableObject):
- def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
+ def __init__(self, env: Environment,
+ exe: T.Union['Executable', programs.ExternalProgram],
arguments: T.List[str],
output: T.List[str],
# how2dataclass
*,
depfile: T.Optional[str] = None,
capture: bool = False,
- depends: T.Optional[T.List[T.Union[BuildTarget, 'CustomTarget', 'CustomTargetIndex']]] = None,
+ depends: T.Optional[T.List[BuildTargetTypes]] = None,
name: str = 'Generator'):
+ self.environment = env
self.exe = exe
self.depfile = depfile
self.capture = capture
- self.depends: T.List[T.Union[BuildTarget, 'CustomTarget', 'CustomTargetIndex']] = depends or []
+ self.depends: T.List[BuildTargetTypes] = depends or []
self.arglist = arguments
self.outputs = output
self.name = name
@@ -1865,14 +2023,14 @@ class Generator(HoldableObject):
basename = os.path.splitext(plainname)[0]
return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist]
- def process_files(self, files: T.Iterable[T.Union[str, File, 'CustomTarget', 'CustomTargetIndex', 'GeneratedList']],
- state: T.Union['Interpreter', 'ModuleState'],
+ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]],
+ subdir: str = '',
preserve_path_from: T.Optional[str] = None,
extra_args: T.Optional[T.List[str]] = None,
env: T.Optional[EnvironmentVariables] = None) -> 'GeneratedList':
output = GeneratedList(
self,
- state.subdir,
+ subdir,
preserve_path_from,
extra_args=extra_args if extra_args is not None else [],
env=env if env is not None else EnvironmentVariables())
@@ -1887,17 +2045,17 @@ class Generator(HoldableObject):
output.depends.add(e)
fs = [FileInTargetPrivateDir(f) for f in e.get_outputs()]
elif isinstance(e, str):
- fs = [File.from_source_file(state.environment.source_dir, state.subdir, e)]
+ fs = [File.from_source_file(self.environment.source_dir, subdir, e)]
else:
fs = [e]
for f in fs:
if preserve_path_from:
- abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir)
+ abs_f = f.absolute_path(self.environment.source_dir, self.environment.build_dir)
if not is_parent_path(preserve_path_from, abs_f):
raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.')
f = FileMaybeInTargetPrivateDir(f)
- output.add_file(f, state)
+ output.add_file(f, self.environment)
return output
@@ -1936,9 +2094,9 @@ class GeneratedList(HoldableObject):
# know the absolute path of
self.depend_files.append(File.from_absolute_file(path))
- def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], state: T.Union['Interpreter', 'ModuleState']) -> T.List[str]:
+ def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], environment: Environment) -> T.List[str]:
result: T.List[str] = []
- in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir)
+ in_abs = infile.absolute_path(environment.source_dir, environment.build_dir)
assert os.path.isabs(self.preserve_path_from)
rel = os.path.relpath(in_abs, self.preserve_path_from)
path_segment = os.path.dirname(rel)
@@ -1946,11 +2104,11 @@ class GeneratedList(HoldableObject):
result.append(os.path.join(path_segment, of))
return result
- def add_file(self, newfile: FileMaybeInTargetPrivateDir, state: T.Union['Interpreter', 'ModuleState']) -> None:
+ def add_file(self, newfile: FileMaybeInTargetPrivateDir, environment: Environment) -> None:
self.infilelist.append(newfile)
outfiles = self.generator.get_base_outnames(newfile.fname)
if self.preserve_path_from:
- outfiles = self.add_preserved_path_segment(newfile, outfiles, state)
+ outfiles = self.add_preserved_path_segment(newfile, outfiles, environment)
self.outfilelist += outfiles
self.outmap[newfile] = outfiles
@@ -1987,23 +2145,17 @@ class Executable(BuildTarget):
sources: T.List['SourceOutputs'],
structured_sources: T.Optional[StructuredSources],
objects: T.List[ObjectTypes],
- environment: environment.Environment,
+ environment: Environment,
compilers: T.Dict[str, 'Compiler'],
- kwargs):
- key = OptionKey('b_pie')
- if 'pie' not in kwargs and key in environment.coredata.optstore:
- kwargs['pie'] = environment.coredata.optstore.get_value_for(key)
+ kwargs: ExecutableKeywordArguments):
+ self.export_dynamic = kwargs.get('export_dynamic', False)
+ self.rust_crate_type = kwargs.get('rust_crate_type', 'bin')
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
self.win_subsystem = kwargs.get('win_subsystem') or 'console'
- assert kwargs.get('android_exe_type') is None or kwargs.get('android_exe_type') in {'application', 'executable'}
+ self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie')
# Check for export_dynamic
- self.export_dynamic = kwargs.get('export_dynamic', False)
- if not isinstance(self.export_dynamic, bool):
- raise InvalidArguments('"export_dynamic" keyword argument must be a boolean')
- self.implib = kwargs.get('implib')
- if not isinstance(self.implib, (bool, str, type(None))):
- raise InvalidArguments('"export_dynamic" keyword argument must be a boolean or string')
+ self.implib_name = kwargs.get('implib')
# Only linkwithable if using export_dynamic
self.is_linkwithable = self.export_dynamic
# Remember that this exe was returned by `find_program()` through an override
@@ -2012,6 +2164,12 @@ class Executable(BuildTarget):
self.vs_module_defs: T.Optional[File] = None
self.process_vs_module_defs_kw(kwargs)
+ def _set_vala_args(self, kwargs: BuildTargetKeywordArguments) -> None:
+ # These don't get generated if the executable doesn't have
+ # export_dynamic set to true.
+ if self.export_dynamic:
+ super()._set_vala_args(kwargs)
+
def post_init(self) -> None:
super().post_init()
machine = self.environment.machines[self.for_machine]
@@ -2057,9 +2215,7 @@ class Executable(BuildTarget):
# If using export_dynamic, set the import library name
if self.export_dynamic:
- implib_basename = self.name + '.exe'
- if isinstance(self.implib, str):
- implib_basename = self.implib
+ implib_basename = self.implib_name or self.name + '.exe'
if machine.is_windows() or machine.is_cygwin():
if self.get_using_msvc():
self.import_filename = f'{implib_basename}.lib'
@@ -2082,13 +2238,6 @@ class Executable(BuildTarget):
name += '_' + self.suffix
self.debug_filename = name + '.pdb'
- def process_kwargs(self, kwargs):
- super().process_kwargs(kwargs)
-
- self.rust_crate_type = kwargs.get('rust_crate_type') or 'bin'
- if self.rust_crate_type != 'bin':
- raise InvalidArguments('Invalid rust_crate_type: must be "bin" for executables.')
-
def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, None]]:
return self.environment.get_bindir(), '{bindir}'
@@ -2115,7 +2264,7 @@ class Executable(BuildTarget):
"""
return self.debug_filename
- def is_linkable_target(self):
+ def is_linkable_target(self) -> bool:
return self.is_linkwithable
def get_command(self) -> 'ImmutableListProtocol[str]':
@@ -2148,12 +2297,16 @@ class StaticLibrary(BuildTarget):
sources: T.List['SourceOutputs'],
structured_sources: T.Optional[StructuredSources],
objects: T.List[ObjectTypes],
- environment: environment.Environment,
+ environment: Environment,
compilers: T.Dict[str, 'Compiler'],
- kwargs):
- self.prelink = T.cast('bool', kwargs.get('prelink', False))
+ kwargs: StaticLibraryKeywordArguments):
+ self.prelink = kwargs.get('prelink', False)
+ self.rust_crate_type = kwargs.get('rust_crate_type', 'rlib')
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
+ self.pic = self._extract_pic_pie(kwargs, 'pic', 'b_staticpic')
+ if not self.pic:
+ self.pie = self._extract_pic_pie(kwargs, 'pie', 'b_pie')
def post_init(self) -> None:
super().post_init()
@@ -2174,11 +2327,30 @@ class StaticLibrary(BuildTarget):
# and thus, machine_info kernel should be set to 'none'.
# In that case, native_static_libs list is empty.
rustc = self.compilers['rust']
- d = dependencies.InternalDependency('undefined', [], [],
- rustc.native_static_libs,
+ link_args = ['-L' + rustc.get_target_libdir() + '/self-contained']
+ link_args += rustc.native_static_libs
+ d = dependencies.InternalDependency('undefined', [], [], link_args,
[], [], [], [], [], {}, [], [], [],
'_rust_native_static_libs')
self.external_deps.append(d)
+
+ default_prefix, default_suffix = self.determine_default_prefix_and_suffix()
+ if not self.name_prefix_set:
+ self.prefix = default_prefix
+ if not self.name_suffix_set:
+ self.suffix = default_suffix
+ self.filename = self.prefix + self.name + '.' + self.suffix
+ self.outputs[0] = self.filename
+
+ def determine_default_prefix_and_suffix(self) -> T.Tuple[str, str]:
+ scheme = self.environment.coredata.get_option_for_target(self, 'namingscheme')
+ assert isinstance(scheme, str), 'for mypy'
+ if scheme == 'platform':
+ schemename = self.get_platform_scheme_name()
+ prefix, suffix = DEFAULT_STATIC_LIBRARY_NAMES[schemename]
+ else:
+ prefix = ''
+ suffix = ''
# By default a static library is named libfoo.a even on Windows because
# MSVC does not have a consistent convention for what static libraries
# are called. The MSVC CRT uses libfoo.lib syntax but nothing else uses
@@ -2190,21 +2362,25 @@ class StaticLibrary(BuildTarget):
# See our FAQ for more detailed rationale:
# https://mesonbuild.com/FAQ.html#why-does-building-my-project-with-msvc-output-static-libraries-called-libfooa
if not hasattr(self, 'prefix'):
- self.prefix = 'lib'
+ prefix = 'lib'
if not hasattr(self, 'suffix'):
if self.uses_rust():
if self.rust_crate_type == 'rlib':
# default Rust static library suffix
- self.suffix = 'rlib'
+ suffix = 'rlib'
elif self.rust_crate_type == 'staticlib':
- self.suffix = 'a'
+ suffix = 'a'
+ elif self.environment.machines[self.for_machine].is_os2() and self.environment.coredata.optstore.get_value_for(OptionKey('os2_emxomf')):
+ suffix = 'lib'
else:
- if 'c' in self.compilers and self.compilers['c'].get_id() == 'tasking':
- self.suffix = 'ma' if self.options.get_value('b_lto') and not self.prelink else 'a'
- else:
- self.suffix = 'a'
- self.filename = self.prefix + self.name + '.' + self.suffix
- self.outputs[0] = self.filename
+ suffix = 'a'
+ if 'c' in self.compilers and self.compilers['c'].get_id() == 'tasking' and not self.prelink:
+ key = OptionKey('b_lto', self.subproject, self.for_machine)
+ v = self.environment.coredata.get_option_for_target(self, key)
+ assert isinstance(v, bool), 'for mypy'
+ if v:
+ suffix = 'ma'
+ return (prefix, suffix)
def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]:
return {}
@@ -2213,26 +2389,9 @@ class StaticLibrary(BuildTarget):
return self.environment.get_static_lib_dir(), '{libdir_static}'
def type_suffix(self):
- return "@sta"
+ return "@rlib" if self.uses_rust_abi() else "@sta"
- def process_kwargs(self, kwargs):
- super().process_kwargs(kwargs)
-
- rust_abi = kwargs.get('rust_abi')
- rust_crate_type = kwargs.get('rust_crate_type')
- if rust_crate_type:
- if rust_abi:
- raise InvalidArguments('rust_abi and rust_crate_type are mutually exclusive.')
- if rust_crate_type == 'lib':
- self.rust_crate_type = 'rlib'
- elif rust_crate_type in {'rlib', 'staticlib'}:
- self.rust_crate_type = rust_crate_type
- else:
- raise InvalidArguments(f'Crate type {rust_crate_type!r} invalid for static libraries; must be "rlib" or "staticlib"')
- else:
- self.rust_crate_type = 'staticlib' if rust_abi == 'c' else 'rlib'
-
- def is_linkable_target(self):
+ def is_linkable_target(self) -> bool:
return True
def is_internal(self) -> bool:
@@ -2267,7 +2426,7 @@ class SharedLibrary(BuildTarget):
sources: T.List['SourceOutputs'],
structured_sources: T.Optional[StructuredSources],
objects: T.List[ObjectTypes],
- environment: environment.Environment,
+ environment: Environment,
compilers: T.Dict[str, 'Compiler'],
kwargs):
self.soversion: T.Optional[str] = None
@@ -2275,12 +2434,14 @@ class SharedLibrary(BuildTarget):
# Max length 2, first element is compatibility_version, second is current_version
self.darwin_versions: T.Optional[T.Tuple[str, str]] = None
self.vs_module_defs = None
+ self.shortname: T.Optional[str] = None
# The import library this target will generate
self.import_filename = None
# The debugging information file this target will generate
self.debug_filename = None
# Use by the pkgconfig module
self.shared_library_only = False
+ self.rust_crate_type = kwargs.get('rust_crate_type', 'dylib')
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
@@ -2315,23 +2476,18 @@ class SharedLibrary(BuildTarget):
def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, None]]:
return self.environment.get_shared_lib_dir(), '{libdir_shared}'
- def determine_filenames(self):
- """
- See https://github.com/mesonbuild/meson/pull/417 for details.
-
- First we determine the filename template (self.filename_tpl), then we
- set the output filename (self.filename).
-
- The template is needed while creating aliases (self.get_aliases),
- which are needed while generating .so shared libraries for Linux.
-
- Besides this, there's also the import library name (self.import_filename),
- which is only used on Windows since on that platform the linker uses a
- separate library called the "import library" during linking instead of
- the shared library (DLL).
- """
- prefix = ''
- suffix = ''
+ def determine_naming_info(self) -> T.Tuple[str, str, str, str, bool]:
+ scheme = self.environment.coredata.get_option_for_target(self, 'namingscheme')
+ assert isinstance(scheme, str), 'for mypy'
+ if scheme == 'platform':
+ schemename = self.get_platform_scheme_name()
+ prefix, suffix, import_suffix = DEFAULT_SHARED_LIBRARY_NAMES[schemename]
+ else:
+ prefix = None
+ suffix = None
+ import_suffix = None
+ filename_tpl = self.basic_filename_tpl
+ create_debug_file = False
create_debug_file = False
self.filename_tpl = self.basic_filename_tpl
import_filename_tpl = None
@@ -2340,82 +2496,125 @@ class SharedLibrary(BuildTarget):
if 'cs' in self.compilers:
prefix = ''
suffix = 'dll'
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
create_debug_file = True
# C, C++, Swift, Vala
- # Only Windows uses a separate import library for linking
+ # Only Windows and OS/2 uses a separate import library for linking
# For all other targets/platforms import_filename stays None
elif self.environment.machines[self.for_machine].is_windows():
- suffix = 'dll'
+ suffix = suffix if suffix is not None else 'dll'
if self.uses_rust():
# Shared library is of the form foo.dll
- prefix = ''
+ prefix = prefix if prefix is not None else ''
# Import library is called foo.dll.lib
import_filename_tpl = '{0.prefix}{0.name}.dll.lib'
# .pdb file is only created when debug symbols are enabled
create_debug_file = self.environment.coredata.optstore.get_value_for(OptionKey("debug"))
elif self.get_using_msvc():
# Shared library is of the form foo.dll
- prefix = ''
+ prefix = prefix if prefix is not None else ''
# Import library is called foo.lib
- import_filename_tpl = '{0.prefix}{0.name}.lib'
+ import_suffix = import_suffix if import_suffix is not None else 'lib'
+ import_filename_tpl = '{0.prefix}{0.name}.' + import_suffix
# .pdb file is only created when debug symbols are enabled
create_debug_file = self.environment.coredata.optstore.get_value_for(OptionKey("debug"))
# Assume GCC-compatible naming
else:
# Shared library is of the form libfoo.dll
- prefix = 'lib'
+ prefix = prefix if prefix is not None else 'lib'
# Import library is called libfoo.dll.a
- import_filename_tpl = '{0.prefix}{0.name}.dll.a'
+ import_suffix = import_suffix if import_suffix is not None else '.dll.a'
+ import_filename_tpl = '{0.prefix}{0.name}' + import_suffix
# Shared library has the soversion if it is defined
if self.soversion:
- self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
else:
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
elif self.environment.machines[self.for_machine].is_cygwin():
suffix = 'dll'
# Shared library is of the form cygfoo.dll
# (ld --dll-search-prefix=cyg is the default)
prefix = 'cyg'
# Import library is called libfoo.dll.a
+ import_suffix = import_suffix if import_suffix is not None else '.dll.a'
import_prefix = self.prefix if self.prefix is not None else 'lib'
- import_filename_tpl = import_prefix + '{0.name}.dll.a'
+ import_filename_tpl = import_prefix + '{0.name}' + import_suffix
if self.soversion:
- self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
else:
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
elif self.environment.machines[self.for_machine].is_darwin():
- prefix = 'lib'
- suffix = 'dylib'
+ prefix = prefix if prefix is not None else 'lib'
+ suffix = suffix if suffix is not None else 'dylib'
# On macOS, the filename can only contain the major version
if self.soversion:
# libfoo.X.dylib
- self.filename_tpl = '{0.prefix}{0.name}.{0.soversion}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.soversion}.{0.suffix}'
else:
# libfoo.dylib
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
elif self.environment.machines[self.for_machine].is_android():
- prefix = 'lib'
- suffix = 'so'
+ prefix = prefix if prefix is not None else 'lib'
+ suffix = suffix if suffix is not None else 'so'
# Android doesn't support shared_library versioning
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ elif self.environment.machines[self.for_machine].is_os2():
+ # Shared library is of the form foo.dll
+ prefix = prefix if prefix is not None else ''
+ suffix = suffix if suffix is not None else 'dll'
+ # Import library is called foo_dll.a or foo_dll.lib
+ if import_suffix is None:
+ import_suffix = '_dll.lib' if self.environment.coredata.optstore.get_value_for(OptionKey('os2_emxomf')) else '_dll.a'
+ import_filename_tpl = '{0.prefix}{0.name}' + import_suffix
+ filename_tpl = '{0.shortname}' if self.shortname else '{0.prefix}{0.name}'
+ if self.soversion:
+ # fooX.dll
+ filename_tpl += '{0.soversion}'
+ filename_tpl += '.{0.suffix}'
else:
- prefix = 'lib'
- suffix = 'so'
+ prefix = prefix if prefix is not None else 'lib'
+ suffix = suffix if suffix is not None else 'so'
if self.ltversion:
# libfoo.so.X[.Y[.Z]] (.Y and .Z are optional)
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.ltversion}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.ltversion}'
elif self.soversion:
# libfoo.so.X
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.soversion}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.soversion}'
else:
# No versioning, libfoo.so
- self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ return (prefix, suffix, filename_tpl, import_filename_tpl, create_debug_file)
+
+ def determine_filenames(self):
+ """
+ See https://github.com/mesonbuild/meson/pull/417 for details.
+
+ First we determine the filename template (self.filename_tpl), then we
+ set the output filename (self.filename).
+
+ The template is needed while creating aliases (self.get_aliases),
+ which are needed while generating .so shared libraries for Linux.
+
+ Besides this, there's also the import library name (self.import_filename),
+ which is only used on Windows and OS/2 since on that platform the linker uses a
+ separate library called the "import library" during linking instead of
+ the shared library (DLL).
+ """
+ prefix, suffix, filename_tpl, import_filename_tpl, create_debug_file = self.determine_naming_info()
if self.prefix is None:
self.prefix = prefix
if self.suffix is None:
self.suffix = suffix
+ self.filename_tpl = filename_tpl
self.filename = self.filename_tpl.format(self)
+ if self.environment.machines[self.for_machine].is_os2():
+ # OS/2 does not allow a longer DLL name than 8 chars
+ name = os.path.splitext(self.filename)[0]
+ if len(name) > 8:
+ name = name[:8]
+ if self.soversion:
+ name = name[:-len(self.soversion)] + self.soversion
+ self.filename = '{}.{}'.format(name, self.suffix)
if import_filename_tpl:
self.import_filename = import_filename_tpl.format(self)
# There may have been more outputs added by the time we get here, so
@@ -2424,20 +2623,20 @@ class SharedLibrary(BuildTarget):
if create_debug_file:
self.debug_filename = os.path.splitext(self.filename)[0] + '.pdb'
- def process_kwargs(self, kwargs):
+ def process_kwargs(self, kwargs: SharedLibraryKeywordArguments) -> None:
super().process_kwargs(kwargs)
if not self.environment.machines[self.for_machine].is_android():
# Shared library version
- self.ltversion = T.cast('T.Optional[str]', kwargs.get('version'))
- self.soversion = T.cast('T.Optional[str]', kwargs.get('soversion'))
+ self.ltversion = kwargs.get('version')
+ self.soversion = kwargs.get('soversion')
if self.soversion is None and self.ltversion is not None:
# library version is defined, get the soversion from that
# We replicate what Autotools does here and take the first
# number of the version by default.
self.soversion = self.ltversion.split('.')[0]
# macOS, iOS and tvOS dylib compatibility_version and current_version
- self.darwin_versions = T.cast('T.Optional[T.Tuple[str, str]]', kwargs.get('darwin_versions'))
+ self.darwin_versions = kwargs.get('darwin_versions')
if self.darwin_versions is None and self.soversion is not None:
# If unspecified, pick the soversion
self.darwin_versions = (self.soversion, self.soversion)
@@ -2445,19 +2644,8 @@ class SharedLibrary(BuildTarget):
# Visual Studio module-definitions file
self.process_vs_module_defs_kw(kwargs)
- rust_abi = kwargs.get('rust_abi')
- rust_crate_type = kwargs.get('rust_crate_type')
- if rust_crate_type:
- if rust_abi:
- raise InvalidArguments('rust_abi and rust_crate_type are mutually exclusive.')
- if rust_crate_type == 'lib':
- self.rust_crate_type = 'dylib'
- elif rust_crate_type in {'dylib', 'cdylib', 'proc-macro'}:
- self.rust_crate_type = rust_crate_type
- else:
- raise InvalidArguments(f'Crate type {rust_crate_type!r} invalid for shared libraries; must be "dylib", "cdylib" or "proc-macro"')
- else:
- self.rust_crate_type = 'cdylib' if rust_abi == 'c' else 'dylib'
+ # OS/2 uses a 8.3 name for a DLL
+ self.shortname = kwargs.get('shortname')
def get_import_filename(self) -> T.Optional[str]:
"""
@@ -2510,7 +2698,7 @@ class SharedLibrary(BuildTarget):
def type_suffix(self):
return "@sha"
- def is_linkable_target(self):
+ def is_linkable_target(self) -> bool:
return True
def set_static(self, static_library: StaticLibrary) -> None:
@@ -2544,7 +2732,7 @@ class SharedModule(SharedLibrary):
sources: T.List['SourceOutputs'],
structured_sources: T.Optional[StructuredSources],
objects: T.List[ObjectTypes],
- environment: environment.Environment,
+ environment: Environment,
compilers: T.Dict[str, 'Compiler'],
kwargs):
if 'version' in kwargs:
@@ -2594,7 +2782,7 @@ class CommandBase:
subproject: str
def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.ExternalProgram, BuildTargetTypes]]) -> \
- T.List[T.Union[str, File, BuildTarget, 'CustomTarget']]:
+ T.List[T.Union[str, File, BuildTarget, CustomTarget, programs.ExternalProgram]]:
cmd = listify(cmd)
final_cmd: T.List[T.Union[str, File, BuildTarget, 'CustomTarget']] = []
for c in cmd:
@@ -2611,7 +2799,8 @@ class CommandBase:
# Can only add a dependency on an external program which we
# know the absolute path of
self.depend_files.append(File.from_absolute_file(path))
- final_cmd += c.get_command()
+ # Do NOT flatten -- it is needed for later parsing
+ final_cmd.append(c)
elif isinstance(c, (BuildTarget, CustomTarget)):
self.dependencies.append(c)
final_cmd.append(c)
@@ -2659,7 +2848,7 @@ class CustomTarget(Target, CustomTargetBase, CommandBase):
name: T.Optional[str],
subdir: str,
subproject: str,
- environment: environment.Environment,
+ environment: Environment,
command: T.Sequence[T.Union[
str, BuildTargetTypes, GeneratedList,
programs.ExternalProgram, File]],
@@ -2681,13 +2870,15 @@ class CustomTarget(Target, CustomTargetBase, CommandBase):
install_dir: T.Optional[T.List[T.Union[str, Literal[False]]]] = None,
install_mode: T.Optional[FileMode] = None,
install_tag: T.Optional[T.List[T.Optional[str]]] = None,
+ rspable: bool = False,
absolute_paths: bool = False,
backend: T.Optional['Backend'] = None,
description: str = 'Generating {} with a custom command',
+ build_subdir: str = '',
):
# TODO expose keyword arg to make MachineChoice.HOST configurable
super().__init__(name, subdir, subproject, False, MachineChoice.HOST, environment,
- install, build_always_stale)
+ install, build_always_stale, build_subdir = build_subdir)
self.sources = list(sources)
self.outputs = substitute_values(
outputs, get_filenames_templates_dict(
@@ -2713,6 +2904,9 @@ class CustomTarget(Target, CustomTargetBase, CommandBase):
# Whether to use absolute paths for all files on the commandline
self.absolute_paths = absolute_paths
+ # Whether to enable using response files for the underlying tool
+ self.rspable = rspable
+
def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, None]]:
return None, None
@@ -2866,7 +3060,7 @@ class CompileTarget(BuildTarget):
name: str,
subdir: str,
subproject: str,
- environment: environment.Environment,
+ environment: Environment,
sources: T.List['SourceOutputs'],
output_templ: str,
compiler: Compiler,
@@ -2874,7 +3068,7 @@ class CompileTarget(BuildTarget):
compile_args: T.List[str],
include_directories: T.List[IncludeDirs],
dependencies: T.List[dependencies.Dependency],
- depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]):
+ depends: T.List[BuildTargetTypes]):
compilers = {compiler.get_language(): compiler}
kwargs = {
'build_by_default': False,
@@ -2919,10 +3113,10 @@ class RunTarget(Target, CommandBase):
def __init__(self, name: str,
command: T.Sequence[T.Union[str, File, BuildTargetTypes, programs.ExternalProgram]],
- dependencies: T.Sequence[T.Union[Target, CustomTargetIndex]],
+ dependencies: T.Sequence[AnyTargetType],
subdir: str,
subproject: str,
- environment: environment.Environment,
+ environment: Environment,
env: T.Optional[EnvironmentVariables] = None,
default_env: bool = True):
# These don't produce output artifacts
@@ -2938,7 +3132,7 @@ class RunTarget(Target, CommandBase):
repr_str = "<{0} {1}: {2}>"
return repr_str.format(self.__class__.__name__, self.get_id(), self.command[0])
- def get_dependencies(self) -> T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]:
+ def get_dependencies(self) -> T.List[BuildTargetTypes]:
return self.dependencies
def get_generated_sources(self) -> T.List[GeneratedTypes]:
@@ -2969,7 +3163,7 @@ class AliasTarget(RunTarget):
typename = 'alias'
def __init__(self, name: str, dependencies: T.Sequence[Target],
- subdir: str, subproject: str, environment: environment.Environment):
+ subdir: str, subproject: str, environment: Environment):
super().__init__(name, [], dependencies, subdir, subproject, environment)
def __repr__(self):
@@ -2980,10 +3174,11 @@ class Jar(BuildTarget):
known_kwargs = known_jar_kwargs
typename = 'jar'
+ rust_crate_type = '' # type: ignore[assignment]
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'],
- objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'],
+ objects, environment: Environment, compilers: T.Dict[str, 'Compiler'],
kwargs):
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
@@ -3001,10 +3196,10 @@ class Jar(BuildTarget):
self.main_class = kwargs.get('main_class', '')
self.java_resources: T.Optional[StructuredSources] = kwargs.get('java_resources', None)
- def get_main_class(self):
+ def get_main_class(self) -> str:
return self.main_class
- def type_suffix(self):
+ def type_suffix(self) -> str:
return "@jar"
def get_java_args(self):
@@ -3020,7 +3215,7 @@ class Jar(BuildTarget):
def is_linkable_target(self):
return True
- def get_classpath_args(self):
+ def get_classpath_args(self) -> T.List[str]:
cp_paths = [os.path.join(l.get_subdir(), l.get_filename()) for l in self.link_targets]
cp_string = os.pathsep.join(cp_paths)
if cp_string:
@@ -3051,6 +3246,14 @@ class CustomTargetIndex(CustomTargetBase, HoldableObject):
def name(self) -> str:
return f'{self.target.name}[{self.output}]'
+ @property
+ def depend_files(self) -> T.List[File]:
+ return self.target.depend_files
+
+ @property
+ def subdir(self) -> str:
+ return self.target.subdir
+
def __repr__(self):
return '<CustomTargetIndex: {!r}[{}]>'.format(self.target, self.output)
@@ -3060,6 +3263,12 @@ class CustomTargetIndex(CustomTargetBase, HoldableObject):
def get_subdir(self) -> str:
return self.target.get_subdir()
+ def get_build_subdir(self) -> str:
+ return self.target.get_build_subdir()
+
+ def get_builddir(self) -> str:
+ return self.target.get_builddir()
+
def get_filename(self) -> str:
return self.output
@@ -3104,6 +3313,10 @@ class CustomTargetIndex(CustomTargetBase, HoldableObject):
def get_custom_install_dir(self) -> T.List[T.Union[str, Literal[False]]]:
return self.target.get_custom_install_dir()
+ def get_basename(self) -> str:
+ return self.target.get_basename()
+
+
class ConfigurationData(HoldableObject):
def __init__(self, initial_values: T.Optional[T.Union[
T.Dict[str, T.Tuple[T.Union[str, int, bool], T.Optional[str]]],
@@ -3129,6 +3342,18 @@ class ConfigurationData(HoldableObject):
def keys(self) -> T.Iterator[str]:
return self.values.keys()
+class OverrideExecutable(Executable):
+ def __init__(self, executable: Executable, version: str):
+ self._executable = executable
+ self._version = version
+
+ def __getattr__(self, name: str) -> T.Any:
+ _executable = object.__getattribute__(self, '_executable')
+ return getattr(_executable, name)
+
+ def get_version(self, interpreter: T.Optional[Interpreter] = None) -> str:
+ return self._version
+
# A bit poorly named, but this represents plain data files to copy
# during install.
@dataclass(eq=False)
diff --git a/mesonbuild/cargo/__init__.py b/mesonbuild/cargo/__init__.py
index 0a4d5f2..c5b157f 100644
--- a/mesonbuild/cargo/__init__.py
+++ b/mesonbuild/cargo/__init__.py
@@ -1,6 +1,7 @@
__all__ = [
'Interpreter',
- 'load_wraps',
+ 'TomlImplementationMissing',
]
-from .interpreter import Interpreter, load_wraps
+from .interpreter import Interpreter
+from .toml import TomlImplementationMissing
diff --git a/mesonbuild/cargo/builder.py b/mesonbuild/cargo/builder.py
index 112c7c5..16abe26 100644
--- a/mesonbuild/cargo/builder.py
+++ b/mesonbuild/cargo/builder.py
@@ -44,7 +44,7 @@ class Builder:
:param value: the value of the string
:return: A StringNode
"""
- return mparser.StringNode(self._token('string', value))
+ return mparser.StringNode(self._token('string', value), escape=False)
def number(self, value: int) -> mparser.NumberNode:
"""Build A NumberNode
@@ -162,7 +162,7 @@ class Builder:
:param rhs: the right hand side of the "not in"
:return: A comparison node
"""
- return mparser.ComparisonNode('notin', lhs, self._symbol('not in'), rhs)
+ return mparser.ComparisonNode('not in', lhs, self._symbol('not in'), rhs)
def or_(self, lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.OrNode:
"""Create and OrNode
@@ -202,7 +202,7 @@ class Builder:
:param rhs: The right of the addition
:return: The ArithmeticNode
"""
- return mparser.ArithmeticNode('add', lhs, self._symbol('+'), rhs)
+ return mparser.ArithmeticNode('+', lhs, self._symbol('+'), rhs)
def plusassign(self, value: mparser.BaseNode, varname: str) -> mparser.PlusAssignmentNode:
"""Create a "+=" node
diff --git a/mesonbuild/cargo/cfg.py b/mesonbuild/cargo/cfg.py
index 0d49527..a0ee6e2 100644
--- a/mesonbuild/cargo/cfg.py
+++ b/mesonbuild/cargo/cfg.py
@@ -4,6 +4,7 @@
"""Rust CFG parser.
Rust uses its `cfg()` format in cargo.
+https://doc.rust-lang.org/reference/conditional-compilation.html
This may have the following functions:
- all()
@@ -22,18 +23,15 @@ so you could have examples like:
from __future__ import annotations
import dataclasses
import enum
-import functools
import typing as T
-from . import builder
-from .. import mparser
from ..mesonlib import MesonBugException
if T.TYPE_CHECKING:
_T = T.TypeVar('_T')
_LEX_TOKEN = T.Tuple['TokenType', T.Optional[str]]
- _LEX_STREAM = T.Iterable[_LEX_TOKEN]
+ _LEX_STREAM = T.Iterator[_LEX_TOKEN]
_LEX_STREAM_AH = T.Iterator[T.Tuple[_LEX_TOKEN, T.Optional[_LEX_TOKEN]]]
@@ -48,6 +46,7 @@ class TokenType(enum.Enum):
NOT = enum.auto()
COMMA = enum.auto()
EQUAL = enum.auto()
+ CFG = enum.auto()
def lexer(raw: str) -> _LEX_STREAM:
@@ -56,45 +55,41 @@ def lexer(raw: str) -> _LEX_STREAM:
:param raw: The raw cfg() expression
:return: An iterable of tokens
"""
- buffer: T.List[str] = []
+ start: int = 0
is_string: bool = False
- for s in raw:
- if s.isspace() or s in {')', '(', ',', '='} or (s == '"' and buffer):
- val = ''.join(buffer)
- buffer.clear()
- if is_string:
+ for i, s in enumerate(raw):
+ if s.isspace() or s in {')', '(', ',', '=', '"'}:
+ val = raw[start:i]
+ start = i + 1
+ if s == '"' and is_string:
yield (TokenType.STRING, val)
+ is_string = False
+ continue
elif val == 'any':
yield (TokenType.ANY, None)
elif val == 'all':
yield (TokenType.ALL, None)
elif val == 'not':
yield (TokenType.NOT, None)
+ elif val == 'cfg':
+ yield (TokenType.CFG, None)
elif val:
yield (TokenType.IDENTIFIER, val)
if s == '(':
yield (TokenType.LPAREN, None)
- continue
elif s == ')':
yield (TokenType.RPAREN, None)
- continue
elif s == ',':
yield (TokenType.COMMA, None)
- continue
elif s == '=':
yield (TokenType.EQUAL, None)
- continue
- elif s.isspace():
- continue
-
- if s == '"':
- is_string = not is_string
- else:
- buffer.append(s)
- if buffer:
+ elif s == '"':
+ is_string = True
+ val = raw[start:]
+ if val:
# This should always be an identifier
- yield (TokenType.IDENTIFIER, ''.join(buffer))
+ yield (TokenType.IDENTIFIER, val)
def lookahead(iter: T.Iterator[_T]) -> T.Iterator[T.Tuple[_T, T.Optional[_T]]]:
@@ -146,8 +141,8 @@ class Identifier(IR):
@dataclasses.dataclass
class Equal(IR):
- lhs: IR
- rhs: IR
+ lhs: Identifier
+ rhs: String
@dataclasses.dataclass
@@ -175,41 +170,40 @@ def _parse(ast: _LEX_STREAM_AH) -> IR:
else:
ntoken, _ = (None, None)
- stream: T.List[_LEX_TOKEN]
if token is TokenType.IDENTIFIER:
+ assert value
+ id_ = Identifier(value)
if ntoken is TokenType.EQUAL:
- return Equal(Identifier(value), _parse(ast))
- if token is TokenType.STRING:
- return String(value)
- if token is TokenType.EQUAL:
- # In this case the previous caller already has handled the equal
- return _parse(ast)
- if token in {TokenType.ANY, TokenType.ALL}:
+ next(ast)
+ (token, value), _ = next(ast)
+ assert token is TokenType.STRING
+ assert value is not None
+ return Equal(id_, String(value))
+ return id_
+ elif token in {TokenType.ANY, TokenType.ALL}:
type_ = All if token is TokenType.ALL else Any
- assert ntoken is TokenType.LPAREN
- next(ast) # advance the iterator to get rid of the LPAREN
- stream = []
args: T.List[IR] = []
- while token is not TokenType.RPAREN:
+ (token, value), n_stream = next(ast)
+ assert token is TokenType.LPAREN
+ if n_stream and n_stream[0] == TokenType.RPAREN:
+ return type_(args)
+ while True:
+ args.append(_parse(ast))
(token, value), _ = next(ast)
- if token is TokenType.COMMA:
- args.append(_parse(lookahead(iter(stream))))
- stream.clear()
- else:
- stream.append((token, value))
- if stream:
- args.append(_parse(lookahead(iter(stream))))
+ if token is TokenType.RPAREN:
+ break
+ assert token is TokenType.COMMA
return type_(args)
- if token is TokenType.NOT:
- next(ast) # advance the iterator to get rid of the LPAREN
- stream = []
- # Mypy can't figure out that token is overridden inside the while loop
- while token is not TokenType.RPAREN: # type: ignore
- (token, value), _ = next(ast)
- stream.append((token, value))
- return Not(_parse(lookahead(iter(stream))))
-
- raise MesonBugException(f'Unhandled Cargo token: {token}')
+ elif token in {TokenType.NOT, TokenType.CFG}:
+ is_not = token is TokenType.NOT
+ (token, value), _ = next(ast)
+ assert token is TokenType.LPAREN
+ arg = _parse(ast)
+ (token, value), _ = next(ast)
+ assert token is TokenType.RPAREN
+ return Not(arg) if is_not else arg
+ else:
+ raise MesonBugException(f'Unhandled Cargo token:{token} {value}')
def parse(ast: _LEX_STREAM) -> IR:
@@ -218,57 +212,24 @@ def parse(ast: _LEX_STREAM) -> IR:
:param ast: An iterable of Tokens
:return: An mparser Node to be used as a conditional
"""
- ast_i: _LEX_STREAM_AH = lookahead(iter(ast))
+ ast_i: _LEX_STREAM_AH = lookahead(ast)
return _parse(ast_i)
-@functools.singledispatch
-def ir_to_meson(ir: T.Any, build: builder.Builder) -> mparser.BaseNode:
- raise NotImplementedError
-
-
-@ir_to_meson.register
-def _(ir: String, build: builder.Builder) -> mparser.BaseNode:
- return build.string(ir.value)
-
-
-@ir_to_meson.register
-def _(ir: Identifier, build: builder.Builder) -> mparser.BaseNode:
- host_machine = build.identifier('host_machine')
- if ir.value == "target_arch":
- return build.method('cpu_family', host_machine)
- elif ir.value in {"target_os", "target_family"}:
- return build.method('system', host_machine)
- elif ir.value == "target_endian":
- return build.method('endian', host_machine)
- raise MesonBugException(f"Unhandled Cargo identifier: {ir.value}")
-
-
-@ir_to_meson.register
-def _(ir: Equal, build: builder.Builder) -> mparser.BaseNode:
- return build.equal(ir_to_meson(ir.lhs, build), ir_to_meson(ir.rhs, build))
-
-
-@ir_to_meson.register
-def _(ir: Not, build: builder.Builder) -> mparser.BaseNode:
- return build.not_(ir_to_meson(ir.value, build))
-
-
-@ir_to_meson.register
-def _(ir: Any, build: builder.Builder) -> mparser.BaseNode:
- args = iter(reversed(ir.args))
- last = next(args)
- cur = build.or_(ir_to_meson(next(args), build), ir_to_meson(last, build))
- for a in args:
- cur = build.or_(ir_to_meson(a, build), cur)
- return cur
+def _eval_cfg(ir: IR, cfgs: T.Dict[str, str]) -> bool:
+ if isinstance(ir, Identifier):
+ return ir.value in cfgs
+ elif isinstance(ir, Equal):
+ return cfgs.get(ir.lhs.value) == ir.rhs.value
+ elif isinstance(ir, Not):
+ return not _eval_cfg(ir.value, cfgs)
+ elif isinstance(ir, Any):
+ return any(_eval_cfg(i, cfgs) for i in ir.args)
+ elif isinstance(ir, All):
+ return all(_eval_cfg(i, cfgs) for i in ir.args)
+ else:
+ raise MesonBugException(f'Unhandled Cargo cfg IR: {ir}')
-@ir_to_meson.register
-def _(ir: All, build: builder.Builder) -> mparser.BaseNode:
- args = iter(reversed(ir.args))
- last = next(args)
- cur = build.and_(ir_to_meson(next(args), build), ir_to_meson(last, build))
- for a in args:
- cur = build.and_(ir_to_meson(a, build), cur)
- return cur
+def eval_cfg(raw: str, cfgs: T.Dict[str, str]) -> bool:
+ return _eval_cfg(parse(lexer(raw)), cfgs)
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index af272a8..e262986 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -11,460 +11,175 @@ port will be required.
from __future__ import annotations
import dataclasses
-import importlib
-import json
+import functools
import os
-import shutil
+import pathlib
import collections
import urllib.parse
-import itertools
import typing as T
+from pathlib import PurePath
-from . import builder
-from . import version
-from ..mesonlib import MesonException, Popen_safe
+from . import builder, version
+from .cfg import eval_cfg
+from .toml import load_toml
+from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname
+from ..mesonlib import is_parent_path, MesonException, MachineChoice, version_compare
from .. import coredata, mlog
from ..wrap.wrap import PackageDefinition
if T.TYPE_CHECKING:
- from types import ModuleType
-
- from typing_extensions import Protocol, Self
-
- from . import manifest
+ from . import raw
from .. import mparser
+ from .manifest import Dependency, SystemDependency
from ..environment import Environment
from ..interpreterbase import SubProject
+ from ..compilers.rust import RustCompiler
- # Copied from typeshed. Blarg that they don't expose this
- class DataclassInstance(Protocol):
- __dataclass_fields__: T.ClassVar[dict[str, dataclasses.Field[T.Any]]]
-
- _UnknownKeysT = T.TypeVar('_UnknownKeysT', manifest.FixedPackage,
- manifest.FixedDependency, manifest.FixedLibTarget,
- manifest.FixedBuildTarget)
-
-
-# tomllib is present in python 3.11, before that it is a pypi module called tomli,
-# we try to import tomllib, then tomli,
-# TODO: add a fallback to toml2json?
-tomllib: T.Optional[ModuleType] = None
-toml2json: T.Optional[str] = None
-for t in ['tomllib', 'tomli']:
- try:
- tomllib = importlib.import_module(t)
- break
- except ImportError:
- pass
-else:
- # TODO: it would be better to use an Executable here, which could be looked
- # up in the cross file or provided by a wrap. However, that will have to be
- # passed in externally, since we don't have (and I don't think we should),
- # have access to the `Environment` for that in this module.
- toml2json = shutil.which('toml2json')
-
-
-_EXTRA_KEYS_WARNING = (
- "This may (unlikely) be an error in the cargo manifest, or may be a missing "
- "implementation in Meson. If this issue can be reproduced with the latest "
- "version of Meson, please help us by opening an issue at "
- "https://github.com/mesonbuild/meson/issues. Please include the crate and "
- "version that is generating this warning if possible."
-)
-
-class TomlImplementationMissing(MesonException):
- pass
-
-
-def load_toml(filename: str) -> T.Dict[object, object]:
- if tomllib:
- with open(filename, 'rb') as f:
- raw = tomllib.load(f)
- else:
- if toml2json is None:
- raise TomlImplementationMissing('Could not find an implementation of tomllib, nor toml2json')
-
- p, out, err = Popen_safe([toml2json, filename])
- if p.returncode != 0:
- raise MesonException('toml2json failed to decode output\n', err)
-
- raw = json.loads(out)
-
- if not isinstance(raw, dict):
- raise MesonException("Cargo.toml isn't a dictionary? How did that happen?")
-
- return raw
-
-
-def fixup_meson_varname(name: str) -> str:
- """Fixup a meson variable name
-
- :param name: The name to fix
- :return: the fixed name
- """
- return name.replace('-', '_')
-
-
-# Pylance can figure out that these do not, in fact, overlap, but mypy can't
-@T.overload
-def _fixup_raw_mappings(d: manifest.BuildTarget) -> manifest.FixedBuildTarget: ... # type: ignore
-
-@T.overload
-def _fixup_raw_mappings(d: manifest.LibTarget) -> manifest.FixedLibTarget: ... # type: ignore
-
-@T.overload
-def _fixup_raw_mappings(d: manifest.Dependency) -> manifest.FixedDependency: ...
-
-def _fixup_raw_mappings(d: T.Union[manifest.BuildTarget, manifest.LibTarget, manifest.Dependency]
- ) -> T.Union[manifest.FixedBuildTarget, manifest.FixedLibTarget,
- manifest.FixedDependency]:
- """Fixup raw cargo mappings to ones more suitable for python to consume.
-
- This does the following:
- * replaces any `-` with `_`, cargo likes the former, but python dicts make
- keys with `-` in them awkward to work with
- * Convert Dependency versions from the cargo format to something meson
- understands
-
- :param d: The mapping to fix
- :return: the fixed string
- """
- raw = {fixup_meson_varname(k): v for k, v in d.items()}
- if 'version' in raw:
- assert isinstance(raw['version'], str), 'for mypy'
- raw['version'] = version.convert(raw['version'])
- return T.cast('T.Union[manifest.FixedBuildTarget, manifest.FixedLibTarget, manifest.FixedDependency]', raw)
-
-
-def _handle_unknown_keys(data: _UnknownKeysT, cls: T.Union[DataclassInstance, T.Type[DataclassInstance]],
- msg: str) -> _UnknownKeysT:
- """Remove and warn on keys that are coming from cargo, but are unknown to
- our representations.
-
- This is intended to give users the possibility of things proceeding when a
- new key is added to Cargo.toml that we don't yet handle, but to still warn
- them that things might not work.
-
- :param data: The raw data to look at
- :param cls: The Dataclass derived type that will be created
- :param msg: the header for the error message. Usually something like "In N structure".
- :return: The original data structure, but with all unknown keys removed.
- """
- unexpected = set(data) - {x.name for x in dataclasses.fields(cls)}
- if unexpected:
- mlog.warning(msg, 'has unexpected keys', '"{}".'.format(', '.join(sorted(unexpected))),
- _EXTRA_KEYS_WARNING)
- for k in unexpected:
- # Mypy and Pyright can't prove that this is okay
- del data[k] # type: ignore[misc]
- return data
-
-
-@dataclasses.dataclass
-class Package:
-
- """Representation of a Cargo Package entry, with defaults filled in."""
-
- name: str
- version: str
- description: T.Optional[str] = None
- resolver: T.Optional[str] = None
- authors: T.List[str] = dataclasses.field(default_factory=list)
- edition: manifest.EDITION = '2015'
- rust_version: T.Optional[str] = None
- documentation: T.Optional[str] = None
- readme: T.Optional[str] = None
- homepage: T.Optional[str] = None
- repository: T.Optional[str] = None
- license: T.Optional[str] = None
- license_file: T.Optional[str] = None
- keywords: T.List[str] = dataclasses.field(default_factory=list)
- categories: T.List[str] = dataclasses.field(default_factory=list)
- workspace: T.Optional[str] = None
- build: T.Optional[str] = None
- links: T.Optional[str] = None
- exclude: T.List[str] = dataclasses.field(default_factory=list)
- include: T.List[str] = dataclasses.field(default_factory=list)
- publish: bool = True
- metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict)
- default_run: T.Optional[str] = None
- autolib: bool = True
- autobins: bool = True
- autoexamples: bool = True
- autotests: bool = True
- autobenches: bool = True
- api: str = dataclasses.field(init=False)
-
- def __post_init__(self) -> None:
- self.api = _version_to_api(self.version)
-
- @classmethod
- def from_raw(cls, raw: manifest.Package) -> Self:
- pkg = T.cast('manifest.FixedPackage',
- {fixup_meson_varname(k): v for k, v in raw.items()})
- pkg = _handle_unknown_keys(pkg, cls, f'Package entry {pkg["name"]}')
- return cls(**pkg)
-
-@dataclasses.dataclass
-class SystemDependency:
-
- """ Representation of a Cargo system-deps entry
- https://docs.rs/system-deps/latest/system_deps
- """
-
- name: str
- version: T.List[str]
- optional: bool = False
- feature: T.Optional[str] = None
- feature_overrides: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict)
-
- @classmethod
- def from_raw(cls, name: str, raw: T.Any) -> SystemDependency:
- if isinstance(raw, str):
- return cls(name, SystemDependency.convert_version(raw))
- name = raw.get('name', name)
- version = SystemDependency.convert_version(raw.get('version'))
- optional = raw.get('optional', False)
- feature = raw.get('feature')
- # Everything else are overrides when certain features are enabled.
- feature_overrides = {k: v for k, v in raw.items() if k not in {'name', 'version', 'optional', 'feature'}}
- return cls(name, version, optional, feature, feature_overrides)
-
- @staticmethod
- def convert_version(version: T.Optional[str]) -> T.List[str]:
- vers = version.split(',') if version is not None else []
- result: T.List[str] = []
- for v in vers:
- v = v.strip()
- if v[0] not in '><=':
- v = f'>={v}'
- result.append(v)
- return result
-
- def enabled(self, features: T.Set[str]) -> bool:
- return self.feature is None or self.feature in features
-
-@dataclasses.dataclass
-class Dependency:
-
- """Representation of a Cargo Dependency Entry."""
-
- name: dataclasses.InitVar[str]
- version: T.List[str]
- registry: T.Optional[str] = None
- git: T.Optional[str] = None
- branch: T.Optional[str] = None
- rev: T.Optional[str] = None
- path: T.Optional[str] = None
- optional: bool = False
- package: str = ''
- default_features: bool = True
- features: T.List[str] = dataclasses.field(default_factory=list)
- api: str = dataclasses.field(init=False)
-
- def __post_init__(self, name: str) -> None:
- self.package = self.package or name
- # Extract wanted API version from version constraints.
- api = set()
- for v in self.version:
- if v.startswith(('>=', '==')):
- api.add(_version_to_api(v[2:].strip()))
- elif v.startswith('='):
- api.add(_version_to_api(v[1:].strip()))
- if not api:
- self.api = '0'
- elif len(api) == 1:
- self.api = api.pop()
- else:
- raise MesonException(f'Cannot determine minimum API version from {self.version}.')
-
- @classmethod
- def from_raw(cls, name: str, raw: manifest.DependencyV) -> Dependency:
- """Create a dependency from a raw cargo dictionary"""
- if isinstance(raw, str):
- return cls(name, version.convert(raw))
- fixed = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Dependency entry {name}')
- return cls(name, **fixed)
-
-
-@dataclasses.dataclass
-class BuildTarget:
-
- name: str
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
- path: dataclasses.InitVar[T.Optional[str]] = None
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-test-field
- # True for lib, bin, test
- test: bool = True
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doctest-field
- # True for lib
- doctest: bool = False
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-bench-field
- # True for lib, bin, benchmark
- bench: bool = True
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doc-field
- # True for libraries and binaries
- doc: bool = False
-
- harness: bool = True
- edition: manifest.EDITION = '2015'
- required_features: T.List[str] = dataclasses.field(default_factory=list)
- plugin: bool = False
-
- @classmethod
- def from_raw(cls, raw: manifest.BuildTarget) -> Self:
- name = raw.get('name', '<anonymous>')
- build = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Binary entry {name}')
- return cls(**build)
-
-@dataclasses.dataclass
-class Library(BuildTarget):
+ from typing_extensions import Literal
- """Representation of a Cargo Library Entry."""
+def _dependency_name(package_name: str, api: str, suffix: str = '-rs') -> str:
+ basename = package_name[:-len(suffix)] if suffix and package_name.endswith(suffix) else package_name
+ return f'{basename}-{api}{suffix}'
- doctest: bool = True
- doc: bool = True
- path: str = os.path.join('src', 'lib.rs')
- proc_macro: bool = False
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
- doc_scrape_examples: bool = True
- @classmethod
- def from_raw(cls, raw: manifest.LibTarget, fallback_name: str) -> Self: # type: ignore[override]
- fixed = _fixup_raw_mappings(raw)
+def _dependency_varname(dep: Dependency) -> str:
+ return f'{fixup_meson_varname(dep.package)}_{(dep.api.replace(".", "_"))}_dep'
- # We need to set the name field if it's not set manually, including if
- # other fields are set in the lib section
- if 'name' not in fixed:
- fixed['name'] = fallback_name
- fixed = _handle_unknown_keys(fixed, cls, f'Library entry {fixed["name"]}')
- return cls(**fixed)
+def _library_name(name: str, api: str, lib_type: Literal['rust', 'c', 'proc-macro'] = 'rust') -> str:
+ # Add the API version to the library name to avoid conflicts when multiple
+ # versions of the same crate are used. The Ninja backend removed everything
+ # after the + to form the crate name.
+ if lib_type == 'c':
+ return name
+ return f'{name}+{api.replace(".", "_")}'
-@dataclasses.dataclass
-class Binary(BuildTarget):
-
- """Representation of a Cargo Bin Entry."""
-
- doc: bool = True
-
-
-@dataclasses.dataclass
-class Test(BuildTarget):
+def _extra_args_varname() -> str:
+ return 'extra_args'
- """Representation of a Cargo Test Entry."""
- bench: bool = True
+def _extra_deps_varname() -> str:
+ return 'extra_deps'
@dataclasses.dataclass
-class Benchmark(BuildTarget):
-
- """Representation of a Cargo Benchmark Entry."""
-
- test: bool = True
+class PackageConfiguration:
+ """Configuration for a package during dependency resolution."""
+ features: T.Set[str] = dataclasses.field(default_factory=set)
+ required_deps: T.Set[str] = dataclasses.field(default_factory=set)
+ optional_deps_features: T.Dict[str, T.Set[str]] = dataclasses.field(default_factory=lambda: collections.defaultdict(set))
+ # Cache of resolved dependency packages
+ dep_packages: T.Dict[PackageKey, PackageState] = dataclasses.field(default_factory=dict)
+
+ def get_features_args(self) -> T.List[str]:
+ """Get feature configuration arguments."""
+ args: T.List[str] = []
+ for feature in self.features:
+ args.extend(['--cfg', f'feature="{feature}"'])
+ return args
+
+ def get_dependency_map(self, manifest: Manifest) -> T.Dict[str, str]:
+ """Get the rust dependency mapping for this package configuration."""
+ dependency_map: T.Dict[str, str] = {}
+ for name in self.required_deps:
+ dep = manifest.dependencies[name]
+ dep_key = PackageKey(dep.package, dep.api)
+ dep_pkg = self.dep_packages[dep_key]
+ dep_lib_name = _library_name(dep_pkg.manifest.lib.name, dep_pkg.manifest.package.api)
+ dep_crate_name = name if name != dep.package else dep_pkg.manifest.lib.name
+ dependency_map[dep_lib_name] = dep_crate_name
+ return dependency_map
@dataclasses.dataclass
-class Example(BuildTarget):
+class PackageState:
+ manifest: Manifest
+ downloaded: bool = False
+ # If this package is member of a workspace.
+ ws_subdir: T.Optional[str] = None
+ ws_member: T.Optional[str] = None
+ # Package configuration state
+ cfg: T.Optional[PackageConfiguration] = None
+
+ def get_env_dict(self, environment: Environment, subdir: str) -> T.Dict[str, str]:
+ """Get environment variables for this package."""
+ # Common variables for build.rs and crates
+ # https://doc.rust-lang.org/cargo/reference/environment-variables.html
+ # OUT_DIR is the directory where build.rs generate files. In our case,
+ # it's the directory where meson/meson.build places generated files.
+ out_dir = os.path.join(environment.build_dir, subdir, 'meson')
+ os.makedirs(out_dir, exist_ok=True)
+ version_arr = self.manifest.package.version.split('.')
+ version_arr += [''] * (4 - len(version_arr))
+
+ return {
+ 'OUT_DIR': out_dir,
+ 'CARGO_MANIFEST_DIR': os.path.join(environment.source_dir, subdir),
+ 'CARGO_MANIFEST_PATH': os.path.join(environment.source_dir, subdir, 'Cargo.toml'),
+ 'CARGO_PKG_VERSION': self.manifest.package.version,
+ 'CARGO_PKG_VERSION_MAJOR': version_arr[0],
+ 'CARGO_PKG_VERSION_MINOR': version_arr[1],
+ 'CARGO_PKG_VERSION_PATCH': version_arr[2],
+ 'CARGO_PKG_VERSION_PRE': version_arr[3],
+ 'CARGO_PKG_AUTHORS': ','.join(self.manifest.package.authors),
+ 'CARGO_PKG_NAME': self.manifest.package.name,
+ # FIXME: description can contain newlines which breaks ninja.
+ #'CARGO_PKG_DESCRIPTION': self.manifest.package.description or '',
+ 'CARGO_PKG_HOMEPAGE': self.manifest.package.homepage or '',
+ 'CARGO_PKG_REPOSITORY': self.manifest.package.repository or '',
+ 'CARGO_PKG_LICENSE': self.manifest.package.license or '',
+ 'CARGO_PKG_LICENSE_FILE': self.manifest.package.license_file or '',
+ 'CARGO_PKG_RUST_VERSION': self.manifest.package.rust_version or '',
+ 'CARGO_PKG_README': self.manifest.package.readme or '',
+ 'CARGO_CRATE_NAME': fixup_meson_varname(self.manifest.package.name),
+ }
- """Representation of a Cargo Example Entry."""
+ def get_lint_args(self, rustc: RustCompiler) -> T.List[str]:
+ """Get lint arguments for this package."""
+ args: T.List[str] = []
+ has_check_cfg = rustc.has_check_cfg
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['bin'])
+ for lint in self.manifest.lints:
+ args.extend(lint.to_arguments(has_check_cfg))
+ if has_check_cfg:
+ for feature in self.manifest.features:
+ if feature != 'default':
+ args.append('--check-cfg')
+ args.append(f'cfg(feature,values("{feature}"))')
+ for name in self.manifest.system_dependencies:
+ args.append('--check-cfg')
+ args.append(f'cfg(system_deps_have_{fixup_meson_varname(name)})')
-@dataclasses.dataclass
-class Manifest:
-
- """Cargo Manifest definition.
-
- Most of these values map up to the Cargo Manifest, but with default values
- if not provided.
-
- Cargo subprojects can contain what Meson wants to treat as multiple,
- interdependent, subprojects.
-
- :param path: the path within the cargo subproject.
- """
-
- package: Package
- dependencies: T.Dict[str, Dependency]
- dev_dependencies: T.Dict[str, Dependency]
- build_dependencies: T.Dict[str, Dependency]
- system_dependencies: T.Dict[str, SystemDependency] = dataclasses.field(init=False)
- lib: Library
- bin: T.List[Binary]
- test: T.List[Test]
- bench: T.List[Benchmark]
- example: T.List[Example]
- features: T.Dict[str, T.List[str]]
- target: T.Dict[str, T.Dict[str, Dependency]]
- path: str = ''
-
- def __post_init__(self) -> None:
- self.features.setdefault('default', [])
- self.system_dependencies = {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()}
-
-
-def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = '') -> Manifest:
- return Manifest(
- Package.from_raw(raw_manifest['package']),
- {k: Dependency.from_raw(k, v) for k, v in raw_manifest.get('dependencies', {}).items()},
- {k: Dependency.from_raw(k, v) for k, v in raw_manifest.get('dev-dependencies', {}).items()},
- {k: Dependency.from_raw(k, v) for k, v in raw_manifest.get('build-dependencies', {}).items()},
- Library.from_raw(raw_manifest.get('lib', {}), raw_manifest['package']['name']),
- [Binary.from_raw(b) for b in raw_manifest.get('bin', {})],
- [Test.from_raw(b) for b in raw_manifest.get('test', {})],
- [Benchmark.from_raw(b) for b in raw_manifest.get('bench', {})],
- [Example.from_raw(b) for b in raw_manifest.get('example', {})],
- raw_manifest.get('features', {}),
- {k: {k2: Dependency.from_raw(k2, v2) for k2, v2 in v.get('dependencies', {}).items()}
- for k, v in raw_manifest.get('target', {}).items()},
- path,
- )
-
-
-def _version_to_api(version: str) -> str:
- # x.y.z -> x
- # 0.x.y -> 0.x
- # 0.0.x -> 0
- vers = version.split('.')
- if int(vers[0]) != 0:
- return vers[0]
- elif len(vers) >= 2 and int(vers[1]) != 0:
- return f'0.{vers[1]}'
- return '0'
-
-
-def _dependency_name(package_name: str, api: str) -> str:
- basename = package_name[:-3] if package_name.endswith('-rs') else package_name
- return f'{basename}-{api}-rs'
-
-
-def _dependency_varname(package_name: str) -> str:
- return f'{fixup_meson_varname(package_name)}_dep'
+ return args
+ def get_env_args(self, rustc: RustCompiler, environment: Environment, subdir: str) -> T.List[str]:
+ """Get environment variable arguments for rustc."""
+ enable_env_set_args = rustc.enable_env_set_args()
+ if enable_env_set_args is None:
+ return []
-def _extra_args_varname() -> str:
- return 'extra_args'
+ env_dict = self.get_env_dict(environment, subdir)
+ env_args = list(enable_env_set_args)
+ for k, v in env_dict.items():
+ env_args.extend(['--env-set', f'{k}={v}'])
+ return env_args
+ def get_rustc_args(self, environment: Environment, subdir: str, machine: MachineChoice) -> T.List[str]:
+ """Get rustc arguments for this package."""
+ if not environment.is_cross_build():
+ machine = MachineChoice.HOST
-def _extra_deps_varname() -> str:
- return 'extra_deps'
+ rustc = T.cast('RustCompiler', environment.coredata.compilers[machine]['rust'])
+ cfg = self.cfg
-class PackageState:
- def __init__(self, manifest: Manifest, downloaded: bool) -> None:
- self.manifest = manifest
- self.downloaded = downloaded
- self.features: T.Set[str] = set()
- self.required_deps: T.Set[str] = set()
- self.optional_deps_features: T.Dict[str, T.Set[str]] = collections.defaultdict(set)
+ args: T.List[str] = []
+ args.extend(self.get_lint_args(rustc))
+ args.extend(cfg.get_features_args())
+ args.extend(self.get_env_args(rustc, environment, subdir))
+ return args
@dataclasses.dataclass(frozen=True)
@@ -473,103 +188,315 @@ class PackageKey:
api: str
+@dataclasses.dataclass
+class WorkspaceState:
+ workspace: Workspace
+ subdir: str
+ downloaded: bool = False
+ # member path -> PackageState, for all members of this workspace
+ packages: T.Dict[str, PackageState] = dataclasses.field(default_factory=dict)
+ # package name to member path, for all members of this workspace
+ packages_to_member: T.Dict[str, str] = dataclasses.field(default_factory=dict)
+ # member paths that are required to be built
+ required_members: T.List[str] = dataclasses.field(default_factory=list)
+
+
class Interpreter:
- def __init__(self, env: Environment) -> None:
+ def __init__(self, env: Environment, subdir: str, subprojects_dir: str) -> None:
self.environment = env
+ self.subprojects_dir = subprojects_dir
# Map Cargo.toml's subdir to loaded manifest.
- self.manifests: T.Dict[str, Manifest] = {}
+ self.manifests: T.Dict[str, T.Union[Manifest, Workspace]] = {}
# Map of cargo package (name + api) to its state
self.packages: T.Dict[PackageKey, PackageState] = {}
-
- def interpret(self, subdir: str) -> mparser.CodeBlockNode:
- manifest = self._load_manifest(subdir)
- pkg, cached = self._fetch_package(manifest.package.name, manifest.package.api)
- if not cached:
- # This is an entry point, always enable the 'default' feature.
- # FIXME: We should have a Meson option similar to `cargo build --no-default-features`
+ # Map subdir to workspace
+ self.workspaces: T.Dict[str, WorkspaceState] = {}
+ # Files that should trigger a reconfigure if modified
+ self.build_def_files: T.List[str] = []
+ # Cargo packages
+ filename = os.path.join(self.environment.get_source_dir(), subdir, 'Cargo.lock')
+ subprojects_dir = os.path.join(self.environment.get_source_dir(), subprojects_dir)
+ self.cargolock = load_cargo_lock(filename, subprojects_dir)
+ if self.cargolock:
+ self.environment.wrap_resolver.merge_wraps(self.cargolock.wraps)
+ self.build_def_files.append(filename)
+
+ def get_build_def_files(self) -> T.List[str]:
+ return self.build_def_files
+
+ def _prepare_entry_point(self, ws: WorkspaceState) -> None:
+ pkgs = [self._require_workspace_member(ws, m) for m in ws.workspace.default_members]
+ for pkg in pkgs:
+ self._prepare_package(pkg)
self._enable_feature(pkg, 'default')
- # Build an AST for this package
+ def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparser.CodeBlockNode:
+ manifest, cached = self._load_manifest(subdir)
filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml')
build = builder.Builder(filename)
- ast = self._create_project(pkg, build)
- ast += [
- build.assign(build.function('import', [build.string('rust')]), 'rust'),
+ if project_root:
+ # this is a subdir()
+ assert isinstance(manifest, Manifest)
+ return self.interpret_package(manifest, build, subdir, project_root)
+
+ ws = self._get_workspace(manifest, subdir, downloaded=False)
+ if not cached:
+ self._prepare_entry_point(ws)
+ return self.interpret_workspace(ws, build, subdir)
+
+ def interpret_package(self, manifest: Manifest, build: builder.Builder, subdir: str, project_root: str) -> mparser.CodeBlockNode:
+ # Build an AST for this package
+ ws = self.workspaces[project_root]
+ member = ws.packages_to_member[manifest.package.name]
+ pkg = ws.packages[member]
+ ast = self._create_package(pkg, build, subdir)
+ return build.block(ast)
+
+ def _create_package(self, pkg: PackageState, build: builder.Builder, subdir: str) -> T.List[mparser.BaseNode]:
+ cfg = pkg.cfg
+ ast: T.List[mparser.BaseNode] = [
+ build.assign(build.array([build.string(f) for f in cfg.features]), 'features'),
build.function('message', [
build.string('Enabled features:'),
- build.array([build.string(f) for f in pkg.features]),
+ build.identifier('features'),
]),
]
ast += self._create_dependencies(pkg, build)
ast += self._create_meson_subdir(build)
- # Libs are always auto-discovered and there's no other way to handle them,
- # which is unfortunate for reproducability
- if os.path.exists(os.path.join(self.environment.source_dir, subdir, pkg.manifest.path, pkg.manifest.lib.path)):
- for crate_type in pkg.manifest.lib.crate_type:
- ast.extend(self._create_lib(pkg, build, crate_type))
+ if pkg.manifest.lib:
+ crate_type = pkg.manifest.lib.crate_type
+ if 'dylib' in crate_type and 'cdylib' in crate_type:
+ raise MesonException('Cannot build both dylib and cdylib due to file name conflict')
+ if 'proc-macro' in crate_type:
+ ast.extend(self._create_lib(pkg, build, subdir, 'proc-macro', shared=True))
+ if any(x in crate_type for x in ['lib', 'rlib', 'dylib']):
+ ast.extend(self._create_lib(pkg, build, subdir, 'rust',
+ static=('lib' in crate_type or 'rlib' in crate_type),
+ shared='dylib' in crate_type))
+ if any(x in crate_type for x in ['staticlib', 'cdylib']):
+ ast.extend(self._create_lib(pkg, build, subdir, 'c',
+ static='staticlib' in crate_type,
+ shared='cdylib' in crate_type))
+
+ return ast
+
+ def interpret_workspace(self, ws: WorkspaceState, build: builder.Builder, subdir: str) -> mparser.CodeBlockNode:
+ name = os.path.dirname(subdir)
+ subprojects_dir = os.path.join(subdir, 'subprojects')
+ self.environment.wrap_resolver.load_and_merge(subprojects_dir, T.cast('SubProject', name))
+ ast: T.List[mparser.BaseNode] = []
+
+ # Call subdir() for each required member of the workspace. The order is
+ # important, if a member depends on another member, that member must be
+ # processed first.
+ processed_members: T.Dict[str, PackageState] = {}
+ def _process_member(member: str) -> None:
+ if member in processed_members:
+ return
+ pkg = ws.packages[member]
+ cfg = pkg.cfg
+ for depname in cfg.required_deps:
+ dep = pkg.manifest.dependencies[depname]
+ if dep.path:
+ dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path))
+ _process_member(dep_member)
+ if member == '.':
+ ast.extend(self._create_package(pkg, build, subdir))
+ elif is_parent_path(self.subprojects_dir, member):
+ depname = _dependency_name(pkg.manifest.package.name, pkg.manifest.package.api)
+ ast.append(build.function('subproject', [build.string(depname)]))
+ else:
+ ast.append(build.function('subdir', [build.string(member)]))
+ processed_members[member] = pkg
+
+ ast.append(build.assign(build.function('import', [build.string('rust')]), 'rust'))
+ for member in ws.required_members:
+ _process_member(member)
+ ast = self._create_project(name, processed_members.get('.'), build) + ast
return build.block(ast)
- def _fetch_package(self, package_name: str, api: str) -> T.Tuple[PackageState, bool]:
+ def _load_workspace_member(self, ws: WorkspaceState, m: str) -> None:
+ m = os.path.normpath(m)
+ if m in ws.packages:
+ return
+ # Load member's manifest
+ m_subdir = os.path.join(ws.subdir, m)
+ manifest_, _ = self._load_manifest(m_subdir, ws.workspace, m)
+ assert isinstance(manifest_, Manifest)
+ self._add_workspace_member(manifest_, ws, m)
+
+ def _add_workspace_member(self, manifest_: Manifest, ws: WorkspaceState, m: str) -> None:
+ key = PackageKey(manifest_.package.name, manifest_.package.api)
+ ws.packages_to_member[manifest_.package.name] = m
+ if key in self.packages:
+ ws.packages[m] = self.packages[key]
+ self._require_workspace_member(ws, m)
+ else:
+ ws.packages[m] = PackageState(manifest_, ws_subdir=ws.subdir, ws_member=m, downloaded=ws.downloaded)
+
+ def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, downloaded: bool) -> WorkspaceState:
+ ws = self.workspaces.get(subdir)
+ if ws:
+ return ws
+ workspace = manifest if isinstance(manifest, Workspace) else \
+ Workspace(root_package=manifest, members=['.'], default_members=['.'])
+ ws = WorkspaceState(workspace, subdir, downloaded=downloaded)
+ if workspace.root_package:
+ self._add_workspace_member(workspace.root_package, ws, '.')
+ for m in workspace.members:
+ self._load_workspace_member(ws, m)
+ self.workspaces[subdir] = ws
+ return ws
+
+ def _record_package(self, pkg: PackageState) -> None:
+ key = PackageKey(pkg.manifest.package.name, pkg.manifest.package.api)
+ if key not in self.packages:
+ self.packages[key] = pkg
+
+ def _require_workspace_member(self, ws: WorkspaceState, member: str) -> PackageState:
+ member = os.path.normpath(member)
+ pkg = ws.packages[member]
+ if member not in ws.required_members:
+ self._record_package(pkg)
+ ws.required_members.append(member)
+ return pkg
+
+ def _fetch_package(self, package_name: str, api: str) -> PackageState:
key = PackageKey(package_name, api)
pkg = self.packages.get(key)
if pkg:
- return pkg, True
+ return pkg
meson_depname = _dependency_name(package_name, api)
- subdir, _ = self.environment.wrap_resolver.resolve(meson_depname)
+ return self._fetch_package_from_subproject(package_name, meson_depname)
+
+ def _resolve_package(self, package_name: str, version_constraints: T.List[str]) -> T.Optional[CargoLockPackage]:
+ """From all available versions from Cargo.lock, pick the most recent
+ satisfying the constraints and return it."""
+ if self.cargolock:
+ cargo_lock_pkgs = self.cargolock.named(package_name)
+ else:
+ cargo_lock_pkgs = []
+ for cargo_pkg in cargo_lock_pkgs:
+ if all(version_compare(cargo_pkg.version, v) for v in version_constraints):
+ return cargo_pkg
+
+ if not version_constraints:
+ raise MesonException(f'Cannot determine version of cargo package {package_name}')
+ return None
+
+ def _fetch_package_from_subproject(self, package_name: str, meson_depname: str) -> PackageState:
+ subp_name, _ = self.environment.wrap_resolver.find_dep_provider(meson_depname)
+ if subp_name is None:
+ if self.cargolock is None:
+ raise MesonException(f'Dependency {meson_depname!r} not found in any wrap files.')
+ # If Cargo.lock has a different version, this could be a resolution
+ # bug, but maybe also a version mismatch? I am not sure yet...
+ similar_deps = [pkg.subproject
+ for pkg in self.cargolock.named(package_name)]
+ if similar_deps:
+ similar_msg = f'Cargo.lock provides: {", ".join(similar_deps)}.'
+ else:
+ similar_msg = 'Cargo.lock does not contain this crate name.'
+ raise MesonException(f'Dependency {meson_depname!r} not found in any wrap files or Cargo.lock; {similar_msg} This could be a Meson bug, please report it.')
+
+ subdir, _ = self.environment.wrap_resolver.resolve(subp_name)
subprojects_dir = os.path.join(subdir, 'subprojects')
self.environment.wrap_resolver.load_and_merge(subprojects_dir, T.cast('SubProject', meson_depname))
- manifest = self._load_manifest(subdir)
+ manifest, _ = self._load_manifest(subdir)
downloaded = \
- meson_depname in self.environment.wrap_resolver.wraps and \
- self.environment.wrap_resolver.wraps[meson_depname].type is not None
- pkg = PackageState(manifest, downloaded)
- self.packages[key] = pkg
+ subp_name in self.environment.wrap_resolver.wraps and \
+ self.environment.wrap_resolver.wraps[subp_name].type is not None
+
+ ws = self._get_workspace(manifest, subdir, downloaded=downloaded)
+ member = ws.packages_to_member[package_name]
+ pkg = self._require_workspace_member(ws, member)
+ return pkg
+
+ def _prepare_package(self, pkg: PackageState) -> None:
+ key = PackageKey(pkg.manifest.package.name, pkg.manifest.package.api)
+ assert key in self.packages
+ if pkg.cfg:
+ return
+
+ pkg.cfg = PackageConfiguration()
+ # Merge target specific dependencies that are enabled
+ cfgs = self._get_cfgs(MachineChoice.HOST)
+ for condition, dependencies in pkg.manifest.target.items():
+ if eval_cfg(condition, cfgs):
+ pkg.manifest.dependencies.update(dependencies)
# Fetch required dependencies recursively.
- for depname, dep in manifest.dependencies.items():
+ for depname, dep in pkg.manifest.dependencies.items():
if not dep.optional:
self._add_dependency(pkg, depname)
- return pkg, False
- def _dep_package(self, dep: Dependency) -> PackageState:
- return self.packages[PackageKey(dep.package, dep.api)]
+ def _dep_package(self, pkg: PackageState, dep: Dependency) -> PackageState:
+ if dep.path:
+ ws = self.workspaces[pkg.ws_subdir]
+ dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path))
+ if is_parent_path(self.subprojects_dir, dep_member):
+ if len(pathlib.PurePath(dep_member).parts) != 2:
+ raise MesonException('found "{self.subprojects_dir}" in path but it is not a valid subproject path')
+ self._load_workspace_member(ws, dep_member)
+ dep_pkg = self._require_workspace_member(ws, dep_member)
+ elif dep.git:
+ _, _, directory = _parse_git_url(dep.git, dep.branch)
+ dep_pkg = self._fetch_package_from_subproject(dep.package, directory)
+ else:
+ cargo_pkg = self._resolve_package(dep.package, dep.meson_version)
+ if cargo_pkg:
+ dep.update_version(f'={cargo_pkg.version}')
+ dep_pkg = self._fetch_package(dep.package, dep.api)
+
+ if not dep.version:
+ dep.update_version(f'={dep_pkg.manifest.package.version}')
+
+ dep_key = PackageKey(dep.package, dep.api)
+ pkg.cfg.dep_packages.setdefault(dep_key, dep_pkg)
+ assert pkg.cfg.dep_packages[dep_key] == dep_pkg
+ return dep_pkg
- def _load_manifest(self, subdir: str) -> Manifest:
+ def _load_manifest(self, subdir: str, workspace: T.Optional[Workspace] = None, member_path: str = '') -> T.Tuple[T.Union[Manifest, Workspace], bool]:
manifest_ = self.manifests.get(subdir)
- if not manifest_:
- filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml')
- raw = load_toml(filename)
- if 'package' in raw:
- raw_manifest = T.cast('manifest.Manifest', raw)
- manifest_ = _convert_manifest(raw_manifest, subdir)
- self.manifests[subdir] = manifest_
- else:
- raise MesonException(f'{subdir}/Cargo.toml does not have [package] section')
- return manifest_
+ if manifest_:
+ return manifest_, True
+ path = os.path.join(self.environment.source_dir, subdir)
+ filename = os.path.join(path, 'Cargo.toml')
+ self.build_def_files.append(filename)
+ raw_manifest = T.cast('raw.Manifest', load_toml(filename))
+ if 'workspace' in raw_manifest:
+ manifest_ = Workspace.from_raw(raw_manifest, path)
+ elif 'package' in raw_manifest:
+ manifest_ = Manifest.from_raw(raw_manifest, path, workspace, member_path)
+ else:
+ raise MesonException(f'{subdir}/Cargo.toml does not have [package] or [workspace] section')
+ self.manifests[subdir] = manifest_
+ return manifest_, False
def _add_dependency(self, pkg: PackageState, depname: str) -> None:
- if depname in pkg.required_deps:
+ cfg = pkg.cfg
+ if depname in cfg.required_deps:
return
dep = pkg.manifest.dependencies.get(depname)
if not dep:
- if depname in itertools.chain(pkg.manifest.dev_dependencies, pkg.manifest.build_dependencies):
- # FIXME: Not supported yet
- return
- raise MesonException(f'Dependency {depname} not defined in {pkg.manifest.package.name} manifest')
- pkg.required_deps.add(depname)
- dep_pkg, _ = self._fetch_package(dep.package, dep.api)
+ # It could be build/dev/target dependency. Just ignore it.
+ return
+ cfg.required_deps.add(depname)
+ dep_pkg = self._dep_package(pkg, dep)
+ self._prepare_package(dep_pkg)
if dep.default_features:
self._enable_feature(dep_pkg, 'default')
for f in dep.features:
self._enable_feature(dep_pkg, f)
- for f in pkg.optional_deps_features[depname]:
+ for f in cfg.optional_deps_features[depname]:
self._enable_feature(dep_pkg, f)
def _enable_feature(self, pkg: PackageState, feature: str) -> None:
- if feature in pkg.features:
+ cfg = pkg.cfg
+ if feature in cfg.features:
return
- pkg.features.add(feature)
+ cfg.features.add(feature)
# A feature can also be a dependency.
if feature in pkg.manifest.dependencies:
self._add_dependency(pkg, feature)
@@ -580,50 +507,78 @@ class Interpreter:
depname, dep_f = f.split('/', 1)
if depname[-1] == '?':
depname = depname[:-1]
- if depname in pkg.required_deps:
- dep = pkg.manifest.dependencies[depname]
- dep_pkg = self._dep_package(dep)
- self._enable_feature(dep_pkg, dep_f)
- else:
- # This feature will be enabled only if that dependency
- # is later added.
- pkg.optional_deps_features[depname].add(dep_f)
else:
self._add_dependency(pkg, depname)
- dep = pkg.manifest.dependencies.get(depname)
- if dep:
- dep_pkg = self._dep_package(dep)
- self._enable_feature(dep_pkg, dep_f)
+ if depname in cfg.required_deps:
+ dep = pkg.manifest.dependencies[depname]
+ dep_pkg = self._dep_package(pkg, dep)
+ self._enable_feature(dep_pkg, dep_f)
+ else:
+ # This feature will be enabled only if that dependency
+ # is later added.
+ cfg.optional_deps_features[depname].add(dep_f)
elif f.startswith('dep:'):
self._add_dependency(pkg, f[4:])
else:
self._enable_feature(pkg, f)
- def _create_project(self, pkg: PackageState, build: builder.Builder) -> T.List[mparser.BaseNode]:
+ def has_check_cfg(self, machine: MachineChoice) -> bool:
+ if not self.environment.is_cross_build():
+ machine = MachineChoice.HOST
+ rustc = T.cast('RustCompiler', self.environment.coredata.compilers[machine]['rust'])
+ return rustc.has_check_cfg
+
+ @functools.lru_cache(maxsize=None)
+ def _get_cfgs(self, machine: MachineChoice) -> T.Dict[str, str]:
+ if not self.environment.is_cross_build():
+ machine = MachineChoice.HOST
+ rustc = T.cast('RustCompiler', self.environment.coredata.compilers[machine]['rust'])
+ cfgs = rustc.get_cfgs().copy()
+ rustflags = self.environment.coredata.get_external_args(machine, 'rust')
+ rustflags_i = iter(rustflags)
+ for i in rustflags_i:
+ if i == '--cfg':
+ cfgs.append(next(rustflags_i))
+ return dict(self._split_cfg(i) for i in cfgs)
+
+ @staticmethod
+ def _split_cfg(cfg: str) -> T.Tuple[str, str]:
+ pair = cfg.split('=', maxsplit=1)
+ value = pair[1] if len(pair) > 1 else ''
+ if value and value[0] == '"':
+ value = value[1:-1]
+ return pair[0], value
+
+ def _create_project(self, name: str, pkg: T.Optional[PackageState], build: builder.Builder) -> T.List[mparser.BaseNode]:
"""Create the project() function call
:param pkg: The package to generate from
:param build: The AST builder
:return: a list nodes
"""
- default_options: T.List[mparser.BaseNode] = []
- default_options.append(build.string(f'rust_std={pkg.manifest.package.edition}'))
- if pkg.downloaded:
- default_options.append(build.string('warning_level=0'))
-
- args: T.List[mparser.BaseNode] = []
- args.extend([
- build.string(pkg.manifest.package.name),
+ args: T.List[mparser.BaseNode] = [
+ build.string(name),
build.string('rust'),
- ])
+ ]
kwargs: T.Dict[str, mparser.BaseNode] = {
- 'version': build.string(pkg.manifest.package.version),
# Always assume that the generated meson is using the latest features
# This will warn when when we generate deprecated code, which is helpful
# for the upkeep of the module
'meson_version': build.string(f'>= {coredata.stable_version}'),
- 'default_options': build.array(default_options),
}
+ if not pkg:
+ return [
+ build.function('project', args, kwargs),
+ ]
+
+ default_options: T.Dict[str, mparser.BaseNode] = {}
+ if pkg.downloaded:
+ default_options['warning_level'] = build.string('0')
+
+ kwargs.update({
+ 'version': build.string(pkg.manifest.package.version),
+ 'default_options': build.dict({build.string(k): v for k, v in default_options.items()}),
+ })
if pkg.manifest.package.license:
kwargs['license'] = build.string(pkg.manifest.package.license)
elif pkg.manifest.package.license_file:
@@ -632,19 +587,23 @@ class Interpreter:
return [build.function('project', args, kwargs)]
def _create_dependencies(self, pkg: PackageState, build: builder.Builder) -> T.List[mparser.BaseNode]:
+ cfg = pkg.cfg
ast: T.List[mparser.BaseNode] = []
- for depname in pkg.required_deps:
+ for depname in cfg.required_deps:
dep = pkg.manifest.dependencies[depname]
- ast += self._create_dependency(dep, build)
+ dep_pkg = self._dep_package(pkg, dep)
+ if dep_pkg.manifest.lib:
+ ast += self._create_dependency(dep_pkg, dep, build)
ast.append(build.assign(build.array([]), 'system_deps_args'))
for name, sys_dep in pkg.manifest.system_dependencies.items():
- if sys_dep.enabled(pkg.features):
+ if sys_dep.enabled(cfg.features):
ast += self._create_system_dependency(name, sys_dep, build)
return ast
def _create_system_dependency(self, name: str, dep: SystemDependency, build: builder.Builder) -> T.List[mparser.BaseNode]:
+ # TODO: handle feature_overrides
kw = {
- 'version': build.array([build.string(s) for s in dep.version]),
+ 'version': build.array([build.string(s) for s in dep.meson_version]),
'required': build.bool(not dep.optional),
}
varname = f'{fixup_meson_varname(name)}_system_dep'
@@ -668,10 +627,11 @@ class Interpreter:
),
]
- def _create_dependency(self, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]:
- pkg = self._dep_package(dep)
+ def _create_dependency(self, pkg: PackageState, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]:
+ cfg = pkg.cfg
+ version_ = dep.meson_version or [pkg.manifest.package.version]
kw = {
- 'version': build.array([build.string(s) for s in dep.version]),
+ 'version': build.array([build.string(s) for s in version_]),
}
# Lookup for this dependency with the features we want in default_options kwarg.
#
@@ -692,7 +652,7 @@ class Interpreter:
[build.string(_dependency_name(dep.package, dep.api))],
kw,
),
- _dependency_varname(dep.package),
+ _dependency_varname(dep),
),
# actual_features = xxx_dep.get_variable('features', default_value : '').split(',')
build.assign(
@@ -700,7 +660,7 @@ class Interpreter:
'split',
build.method(
'get_variable',
- build.identifier(_dependency_varname(dep.package)),
+ build.identifier(_dependency_varname(dep)),
[build.string('features')],
{'default_value': build.string('')}
),
@@ -714,7 +674,7 @@ class Interpreter:
# error()
# endif
# endforeach
- build.assign(build.array([build.string(f) for f in pkg.features]), 'needed_features'),
+ build.assign(build.array([build.string(f) for f in cfg.features]), 'needed_features'),
build.foreach(['f'], build.identifier('needed_features'), build.block([
build.if_(build.not_in(build.identifier('f'), build.identifier('actual_features')), build.block([
build.function('error', [
@@ -747,30 +707,37 @@ class Interpreter:
build.block([build.function('subdir', [build.string('meson')])]))
]
- def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: manifest.CRATE_TYPE) -> T.List[mparser.BaseNode]:
+ def _create_lib(self, pkg: PackageState, build: builder.Builder, subdir: str,
+ lib_type: Literal['rust', 'c', 'proc-macro'],
+ static: bool = False, shared: bool = False) -> T.List[mparser.BaseNode]:
+ cfg = pkg.cfg
dependencies: T.List[mparser.BaseNode] = []
- dependency_map: T.Dict[mparser.BaseNode, mparser.BaseNode] = {}
- for name in pkg.required_deps:
+ for name in cfg.required_deps:
dep = pkg.manifest.dependencies[name]
- dependencies.append(build.identifier(_dependency_varname(dep.package)))
- if name != dep.package:
- dep_pkg = self._dep_package(dep)
- dep_lib_name = dep_pkg.manifest.lib.name
- dependency_map[build.string(fixup_meson_varname(dep_lib_name))] = build.string(name)
+ dependencies.append(build.identifier(_dependency_varname(dep)))
+
+ dependency_map: T.Dict[mparser.BaseNode, mparser.BaseNode] = {
+ build.string(k): build.string(v) for k, v in cfg.get_dependency_map(pkg.manifest).items()}
+
for name, sys_dep in pkg.manifest.system_dependencies.items():
- if sys_dep.enabled(pkg.features):
+ if sys_dep.enabled(cfg.features):
dependencies.append(build.identifier(f'{fixup_meson_varname(name)}_system_dep'))
- rust_args: T.List[mparser.BaseNode] = [
- build.identifier('features_args'),
- build.identifier(_extra_args_varname()),
- build.identifier('system_deps_args'),
- ]
+ rustc_args_list = pkg.get_rustc_args(self.environment, subdir, MachineChoice.HOST)
+ extra_args_ref = build.identifier(_extra_args_varname())
+ system_deps_args_ref = build.identifier('system_deps_args')
+ rust_args: T.List[mparser.BaseNode] = [build.string(a) for a in rustc_args_list]
+ rust_args.append(extra_args_ref)
+ rust_args.append(system_deps_args_ref)
dependencies.append(build.identifier(_extra_deps_varname()))
+ override_options: T.Dict[mparser.BaseNode, mparser.BaseNode] = {
+ build.string('rust_std'): build.string(pkg.manifest.package.edition),
+ }
+
posargs: T.List[mparser.BaseNode] = [
- build.string(fixup_meson_varname(pkg.manifest.lib.name)),
+ build.string(_library_name(pkg.manifest.lib.name, pkg.manifest.package.api, lib_type)),
build.string(pkg.manifest.lib.path),
]
@@ -778,32 +745,28 @@ class Interpreter:
'dependencies': build.array(dependencies),
'rust_dependency_map': build.dict(dependency_map),
'rust_args': build.array(rust_args),
+ 'override_options': build.dict(override_options),
}
+ depname_suffix = '' if lib_type == 'c' else '-rs'
+ depname = _dependency_name(pkg.manifest.package.name, pkg.manifest.package.api, depname_suffix)
+
lib: mparser.BaseNode
- if pkg.manifest.lib.proc_macro or crate_type == 'proc-macro':
+ if lib_type == 'proc-macro':
lib = build.method('proc_macro', build.identifier('rust'), posargs, kwargs)
else:
- if crate_type in {'lib', 'rlib', 'staticlib'}:
- target_type = 'static_library'
- elif crate_type in {'dylib', 'cdylib'}:
- target_type = 'shared_library'
+ if static and shared:
+ target_type = 'both_libraries'
else:
- raise MesonException(f'Unsupported crate type {crate_type}')
- if crate_type in {'staticlib', 'cdylib'}:
- kwargs['rust_abi'] = build.string('c')
- lib = build.function(target_type, posargs, kwargs)
+ target_type = 'shared_library' if shared else 'static_library'
- features_args: T.List[mparser.BaseNode] = []
- for f in pkg.features:
- features_args += [build.string('--cfg'), build.string(f'feature="{f}"')]
+ kwargs['rust_abi'] = build.string(lib_type)
+ lib = build.function(target_type, posargs, kwargs)
- # features_args = ['--cfg', 'feature="f1"', ...]
# lib = xxx_library()
# dep = declare_dependency()
# meson.override_dependency()
return [
- build.assign(build.array(features_args), 'features_args'),
build.assign(lib, 'lib'),
build.assign(
build.function(
@@ -811,8 +774,9 @@ class Interpreter:
kw={
'link_with': build.identifier('lib'),
'variables': build.dict({
- build.string('features'): build.string(','.join(pkg.features)),
- })
+ build.string('features'): build.string(','.join(cfg.features)),
+ }),
+ 'version': build.string(pkg.manifest.package.version),
},
),
'dep'
@@ -821,57 +785,79 @@ class Interpreter:
'override_dependency',
build.identifier('meson'),
[
- build.string(_dependency_name(pkg.manifest.package.name, pkg.manifest.package.api)),
+ build.string(depname),
build.identifier('dep'),
],
),
]
-def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition]:
+def _parse_git_url(url: str, branch: T.Optional[str] = None) -> T.Tuple[str, str, str]:
+ if url.startswith('git+'):
+ url = url[4:]
+ parts = urllib.parse.urlparse(url)
+ query = urllib.parse.parse_qs(parts.query)
+ query_branch = query['branch'][0] if 'branch' in query else ''
+ branch = branch or query_branch
+ revision = parts.fragment or branch
+ directory = PurePath(parts.path).name
+ if directory.endswith('.git'):
+ directory = directory[:-4]
+ if branch:
+ directory += f'-{branch}'
+ url = urllib.parse.urlunparse(parts._replace(params='', query='', fragment=''))
+ return url, revision, directory
+
+
+def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock]:
""" Convert Cargo.lock into a list of wraps """
- wraps: T.List[PackageDefinition] = []
- filename = os.path.join(source_dir, 'Cargo.lock')
+ # Map directory -> PackageDefinition, to avoid duplicates. Multiple packages
+ # can have the same source URL, in that case we have a single wrap that
+ # provides multiple dependency names.
if os.path.exists(filename):
- try:
- cargolock = T.cast('manifest.CargoLock', load_toml(filename))
- except TomlImplementationMissing as e:
- mlog.warning('Failed to load Cargo.lock:', str(e), fatal=False)
- return wraps
- for package in cargolock['package']:
- name = package['name']
- version = package['version']
- subp_name = _dependency_name(name, _version_to_api(version))
- source = package.get('source')
- if source is None:
+ toml = load_toml(filename)
+ raw_cargolock = T.cast('raw.CargoLock', toml)
+ cargolock = CargoLock.from_raw(raw_cargolock)
+ packagefiles_dir = os.path.join(subproject_dir, 'packagefiles')
+ wraps: T.Dict[str, PackageDefinition] = {}
+ for package in cargolock.package:
+ meson_depname = _dependency_name(package.name, version.api(package.version))
+ if package.source is None:
# This is project's package, or one of its workspace members.
pass
- elif source == 'registry+https://github.com/rust-lang/crates.io-index':
- checksum = package.get('checksum')
+ elif package.source == 'registry+https://github.com/rust-lang/crates.io-index':
+ checksum = package.checksum
if checksum is None:
- checksum = cargolock['metadata'][f'checksum {name} {version} ({source})']
- url = f'https://crates.io/api/v1/crates/{name}/{version}/download'
- directory = f'{name}-{version}'
- wraps.append(PackageDefinition.from_values(subp_name, subproject_dir, 'file', {
+ checksum = cargolock.metadata[f'checksum {package.name} {package.version} ({package.source})']
+ url = f'https://crates.io/api/v1/crates/{package.name}/{package.version}/download'
+ directory = f'{package.name}-{package.version}'
+ name = meson_depname
+ wrap_type = 'file'
+ cfg = {
'directory': directory,
'source_url': url,
'source_filename': f'{directory}.tar.gz',
'source_hash': checksum,
'method': 'cargo',
- }))
- elif source.startswith('git+'):
- parts = urllib.parse.urlparse(source[4:])
- query = urllib.parse.parse_qs(parts.query)
- branch = query['branch'][0] if 'branch' in query else ''
- revision = parts.fragment or branch
- url = urllib.parse.urlunparse(parts._replace(params='', query='', fragment=''))
- wraps.append(PackageDefinition.from_values(subp_name, subproject_dir, 'git', {
- 'directory': name,
+ }
+ elif package.source.startswith('git+'):
+ url, revision, directory = _parse_git_url(package.source)
+ name = directory
+ wrap_type = 'git'
+ cfg = {
'url': url,
'revision': revision,
'method': 'cargo',
- }))
+ }
else:
- mlog.warning(f'Unsupported source URL in {filename}: {source}')
- return wraps
+ mlog.warning(f'Unsupported source URL in {filename}: {package.source}')
+ continue
+ if os.path.isdir(os.path.join(packagefiles_dir, name)):
+ cfg['patch_directory'] = name
+ if directory not in wraps:
+ wraps[directory] = PackageDefinition.from_values(name, subproject_dir, wrap_type, cfg)
+ wraps[directory].add_provided_dep(meson_depname)
+ cargolock.wraps = {w.name: w for w in wraps.values()}
+ return cargolock
+ return None
diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py
index d95df7f..ec84e4b 100644
--- a/mesonbuild/cargo/manifest.py
+++ b/mesonbuild/cargo/manifest.py
@@ -4,244 +4,622 @@
"""Type definitions for cargo manifest files."""
from __future__ import annotations
+
+import collections
+import dataclasses
+import os
import typing as T
-from typing_extensions import Literal, TypedDict, Required
-
-EDITION = Literal['2015', '2018', '2021']
-CRATE_TYPE = Literal['bin', 'lib', 'dylib', 'staticlib', 'cdylib', 'rlib', 'proc-macro']
-
-Package = TypedDict(
- 'Package',
- {
- 'name': Required[str],
- 'version': Required[str],
- 'authors': T.List[str],
- 'edition': EDITION,
- 'rust-version': str,
- 'description': str,
- 'readme': str,
- 'license': str,
- 'license-file': str,
- 'keywords': T.List[str],
- 'categories': T.List[str],
- 'workspace': str,
- 'build': str,
- 'links': str,
- 'include': T.List[str],
- 'exclude': T.List[str],
- 'publish': bool,
- 'metadata': T.Dict[str, T.Dict[str, str]],
- 'default-run': str,
- 'autolib': bool,
- 'autobins': bool,
- 'autoexamples': bool,
- 'autotests': bool,
- 'autobenches': bool,
- },
- total=False,
-)
-"""A description of the Package Dictionary."""
-class FixedPackage(TypedDict, total=False):
+from . import version
+from ..mesonlib import MesonException, lazy_property, Version
+from .. import mlog
- """A description of the Package Dictionary, fixed up."""
+if T.TYPE_CHECKING:
+ from typing_extensions import Protocol, Self
- name: Required[str]
- version: Required[str]
- authors: T.List[str]
- edition: EDITION
- rust_version: str
- description: str
- readme: str
- license: str
- license_file: str
- keywords: T.List[str]
- categories: T.List[str]
- workspace: str
- build: str
- links: str
- include: T.List[str]
- exclude: T.List[str]
- publish: bool
- metadata: T.Dict[str, T.Dict[str, str]]
- default_run: str
- autolib: bool
- autobins: bool
- autoexamples: bool
- autotests: bool
- autobenches: bool
-
-
-class Badge(TypedDict):
-
- """An entry in the badge section."""
-
- status: Literal['actively-developed', 'passively-developed', 'as-is', 'experimental', 'deprecated', 'none']
-
-
-Dependency = TypedDict(
- 'Dependency',
- {
- 'version': str,
- 'registry': str,
- 'git': str,
- 'branch': str,
- 'rev': str,
- 'path': str,
- 'optional': bool,
- 'package': str,
- 'default-features': bool,
- 'features': T.List[str],
- },
- total=False,
+ from . import raw
+ from .raw import EDITION, CRATE_TYPE, LINT_LEVEL
+ from ..wrap.wrap import PackageDefinition
+
+ # Copied from typeshed. Blarg that they don't expose this
+ class DataclassInstance(Protocol):
+ __dataclass_fields__: T.ClassVar[dict[str, dataclasses.Field[T.Any]]]
+
+_DI = T.TypeVar('_DI', bound='DataclassInstance')
+
+_EXTRA_KEYS_WARNING = (
+ "This may (unlikely) be an error in the cargo manifest, or may be a missing "
+ "implementation in Meson. If this issue can be reproduced with the latest "
+ "version of Meson, please help us by opening an issue at "
+ "https://github.com/mesonbuild/meson/issues. Please include the crate and "
+ "version that is generating this warning if possible."
)
-"""An entry in the *dependencies sections."""
-class FixedDependency(TypedDict, total=False):
+def fixup_meson_varname(name: str) -> str:
+ """Fixup a meson variable name
- """An entry in the *dependencies sections, fixed up."""
+ :param name: The name to fix
+ :return: the fixed name
+ """
+ return name.replace('-', '_')
- version: T.List[str]
- registry: str
- git: str
- branch: str
- rev: str
- path: str
- optional: bool
- package: str
- default_features: bool
- features: T.List[str]
-
-
-DependencyV = T.Union[Dependency, str]
-"""A Dependency entry, either a string or a Dependency Dict."""
-
-
-_BaseBuildTarget = TypedDict(
- '_BaseBuildTarget',
- {
- 'path': str,
- 'test': bool,
- 'doctest': bool,
- 'bench': bool,
- 'doc': bool,
- 'plugin': bool,
- 'proc-macro': bool,
- 'harness': bool,
- 'edition': EDITION,
- 'crate-type': T.List[CRATE_TYPE],
- 'required-features': T.List[str],
- },
- total=False,
-)
+
+class DefaultValue:
+ """Base class to converts a raw value from cargo manifest to a meson value
+
+ It returns the value from current manifest, or fallback to the
+ workspace value. If both are None, its default value is used. Subclasses can
+ override the convert() method to implement custom conversion logic.
+ """
+
+ def __init__(self, default: object = None) -> None:
+ self.default = default
+
+ def convert(self, v: T.Any, ws_v: T.Any) -> object:
+ return v if v is not None else ws_v
+
+
+class MergeValue(DefaultValue):
+ def __init__(self, func: T.Callable[[T.Any, T.Any], object], default: object = None) -> None:
+ super().__init__(default)
+ self.func = func
+
+ def convert(self, v: T.Any, ws_v: T.Any) -> object:
+ return self.func(v, ws_v)
+
+
+class ConvertValue(DefaultValue):
+ def __init__(self, func: T.Callable[[T.Any], object], default: object = None) -> None:
+ super().__init__(default)
+ self.func = func
+
+ def convert(self, v: T.Any, ws_v: T.Any) -> object:
+ return self.func(v if v is not None else ws_v)
-class BuildTarget(_BaseBuildTarget, total=False):
+def _raw_to_dataclass(raw: T.Mapping[str, object], cls: T.Type[_DI], msg: str,
+ raw_from_workspace: T.Optional[T.Mapping[str, object]] = None,
+ ignored_fields: T.Optional[T.List[str]] = None,
+ **kwargs: DefaultValue) -> _DI:
+ """Fixup raw cargo mappings to a dataclass.
- name: Required[str]
+ * Inherit values from the workspace.
+ * Replaces any `-` with `_` in the keys.
+ * Optionally pass values through the functions in kwargs, in order to do
+ recursive conversions.
+ * Remove and warn on keys that are coming from cargo, but are unknown to
+ our representations.
-class LibTarget(_BaseBuildTarget, total=False):
+ This is intended to give users the possibility of things proceeding when a
+ new key is added to Cargo.toml that we don't yet handle, but to still warn
+ them that things might not work.
+
+ :param raw: The raw data to look at
+ :param cls: The Dataclass derived type that will be created
+ :param msg: the header for the error message. Usually something like "In N structure".
+ :param raw_from_workspace: If inheriting from a workspace, the raw data from the workspace.
+ :param kwargs: DefaultValue instances to convert values.
+ :return: A @cls instance.
+ """
+ new_dict = {}
+ unexpected = set()
+ fields = {x.name for x in dataclasses.fields(cls)}
+ raw_from_workspace = raw_from_workspace or {}
+ ignored_fields = ignored_fields or []
+ inherit = raw.get('workspace', False)
+
+ for orig_k, v in raw.items():
+ if orig_k == 'workspace':
+ continue
+ ws_v = None
+ if isinstance(v, dict) and v.get('workspace', False):
+ # foo.workspace = true, take value from workspace.
+ ws_v = raw_from_workspace[orig_k]
+ v = None
+ elif inherit:
+ # foo = {}, give the workspace value, if any, to the converter
+ # function in the case it wants to merge values.
+ ws_v = raw_from_workspace.get(orig_k)
+ k = fixup_meson_varname(orig_k)
+ if k not in fields:
+ if orig_k not in ignored_fields:
+ unexpected.add(orig_k)
+ continue
+ if k in kwargs:
+ new_dict[k] = kwargs[k].convert(v, ws_v)
+ else:
+ new_dict[k] = v if v is not None else ws_v
+
+ if inherit:
+ # Inherit any keys from the workspace that we don't have yet.
+ for orig_k, ws_v in raw_from_workspace.items():
+ k = fixup_meson_varname(orig_k)
+ if k not in fields:
+ if orig_k not in ignored_fields:
+ unexpected.add(orig_k)
+ continue
+ if k in new_dict:
+ continue
+ if k in kwargs:
+ new_dict[k] = kwargs[k].convert(None, ws_v)
+ else:
+ new_dict[k] = ws_v
+
+ # Finally, set default values.
+ for k, convertor in kwargs.items():
+ if k not in new_dict and convertor.default is not None:
+ new_dict[k] = convertor.default
+
+ if unexpected:
+ mlog.warning(msg, 'has unexpected keys', '"{}".'.format(', '.join(sorted(unexpected))),
+ _EXTRA_KEYS_WARNING)
+
+ return cls(**new_dict)
+
+
+@dataclasses.dataclass
+class Package:
+
+ """Representation of a Cargo Package entry, with defaults filled in."""
name: str
+ version: str = "0"
+ description: T.Optional[str] = None
+ resolver: T.Optional[str] = None
+ authors: T.List[str] = dataclasses.field(default_factory=list)
+ edition: EDITION = '2015'
+ rust_version: T.Optional[str] = None
+ documentation: T.Optional[str] = None
+ readme: T.Optional[str] = None
+ homepage: T.Optional[str] = None
+ repository: T.Optional[str] = None
+ license: T.Optional[str] = None
+ license_file: T.Optional[str] = None
+ keywords: T.List[str] = dataclasses.field(default_factory=list)
+ categories: T.List[str] = dataclasses.field(default_factory=list)
+ workspace: T.Optional[str] = None
+ build: T.Optional[str] = None
+ links: T.Optional[str] = None
+ exclude: T.List[str] = dataclasses.field(default_factory=list)
+ include: T.List[str] = dataclasses.field(default_factory=list)
+ publish: bool = True
+ metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict)
+ default_run: T.Optional[str] = None
+ autolib: bool = True
+ autobins: bool = True
+ autoexamples: bool = True
+ autotests: bool = True
+ autobenches: bool = True
+
+ @lazy_property
+ def api(self) -> str:
+ return version.api(self.version)
+
+ @classmethod
+ def from_raw(cls, raw_pkg: raw.Package, workspace: T.Optional[Workspace] = None) -> Self:
+ raw_ws_pkg = workspace.package if workspace else None
+ return _raw_to_dataclass(raw_pkg, cls, f'Package entry {raw_pkg["name"]}', raw_ws_pkg)
+
+
+@dataclasses.dataclass
+class SystemDependency:
+
+ """ Representation of a Cargo system-deps entry
+ https://docs.rs/system-deps/latest/system_deps
+ """
+ name: str
+ version: str = ''
+ optional: bool = False
+ feature: T.Optional[str] = None
+ # TODO: convert values to dataclass
+ feature_overrides: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict)
+
+ @lazy_property
+ def meson_version(self) -> T.List[str]:
+ vers = self.version.split(',') if self.version else []
+ result: T.List[str] = []
+ for v in vers:
+ v = v.strip()
+ if v[0] not in '><=':
+ v = f'>={v}'
+ result.append(v)
+ return result
+
+ def enabled(self, features: T.Set[str]) -> bool:
+ return self.feature is None or self.feature in features
+
+ @classmethod
+ def from_raw(cls, name: str, raw: T.Union[T.Dict[str, T.Any], str]) -> Self:
+ if isinstance(raw, str):
+ raw = {'version': raw}
+ name = raw.get('name', name)
+ version = raw.get('version', '')
+ optional = raw.get('optional', False)
+ feature = raw.get('feature')
+ # Everything else are overrides when certain features are enabled.
+ feature_overrides = {k: v for k, v in raw.items() if k not in {'name', 'version', 'optional', 'feature'}}
+ return cls(name, version, optional, feature, feature_overrides)
+
+
+@dataclasses.dataclass
+class Dependency:
+
+ """Representation of a Cargo Dependency Entry."""
-class _BaseFixedBuildTarget(TypedDict, total=False):
+ package: str
+ version: str = ''
+ registry: T.Optional[str] = None
+ git: T.Optional[str] = None
+ branch: T.Optional[str] = None
+ rev: T.Optional[str] = None
+ path: T.Optional[str] = None
+ optional: bool = False
+ default_features: bool = True
+ features: T.List[str] = dataclasses.field(default_factory=list)
+
+ @lazy_property
+ def meson_version(self) -> T.List[str]:
+ return version.convert(self.version)
+
+ @lazy_property
+ def api(self) -> str:
+ # Extract wanted API version from version constraints.
+ api = set()
+ for v in self.meson_version:
+ if v.startswith(('>=', '==')):
+ api.add(version.api(v[2:].strip()))
+ elif v.startswith('='):
+ api.add(version.api(v[1:].strip()))
+ if not api:
+ return ''
+ elif len(api) == 1:
+ return api.pop()
+ else:
+ raise MesonException(f'Cannot determine minimum API version from {self.version}.')
+
+ def update_version(self, v: str) -> None:
+ self.version = v
+ try:
+ delattr(self, 'api')
+ except AttributeError:
+ pass
+ try:
+ delattr(self, 'meson_version')
+ except AttributeError:
+ pass
+
+ @T.overload
+ @staticmethod
+ def _depv_to_dep(depv: raw.FromWorkspace) -> raw.FromWorkspace: ...
+
+ @T.overload
+ @staticmethod
+ def _depv_to_dep(depv: raw.DependencyV) -> raw.Dependency: ...
+
+ @staticmethod
+ def _depv_to_dep(depv: T.Union[raw.FromWorkspace, raw.DependencyV]) -> T.Union[raw.FromWorkspace, raw.Dependency]:
+ return {'version': depv} if isinstance(depv, str) else depv
+
+ @classmethod
+ def from_raw(cls, name: str, raw_depv: T.Union[raw.FromWorkspace, raw.DependencyV], member_path: str = '', workspace: T.Optional[Workspace] = None) -> Self:
+ """Create a dependency from a raw cargo dictionary or string"""
+ raw_ws_dep = workspace.dependencies.get(name) if workspace else None
+ raw_ws_dep = cls._depv_to_dep(raw_ws_dep or {})
+ raw_dep = cls._depv_to_dep(raw_depv)
+
+ def path_convertor(path: T.Optional[str], ws_path: T.Optional[str]) -> T.Optional[str]:
+ if path:
+ return path
+ if ws_path:
+ return os.path.relpath(ws_path, member_path)
+ return None
+
+ return _raw_to_dataclass(raw_dep, cls, f'Dependency entry {name}', raw_ws_dep,
+ package=DefaultValue(name),
+ path=MergeValue(path_convertor),
+ features=MergeValue(lambda features, ws_features: (features or []) + (ws_features or [])))
+
+
+@dataclasses.dataclass
+class BuildTarget:
+
+ # https://doc.rust-lang.org/cargo/reference/cargo-targets.html
+ # Some default values are overridden in subclasses
+ name: str
path: str
- test: bool
- doctest: bool
- bench: bool
- doc: bool
- plugin: bool
- harness: bool
edition: EDITION
- crate_type: T.List[CRATE_TYPE]
- required_features: T.List[str]
+ test: bool = True
+ doctest: bool = True
+ bench: bool = True
+ doc: bool = True
+ harness: bool = True
+ crate_type: T.List[CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['bin'])
+ required_features: T.List[str] = dataclasses.field(default_factory=list)
+ plugin: bool = False
-class FixedBuildTarget(_BaseFixedBuildTarget, total=False):
+@dataclasses.dataclass
+class Library(BuildTarget):
- name: str
+ """Representation of a Cargo Library Entry."""
-class FixedLibTarget(_BaseFixedBuildTarget, total=False):
+ @classmethod
+ def from_raw(cls, raw: raw.LibTarget, pkg: Package) -> Self:
+ name = raw.get('name', fixup_meson_varname(pkg.name))
+ # If proc_macro is True, it takes precedence and sets crate_type to proc-macro
+ proc_macro = raw.get('proc-macro', False)
+ return _raw_to_dataclass(raw, cls, f'Library entry {name}',
+ ignored_fields=['proc-macro'],
+ name=DefaultValue(name),
+ path=DefaultValue('src/lib.rs'),
+ edition=DefaultValue(pkg.edition),
+ crate_type=ConvertValue(lambda x: ['proc-macro'] if proc_macro else x,
+ ['proc-macro'] if proc_macro else ['lib']))
- name: Required[str]
- proc_macro: bool
+@dataclasses.dataclass
+class Binary(BuildTarget):
-class Target(TypedDict):
+ """Representation of a Cargo Bin Entry."""
- """Target entry in the Manifest File."""
+ @classmethod
+ def from_raw(cls, raw: raw.BuildTarget, pkg: Package) -> Self:
+ name = raw["name"]
+ return _raw_to_dataclass(raw, cls, f'Binary entry {name}',
+ path=DefaultValue('src/main.rs'),
+ edition=DefaultValue(pkg.edition))
- dependencies: T.Dict[str, DependencyV]
+@dataclasses.dataclass
+class Test(BuildTarget):
-class Workspace(TypedDict):
+ """Representation of a Cargo Test Entry."""
- """The representation of a workspace.
+ @classmethod
+ def from_raw(cls, raw: raw.BuildTarget, pkg: Package) -> Self:
+ name = raw["name"]
+ return _raw_to_dataclass(raw, cls, f'Test entry {name}',
+ path=DefaultValue(f'tests/{name}.rs'),
+ edition=DefaultValue(pkg.edition),
+ bench=DefaultValue(False),
+ doc=DefaultValue(False))
- In a vritual manifest the :attribute:`members` is always present, but in a
- project manifest, an empty workspace may be provided, in which case the
- workspace is implicitly filled in by values from the path based dependencies.
- the :attribute:`exclude` is always optional
- """
+@dataclasses.dataclass
+class Benchmark(BuildTarget):
+
+ """Representation of a Cargo Benchmark Entry."""
+
+ @classmethod
+ def from_raw(cls, raw: raw.BuildTarget, pkg: Package) -> Self:
+ name = raw["name"]
+ return _raw_to_dataclass(raw, cls, f'Benchmark entry {name}',
+ path=DefaultValue(f'benches/{name}.rs'),
+ edition=DefaultValue(pkg.edition),
+ test=DefaultValue(False),
+ doc=DefaultValue(False))
- members: T.List[str]
- exclude: T.List[str]
-
-
-Manifest = TypedDict(
- 'Manifest',
- {
- 'package': Required[Package],
- 'badges': T.Dict[str, Badge],
- 'dependencies': T.Dict[str, DependencyV],
- 'dev-dependencies': T.Dict[str, DependencyV],
- 'build-dependencies': T.Dict[str, DependencyV],
- 'lib': LibTarget,
- 'bin': T.List[BuildTarget],
- 'test': T.List[BuildTarget],
- 'bench': T.List[BuildTarget],
- 'example': T.List[BuildTarget],
- 'features': T.Dict[str, T.List[str]],
- 'target': T.Dict[str, Target],
- 'workspace': Workspace,
-
- # TODO: patch?
- # TODO: replace?
- },
- total=False,
-)
-"""The Cargo Manifest format."""
+@dataclasses.dataclass
+class Example(BuildTarget):
-class VirtualManifest(TypedDict):
+ """Representation of a Cargo Example Entry."""
- """The Representation of a virtual manifest.
+ @classmethod
+ def from_raw(cls, raw: raw.BuildTarget, pkg: Package) -> Self:
+ name = raw["name"]
+ return _raw_to_dataclass(raw, cls, f'Example entry {name}',
+ path=DefaultValue(f'examples/{name}.rs'),
+ edition=DefaultValue(pkg.edition),
+ test=DefaultValue(False),
+ bench=DefaultValue(False),
+ doc=DefaultValue(False))
- Cargo allows a root manifest that contains only a workspace, this is called
- a virtual manifest. This doesn't really map 1:1 with any meson concept,
- except perhaps the proposed "meta project".
+
+@dataclasses.dataclass
+class Lint:
+
+ """Cargo Lint definition.
+ """
+
+ name: str
+ level: LINT_LEVEL
+ priority: int
+ check_cfg: T.Optional[T.List[str]]
+
+ @classmethod
+ def from_raw(cls, r: T.Union[raw.FromWorkspace, T.Dict[str, T.Dict[str, raw.LintV]]]) -> T.List[Lint]:
+ r = T.cast('T.Dict[str, T.Dict[str, raw.LintV]]', r)
+ lints: T.Dict[str, Lint] = {}
+ for tool, raw_lints in r.items():
+ prefix = '' if tool == 'rust' else f'{tool}::'
+ for name, settings in raw_lints.items():
+ name = prefix + name
+ if isinstance(settings, str):
+ settings = T.cast('raw.Lint', {'level': settings})
+ check_cfg = None
+ if name == 'unexpected_cfgs':
+ # 'cfg(test)' is added automatically by cargo
+ check_cfg = ['cfg(test)'] + settings.get('check-cfg', [])
+ lints[name] = Lint(name=name,
+ level=settings['level'],
+ priority=settings.get('priority', 0),
+ check_cfg=check_cfg)
+
+ lints_final = list(lints.values())
+ lints_final.sort(key=lambda x: x.priority)
+ return lints_final
+
+ def to_arguments(self, check_cfg: bool) -> T.List[str]:
+ if self.level == "deny":
+ flag = "-D"
+ elif self.level == "allow":
+ flag = "-A"
+ elif self.level == "warn":
+ flag = "-W"
+ elif self.level == "forbid":
+ flag = "-F"
+ else:
+ raise MesonException(f"invalid level {self.level!r} for {self.name}")
+ args = [flag, self.name]
+ if check_cfg and self.check_cfg:
+ for arg in self.check_cfg:
+ args.append('--check-cfg')
+ args.append(arg)
+ return args
+
+
+@dataclasses.dataclass
+class Manifest:
+
+ """Cargo Manifest definition.
+
+ Most of these values map up to the Cargo Manifest, but with default values
+ if not provided.
+
+ Cargo subprojects can contain what Meson wants to treat as multiple,
+ interdependent, subprojects.
+
+ :param path: the path within the cargo subproject.
"""
- workspace: Workspace
+ package: Package
+ dependencies: T.Dict[str, Dependency] = dataclasses.field(default_factory=dict)
+ dev_dependencies: T.Dict[str, Dependency] = dataclasses.field(default_factory=dict)
+ build_dependencies: T.Dict[str, Dependency] = dataclasses.field(default_factory=dict)
+ lib: T.Optional[Library] = None
+ bin: T.List[Binary] = dataclasses.field(default_factory=list)
+ test: T.List[Test] = dataclasses.field(default_factory=list)
+ bench: T.List[Benchmark] = dataclasses.field(default_factory=list)
+ example: T.List[Example] = dataclasses.field(default_factory=list)
+ features: T.Dict[str, T.List[str]] = dataclasses.field(default_factory=dict)
+ target: T.Dict[str, T.Dict[str, Dependency]] = dataclasses.field(default_factory=dict)
+ lints: T.List[Lint] = dataclasses.field(default_factory=list)
+
+ # missing: profile
+
+ def __post_init__(self) -> None:
+ self.features.setdefault('default', [])
+
+ @lazy_property
+ def system_dependencies(self) -> T.Dict[str, SystemDependency]:
+ return {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()}
+
+ @classmethod
+ def from_raw(cls, raw: raw.Manifest, path: str, workspace: T.Optional[Workspace] = None, member_path: str = '') -> Self:
+ pkg = Package.from_raw(raw['package'], workspace)
+
+ autolib = None
+ if pkg.autolib and os.path.exists(os.path.join(path, 'src/lib.rs')):
+ autolib = Library.from_raw({}, pkg)
+
+ def dependencies_from_raw(x: T.Dict[str, T.Any]) -> T.Dict[str, Dependency]:
+ return {k: Dependency.from_raw(k, v, member_path, workspace) for k, v in x.items()}
+
+ return _raw_to_dataclass(raw, cls, f'Cargo.toml package {pkg.name}',
+ raw_from_workspace=workspace.inheritable if workspace else None,
+ ignored_fields=['badges', 'workspace'],
+ package=ConvertValue(lambda _: pkg),
+ dependencies=ConvertValue(dependencies_from_raw),
+ dev_dependencies=ConvertValue(dependencies_from_raw),
+ build_dependencies=ConvertValue(dependencies_from_raw),
+ lints=ConvertValue(Lint.from_raw),
+ lib=ConvertValue(lambda x: Library.from_raw(x, pkg), default=autolib),
+ bin=ConvertValue(lambda x: [Binary.from_raw(b, pkg) for b in x]),
+ test=ConvertValue(lambda x: [Test.from_raw(b, pkg) for b in x]),
+ bench=ConvertValue(lambda x: [Benchmark.from_raw(b, pkg) for b in x]),
+ example=ConvertValue(lambda x: [Example.from_raw(b, pkg) for b in x]),
+ target=ConvertValue(lambda x: {k: dependencies_from_raw(v.get('dependencies', {})) for k, v in x.items()}))
+
+
+@dataclasses.dataclass
+class Workspace:
+
+ """Cargo Workspace definition.
+ """
-class CargoLockPackage(TypedDict, total=False):
+ resolver: str = dataclasses.field(default_factory=lambda: '2')
+ members: T.List[str] = dataclasses.field(default_factory=list)
+ exclude: T.List[str] = dataclasses.field(default_factory=list)
+ default_members: T.List[str] = dataclasses.field(default_factory=list)
+
+ # inheritable settings are kept in raw format, for use with _raw_to_dataclass
+ package: T.Optional[raw.Package] = None
+ dependencies: T.Dict[str, raw.Dependency] = dataclasses.field(default_factory=dict)
+ lints: T.Dict[str, T.Dict[str, raw.LintV]] = dataclasses.field(default_factory=dict)
+ metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict)
+
+ # A workspace can also have a root package.
+ root_package: T.Optional[Manifest] = None
+
+ @lazy_property
+ def inheritable(self) -> T.Dict[str, object]:
+ # the whole lints table is inherited. Do not add package, dependencies
+ # etc. because they can only be inherited a field at a time.
+ return {
+ 'lints': self.lints,
+ }
+
+ @classmethod
+ def from_raw(cls, raw: raw.Manifest, path: str) -> Self:
+ ws = _raw_to_dataclass(raw['workspace'], cls, 'Workspace')
+ if 'package' in raw:
+ ws.root_package = Manifest.from_raw(raw, path, ws, '.')
+ if not ws.default_members:
+ ws.default_members = ['.'] if ws.root_package else ws.members
+ return ws
+
+
+@dataclasses.dataclass
+class CargoLockPackage:
"""A description of a package in the Cargo.lock file format."""
name: str
version: str
- source: str
- checksum: str
+ source: T.Optional[str] = None
+ checksum: T.Optional[str] = None
+ dependencies: T.List[str] = dataclasses.field(default_factory=list)
+
+ @lazy_property
+ def api(self) -> str:
+ return version.api(self.version)
+
+ @lazy_property
+ def subproject(self) -> str:
+ return f'{self.name}-{self.api}-rs'
+ @classmethod
+ def from_raw(cls, raw: raw.CargoLockPackage) -> Self:
+ return _raw_to_dataclass(raw, cls, 'Cargo.lock package')
-class CargoLock(TypedDict, total=False):
+
+@dataclasses.dataclass
+class CargoLock:
"""A description of the Cargo.lock file format."""
- version: str
- package: T.List[CargoLockPackage]
- metadata: T.Dict[str, str]
+ version: int = 1
+ package: T.List[CargoLockPackage] = dataclasses.field(default_factory=list)
+ metadata: T.Dict[str, str] = dataclasses.field(default_factory=dict)
+ wraps: T.Dict[str, PackageDefinition] = dataclasses.field(default_factory=dict)
+
+ def named(self, name: str) -> T.Sequence[CargoLockPackage]:
+ return self._versions[name]
+
+ @lazy_property
+ def _versions(self) -> T.Dict[str, T.List[CargoLockPackage]]:
+ versions = collections.defaultdict(list)
+ for pkg in self.package:
+ versions[pkg.name].append(pkg)
+ for pkg_versions in versions.values():
+ pkg_versions.sort(reverse=True, key=lambda pkg: Version(pkg.version))
+ return versions
+
+ @classmethod
+ def from_raw(cls, raw: raw.CargoLock) -> Self:
+ return _raw_to_dataclass(raw, cls, 'Cargo.lock',
+ package=ConvertValue(lambda x: [CargoLockPackage.from_raw(p) for p in x]))
diff --git a/mesonbuild/cargo/raw.py b/mesonbuild/cargo/raw.py
new file mode 100644
index 0000000..b683f06
--- /dev/null
+++ b/mesonbuild/cargo/raw.py
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2022-2024 Intel Corporation
+
+"""Type definitions for cargo manifest files."""
+
+from __future__ import annotations
+import typing as T
+
+from typing_extensions import Literal, TypedDict, Required
+
+EDITION = Literal['2015', '2018', '2021']
+CRATE_TYPE = Literal['bin', 'lib', 'dylib', 'staticlib', 'cdylib', 'rlib', 'proc-macro']
+LINT_LEVEL = Literal['allow', 'deny', 'forbid', 'warn']
+
+
+class FromWorkspace(TypedDict):
+
+ """An entry or section that is copied from the workspace."""
+
+ workspace: bool
+
+
+Package = TypedDict(
+ 'Package',
+ {
+ 'name': Required[str],
+ 'version': Required[T.Union[FromWorkspace, str]],
+ 'authors': T.Union[FromWorkspace, T.List[str]],
+ 'edition': T.Union[FromWorkspace, EDITION],
+ 'rust-version': T.Union[FromWorkspace, str],
+ 'description': T.Union[FromWorkspace, str],
+ 'readme': T.Union[FromWorkspace, str],
+ 'license': T.Union[FromWorkspace, str],
+ 'license-file': T.Union[FromWorkspace, str],
+ 'keywords': T.Union[FromWorkspace, T.List[str]],
+ 'categories': T.Union[FromWorkspace, T.List[str]],
+ 'homepage': T.Union[FromWorkspace, str],
+ 'repository': T.Union[FromWorkspace, str],
+ 'documentation': T.Union[FromWorkspace, str],
+ 'workspace': str,
+ 'build': str,
+ 'links': str,
+ 'include': T.Union[FromWorkspace, T.List[str]],
+ 'exclude': T.Union[FromWorkspace, T.List[str]],
+ 'publish': T.Union[FromWorkspace, bool],
+ 'metadata': T.Dict[str, T.Dict[str, str]],
+ 'default-run': str,
+ 'autolib': bool,
+ 'autobins': bool,
+ 'autoexamples': bool,
+ 'autotests': bool,
+ 'autobenches': bool,
+ },
+ total=False,
+)
+"""A description of the Package Dictionary."""
+
+class Badge(TypedDict):
+
+ """An entry in the badge section."""
+
+ status: Literal['actively-developed', 'passively-developed', 'as-is', 'experimental', 'deprecated', 'none']
+ repository: str
+
+
+Dependency = TypedDict(
+ 'Dependency',
+ {
+ 'version': str,
+ 'registry': str,
+ 'git': str,
+ 'branch': str,
+ 'rev': str,
+ 'path': str,
+ 'optional': bool,
+ 'package': str,
+ 'default-features': bool,
+ 'features': T.List[str],
+ },
+ total=False,
+)
+"""An entry in the *dependencies sections."""
+
+
+DependencyV = T.Union[Dependency, str]
+"""A Dependency entry, either a string or a Dependency Dict."""
+
+
+_BaseBuildTarget = TypedDict(
+ '_BaseBuildTarget',
+ {
+ 'path': str,
+ 'test': bool,
+ 'doctest': bool,
+ 'bench': bool,
+ 'doc': bool,
+ 'plugin': bool,
+ 'proc-macro': bool,
+ 'harness': bool,
+ 'edition': EDITION,
+ 'crate-type': T.List[CRATE_TYPE],
+ 'required-features': T.List[str],
+ },
+ total=False,
+)
+
+
+class BuildTarget(_BaseBuildTarget, total=False):
+
+ name: Required[str]
+
+
+class LibTarget(_BaseBuildTarget, total=False):
+
+ name: str
+
+
+class Target(TypedDict):
+
+ """Target entry in the Manifest File."""
+
+ dependencies: T.Dict[str, T.Union[FromWorkspace, DependencyV]]
+
+
+Lint = TypedDict(
+ 'Lint',
+ {
+ 'level': Required[LINT_LEVEL],
+ 'priority': int,
+ 'check-cfg': T.List[str],
+ },
+ total=True,
+)
+"""The representation of a linter setting.
+
+This does not include the name or tool, since those are the keys of the
+dictionaries that point to Lint.
+"""
+
+
+LintV = T.Union[Lint, str]
+"""A Lint entry, either a string or a Lint Dict."""
+
+
+class Workspace(TypedDict):
+
+ """The representation of a workspace.
+
+ In a vritual manifest the :attribute:`members` is always present, but in a
+ project manifest, an empty workspace may be provided, in which case the
+ workspace is implicitly filled in by values from the path based dependencies.
+
+ the :attribute:`exclude` is always optional
+ """
+
+ members: T.List[str]
+ exclude: T.List[str]
+ package: Package
+ dependencies: T.Dict[str, DependencyV]
+
+
+Manifest = TypedDict(
+ 'Manifest',
+ {
+ 'package': Package,
+ 'badges': T.Dict[str, Badge],
+ 'dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
+ 'dev-dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
+ 'build-dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
+ 'lib': LibTarget,
+ 'bin': T.List[BuildTarget],
+ 'test': T.List[BuildTarget],
+ 'bench': T.List[BuildTarget],
+ 'example': T.List[BuildTarget],
+ 'features': T.Dict[str, T.List[str]],
+ 'target': T.Dict[str, Target],
+ 'workspace': Workspace,
+ 'lints': T.Union[FromWorkspace, T.Dict[str, T.Dict[str, LintV]]],
+
+ # TODO: patch?
+ # TODO: replace?
+ },
+ total=False,
+)
+"""The Cargo Manifest format."""
+
+
+class CargoLockPackage(TypedDict, total=False):
+
+ """A description of a package in the Cargo.lock file format."""
+
+ name: str
+ version: str
+ source: str
+ checksum: str
+
+
+class CargoLock(TypedDict, total=False):
+
+ """A description of the Cargo.lock file format."""
+
+ version: int
+ package: T.List[CargoLockPackage]
+ metadata: T.Dict[str, str]
diff --git a/mesonbuild/cargo/toml.py b/mesonbuild/cargo/toml.py
new file mode 100644
index 0000000..601510e
--- /dev/null
+++ b/mesonbuild/cargo/toml.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+import importlib
+import shutil
+import json
+import typing as T
+
+from ..mesonlib import MesonException, Popen_safe
+if T.TYPE_CHECKING:
+ from types import ModuleType
+
+
+# tomllib is present in python 3.11, before that it is a pypi module called tomli,
+# we try to import tomllib, then tomli,
+tomllib: T.Optional[ModuleType] = None
+toml2json: T.Optional[str] = None
+for t in ['tomllib', 'tomli']:
+ try:
+ tomllib = importlib.import_module(t)
+ break
+ except ImportError:
+ pass
+else:
+ # TODO: it would be better to use an Executable here, which could be looked
+ # up in the cross file or provided by a wrap. However, that will have to be
+ # passed in externally, since we don't have (and I don't think we should),
+ # have access to the `Environment` for that in this module.
+ toml2json = shutil.which('toml2json')
+
+class TomlImplementationMissing(MesonException):
+ pass
+
+
+def load_toml(filename: str) -> T.Dict[str, object]:
+ if tomllib:
+ with open(filename, 'rb') as f:
+ raw = tomllib.load(f)
+ else:
+ if toml2json is None:
+ raise TomlImplementationMissing('Could not find an implementation of tomllib, nor toml2json')
+
+ p, out, err = Popen_safe([toml2json, filename])
+ if p.returncode != 0:
+ raise MesonException('toml2json failed to decode output\n', err)
+
+ raw = json.loads(out)
+
+ # tomllib.load() returns T.Dict[str, T.Any] but not other implementations.
+ return T.cast('T.Dict[str, object]', raw)
diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py
index cde7a83..ce58945 100644
--- a/mesonbuild/cargo/version.py
+++ b/mesonbuild/cargo/version.py
@@ -7,6 +7,18 @@ from __future__ import annotations
import typing as T
+def api(version: str) -> str:
+ # x.y.z -> x
+ # 0.x.y -> 0.x
+ # 0.0.x -> 0
+ vers = version.split('.')
+ if int(vers[0]) != 0:
+ return vers[0]
+ elif len(vers) >= 2 and int(vers[1]) != 0:
+ return f'0.{vers[1]}'
+ return '0'
+
+
def convert(cargo_ver: str) -> T.List[str]:
"""Convert a Cargo compatible version into a Meson compatible one.
@@ -15,6 +27,8 @@ def convert(cargo_ver: str) -> T.List[str]:
"""
# Cleanup, just for safety
cargo_ver = cargo_ver.strip()
+ if not cargo_ver:
+ return []
cargo_vers = [c.strip() for c in cargo_ver.split(',')]
out: T.List[str] = []
diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py
index 7644c0b..b7ab1ba 100644
--- a/mesonbuild/cmake/common.py
+++ b/mesonbuild/cmake/common.py
@@ -19,6 +19,7 @@ language_map = {
'cuda': 'CUDA',
'objc': 'OBJC',
'objcpp': 'OBJCXX',
+ 'nasm': 'ASM_NASM',
'cs': 'CSharp',
'java': 'Java',
'fortran': 'Fortran',
diff --git a/mesonbuild/cmake/generator.py b/mesonbuild/cmake/generator.py
index a617f8a..fcc37cd 100644
--- a/mesonbuild/cmake/generator.py
+++ b/mesonbuild/cmake/generator.py
@@ -170,6 +170,8 @@ def parse_generator_expressions(
# Evaluate the function
if func in supported:
res = supported[func](args)
+ else:
+ mlog.warning(f"Unknown generator expression '$<{func}:{args}>'.", once=True, fatal=False)
return res
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 9296276..bdaa6d5 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -125,7 +125,7 @@ TRANSFER_DEPENDENCIES_FROM: T.Collection[str] = ['header_only']
_cmake_name_regex = re.compile(r'[^_a-zA-Z0-9]')
def _sanitize_cmake_name(name: str) -> str:
name = _cmake_name_regex.sub('_', name)
- if name in FORBIDDEN_TARGET_NAMES or name.startswith('meson'):
+ if name in FORBIDDEN_TARGET_NAMES or name.startswith('meson') or name[0].isdigit():
name = 'cm_' + name
return name
@@ -223,6 +223,7 @@ class ConverterTarget:
self.install = target.install
self.install_dir: T.Optional[Path] = None
self.link_libraries = target.link_libraries
+ self.link_targets: T.List[str] = []
self.link_flags = target.link_flags + target.link_lang_flags
self.public_link_flags: T.List[str] = []
self.depends_raw: T.List[str] = []
@@ -242,6 +243,8 @@ class ConverterTarget:
self.compile_opts: T.Dict[str, T.List[str]] = {}
self.public_compile_opts: T.List[str] = []
self.pie = False
+ self.version: T.Optional[str] = None
+ self.soversion: T.Optional[str] = None
# Project default override options (c_std, cpp_std, etc.)
self.override_options: T.List[str] = []
@@ -356,6 +359,8 @@ class ConverterTarget:
tgt = trace.targets.get(self.cmake_name)
if tgt:
self.depends_raw = trace.targets[self.cmake_name].depends
+ self.version = trace.targets[self.cmake_name].properties.get('VERSION', [None])[0]
+ self.soversion = trace.targets[self.cmake_name].properties.get('SOVERSION', [None])[0]
rtgt = resolve_cmake_trace_targets(self.cmake_name, trace, self.env, clib_compiler=self.clib_compiler)
self.includes += [Path(x) for x in rtgt.include_directories]
@@ -363,6 +368,8 @@ class ConverterTarget:
self.public_link_flags += rtgt.public_link_flags
self.public_compile_opts += rtgt.public_compile_opts
self.link_libraries += rtgt.libraries
+ self.depends_raw += rtgt.target_dependencies
+ self.link_targets += rtgt.target_dependencies
elif self.type.upper() not in ['EXECUTABLE', 'OBJECT_LIBRARY']:
mlog.warning('CMake: Target', mlog.bold(self.cmake_name), 'not found in CMake trace. This can lead to build errors')
@@ -779,12 +786,12 @@ class ConverterCustomTarget:
mlog.log(' -- depends: ', mlog.bold(str(self.depends)))
class CMakeInterpreter:
- def __init__(self, subdir: Path, install_prefix: Path, env: 'Environment', backend: 'Backend'):
+ def __init__(self, subdir: Path, env: 'Environment', backend: 'Backend'):
self.subdir = subdir
self.src_dir = Path(env.get_source_dir(), subdir)
self.build_dir_rel = subdir / '__CMake_build'
self.build_dir = Path(env.get_build_dir()) / self.build_dir_rel
- self.install_prefix = install_prefix
+ self.install_prefix = Path(T.cast('str', env.coredata.optstore.get_value_for(OptionKey('prefix'))))
self.env = env
self.for_machine = MachineChoice.HOST # TODO make parameter
self.backend_name = backend.name
@@ -835,6 +842,8 @@ class CMakeInterpreter:
cmake_args = []
cmake_args += cmake_get_generator_args(self.env)
cmake_args += [f'-DCMAKE_INSTALL_PREFIX={self.install_prefix}']
+ libdir = self.env.coredata.optstore.get_value_for(OptionKey('libdir'))
+ cmake_args += [f'-DCMAKE_INSTALL_LIBDIR={libdir}']
cmake_args += extra_cmake_options
if not any(arg.startswith('-DCMAKE_BUILD_TYPE=') for arg in cmake_args):
# Our build type is favored over any CMAKE_BUILD_TYPE environment variable
@@ -844,6 +853,10 @@ class CMakeInterpreter:
trace_args = self.trace.trace_args()
cmcmp_args = [f'-DCMAKE_POLICY_WARNING_{x}=OFF' for x in DISABLE_POLICY_WARNINGS]
+ if mesonlib.version_compare(cmake_exe.version(), '>= 3.25'):
+ # Enable MSVC debug information variable
+ cmcmp_args += ['-DCMAKE_POLICY_CMP0141=NEW']
+
self.fileapi.setup_request()
# Run CMake
@@ -957,17 +970,27 @@ class CMakeInterpreter:
object_libs += [tgt]
self.languages += [x for x in tgt.languages if x not in self.languages]
- # Second pass: Detect object library dependencies
+ # Second pass: Populate link_with project internal targets
+ for tgt in self.targets:
+ for i in tgt.link_targets:
+ # Handle target-based link libraries
+ link_with = self.output_target_map.target(i)
+ if not link_with or isinstance(link_with, ConverterCustomTarget):
+ # Generated file etc.
+ continue
+ tgt.link_with.append(link_with)
+
+ # Third pass: Detect object library dependencies
for tgt in self.targets:
tgt.process_object_libs(object_libs, self._object_lib_workaround)
- # Third pass: Reassign dependencies to avoid some loops
+ # Fourth pass: Reassign dependencies to avoid some loops
for tgt in self.targets:
tgt.process_inter_target_dependencies()
for ctgt in self.custom_targets:
ctgt.process_inter_target_dependencies()
- # Fourth pass: Remove rassigned dependencies
+ # Fifth pass: Remove reassigned dependencies
for tgt in self.targets:
tgt.cleanup_dependencies()
@@ -1159,6 +1182,12 @@ class CMakeInterpreter:
'objects': [method(x, 'extract_all_objects') for x in objec_libs],
}
+ # Only set version if we know it
+ if tgt.version:
+ tgt_kwargs['version'] = tgt.version
+ if tgt.soversion:
+ tgt_kwargs['soversion'] = tgt.soversion
+
# Only set if installed and only override if it is set
if install_tgt and tgt.install_dir:
tgt_kwargs['install_dir'] = tgt.install_dir
diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py
index d410886..902d6b0 100644
--- a/mesonbuild/cmake/toolchain.py
+++ b/mesonbuild/cmake/toolchain.py
@@ -174,8 +174,23 @@ class CMakeToolchain:
return p
# Set the compiler variables
+ comp_obj = self.compilers.get('c', self.compilers.get('cpp', None))
+ if comp_obj and comp_obj.get_id() == 'msvc':
+ debug_args = comp_obj.get_debug_args(True)
+ if '/Z7' in debug_args:
+ defaults['CMAKE_MSVC_DEBUG_INFORMATION_FORMAT'] = ['Embedded']
+ elif '/Zi' in debug_args:
+ defaults['CMAKE_MSVC_DEBUG_INFORMATION_FORMAT'] = ['ProgramDatabase']
+ elif '/ZI' in debug_args:
+ defaults['CMAKE_MSVC_DEBUG_INFORMATION_FORMAT'] = ['EditAndContinue']
+
for lang, comp_obj in self.compilers.items():
- prefix = 'CMAKE_{}_'.format(language_map.get(lang, lang.upper()))
+ language = language_map.get(lang, None)
+
+ if not language:
+ continue # unsupported language
+
+ prefix = 'CMAKE_{}_'.format(language)
exe_list = comp_obj.get_exelist()
if not exe_list:
@@ -211,7 +226,7 @@ class CMakeToolchain:
# Generate the CMakeLists.txt
mlog.debug('CMake Toolchain: Calling CMake once to generate the compiler state')
languages = list(self.compilers.keys())
- lang_ids = [language_map.get(x, x.upper()) for x in languages]
+ lang_ids = [language_map.get(x) for x in languages if x in language_map]
cmake_content = dedent(f'''
cmake_minimum_required(VERSION 3.10)
project(CompInfo {' '.join(lang_ids)})
diff --git a/mesonbuild/cmake/tracetargets.py b/mesonbuild/cmake/tracetargets.py
index 2cc0c17..b1ecabd 100644
--- a/mesonbuild/cmake/tracetargets.py
+++ b/mesonbuild/cmake/tracetargets.py
@@ -45,6 +45,7 @@ class ResolvedTarget:
self.public_link_flags: T.List[str] = []
self.public_compile_opts: T.List[str] = []
self.libraries: T.List[str] = []
+ self.target_dependencies: T.List[str] = []
def resolve_cmake_trace_targets(target_name: str,
trace: 'CMakeTraceParser',
@@ -86,6 +87,7 @@ def resolve_cmake_trace_targets(target_name: str,
curr_path = Path(*path_to_framework)
framework_path = curr_path.parent
framework_name = curr_path.stem
+ res.public_compile_opts += [f"-F{framework_path}"]
res.libraries += [f'-F{framework_path}', '-framework', framework_name]
else:
res.libraries += [curr]
@@ -94,7 +96,7 @@ def resolve_cmake_trace_targets(target_name: str,
# CMake brute-forces a combination of prefix/suffix combinations to find the
# right library. Assume any bare argument passed which is not also a CMake
# target must be a system library we should try to link against.
- flib = clib_compiler.find_library(curr, env, [])
+ flib = clib_compiler.find_library(curr, [])
if flib is not None:
res.libraries += flib
else:
@@ -144,9 +146,13 @@ def resolve_cmake_trace_targets(target_name: str,
targets += [x for x in tgt.properties['IMPORTED_LOCATION'] if x]
if 'LINK_LIBRARIES' in tgt.properties:
- targets += [x for x in tgt.properties['LINK_LIBRARIES'] if x]
+ link_libraries = [x for x in tgt.properties['LINK_LIBRARIES'] if x]
+ targets += link_libraries
+ res.target_dependencies += link_libraries
if 'INTERFACE_LINK_LIBRARIES' in tgt.properties:
- targets += [x for x in tgt.properties['INTERFACE_LINK_LIBRARIES'] if x]
+ link_libraries = [x for x in tgt.properties['INTERFACE_LINK_LIBRARIES'] if x]
+ targets += link_libraries
+ res.target_dependencies += link_libraries
if f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}' in tgt.properties:
targets += [x for x in tgt.properties[f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}'] if x]
diff --git a/mesonbuild/cmdline.py b/mesonbuild/cmdline.py
new file mode 100644
index 0000000..04f7496
--- /dev/null
+++ b/mesonbuild/cmdline.py
@@ -0,0 +1,182 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2013-2025 The Meson development team
+
+from __future__ import annotations
+
+import argparse
+import ast
+import configparser
+import os
+import shlex
+import typing as T
+from itertools import chain
+
+from . import options
+from .mesonlib import MesonException
+from .options import OptionKey
+
+if T.TYPE_CHECKING:
+ from typing_extensions import Protocol
+
+ # typeshed
+ StrOrBytesPath = T.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]
+
+ class SharedCMDOptions(Protocol):
+ """Representation of command line options from Meson setup, configure,
+ and dist.
+
+ :param cmd_line_options: command line options parsed into an OptionKey:
+ str mapping
+ """
+
+ cmd_line_options: T.Dict[OptionKey, T.Optional[str]]
+ cross_file: T.List[str]
+ native_file: T.List[str]
+
+
+class CmdLineFileParser(configparser.ConfigParser):
+ def __init__(self) -> None:
+ # We don't want ':' as key delimiter, otherwise it would break when
+ # storing subproject options like "subproject:option=value"
+ super().__init__(delimiters=['='], interpolation=None)
+
+ def read(self, filenames: T.Union['StrOrBytesPath', T.Iterable['StrOrBytesPath']], encoding: T.Optional[str] = 'utf-8') -> T.List[str]:
+ return super().read(filenames, encoding)
+
+ def optionxform(self, optionstr: str) -> str:
+ # Don't call str.lower() on keys
+ return optionstr
+
+
+def get_cmd_line_file(build_dir: str) -> str:
+ return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')
+
+def read_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
+ filename = get_cmd_line_file(build_dir)
+ if not os.path.isfile(filename):
+ return
+
+ config = CmdLineFileParser()
+ config.read(filename)
+
+ # Do a copy because config is not really a dict. options.cmd_line_options
+ # overrides values from the file.
+ d = {OptionKey.from_string(k): v for k, v in config['options'].items()}
+ d.update(options.cmd_line_options)
+ options.cmd_line_options = d
+
+ properties = config['properties']
+ if not options.cross_file:
+ options.cross_file = ast.literal_eval(properties.get('cross_file', '[]'))
+ if not options.native_file:
+ # This will be a string in the form: "['first', 'second', ...]", use
+ # literal_eval to get it into the list of strings.
+ options.native_file = ast.literal_eval(properties.get('native_file', '[]'))
+
+def write_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
+ filename = get_cmd_line_file(build_dir)
+ config = CmdLineFileParser()
+
+ properties: T.Dict[str, T.List[str]] = {}
+ if options.cross_file:
+ properties['cross_file'] = options.cross_file
+ if options.native_file:
+ properties['native_file'] = options.native_file
+
+ config['options'] = {str(k): str(v) for k, v in options.cmd_line_options.items()}
+ config['properties'] = {k: repr(v) for k, v in properties.items()}
+ with open(filename, 'w', encoding='utf-8') as f:
+ config.write(f)
+
+def update_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
+ filename = get_cmd_line_file(build_dir)
+ config = CmdLineFileParser()
+ config.read(filename)
+ for k, v in options.cmd_line_options.items():
+ keystr = str(k)
+ if v is not None:
+ config['options'][keystr] = str(v)
+ elif keystr in config['options']:
+ del config['options'][keystr]
+
+ with open(filename, 'w', encoding='utf-8') as f:
+ config.write(f)
+
+def format_cmd_line_options(options: SharedCMDOptions) -> str:
+ cmdline = ['-D{}={}'.format(str(k), v) for k, v in options.cmd_line_options.items()]
+ if options.cross_file:
+ cmdline += [f'--cross-file={f}' for f in options.cross_file]
+ if options.native_file:
+ cmdline += [f'--native-file={f}' for f in options.native_file]
+ return ' '.join([shlex.quote(x) for x in cmdline])
+
+
+class KeyNoneAction(argparse.Action):
+ """
+ Custom argparse Action that stores values in a dictionary as keys with value None.
+ """
+
+ def __init__(self, option_strings: str, dest: str, nargs: T.Optional[T.Union[int, str]] = None, **kwargs: T.Any) -> None:
+ assert nargs is None or nargs == 1
+ super().__init__(option_strings, dest, nargs=1, **kwargs)
+
+ def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace,
+ arg: T.List[str], option_string: str = None) -> None: # type: ignore[override]
+ current_dict = getattr(namespace, self.dest)
+ if current_dict is None:
+ current_dict = {}
+ setattr(namespace, self.dest, current_dict)
+
+ key = OptionKey.from_string(arg[0])
+ current_dict[key] = None
+
+
+class KeyValueAction(argparse.Action):
+ """
+ Custom argparse Action that parses KEY=VAL arguments and stores them in a dictionary.
+ """
+
+ def __init__(self, option_strings: str, dest: str, nargs: T.Optional[T.Union[int, str]] = None, **kwargs: T.Any) -> None:
+ assert nargs is None or nargs == 1
+ super().__init__(option_strings, dest, nargs=1, **kwargs)
+
+ def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace,
+ arg: T.List[str], option_string: str = None) -> None: # type: ignore[override]
+ current_dict = getattr(namespace, self.dest)
+ if current_dict is None:
+ current_dict = {}
+ setattr(namespace, self.dest, current_dict)
+
+ try:
+ keystr, value = arg[0].split('=', 1)
+ key = OptionKey.from_string(keystr)
+ current_dict[key] = value
+ except ValueError:
+ parser.error(f'The argument for option {option_string!r} must be in OPTION=VALUE format.')
+
+
+def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
+ for n, b in options.BUILTIN_OPTIONS.items():
+ options.option_to_argparse(b, n, parser, '')
+ for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items():
+ options.option_to_argparse(b, n, parser, ' (just for host machine)')
+ options.option_to_argparse(b, n.as_build(), parser, ' (just for build machine)')
+ parser.add_argument('-D', action=KeyValueAction, dest='cmd_line_options', default={}, metavar="option=value",
+ help='Set the value of an option, can be used several times to set multiple options.')
+
+def parse_cmd_line_options(args: SharedCMDOptions) -> None:
+ # Merge builtin options set with --option into the dict.
+ for key in chain(
+ options.BUILTIN_OPTIONS.keys(),
+ (k.as_build() for k in options.BUILTIN_OPTIONS_PER_MACHINE.keys()),
+ options.BUILTIN_OPTIONS_PER_MACHINE.keys(),
+ ):
+ name = str(key)
+ value = getattr(args, name, None)
+ if value is not None:
+ if key in args.cmd_line_options:
+ cmdline_name = options.argparse_name_to_arg(name)
+ raise MesonException(
+ f'Got argument {name} as both -D{name} and {cmdline_name}. Pick one.')
+ args.cmd_line_options[key] = value
+ delattr(args, name)
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index aab761a..f645090 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -18,6 +18,7 @@ __all__ = [
'is_library',
'is_llvm_ir',
'is_object',
+ 'is_separate_compile',
'is_source',
'is_java',
'is_known_suffix',
@@ -62,6 +63,7 @@ from .compilers import (
is_object,
is_library,
is_known_suffix,
+ is_separate_compile,
lang_suffixes,
LANGUAGES_USING_LDFLAGS,
sort_clink,
diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py
index d358ca9..c298933 100644
--- a/mesonbuild/compilers/asm.py
+++ b/mesonbuild/compilers/asm.py
@@ -6,14 +6,14 @@ import typing as T
from ..mesonlib import EnvironmentException, get_meson_command
from ..options import OptionKey
from .compilers import Compiler
+from ..linkers.linkers import VisualStudioLikeLinkerMixin
from .mixins.metrowerks import MetrowerksCompiler, mwasmarm_instruction_set_args, mwasmeppc_instruction_set_args
from .mixins.ti import TICompiler
if T.TYPE_CHECKING:
- from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
- from ..envconfig import MachineInfo
+ from ..environment import Environment
nasm_optimization_args: T.Dict[str, T.List[str]] = {
'plain': [],
@@ -26,7 +26,26 @@ nasm_optimization_args: T.Dict[str, T.List[str]] = {
}
-class NasmCompiler(Compiler):
+class ASMCompiler(Compiler):
+
+ """Shared base class for all ASM Compilers (Assemblers)"""
+
+ _SUPPORTED_ARCHES: T.Set[str] = set()
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
+ for_machine: MachineChoice, env: Environment,
+ linker: T.Optional[DynamicLinker] = None,
+ full_version: T.Optional[str] = None):
+ info = env.machines[for_machine]
+ if self._SUPPORTED_ARCHES and info.cpu_family not in self._SUPPORTED_ARCHES:
+ raise EnvironmentException(f'ASM Compiler {self.id} does not support building for {info.cpu_family} CPU family.')
+ super().__init__(ccache, exelist, version, for_machine, env, linker, full_version)
+
+ def sanity_check(self, work_dir: str) -> None:
+ return None
+
+
+class NasmCompiler(ASMCompiler):
language = 'nasm'
id = 'nasm'
@@ -39,15 +58,15 @@ class NasmCompiler(Compiler):
'mtd': ['/DEFAULTLIB:libucrtd.lib', '/DEFAULTLIB:libvcruntimed.lib', '/DEFAULTLIB:libcmtd.lib'],
}
+ _SUPPORTED_ARCHES = {'x86', 'x86_64'}
+
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
- for_machine: 'MachineChoice', info: 'MachineInfo',
+ for_machine: 'MachineChoice', env: Environment,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None, is_cross: bool = False):
- super().__init__(ccache, exelist, version, for_machine, info, linker, full_version, is_cross)
- self.links_with_msvc = False
- if 'link' in self.linker.id:
+ full_version: T.Optional[str] = None):
+ super().__init__(ccache, exelist, version, for_machine, env, linker, full_version)
+ if isinstance(self.linker, VisualStudioLikeLinkerMixin):
self.base_options.add(OptionKey('b_vscrt'))
- self.links_with_msvc = True
def needs_static_linker(self) -> bool:
return True
@@ -96,10 +115,6 @@ class NasmCompiler(Compiler):
def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
return ['-MD', outfile, '-MQ', outtarget]
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
- if self.info.cpu_family not in {'x86', 'x86_64'}:
- raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
-
def get_pic_args(self) -> T.List[str]:
return []
@@ -122,7 +137,7 @@ class NasmCompiler(Compiler):
# require this, otherwise it'll fail to find
# _WinMain or _DllMainCRTStartup.
def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
- if not self.info.is_windows():
+ if not isinstance(self.linker, VisualStudioLikeLinkerMixin):
return []
return self.crt_args[self.get_crt_val(crt_val, buildtype)]
@@ -140,7 +155,7 @@ class YasmCompiler(NasmCompiler):
def get_debug_args(self, is_debug: bool) -> T.List[str]:
if is_debug:
- if self.info.is_windows() and self.links_with_msvc:
+ if isinstance(self.linker, VisualStudioLikeLinkerMixin):
return ['-g', 'cv8']
elif self.info.is_darwin():
return ['-g', 'null']
@@ -152,10 +167,12 @@ class YasmCompiler(NasmCompiler):
return ['--depfile', outfile]
# https://learn.microsoft.com/en-us/cpp/assembler/masm/ml-and-ml64-command-line-reference
-class MasmCompiler(Compiler):
+class MasmCompiler(ASMCompiler):
language = 'masm'
id = 'ml'
+ _SUPPORTED_ARCHES = {'x86', 'x86_64'}
+
def get_compile_only_args(self) -> T.List[str]:
return ['/c']
@@ -183,10 +200,6 @@ class MasmCompiler(Compiler):
return ['/Zi']
return []
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
- if self.info.cpu_family not in {'x86', 'x86_64'}:
- raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
-
def get_pic_args(self) -> T.List[str]:
return []
@@ -210,9 +223,10 @@ class MasmCompiler(Compiler):
# https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference
-class MasmARMCompiler(Compiler):
+class MasmARMCompiler(ASMCompiler):
language = 'masm'
id = 'armasm'
+ _SUPPORTED_ARCHES = {'arm', 'aarch64'}
def needs_static_linker(self) -> bool:
return True
@@ -234,10 +248,6 @@ class MasmARMCompiler(Compiler):
return ['-g']
return []
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
- if self.info.cpu_family not in {'arm', 'aarch64'}:
- raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
-
def get_pic_args(self) -> T.List[str]:
return []
@@ -256,19 +266,23 @@ class MasmARMCompiler(Compiler):
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
return []
+ def get_depfile_format(self) -> str:
+ return 'msvc'
+
def depfile_for_object(self, objfile: str) -> T.Optional[str]:
return None
# https://downloads.ti.com/docs/esd/SPRUI04/
-class TILinearAsmCompiler(TICompiler, Compiler):
+class TILinearAsmCompiler(TICompiler, ASMCompiler):
language = 'linearasm'
+ _SUPPORTED_ARCHES = {'c6000'}
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
- for_machine: MachineChoice, info: MachineInfo,
+ for_machine: MachineChoice, env: Environment,
linker: T.Optional[DynamicLinker] = None,
- full_version: T.Optional[str] = None, is_cross: bool = False):
- Compiler.__init__(self, ccache, exelist, version, for_machine, info, linker, full_version, is_cross)
+ full_version: T.Optional[str] = None):
+ ASMCompiler.__init__(self, ccache, exelist, version, for_machine, env, linker, full_version)
TICompiler.__init__(self)
def needs_static_linker(self) -> bool:
@@ -280,22 +294,18 @@ class TILinearAsmCompiler(TICompiler, Compiler):
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
return []
- def sanity_check(self, work_dir: str, environment: Environment) -> None:
- if self.info.cpu_family not in {'c6000'}:
- raise EnvironmentException(f'TI Linear ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
-
def get_depfile_suffix(self) -> str:
return 'd'
-class MetrowerksAsmCompiler(MetrowerksCompiler, Compiler):
+class MetrowerksAsmCompiler(MetrowerksCompiler, ASMCompiler):
language = 'nasm'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
- for_machine: 'MachineChoice', info: 'MachineInfo',
+ for_machine: 'MachineChoice', env: Environment,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None, is_cross: bool = False):
- Compiler.__init__(self, ccache, exelist, version, for_machine, info, linker, full_version, is_cross)
+ full_version: T.Optional[str] = None):
+ ASMCompiler.__init__(self, ccache, exelist, version, for_machine, env, linker, full_version)
MetrowerksCompiler.__init__(self)
self.warn_args: T.Dict[str, T.List[str]] = {
@@ -321,21 +331,15 @@ class MetrowerksAsmCompiler(MetrowerksCompiler, Compiler):
class MetrowerksAsmCompilerARM(MetrowerksAsmCompiler):
id = 'mwasmarm'
+ _SUPPORTED_ARCHES = {'arm'}
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
return mwasmarm_instruction_set_args.get(instruction_set, None)
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
- if self.info.cpu_family not in {'arm'}:
- raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
-
class MetrowerksAsmCompilerEmbeddedPowerPC(MetrowerksAsmCompiler):
id = 'mwasmeppc'
+ _SUPPORTED_ARCHES = {'ppc'}
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
return mwasmeppc_instruction_set_args.get(instruction_set, None)
-
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
- if self.info.cpu_family not in {'ppc'}:
- raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 7a2fec5..d60ed99 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -15,7 +15,7 @@ from .c_function_attributes import C_FUNC_ATTRIBUTES
from .mixins.apple import AppleCompilerMixin, AppleCStdsMixin
from .mixins.clike import CLikeCompiler
from .mixins.ccrx import CcrxCompiler
-from .mixins.xc16 import Xc16Compiler
+from .mixins.microchip import Xc16Compiler, Xc32Compiler, Xc32CStds
from .mixins.compcert import CompCertCompiler
from .mixins.ti import TICompiler
from .mixins.arm import ArmCompiler, ArmclangCompiler
@@ -39,7 +39,6 @@ from .compilers import (
if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -64,24 +63,22 @@ class CCompiler(CLikeCompiler, Compiler):
language = 'c'
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
# If a child ObjC or CPP class has already set it, don't set it ourselves
- Compiler.__init__(self, ccache, exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version, linker=linker)
+ Compiler.__init__(self, ccache, exelist, version, for_machine, env,
+ full_version=full_version, linker=linker)
CLikeCompiler.__init__(self)
def get_no_stdinc_args(self) -> T.List[str]:
return ['-nostdinc']
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = 'int main(void) { int class=0; return class; }\n'
- return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+ return self._sanity_check_impl(work_dir, 'sanitycheckc.c', code)
- def has_header_symbol(self, hname: str, symbol: str, prefix: str,
- env: 'Environment', *,
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
@@ -94,7 +91,7 @@ class CCompiler(CLikeCompiler, Compiler):
#endif
return 0;
}}'''
- return self.compiles(t.format(**fargs), env, extra_args=extra_args,
+ return self.compiles(t.format(**fargs), extra_args=extra_args,
dependencies=dependencies)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -108,12 +105,11 @@ class CCompiler(CLikeCompiler, Compiler):
class ClangCCompiler(ClangCStds, ClangCompiler, CCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -132,17 +128,17 @@ class ClangCCompiler(ClangCStds, ClangCompiler, CCompiler):
gnu_winlibs)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
- retval = self.get_compileropt_value('winlibs', env, target, subproject)
+ retval = self.get_compileropt_value('winlibs', target, subproject)
assert isinstance(retval, list)
libs: T.List[str] = retval.copy()
for l in libs:
@@ -179,18 +175,16 @@ class EmscriptenCCompiler(EmscriptenMixin, ClangCCompiler):
_C2X_VERSION = '>=1.38.35' # 1.38.35 used Clang 9.0.0
_C23_VERSION = '>=3.1.45' # 3.1.45 used Clang 18.0.0
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- if not is_cross:
+ if not env.is_cross_build(for_machine):
raise MesonException('Emscripten compiler can only be used for cross compilation.')
if not version_compare(version, '>=1.39.19'):
raise MesonException('Meson requires Emscripten >= 1.39.19')
- ClangCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker,
- defines=defines, full_version=full_version)
+ ClangCCompiler.__init__(self, ccache, exelist, version, for_machine, env,
+ linker=linker, defines=defines, full_version=full_version)
class ArmclangCCompiler(ArmclangCompiler, CCompiler):
@@ -198,12 +192,11 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler):
Keil armclang
'''
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ArmclangCompiler.__init__(self)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -220,15 +213,15 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler):
std_opt.set_versions(['c90', 'c99', 'c11'], gnu=True)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
@@ -236,12 +229,11 @@ class GnuCCompiler(GnuCStds, GnuCompiler, CCompiler):
_INVALID_PCH_VERSION = ">=3.4.0"
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, env, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall']
if version_compare(self.version, self._INVALID_PCH_VERSION):
@@ -264,19 +256,19 @@ class GnuCCompiler(GnuCStds, GnuCompiler, CCompiler):
gnu_winlibs)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
key = OptionKey('c_std', machine=self.for_machine)
- std = self.get_compileropt_value(key, env, target, subproject)
+ std = self.get_compileropt_value(key, target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typeddict mypy can't figure this out
- retval = self.get_compileropt_value('winlibs', env, target, subproject)
+ retval = self.get_compileropt_value('winlibs', target, subproject)
assert isinstance(retval, list)
libs: T.List[str] = retval.copy()
@@ -290,12 +282,11 @@ class GnuCCompiler(GnuCStds, GnuCompiler, CCompiler):
class PGICCompiler(PGICompiler, CCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
@@ -303,12 +294,11 @@ class NvidiaHPC_CCompiler(PGICompiler, CCompiler):
id = 'nvidia_hpc'
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -319,15 +309,22 @@ class NvidiaHPC_CCompiler(PGICompiler, CCompiler):
std_opt.set_versions(cppstd_choices, gnu=True)
return opts
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
+ args: T.List[str] = []
+ std = self.get_compileropt_value('std', target, subproject)
+ assert isinstance(std, str)
+ if std != 'none':
+ args.append('-std=' + std)
+ return args
+
class ElbrusCCompiler(ElbrusCompiler, CCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ElbrusCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -350,24 +347,20 @@ class ElbrusCCompiler(ElbrusCompiler, CCompiler):
# Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error.
# So we should explicitly fail at this case.
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if funcname == 'lchmod':
return False, False
- else:
- return super().has_function(funcname, prefix, env,
- extra_args=extra_args,
- dependencies=dependencies)
+ return super().has_function(funcname, prefix, extra_args=extra_args, dependencies=dependencies)
class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
IntelGnuLikeCompiler.__init__(self)
self.lang_header = 'c-header'
default_warn_args = ['-Wall', '-w3']
@@ -388,9 +381,9 @@ class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
std_opt.set_versions(stds, gnu=True)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
@@ -415,8 +408,8 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
msvc_winlibs)
return opts
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
- retval = self.get_compileropt_value('winlibs', env, target, subproject)
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
+ retval = self.get_compileropt_value('winlibs', target, subproject)
assert isinstance(retval, list)
libs: T.List[str] = retval.copy()
for l in libs:
@@ -430,12 +423,11 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
_C17_VERSION = '>=19.28'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker,
- full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MSVCCompiler.__init__(self, target)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -451,9 +443,9 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
std_opt.set_versions(stds, gnu=True, gnu_deprecated=True)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
# As of MVSC 16.8, /std:c11 and /std:c17 are the only valid C standard options.
if std in {'c11'}:
@@ -465,16 +457,15 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
class ClangClCCompiler(ClangCStds, ClangClCompiler, VisualStudioLikeCCompilerMixin, CCompiler):
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
- info, linker=linker,
- full_version=full_version)
+ CCompiler.__init__(self, [], exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ClangClCompiler.__init__(self, target)
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
- std = self.get_compileropt_value('std', env, target, subproject)
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != "none":
return [f'/clang:-std={std}']
@@ -486,12 +477,11 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM
"""Intel "ICL" compiler abstraction."""
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
- info, linker=linker,
- full_version=full_version)
+ CCompiler.__init__(self, [], exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
IntelVisualStudioLikeCompiler.__init__(self, target)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -502,9 +492,9 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM
std_opt.set_versions(['c89', 'c99', 'c11'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('winlibs', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std == 'c89':
mlog.log("ICL doesn't explicitly implement c89, setting the standard to 'none', which is close.", once=True)
@@ -520,12 +510,11 @@ class IntelLLVMClCCompiler(IntelClCCompiler):
class ArmCCompiler(ArmCompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker,
- full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ArmCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -536,9 +525,9 @@ class ArmCCompiler(ArmCompiler, CCompiler):
std_opt.set_versions(['c89', 'c99', 'c11'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('--' + std)
@@ -547,11 +536,11 @@ class ArmCCompiler(ArmCompiler, CCompiler):
class CcrxCCompiler(CcrxCompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
CcrxCompiler.__init__(self)
# Override CCompiler.get_always_args
@@ -569,9 +558,9 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std == 'c89':
args.append('-lang=c')
@@ -599,11 +588,11 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
class Xc16CCompiler(Xc16Compiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
Xc16Compiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -617,9 +606,9 @@ class Xc16CCompiler(Xc16Compiler, CCompiler):
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-ansi')
@@ -643,13 +632,27 @@ class Xc16CCompiler(Xc16Compiler, CCompiler):
path = '.'
return ['-I' + path]
+
+class Xc32CCompiler(Xc32CStds, Xc32Compiler, GnuCCompiler):
+
+ """Microchip XC32 C compiler."""
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional[DynamicLinker] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ GnuCCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version, defines=defines)
+ Xc32Compiler.__init__(self)
+
+
class CompCertCCompiler(CompCertCompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
CompCertCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -676,11 +679,11 @@ class CompCertCCompiler(CompCertCompiler, CCompiler):
class TICCompiler(TICompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
TICompiler.__init__(self)
# Override CCompiler.get_always_args
@@ -698,9 +701,9 @@ class TICCompiler(TICompiler, CCompiler):
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('--' + std)
@@ -717,11 +720,11 @@ class MetrowerksCCompilerARM(MetrowerksCompiler, CCompiler):
id = 'mwccarm'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MetrowerksCompiler.__init__(self)
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -732,9 +735,9 @@ class MetrowerksCCompilerARM(MetrowerksCompiler, CCompiler):
self._update_language_stds(opts, ['c99'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-lang')
@@ -745,11 +748,11 @@ class MetrowerksCCompilerEmbeddedPowerPC(MetrowerksCompiler, CCompiler):
id = 'mwcceppc'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MetrowerksCompiler.__init__(self)
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -760,9 +763,9 @@ class MetrowerksCCompilerEmbeddedPowerPC(MetrowerksCompiler, CCompiler):
self._update_language_stds(opts, ['c99'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-lang ' + std)
@@ -772,9 +775,9 @@ class TaskingCCompiler(TaskingCompiler, CCompiler):
id = 'tasking'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
TaskingCompiler.__init__(self)
diff --git a/mesonbuild/compilers/c_function_attributes.py b/mesonbuild/compilers/c_function_attributes.py
index a7258a1..e425dbf 100644
--- a/mesonbuild/compilers/c_function_attributes.py
+++ b/mesonbuild/compilers/c_function_attributes.py
@@ -30,6 +30,13 @@ C_FUNC_ATTRIBUTES = {
'int foo(void) __attribute__((constructor));',
'constructor_priority':
'int foo( void ) __attribute__((__constructor__(65535/2)));',
+ 'counted_by':
+ '''
+ struct foo {
+ unsigned int count;
+ char bar[] __attribute__((counted_by(count)));
+ };
+ ''',
'deprecated':
'int foo(void) __attribute__((deprecated("")));',
'destructor':
@@ -139,7 +146,7 @@ CXX_FUNC_ATTRIBUTES = {
'ifunc':
('extern "C" {'
'int my_foo(void) { return 0; }'
- 'static int (*resolve_foo(void))(void) { return my_foo; }'
+ 'int (*resolve_foo(void))(void) { return my_foo; }'
'}'
'int foo(void) __attribute__((ifunc("resolve_foo")));'),
}
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index ad252a1..2d813eb 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -42,7 +42,7 @@ _T = T.TypeVar('_T')
about. To support a new compiler, add its information below.
Also add corresponding autodetection code in detect.py."""
-header_suffixes = {'h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di'}
+header_suffixes = {'h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di', 'pxd', 'pxi'}
obj_suffixes = {'o', 'obj', 'res'}
# To the emscripten compiler, .js files are libraries
lib_suffixes = {'a', 'lib', 'dll', 'dll.a', 'dylib', 'so', 'js'}
@@ -84,7 +84,7 @@ clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'nasm', 'fortran')
# List of languages that can be linked with C code directly by the linker
# used in build.py:process_compilers() and build.py:get_dynamic_linker()
# This must be sorted, see sort_clink().
-clink_langs = ('d', 'cuda') + clib_langs
+clink_langs = ('rust', 'd', 'cuda') + clib_langs
SUFFIX_TO_LANG = dict(itertools.chain(*(
[(suffix, lang) for suffix in v] for lang, v in lang_suffixes.items())))
@@ -154,6 +154,9 @@ def is_java(fname: mesonlib.FileOrString) -> bool:
suffix = fname.split('.')[-1]
return suffix in lang_suffixes['java']
+def is_separate_compile(fname: mesonlib.FileOrString) -> bool:
+ return not fname.endswith('.rs')
+
def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool:
if isinstance(fname, mesonlib.File):
fname = fname.fname
@@ -170,16 +173,19 @@ def is_object(fname: 'mesonlib.FileOrString') -> bool:
fname = fname.fname
return cached_is_object_by_name(fname)
-def is_library(fname: 'mesonlib.FileOrString') -> bool:
- if isinstance(fname, mesonlib.File):
- fname = fname.fname
-
+@lru_cache(maxsize=None)
+def cached_is_library_by_name(fname: str) -> bool:
if soregex.match(fname):
return True
suffix = fname.split('.')[-1]
return suffix in lib_suffixes
+def is_library(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ return cached_is_library_by_name(fname)
+
def is_known_suffix(fname: 'mesonlib.FileOrString') -> bool:
if isinstance(fname, mesonlib.File):
fname = fname.fname
@@ -267,13 +273,16 @@ def are_asserts_disabled_for_subproject(subproject: str, env: 'Environment') ->
def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Environment') -> T.List[str]:
args: T.List[str] = []
+ lto = False
try:
if env.coredata.get_option_for_target(target, 'b_lto'):
num_threads = get_option_value_for_target(env, target, OptionKey('b_lto_threads'), 0)
ltomode = get_option_value_for_target(env, target, OptionKey('b_lto_mode'), 'default')
args.extend(compiler.get_lto_compile_args(
+ target=target,
threads=num_threads,
mode=ltomode))
+ lto = True
except (KeyError, AttributeError):
pass
try:
@@ -287,11 +296,11 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env
assert isinstance(sanitize, list)
if sanitize == ['none']:
sanitize = []
- sanitize_args = compiler.sanitizer_compile_args(sanitize)
+ sanitize_args = compiler.sanitizer_compile_args(target, sanitize)
# We consider that if there are no sanitizer arguments returned, then
# the language doesn't support them.
if sanitize_args:
- if not compiler.has_multi_arguments(sanitize_args, env)[0]:
+ if not compiler.has_multi_arguments(sanitize_args)[0]:
raise MesonException(f'Compiler {compiler.name_string()} does not support sanitizer arguments {sanitize_args}')
args.extend(sanitize_args)
except KeyError:
@@ -310,12 +319,12 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env
except (KeyError, AttributeError):
pass
try:
- args += compiler.get_assert_args(are_asserts_disabled(target, env), env)
+ args += compiler.get_assert_args(are_asserts_disabled(target, env))
except KeyError:
pass
# This does not need a try...except
- if option_enabled(compiler.base_options, target, env, 'b_bitcode'):
- args.append('-fembed-bitcode')
+ bitcode = option_enabled(compiler.base_options, target, env, 'b_bitcode')
+ args.extend(compiler.get_embed_bitcode_args(bitcode, lto))
try:
crt_val = env.coredata.get_option_for_target(target, 'b_vscrt')
assert isinstance(crt_val, str)
@@ -345,12 +354,16 @@ def get_base_link_args(target: 'BuildTarget',
thinlto_cache_dir = get_option_value_for_target(env, target, OptionKey('b_thinlto_cache_dir'), '')
if thinlto_cache_dir == '':
thinlto_cache_dir = os.path.join(build_dir, 'meson-private', 'thinlto-cache')
+ os.mkdir(thinlto_cache_dir)
num_threads = get_option_value_for_target(env, target, OptionKey('b_lto_threads'), 0)
lto_mode = get_option_value_for_target(env, target, OptionKey('b_lto_mode'), 'default')
args.extend(linker.get_lto_link_args(
+ target=target,
threads=num_threads,
mode=lto_mode,
thinlto_cache_dir=thinlto_cache_dir))
+ obj_cache_path = os.path.join('@PRIVATE_DIR@', "lto.o")
+ args.extend(linker.get_lto_obj_cache_path(obj_cache_path))
except (KeyError, AttributeError):
pass
try:
@@ -358,11 +371,11 @@ def get_base_link_args(target: 'BuildTarget',
assert isinstance(sanitizer, list)
if sanitizer == ['none']:
sanitizer = []
- sanitizer_args = linker.sanitizer_link_args(sanitizer)
+ sanitizer_args = linker.sanitizer_link_args(target, sanitizer)
# We consider that if there are no sanitizer arguments returned, then
# the language doesn't support them.
if sanitizer_args:
- if not linker.has_multi_link_arguments(sanitizer_args, env)[0]:
+ if not linker.has_multi_link_arguments(sanitizer_args)[0]:
raise MesonException(f'Linker {linker.name_string()} does not support sanitizer arguments {sanitizer_args}')
args.extend(sanitizer_args)
except KeyError:
@@ -461,9 +474,9 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
mode = 'COMPILER'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
- for_machine: MachineChoice, info: 'MachineInfo',
+ for_machine: MachineChoice, environment: Environment,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None, is_cross: bool = False):
+ full_version: T.Optional[str] = None):
self.exelist = ccache + exelist
self.exelist_no_ccache = exelist
self.file_suffixes = lang_suffixes[self.language]
@@ -474,10 +487,20 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
self.for_machine = for_machine
self.base_options: T.Set[OptionKey] = set()
self.linker = linker
- self.info = info
- self.is_cross = is_cross
+ self.environment = environment
+ self.is_cross = environment.is_cross_build(for_machine)
self.modes: T.List[Compiler] = []
+ @property
+ def info(self) -> MachineInfo:
+ # This must be fetched dynamically because it may be re-evaluated later,
+ # and we could end up with a stale copy
+ # see :class:`Interpreter._redetect_machines()`
+ return self.environment.machines[self.for_machine]
+
+ def init_from_options(self) -> None:
+ """Initializer compiler attributes that require options to be set."""
+
def __repr__(self) -> str:
repr_str = "<{0}: v{1} `{2}`>"
return repr_str.format(self.__class__.__name__, self.version,
@@ -529,14 +552,14 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_default_suffix(self) -> str:
return self.default_suffix
- def get_define(self, dname: str, prefix: str, env: 'Environment',
+ def get_define(self, dname: str, prefix: str,
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.List['Dependency'],
disable_cache: bool = False) -> T.Tuple[str, bool]:
raise EnvironmentException('%s does not support get_define ' % self.get_id())
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
- guess: T.Optional[int], prefix: str, env: 'Environment', *,
+ guess: T.Optional[int], prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']]) -> int:
raise EnvironmentException('%s does not support compute_int ' % self.get_id())
@@ -545,18 +568,17 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
build_dir: str) -> T.List[str]:
raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id())
- def has_members(self, typename: str, membernames: T.List[str],
- prefix: str, env: 'Environment', *,
+ def has_members(self, typename: str, membernames: T.List[str], prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_member(s) ' % self.get_id())
- def has_type(self, typename: str, prefix: str, env: 'Environment',
+ def has_type(self, typename: str, prefix: str,
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]]], *,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_type ' % self.get_id())
- def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ def symbols_have_underscore_prefix(self) -> bool:
raise EnvironmentException('%s does not support symbols_have_underscore_prefix ' % self.get_id())
def get_exelist(self, ccache: bool = True) -> T.List[str]:
@@ -606,24 +628,19 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def make_option_name(self, key: OptionKey) -> str:
return f'{self.language}_{key.name}'
- @staticmethod
- def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, options.AnyOptionType]) -> MutableKeyedOptionDictType:
- options.update(args)
- return options
-
def get_options(self) -> 'MutableKeyedOptionDictType':
return {}
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
- return self.linker.get_option_link_args(target, env, subproject)
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
+ return self.linker.get_option_link_args(target, subproject)
- def check_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def check_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
"""Check that header is usable.
@@ -634,7 +651,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
"""
raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
- def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def has_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
@@ -654,26 +671,25 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
"""
raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
- def has_header_symbol(self, hname: str, symbol: str, prefix: str,
- env: 'Environment', *,
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language())
- def run(self, code: 'mesonlib.FileOrString', env: 'Environment',
+ def run(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
run_env: T.Optional[T.Dict[str, str]] = None,
run_cwd: T.Optional[str] = None) -> RunResult:
- need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
- if need_exe_wrapper and not env.has_exe_wrapper():
+ need_exe_wrapper = self.environment.need_exe_wrapper(self.for_machine)
+ if need_exe_wrapper and not self.environment.has_exe_wrapper():
raise CrossNoRunException('Can not run test applications in this cross environment.')
- with self._build_wrapper(code, env, extra_args, dependencies, mode=CompileCheckMode.LINK, want_output=True) as p:
+ with self._build_wrapper(code, extra_args, dependencies, mode=CompileCheckMode.LINK, want_output=True) as p:
if p.returncode != 0:
mlog.debug(f'Could not compile test file {p.input_name}: {p.returncode}\n')
return RunResult(False)
if need_exe_wrapper:
- cmdlist = env.exe_wrapper.get_command() + [p.output_name]
+ cmdlist = self.environment.exe_wrapper.get_command() + [p.output_name]
else:
cmdlist = [p.output_name]
try:
@@ -694,11 +710,11 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
# For now we just accept code as a string, as that's what internal callers
# need anyway. If we wanted to accept files, the cache key would need to
# include mtime.
- def cached_run(self, code: str, env: 'Environment', *,
+ def cached_run(self, code: str, *,
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> RunResult:
- run_check_cache = env.coredata.run_check_cache
- args = self.build_wrapper_args(env, extra_args, dependencies, CompileCheckMode('link'))
+ run_check_cache = self.environment.coredata.run_check_cache
+ args = self.build_wrapper_args(extra_args, dependencies, CompileCheckMode('link'))
key = (code, tuple(args))
if key in run_check_cache:
p = run_check_cache[key]
@@ -710,21 +726,21 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
mlog.debug('Cached run stdout:\n', p.stdout)
mlog.debug('Cached run stderr:\n', p.stderr)
else:
- p = self.run(code, env, extra_args=extra_args, dependencies=dependencies)
+ p = self.run(code, extra_args=extra_args, dependencies=dependencies)
run_check_cache[key] = p
return p
- def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ def sizeof(self, typename: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language())
- def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ def alignment(self, typename: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language())
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
"""See if a function exists.
@@ -748,20 +764,19 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
"Always returns a copy that can be independently mutated"
return args.copy()
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
raise EnvironmentException(f'Language {self.get_display_language()} does not support library finding.')
- def get_library_naming(self, env: 'Environment', libtype: LibType,
- strict: bool = False) -> T.Optional[T.Tuple[str, ...]]:
+ def get_library_naming(self, libtype: LibType, strict: bool = False) -> T.Optional[T.Tuple[str, ...]]:
raise EnvironmentException(
'Language {} does not support get_library_naming.'.format(
self.get_display_language()))
- def get_program_dirs(self, env: 'Environment') -> T.List[str]:
+ def get_program_dirs(self) -> T.List[str]:
return []
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
"""Checks if the compiler has all of the arguments.
:returns:
@@ -772,14 +787,14 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
'Language {} does not support has_multi_arguments.'.format(
self.get_display_language()))
- def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_multi_link_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
"""Checks if the linker has all of the arguments.
:returns:
A tuple of (bool, bool). The first value is whether the check
succeeded, and the second is whether it was retrieved from a cache
"""
- return self.linker.has_multi_arguments(args, env)
+ return self.linker.has_multi_arguments(args)
def _get_compile_output(self, dirname: str, mode: CompileCheckMode) -> str:
assert mode != CompileCheckMode.PREPROCESS, 'In pre-processor mode, the output is sent to stdout and discarded'
@@ -819,7 +834,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
if extra_args is None:
extra_args = []
- with TemporaryDirectoryWinProof(dir=temp_dir) as tmpdirname:
+ with TemporaryDirectoryWinProof(dir=temp_dir if temp_dir else None) as tmpdirname:
no_ccache = False
if isinstance(code, str):
srcname = os.path.join(tmpdirname,
@@ -867,7 +882,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
yield result
@contextlib.contextmanager
- def cached_compile(self, code: 'mesonlib.FileOrString', cdata: coredata.CoreData, *,
+ def cached_compile(self, code: 'mesonlib.FileOrString', *,
extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
mode: CompileCheckMode = CompileCheckMode.LINK,
temp_dir: T.Optional[str] = None) -> T.Iterator[CompileResult]:
@@ -878,8 +893,9 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
key: coredata.CompilerCheckCacheKey = (tuple(self.exelist), self.version, code, textra_args, mode)
# Check if not cached, and generate, otherwise get from the cache
- if key in cdata.compiler_check_cache:
- p = cdata.compiler_check_cache[key]
+ cache = self.environment.coredata.compiler_check_cache
+ if key in cache:
+ p = cache[key]
p.cached = True
mlog.debug('Using cached compile:')
mlog.debug('Cached command line: ', ' '.join(p.command), '\n')
@@ -889,7 +905,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
yield p
else:
with self.compile(code, extra_args=extra_args, mode=mode, want_output=False, temp_dir=temp_dir) as p:
- cdata.compiler_check_cache[key] = p
+ cache[key] = p
yield p
def get_colorout_args(self, colortype: str) -> T.List[str]:
@@ -933,11 +949,10 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
"""
return None
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: 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)
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ return self.linker.build_rpath_args(build_dir, from_dir, target, extra_paths)
def get_archive_name(self, filename: str) -> str:
return self.linker.get_archive_name(filename)
@@ -947,19 +962,19 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
return []
return self.linker.get_command_to_archive_shlib()
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
- def thread_link_flags(self, env: 'Environment') -> T.List[str]:
- return self.linker.thread_flags(env)
+ def thread_link_flags(self) -> T.List[str]:
+ return self.linker.thread_flags()
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language())
- def openmp_link_flags(self, env: Environment) -> T.List[str]:
- return self.openmp_flags(env)
+ def openmp_link_flags(self) -> T.List[str]:
+ return self.openmp_flags()
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
return []
def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
@@ -971,7 +986,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
# or does not target Windows
return self.linker.get_win_subsystem_args(value)
- def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_func_attribute(self, name: str) -> T.Tuple[bool, bool]:
raise EnvironmentException(
f'Language {self.get_display_language()} does not support function attributes.')
@@ -1027,17 +1042,24 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
ret.append(arg)
return ret
- def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ def get_embed_bitcode_args(self, bitcode: bool, lto: bool) -> T.List[str]:
+ return []
+
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default') -> T.List[str]:
return []
- def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
- thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
return self.linker.get_lto_args()
- def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
+ def get_lto_obj_cache_path(self, path: str) -> T.List[str]:
+ return self.linker.get_lto_obj_cache_path(path)
+
+ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
return []
- def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
+ def sanitizer_link_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
return self.linker.sanitizer_args(value)
def get_asneeded_args(self) -> T.List[str]:
@@ -1052,11 +1074,10 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_optimization_link_args(self, optimization_level: str) -> T.List[str]:
return self.linker.get_optimization_link_args(optimization_level)
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str,
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str,
darwin_versions: T.Tuple[str, str]) -> T.List[str]:
return self.linker.get_soname_args(
- env, prefix, shlib_name, suffix, soversion,
+ prefix, shlib_name, suffix, soversion,
darwin_versions)
def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]:
@@ -1080,7 +1101,10 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_coverage_link_args(self) -> T.List[str]:
return self.linker.get_coverage_args()
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ return self.linker.gen_vs_module_defs_args(defsfile)
+
+ def get_assert_args(self, disable: bool) -> T.List[str]:
"""Get arguments to enable or disable assertion.
:param disable: Whether to disable assertions
@@ -1148,27 +1172,24 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
# those features explicitly.
return []
- def get_library_dirs(self, env: 'Environment',
- elf_class: T.Optional[int] = None) -> T.List[str]:
+ def get_library_dirs(self, elf_class: T.Optional[int] = None) -> T.List[str]:
return []
def get_return_value(self,
fname: str,
rtype: str,
prefix: str,
- env: 'Environment',
extra_args: T.Optional[T.List[str]],
dependencies: T.Optional[T.List['Dependency']]) -> T.Union[str, int]:
raise EnvironmentException(f'{self.id} does not support get_return_value')
def find_framework(self,
name: str,
- env: 'Environment',
extra_dirs: T.List[str],
allow_system: bool = True) -> T.Optional[T.List[str]]:
raise EnvironmentException(f'{self.id} does not support find_framework')
- def find_framework_paths(self, env: 'Environment') -> T.List[str]:
+ def find_framework_paths(self) -> T.List[str]:
raise EnvironmentException(f'{self.id} does not support find_framework_paths')
def attribute_check_func(self, name: str) -> str:
@@ -1190,7 +1211,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
return ' '.join(self.exelist)
@abc.abstractmethod
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
"""Check that this compiler actually works.
This should provide a simple compile/link test. Something as simple as:
@@ -1200,13 +1221,13 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
is good enough here.
"""
- def run_sanity_check(self, environment: Environment, cmdlist: T.List[str], work_dir: str, use_exe_wrapper_for_cross: bool = True) -> T.Tuple[str, str]:
+ def run_sanity_check(self, cmdlist: T.List[str], work_dir: str, use_exe_wrapper_for_cross: bool = True) -> T.Tuple[str, str]:
# Run sanity check
if self.is_cross and use_exe_wrapper_for_cross:
- if not environment.has_exe_wrapper():
+ if not self.environment.has_exe_wrapper():
# Can't check if the binaries run so we have to assume they do
return ('', '')
- cmdlist = environment.exe_wrapper.get_command() + cmdlist
+ cmdlist = self.environment.exe_wrapper.get_command() + cmdlist
mlog.debug('Running test binary command: ', mesonlib.join_args(cmdlist))
try:
pe, stdo, stde = Popen_safe_logged(cmdlist, 'Sanity check', cwd=work_dir)
@@ -1230,6 +1251,9 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
return []
+ def get_depfile_format(self) -> str:
+ return 'msvc' if self.get_argument_syntax() == 'msvc' else 'gcc'
+
def depfile_for_object(self, objfile: str) -> T.Optional[str]:
return objfile + '.' + self.get_depfile_suffix()
@@ -1275,7 +1299,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
"""Arguments to the compiler to turn off all optimizations."""
return []
- def build_wrapper_args(self, env: 'Environment',
+ def build_wrapper_args(self,
extra_args: T.Union[None, CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']],
mode: CompileCheckMode = CompileCheckMode.COMPILE) -> CompilerArgs:
@@ -1304,16 +1328,16 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
if mode is CompileCheckMode.COMPILE:
# Add DFLAGS from the env
- args += env.coredata.get_external_args(self.for_machine, self.language)
+ args += self.environment.coredata.get_external_args(self.for_machine, self.language)
elif mode is CompileCheckMode.LINK:
# Add LDFLAGS from the env
- args += env.coredata.get_external_link_args(self.for_machine, self.language)
+ args += self.environment.coredata.get_external_link_args(self.for_machine, self.language)
# extra_args must override all other arguments, so we add them last
args += extra_args
return args
@contextlib.contextmanager
- def _build_wrapper(self, code: 'mesonlib.FileOrString', env: 'Environment',
+ def _build_wrapper(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[None, CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: CompileCheckMode = CompileCheckMode.COMPILE, want_output: bool = False,
@@ -1323,15 +1347,15 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
This method isn't meant to be called externally, it's mean to be
wrapped by other methods like compiles() and links().
"""
- args = self.build_wrapper_args(env, extra_args, dependencies, mode)
+ args = self.build_wrapper_args(extra_args, dependencies, mode)
if disable_cache or want_output:
- with self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir) as r:
+ with self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=self.environment.scratch_dir) as r:
yield r
else:
- with self.cached_compile(code, env.coredata, extra_args=args, mode=mode, temp_dir=env.scratch_dir) as r:
+ with self.cached_compile(code, extra_args=args, mode=mode, temp_dir=self.environment.scratch_dir) as r:
yield r
- def compiles(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ def compiles(self, code: 'mesonlib.FileOrString', *,
extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: CompileCheckMode = CompileCheckMode.COMPILE,
@@ -1342,21 +1366,21 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
A tuple of (bool, bool). The first value is whether the check
succeeded, and the second is whether it was retrieved from a cache
"""
- with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
+ with self._build_wrapper(code, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
- def links(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ def links(self, code: 'mesonlib.FileOrString', *,
compiler: T.Optional['Compiler'] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
if compiler:
- with compiler._build_wrapper(code, env, dependencies=dependencies, want_output=True) as r:
+ with compiler._build_wrapper(code, dependencies=dependencies, want_output=True) as r:
objfile = mesonlib.File.from_absolute_file(r.output_name)
- return self.compiles(objfile, env, extra_args=extra_args,
+ return self.compiles(objfile, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.LINK, disable_cache=True)
- return self.compiles(code, env, extra_args=extra_args,
+ return self.compiles(code, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.LINK, disable_cache=disable_cache)
def get_feature_args(self, kwargs: DFeatures, build_to_src: str) -> T.List[str]:
@@ -1396,16 +1420,15 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_compileropt_value(self,
key: T.Union[str, OptionKey],
- env: Environment,
target: T.Optional[BuildTarget],
subproject: T.Optional[str] = None
) -> options.ElementaryOptionValues:
if isinstance(key, str):
key = self.form_compileropt_key(key)
if target:
- return env.coredata.get_option_for_target(target, key)
+ return self.environment.coredata.get_option_for_target(target, key)
else:
- return env.coredata.optstore.get_value_for(key.evolve(subproject=subproject))
+ return self.environment.coredata.optstore.get_value_for(key.evolve(subproject=subproject))
def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[str]) -> None:
key = self.form_compileropt_key('std')
@@ -1414,43 +1437,3 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
if 'none' not in value:
value = ['none'] + value
std.choices = value
-
-
-def get_global_options(lang: str,
- comp: T.Type[Compiler],
- for_machine: MachineChoice,
- env: 'Environment') -> dict[OptionKey, options.AnyOptionType]:
- """Retrieve options that apply to all compilers for a given language."""
- description = f'Extra arguments passed to the {lang}'
- argkey = OptionKey(f'{lang}_args', machine=for_machine)
- largkey = OptionKey(f'{lang}_link_args', machine=for_machine)
- envkey = OptionKey(f'{lang}_env_args', machine=for_machine)
-
- comp_key = argkey if argkey in env.options else envkey
-
- comp_options = env.options.get(comp_key, [])
- link_options = env.options.get(largkey, [])
- assert isinstance(comp_options, (str, list)), 'for mypy'
- assert isinstance(link_options, (str, list)), 'for mypy'
-
- cargs = options.UserStringArrayOption(
- argkey.name,
- description + ' compiler',
- comp_options, split_args=True, allow_dups=True)
-
- largs = options.UserStringArrayOption(
- largkey.name,
- description + ' linker',
- link_options, split_args=True, allow_dups=True)
-
- if comp.INVOKES_LINKER and comp_key == envkey:
- # If the compiler acts as a linker driver, and we're using the
- # environment variable flags for both the compiler and linker
- # arguments, then put the compiler flags in the linker flags as well.
- # This is how autotools works, and the env vars feature is for
- # autotools compatibility.
- largs.extend_value(comp_options)
-
- opts: dict[OptionKey, options.AnyOptionType] = {argkey: cargs, largkey: largs}
-
- return opts
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 01b9bb9..5ed4aea 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -32,11 +32,11 @@ from .mixins.pgi import PGICompiler
from .mixins.emscripten import EmscriptenMixin
from .mixins.metrowerks import MetrowerksCompiler
from .mixins.metrowerks import mwccarm_instruction_set_args, mwcceppc_instruction_set_args
+from .mixins.microchip import Xc32Compiler, Xc32CPPStds
if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -66,14 +66,12 @@ class CPPCompiler(CLikeCompiler, Compiler):
language = 'cpp'
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
# If a child ObjCPP class has already set it, don't set it ourselves
- Compiler.__init__(self, ccache, exelist, version, for_machine, info,
- is_cross=is_cross, linker=linker,
- full_version=full_version)
+ Compiler.__init__(self, ccache, exelist, version, for_machine, env,
+ linker=linker, full_version=full_version)
CLikeCompiler.__init__(self)
@classmethod
@@ -86,9 +84,9 @@ class CPPCompiler(CLikeCompiler, Compiler):
def get_no_stdlib_link_args(self) -> T.List[str]:
return ['-nostdlib++']
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = 'class breakCCompiler;int main(void) { return 0; }\n'
- return self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
+ return self._sanity_check_impl(work_dir, 'sanitycheckcpp.cc', code)
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
# -fpermissive allows non-conforming code to compile which is necessary
@@ -96,12 +94,11 @@ class CPPCompiler(CLikeCompiler, Compiler):
# too strict without this and always fails.
return super().get_compiler_check_args(mode) + ['-fpermissive']
- def has_header_symbol(self, hname: str, symbol: str, prefix: str,
- env: 'Environment', *,
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
# Check if it's a C-like symbol
- found, cached = super().has_header_symbol(hname, symbol, prefix, env,
+ found, cached = super().has_header_symbol(hname, symbol, prefix,
extra_args=extra_args,
dependencies=dependencies)
if found:
@@ -113,7 +110,7 @@ class CPPCompiler(CLikeCompiler, Compiler):
#include <{hname}>
using {symbol};
int main(void) {{ return 0; }}'''
- return self.compiles(t, env, extra_args=extra_args,
+ return self.compiles(t, extra_args=extra_args,
dependencies=dependencies)
def _test_cpp_std_arg(self, cpp_std_value: str) -> bool:
@@ -158,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', 'armltdclang', 'intel-llvm'])
+ assert self.id in frozenset(['clang', 'lcc', 'gcc', 'emscripten', 'armltdclang', 'intel-llvm', 'nvidia_hpc', 'xc32-gcc'])
if cpp_std not in CPP_FALLBACKS:
# 'c++03' and 'c++98' don't have fallback types
@@ -186,13 +183,13 @@ class _StdCPPLibMixin(CompilerMixinBase):
def language_stdlib_provider(self, env: Environment) -> str:
# https://stackoverflow.com/a/31658120
- header = 'version' if self.has_header('version', '', env)[0] else 'ciso646'
- is_libcxx = self.has_header_symbol(header, '_LIBCPP_VERSION', '', env)[0]
+ header = 'version' if self.has_header('version', '')[0] else 'ciso646'
+ is_libcxx = self.has_header_symbol(header, '_LIBCPP_VERSION', '')[0]
lib = 'c++' if is_libcxx else 'stdc++'
return lib
@functools.lru_cache(None)
- def language_stdlib_only_link_flags(self, env: Environment) -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
"""Detect the C++ stdlib and default search dirs
As an optimization, this method will cache the value, to avoid building the same values over and over
@@ -205,13 +202,10 @@ class _StdCPPLibMixin(CompilerMixinBase):
# be passed to a different compiler with a different set of default
# search paths, such as when using Clang for C/C++ and gfortran for
# fortran.
- search_dirs = [f'-L{d}' for d in self.get_compiler_dirs(env, 'libraries')]
-
- machine = env.machines[self.for_machine]
- assert machine is not None, 'for mypy'
+ search_dirs = [f'-L{d}' for d in self.get_compiler_dirs('libraries')]
- lib = self.language_stdlib_provider(env)
- if self.find_library(lib, env, []) is not None:
+ lib = self.language_stdlib_provider(self.environment)
+ if self.find_library(lib, []) is not None:
return search_dirs + [f'-l{lib}']
# TODO: maybe a bug exception?
@@ -220,13 +214,12 @@ class _StdCPPLibMixin(CompilerMixinBase):
class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -265,12 +258,12 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
gnu_winlibs)
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- rtti = self.get_compileropt_value('rtti', env, target, subproject)
- debugstl = self.get_compileropt_value('debugstl', env, target, subproject)
- eh = self.get_compileropt_value('eh', env, target, subproject)
+ rtti = self.get_compileropt_value('rtti', target, subproject)
+ debugstl = self.get_compileropt_value('debugstl', target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
assert isinstance(rtti, bool)
assert isinstance(eh, str)
@@ -285,6 +278,7 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
# https://discourse.llvm.org/t/building-a-program-with-d-libcpp-debug-1-against-a-libc-that-is-not-itself-built-with-that-define/59176/3
# Note that unlike _GLIBCXX_DEBUG, _MODE_DEBUG doesn't break ABI. It's just slow.
if version_compare(self.version, '>=18'):
+ args.append('-U_LIBCPP_HARDENING_MODE')
args.append('-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG')
if not rtti:
@@ -292,18 +286,18 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
return args
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append(self._find_best_cpp_std(std))
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typedict mypy can't understand this.
- retval = self.get_compileropt_value('winlibs', env, target, subproject)
+ retval = self.get_compileropt_value('winlibs', target, subproject)
assert isinstance(retval, list)
libs = retval[:]
for l in libs:
@@ -311,7 +305,7 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
return libs
return []
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['-DNDEBUG']
@@ -320,15 +314,10 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
if self.defines.get(macro) is not None:
return []
- if self.language_stdlib_provider(env) == 'stdc++':
+ if self.language_stdlib_provider(self.environment) == 'stdc++':
return ['-D_GLIBCXX_ASSERTIONS=1']
- else:
- if version_compare(self.version, '>=18'):
- return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST']
- elif version_compare(self.version, '>=15'):
- return ['-D_LIBCPP_ENABLE_ASSERTIONS=1']
- return []
+ return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST']
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
args = super().get_pch_use_args(pch_dir, header)
@@ -359,22 +348,20 @@ class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler):
_CPP23_VERSION = '>=2.0.10'
_CPP26_VERSION = '>=3.1.39'
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- if not is_cross:
+ if not env.is_cross_build(for_machine):
raise MesonException('Emscripten compiler can only be used for cross compilation.')
if not version_compare(version, '>=1.39.19'):
raise MesonException('Meson requires Emscripten >= 1.39.19')
- ClangCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker,
- defines=defines, full_version=full_version)
+ ClangCPPCompiler.__init__(self, ccache, exelist, version, for_machine, env,
+ linker=linker, defines=defines, full_version=full_version)
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append(self._find_best_cpp_std(std))
@@ -386,12 +373,11 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
Keil armclang
'''
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ArmclangCompiler.__init__(self)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -416,31 +402,30 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
- eh = self.get_compileropt_value('eh', env, target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
assert isinstance(eh, str)
non_msvc_eh_options(eh, args)
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -480,14 +465,21 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
'Standard Win libraries to link against',
gnu_winlibs)
+ if version_compare(self.version, '>=15.1'):
+ key = key.evolve(name='cpp_importstd')
+ opts[key] = options.UserComboOption(self.make_option_name(key),
+ 'Use #import std.',
+ 'false',
+ choices=['false', 'true'])
+
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- rtti = self.get_compileropt_value('rtti', env, target, subproject)
- debugstl = self.get_compileropt_value('debugstl', env, target, subproject)
- eh = self.get_compileropt_value('eh', env, target, subproject)
+ rtti = self.get_compileropt_value('rtti', target, subproject)
+ debugstl = self.get_compileropt_value('debugstl', target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
assert isinstance(rtti, bool)
assert isinstance(eh, str)
@@ -498,22 +490,23 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
if not rtti:
args.append('-fno-rtti')
+ # We may want to handle libc++'s debugstl mode here too
if debugstl:
args.append('-D_GLIBCXX_DEBUG=1')
return args
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append(self._find_best_cpp_std(std))
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typedict mypy can't understand this.
- retval = self.get_compileropt_value('winlibs', env, target, subproject)
+ retval = self.get_compileropt_value('winlibs', target, subproject)
assert isinstance(retval, list)
libs: T.List[str] = retval[:]
for l in libs:
@@ -521,7 +514,7 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
return libs
return []
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['-DNDEBUG']
@@ -530,13 +523,17 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
if self.defines.get(macro) is not None:
return []
- if self.language_stdlib_provider(env) == 'stdc++':
+ # For GCC, we can assume that the libstdc++ version is the same as
+ # the compiler itself. Anything else isn't supported.
+ if self.language_stdlib_provider(self.environment) == 'stdc++':
return ['-D_GLIBCXX_ASSERTIONS=1']
else:
+ # One can use -stdlib=libc++ with GCC, it just (as of 2025) requires
+ # an experimental configure arg to expose that. libc++ supports "multiple"
+ # versions of GCC (only ever one version of GCC per libc++ version), but
+ # that is "multiple" for our purposes as we can't assume a mapping.
if version_compare(self.version, '>=18'):
return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST']
- elif version_compare(self.version, '>=15'):
- return ['-D_LIBCPP_ENABLE_ASSERTIONS=1']
return []
@@ -545,12 +542,11 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
class PGICPPCompiler(PGICompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
@@ -558,12 +554,11 @@ class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler):
id = 'nvidia_hpc'
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -577,15 +572,22 @@ class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler):
std_opt.set_versions(cppstd_choices)
return opts
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
+ args: T.List[str] = []
+ std = self.get_compileropt_value('std', target, subproject)
+ assert isinstance(std, str)
+ if std != 'none':
+ args.append(self._find_best_cpp_std(std))
+ return args
+
class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
defines: T.Optional[T.Dict[str, str]] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ElbrusCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -628,33 +630,30 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
# Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error.
# So we should explicitly fail at this case.
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if funcname == 'lchmod':
return False, False
- else:
- return super().has_function(funcname, prefix, env,
- extra_args=extra_args,
- dependencies=dependencies)
+ return super().has_function(funcname, prefix, extra_args=extra_args, dependencies=dependencies)
# Elbrus C++ compiler does not support RTTI, so don't check for it.
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- eh = self.get_compileropt_value('eh', env, target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
assert isinstance(eh, str)
non_msvc_eh_options(eh, args)
- debugstl = self.get_compileropt_value('debugstl', env, target, subproject)
+ debugstl = self.get_compileropt_value('debugstl', target, subproject)
assert isinstance(debugstl, str)
if debugstl:
args.append('-D_GLIBCXX_DEBUG=1')
return args
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append(self._find_best_cpp_std(std))
@@ -662,12 +661,11 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
IntelGnuLikeCompiler.__init__(self)
self.lang_header = 'c++-header'
default_warn_args = ['-Wall', '-w3', '-Wpch-messages']
@@ -718,12 +716,12 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
self._update_language_stds(opts, c_stds + g_stds)
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- rtti = self.get_compileropt_value('rtti', env, target, subproject)
- debugstl = self.get_compileropt_value('debugstl', env, target, subproject)
- eh = self.get_compileropt_value('eh', env, target, subproject)
+ rtti = self.get_compileropt_value('rtti', target, subproject)
+ debugstl = self.get_compileropt_value('debugstl', target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
assert isinstance(rtti, bool)
assert isinstance(eh, str)
@@ -731,15 +729,15 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
if eh == 'none':
args.append('-fno-exceptions')
- if rtti:
+ if not rtti:
args.append('-fno-rtti')
if debugstl:
args.append('-D_GLIBCXX_DEBUG=1')
return args
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
remap_cpp03 = {
@@ -750,7 +748,7 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
@@ -777,13 +775,13 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
'c++latest': (False, "latest"),
}
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
# need a typeddict for this
key = self.form_compileropt_key('winlibs').evolve(subproject=subproject)
if target:
- value = env.coredata.get_option_for_target(target, key)
+ value = self.environment.coredata.get_option_for_target(target, key)
else:
- value = env.coredata.optstore.get_value_for(key)
+ value = self.environment.coredata.optstore.get_value_for(key)
return T.cast('T.List[str]', value)[:]
def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List[str]) -> 'MutableKeyedOptionDictType':
@@ -811,13 +809,20 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
std_opt = opts[self.form_compileropt_key('std')]
assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(cpp_stds)
+
+ if version_compare(self.version, '>=19.44.35219'):
+ key = self.form_compileropt_key('importstd')
+ opts[key] = options.UserComboOption(self.make_option_name(key),
+ 'Use #import std.',
+ 'false',
+ choices=['false', 'true'])
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- eh = self.get_compileropt_value('eh', env, target, subproject)
- rtti = self.get_compileropt_value('rtti', env, target, subproject)
+ eh = self.get_compileropt_value('eh', target, subproject)
+ rtti = self.get_compileropt_value('rtti', target, subproject)
assert isinstance(rtti, bool)
assert isinstance(eh, str)
@@ -834,9 +839,9 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
return args
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
permissive, ver = self.VC_VERSION_MAP[std]
@@ -857,23 +862,22 @@ class CPP11AsCPP14Mixin(CompilerMixinBase):
This is a limitation of Clang and MSVC that ICL doesn't share.
"""
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
# Note: there is no explicit flag for supporting C++11; we attempt to do the best we can
# which means setting the C++ standard version to C++14, in compilers that support it
# (i.e., after VS2015U3)
# if one is using anything before that point, one cannot set the standard.
stdkey = self.form_compileropt_key('std').evolve(subproject=subproject)
if target is not None:
- std = env.coredata.get_option_for_target(target, stdkey)
+ std = self.environment.coredata.get_option_for_target(target, stdkey)
else:
- std = env.coredata.optstore.get_value_for(stdkey)
+ std = self.environment.coredata.optstore.get_value_for(stdkey)
if std in {'vc++11', 'c++11'}:
mlog.warning(self.id, 'does not support C++11;',
'attempting best effort; setting the standard to C++14',
once=True, fatal=False)
- original_args = super().get_option_std_args(target, env, subproject)
- std_mapping = {'/std:c++11': '/std:c++14',
- '/std:c++14': '/std:vc++14'}
+ original_args = super().get_option_std_args(target, subproject)
+ std_mapping = {'/std:c++11': '/std:c++14'}
processed_args = [std_mapping.get(x, x) for x in original_args]
return processed_args
@@ -883,11 +887,11 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi
id = 'msvc'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MSVCCompiler.__init__(self, target)
# By default, MSVC has a broken __cplusplus define that pretends to be c++98:
@@ -908,12 +912,12 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi
cpp_stds.extend(['c++20', 'vc++20'])
return self._get_options_impl(super().get_options(), cpp_stds)
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
- std = self.get_compileropt_value('std', env, target, subproject)
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
+ std = self.get_compileropt_value('std', target, subproject)
if std != 'none' and version_compare(self.version, '<19.00.24210'):
mlog.warning('This version of MSVC does not support cpp_std arguments', fatal=False)
- args = super().get_option_std_args(target, env, subproject)
+ args = super().get_option_std_args(target, subproject)
if version_compare(self.version, '<19.11'):
try:
@@ -928,11 +932,11 @@ class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, Cl
id = 'clang-cl'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, [], exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ClangClCompiler.__init__(self, target)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -943,11 +947,11 @@ class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, Cl
class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLikeCompiler, CPPCompiler):
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, [], exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
IntelVisualStudioLikeCompiler.__init__(self, target)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -971,12 +975,11 @@ class IntelLLVMClCPPCompiler(IntelClCPPCompiler):
class ArmCPPCompiler(ArmCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ArmCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -986,9 +989,9 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler):
std_opt.set_versions(['c++03', 'c++11'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std == 'c++11':
args.append('--cpp11')
@@ -996,7 +999,7 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler):
args.append('--cpp')
return args
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
@@ -1004,12 +1007,11 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler):
class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
CcrxCompiler.__init__(self)
# Override CCompiler.get_always_args
@@ -1022,19 +1024,18 @@ class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
def get_output_args(self, outputname: str) -> T.List[str]:
return [f'-output=obj={outputname}']
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
return []
class TICPPCompiler(TICompiler, CPPCompiler):
- def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
TICompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -1045,9 +1046,9 @@ class TICPPCompiler(TICompiler, CPPCompiler):
std_opt.set_versions(['c++03'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('--' + std)
@@ -1056,7 +1057,7 @@ class TICPPCompiler(TICompiler, CPPCompiler):
def get_always_args(self) -> T.List[str]:
return []
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
class C2000CPPCompiler(TICPPCompiler):
@@ -1070,11 +1071,10 @@ class MetrowerksCPPCompilerARM(MetrowerksCompiler, CPPCompiler):
id = 'mwccarm'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MetrowerksCompiler.__init__(self)
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -1085,9 +1085,9 @@ class MetrowerksCPPCompilerARM(MetrowerksCompiler, CPPCompiler):
self._update_language_stds(opts, [])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-lang')
@@ -1098,11 +1098,10 @@ class MetrowerksCPPCompilerEmbeddedPowerPC(MetrowerksCompiler, CPPCompiler):
id = 'mwcceppc'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
MetrowerksCompiler.__init__(self)
def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -1113,10 +1112,23 @@ class MetrowerksCPPCompilerEmbeddedPowerPC(MetrowerksCompiler, CPPCompiler):
self._update_language_stds(opts, [])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-lang ' + std)
return args
+
+
+class Xc32CPPCompiler(Xc32CPPStds, Xc32Compiler, GnuCPPCompiler):
+
+ """Microchip XC32 C++ compiler."""
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional[DynamicLinker] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ GnuCPPCompiler.__init__(self, ccache, exelist, version, for_machine, env,
+ linker=linker, full_version=full_version, defines=defines)
+ Xc32Compiler.__init__(self)
diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py
index 4bbddeb..cacd9ee 100644
--- a/mesonbuild/compilers/cs.py
+++ b/mesonbuild/compilers/cs.py
@@ -15,7 +15,6 @@ from .mixins.islinker import BasicLinkerIsCompilerMixin
if T.TYPE_CHECKING:
from ..dependencies import Dependency
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..mesonlib import MachineChoice
@@ -35,8 +34,8 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
language = 'cs'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- info: 'MachineInfo', runner: T.Optional[str] = None):
- super().__init__([], exelist, version, for_machine, info)
+ env: Environment, runner: T.Optional[str] = None):
+ super().__init__([], exelist, version, for_machine, env)
self.runner = runner
@classmethod
@@ -83,7 +82,7 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
def get_pch_name(self, header_name: str) -> str:
return ''
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
src = 'sanity.cs'
obj = 'sanity.exe'
source_name = os.path.join(work_dir, src)
@@ -102,7 +101,7 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
cmdlist = [self.runner, obj]
else:
cmdlist = [os.path.join(work_dir, obj)]
- self.run_sanity_check(environment, cmdlist, work_dir, use_exe_wrapper_for_cross=False)
+ self.run_sanity_check(cmdlist, work_dir, use_exe_wrapper_for_cross=False)
def needs_static_linker(self) -> bool:
return False
@@ -119,8 +118,8 @@ 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, runner='mono')
+ env: Environment):
+ super().__init__(exelist, version, for_machine, env, runner='mono')
def rsp_file_syntax(self) -> 'RSPFileSyntax':
return RSPFileSyntax.GCC
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index ab00cf1..cf0fcec 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -22,7 +22,6 @@ if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
from ..environment import Environment # noqa: F401
- from ..envconfig import MachineInfo
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -183,11 +182,10 @@ class CudaCompiler(Compiler):
id = 'nvcc'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool,
- host_compiler: Compiler, info: 'MachineInfo',
+ host_compiler: Compiler, env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- super().__init__(ccache, exelist, version, for_machine, info, linker=linker, full_version=full_version, is_cross=is_cross)
+ super().__init__(ccache, exelist, version, for_machine, env, linker=linker, full_version=full_version)
self.host_compiler = host_compiler
self.base_options = host_compiler.base_options
# -Wpedantic generates useless churn due to nvcc's dual compilation model producing
@@ -198,6 +196,7 @@ class CudaCompiler(Compiler):
for level, flags in host_compiler.warn_args.items()
}
self.host_werror_args = ['-Xcompiler=' + x for x in self.host_compiler.get_werror_args()]
+ self.debug_macros_available = version_compare(self.version, '>=12.9')
@classmethod
def _shield_nvcc_list_arg(cls, arg: str, listmode: bool = True) -> str:
@@ -495,10 +494,10 @@ class CudaCompiler(Compiler):
def needs_static_linker(self) -> bool:
return False
- def thread_link_flags(self, environment: 'Environment') -> T.List[str]:
- return self._to_host_flags(self.host_compiler.thread_link_flags(environment), Phase.LINKER)
+ def thread_link_flags(self) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.thread_link_flags(), Phase.LINKER)
- def sanity_check(self, work_dir: str, env: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist))
mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
@@ -551,10 +550,10 @@ class CudaCompiler(Compiler):
# Use the -ccbin option, if available, even during sanity checking.
# Otherwise, on systems where CUDA does not support the default compiler,
# NVCC becomes unusable.
- flags += self.get_ccbin_args(None, env, '')
+ flags += self._get_ccbin_args(None, '')
# If cross-compiling, we can't run the sanity check, only compile it.
- if self.is_cross and not env.has_exe_wrapper():
+ if self.is_cross and not self.environment.has_exe_wrapper():
# Linking cross built apps is painful. You can't really
# tell if you should use -nostdlib or not and for example
# on OSX the compiler binary is the same but you need
@@ -581,7 +580,7 @@ class CudaCompiler(Compiler):
cmdlist = self.exelist + ['--run', f'"{binary_name}"']
try:
- stdo, stde = self.run_sanity_check(env, cmdlist, work_dir)
+ stdo, stde = self.run_sanity_check(cmdlist, work_dir)
except EnvironmentException:
raise EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.')
@@ -591,8 +590,7 @@ class CudaCompiler(Compiler):
if stde == '':
self.detected_cc = stdo
- def has_header_symbol(self, hname: str, symbol: str, prefix: str,
- env: 'Environment', *,
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
@@ -608,7 +606,7 @@ class CudaCompiler(Compiler):
#endif
return 0;
}}'''
- found, cached = self.compiles(t.format_map(fargs), env, extra_args=extra_args, dependencies=dependencies)
+ found, cached = self.compiles(t.format_map(fargs), extra_args=extra_args, dependencies=dependencies)
if found:
return True, cached
# Check if it's a class or a template
@@ -618,7 +616,7 @@ class CudaCompiler(Compiler):
int main(void) {{
return 0;
}}'''
- return self.compiles(t.format_map(fargs), env, extra_args=extra_args, dependencies=dependencies)
+ return self.compiles(t.format_map(fargs), extra_args=extra_args, dependencies=dependencies)
_CPP14_VERSION = '>=9.0'
_CPP17_VERSION = '>=11.0'
@@ -650,40 +648,39 @@ class CudaCompiler(Compiler):
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
- args = self.get_ccbin_args(target, env, subproject)
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
+ args = self._get_ccbin_args(target, subproject)
try:
- host_compiler_args = self.host_compiler.get_option_compile_args(target, env, subproject)
+ host_compiler_args = self.host_compiler.get_option_compile_args(target, subproject)
except KeyError:
host_compiler_args = []
return args + self._to_host_flags(host_compiler_args)
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
# On Windows, the version of the C++ standard used by nvcc is dictated by
# the combination of CUDA version and MSVC version; the --std= is thus ignored
# and attempting to use it will result in a warning: https://stackoverflow.com/a/51272091/741027
if not is_windows():
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
return ['--std=' + std]
try:
- host_compiler_args = self.host_compiler.get_option_std_args(target, env, subproject)
+ host_compiler_args = self.host_compiler.get_option_std_args(target, subproject)
except KeyError:
host_compiler_args = []
return self._to_host_flags(host_compiler_args)
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
- args = self.get_ccbin_args(target, env, subproject)
- return args + self._to_host_flags(self.host_compiler.get_option_link_args(target, env, subproject), Phase.LINKER)
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
+ args = self._get_ccbin_args(target, subproject)
+ return args + self._to_host_flags(self.host_compiler.get_option_link_args(target, subproject), Phase.LINKER)
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str,
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str,
darwin_versions: T.Tuple[str, str]) -> T.List[str]:
return self._to_host_flags(self.host_compiler.get_soname_args(
- env, prefix, shlib_name, suffix, soversion, darwin_versions), Phase.LINKER)
+ prefix, shlib_name, suffix, soversion, darwin_versions), Phase.LINKER)
def get_compile_only_args(self) -> T.List[str]:
return ['-c']
@@ -697,11 +694,11 @@ class CudaCompiler(Compiler):
# return self._to_host_flags(self.host_compiler.get_optimization_args(optimization_level))
return cuda_optimization_args[optimization_level]
- def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
- return self._to_host_flags(self.host_compiler.sanitizer_compile_args(value))
+ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.sanitizer_compile_args(target, value))
- def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
- return self._to_host_flags(self.host_compiler.sanitizer_link_args(value))
+ def sanitizer_link_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.sanitizer_link_args(target, value))
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return cuda_debug_args[is_debug]
@@ -730,11 +727,11 @@ class CudaCompiler(Compiler):
def get_optimization_link_args(self, optimization_level: str) -> T.List[str]:
return self._to_host_flags(self.host_compiler.get_optimization_link_args(optimization_level), Phase.LINKER)
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> 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)
+ build_dir, from_dir, target, extra_paths)
return (self._to_host_flags(rpath_args, Phase.LINKER), rpath_dirs_to_remove)
def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
@@ -762,9 +759,9 @@ class CudaCompiler(Compiler):
def get_std_exe_link_args(self) -> T.List[str]:
return self._to_host_flags(self.host_compiler.get_std_exe_link_args(), Phase.LINKER)
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
- return self.host_compiler.find_library(libname, env, extra_dirs, libtype, lib_prefix_warning)
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
+ return self.host_compiler.find_library(libname, extra_dirs, libtype, lib_prefix_warning, ignore_system_dirs)
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
return self._to_host_flags(self.host_compiler.get_crt_compile_args(crt_val, buildtype))
@@ -787,15 +784,13 @@ class CudaCompiler(Compiler):
def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
return self._to_host_flags(super().get_dependency_link_args(dep), Phase.LINKER)
- def get_ccbin_args(self,
- target: 'T.Optional[BuildTarget]',
- env: 'Environment',
- subproject: T.Optional[str] = None) -> T.List[str]:
+ def _get_ccbin_args(self, target: 'T.Optional[BuildTarget]',
+ subproject: T.Optional[str] = None) -> T.List[str]:
key = self.form_compileropt_key('ccbindir').evolve(subproject=subproject)
if target:
- ccbindir = env.coredata.get_option_for_target(target, key)
+ ccbindir = self.environment.coredata.get_option_for_target(target, key)
else:
- ccbindir = env.coredata.optstore.get_value_for(key)
+ ccbindir = self.environment.coredata.optstore.get_value_for(key)
if isinstance(ccbindir, str) and ccbindir != '':
return [self._shield_nvcc_list_arg('-ccbin='+ccbindir, False)]
else:
@@ -807,14 +802,19 @@ class CudaCompiler(Compiler):
def get_profile_use_args(self) -> T.List[str]:
return ['-Xcompiler=' + x for x in self.host_compiler.get_profile_use_args()]
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
- return self.host_compiler.get_assert_args(disable, env)
+ def get_assert_args(self, disable: bool) -> T.List[str]:
+ cccl_macros = []
+ if not disable and self.debug_macros_available:
+ # https://github.com/NVIDIA/cccl/pull/2382
+ cccl_macros = ['-DCCCL_ENABLE_ASSERTIONS=1']
- def has_multi_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]:
+ return self.host_compiler.get_assert_args(disable) + cccl_macros
+
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
args = self._to_host_flags(args)
- return self.compiles('int main(void) { return 0; }', env, extra_args=args, mode=CompileCheckMode.COMPILE)
+ return self.compiles('int main(void) { return 0; }', extra_args=args, mode=CompileCheckMode.COMPILE)
- def has_multi_link_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]:
+ def has_multi_link_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
args = ['-Xnvlink='+self._shield_nvcc_list_arg(s) for s in self.linker.fatal_warnings()]
args += self._to_host_flags(args, phase=Phase.LINKER)
- return self.compiles('int main(void) { return 0; }', env, extra_args=args, mode=CompileCheckMode.LINK)
+ return self.compiles('int main(void) { return 0; }', extra_args=args, mode=CompileCheckMode.LINK)
diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py
index 50bb465..b33147e 100644
--- a/mesonbuild/compilers/cython.py
+++ b/mesonbuild/compilers/cython.py
@@ -12,7 +12,6 @@ from .compilers import Compiler
if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
- from ..environment import Environment
from ..build import BuildTarget
@@ -49,9 +48,9 @@ class CythonCompiler(Compiler):
def get_depfile_suffix(self) -> str:
return 'dep'
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = 'print("hello world")'
- with self.cached_compile(code, environment.coredata) as p:
+ with self.cached_compile(code) as p:
if p.returncode != 0:
raise EnvironmentException(f'Cython compiler {self.id!r} cannot compile programs')
@@ -86,13 +85,13 @@ class CythonCompiler(Compiler):
return opts
- def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_compile_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- version = self.get_compileropt_value('version', env, target, subproject)
+ version = self.get_compileropt_value('version', target, subproject)
assert isinstance(version, str)
args.append(f'-{version}')
- lang = self.get_compileropt_value('language', env, target, subproject)
+ lang = self.get_compileropt_value('language', target, subproject)
assert isinstance(lang, str)
if lang == 'cpp':
args.append('--cplus')
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index 51f2436..dd12094 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -26,7 +26,7 @@ from .mixins.gnu import gnu_common_warning_args
if T.TYPE_CHECKING:
from . import compilers
- from ..build import DFeatures
+ from ..build import BuildTarget, DFeatures
from ..dependencies import Dependency
from ..envconfig import MachineInfo
from ..environment import Environment
@@ -175,9 +175,9 @@ class DmdLikeCompilerMixin(CompilerMixinBase):
def gen_import_library_args(self, implibname: str) -> T.List[str]:
return self.linker.import_library_args(implibname)
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
if self.info.is_windows():
return ([], set())
@@ -188,7 +188,7 @@ class DmdLikeCompilerMixin(CompilerMixinBase):
# split into two separate arguments both prefaced with the -L=.
args: T.List[str] = []
(rpath_args, rpath_dirs_to_remove) = super().build_rpath_args(
- env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+ build_dir, from_dir, target)
for r in rpath_args:
if ',' in r:
a, b = r.split(',', maxsplit=1)
@@ -199,7 +199,7 @@ class DmdLikeCompilerMixin(CompilerMixinBase):
return (args, rpath_dirs_to_remove)
return super().build_rpath_args(
- env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+ build_dir, from_dir, target)
@classmethod
def _translate_args_to_nongnu(cls, args: T.List[str], info: MachineInfo, link_id: str) -> T.List[str]:
@@ -377,11 +377,9 @@ class DmdLikeCompilerMixin(CompilerMixinBase):
return []
return self.mscrt_args[self.get_crt_val(crt_val, buildtype)]
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str,
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str,
darwin_versions: T.Tuple[str, str]) -> T.List[str]:
- sargs = super().get_soname_args(env, prefix, shlib_name, suffix,
- soversion, darwin_versions)
+ sargs = super().get_soname_args(prefix, shlib_name, suffix, soversion, darwin_versions)
# LDC and DMD actually do use a linker, but they proxy all of that with
# their own arguments
@@ -431,15 +429,14 @@ class DCompiler(Compiler):
language = 'd'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- info: 'MachineInfo', arch: str, *,
+ env: Environment, arch: str, *,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None,
- is_cross: bool = False):
- super().__init__([], exelist, version, for_machine, info, linker=linker,
- full_version=full_version, is_cross=is_cross)
+ full_version: T.Optional[str] = None):
+ super().__init__([], exelist, version, for_machine, env, linker=linker,
+ full_version=full_version)
self.arch = arch
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
source_name = os.path.join(work_dir, 'sanity.d')
output_name = os.path.join(work_dir, 'dtest')
with open(source_name, 'w', encoding='utf-8') as ofile:
@@ -448,7 +445,7 @@ class DCompiler(Compiler):
compile_cmdlist = self.exelist + self.get_output_args(output_name) + self._get_target_arch_args() + [source_name]
# If cross-compiling, we can't run the sanity check, only compile it.
- if self.is_cross and not environment.has_exe_wrapper():
+ if self.is_cross and not self.environment.has_exe_wrapper():
compile_cmdlist += self.get_compile_only_args()
pc = subprocess.Popen(compile_cmdlist, cwd=work_dir)
@@ -456,7 +453,7 @@ class DCompiler(Compiler):
if pc.returncode != 0:
raise EnvironmentException('D compiler %s cannot compile programs.' % self.name_string())
- stdo, stde = self.run_sanity_check(environment, [output_name], work_dir)
+ stdo, stde = self.run_sanity_check([output_name], work_dir)
def needs_static_linker(self) -> bool:
return True
@@ -539,8 +536,8 @@ class DCompiler(Compiler):
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> DCompilerArgs:
return DCompilerArgs(self, args)
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
- return self.compiles('int i;\n', env, extra_args=args)
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self.compiles('int i;\n', extra_args=args)
def _get_target_arch_args(self) -> T.List[str]:
# LDC2 on Windows targets to current OS architecture, but
@@ -570,15 +567,15 @@ class DCompiler(Compiler):
args.append(extra_args)
return args
- def run(self, code: 'mesonlib.FileOrString', env: 'Environment',
+ def run(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
run_env: T.Optional[T.Dict[str, str]] = None,
run_cwd: T.Optional[str] = None) -> compilers.RunResult:
extra_args = self._get_compile_extra_args(extra_args)
- return super().run(code, env, extra_args, dependencies, run_env, run_cwd)
+ return super().run(code, extra_args, dependencies, run_env, run_cwd)
- def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ def sizeof(self, typename: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
@@ -590,7 +587,7 @@ class DCompiler(Compiler):
writeln(({typename}).sizeof);
}}
'''
- res = self.cached_run(t, env, extra_args=extra_args,
+ res = self.cached_run(t, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1, False
@@ -598,7 +595,7 @@ class DCompiler(Compiler):
raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout), res.cached
- def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ def alignment(self, typename: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
@@ -610,8 +607,7 @@ class DCompiler(Compiler):
writeln(({typename}).alignof);
}}
'''
- res = self.run(t, env, extra_args=extra_args,
- dependencies=dependencies)
+ res = self.run(t, extra_args=extra_args, dependencies=dependencies)
if not res.compiled:
raise mesonlib.EnvironmentException('Could not compile alignment test.')
if res.returncode != 0:
@@ -621,7 +617,7 @@ class DCompiler(Compiler):
raise mesonlib.EnvironmentException(f'Could not determine alignment of {typename}. Sorry. You might want to file a bug.')
return align, res.cached
- def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def has_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
@@ -630,7 +626,7 @@ class DCompiler(Compiler):
code = f'''{prefix}
import {hname};
'''
- return self.compiles(code, env, extra_args=extra_args,
+ return self.compiles(code, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.COMPILE, disable_cache=disable_cache)
class GnuDCompiler(GnuCompiler, DCompiler):
@@ -640,13 +636,11 @@ class GnuDCompiler(GnuCompiler, DCompiler):
id = 'gcc'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- info: 'MachineInfo', arch: str, *,
+ env: Environment, arch: str, *,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None,
- is_cross: bool = False):
- DCompiler.__init__(self, exelist, version, for_machine, info, arch,
- linker=linker,
- full_version=full_version, is_cross=is_cross)
+ full_version: T.Optional[str] = None):
+ DCompiler.__init__(self, exelist, version, for_machine, env, arch,
+ linker=linker, full_version=full_version)
GnuCompiler.__init__(self, {})
default_warn_args = ['-Wall', '-Wdeprecated']
self.warn_args = {'0': [],
@@ -699,7 +693,7 @@ class GnuDCompiler(GnuCompiler, DCompiler):
return args
return args + ['-shared-libphobos']
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['-frelease']
return []
@@ -723,13 +717,12 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
id = 'llvm'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- info: 'MachineInfo', arch: str, *,
+ env: Environment, arch: str, *,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None,
- is_cross: bool = False, version_output: T.Optional[str] = None):
- DCompiler.__init__(self, exelist, version, for_machine, info, arch,
- linker=linker,
- full_version=full_version, is_cross=is_cross)
+ version_output: T.Optional[str] = None):
+ DCompiler.__init__(self, exelist, version, for_machine, env, arch,
+ linker=linker, full_version=full_version)
DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output))
self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
@@ -769,7 +762,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
return args
return args + ['-link-defaultlib-shared']
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['--release']
return []
@@ -786,13 +779,11 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
id = 'dmd'
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- info: 'MachineInfo', arch: str, *,
+ env: Environment, arch: str, *,
linker: T.Optional['DynamicLinker'] = None,
- full_version: T.Optional[str] = None,
- is_cross: bool = False):
- DCompiler.__init__(self, exelist, version, for_machine, info, arch,
- linker=linker,
- full_version=full_version, is_cross=is_cross)
+ full_version: T.Optional[str] = None):
+ DCompiler.__init__(self, exelist, version, for_machine, env, arch,
+ linker=linker, full_version=full_version)
DmdLikeCompilerMixin.__init__(self, version)
self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
@@ -855,7 +846,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
return args
return args + ['-defaultlib=phobos2', '-debuglib=phobos2']
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['-release']
return []
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
index 53bdd85..db2bdf6 100644
--- a/mesonbuild/compilers/detect.py
+++ b/mesonbuild/compilers/detect.py
@@ -5,9 +5,10 @@ from __future__ import annotations
from ..mesonlib import (
MesonException, EnvironmentException, MachineChoice, join_args,
- search_version, is_windows, Popen_safe, Popen_safe_logged, windows_proof_rm,
+ search_version, is_windows, Popen_safe, Popen_safe_logged, version_compare, windows_proof_rm,
)
-from ..envconfig import BinaryTable
+from ..programs import ExternalProgram
+from ..envconfig import BinaryTable, detect_cpu_family, detect_machine_info
from .. import mlog
from ..linkers import guess_win_linker, guess_nix_linker
@@ -22,6 +23,7 @@ import typing as T
if T.TYPE_CHECKING:
from .compilers import Compiler
+ from .asm import ASMCompiler
from .c import CCompiler
from .cpp import CPPCompiler
from .fortran import FortranCompiler
@@ -46,7 +48,7 @@ if is_windows():
# There is currently no pgc++ for Windows, only for Mac and Linux.
defaults['cpp'] = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl']
# the binary flang-new will be renamed to flang in the foreseeable future
- defaults['fortran'] = ['ifort', 'gfortran', 'flang-new', 'flang', 'pgfortran', 'g95']
+ defaults['fortran'] = ['ifort', 'ifx', 'gfortran', 'flang-new', 'flang', 'pgfortran', 'g95']
defaults['objc'] = ['clang', 'clang-cl', 'gcc']
defaults['objcpp'] = ['clang++', 'clang-cl', 'g++']
defaults['cs'] = ['csc', 'mcs']
@@ -78,6 +80,7 @@ defaults['clang_cl_static_linker'] = ['llvm-lib']
defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']
+defaults['emxomf_static_linker'] = ['emxomfar']
defaults['nasm'] = ['nasm', 'yasm']
@@ -107,9 +110,9 @@ def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoic
if comp is None:
return comp
assert comp.for_machine == for_machine
- env.coredata.process_compiler_options(lang, comp, env, subproject)
+ env.coredata.process_compiler_options(lang, comp, subproject)
if not skip_sanity_check:
- comp.sanity_check(env.get_scratch_dir(), env)
+ comp.sanity_check(env.get_scratch_dir())
env.coredata.compilers[comp.for_machine][lang] = comp
return comp
@@ -118,7 +121,7 @@ def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoic
# =======
def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice,
- allow_build_machine: bool = False) -> T.Tuple[T.List[T.List[str]], T.List[str]]:
+ allow_build_machine: bool = False) -> T.Tuple[T.List[T.List[str]], T.Union[None, ExternalProgram]]:
'''
The list of compilers is detected in the exact same way for
C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
@@ -156,6 +159,7 @@ def _handle_exceptions(
def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker:
from . import d
from ..linkers import linkers
+ from ..options import OptionKey
linker = env.lookup_binary_entry(compiler.for_machine, 'ar')
if linker is not None:
trials = [linker]
@@ -165,6 +169,8 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker
trials = [defaults['cuda_static_linker']] + default_linkers
elif compiler.get_argument_syntax() == 'msvc':
trials = [defaults['vs_static_linker'], defaults['clang_cl_static_linker']]
+ elif env.machines[compiler.for_machine].is_os2() and env.coredata.optstore.get_value_for(OptionKey('os2_emxomf')):
+ trials = [defaults['emxomf_static_linker']] + default_linkers
elif compiler.id == 'gcc':
# Use gcc-ar if available; needed for LTO
trials = [defaults['gcc_static_linker']] + default_linkers
@@ -207,50 +213,55 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker
popen_exceptions[join_args(linker + [arg])] = e
continue
if "xilib: executing 'lib'" in err:
- return linkers.IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ return linkers.IntelVisualStudioLinker(linker, env, getattr(compiler, 'machine', None))
if '/OUT:' in out.upper() or '/OUT:' in err.upper():
- return linkers.VisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ return linkers.VisualStudioLinker(linker, env, getattr(compiler, 'machine', None))
if 'ar-Error-Unknown switch: --version' in err:
- return linkers.PGIStaticLinker(linker)
+ return linkers.PGIStaticLinker(linker, env)
if p.returncode == 0 and 'armar' in linker_name:
- return linkers.ArmarLinker(linker)
+ return linkers.ArmarLinker(linker, env)
if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
assert isinstance(compiler, d.DCompiler)
- return linkers.DLinker(linker, compiler.arch)
+ return linkers.DLinker(linker, env, compiler.arch)
if 'LDC - the LLVM D compiler' in out:
assert isinstance(compiler, d.DCompiler)
- return linkers.DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
+ return linkers.DLinker(linker, env, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
if 'GDC' in out and ' based on D ' in out:
assert isinstance(compiler, d.DCompiler)
- return linkers.DLinker(linker, compiler.arch)
+ return linkers.DLinker(linker, env, compiler.arch)
if err.startswith('Renesas') and 'rlink' in linker_name:
- return linkers.CcrxLinker(linker)
- if out.startswith('GNU ar') and 'xc16-ar' in linker_name:
- return linkers.Xc16Linker(linker)
+ return linkers.CcrxLinker(linker, env)
+ if out.startswith('GNU ar'):
+ if 'xc16-ar' in linker_name:
+ return linkers.Xc16Linker(linker, env)
+ elif 'xc32-ar' in linker_name:
+ return linkers.Xc32ArLinker(compiler.for_machine, linker, env)
if 'Texas Instruments Incorporated' in out:
if 'ar2000' in linker_name:
- return linkers.C2000Linker(linker)
+ return linkers.C2000Linker(linker, env)
elif 'ar6000' in linker_name:
- return linkers.C6000Linker(linker)
+ return linkers.C6000Linker(linker, env)
else:
- return linkers.TILinker(linker)
+ return linkers.TILinker(linker, env)
if out.startswith('The CompCert'):
- return linkers.CompCertLinker(linker)
+ return linkers.CompCertLinker(linker, env)
if out.strip().startswith('Metrowerks') or out.strip().startswith('Freescale'):
if 'ARM' in out:
- return linkers.MetrowerksStaticLinkerARM(linker)
+ return linkers.MetrowerksStaticLinkerARM(linker, env)
else:
- return linkers.MetrowerksStaticLinkerEmbeddedPowerPC(linker)
+ return linkers.MetrowerksStaticLinkerEmbeddedPowerPC(linker, env)
if 'TASKING VX-toolset' in err:
- return linkers.TaskingStaticLinker(linker)
+ return linkers.TaskingStaticLinker(linker, env)
if p.returncode == 0:
- return linkers.ArLinker(compiler.for_machine, linker)
+ return linkers.ArLinker(compiler.for_machine, linker, env)
if p.returncode == 1 and err.startswith('usage'): # OSX
- return linkers.AppleArLinker(compiler.for_machine, linker)
+ return linkers.AppleArLinker(compiler.for_machine, linker, env)
if p.returncode == 1 and err.startswith('Usage'): # AIX
- return linkers.AIXArLinker(linker)
+ return linkers.AIXArLinker(linker, env)
if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
- return linkers.ArLinker(compiler.for_machine, linker)
+ return linkers.ArLinker(compiler.for_machine, linker, env)
+ if p.returncode == 1 and err.startswith('emxomfar'):
+ return linkers.EmxomfArLinker(compiler.for_machine, linker, env)
_handle_exceptions(popen_exceptions, trials, 'linker')
raise EnvironmentException('Unreachable code (exception to make mypy happy)')
@@ -269,11 +280,10 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
from . import c, cpp
from ..linkers import linkers
popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
- compilers, ccache = _get_compilers(env, lang, for_machine)
+ compilers, ccache_exe = _get_compilers(env, lang, for_machine)
+ ccache = ccache_exe.get_command() if (ccache_exe and ccache_exe.found()) else []
if override_compiler is not None:
compilers = [override_compiler]
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
cls: T.Union[T.Type[CCompiler], T.Type[CPPCompiler]]
lnk: T.Union[T.Type[StaticLinker], T.Type[DynamicLinker]]
@@ -340,7 +350,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
guess_gcc_or_lcc = 'gcc'
if 'e2k' in out and 'lcc' in out:
guess_gcc_or_lcc = 'lcc'
- if 'Microchip Technology' in out:
+ if 'Microchip' in out:
# this output has "Free Software Foundation" in its version
guess_gcc_or_lcc = None
@@ -360,13 +370,13 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross,
- info, defines=defines, full_version=full_version,
+ ccache, compiler, version, for_machine,
+ env, defines=defines, full_version=full_version,
linker=linker)
if 'Emscripten' in out:
cls = c.EmscriptenCCompiler if lang == 'c' else cpp.EmscriptenCPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
# emcc requires a file input in order to pass arguments to the
# linker. It'll exit with an error code, but still print the
@@ -376,10 +386,10 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
_, o, _ = Popen_safe(cmd)
linker = linkers.WASMDynamicLinker(
- compiler, for_machine, cls.LINKER_PREFIX,
+ compiler, env, for_machine, cls.LINKER_PREFIX,
[], version=search_version(o))
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
linker=linker, full_version=full_version)
if 'Arm C/C++/Fortran Compiler' in out:
@@ -392,7 +402,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
cls = cpp.ArmLtdClangCPPCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
linker=linker)
if 'armclang' in out:
# The compiler version is not present in the first line of output,
@@ -409,10 +419,10 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
version = search_version(arm_ver_str)
full_version = arm_ver_str
cls = c.ArmclangCCompiler if lang == 'c' else cpp.ArmclangCPPCompiler
- linker = linkers.ArmClangDynamicLinker(for_machine, version=version)
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.ArmClangDynamicLinker(env, for_machine, version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'CL.EXE COMPATIBILITY' in out:
# if this is clang-cl masquerading as cl, detect it as cl, not
@@ -431,7 +441,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
cls = c.ClangClCCompiler if lang == 'c' else cpp.ClangClCPPCompiler
linker = guess_win_linker(env, ['lld-link'], cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info, target,
+ compiler, version, for_machine, env, target,
linker=linker)
# must be detected here before clang because TI compilers contain 'clang' in their output and so that they can be detected as 'clang'
@@ -445,10 +455,10 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
if identifier in out:
cls = compiler_classes[0] if lang == 'c' else compiler_classes[1]
lnk = compiler_classes[2]
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = lnk(compiler, for_machine, version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = lnk(compiler, env, for_machine, version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'clang' in out or 'Clang' in out:
@@ -475,26 +485,26 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
defines=defines, full_version=full_version, linker=linker)
if 'Intel(R) C++ Intel(R)' in err:
version = search_version(err)
target = 'x86' if 'IA-32' in err else 'x86_64'
cls = c.IntelClCCompiler if lang == 'c' else cpp.IntelClCPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.XilinkDynamicLinker(env, for_machine, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info, target,
+ compiler, version, for_machine, env, target,
linker=linker)
if 'Intel(R) oneAPI DPC++/C++ Compiler for applications' in err:
version = search_version(err)
target = 'x86' if 'IA-32' in err else 'x86_64'
cls = c.IntelLLVMClCCompiler if lang == 'c' else cpp.IntelLLVMClCPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.XilinkDynamicLinker(env, for_machine, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info, target,
+ compiler, version, for_machine, env, target,
linker=linker)
if 'Microsoft' in out or 'Microsoft' in err:
# Latest versions of Visual Studio print version
@@ -516,67 +526,89 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
raise EnvironmentException(m)
cls = c.VisualStudioCCompiler if lang == 'c' else cpp.VisualStudioCPPCompiler
linker = guess_win_linker(env, ['link'], cls, version, for_machine)
- # As of this writing, CCache does not support MSVC but sccache does.
- if 'sccache' not in ccache:
- ccache = []
+ if ccache_exe and ccache_exe.found():
+ if ccache_exe.get_name() == 'ccache' and version_compare(ccache_exe.get_version(), '< 4.6'):
+ mlog.warning('Visual Studio support requires ccache 4.6 or higher. You have ccache {}. '.format(ccache_exe.get_version()), once=True)
+ ccache = []
return cls(
- ccache, compiler, version, for_machine, is_cross, info, target,
+ ccache, compiler, version, for_machine, env, target,
full_version=cl_signature, linker=linker)
if 'PGI Compilers' in out:
cls = c.PGICCompiler if lang == 'c' else cpp.PGICPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.PGIDynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross,
- info, linker=linker)
+ ccache, compiler, version, for_machine,
+ env, linker=linker)
if 'NVIDIA Compilers and Tools' in out:
cls = c.NvidiaHPC_CCompiler if lang == 'c' else cpp.NvidiaHPC_CPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.NvidiaHPC_DynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross,
- info, linker=linker)
+ ccache, compiler, version, for_machine,
+ env, linker=linker)
if '(ICC)' in out:
cls = c.IntelCCompiler if lang == 'c' else cpp.IntelCPPCompiler
l = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=l)
if 'Intel(R) oneAPI' in out:
cls = c.IntelLLVMCCompiler if lang == 'c' else cpp.IntelLLVMCPPCompiler
l = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=l)
if 'ARM' in out and not ('Metrowerks' in out or 'Freescale' in out):
cls = c.ArmCCompiler if lang == 'c' else cpp.ArmCPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.ArmDynamicLinker(for_machine, version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.ArmDynamicLinker(env, for_machine, version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross,
- info, full_version=full_version, linker=linker)
+ ccache, compiler, version, for_machine,
+ env, full_version=full_version, linker=linker)
if 'RX Family' in out:
cls = c.CcrxCCompiler if lang == 'c' else cpp.CcrxCPPCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.CcrxDynamicLinker(for_machine, version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.CcrxDynamicLinker(env, for_machine, version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
- if 'Microchip Technology' in out:
- cls = c.Xc16CCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.Xc16DynamicLinker(for_machine, version=version)
- return cls(
- ccache, compiler, version, for_machine, is_cross, info,
- full_version=full_version, linker=linker)
+ if 'Microchip' in out:
+ if 'XC32' in out:
+ # XC32 versions always have the form 'vMAJOR.MINOR'
+ match = re.search(r'XC32.*v(\d+\.\d+)', out)
+ if match:
+ version = match.group(1)
+ else:
+ raise EnvironmentException(f'Failed to detect XC32 compiler version: full version was\n{full_version}')
+
+ cls = c.Xc32CCompiler if lang == 'c' else cpp.Xc32CPPCompiler
+ defines = _get_gnu_compiler_defines(compiler, lang)
+ cls.gcc_version = _get_gnu_version_from_defines(defines)
+
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.Xc32DynamicLinker(compiler, env, for_machine, cls.LINKER_PREFIX, [], version=version)
+
+ return cls(
+ ccache, compiler, version, for_machine,
+ env, defines=defines, full_version=full_version,
+ linker=linker)
+ else:
+ cls = c.Xc16CCompiler
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.Xc16DynamicLinker(env, for_machine, version=version)
+
+ return cls(
+ ccache, compiler, version, for_machine, env,
+ full_version=full_version, linker=linker)
if 'CompCert' in out:
cls = c.CompCertCCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.CompCertDynamicLinker(for_machine, version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.CompCertDynamicLinker(env, for_machine, version=version)
return cls(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'Metrowerks C/C++' in out or 'Freescale C/C++' in out:
@@ -591,7 +623,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
assert mwcc_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None
compiler_version = '.'.join(x for x in mwcc_ver_match.groups() if x is not None)
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
ld = env.lookup_binary_entry(for_machine, cls.language + '_ld')
if ld is not None:
@@ -601,12 +633,12 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
assert mwld_ver_match is not None, 'for mypy' # because mypy *should* be complaining that this could be None
linker_version = '.'.join(x for x in mwld_ver_match.groups() if x is not None)
- linker = lnk(ld, for_machine, version=linker_version)
+ linker = lnk(ld, env, for_machine, version=linker_version)
else:
raise EnvironmentException(f'Failed to detect linker for {cls.id!r} compiler. Please update your cross file(s).')
return cls(
- ccache, compiler, compiler_version, for_machine, is_cross, info,
+ ccache, compiler, compiler_version, for_machine, env,
full_version=full_version, linker=linker)
if 'TASKING VX-toolset' in err:
cls = c.TaskingCCompiler
@@ -616,14 +648,14 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
assert tasking_ver_match is not None, 'for mypy'
tasking_version = '.'.join(x for x in tasking_ver_match.groups() if x is not None)
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
ld = env.lookup_binary_entry(for_machine, cls.language + '_ld')
if ld is None:
raise MesonException(f'{cls.language}_ld was not properly defined in your cross file')
- linker = lnk(ld, for_machine, version=tasking_version)
+ linker = lnk(ld, env, for_machine, version=tasking_version)
return cls(
- ccache, compiler, tasking_version, for_machine, is_cross, info,
+ ccache, compiler, tasking_version, for_machine, env,
full_version=full_version, linker=linker)
_handle_exceptions(popen_exceptions, compilers)
@@ -640,9 +672,8 @@ def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
from ..options import OptionKey
from ..linkers.linkers import CudaLinker
popen_exceptions = {}
- is_cross = env.is_cross_build(for_machine)
- compilers, ccache = _get_compilers(env, 'cuda', for_machine)
- info = env.machines[for_machine]
+ compilers, ccache_exe = _get_compilers(env, 'cuda', for_machine)
+ ccache = ccache_exe.get_command() if (ccache_exe and ccache_exe.found()) else []
for compiler in compilers:
arg = '--version'
try:
@@ -668,15 +699,17 @@ def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
version = out.strip().rsplit('V', maxsplit=1)[-1]
cpp_compiler = detect_cpp_compiler(env, for_machine)
cls = CudaCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
key = OptionKey('cuda_link_args', machine=for_machine)
+ if env.is_cross_build(for_machine):
+ key = key.as_host()
if key in env.options:
# To fix LDFLAGS issue
val = env.options[key]
assert isinstance(val, list)
- env.coredata.set_options({key: cls.to_host_flags_base(val, Phase.LINKER)})
- linker = CudaLinker(compiler, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
- return cls(ccache, compiler, version, for_machine, is_cross, host_compiler=cpp_compiler, info=info, linker=linker)
+ env.coredata.optstore.set_option(key, cls.to_host_flags_base(val, Phase.LINKER))
+ linker = CudaLinker(compiler, env, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
+ return cls(ccache, compiler, version, for_machine, cpp_compiler, env, linker=linker)
raise EnvironmentException(f'Could not find suitable CUDA compiler: "{"; ".join([" ".join(c) for c in compilers])}"')
def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
@@ -684,8 +717,6 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
from ..linkers import linkers
popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
compilers, ccache = _get_compilers(env, 'fortran', for_machine)
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
cls: T.Type[FortranCompiler]
for compiler in compilers:
# capture help text for possible fallback
@@ -721,14 +752,14 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
cls = fortran.ElbrusFortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
defines, full_version=full_version, linker=linker)
else:
version = _get_gnu_version_from_defines(defines)
cls = fortran.GnuFortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
defines, full_version=full_version, linker=linker)
if 'Arm C/C++/Fortran Compiler' in out:
@@ -738,13 +769,13 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
version = '.'.join([x for x in arm_ver_match.groups() if x is not None])
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
linker=linker)
if 'G95' in out:
cls = fortran.G95FortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'Sun Fortran' in err:
@@ -752,64 +783,64 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
cls = fortran.SunFortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'Intel(R) Fortran Compiler for applications' in err:
version = search_version(err)
target = 'x86' if 'IA-32' in err else 'x86_64'
cls = fortran.IntelLLVMClFortranCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.XilinkDynamicLinker(env, for_machine, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
target, linker=linker)
if 'Intel(R) Visual Fortran' in err or 'Intel(R) Fortran' in err:
version = search_version(err)
target = 'x86' if 'IA-32' in err else 'x86_64'
cls = fortran.IntelClFortranCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.XilinkDynamicLinker(env, for_machine, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
target, linker=linker)
if 'ifort (IFORT)' in out:
cls = fortran.IntelFortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'ifx (IFORT)' in out or 'ifx (IFX)' in out:
cls = fortran.IntelLLVMFortranCompiler
linker = guess_nix_linker(env, compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'PathScale EKOPath(tm)' in err:
return fortran.PathScaleFortranCompiler(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version)
if 'PGI Compilers' in out:
cls = fortran.PGIFortranCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.PGIDynamicLinker(compiler, for_machine,
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.PGIDynamicLinker(compiler, env, for_machine,
cls.LINKER_PREFIX, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'NVIDIA Compilers and Tools' in out:
cls = fortran.NvidiaHPC_FortranCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- linker = linkers.PGIDynamicLinker(compiler, for_machine,
+ env.add_lang_args(cls.language, cls, for_machine)
+ linker = linkers.PGIDynamicLinker(compiler, env, for_machine,
cls.LINKER_PREFIX, [], version=version)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
def _get_linker_try_windows(cls: T.Type['Compiler']) -> T.Optional['DynamicLinker']:
@@ -834,14 +865,14 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
cls = fortran.LlvmFlangFortranCompiler
linker = _get_linker_try_windows(cls)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'flang' in out or 'clang' in out:
cls = fortran.ClassicFlangFortranCompiler
linker = _get_linker_try_windows(cls)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'Open64 Compiler Suite' in err:
@@ -849,19 +880,19 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
linker = guess_nix_linker(env,
compiler, cls, version, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'NAG Fortran' in err:
full_version = err.split('\n', 1)[0]
version = full_version.split()[-1]
cls = fortran.NAGFortranCompiler
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
linker = linkers.NAGDynamicLinker(
- compiler, for_machine, cls.LINKER_PREFIX, [],
+ compiler, env, for_machine, cls.LINKER_PREFIX, [],
version=version)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
full_version=full_version, linker=linker)
_handle_exceptions(popen_exceptions, compilers)
@@ -876,9 +907,8 @@ def detect_objcpp_compiler(env: 'Environment', for_machine: MachineChoice) -> 'C
def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice) -> 'Compiler':
from . import objc, objcpp
popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
- compilers, ccache = _get_compilers(env, lang, for_machine)
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
+ compilers, ccache_exe = _get_compilers(env, lang, for_machine)
+ ccache = ccache_exe.get_command() if (ccache_exe and ccache_exe.found()) else []
comp: T.Union[T.Type[objc.ObjCCompiler], T.Type[objcpp.ObjCPPCompiler]]
for compiler in compilers:
@@ -898,9 +928,9 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
comp = objc.GnuObjCCompiler if lang == 'objc' else objcpp.GnuObjCPPCompiler
linker = guess_nix_linker(env, compiler, comp, version, for_machine)
c = comp(
- ccache, compiler, version, for_machine, is_cross, info,
+ ccache, compiler, version, for_machine, env,
defines, linker=linker)
- if not c.compiles('int main(void) { return 0; }', env)[0]:
+ if not c.compiles('int main(void) { return 0; }')[0]:
popen_exceptions[join_args(compiler)] = f'GCC was not built with support for {"objective-c" if lang == "objc" else "objective-c++"}'
continue
return c
@@ -925,14 +955,13 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
linker = guess_nix_linker(env, compiler, comp, version, for_machine)
return comp(
ccache, compiler, version, for_machine,
- is_cross, info, linker=linker, defines=defines)
+ env, linker=linker, defines=defines)
_handle_exceptions(popen_exceptions, compilers)
raise EnvironmentException('Unreachable code (exception to make mypy happy)')
def detect_java_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from .java import JavaCompiler
exelist = env.lookup_binary_entry(for_machine, 'java')
- info = env.machines[for_machine]
if exelist is None:
# TODO support fallback
exelist = [defaults['java'][0]]
@@ -948,15 +977,14 @@ def detect_java_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
if len(parts) > 1:
version = parts[1]
comp_class = JavaCompiler
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class(exelist, version, for_machine, info)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class(exelist, version, for_machine, env)
raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
def detect_cs_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from . import cs
compilers, ccache = _get_compilers(env, 'cs', for_machine)
popen_exceptions = {}
- info = env.machines[for_machine]
for comp in compilers:
try:
p, out, err = Popen_safe_logged(comp + ['--version'], msg='Detecting compiler via')
@@ -972,8 +1000,8 @@ def detect_cs_compiler(env: 'Environment', for_machine: MachineChoice) -> Compil
cls = cs.VisualStudioCsCompiler
else:
continue
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
- return cls(comp, version, for_machine, info)
+ env.add_lang_args(cls.language, cls, for_machine)
+ return cls(comp, version, for_machine, env)
_handle_exceptions(popen_exceptions, compilers)
raise EnvironmentException('Unreachable code (exception to make mypy happy)')
@@ -982,8 +1010,6 @@ def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Co
"""Search for a cython compiler."""
from .cython import CythonCompiler
compilers, _ = _get_compilers(env, 'cython', MachineChoice.BUILD)
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
popen_exceptions: T.Dict[str, Exception] = {}
for comp in compilers:
@@ -1002,16 +1028,14 @@ def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Co
version = search_version(err)
if version is not None:
comp_class = CythonCompiler
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class([], comp, version, for_machine, info, is_cross=is_cross)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class([], comp, version, for_machine, env)
_handle_exceptions(popen_exceptions, compilers)
raise EnvironmentException('Unreachable code (exception to make mypy happy)')
def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from .vala import ValaCompiler
exelist = env.lookup_binary_entry(MachineChoice.BUILD, 'vala')
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
if exelist is None:
# TODO support fallback
exelist = [defaults['vala'][0]]
@@ -1023,8 +1047,8 @@ def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
version = search_version(out)
if 'Vala' in out:
comp_class = ValaCompiler
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class(exelist, version, for_machine, is_cross, info)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class(exelist, version, for_machine, env)
raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> RustCompiler:
@@ -1032,8 +1056,6 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust
from ..linkers import linkers
popen_exceptions: T.Dict[str, Exception] = {}
compilers, _ = _get_compilers(env, 'rust', for_machine)
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
cc = detect_c_compiler(env, for_machine)
is_link_exe = isinstance(cc.linker, linkers.VisualStudioLikeLinkerMixin)
@@ -1117,11 +1139,16 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust
# so we can initialize a new copy for the Rust Compiler
# TODO rewrite this without type: ignore
assert cc.linker is not None, 'for mypy'
+ linker: DynamicLinker
if is_link_exe:
- linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist, # type: ignore
- version=cc.linker.version, **extra_args) # type: ignore
+ # TODO: Due to initializer mismatch we can't use the VisualStudioLikeMixin here
+ # But all of these ahve the same API so we can just pick one.
+ linker = T.cast('T.Type[linkers.MSVCDynamicLinker]', type(cc.linker))(
+ env, for_machine, always_args,
+ exelist=cc.linker.exelist, version=cc.linker.version,
+ **extra_args) # type: ignore
else:
- linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX,
+ linker = type(cc.linker)(compiler, env, for_machine, cc.LINKER_PREFIX,
always_args=always_args, system=cc.linker.system,
version=cc.linker.version, **extra_args)
elif 'link' in override[0]:
@@ -1145,9 +1172,9 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust
c = linker.exelist[1] if linker.exelist[0].endswith('ccache') else linker.exelist[0]
compiler.extend(cls.use_linker_args(c, ''))
- env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ env.add_lang_args(cls.language, cls, for_machine)
return cls(
- compiler, version, for_machine, is_cross, info,
+ compiler, version, for_machine, env,
linker=linker, full_version=full_version)
_handle_exceptions(popen_exceptions, compilers)
@@ -1155,7 +1182,6 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust
def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from . import c, d
- info = env.machines[for_machine]
# Detect the target architecture, required for proper architecture handling on Windows.
# MSVC compiler is required for correct platform detection.
@@ -1164,14 +1190,12 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile
if not is_msvc:
c_compiler = {}
- # Import here to avoid circular imports
- from ..environment import detect_cpu_family
arch = detect_cpu_family(c_compiler)
if is_msvc and arch == 'x86':
arch = 'x86_mscoff'
popen_exceptions = {}
- is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
compilers, ccache = _get_compilers(env, 'd', for_machine)
cls: T.Type[d.DCompiler]
for exelist in compilers:
@@ -1208,7 +1232,7 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile
if info.is_windows() or info.is_cygwin():
objfile = os.path.basename(f)[:-1] + 'obj'
extra_args = [f]
- if is_cross:
+ if env.is_cross_build(for_machine):
extra_args.append(f'-mtriple={info.cpu}-windows')
linker = guess_win_linker(env,
@@ -1228,15 +1252,15 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile
windows_proof_rm(objfile)
return cls(
- exelist, version, for_machine, info, arch,
+ exelist, version, for_machine, env, arch,
full_version=full_version, linker=linker,
- is_cross=is_cross, version_output=out)
+ version_output=out)
elif 'gdc' in out:
cls = d.GnuDCompiler
linker = guess_nix_linker(env, exelist, cls, version, for_machine)
return cls(
- exelist, version, for_machine, info, arch,
- is_cross=is_cross, full_version=full_version, linker=linker)
+ exelist, version, for_machine, env, arch,
+ full_version=full_version, linker=linker)
elif 'The D Language Foundation' in out or 'Digital Mars' in out:
cls = d.DmdDCompiler
# DMD seems to require a file
@@ -1265,7 +1289,7 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile
windows_proof_rm(objfile)
return cls(
- exelist, version, for_machine, info, arch,
+ exelist, version, for_machine, env, arch,
full_version=full_version, linker=linker)
raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
@@ -1275,8 +1299,6 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile
def detect_swift_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from .swift import SwiftCompiler
exelist = env.lookup_binary_entry(for_machine, 'swift')
- is_cross = env.is_cross_build(for_machine)
- info = env.machines[for_machine]
if exelist is None:
# TODO support fallback
exelist = [defaults['swift'][0]]
@@ -1294,13 +1316,12 @@ def detect_swift_compiler(env: 'Environment', for_machine: MachineChoice) -> Com
exelist, cls, version, for_machine,
extra_args=[f.name, '-o', '/dev/null'])
return cls(
- exelist, version, for_machine, is_cross, info, linker=linker)
+ exelist, version, for_machine, env, linker=linker)
raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
from .asm import NasmCompiler, YasmCompiler, MetrowerksAsmCompilerARM, MetrowerksAsmCompilerEmbeddedPowerPC
- is_cross = env.is_cross_build(for_machine)
# When cross compiling and nasm is not defined in the cross file we can
# fallback to the build machine nasm.
@@ -1308,11 +1329,6 @@ def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
# We need a C compiler to properly detect the machine info and linker
cc = detect_c_compiler(env, for_machine)
- if not is_cross:
- from ..environment import detect_machine_info
- info = detect_machine_info({'c': cc})
- else:
- info = env.machines[for_machine]
popen_exceptions: T.Dict[str, Exception] = {}
for comp in compilers:
@@ -1327,39 +1343,38 @@ def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
continue
version = search_version(output)
+ comp_class: T.Type[ASMCompiler]
if 'NASM' in output:
comp_class = NasmCompiler
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class([], comp, version, for_machine, env, cc.linker)
elif 'yasm' in output:
comp_class = YasmCompiler
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class([], comp, version, for_machine, env, cc.linker)
elif 'Metrowerks' in output or 'Freescale' in output:
if 'ARM' in output:
comp_class_mwasmarm = MetrowerksAsmCompilerARM
- env.coredata.add_lang_args(comp_class_mwasmarm.language, comp_class_mwasmarm, for_machine, env)
- return comp_class_mwasmarm([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class_mwasmarm.language, comp_class_mwasmarm, for_machine)
+ return comp_class_mwasmarm([], comp, version, for_machine, env, cc.linker)
else:
comp_class_mwasmeppc = MetrowerksAsmCompilerEmbeddedPowerPC
- env.coredata.add_lang_args(comp_class_mwasmeppc.language, comp_class_mwasmeppc, for_machine, env)
- return comp_class_mwasmeppc([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class_mwasmeppc.language, comp_class_mwasmeppc, for_machine)
+ return comp_class_mwasmeppc([], comp, version, for_machine, env, cc.linker)
_handle_exceptions(popen_exceptions, compilers)
raise EnvironmentException('Unreachable code (exception to make mypy happy)')
def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
# We need a C compiler to properly detect the machine info and linker
- is_cross = env.is_cross_build(for_machine)
cc = detect_c_compiler(env, for_machine)
- if not is_cross:
- from ..environment import detect_machine_info
+ if not env.is_cross_build(for_machine):
info = detect_machine_info({'c': cc})
else:
info = env.machines[for_machine]
from .asm import MasmCompiler, MasmARMCompiler
- comp_class: T.Type[Compiler]
+ comp_class: T.Type[ASMCompiler]
if info.cpu_family == 'x86':
comp = ['ml']
comp_class = MasmCompiler
@@ -1383,8 +1398,8 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
try:
output = Popen_safe(comp + [arg])[2]
version = search_version(output)
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class([], comp, version, for_machine, env, cc.linker)
except OSError as e:
popen_exceptions[' '.join(comp + [arg])] = e
_handle_exceptions(popen_exceptions, [comp])
@@ -1393,18 +1408,16 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
def detect_linearasm_compiler(env: Environment, for_machine: MachineChoice) -> Compiler:
from .asm import TILinearAsmCompiler
comp = ['cl6x']
- comp_class: T.Type[Compiler] = TILinearAsmCompiler
+ comp_class: T.Type[ASMCompiler] = TILinearAsmCompiler
arg = '-h'
- info = env.machines[for_machine]
cc = detect_c_compiler(env, for_machine)
- is_cross = env.is_cross_build(for_machine)
popen_exceptions: T.Dict[str, Exception] = {}
try:
output = Popen_safe(comp + [arg])[2]
version = search_version(output)
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
- return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
+ return comp_class([], comp, version, for_machine, env, cc.linker)
except OSError as e:
popen_exceptions[' '.join(comp + [arg])] = e
_handle_exceptions(popen_exceptions, [comp])
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 5794db0..7654b3f 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -29,7 +29,6 @@ from mesonbuild.mesonlib import (
if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -40,15 +39,14 @@ class FortranCompiler(CLikeCompiler, Compiler):
language = 'fortran'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- Compiler.__init__(self, [], exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version, linker=linker)
+ Compiler.__init__(self, [], exelist, version, for_machine, env,
+ full_version=full_version, linker=linker)
CLikeCompiler.__init__(self)
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise MesonException('Fortran does not have "has_function" capability.\n'
@@ -56,15 +54,15 @@ class FortranCompiler(CLikeCompiler, Compiler):
"meson.get_compiler('fortran').links('block; end block; end program')\n\n"
'that example is to see if the compiler has Fortran 2008 Block element.')
- def _get_basic_compiler_args(self, env: 'Environment', mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
- cargs = env.coredata.get_external_args(self.for_machine, self.language)
- largs = env.coredata.get_external_link_args(self.for_machine, self.language)
+ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
+ cargs = self.environment.coredata.get_external_args(self.for_machine, self.language)
+ largs = self.environment.coredata.get_external_link_args(self.for_machine, self.language)
return cargs, largs
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
source_name = 'sanitycheckf.f'
code = ' PROGRAM MAIN\n PRINT *, "Fortran compilation is working."\n END\n'
- return self._sanity_check_impl(work_dir, environment, source_name, code)
+ return self._sanity_check_impl(work_dir, source_name, code)
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return gnu_optimization_args[optimization_level]
@@ -103,16 +101,16 @@ class FortranCompiler(CLikeCompiler, Compiler):
return filename
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
code = 'stop; end program'
- return self._find_library_impl(libname, env, extra_dirs, code, libtype, lib_prefix_warning)
+ return self._find_library_impl(libname, extra_dirs, code, libtype, lib_prefix_warning, ignore_system_dirs)
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
- return self._has_multi_arguments(args, env, 'stop; end program')
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self._has_multi_arguments(args, 'stop; end program')
- def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
- return self._has_multi_link_arguments(args, env, 'stop; end program')
+ def has_multi_link_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self._has_multi_link_arguments(args, 'stop; end program')
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
@@ -126,7 +124,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
return opts
- def _compile_int(self, expression: str, prefix: str, env: 'Environment',
+ def _compile_int(self, expression: str, prefix: str,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']]) -> bool:
# Use a trick for emulating a static assert
@@ -135,28 +133,27 @@ class FortranCompiler(CLikeCompiler, Compiler):
{prefix}
real(merge(kind(1.),-1,({expression}))), parameter :: fail = 1.
end program test'''
- return self.compiles(t, env, extra_args=extra_args,
- dependencies=dependencies)[0]
+ return self.compiles(t, extra_args=extra_args, dependencies=dependencies)[0]
- def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
- guess: T.Optional[int], prefix: str, env: 'Environment',
- extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ def _cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
# This only difference between this implementation and that of CLikeCompiler
# is a change in logical conjunction operator (.and. instead of &&)
# Try user's guess first
if isinstance(guess, int):
- if self._compile_int(f'{expression} == {guess}', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} == {guess}', prefix, extra_args, dependencies):
return guess
# If no bounds are given, compute them in the limit of int32
maxint = 0x7fffffff
minint = -0x80000000
if not isinstance(low, int) or not isinstance(high, int):
- if self._compile_int(f'{expression} >= 0', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} >= 0', prefix, extra_args, dependencies):
low = cur = 0
- while self._compile_int(f'{expression} > {cur}', prefix, env, extra_args, dependencies):
+ while self._compile_int(f'{expression} > {cur}', prefix, extra_args, dependencies):
low = cur + 1
if low > maxint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
@@ -164,7 +161,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
high = cur
else:
high = cur = -1
- while self._compile_int(f'{expression} < {cur}', prefix, env, extra_args, dependencies):
+ while self._compile_int(f'{expression} < {cur}', prefix, extra_args, dependencies):
high = cur - 1
if high < minint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
@@ -175,13 +172,13 @@ class FortranCompiler(CLikeCompiler, Compiler):
if high < low:
raise mesonlib.EnvironmentException('high limit smaller than low limit')
condition = f'{expression} <= {high} .and. {expression} >= {low}'
- if not self._compile_int(condition, prefix, env, extra_args, dependencies):
+ if not self._compile_int(condition, prefix, extra_args, dependencies):
raise mesonlib.EnvironmentException('Value out of given range')
# Binary search
while low != high:
cur = low + int((high - low) / 2)
- if self._compile_int(f'{expression} <= {cur}', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} <= {cur}', prefix, extra_args, dependencies):
high = cur
else:
low = cur + 1
@@ -189,29 +186,28 @@ class FortranCompiler(CLikeCompiler, Compiler):
return low
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
- guess: T.Optional[int], prefix: str, env: 'Environment', *,
+ guess: T.Optional[int], prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
- return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies)
+ return self._cross_compute_int(expression, low, high, guess, prefix, extra_args, dependencies)
t = f'''program test
{prefix}
print '(i0)', {expression}
end program test
'''
- res = self.run(t, env, extra_args=extra_args,
- dependencies=dependencies)
+ res = self.run(t, extra_args=extra_args, dependencies=dependencies)
if not res.compiled:
return -1
if res.returncode != 0:
raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
- def cross_sizeof(self, typename: str, prefix: str, env: 'Environment', *,
- extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ def _cross_sizeof(self, typename: str, prefix: str, *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
t = f'''program test
@@ -220,19 +216,19 @@ class FortranCompiler(CLikeCompiler, Compiler):
{typename} :: something
end program test
'''
- if not self.compiles(t, env, extra_args=extra_args,
+ if not self.compiles(t, extra_args=extra_args,
dependencies=dependencies)[0]:
return -1
- return self.cross_compute_int('c_sizeof(x)', None, None, None, prefix + '\nuse iso_c_binding\n' + typename + ' :: x', env, extra_args, dependencies)
+ return self._cross_compute_int('c_sizeof(x)', None, None, None, prefix + '\nuse iso_c_binding\n' + typename + ' :: x', extra_args, dependencies)
- def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ def sizeof(self, typename: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
extra_args = []
if self.is_cross:
- r = self.cross_sizeof(typename, prefix, env, extra_args=extra_args,
- dependencies=dependencies)
+ r = self._cross_sizeof(typename, prefix, extra_args=extra_args,
+ dependencies=dependencies)
return r, False
t = f'''program test
use iso_c_binding
@@ -241,7 +237,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
print '(i0)', c_sizeof(x)
end program test
'''
- res = self.cached_run(t, env, extra_args=extra_args,
+ res = self.cached_run(t, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1, False
@@ -250,23 +246,22 @@ class FortranCompiler(CLikeCompiler, Compiler):
return int(res.stdout), res.cached
@functools.lru_cache()
- def output_is_64bit(self, env: 'Environment') -> bool:
+ def output_is_64bit(self) -> bool:
'''
returns true if the output produced is 64-bit, false if 32-bit
'''
- return self.sizeof('type(c_ptr)', '', env)[0] == 8
+ return self.sizeof('type(c_ptr)', '')[0] == 8
class GnuFortranCompiler(GnuCompiler, FortranCompiler):
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall']
self.warn_args = {'0': [],
@@ -285,9 +280,9 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler):
self._update_language_stds(opts, fortran_stds)
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
@@ -302,17 +297,17 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler):
def get_module_outdir_args(self, path: str) -> T.List[str]:
return ['-J' + path]
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
# We need to apply the search prefix here, as these link arguments may
# be passed to a different compiler with a different set of default
# search paths, such as when using Clang for C/C++ and gfortran for
# fortran,
search_dirs: T.List[str] = []
- for d in self.get_compiler_dirs(env, 'libraries'):
+ for d in self.get_compiler_dirs('libraries'):
search_dirs.append(f'-L{d}')
return search_dirs + ['-lgfortran', '-lm']
- def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def has_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
@@ -322,18 +317,18 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler):
https://github.com/mesonbuild/meson/issues/7017
'''
code = f'{prefix}\n#include <{hname}>'
- return self.compiles(code, env, extra_args=extra_args,
+ return self.compiles(code, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.PREPROCESS, disable_cache=disable_cache)
class ElbrusFortranCompiler(ElbrusCompiler, FortranCompiler):
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- FortranCompiler.__init__(self, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ElbrusCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -350,13 +345,11 @@ 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',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
default_warn_args = ['-Wall']
self.warn_args = {'0': [],
'1': default_warn_args,
@@ -388,7 +381,7 @@ class SunFortranCompiler(FortranCompiler):
def get_module_outdir_args(self, path: str) -> T.List[str]:
return ['-moddir=' + path]
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-xopenmp']
@@ -396,13 +389,11 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
id = 'intel'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
# FIXME: Add support for OS X and Windows in detect_fortran_compiler so
# we are sent the type of compiler
IntelGnuLikeCompiler.__init__(self)
@@ -419,9 +410,9 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
assert isinstance(std, str)
if std != 'none':
@@ -434,7 +425,7 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
def get_werror_args(self) -> T.List[str]:
return ['-warn', 'errors']
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
# TODO: needs default search path added
return ['-lifcore', '-limf']
@@ -446,18 +437,22 @@ class IntelLLVMFortranCompiler(IntelFortranCompiler):
id = 'intel-llvm'
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-preprocess-only']
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return []
class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
always_args = ['/nologo']
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo', target: str,
+ env: Environment, target: str,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
IntelVisualStudioLikeCompiler.__init__(self, target)
self.file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp', )
@@ -473,9 +468,9 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'])
return opts
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
assert isinstance(std, str)
if std != 'none':
@@ -497,13 +492,11 @@ class PathScaleFortranCompiler(FortranCompiler):
id = 'pathscale'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
default_warn_args = ['-fullwarn']
self.warn_args = {'0': [],
'1': default_warn_args,
@@ -511,19 +504,17 @@ class PathScaleFortranCompiler(FortranCompiler):
'3': default_warn_args,
'everything': default_warn_args}
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-mp']
class PGIFortranCompiler(PGICompiler, FortranCompiler):
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
default_warn_args = ['-Minform=inform']
@@ -533,7 +524,7 @@ class PGIFortranCompiler(PGICompiler, FortranCompiler):
'3': default_warn_args + ['-Mdclchk'],
'everything': default_warn_args + ['-Mdclchk']}
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
# TODO: needs default search path added
return ['-lpgf90rtl', '-lpgf90', '-lpgf90_rpm1', '-lpgf902',
'-lpgf90rtl', '-lpgftnrtl', '-lrt']
@@ -543,13 +534,11 @@ 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',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
PGICompiler.__init__(self)
default_warn_args = ['-Minform=inform']
@@ -564,13 +553,11 @@ class ClassicFlangFortranCompiler(ClangCompiler, FortranCompiler):
id = 'flang'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, {})
default_warn_args = ['-Minform=inform']
self.warn_args = {'0': [],
@@ -579,14 +566,14 @@ class ClassicFlangFortranCompiler(ClangCompiler, FortranCompiler):
'3': default_warn_args,
'everything': default_warn_args}
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
# We need to apply the search prefix here, as these link arguments may
# be passed to a different compiler with a different set of default
# search paths, such as when using Clang for C/C++ and gfortran for
# fortran,
# XXX: Untested....
search_dirs: T.List[str] = []
- for d in self.get_compiler_dirs(env, 'libraries'):
+ for d in self.get_compiler_dirs('libraries'):
search_dirs.append(f'-L{d}')
return search_dirs + ['-lflang', '-lpgmath']
@@ -600,12 +587,11 @@ class LlvmFlangFortranCompiler(ClangCompiler, FortranCompiler):
id = 'llvm-flang'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo', linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, {})
default_warn_args = ['-Wall']
self.warn_args = {'0': [],
@@ -632,10 +618,10 @@ class LlvmFlangFortranCompiler(ClangCompiler, FortranCompiler):
# https://github.com/llvm/llvm-project/issues/92459
return []
- def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
# matching setup from ClassicFlangFortranCompiler
search_dirs: T.List[str] = []
- for d in self.get_compiler_dirs(env, 'libraries'):
+ for d in self.get_compiler_dirs('libraries'):
search_dirs.append(f'-L{d}')
# does not automatically link to Fortran_main anymore after
# https://github.com/llvm/llvm-project/commit/9d6837d595719904720e5ff68ec1f1a2665bdc2f
@@ -643,20 +629,22 @@ class LlvmFlangFortranCompiler(ClangCompiler, FortranCompiler):
# https://github.com/llvm/llvm-project/commit/8d5386669ed63548daf1bee415596582d6d78d7d;
# it seems flang 18 doesn't work if something accidentally includes a program unit, see
# https://github.com/llvm/llvm-project/issues/92496
- return search_dirs + ['-lFortranRuntime', '-lFortranDecimal']
+ # Only link FortranRuntime and FortranDecimal for flang < 19, see
+ # https://github.com/scipy/scipy/issues/21562#issuecomment-2942938509
+ if version_compare(self.version, '<19'):
+ search_dirs += ['-lFortranRuntime', '-lFortranDecimal']
+ return search_dirs
class Open64FortranCompiler(FortranCompiler):
id = 'open64'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
default_warn_args = ['-fullwarn']
self.warn_args = {'0': [],
'1': default_warn_args,
@@ -664,7 +652,7 @@ class Open64FortranCompiler(FortranCompiler):
'3': default_warn_args,
'everything': default_warn_args}
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-mp']
@@ -672,13 +660,11 @@ class NAGFortranCompiler(FortranCompiler):
id = 'nagfor'
- def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
- info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
FortranCompiler.__init__(self, exelist, version, for_machine,
- is_cross, info, linker=linker,
- full_version=full_version)
+ env, linker=linker, full_version=full_version)
# Warnings are on by default; -w disables (by category):
self.warn_args = {
'0': ['-w=all'],
@@ -707,5 +693,5 @@ class NAGFortranCompiler(FortranCompiler):
def get_std_exe_link_args(self) -> T.List[str]:
return self.get_always_args()
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-openmp']
diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py
index 47d2ac9..f60bc6b 100644
--- a/mesonbuild/compilers/java.py
+++ b/mesonbuild/compilers/java.py
@@ -15,7 +15,6 @@ from .compilers import Compiler
from .mixins.islinker import BasicLinkerIsCompilerMixin
if T.TYPE_CHECKING:
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..mesonlib import MachineChoice
@@ -38,8 +37,8 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
}
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)
+ env: Environment, full_version: T.Optional[str] = None):
+ super().__init__([], exelist, version, for_machine, env, full_version=full_version)
self.javarunner = 'java'
def get_warn_args(self, level: str) -> T.List[str]:
@@ -72,7 +71,7 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
return parameter_list
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
src = 'SanityCheck.java'
obj = 'SanityCheck'
source_name = os.path.join(work_dir, src)
@@ -91,7 +90,7 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
runner = shutil.which(self.javarunner)
if runner:
cmdlist = [runner, '-cp', '.', obj]
- self.run_sanity_check(environment, cmdlist, work_dir, use_exe_wrapper_for_cross=False)
+ self.run_sanity_check(cmdlist, work_dir, use_exe_wrapper_for_cross=False)
else:
m = "Java Virtual Machine wasn't found, but it's needed by Meson. " \
"Please install a JRE.\nIf you have specific needs where this " \
diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py
index 2a09393..4ed2561 100644
--- a/mesonbuild/compilers/mixins/apple.py
+++ b/mesonbuild/compilers/mixins/apple.py
@@ -10,7 +10,7 @@ from ...mesonlib import MesonException
if T.TYPE_CHECKING:
from ..._typing import ImmutableListProtocol
- from ...environment import Environment
+ from ...envconfig import MachineInfo
from ..compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -26,7 +26,11 @@ class AppleCompilerMixin(Compiler):
__BASE_OMP_FLAGS: ImmutableListProtocol[str] = ['-Xpreprocessor', '-fopenmp']
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ if T.TYPE_CHECKING:
+ # Older versions of mypy can't figure this out
+ info: MachineInfo
+
+ def openmp_flags(self) -> T.List[str]:
"""Flags required to compile with OpenMP on Apple.
The Apple Clang Compiler doesn't have builtin support for OpenMP, it
@@ -35,23 +39,19 @@ class AppleCompilerMixin(Compiler):
:return: A list of arguments
"""
- m = env.machines[self.for_machine]
- assert m is not None, 'for mypy'
- if m.cpu_family.startswith('x86'):
+ if self.info.cpu_family.startswith('x86'):
root = '/usr/local'
else:
root = '/opt/homebrew'
return self.__BASE_OMP_FLAGS + [f'-I{root}/opt/libomp/include']
- def openmp_link_flags(self, env: Environment) -> T.List[str]:
- m = env.machines[self.for_machine]
- assert m is not None, 'for mypy'
- if m.cpu_family.startswith('x86'):
+ def openmp_link_flags(self) -> T.List[str]:
+ if self.info.cpu_family.startswith('x86'):
root = '/usr/local'
else:
root = '/opt/homebrew'
- link = self.find_library('omp', env, [f'{root}/opt/libomp/lib'])
+ link = self.find_library('omp', [f'{root}/opt/libomp/lib'])
if not link:
raise MesonException("Couldn't find libomp")
return self.__BASE_OMP_FLAGS + link
@@ -67,7 +67,9 @@ class AppleCStdsMixin(Compiler):
_C17_VERSION = '>=10.0.0'
_C18_VERSION = '>=11.0.0'
- _C2X_VERSION = '>=11.0.0'
+ _C2X_VERSION = '>=11.0.3'
+ _C23_VERSION = '>=17.0.0'
+ _C2Y_VERSION = '>=17.0.0'
class AppleCPPStdsMixin(Compiler):
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
index a70ec4f..e1212a2 100644
--- a/mesonbuild/compilers/mixins/arm.py
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -15,7 +15,6 @@ from ..compilers import clike_debug_args
from .clang import clang_color_args
if T.TYPE_CHECKING:
- from ...environment import Environment
from ...compilers.compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -90,7 +89,7 @@ class ArmCompiler(Compiler):
# PCH files."
return 'pch'
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self,) -> T.List[str]:
return []
def get_coverage_args(self) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py
index d1badaa..a040618 100644
--- a/mesonbuild/compilers/mixins/ccrx.py
+++ b/mesonbuild/compilers/mixins/ccrx.py
@@ -12,7 +12,6 @@ from ...mesonlib import EnvironmentException
if T.TYPE_CHECKING:
from ...envconfig import MachineInfo
- from ...environment import Environment
from ...compilers.compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -67,7 +66,7 @@ class CcrxCompiler(Compiler):
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
def get_coverage_args(self) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
index ae5ab63..851b22f 100644
--- a/mesonbuild/compilers/mixins/clang.py
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -11,16 +11,16 @@ import typing as T
from ... import mesonlib
from ... import options
-from ...linkers.linkers import AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuGoldDynamicLinker, \
- MoldDynamicLinker, MSVCDynamicLinker
+from ...linkers.linkers import AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, \
+ GnuBFDDynamicLinker, GnuGoldDynamicLinker, MoldDynamicLinker, VisualStudioLikeLinkerMixin
from ...options import OptionKey
from ..compilers import CompileCheckMode
from .gnu import GnuLikeCompiler
if T.TYPE_CHECKING:
from ...options import MutableKeyedOptionDictType
- from ...environment import Environment
from ...dependencies import Dependency # noqa: F401
+ from ...build import BuildTarget
from ..compilers import Compiler
CompilerMixinBase = Compiler
@@ -54,6 +54,26 @@ class ClangCompiler(GnuLikeCompiler):
id = 'clang'
+ # -fms-runtime-lib is a compilation option which sets up an automatic dependency
+ # from the .o files to the final link product
+ CRT_D_ARGS: T.Dict[str, T.List[str]] = {
+ 'none': [],
+ 'md': ['-fms-runtime-lib=dll'],
+ 'mdd': ['-fms-runtime-lib=dll_dbg'],
+ 'mt': ['-fms-runtime-lib=static'],
+ 'mtd': ['-fms-runtime-lib=static_dbg'],
+ }
+
+ # disable libcmt to avoid warnings, as that is the default and clang
+ # adds it by default.
+ CRT_ARGS: T.Dict[str, T.List[str]] = {
+ 'none': [],
+ 'md': ['-Wl,/nodefaultlib:libcmt'],
+ 'mdd': ['-Wl,/nodefaultlib:libcmt'],
+ 'mt': [],
+ 'mtd': ['-Wl,/nodefaultlib:libcmt'],
+ }
+
def __init__(self, defines: T.Optional[T.Dict[str, str]]):
super().__init__()
self.defines = defines or {}
@@ -65,11 +85,23 @@ class ClangCompiler(GnuLikeCompiler):
# linkers don't have base_options.
if isinstance(self.linker, AppleDynamicLinker):
self.base_options.add(OptionKey('b_bitcode'))
- elif isinstance(self.linker, MSVCDynamicLinker):
+ elif isinstance(self.linker, VisualStudioLikeLinkerMixin):
self.base_options.add(OptionKey('b_vscrt'))
# All Clang backends can also do LLVM IR
self.can_compile_suffixes.add('ll')
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ if not isinstance(self.linker, VisualStudioLikeLinkerMixin):
+ return []
+ crt_val = self.get_crt_val(crt_val, buildtype)
+ return self.CRT_D_ARGS[crt_val]
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ if not isinstance(self.linker, VisualStudioLikeLinkerMixin):
+ return []
+ crt_val = self.get_crt_val(crt_val, buildtype)
+ return self.CRT_ARGS[crt_val]
+
def get_colorout_args(self, colortype: str) -> T.List[str]:
return clang_color_args[colortype][:]
@@ -117,7 +149,7 @@ class ClangCompiler(GnuLikeCompiler):
myargs.append('-Werror=ignored-optimization-argument')
return super().get_compiler_check_args(mode) + myargs
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
@@ -129,10 +161,10 @@ class ClangCompiler(GnuLikeCompiler):
# TODO: this really should be communicated by the linker
if isinstance(self.linker, AppleDynamicLinker) and mesonlib.version_compare(self.version, '>=8.0'):
extra_args.append('-Wl,-no_weak_imports')
- return super().has_function(funcname, prefix, env, extra_args=extra_args,
+ return super().has_function(funcname, prefix, extra_args=extra_args,
dependencies=dependencies)
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
if mesonlib.version_compare(self.version, '>=3.8.0'):
return ['-fopenmp']
elif mesonlib.version_compare(self.version, '>=3.7.0'):
@@ -141,13 +173,6 @@ class ClangCompiler(GnuLikeCompiler):
# Shouldn't work, but it'll be checked explicitly in the OpenMP dependency.
return []
- def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
- if isinstance(self.linker, (ClangClDynamicLinker, MSVCDynamicLinker)):
- # With MSVC, DLLs only export symbols that are explicitly exported,
- # so if a module defs file is specified, we use that to export symbols
- return ['-Wl,/DEF:' + defsfile]
- return super().gen_vs_module_defs_args(defsfile)
-
@classmethod
def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
# Clang additionally can use a linker specified as a path, which GCC
@@ -155,7 +180,10 @@ class ClangCompiler(GnuLikeCompiler):
# llvm based) is retargetable, while GCC is not.
#
- # qcld: Qualcomm Snapdragon linker, based on LLVM
+ # eld: Qualcomm's opensource embedded linker
+ if linker == 'eld':
+ return ['-fuse-ld=eld']
+ # qcld: Qualcomm's deprecated linker
if linker == 'qcld':
return ['-fuse-ld=qcld']
if linker == 'mold':
@@ -173,10 +201,19 @@ class ClangCompiler(GnuLikeCompiler):
# error.
return ['-Werror=attributes']
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]:
+ if not mesonlib.version_compare(self.version, '>=14'):
+ raise mesonlib.MesonException('prelinking requires clang >=14')
+ return [prelink_name], ['-r', '-o', prelink_name] + obj_list
+
def get_coverage_link_args(self) -> T.List[str]:
return ['--coverage']
- def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ def get_embed_bitcode_args(self, bitcode: bool, lto: bool) -> T.List[str]:
+ return ['-fembed-bitcode'] if bitcode else []
+
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default') -> T.List[str]:
args: T.List[str] = []
if mode == 'thin':
# ThinLTO requires the use of gold, lld, ld64, lld-link or mold 1.1+
@@ -184,23 +221,23 @@ class ClangCompiler(GnuLikeCompiler):
# https://github.com/rui314/mold/commit/46995bcfc3e3113133620bf16445c5f13cd76a18
if not mesonlib.version_compare(self.linker.version, '>=1.1'):
raise mesonlib.MesonException("LLVM's ThinLTO requires mold 1.1+")
- elif not isinstance(self.linker, (AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuGoldDynamicLinker)):
- raise mesonlib.MesonException(f"LLVM's ThinLTO only works with gold, lld, lld-link, ld64 or mold, not {self.linker.id}")
+ elif not isinstance(self.linker, (AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuBFDDynamicLinker, GnuGoldDynamicLinker)):
+ raise mesonlib.MesonException(f"LLVM's ThinLTO only works with bfd, gold, lld, lld-link, ld64, or mold, not {self.linker.id}")
args.append(f'-flto={mode}')
else:
assert mode == 'default', 'someone forgot to wire something up'
- args.extend(super().get_lto_compile_args(threads=threads))
+ args.extend(super().get_lto_compile_args(target=target, threads=threads))
return args
def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
- if isinstance(self.linker, (ClangClDynamicLinker, MSVCDynamicLinker)):
+ if isinstance(self.linker, VisualStudioLikeLinkerMixin):
return [flag if flag.startswith('-Wl,') or flag.startswith('-fuse-ld=') else f'-Wl,{flag}' for flag in args]
else:
return args
- def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
- thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
- args = self.get_lto_compile_args(threads=threads, mode=mode)
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ args = self.get_lto_compile_args(target=target, threads=threads, mode=mode)
if mode == 'thin' and thinlto_cache_dir is not None:
# We check for ThinLTO linker support above in get_lto_compile_args, and all of them support
# get_thinlto_cache_args as well
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index e45c485..ef18912 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -128,7 +128,7 @@ class CLikeCompiler(Compiler):
warn_args: T.Dict[str, T.List[str]] = {}
# TODO: Replace this manual cache with functools.lru_cache
- find_library_cache: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], str, LibType], T.Optional[T.List[str]]] = {}
+ find_library_cache: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], str, LibType, bool], T.Optional[T.List[str]]] = {}
find_framework_cache: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], bool], T.Optional[T.List[str]]] = {}
internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
@@ -186,17 +186,16 @@ class CLikeCompiler(Compiler):
return ['-isystem', path]
return ['-I' + path]
- def get_compiler_dirs(self, env: 'Environment', name: str) -> T.List[str]:
+ def get_compiler_dirs(self, name: str) -> T.List[str]:
'''
Get dirs from the compiler, either `libraries:` or `programs:`
'''
return []
@functools.lru_cache()
- def _get_library_dirs(self, env: 'Environment',
- elf_class: T.Optional[int] = None) -> 'ImmutableListProtocol[str]':
+ def _get_library_dirs(self, elf_class: T.Optional[int] = None) -> 'ImmutableListProtocol[str]':
# TODO: replace elf_class with enum
- dirs = self.get_compiler_dirs(env, 'libraries')
+ dirs = self.get_compiler_dirs('libraries')
if elf_class is None or elf_class == 0:
return dirs
@@ -232,23 +231,22 @@ class CLikeCompiler(Compiler):
return retval
- def get_library_dirs(self, env: 'Environment',
- elf_class: T.Optional[int] = None) -> T.List[str]:
+ def get_library_dirs(self, elf_class: T.Optional[int] = None) -> T.List[str]:
"""Wrap the lru_cache so that we return a new copy and don't allow
mutation of the cached value.
"""
- return self._get_library_dirs(env, elf_class).copy()
+ return self._get_library_dirs(elf_class).copy()
@functools.lru_cache()
- def _get_program_dirs(self, env: 'Environment') -> 'ImmutableListProtocol[str]':
+ def _get_program_dirs(self) -> 'ImmutableListProtocol[str]':
'''
Programs used by the compiler. Also where toolchain DLLs such as
libstdc++-6.dll are found with MinGW.
'''
- return self.get_compiler_dirs(env, 'programs')
+ return self.get_compiler_dirs('programs')
- def get_program_dirs(self, env: 'Environment') -> T.List[str]:
- return self._get_program_dirs(env).copy()
+ def get_program_dirs(self) -> T.List[str]:
+ return self._get_program_dirs().copy()
def get_pic_args(self) -> T.List[str]:
return ['-fPIC']
@@ -262,14 +260,13 @@ class CLikeCompiler(Compiler):
def get_default_include_dirs(self) -> T.List[str]:
return []
- def gen_export_dynamic_link_args(self, env: 'Environment') -> T.List[str]:
- return self.linker.export_dynamic_args(env)
+ def gen_export_dynamic_link_args(self) -> T.List[str]:
+ return self.linker.export_dynamic_args()
def gen_import_library_args(self, implibname: str) -> T.List[str]:
return self.linker.import_library_args(implibname)
- def _sanity_check_impl(self, work_dir: str, environment: 'Environment',
- sname: str, code: str) -> None:
+ def _sanity_check_impl(self, work_dir: str, sname: str, code: str) -> None:
mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', mesonlib.join_args(self.exelist))
mlog.debug(f'Is cross compiler: {self.is_cross!s}.')
@@ -278,14 +275,14 @@ class CLikeCompiler(Compiler):
mode = CompileCheckMode.LINK
if self.is_cross:
binname += '_cross'
- if not environment.has_exe_wrapper():
+ if not self.environment.has_exe_wrapper():
# Linking cross built C/C++ apps is painful. You can't really
# tell if you should use -nostdlib or not and for example
# on OSX the compiler binary is the same but you need
# a ton of compiler flags to differentiate between
# arm and x86_64. So just compile.
mode = CompileCheckMode.COMPILE
- cargs, largs = self._get_basic_compiler_args(environment, mode)
+ cargs, largs = self._get_basic_compiler_args(mode)
extra_flags = cargs + self.linker_to_compiler_args(largs)
# Is a valid executable output for all toolchains and platforms
@@ -307,21 +304,20 @@ class CLikeCompiler(Compiler):
mlog.debug('-----')
if pc.returncode != 0:
raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.')
- self.run_sanity_check(environment, [binary_name], work_dir)
+ self.run_sanity_check([binary_name], work_dir)
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = 'int main(void) { int class=0; return class; }\n'
- return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+ return self._sanity_check_impl(work_dir, 'sanitycheckc.c', code)
- def check_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def check_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
code = f'''{prefix}
#include <{hname}>'''
- return self.compiles(code, env, extra_args=extra_args,
- dependencies=dependencies)
+ return self.compiles(code, extra_args=extra_args, dependencies=dependencies)
- def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ def has_header(self, hname: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
@@ -333,11 +329,10 @@ class CLikeCompiler(Compiler):
#else
#include <{hname}>
#endif'''
- return self.compiles(code, env, extra_args=extra_args,
+ return self.compiles(code, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.PREPROCESS, disable_cache=disable_cache)
- def has_header_symbol(self, hname: str, symbol: str, prefix: str,
- env: 'Environment', *,
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
t = f'''{prefix}
@@ -349,10 +344,10 @@ class CLikeCompiler(Compiler):
#endif
return 0;
}}'''
- return self.compiles(t, env, extra_args=extra_args,
+ return self.compiles(t, extra_args=extra_args,
dependencies=dependencies)
- def _get_basic_compiler_args(self, env: 'Environment', mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
+ def _get_basic_compiler_args(self, mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
cargs: T.List[str] = []
largs: T.List[str] = []
if mode is CompileCheckMode.LINK:
@@ -361,14 +356,17 @@ class CLikeCompiler(Compiler):
# linking with static libraries since MSVC won't select a CRT for
# us in that case and will error out asking us to pick one.
try:
- crt_val = env.coredata.optstore.get_value('b_vscrt')
- buildtype = env.coredata.optstore.get_value('buildtype')
- cargs += self.get_crt_compile_args(crt_val, buildtype) # type: ignore[arg-type]
+ crt_val = self.environment.coredata.optstore.get_value_for('b_vscrt')
+ buildtype = self.environment.coredata.optstore.get_value_for('buildtype')
+ assert isinstance(crt_val, str), 'for mypy'
+ assert isinstance(buildtype, str), 'for mypy'
+ cargs += self.get_crt_compile_args(crt_val, buildtype)
+ largs += self.get_crt_link_args(crt_val, buildtype)
except (KeyError, AttributeError):
pass
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env
- sys_args = env.coredata.get_external_args(self.for_machine, self.language)
+ sys_args = self.environment.coredata.get_external_args(self.for_machine, self.language)
if isinstance(sys_args, str):
sys_args = [sys_args]
# Apparently it is a thing to inject linker flags both
@@ -379,20 +377,20 @@ class CLikeCompiler(Compiler):
cargs += cleaned_sys_args
if mode is CompileCheckMode.LINK:
- ld_value = env.lookup_binary_entry(self.for_machine, self.language + '_ld')
+ ld_value = self.environment.lookup_binary_entry(self.for_machine, self.language + '_ld')
if ld_value is not None:
largs += self.use_linker_args(ld_value[0], self.version)
# Add LDFLAGS from the env
- sys_ld_args = env.coredata.get_external_link_args(self.for_machine, self.language)
+ sys_ld_args = self.environment.coredata.get_external_link_args(self.for_machine, self.language)
# CFLAGS and CXXFLAGS go to both linking and compiling, but we want them
# to only appear on the command line once. Remove dupes.
- largs += [x for x in sys_ld_args if x not in sys_args]
+ largs += [x for x in sys_ld_args if x not in cleaned_sys_args]
cargs += self.get_compiler_args_for_mode(mode)
return cargs, largs
- def build_wrapper_args(self, env: 'Environment',
+ def build_wrapper_args(self,
extra_args: T.Union[None, arglist.CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']],
mode: CompileCheckMode = CompileCheckMode.COMPILE) -> arglist.CompilerArgs:
@@ -417,13 +415,13 @@ class CLikeCompiler(Compiler):
cargs += d.get_compile_args()
system_incdir = d.get_include_type() == 'system'
for i in d.get_include_dirs():
- for idir in i.to_string_list(env.get_source_dir(), env.get_build_dir()):
+ for idir in i.to_string_list(self.environment.get_source_dir(), self.environment.get_build_dir()):
cargs.extend(self.get_include_args(idir, system_incdir))
if mode is CompileCheckMode.LINK:
# Add link flags needed to find dependencies
largs += d.get_link_args()
- ca, la = self._get_basic_compiler_args(env, mode)
+ ca, la = self._get_basic_compiler_args(mode)
cargs += ca
cargs += self.get_compiler_check_args(mode)
@@ -443,35 +441,34 @@ class CLikeCompiler(Compiler):
args = cargs + extra_args + largs
return args
- def _compile_int(self, expression: str, prefix: str, env: 'Environment',
+ def _compile_int(self, expression: str, prefix: str,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']]) -> bool:
t = f'''{prefix}
#include <stddef.h>
int main(void) {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}'''
- return self.compiles(t, env, extra_args=extra_args,
- dependencies=dependencies)[0]
+ return self.compiles(t, extra_args=extra_args, dependencies=dependencies)[0]
- def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
- guess: T.Optional[int], prefix: str, env: 'Environment',
- extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ def _cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
# Try user's guess first
if isinstance(guess, int):
- if self._compile_int(f'{expression} == {guess}', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} == {guess}', prefix, extra_args, dependencies):
return guess
# Try to expand the expression and evaluate it on the build machines compiler
- if self.language in env.coredata.compilers.build:
+ if self.language in self.environment.coredata.compilers.build:
try:
- expanded, _ = self.get_define(expression, prefix, env, extra_args, dependencies, False)
+ expanded, _ = self.get_define(expression, prefix, extra_args, dependencies, False)
evaluate_expanded = f'''
#include <stdio.h>
#include <stdint.h>
int main(void) {{ int expression = {expanded}; printf("%d", expression); return 0; }}'''
- run = env.coredata.compilers.build[self.language].run(evaluate_expanded, env)
+ run = self.environment.coredata.compilers.build[self.language].run(evaluate_expanded)
if run and run.compiled and run.returncode == 0:
- if self._compile_int(f'{expression} == {run.stdout}', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} == {run.stdout}', prefix, extra_args, dependencies):
return int(run.stdout)
except mesonlib.EnvironmentException:
pass
@@ -480,9 +477,9 @@ class CLikeCompiler(Compiler):
maxint = 0x7fffffff
minint = -0x80000000
if not isinstance(low, int) or not isinstance(high, int):
- if self._compile_int(f'{expression} >= 0', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} >= 0', prefix, extra_args, dependencies):
low = cur = 0
- while self._compile_int(f'{expression} > {cur}', prefix, env, extra_args, dependencies):
+ while self._compile_int(f'{expression} > {cur}', prefix, extra_args, dependencies):
low = cur + 1
if low > maxint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
@@ -490,7 +487,7 @@ class CLikeCompiler(Compiler):
high = cur
else:
high = cur = -1
- while self._compile_int(f'{expression} < {cur}', prefix, env, extra_args, dependencies):
+ while self._compile_int(f'{expression} < {cur}', prefix, extra_args, dependencies):
high = cur - 1
if high < minint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
@@ -501,13 +498,13 @@ class CLikeCompiler(Compiler):
if high < low:
raise mesonlib.EnvironmentException('high limit smaller than low limit')
condition = f'{expression} <= {high} && {expression} >= {low}'
- if not self._compile_int(condition, prefix, env, extra_args, dependencies):
+ if not self._compile_int(condition, prefix, extra_args, dependencies):
raise mesonlib.EnvironmentException('Value out of given range')
# Binary search
while low != high:
cur = low + int((high - low) / 2)
- if self._compile_int(f'{expression} <= {cur}', prefix, env, extra_args, dependencies):
+ if self._compile_int(f'{expression} <= {cur}', prefix, extra_args, dependencies):
high = cur
else:
low = cur + 1
@@ -515,13 +512,13 @@ class CLikeCompiler(Compiler):
return low
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
- guess: T.Optional[int], prefix: str, env: 'Environment', *,
+ guess: T.Optional[int], prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
- return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies)
+ return self._cross_compute_int(expression, low, high, guess, prefix, extra_args, dependencies)
t = f'''{prefix}
#include<stddef.h>
#include<stdio.h>
@@ -529,7 +526,7 @@ class CLikeCompiler(Compiler):
printf("%ld\\n", (long)({expression}));
return 0;
}}'''
- res = self.run(t, env, extra_args=extra_args,
+ res = self.run(t, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1
@@ -537,9 +534,9 @@ class CLikeCompiler(Compiler):
raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
- def cross_sizeof(self, typename: str, prefix: str, env: 'Environment', *,
- extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ def _cross_sizeof(self, typename: str, prefix: str, *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
t = f'''{prefix}
@@ -548,19 +545,19 @@ class CLikeCompiler(Compiler):
{typename} something;
return 0;
}}'''
- if not self.compiles(t, env, extra_args=extra_args,
+ if not self.compiles(t, extra_args=extra_args,
dependencies=dependencies)[0]:
return -1
- return self.cross_compute_int(f'sizeof({typename})', None, None, None, prefix, env, extra_args, dependencies)
+ return self._cross_compute_int(f'sizeof({typename})', None, None, None, prefix, extra_args, dependencies)
- def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ def sizeof(self, typename: str, prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
extra_args = []
if self.is_cross:
- r = self.cross_sizeof(typename, prefix, env, extra_args=extra_args,
- dependencies=dependencies)
+ r = self._cross_sizeof(typename, prefix, extra_args=extra_args,
+ dependencies=dependencies)
return r, False
t = f'''{prefix}
#include<stddef.h>
@@ -569,7 +566,7 @@ class CLikeCompiler(Compiler):
printf("%ld\\n", (long)(sizeof({typename})));
return 0;
}}'''
- res = self.cached_run(t, env, extra_args=extra_args,
+ res = self.cached_run(t, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1, False
@@ -577,9 +574,9 @@ class CLikeCompiler(Compiler):
raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout), res.cached
- def cross_alignment(self, typename: str, prefix: str, env: 'Environment', *,
- extra_args: T.Optional[T.List[str]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ def _cross_alignment(self, typename: str, prefix: str, *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
t = f'''{prefix}
@@ -588,7 +585,7 @@ class CLikeCompiler(Compiler):
{typename} something;
return 0;
}}'''
- if not self.compiles(t, env, extra_args=extra_args,
+ if not self.compiles(t, extra_args=extra_args,
dependencies=dependencies)[0]:
return -1
t = f'''{prefix}
@@ -597,16 +594,16 @@ class CLikeCompiler(Compiler):
char c;
{typename} target;
}};'''
- return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t, env, extra_args, dependencies)
+ return self._cross_compute_int('offsetof(struct tmp, target)', None, None, None, t, extra_args, dependencies)
- def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ def alignment(self, typename: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
extra_args = []
if self.is_cross:
- r = self.cross_alignment(typename, prefix, env, extra_args=extra_args,
- dependencies=dependencies)
+ r = self._cross_alignment(typename, prefix, extra_args=extra_args,
+ dependencies=dependencies)
return r, False
t = f'''{prefix}
#include <stdio.h>
@@ -619,7 +616,7 @@ class CLikeCompiler(Compiler):
printf("%d", (int)offsetof(struct tmp, target));
return 0;
}}'''
- res = self.cached_run(t, env, extra_args=extra_args,
+ res = self.cached_run(t, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
raise mesonlib.EnvironmentException('Could not compile alignment test.')
@@ -638,7 +635,7 @@ class CLikeCompiler(Compiler):
return align, res.cached
- def get_define(self, dname: str, prefix: str, env: 'Environment',
+ def get_define(self, dname: str, prefix: str,
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']],
disable_cache: bool = False) -> T.Tuple[str, bool]:
@@ -651,9 +648,9 @@ class CLikeCompiler(Compiler):
# define {dname} {sentinel_undef}
#endif
{delim_start}{dname}{delim_end}'''
- args = self.build_wrapper_args(env, extra_args, dependencies,
+ args = self.build_wrapper_args(extra_args, dependencies,
mode=CompileCheckMode.PREPROCESS).to_native()
- func = functools.partial(self.cached_compile, code, env.coredata, extra_args=args, mode=CompileCheckMode.PREPROCESS)
+ func = functools.partial(self.cached_compile, code, extra_args=args, mode=CompileCheckMode.PREPROCESS)
if disable_cache:
func = functools.partial(self.compile, code, extra_args=args, mode=CompileCheckMode.PREPROCESS)
with func() as p:
@@ -677,7 +674,7 @@ class CLikeCompiler(Compiler):
return define_value, cached
def get_return_value(self, fname: str, rtype: str, prefix: str,
- env: 'Environment', extra_args: T.Optional[T.List[str]],
+ extra_args: T.Optional[T.List[str]],
dependencies: T.Optional[T.List['Dependency']]) -> T.Union[str, int]:
# TODO: rtype should be an enum.
# TODO: maybe we can use overload to tell mypy when this will return int vs str?
@@ -695,7 +692,7 @@ class CLikeCompiler(Compiler):
printf ("{fmt}", {cast} {fname}());
return 0;
}}'''
- res = self.run(code, env, extra_args=extra_args, dependencies=dependencies)
+ res = self.run(code, extra_args=extra_args, dependencies=dependencies)
if not res.compiled:
raise mesonlib.EnvironmentException(f'Could not get return value of {fname}()')
if rtype == 'string':
@@ -763,7 +760,7 @@ class CLikeCompiler(Compiler):
}}'''
return head, main
- def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ def has_function(self, funcname: str, prefix: str, *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
"""Determine if a function exists.
@@ -781,7 +778,7 @@ class CLikeCompiler(Compiler):
varname = 'has function ' + funcname
varname = varname.replace(' ', '_')
if self.is_cross:
- val = env.properties.host.get(varname, None)
+ val = self.environment.properties.host.get(varname, None)
if val is not None:
if isinstance(val, bool):
return val, False
@@ -818,7 +815,7 @@ class CLikeCompiler(Compiler):
head, main = self._no_prototype_templ()
templ = head + stubs_fail + main
- res, cached = self.links(templ.format(**fargs), env, extra_args=extra_args,
+ res, cached = self.links(templ.format(**fargs), extra_args=extra_args,
dependencies=dependencies)
if res:
return True, cached
@@ -860,11 +857,10 @@ class CLikeCompiler(Compiler):
#endif
return 0;
}}'''
- return self.links(t.format(**fargs), env, extra_args=extra_args,
+ return self.links(t.format(**fargs), extra_args=extra_args,
dependencies=dependencies)
- def has_members(self, typename: str, membernames: T.List[str],
- prefix: str, env: 'Environment', *,
+ def has_members(self, typename: str, membernames: T.List[str], prefix: str, *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
@@ -877,20 +873,18 @@ class CLikeCompiler(Compiler):
{members}
(void) foo;
}}'''
- return self.compiles(t, env, extra_args=extra_args,
- dependencies=dependencies)
+ return self.compiles(t, extra_args=extra_args, dependencies=dependencies)
- def has_type(self, typename: str, prefix: str, env: 'Environment',
+ def has_type(self, typename: str, prefix: str,
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]]], *,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
t = f'''{prefix}
void bar(void) {{
(void) sizeof({typename});
}}'''
- return self.compiles(t, env, extra_args=extra_args,
- dependencies=dependencies)
+ return self.compiles(t, extra_args=extra_args, dependencies=dependencies)
- def _symbols_have_underscore_prefix_searchbin(self, env: 'Environment') -> bool:
+ def _symbols_have_underscore_prefix_searchbin(self) -> bool:
'''
Check if symbols have underscore prefix by compiling a small test binary
and then searching the binary for the string,
@@ -906,7 +900,7 @@ class CLikeCompiler(Compiler):
'''
args = self.get_compiler_check_args(CompileCheckMode.COMPILE)
n = '_symbols_have_underscore_prefix_searchbin'
- with self._build_wrapper(code, env, extra_args=args, mode=CompileCheckMode.COMPILE, want_output=True) as p:
+ with self._build_wrapper(code, extra_args=args, mode=CompileCheckMode.COMPILE, want_output=True) as p:
if p.returncode != 0:
raise RuntimeError(f'BUG: Unable to compile {n!r} check: {p.stderr}')
if not os.path.isfile(p.output_name):
@@ -924,7 +918,7 @@ class CLikeCompiler(Compiler):
return False
raise RuntimeError(f'BUG: {n!r} check did not find symbol string in binary')
- def _symbols_have_underscore_prefix_define(self, env: 'Environment') -> T.Optional[bool]:
+ def _symbols_have_underscore_prefix_define(self) -> T.Optional[bool]:
'''
Check if symbols have underscore prefix by querying the
__USER_LABEL_PREFIX__ define that most compilers provide
@@ -941,7 +935,7 @@ class CLikeCompiler(Compiler):
#endif
{delim}MESON_UNDERSCORE_PREFIX
'''
- with self._build_wrapper(code, env, mode=CompileCheckMode.PREPROCESS, want_output=False) as p:
+ with self._build_wrapper(code, mode=CompileCheckMode.PREPROCESS, want_output=False) as p:
if p.returncode != 0:
raise RuntimeError(f'BUG: Unable to preprocess _symbols_have_underscore_prefix_define check: {p.stdout}')
symbol_prefix = p.stdout.partition(delim)[-1].rstrip()
@@ -954,46 +948,58 @@ class CLikeCompiler(Compiler):
else:
return None
- def _symbols_have_underscore_prefix_list(self, env: 'Environment') -> T.Optional[bool]:
+ def _symbols_have_underscore_prefix_list(self) -> T.Optional[bool]:
'''
Check if symbols have underscore prefix by consulting a hardcoded
list of cases where we know the results.
Return if functions have underscore prefix or None if unknown.
'''
- m = env.machines[self.for_machine]
# Darwin always uses the underscore prefix, not matter what
- if m.is_darwin():
+ if self.info.is_darwin():
return True
# Windows uses the underscore prefix on x86 (32bit) only
- if m.is_windows() or m.is_cygwin():
- return m.cpu_family == 'x86'
+ if self.info.is_windows() or self.info.is_cygwin():
+ return self.info.cpu_family == 'x86'
return None
- def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ def symbols_have_underscore_prefix(self) -> bool:
'''
Check if the compiler prefixes an underscore to global C symbols
'''
# First, try to query the compiler directly
- result = self._symbols_have_underscore_prefix_define(env)
+ result = self._symbols_have_underscore_prefix_define()
if result is not None:
return result
# Else, try to consult a hardcoded list of cases we know
# absolutely have an underscore prefix
- result = self._symbols_have_underscore_prefix_list(env)
+ result = self._symbols_have_underscore_prefix_list()
if result is not None:
return result
# As a last resort, try search in a compiled binary, which is the
# most unreliable way of checking this, see #5482
- return self._symbols_have_underscore_prefix_searchbin(env)
+ return self._symbols_have_underscore_prefix_searchbin()
- def _get_patterns(self, env: 'Environment', prefixes: T.List[str], suffixes: T.List[str], shared: bool = False) -> T.List[str]:
+ def _get_patterns(self, prefixes: T.List[str], suffixes: T.List[str], shared: bool = False) -> T.List[str]:
patterns: T.List[str] = []
+ if self.info.is_os2():
+ # On OS/2, search order for shared libs is
+ # 1. libfoo_dll.a
+ # 2. foo_dll.a
+ # 3. libfoo.a
+ # 4. foo.a
+ # 5. foo.dll
+ # For static libs, `_s' is used instead of `_dll'.
+ for s in suffixes:
+ dot = '' if s.startswith(('_dll.', '_s.')) else '.'
+ for p in prefixes:
+ patterns.append(p + '{}' + dot + s)
+ return patterns
for p in prefixes:
for s in suffixes:
patterns.append(p + '{}.' + s)
- if shared and env.machines[self.for_machine].is_openbsd():
+ if shared and self.info.is_openbsd():
# Shared libraries on OpenBSD can be named libfoo.so.X.Y:
# https://www.openbsd.org/faq/ports/specialtopics.html#SharedLibs
#
@@ -1005,7 +1011,7 @@ class CLikeCompiler(Compiler):
patterns.append(p + '{}.so.[0-9]*.[0-9]*')
return patterns
- def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool = False) -> T.Tuple[str, ...]:
+ def get_library_naming(self, libtype: LibType, strict: bool = False) -> T.Tuple[str, ...]:
'''
Get library prefixes and suffixes for the target platform ordered by
priority
@@ -1015,14 +1021,15 @@ class CLikeCompiler(Compiler):
# people depend on it. Also, some people use prebuilt `foo.so` instead
# of `libfoo.so` for unknown reasons, and may also want to create
# `foo.so` by setting name_prefix to ''
- if strict and not isinstance(self, VisualStudioLikeCompiler): # lib prefix is not usually used with msvc
+ # lib prefix is not usually used with msvc and OS/2
+ if strict and not isinstance(self, VisualStudioLikeCompiler) and not self.info.is_os2():
prefixes = ['lib']
else:
prefixes = ['lib', '']
# Library suffixes and prefixes
- if env.machines[self.for_machine].is_darwin():
+ if self.info.is_darwin():
shlibext = ['dylib', 'so']
- elif env.machines[self.for_machine].is_windows():
+ elif self.info.is_windows():
# FIXME: .lib files can be import or static so we should read the
# file, figure out which one it is, and reject the wrong kind.
if isinstance(self, VisualStudioLikeCompiler):
@@ -1031,28 +1038,31 @@ class CLikeCompiler(Compiler):
shlibext = ['dll.a', 'lib', 'dll']
# Yep, static libraries can also be foo.lib
stlibext += ['lib']
- elif env.machines[self.for_machine].is_cygwin():
+ elif self.info.is_cygwin():
shlibext = ['dll', 'dll.a']
prefixes = ['cyg'] + prefixes
elif self.id.lower() in {'c6000', 'c2000', 'ti'}:
# TI C28x compilers can use both extensions for static or dynamic libs.
stlibext = ['a', 'lib']
shlibext = ['dll', 'so']
+ elif self.info.is_os2():
+ stlibext = ['_s.lib', '_s.a', 'lib', 'a']
+ shlibext = ['_dll.lib', '_dll.a', 'lib', 'a', 'dll']
else:
# Linux/BSDs
shlibext = ['so']
# Search priority
if libtype is LibType.PREFER_SHARED:
- patterns = self._get_patterns(env, prefixes, shlibext, True)
- patterns.extend([x for x in self._get_patterns(env, prefixes, stlibext, False) if x not in patterns])
+ patterns = self._get_patterns(prefixes, shlibext, True)
+ patterns.extend([x for x in self._get_patterns(prefixes, stlibext, False) if x not in patterns])
elif libtype is LibType.PREFER_STATIC:
- patterns = self._get_patterns(env, prefixes, stlibext, False)
- patterns.extend([x for x in self._get_patterns(env, prefixes, shlibext, True) if x not in patterns])
+ patterns = self._get_patterns(prefixes, stlibext, False)
+ patterns.extend([x for x in self._get_patterns(prefixes, shlibext, True) if x not in patterns])
elif libtype is LibType.SHARED:
- patterns = self._get_patterns(env, prefixes, shlibext, True)
+ patterns = self._get_patterns(prefixes, shlibext, True)
else:
assert libtype is LibType.STATIC
- patterns = self._get_patterns(env, prefixes, stlibext, False)
+ patterns = self._get_patterns(prefixes, stlibext, False)
return tuple(patterns)
@staticmethod
@@ -1087,10 +1097,8 @@ class CLikeCompiler(Compiler):
@staticmethod
def _get_file_from_list(env: Environment, paths: T.List[str]) -> T.Optional[Path]:
'''
- We just check whether the library exists. We can't do a link check
- because the library might have unresolved symbols that require other
- libraries. On macOS we check if the library matches our target
- architecture.
+ Check whether the library exists by filename. On macOS, we also
+ check if the library matches our target architecture.
'''
for p in paths:
if os.path.isfile(p):
@@ -1107,13 +1115,14 @@ class CLikeCompiler(Compiler):
return None
@functools.lru_cache()
- def output_is_64bit(self, env: 'Environment') -> bool:
+ def output_is_64bit(self) -> bool:
'''
returns true if the output produced is 64-bit, false if 32-bit
'''
- return self.sizeof('void *', '', env)[0] == 8
+ return self.sizeof('void *', '')[0] == 8
- def _find_library_real(self, libname: str, env: 'Environment', extra_dirs: T.List[str], code: str, libtype: LibType, lib_prefix_warning: bool) -> T.Optional[T.List[str]]:
+ def _find_library_real(self, libname: str, extra_dirs: T.List[str], code: str, libtype: LibType,
+ lib_prefix_warning: bool, ignore_system_dirs: bool) -> T.Optional[T.List[str]]:
# First try if we can just add the library as -l.
# Gcc + co seem to prefer builtin lib dirs to -L dirs.
# Only try to find std libs if no extra dirs specified.
@@ -1125,48 +1134,59 @@ class CLikeCompiler(Compiler):
largs = self.get_linker_always_args() + self.get_allow_undefined_link_args()
extra_args = cargs + self.linker_to_compiler_args(largs)
- if self.links(code, env, extra_args=extra_args, disable_cache=True)[0]:
+ if self.links(code, extra_args=extra_args, disable_cache=True)[0]:
return cargs
# Don't do a manual search for internal libs
if libname in self.internal_libs:
return None
# Not found or we want to use a specific libtype? Try to find the
# library file itself.
- patterns = self.get_library_naming(env, libtype)
+ patterns = self.get_library_naming(libtype)
# try to detect if we are 64-bit or 32-bit. If we can't
# detect, we will just skip path validity checks done in
# get_library_dirs() call
try:
- if self.output_is_64bit(env):
+ if self.output_is_64bit():
elf_class = 2
else:
elf_class = 1
except (mesonlib.MesonException, KeyError): # TODO evaluate if catching KeyError is wanted here
elf_class = 0
# Search in the specified dirs, and then in the system libraries
- for d in itertools.chain(extra_dirs, self.get_library_dirs(env, elf_class)):
+ largs = self.get_linker_always_args() + self.get_allow_undefined_link_args()
+ lcargs = self.linker_to_compiler_args(largs)
+ for d in itertools.chain(extra_dirs, [] if ignore_system_dirs else self.get_library_dirs(elf_class)):
for p in patterns:
trials = self._get_trials_from_pattern(p, d, libname)
if not trials:
continue
- trial = self._get_file_from_list(env, trials)
- if not trial:
+
+ trial_result = ""
+ for trial in trials:
+ if not os.path.isfile(trial):
+ continue
+ extra_args = [trial] + lcargs
+ if self.links(code, extra_args=extra_args, disable_cache=True)[0]:
+ trial_result = trial
+ break
+
+ if not trial_result:
continue
- if libname.startswith('lib') and trial.name.startswith(libname) and lib_prefix_warning:
+ if libname.startswith('lib') and trial_result.startswith(libname) and lib_prefix_warning:
mlog.warning(f'find_library({libname!r}) starting in "lib" only works by accident and is not portable')
- return [trial.as_posix()]
+ return [Path(trial_result).as_posix()]
return None
- def _find_library_impl(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- code: str, libtype: LibType, lib_prefix_warning: bool) -> T.Optional[T.List[str]]:
+ def _find_library_impl(self, libname: str, extra_dirs: T.List[str], code: str, libtype: LibType,
+ lib_prefix_warning: bool, ignore_system_dirs: bool) -> T.Optional[T.List[str]]:
# These libraries are either built-in or invalid
if libname in self.ignore_libs:
return []
if isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
- key = (tuple(self.exelist), libname, tuple(extra_dirs), code, libtype)
+ key = (tuple(self.exelist), libname, tuple(extra_dirs), code, libtype, ignore_system_dirs)
if key not in self.find_library_cache:
- value = self._find_library_real(libname, env, extra_dirs, code, libtype, lib_prefix_warning)
+ value = self._find_library_real(libname, extra_dirs, code, libtype, lib_prefix_warning, ignore_system_dirs)
self.find_library_cache[key] = value
else:
value = self.find_library_cache[key]
@@ -1174,12 +1194,12 @@ class CLikeCompiler(Compiler):
return None
return value.copy()
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }\n'
- return self._find_library_impl(libname, env, extra_dirs, code, libtype, lib_prefix_warning)
+ return self._find_library_impl(libname, extra_dirs, code, libtype, lib_prefix_warning, ignore_system_dirs)
- def find_framework_paths(self, env: 'Environment') -> T.List[str]:
+ def find_framework_paths(self) -> T.List[str]:
'''
These are usually /Library/Frameworks and /System/Library/Frameworks,
unless you select a particular macOS SDK with the -isysroot flag.
@@ -1192,7 +1212,7 @@ class CLikeCompiler(Compiler):
commands = self.get_exelist(ccache=False) + ['-v', '-E', '-']
commands += self.get_always_args()
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
- commands += env.coredata.get_external_args(self.for_machine, self.language)
+ commands += self.environment.coredata.get_external_args(self.for_machine, self.language)
mlog.debug('Finding framework path by running: ', ' '.join(commands), '\n')
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
@@ -1206,7 +1226,7 @@ class CLikeCompiler(Compiler):
paths.append(line[:-21].strip())
return paths
- def _find_framework_real(self, name: str, env: 'Environment', extra_dirs: T.List[str], allow_system: bool) -> T.Optional[T.List[str]]:
+ def _find_framework_real(self, name: str, extra_dirs: T.List[str], allow_system: bool) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }'
link_args: T.List[str] = []
for d in extra_dirs:
@@ -1215,11 +1235,11 @@ class CLikeCompiler(Compiler):
# then we must also pass -L/usr/lib to pick up libSystem.dylib
extra_args = [] if allow_system else ['-Z', '-L/usr/lib']
link_args += ['-framework', name]
- if self.links(code, env, extra_args=(extra_args + link_args), disable_cache=True)[0]:
+ if self.links(code, extra_args=(extra_args + link_args), disable_cache=True)[0]:
return link_args
return None
- def _find_framework_impl(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ def _find_framework_impl(self, name: str, extra_dirs: T.List[str],
allow_system: bool) -> T.Optional[T.List[str]]:
if isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
@@ -1227,20 +1247,20 @@ class CLikeCompiler(Compiler):
if key in self.find_framework_cache:
value = self.find_framework_cache[key]
else:
- value = self._find_framework_real(name, env, extra_dirs, allow_system)
+ value = self._find_framework_real(name, extra_dirs, allow_system)
self.find_framework_cache[key] = value
if value is None:
return None
return value.copy()
- def find_framework(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ def find_framework(self, name: str, extra_dirs: T.List[str],
allow_system: bool = True) -> T.Optional[T.List[str]]:
'''
Finds the framework with the specified name, and returns link args for
the same or returns None when the framework is not found.
'''
# TODO: should probably check for macOS?
- return self._find_framework_impl(name, env, extra_dirs, allow_system)
+ return self._find_framework_impl(name, extra_dirs, allow_system)
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
# TODO: does this belong here or in GnuLike or maybe PosixLike?
@@ -1250,21 +1270,22 @@ class CLikeCompiler(Compiler):
# TODO: does this belong here or in GnuLike or maybe PosixLike?
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
# TODO: does this belong here or in GnuLike or maybe PosixLike?
- host_m = env.machines[self.for_machine]
- if host_m.is_haiku() or host_m.is_darwin():
+ if self.info.is_haiku() or self.info.is_darwin():
return []
+ if self.info.is_os2():
+ return ['-lpthread']
return ['-pthread']
def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
return args.copy()
- def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ def has_arguments(self, args: T.List[str], code: str,
mode: CompileCheckMode) -> T.Tuple[bool, bool]:
- return self.compiles(code, env, extra_args=args, mode=mode)
+ return self.compiles(code, extra_args=args, mode=mode)
- def _has_multi_arguments(self, args: T.List[str], env: 'Environment', code: str) -> T.Tuple[bool, bool]:
+ def _has_multi_arguments(self, args: T.List[str], code: str) -> T.Tuple[bool, bool]:
new_args: T.List[str] = []
for arg in args:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable
@@ -1272,12 +1293,19 @@ class CLikeCompiler(Compiler):
# check the equivalent enable flag too "-Wforgotten-towel".
if arg.startswith('-Wno-'):
# Make an exception for -Wno-attributes=x as -Wattributes=x is invalid
- # for GCC at least. Also, the opposite of -Wno-vla-larger-than is
- # -Wvla-larger-than=N
+ # for GCC at least. Also, the positive form of some flags require a
+ # value to be specified, i.e. we need to pass -Wfoo=N rather than just
+ # -Wfoo.
if arg.startswith('-Wno-attributes='):
pass
- elif arg == '-Wno-vla-larger-than':
- new_args.append('-Wvla-larger-than=1000')
+ elif arg in {'-Wno-alloc-size-larger-than',
+ '-Wno-alloca-larger-than',
+ '-Wno-frame-larger-than',
+ '-Wno-stack-usage',
+ '-Wno-vla-larger-than'}:
+ # Pass an arbitrary value to the enabling flag; since the test program
+ # is trivial, it is unlikely to provoke any of these warnings.
+ new_args.append('-W' + arg[5:] + '=1000')
else:
new_args.append('-W' + arg[5:])
if arg.startswith('-Wl,'):
@@ -1289,21 +1317,18 @@ class CLikeCompiler(Compiler):
'the compiler you are using. has_link_argument or '
'other similar method can be used instead.')
new_args.append(arg)
- return self.has_arguments(new_args, env, code, mode=CompileCheckMode.COMPILE)
+ return self.has_arguments(new_args, code, mode=CompileCheckMode.COMPILE)
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
- return self._has_multi_arguments(args, env, 'extern int i;\nint i;\n')
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self._has_multi_arguments(args, 'extern int i;\nint i;\n')
- def _has_multi_link_arguments(self, args: T.List[str], env: 'Environment', code: str) -> T.Tuple[bool, bool]:
- # First time we check for link flags we need to first check if we have
- # --fatal-warnings, otherwise some linker checks could give some
- # false positive.
+ def _has_multi_link_arguments(self, args: T.List[str], code: str) -> T.Tuple[bool, bool]:
args = self.linker.fatal_warnings() + args
args = self.linker_to_compiler_args(args)
- return self.has_arguments(args, env, code, mode=CompileCheckMode.LINK)
+ return self.has_arguments(args, code, mode=CompileCheckMode.LINK)
- def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
- return self._has_multi_link_arguments(args, env, 'int main(void) { return 0; }\n')
+ def has_multi_link_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self._has_multi_link_arguments(args, 'int main(void) { return 0; }\n')
@staticmethod
def _concatenate_string_literals(s: str) -> str:
@@ -1321,18 +1346,17 @@ class CLikeCompiler(Compiler):
# mixins.
return ['-Werror']
- def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_func_attribute(self, name: str) -> T.Tuple[bool, bool]:
# Just assume that if we're not on windows that dllimport and dllexport
# don't work
- m = env.machines[self.for_machine]
- if not (m.is_windows() or m.is_cygwin()):
+ if not (self.info.is_windows() or self.info.is_cygwin()):
if name in {'dllimport', 'dllexport'}:
return False, False
- return self.compiles(self.attribute_check_func(name), env,
+ return self.compiles(self.attribute_check_func(name),
extra_args=self.get_has_func_attribute_extra_args(name))
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
if disable:
return ['-DNDEBUG']
return []
diff --git a/mesonbuild/compilers/mixins/compcert.py b/mesonbuild/compilers/mixins/compcert.py
index 86d20a0..522b31e 100644
--- a/mesonbuild/compilers/mixins/compcert.py
+++ b/mesonbuild/compilers/mixins/compcert.py
@@ -11,7 +11,6 @@ import typing as T
if T.TYPE_CHECKING:
from ...envconfig import MachineInfo
- from ...environment import Environment
from ...compilers.compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -85,7 +84,7 @@ class CompCertCompiler(Compiler):
patched_args.append(arg)
return patched_args
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
def get_preprocess_only_args(self) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py
index 7037db2..e020123 100644
--- a/mesonbuild/compilers/mixins/elbrus.py
+++ b/mesonbuild/compilers/mixins/elbrus.py
@@ -17,7 +17,6 @@ from ...mesonlib import Popen_safe
from ...options import OptionKey
if T.TYPE_CHECKING:
- from ...environment import Environment
from ...build import BuildTarget
@@ -39,7 +38,7 @@ class ElbrusCompiler(GnuLikeCompiler):
# FIXME: use _build_wrapper to call this so that linker flags from the env
# get applied
- def get_library_dirs(self, env: 'Environment', elf_class: T.Optional[int] = None) -> T.List[str]:
+ def get_library_dirs(self, elf_class: T.Optional[int] = None) -> T.List[str]:
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
stdo = Popen_safe(self.get_exelist(ccache=False) + ['--print-search-dirs'], env=os_env)[1]
@@ -50,7 +49,7 @@ class ElbrusCompiler(GnuLikeCompiler):
return [os.path.realpath(p) for p in libstr.split(':') if os.path.exists(p)]
return []
- def get_program_dirs(self, env: 'Environment') -> T.List[str]:
+ def get_program_dirs(self) -> T.List[str]:
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
stdo = Popen_safe(self.get_exelist(ccache=False) + ['--print-search-dirs'], env=os_env)[1]
@@ -83,17 +82,17 @@ class ElbrusCompiler(GnuLikeCompiler):
# Actually it's not supported for now, but probably will be supported in future
return 'pch'
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
key = OptionKey(f'{self.language}_std', subproject=subproject, machine=self.for_machine)
if target:
- std = env.coredata.get_option_for_target(target, key)
+ std = self.environment.coredata.get_option_for_target(target, key)
else:
- std = env.coredata.optstore.get_value_for(key)
+ std = self.environment.coredata.optstore.get_value_for(key)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
return args
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-fopenmp']
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index 91b25e8..ca7779a 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -15,7 +15,6 @@ from ...mesonlib import LibType
from mesonbuild.compilers.compilers import CompileCheckMode
if T.TYPE_CHECKING:
- from ...environment import Environment
from ...compilers.compilers import Compiler
from ...dependencies import Dependency
else:
@@ -48,9 +47,9 @@ class EmscriptenMixin(Compiler):
suffix = 'o'
return os.path.join(dirname, 'output.' + suffix)
- def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_link_flags(self) -> T.List[str]:
args = ['-pthread']
- count = env.coredata.optstore.get_value(OptionKey(f'{self.language}_thread_count', machine=self.for_machine))
+ count = self.environment.coredata.optstore.get_value_for(OptionKey(f'{self.language}_thread_count', machine=self.for_machine))
assert isinstance(count, int)
if count:
args.append(f'-sPTHREAD_POOL_SIZE={count}')
@@ -75,10 +74,10 @@ class EmscriptenMixin(Compiler):
def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
return wrap_js_includes(super().get_dependency_link_args(dep))
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
if not libname.endswith('.js'):
- return super().find_library(libname, env, extra_dirs, libtype, lib_prefix_warning)
+ return super().find_library(libname, extra_dirs, libtype, lib_prefix_warning)
if os.path.isabs(libname):
if os.path.exists(libname):
return [libname]
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index 9ea591e..7e783e2 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -21,8 +21,8 @@ from mesonbuild.compilers.compilers import CompileCheckMode
if T.TYPE_CHECKING:
from ..._typing import ImmutableListProtocol
+ from ...build import BuildTarget
from ...options import MutableKeyedOptionDictType
- from ...environment import Environment
from ..compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -373,8 +373,8 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
self.can_compile_suffixes.add('sx')
def get_pic_args(self) -> T.List[str]:
- if self.info.is_windows() or self.info.is_cygwin() or self.info.is_darwin():
- return [] # On Window and OS X, pic is always on.
+ if self.info.is_windows() or self.info.is_cygwin() or self.info.is_darwin() or self.info.is_os2():
+ return [] # On Window, OS X and OS/2, pic is always on.
return ['-fPIC']
def get_pie_args(self) -> T.List[str]:
@@ -401,7 +401,7 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return gnulike_default_include_dirs(tuple(self.get_exelist(ccache=False)), self.language).copy()
@abc.abstractmethod
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
pass
def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
@@ -409,16 +409,6 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
vistype = 'hidden'
return gnu_symbol_visibility_args[vistype]
- def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
- if not isinstance(defsfile, str):
- raise RuntimeError('Module definitions file should be str')
- # On Windows targets, .def files may be specified on the linker command
- # line like an object file.
- if self.info.is_windows() or self.info.is_cygwin():
- return [defsfile]
- # For other targets, discard the .def file.
- return []
-
@staticmethod
def get_argument_syntax() -> str:
return 'gcc'
@@ -437,9 +427,9 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return parameter_list
@functools.lru_cache()
- def _get_search_dirs(self, env: 'Environment') -> str:
+ def _get_search_dirs(self) -> str:
extra_args = ['--print-search-dirs']
- with self._build_wrapper('', env, extra_args=extra_args,
+ with self._build_wrapper('', extra_args=extra_args,
dependencies=None, mode=CompileCheckMode.COMPILE,
want_output=True) as p:
return p.stdout
@@ -481,22 +471,23 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
result.append(unresolved)
return result
- def get_compiler_dirs(self, env: 'Environment', name: str) -> T.List[str]:
+ def get_compiler_dirs(self, name: str) -> T.List[str]:
'''
Get dirs from the compiler, either `libraries:` or `programs:`
'''
- stdo = self._get_search_dirs(env)
+ stdo = self._get_search_dirs()
for line in stdo.split('\n'):
if line.startswith(name + ':'):
return self._split_fetch_real_dirs(line.split('=', 1)[1])
return []
- def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default') -> T.List[str]:
# This provides a base for many compilers, GCC and Clang override this
# for their specific arguments
return ['-flto']
- def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
+ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
if not value:
return value
args = ['-fsanitize=' + ','.join(value)]
@@ -522,9 +513,9 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
@classmethod
def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
- if linker not in {'gold', 'bfd', 'lld'}:
+ if linker not in {'bfd', 'eld', 'gold', 'lld'}:
raise mesonlib.MesonException(
- f'Unsupported linker, only bfd, gold, and lld are supported, not {linker}.')
+ f'Unsupported linker, only bfd, eld, gold, and lld are supported, not {linker}.')
return [f'-fuse-ld={linker}']
def get_coverage_args(self) -> T.List[str]:
@@ -534,6 +525,8 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
# We want to allow preprocessing files with any extension, such as
# foo.c.in. In that case we need to tell GCC/CLANG to treat them as
# assembly file.
+ if self.language == 'fortran':
+ return self.get_preprocess_only_args()
lang = gnu_lang_map.get(self.language, 'assembler-with-cpp')
return self.get_preprocess_only_args() + [f'-x{lang}']
@@ -545,13 +538,21 @@ class GnuCompiler(GnuLikeCompiler):
"""
id = 'gcc'
+ _COLOR_VERSION = '>=4.9.0'
+ _WPEDANTIC_VERSION = '>=4.8.0'
+ _LTO_AUTO_VERSION = '>=10.0'
+ _LTO_CACHE_VERSION = '>=15.1'
+ _USE_MOLD_VERSION = '>=12.0.1'
+
def __init__(self, defines: T.Optional[T.Dict[str, str]]):
super().__init__()
self.defines = defines or {}
- self.base_options.update({OptionKey('b_colorout'), OptionKey('b_lto_threads')})
- self._has_color_support = mesonlib.version_compare(self.version, '>=4.9.0')
- self._has_wpedantic_support = mesonlib.version_compare(self.version, '>=4.8.0')
- self._has_lto_auto_support = mesonlib.version_compare(self.version, '>=10.0')
+ self.base_options.update({OptionKey('b_colorout'), OptionKey('b_lto_threads'),
+ OptionKey('b_thinlto_cache'), OptionKey('b_thinlto_cache_dir')})
+ self._has_color_support = mesonlib.version_compare(self.version, self._COLOR_VERSION)
+ self._has_wpedantic_support = mesonlib.version_compare(self.version, self._WPEDANTIC_VERSION)
+ self._has_lto_auto_support = mesonlib.version_compare(self.version, self._LTO_AUTO_VERSION)
+ self._has_lto_cache_support = mesonlib.version_compare(self.version, self._LTO_CACHE_VERSION)
def get_colorout_args(self, colortype: str) -> T.List[str]:
if self._has_color_support:
@@ -588,15 +589,15 @@ class GnuCompiler(GnuLikeCompiler):
def get_pch_suffix(self) -> str:
return 'gch'
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-fopenmp']
- def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ def has_arguments(self, args: T.List[str], code: str,
mode: CompileCheckMode) -> T.Tuple[bool, bool]:
# For some compiler command line arguments, the GNU compilers will
# emit a warning on stderr indicating that an option is valid for a
# another language, but still complete with exit_success
- with self._build_wrapper(code, env, args, None, mode) as p:
+ with self._build_wrapper(code, args, None, mode) as p:
result = p.returncode == 0
if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stderr:
result = False
@@ -612,26 +613,55 @@ class GnuCompiler(GnuLikeCompiler):
def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]:
return [prelink_name], ['-r', '-o', prelink_name] + obj_list
- def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ args: T.List[str] = []
+
if threads == 0:
if self._has_lto_auto_support:
- return ['-flto=auto']
- # This matches clang's behavior of using the number of cpus, but
- # obeying meson's MESON_NUM_PROCESSES convention.
- return [f'-flto={mesonlib.determine_worker_count()}']
+ args.append('-flto=auto')
+ else:
+ # This matches gcc's behavior of using the number of cpus, but
+ # obeying meson's MESON_NUM_PROCESSES convention.
+ args.append(f'-flto={mesonlib.determine_worker_count()}')
elif threads > 0:
- return [f'-flto={threads}']
- return super().get_lto_compile_args(threads=threads)
+ args.append(f'-flto={threads}')
+ else:
+ args.extend(super().get_lto_compile_args(target=target, threads=threads))
+
+ if thinlto_cache_dir is not None:
+ # We check for ThinLTO linker support above in get_lto_compile_args, and all of them support
+ # get_thinlto_cache_args as well
+ args.extend(self.get_thinlto_cache_args(thinlto_cache_dir))
+
+ return args
+
+ def get_thinlto_cache_args(self, path: str) -> T.List[str]:
+ # Unlike the ThinLTO support for Clang, everything is handled in GCC
+ # and the linker has no direct involvement other than the usual w/ LTO.
+ return [f'-flto-incremental={path}']
@classmethod
def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
- if linker == 'mold' and mesonlib.version_compare(version, '>=12.0.1'):
+ if linker == 'mold' and mesonlib.version_compare(version, cls._USE_MOLD_VERSION):
return ['-fuse-ld=mold']
return super().use_linker_args(linker, version)
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ args: T.List[str] = []
+ args.extend(self.get_lto_compile_args(target=target, threads=threads, thinlto_cache_dir=thinlto_cache_dir))
+ return args
+
def get_profile_use_args(self) -> T.List[str]:
return super().get_profile_use_args() + ['-fprofile-correction']
+ def get_always_args(self) -> T.List[str]:
+ args: T.List[str] = []
+ if self.info.is_os2() and self.get_linker_id() == 'emxomfld':
+ args += ['-Zomf']
+ return super().get_always_args() + args
+
class GnuCStds(Compiler):
diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py
index 32cbdf0..5391c84 100644
--- a/mesonbuild/compilers/mixins/intel.py
+++ b/mesonbuild/compilers/mixins/intel.py
@@ -20,9 +20,6 @@ from .gnu import GnuLikeCompiler
from .visualstudio import VisualStudioLikeCompiler
from ...options import OptionKey
-if T.TYPE_CHECKING:
- from ...environment import Environment
-
# XXX: avoid circular dependencies
# TODO: this belongs in a posix compiler class
# NOTE: the default Intel optimization is -O2, unlike GNU which defaults to -O0.
@@ -82,7 +79,7 @@ class IntelGnuLikeCompiler(GnuLikeCompiler):
def get_pch_name(self, name: str) -> str:
return os.path.basename(name) + '.' + self.get_pch_suffix()
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
if mesonlib.version_compare(self.version, '>=15.0.0'):
return ['-qopenmp']
else:
@@ -158,7 +155,7 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
version = int(v1 + v2)
return self._calculate_toolset_version(version)
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['/Qopenmp']
def get_debug_args(self, is_debug: bool) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index 3f35619..1c03619 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -17,7 +17,6 @@ import typing as T
from ...mesonlib import EnvironmentException, MesonException, is_windows
if T.TYPE_CHECKING:
- from ...environment import Environment
from ...compilers.compilers import Compiler
from ...build import BuildTarget
from ...options import OptionStore
@@ -38,11 +37,11 @@ class BasicLinkerIsCompilerMixin(Compiler):
functionality itself.
"""
- def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
+ def sanitizer_link_args(self, target: BuildTarget, value: T.List[str]) -> T.List[str]:
return []
- def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
- thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
return []
def can_linker_accept_rsp(self) -> bool:
@@ -60,10 +59,10 @@ class BasicLinkerIsCompilerMixin(Compiler):
def get_linker_lib_prefix(self) -> str:
return ''
- def get_option_link_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def has_multi_link_args(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_multi_link_args(self, args: T.List[str]) -> T.Tuple[bool, bool]:
return False, False
def get_link_debugfile_args(self, targetfile: str) -> T.List[str]:
@@ -96,14 +95,14 @@ class BasicLinkerIsCompilerMixin(Compiler):
def bitcode_args(self) -> T.List[str]:
raise MesonException("This linker doesn't support bitcode bundles")
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ def get_soname_args(self, prefix: str, shlib_name: str,
suffix: str, soversion: str,
darwin_versions: T.Tuple[str, str]) -> T.List[str]:
raise MesonException("This linker doesn't support soname args")
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
def get_asneeded_args(self) -> T.List[str]:
@@ -115,8 +114,8 @@ class BasicLinkerIsCompilerMixin(Compiler):
def get_link_debugfile_name(self, targetfile: str) -> T.Optional[str]:
return None
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
- def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_link_flags(self) -> T.List[str]:
return []
diff --git a/mesonbuild/compilers/mixins/xc16.py b/mesonbuild/compilers/mixins/microchip.py
index d95674e..b21b032 100644
--- a/mesonbuild/compilers/mixins/xc16.py
+++ b/mesonbuild/compilers/mixins/microchip.py
@@ -3,23 +3,26 @@
from __future__ import annotations
-"""Representations specific to the Microchip XC16 C compiler family."""
+"""Representations specific to the Microchip XC C/C++ compiler family."""
import os
import typing as T
-from ...mesonlib import EnvironmentException
+from .gnu import GnuCStds, GnuCPPStds
+from ..compilers import Compiler
+from ...mesonlib import EnvironmentException, version_compare
if T.TYPE_CHECKING:
+ from ...build import BuildTarget
from ...envconfig import MachineInfo
- from ...environment import Environment
- from ...compilers.compilers import Compiler
+
+ CompilerBase = Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
# Compiler, so we get all of the methods and attributes defined for us, but
# for runtime we make them descend from object (which all classes normally
# do). This gives up DRYer type checking, with no runtime impact
- Compiler = object
+ CompilerBase = object
xc16_optimization_args: T.Dict[str, T.List[str]] = {
'plain': [],
@@ -68,7 +71,7 @@ class Xc16Compiler(Compiler):
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
def get_coverage_args(self) -> T.List[str]:
@@ -109,3 +112,85 @@ class Xc16Compiler(Compiler):
parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
return parameter_list
+
+
+class Xc32Compiler(CompilerBase):
+
+ """Microchip XC32 compiler mixin. GCC based with some options disabled."""
+
+ id = 'xc32-gcc'
+
+ gcc_version = '4.5.1' # Defaults to GCC version used by first XC32 release (v1.00).
+
+ _COLOR_VERSION = '>=3.0' # XC32 version based on GCC 8.3.1+
+ _WPEDANTIC_VERSION = '>=1.40' # XC32 version based on GCC 4.8.3+
+ _LTO_AUTO_VERSION = '>=5.00' # XC32 version based on GCC 13.2.1+
+ _LTO_CACHE_VERSION = '==-1'
+ _USE_MOLD_VERSION = '==-1'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise EnvironmentException('XC32 supports only cross-compilation.')
+
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ return None
+
+ def thread_flags(self) -> T.List[str]:
+ return []
+
+ def openmp_flags(self) -> T.List[str]:
+ return Compiler.openmp_flags(self)
+
+ def get_pic_args(self) -> T.List[str]:
+ return Compiler.get_pic_args(self)
+
+ def get_pie_args(self) -> T.List[str]:
+ return Compiler.get_pie_args(self)
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ return Compiler.get_profile_generate_args(self)
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return Compiler.get_profile_use_args(self)
+
+ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
+ return []
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_largefile_args(self) -> T.List[str]:
+ return []
+
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]:
+ return Compiler.get_prelink_args(self, prelink_name, obj_list)
+
+ def get_prelink_append_compile_args(self) -> bool:
+ return False
+
+ def supported_warn_args(self, warn_args_by_version: T.Dict[str, T.List[str]]) -> T.List[str]:
+ result: T.List[str] = []
+ for version, warn_args in warn_args_by_version.items():
+ if version_compare(self.gcc_version, '>=' + version):
+ result += warn_args
+ return result
+
+class Xc32CStds(GnuCStds):
+
+ """Mixin for setting C standards based on XC32 version."""
+
+ _C18_VERSION = '>=3.0'
+ _C2X_VERSION = '>=5.00'
+ _C23_VERSION = '==-1'
+ _C2Y_VERSION = '==-1'
+
+class Xc32CPPStds(GnuCPPStds):
+
+ """Mixin for setting C++ standards based on XC32 version."""
+
+ _CPP23_VERSION = '>=5.00'
+ _CPP26_VERSION = '==-1'
diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py
index 50335c8..7bb78a8 100644
--- a/mesonbuild/compilers/mixins/pgi.py
+++ b/mesonbuild/compilers/mixins/pgi.py
@@ -13,7 +13,6 @@ from ..compilers import clike_debug_args, clike_optimization_args
from ...options import OptionKey
if T.TYPE_CHECKING:
- from ...environment import Environment
from ...compilers.compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -51,9 +50,15 @@ class PGICompiler(Compiler):
return ['-fPIC']
return []
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['-mp']
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-E', '-P', '-o', '-']
+
+ def get_preprocess_to_file_args(self) -> T.List[str]:
+ return ['-E', '-P']
+
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return clike_optimization_args[optimization_level]
@@ -83,6 +88,6 @@ class PGICompiler(Compiler):
else:
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
# PGI cannot accept -pthread, it's already threaded
return []
diff --git a/mesonbuild/compilers/mixins/ti.py b/mesonbuild/compilers/mixins/ti.py
index 93cc31e..bdab3e4 100644
--- a/mesonbuild/compilers/mixins/ti.py
+++ b/mesonbuild/compilers/mixins/ti.py
@@ -12,7 +12,6 @@ from ...mesonlib import EnvironmentException
if T.TYPE_CHECKING:
from ...envconfig import MachineInfo
- from ...environment import Environment
from ...compilers.compilers import Compiler
else:
# This is a bit clever, for mypy we pretend that these mixins descend from
@@ -41,6 +40,10 @@ class TICompiler(Compiler):
id = 'ti'
+ if T.TYPE_CHECKING:
+ # Older versions of mypy can't figure this out for some reason.
+ is_cross: bool
+
def __init__(self) -> None:
if not self.is_cross:
raise EnvironmentException('TI compilers only support cross-compilation.')
@@ -67,7 +70,7 @@ class TICompiler(Compiler):
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
def get_coverage_args(self) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index 275e7ab..1a43114 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -20,7 +20,7 @@ from ...options import OptionKey
from mesonbuild.linkers.linkers import ClangClDynamicLinker
if T.TYPE_CHECKING:
- from ...environment import Environment
+ from ...build import BuildTarget
from ...dependencies import Dependency
from .clike import CLikeCompiler as Compiler
else:
@@ -66,11 +66,6 @@ msvc_optimization_args: T.Dict[str, T.List[str]] = {
's': ['/O1', '/Gw'],
}
-msvc_debug_args: T.Dict[bool, T.List[str]] = {
- False: [],
- True: ['/Zi']
-}
-
class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
@@ -167,7 +162,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_no_optimization_args(self) -> T.List[str]:
return ['/Od', '/Oi-']
- def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
+ def sanitizer_compile_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
if not value:
return value
return [f'/fsanitize={",".join(value)}']
@@ -180,7 +175,10 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return ['/Fo' + outputname]
def get_debug_args(self, is_debug: bool) -> T.List[str]:
- return msvc_debug_args[is_debug]
+ if is_debug:
+ return ['/Z7']
+ else:
+ return []
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
args = msvc_optimization_args[optimization_level]
@@ -189,30 +187,23 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return args
def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
- return ['/link'] + args
+ return ['/link'] + [arg for arg in args if arg != '/link']
def get_pic_args(self) -> T.List[str]:
return [] # PIC is handled by the loader on Windows
- def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
- if not isinstance(defsfile, str):
- raise RuntimeError('Module definitions file should be str')
- # With MSVC, DLLs only export symbols that are explicitly exported,
- # so if a module defs file is specified, we use that to export symbols
- return ['/DEF:' + defsfile]
-
def gen_pch_args(self, header: str, source: str, pchname: str) -> T.Tuple[str, T.List[str]]:
objname = os.path.splitext(source)[0] + '.obj'
return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname]
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return ['/openmp']
- def openmp_link_flags(self, env: Environment) -> T.List[str]:
+ def openmp_link_flags(self) -> T.List[str]:
return []
# FIXME, no idea what these should be.
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
@classmethod
@@ -296,9 +287,9 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
# Visual Studio is special. It ignores some arguments it does not
# understand and you can't tell it to error out on those.
# http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
- def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: CompileCheckMode) -> T.Tuple[bool, bool]:
+ def has_arguments(self, args: T.List[str], code: str, mode: CompileCheckMode) -> T.Tuple[bool, bool]:
warning_text = '4044' if mode == CompileCheckMode.LINK else '9002'
- with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
+ with self._build_wrapper(code, extra_args=args, mode=mode) as p:
if p.returncode != 0:
return False, p.cached
return not (warning_text in p.stderr or warning_text in p.stdout), p.cached
@@ -359,7 +350,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
crt_val = self.get_crt_val(crt_val, buildtype)
return self.crt_args[crt_val]
- def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_func_attribute(self, name: str) -> T.Tuple[bool, bool]:
# MSVC doesn't have __attribute__ like Clang and GCC do, so just return
# false without compiling anything
return name in {'dllimport', 'dllexport'}, False
@@ -368,7 +359,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_argument_syntax() -> str:
return 'msvc'
- def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ def symbols_have_underscore_prefix(self) -> bool:
'''
Check if the compiler prefixes an underscore to global C symbols.
@@ -378,12 +369,12 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
'''
# Try to consult a hardcoded list of cases we know
# absolutely have an underscore prefix
- result = self._symbols_have_underscore_prefix_list(env)
+ result = self._symbols_have_underscore_prefix_list()
if result is not None:
return result
# As a last resort, try search in a compiled binary
- return self._symbols_have_underscore_prefix_searchbin(env)
+ return self._symbols_have_underscore_prefix_searchbin()
def get_pie_args(self) -> T.List[str]:
return []
@@ -453,10 +444,10 @@ class ClangClCompiler(VisualStudioLikeCompiler):
self.can_compile_suffixes.add('s')
self.can_compile_suffixes.add('sx')
- def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: CompileCheckMode) -> T.Tuple[bool, bool]:
+ def has_arguments(self, args: T.List[str], code: str, mode: CompileCheckMode) -> T.Tuple[bool, bool]:
if mode != CompileCheckMode.LINK:
args = args + ['-Werror=unknown-argument', '-Werror=unknown-warning-option']
- return super().has_arguments(args, env, code, mode)
+ return super().has_arguments(args, code, mode)
def get_toolset_version(self) -> T.Optional[str]:
# XXX: what is the right thing to do here?
@@ -494,14 +485,15 @@ class ClangClCompiler(VisualStudioLikeCompiler):
else:
return dep.get_compile_args()
- def openmp_link_flags(self, env: Environment) -> T.List[str]:
+ def openmp_link_flags(self) -> T.List[str]:
# see https://github.com/mesonbuild/meson/issues/5298
- libs = self.find_library('libomp', env, [])
+ libs = self.find_library('libomp', [])
if libs is None:
raise mesonlib.MesonBugException('Could not find libomp')
- return super().openmp_link_flags(env) + libs
+ return super().openmp_link_flags() + libs
- def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default') -> T.List[str]:
args: T.List[str] = []
if mode == 'thin':
# LTO data generated by clang-cl is only usable by lld-link
@@ -510,11 +502,11 @@ class ClangClCompiler(VisualStudioLikeCompiler):
args.append(f'-flto={mode}')
else:
assert mode == 'default', 'someone forgot to wire something up'
- args.extend(super().get_lto_compile_args(threads=threads))
+ args.extend(super().get_lto_compile_args(target=target, threads=threads))
return args
- def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
- thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
args = []
if mode == 'thin' and thinlto_cache_dir is not None:
args.extend(self.linker.get_thinlto_cache_args(thinlto_cache_dir))
diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py
index d013417..d62113b 100644
--- a/mesonbuild/compilers/objc.py
+++ b/mesonbuild/compilers/objc.py
@@ -15,7 +15,6 @@ from .mixins.clike import CLikeCompiler
from .mixins.gnu import GnuCompiler, GnuCStds, gnu_common_warning_args, gnu_objc_warning_args
if T.TYPE_CHECKING:
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -28,11 +27,11 @@ class ObjCCompiler(CLikeCompiler, Compiler):
language = 'objc'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- Compiler.__init__(self, ccache, exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version,
+ Compiler.__init__(self, ccache, exelist, version, for_machine, env,
+ full_version=full_version,
linker=linker)
CLikeCompiler.__init__(self)
@@ -48,9 +47,9 @@ class ObjCCompiler(CLikeCompiler, Compiler):
def get_display_language() -> str:
return 'Objective-C'
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = '#import<stddef.h>\nint main(void) { return 0; }\n'
- return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjc.m', code)
+ return self._sanity_check_impl(work_dir, 'sanitycheckobjc.m', code)
def form_compileropt_key(self, basename: str) -> OptionKey:
if basename == 'std':
@@ -60,12 +59,12 @@ class ObjCCompiler(CLikeCompiler, Compiler):
class GnuObjCCompiler(GnuCStds, GnuCompiler, ObjCCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ ObjCCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -76,13 +75,13 @@ class GnuObjCCompiler(GnuCStds, GnuCompiler, ObjCCompiler):
self.supported_warn_args(gnu_common_warning_args) +
self.supported_warn_args(gnu_objc_warning_args))}
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
key = OptionKey('c_std', subproject=subproject, machine=self.for_machine)
if target:
- std = env.coredata.get_option_for_target(target, key)
+ std = self.environment.coredata.get_option_for_target(target, key)
else:
- std = env.coredata.optstore.get_value_for(key)
+ std = self.environment.coredata.optstore.get_value_for(key)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
@@ -90,12 +89,12 @@ class GnuObjCCompiler(GnuCStds, GnuCompiler, ObjCCompiler):
class ClangObjCCompiler(ClangCStds, ClangCompiler, ObjCCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ ObjCCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -114,10 +113,10 @@ class ClangObjCCompiler(ClangCStds, ClangCompiler, ObjCCompiler):
return 'c_std'
return super().make_option_name(key)
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
key = OptionKey('c_std', machine=self.for_machine)
- std = self.get_compileropt_value(key, env, target, subproject)
+ std = self.get_compileropt_value(key, target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index 441428b..927ca7e 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -15,7 +15,6 @@ from .mixins.clang import ClangCompiler, ClangCPPStds
from .mixins.clike import CLikeCompiler
if T.TYPE_CHECKING:
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -28,12 +27,11 @@ class ObjCPPCompiler(CLikeCompiler, Compiler):
language = 'objcpp'
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- Compiler.__init__(self, ccache, exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version,
- linker=linker)
+ Compiler.__init__(self, ccache, exelist, version, for_machine, env,
+ full_version=full_version, linker=linker)
CLikeCompiler.__init__(self)
def form_compileropt_key(self, basename: str) -> OptionKey:
@@ -50,9 +48,9 @@ class ObjCPPCompiler(CLikeCompiler, Compiler):
def get_display_language() -> str:
return 'Objective-C++'
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = '#import<stdio.h>\nclass MyClass;int main(void) { return 0; }\n'
- return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code)
+ return self._sanity_check_impl(work_dir, 'sanitycheckobjcpp.mm', code)
def get_options(self) -> MutableKeyedOptionDictType:
opts = super().get_options()
@@ -65,12 +63,12 @@ class ObjCPPCompiler(CLikeCompiler, Compiler):
class GnuObjCPPCompiler(GnuCPPStds, GnuCompiler, ObjCPPCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -81,13 +79,13 @@ class GnuObjCPPCompiler(GnuCPPStds, GnuCompiler, ObjCPPCompiler):
self.supported_warn_args(gnu_common_warning_args) +
self.supported_warn_args(gnu_objc_warning_args))}
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
key = OptionKey('cpp_std', subproject=subproject, machine=self.for_machine)
if target:
- std = env.coredata.get_option_for_target(target, key)
+ std = self.environment.coredata.get_option_for_target(target, key)
else:
- std = env.coredata.optstore.get_value_for(key)
+ std = self.environment.coredata.optstore.get_value_for(key)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
@@ -96,12 +94,12 @@ class GnuObjCPPCompiler(GnuCPPStds, GnuCompiler, ObjCPPCompiler):
class ClangObjCPPCompiler(ClangCPPStds, ClangCompiler, ObjCPPCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
+ env: Environment,
defines: T.Optional[T.Dict[str, str]] = None,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
- ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
- info, linker=linker, full_version=full_version)
+ ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine,
+ env, linker=linker, full_version=full_version)
ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -110,10 +108,10 @@ class ClangObjCPPCompiler(ClangCPPStds, ClangCompiler, ObjCPPCompiler):
'3': default_warn_args + ['-Wextra', '-Wpedantic'],
'everything': ['-Weverything']}
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
key = OptionKey('cpp_std', machine=self.for_machine)
- std = self.get_compileropt_value(key, env, target, subproject)
+ std = self.get_compileropt_value(key, target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('-std=' + std)
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index cc9dc21..ab0706d 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -11,13 +11,13 @@ import re
import typing as T
from .. import options
-from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged
+from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged, version_compare
+from ..linkers.linkers import VisualStudioLikeLinkerMixin
from ..options import OptionKey
-from .compilers import Compiler, CompileCheckMode, clike_debug_args
+from .compilers import Compiler, CompileCheckMode, clike_debug_args, is_library
if T.TYPE_CHECKING:
from ..options import MutableKeyedOptionDictType
- from ..envconfig import MachineInfo
from ..environment import Environment # noqa: F401
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -64,6 +64,15 @@ def get_rustup_run_and_args(exelist: T.List[str]) -> T.Optional[T.Tuple[T.List[s
except StopIteration:
return None
+def rustc_link_args(args: T.List[str]) -> T.List[str]:
+ if not args:
+ return args
+ rustc_args: T.List[str] = []
+ for arg in args:
+ rustc_args.append('-C')
+ rustc_args.append(f'link-arg={arg}')
+ return rustc_args
+
class RustCompiler(Compiler):
# rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX
@@ -78,36 +87,42 @@ class RustCompiler(Compiler):
'everything': ['-W', 'warnings'],
}
- # Those are static libraries, but we use dylib= here as workaround to avoid
- # rust --tests to use /WHOLEARCHIVE.
- # https://github.com/rust-lang/rust/issues/116910
+ allow_nightly: bool
+
+ # libcore can be compiled with either static or dynamic CRT, so disable
+ # both of them just in case.
MSVCRT_ARGS: T.Mapping[str, T.List[str]] = {
'none': [],
- 'md': [], # this is the default, no need to inject anything
- 'mdd': ['-l', 'dylib=msvcrtd'],
- 'mt': ['-l', 'dylib=libcmt'],
- 'mtd': ['-l', 'dylib=libcmtd'],
+ 'md': ['-Clink-arg=/nodefaultlib:libcmt', '-Clink-arg=/defaultlib:msvcrt'],
+ 'mdd': ['-Clink-arg=/nodefaultlib:libcmt', '-Clink-arg=/nodefaultlib:msvcrt', '-Clink-arg=/defaultlib:msvcrtd'],
+ 'mt': ['-Clink-arg=/defaultlib:libcmt', '-Clink-arg=/nodefaultlib:msvcrt'],
+ 'mtd': ['-Clink-arg=/nodefaultlib:libcmt', '-Clink-arg=/nodefaultlib:msvcrt', '-Clink-arg=/defaultlib:libcmtd'],
}
def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
- is_cross: bool, info: 'MachineInfo',
- full_version: T.Optional[str] = None,
+ env: Environment, full_version: T.Optional[str] = None,
linker: T.Optional['DynamicLinker'] = None):
- super().__init__([], exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version,
- linker=linker)
+ super().__init__([], exelist, version, for_machine, env,
+ full_version=full_version, linker=linker)
self.rustup_run_and_args: T.Optional[T.Tuple[T.List[str], T.List[str]]] = get_rustup_run_and_args(exelist)
- self.base_options.update({OptionKey(o) for o in ['b_colorout', 'b_ndebug']})
- if 'link' in self.linker.id:
+ self.base_options.update({OptionKey(o) for o in ['b_colorout', 'b_coverage', 'b_ndebug', 'b_pgo']})
+ if isinstance(self.linker, VisualStudioLikeLinkerMixin):
self.base_options.add(OptionKey('b_vscrt'))
self.native_static_libs: T.List[str] = []
self.is_beta = '-beta' in full_version
self.is_nightly = '-nightly' in full_version
+ self.has_check_cfg = version_compare(version, '>=1.80.0')
+
+ def init_from_options(self) -> None:
+ nightly_opt = self.get_compileropt_value('nightly', None)
+ if nightly_opt == 'enabled' and not self.is_nightly:
+ raise EnvironmentException(f'Rust compiler {self.name_string()} is not a nightly compiler as required by the "nightly" option.')
+ self.allow_nightly = nightly_opt != 'disabled' and self.is_nightly
def needs_static_linker(self) -> bool:
return False
- def sanity_check(self, work_dir: str, environment: Environment) -> None:
+ def sanity_check(self, work_dir: str) -> None:
source_name = os.path.join(work_dir, 'sanity.rs')
output_name = os.path.join(work_dir, 'rusttest.exe')
cmdlist = self.exelist.copy()
@@ -141,7 +156,7 @@ class RustCompiler(Compiler):
if pc.returncode != 0:
raise EnvironmentException(f'Rust compiler {self.name_string()} cannot compile programs.')
self._native_static_libs(work_dir, source_name)
- self.run_sanity_check(environment, [output_name], work_dir)
+ self.run_sanity_check([output_name], work_dir)
def _native_static_libs(self, work_dir: str, source_name: str) -> None:
# Get libraries needed to link with a Rust staticlib
@@ -155,6 +170,9 @@ class RustCompiler(Compiler):
# no match and kernel == none (i.e. baremetal) is a valid use case.
# return and let native_static_libs list empty
return
+ if self.info.system == 'emscripten':
+ # no match and emscripten is valid after rustc 1.84
+ return
raise EnvironmentException('Failed to find native-static-libs in Rust compiler output.')
# Exclude some well known libraries that we don't need because they
# are always part of C/C++ linkers. Rustc probably should not print
@@ -182,10 +200,69 @@ class RustCompiler(Compiler):
return stdo.split('\n', maxsplit=1)[0]
@functools.lru_cache(maxsize=None)
- def get_crt_static(self) -> bool:
+ def get_cfgs(self) -> T.List[str]:
cmd = self.get_exelist(ccache=False) + ['--print', 'cfg']
p, stdo, stde = Popen_safe_logged(cmd)
- return bool(re.search('^target_feature="crt-static"$', stdo, re.MULTILINE))
+ return stdo.splitlines()
+
+ @functools.lru_cache(maxsize=None)
+ def get_crt_static(self) -> bool:
+ return 'target_feature="crt-static"' in self.get_cfgs()
+
+ def get_nightly(self, target: T.Optional[BuildTarget]) -> bool:
+ if not target:
+ return self.allow_nightly
+ key = self.form_compileropt_key('nightly')
+ nightly_opt = self.environment.coredata.get_option_for_target(target, key)
+ if nightly_opt == 'enabled' and not self.is_nightly:
+ raise EnvironmentException(f'Rust compiler {self.name_string()} is not a nightly compiler as required by the "nightly" option.')
+ return nightly_opt != 'disabled' and self.is_nightly
+
+ def sanitizer_link_args(self, target: T.Optional[BuildTarget], value: T.List[str]) -> T.List[str]:
+ # Sanitizers are not supported yet for Rust code. Nightly supports that
+ # with -Zsanitizer=, but procedural macros cannot use them. But even if
+ # Rust code cannot be instrumented, we can link in the sanitizer libraries
+ # for the sake of C/C++ code
+ return rustc_link_args(super().sanitizer_link_args(target, value))
+
+ @functools.lru_cache(maxsize=None)
+ def has_verbatim(self) -> bool:
+ if version_compare(self.version, '< 1.67.0'):
+ return False
+ # GNU ld support '-l:PATH'
+ if 'ld.' in self.linker.id and self.linker.id != 'ld.wasm':
+ return True
+ # -l:+verbatim does not work (yet?) with MSVC link or Apple ld64
+ # (https://github.com/rust-lang/rust/pull/138753). For ld64, it
+ # works together with -l:+whole_archive because -force_load (the macOS
+ # equivalent of --whole-archive), receives the full path to the library
+ # being linked. However, Meson uses "bundle", not "whole_archive".
+ return False
+
+ def lib_file_to_l_arg(self, libname: str) -> T.Optional[str]:
+ """Undo the effects of -l on the filename, returning the
+ argument that can be passed to -l, or None if the
+ library name is not supported."""
+ if not is_library(libname):
+ return None
+ libname, ext = os.path.splitext(libname)
+
+ # On Windows, rustc's -lfoo searches either foo.lib or libfoo.a.
+ # Elsewhere, it searches both static and shared libraries and always with
+ # the "lib" prefix; for simplicity just skip .lib on non-Windows.
+ if self.info.is_windows():
+ if ext == '.lib':
+ return libname
+ if ext != '.a':
+ return None
+ else:
+ if ext == '.lib':
+ return None
+
+ if not libname.startswith('lib'):
+ return None
+ libname = libname[3:]
+ return libname
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return clike_debug_args[is_debug]
@@ -193,19 +270,13 @@ class RustCompiler(Compiler):
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return rust_optimization_args[optimization_level]
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
- args, to_remove = super().build_rpath_args(env, build_dir, from_dir, rpath_paths,
- build_rpath, install_rpath)
-
- # ... but then add rustc's sysroot to account for rustup
- # installations
- rustc_rpath_args = []
- for arg in args:
- rustc_rpath_args.append('-C')
- rustc_rpath_args.append(f'link-arg={arg}:{self.get_target_libdir()}')
- return rustc_rpath_args, to_remove
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ # add rustc's sysroot to account for rustup installations
+ args, to_remove = super().build_rpath_args(
+ build_dir, from_dir, target, [self.get_target_libdir()])
+ return rustc_link_args(args), to_remove
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
@@ -237,6 +308,18 @@ class RustCompiler(Compiler):
'none',
choices=['none', '2015', '2018', '2021', '2024'])
+ key = self.form_compileropt_key('dynamic_std')
+ opts[key] = options.UserBooleanOption(
+ self.make_option_name(key),
+ 'Whether to link Rust build targets to a dynamic libstd',
+ False)
+
+ key = self.form_compileropt_key('nightly')
+ opts[key] = options.UserFeatureOption(
+ self.make_option_name(key),
+ "Nightly Rust compiler (enabled=required, disabled=don't use nightly feature, auto=use nightly feature if available)",
+ 'auto')
+
return opts
def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
@@ -245,9 +328,9 @@ class RustCompiler(Compiler):
# provided by the linker flags.
return []
- def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('--edition=' + std)
@@ -258,8 +341,11 @@ class RustCompiler(Compiler):
return []
def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
- if self.linker.id not in {'link', 'lld-link'}:
+ if not isinstance(self.linker, VisualStudioLikeLinkerMixin):
return []
+ # Rustc always use non-debug Windows runtime. Inject the one selected
+ # by Meson options instead.
+ # https://github.com/rust-lang/rust/issues/39016
return self.MSVCRT_ARGS[self.get_crt_val(crt_val, buildtype)]
def get_colorout_args(self, colortype: str) -> T.List[str]:
@@ -267,13 +353,65 @@ class RustCompiler(Compiler):
return [f'--color={colortype}']
raise MesonException(f'Invalid color type for rust {colortype}')
+ @functools.lru_cache(maxsize=None)
def get_linker_always_args(self) -> T.List[str]:
- args: T.List[str] = []
- # Rust is super annoying, calling -C link-arg foo does not work, it has
- # to be -C link-arg=foo
- for a in super().get_linker_always_args():
- args.extend(['-C', f'link-arg={a}'])
- return args
+ return rustc_link_args(super().get_linker_always_args()) + ['-Cdefault-linker-libraries']
+
+ def get_embed_bitcode_args(self, bitcode: bool, lto: bool) -> T.List[str]:
+ if bitcode:
+ return ['-C', 'embed-bitcode=yes']
+ elif lto:
+ return []
+ else:
+ return ['-C', 'embed-bitcode=no']
+
+ def get_lto_compile_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default') -> T.List[str]:
+ if target.rust_crate_type in {'dylib', 'proc-macro'}:
+ return []
+
+ # TODO: what about -Clinker-plugin-lto?
+ rustc_lto = 'lto=thin' if mode == 'thin' else 'lto'
+ return ['-C', rustc_lto]
+
+ def get_lto_link_args(self, *, target: T.Optional[BuildTarget] = None, threads: int = 0,
+ mode: str = 'default', thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ # no need to specify anything because the rustc command line
+ # includes the result of get_lto_compile_args()
+ return []
+
+ def get_lto_obj_cache_path(self, path: str) -> T.List[str]:
+ return rustc_link_args(super().get_lto_obj_cache_path(path))
+
+ def get_coverage_args(self) -> T.List[str]:
+ return ['-C', 'instrument-coverage']
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return rustc_link_args(super().get_coverage_link_args())
+
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ return rustc_link_args(super().gen_vs_module_defs_args(defsfile))
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ return ['-C', 'profile-generate']
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return ['-C', 'profile-use']
+
+ @functools.lru_cache(maxsize=None)
+ def get_asneeded_args(self) -> T.List[str]:
+ return rustc_link_args(super().get_asneeded_args())
+
+ def bitcode_args(self) -> T.List[str]:
+ return ['-C', 'embed-bitcode=yes']
+
+ @functools.lru_cache(maxsize=None)
+ def headerpad_args(self) -> T.List[str]:
+ return rustc_link_args(super().headerpad_args())
+
+ @functools.lru_cache(maxsize=None)
+ def get_allow_undefined_link_args(self) -> T.List[str]:
+ return rustc_link_args(super().get_allow_undefined_link_args())
def get_werror_args(self) -> T.List[str]:
# Use -D warnings, which makes every warning not explicitly allowed an
@@ -293,11 +431,11 @@ class RustCompiler(Compiler):
# pic is on by rustc
return []
- def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ def get_assert_args(self, disable: bool) -> T.List[str]:
action = "no" if disable else "yes"
return ['-C', f'debug-assertions={action}', '-C', 'overflow-checks=no']
- def get_rust_tool(self, name: str, env: Environment) -> T.List[str]:
+ def get_rust_tool(self, name: str) -> T.List[str]:
if self.rustup_run_and_args:
rustup_exelist, args = self.rustup_run_and_args
# do not use extend so that exelist is copied
@@ -307,7 +445,7 @@ class RustCompiler(Compiler):
args = self.get_exe_args()
from ..programs import find_external_program
- for prog in find_external_program(env, self.for_machine, exelist[0], exelist[0],
+ for prog in find_external_program(self.environment, self.for_machine, exelist[0], exelist[0],
[exelist[0]], allow_default_for_cross=False):
exelist[0] = prog.path
break
@@ -316,22 +454,32 @@ class RustCompiler(Compiler):
return exelist + args
- def has_multi_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]:
- return self.compiles('fn main { std::process::exit(0) };\n', env, extra_args=args, mode=CompileCheckMode.COMPILE)
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ return self.compiles('fn main() { std::process::exit(0) }\n', extra_args=args, mode=CompileCheckMode.COMPILE)
- def has_multi_link_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]:
- args = self.linker.fatal_warnings() + args
- return self.compiles('fn main { std::process::exit(0) };\n', env, extra_args=args, mode=CompileCheckMode.LINK)
+ def has_multi_link_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
+ args = rustc_link_args(self.linker.fatal_warnings()) + args
+ return self.compiles('fn main() { std::process::exit(0) }\n', extra_args=args, mode=CompileCheckMode.LINK)
@functools.lru_cache(maxsize=None)
- def get_rustdoc(self, env: 'Environment') -> T.Optional[RustdocTestCompiler]:
- exelist = self.get_rust_tool('rustdoc', env)
+ def get_rustdoc(self) -> T.Optional[RustdocTestCompiler]:
+ exelist = self.get_rust_tool('rustdoc')
if not exelist:
return None
return RustdocTestCompiler(exelist, self.version, self.for_machine,
- self.is_cross, self.info, full_version=self.full_version,
- linker=self.linker)
+ self.environment,
+ full_version=self.full_version,
+ linker=self.linker, rustc=self)
+
+ def enable_env_set_args(self) -> T.Optional[T.List[str]]:
+ '''Extra arguments to enable --env-set support in rustc.
+ Returns None if not supported.
+ '''
+ if version_compare(self.version, '>= 1.76') and self.allow_nightly:
+ return ['-Z', 'unstable-options']
+ return None
+
class ClippyRustCompiler(RustCompiler):
@@ -351,6 +499,25 @@ class RustdocTestCompiler(RustCompiler):
id = 'rustdoc --test'
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ env: Environment, full_version: T.Optional[str],
+ linker: T.Optional['DynamicLinker'], rustc: RustCompiler):
+ super().__init__(exelist, version, for_machine,
+ env, full_version, linker)
+ self.rustc = rustc
+
+ @functools.lru_cache(maxsize=None)
+ def get_sysroot(self) -> str:
+ return self.rustc.get_sysroot()
+
+ @functools.lru_cache(maxsize=None)
+ def get_target_libdir(self) -> str:
+ return self.rustc.get_target_libdir()
+
+ @functools.lru_cache(maxsize=None)
+ def get_cfgs(self) -> T.List[str]:
+ return self.rustc.get_cfgs()
+
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return []
diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py
index 528d76f..d5f87a2 100644
--- a/mesonbuild/compilers/swift.py
+++ b/mesonbuild/compilers/swift.py
@@ -8,7 +8,7 @@ import subprocess, os.path
import typing as T
from .. import mlog, options
-from ..mesonlib import MesonException, version_compare
+from ..mesonlib import first, MesonException, version_compare
from .compilers import Compiler, clike_debug_args
@@ -16,7 +16,6 @@ if T.TYPE_CHECKING:
from .. import build
from ..options import MutableKeyedOptionDictType
from ..dependencies import Dependency
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..mesonlib import MachineChoice
@@ -38,11 +37,10 @@ class SwiftCompiler(Compiler):
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,
+ env: Environment, full_version: T.Optional[str] = None,
linker: T.Optional['DynamicLinker'] = None):
- super().__init__([], exelist, version, for_machine, info,
- is_cross=is_cross, full_version=full_version,
- linker=linker)
+ super().__init__([], exelist, version, for_machine, env,
+ full_version=full_version, linker=linker)
self.version = version
if self.info.is_darwin():
try:
@@ -130,15 +128,25 @@ class SwiftCompiler(Compiler):
return opts
- def get_option_std_args(self, target: build.BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_std_args(self, target: build.BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args: T.List[str] = []
- std = self.get_compileropt_value('std', env, target, subproject)
+ std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args += ['-swift-version', std]
+ # Pass C compiler -std=... arg to swiftc
+ c_langs = ['objc', 'c']
+ if target.uses_swift_cpp_interop():
+ c_langs = ['objcpp', 'cpp', *c_langs]
+
+ c_lang = first(c_langs, lambda x: x in target.compilers)
+ if c_lang is not None:
+ cc = target.compilers[c_lang]
+ args.extend(arg for c_arg in cc.get_option_std_args(target, subproject) for arg in ['-Xcc', c_arg])
+
return args
def get_working_directory_args(self, path: str) -> T.Optional[T.List[str]]:
@@ -147,6 +155,18 @@ class SwiftCompiler(Compiler):
return ['-working-directory', path]
+ def get_cxx_interoperability_args(self, target: T.Optional[build.BuildTarget] = None) -> T.List[str]:
+ if target is not None and not target.uses_swift_cpp_interop():
+ return []
+
+ if version_compare(self.version, '<5.9'):
+ raise MesonException(f'Compiler {self} does not support C++ interoperability')
+
+ return ['-cxx-interoperability-mode=default']
+
+ def get_library_args(self) -> T.List[str]:
+ return ['-parse-as-library']
+
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
for idx, i in enumerate(parameter_list):
@@ -155,22 +175,22 @@ class SwiftCompiler(Compiler):
return parameter_list
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
src = 'swifttest.swift'
source_name = os.path.join(work_dir, src)
output_name = os.path.join(work_dir, 'swifttest')
extra_flags: T.List[str] = []
- extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
+ extra_flags += self.environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
else:
- extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
+ extra_flags += self.environment.coredata.get_external_link_args(self.for_machine, self.language)
with open(source_name, 'w', encoding='utf-8') as ofile:
ofile.write('''print("Swift compilation is working.")
''')
pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir)
pc.wait()
- self.run_sanity_check(environment, [output_name], work_dir)
+ self.run_sanity_check([output_name], work_dir)
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return clike_debug_args[is_debug]
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
index 28861a6..575cf40 100644
--- a/mesonbuild/compilers/vala.py
+++ b/mesonbuild/compilers/vala.py
@@ -14,7 +14,6 @@ from .compilers import CompileCheckMode, Compiler
if T.TYPE_CHECKING:
from ..arglist import CompilerArgs
- from ..envconfig import MachineInfo
from ..environment import Environment
from ..mesonlib import MachineChoice
from ..dependencies import Dependency
@@ -26,8 +25,8 @@ class ValaCompiler(Compiler):
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)
+ environment: Environment):
+ super().__init__([], exelist, version, for_machine, environment)
self.version = version
self.base_options = {OptionKey('b_colorout')}
self.force_link = False
@@ -39,6 +38,14 @@ class ValaCompiler(Compiler):
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return []
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ if version_compare(self.version, '>=0.47.2'):
+ return ['--depfile', outfile]
+ return []
+
+ def get_depfile_suffix(self) -> str:
+ return 'depfile'
+
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return ['--debug'] if is_debug else []
@@ -99,21 +106,21 @@ class ValaCompiler(Compiler):
return parameter_list
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
code = 'class MesonSanityCheck : Object { }'
extra_flags: T.List[str] = []
- extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
+ extra_flags += self.environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
else:
- extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
- with self.cached_compile(code, environment.coredata, extra_args=extra_flags, mode=CompileCheckMode.COMPILE) as p:
+ extra_flags += self.environment.coredata.get_external_link_args(self.for_machine, self.language)
+ with self.cached_compile(code, extra_args=extra_flags, mode=CompileCheckMode.COMPILE) as p:
if p.returncode != 0:
msg = f'Vala compiler {self.name_string()!r} cannot compile programs'
raise EnvironmentException(msg)
- def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
- libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True) -> T.Optional[T.List[str]]:
+ def find_library(self, libname: str, extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED,
+ lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]:
if extra_dirs and isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
# Valac always looks in the default vapi dir, so only search there if
@@ -121,10 +128,10 @@ class ValaCompiler(Compiler):
if not extra_dirs:
code = 'class MesonFindLibrary : Object { }'
args: T.List[str] = []
- args += env.coredata.get_external_args(self.for_machine, self.language)
+ args += self.environment.coredata.get_external_args(self.for_machine, self.language)
vapi_args = ['--pkg', libname]
args += vapi_args
- with self.cached_compile(code, env.coredata, extra_args=args, mode=CompileCheckMode.COMPILE) as p:
+ with self.cached_compile(code, extra_args=args, mode=CompileCheckMode.COMPILE) as p:
if p.returncode == 0:
return vapi_args
# Not found? Try to find the vapi file itself.
@@ -135,16 +142,16 @@ class ValaCompiler(Compiler):
mlog.debug(f'Searched {extra_dirs!r} and {libname!r} wasn\'t found')
return None
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
- def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_link_flags(self) -> T.List[str]:
return []
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def build_wrapper_args(self, env: 'Environment',
+ def build_wrapper_args(self,
extra_args: T.Union[None, CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']],
mode: CompileCheckMode = CompileCheckMode.COMPILE) -> CompilerArgs:
@@ -180,28 +187,28 @@ class ValaCompiler(Compiler):
if mode is CompileCheckMode.COMPILE:
# Add DFLAGS from the env
- args += env.coredata.get_external_args(self.for_machine, self.language)
+ args += self.environment.coredata.get_external_args(self.for_machine, self.language)
elif mode is CompileCheckMode.LINK:
# Add LDFLAGS from the env
- args += env.coredata.get_external_link_args(self.for_machine, self.language)
+ args += self.environment.coredata.get_external_link_args(self.for_machine, self.language)
# extra_args must override all other arguments, so we add them last
args += extra_args
return args
- def links(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ def links(self, code: 'mesonlib.FileOrString', *,
compiler: T.Optional['Compiler'] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
self.force_link = True
if compiler:
- with compiler._build_wrapper(code, env, dependencies=dependencies, want_output=True) as r:
+ with compiler._build_wrapper(code, dependencies=dependencies, want_output=True) as r:
objfile = mesonlib.File.from_absolute_file(r.output_name)
- result = self.compiles(objfile, env, extra_args=extra_args,
+ result = self.compiles(objfile, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.LINK, disable_cache=True)
self.force_link = False
return result
- result = self.compiles(code, env, extra_args=extra_args,
+ result = self.compiles(code, extra_args=extra_args,
dependencies=dependencies, mode=CompileCheckMode.LINK, disable_cache=disable_cache)
self.force_link = False
return result
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 90157df..8d7a1c5 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -10,69 +10,41 @@ from . import mlog, options
import pickle, os, uuid
import sys
from functools import lru_cache
-from itertools import chain
from collections import OrderedDict
import textwrap
from .mesonlib import (
MesonException, MachineChoice, PerMachine,
PerMachineDefaultable,
- default_prefix,
- stringlistify,
pickle_load
)
from .options import OptionKey
-from .machinefile import CmdLineFileParser
-
-import ast
import enum
-import shlex
import typing as T
if T.TYPE_CHECKING:
- import argparse
- from typing_extensions import Protocol
-
from . import dependencies
from .compilers.compilers import Compiler, CompileResult, RunResult, CompileCheckMode
from .dependencies.detect import TV_DepID
- from .environment import Environment
from .mesonlib import FileOrString
from .cmake.traceparser import CMakeCacheEntry
from .interpreterbase import SubProject
from .options import ElementaryOptionValues, MutableKeyedOptionDictType
from .build import BuildTarget
-
- class SharedCMDOptions(Protocol):
-
- """Representation of command line options from Meson setup, configure,
- and dist.
-
- :param projectoptions: The raw list of command line options given
- :param cmd_line_options: command line options parsed into an OptionKey:
- str mapping
- """
-
- cmd_line_options: T.Dict[OptionKey, str]
- projectoptions: T.List[str]
- cross_file: T.List[str]
- native_file: T.List[str]
+ from .cmdline import SharedCMDOptions
OptionDictType = T.Dict[str, options.AnyOptionType]
CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode]
# code, args
RunCheckCacheKey = T.Tuple[str, T.Tuple[str, ...]]
- # typeshed
- StrOrBytesPath = T.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]
-
# Check major_versions_differ() if changing versioning scheme.
#
# Pip requires that RCs are named like this: '0.1.0.rc1'
# But the corresponding Git tag needs to be '0.1.0rc1'
-version = '1.8.99'
+version = '1.10.99'
# The next stable version when we are in dev. This is used to allow projects to
# require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when
@@ -147,13 +119,13 @@ class DependencyCache:
def __init__(self, builtins: options.OptionStore, for_machine: MachineChoice):
self.__cache: T.MutableMapping[TV_DepID, DependencySubCache] = OrderedDict()
self.__builtins = builtins
- self.__pkg_conf_key = options.OptionKey('pkg_config_path')
- self.__cmake_key = options.OptionKey('cmake_prefix_path')
+ self.__pkg_conf_key = options.OptionKey('pkg_config_path', machine=for_machine)
+ self.__cmake_key = options.OptionKey('cmake_prefix_path', machine=for_machine)
def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[str, ...]:
data: T.Dict[DependencyCacheType, T.List[str]] = {
- DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins.get_value_for(self.__pkg_conf_key)),
- DependencyCacheType.CMAKE: stringlistify(self.__builtins.get_value_for(self.__cmake_key)),
+ DependencyCacheType.PKG_CONFIG: T.cast('T.List[str]', self.__builtins.get_value_for(self.__pkg_conf_key)),
+ DependencyCacheType.CMAKE: T.cast('T.List[str]', self.__builtins.get_value_for(self.__cmake_key)),
DependencyCacheType.OTHER: [],
}
assert type_ in data, 'Someone forgot to update subkey calculations for a new type'
@@ -224,7 +196,7 @@ class CMakeStateCache:
def items(self) -> T.Iterator[T.Tuple[str, T.Dict[str, T.List[str]]]]:
return iter(self.__cache.items())
- def update(self, language: str, variables: T.Dict[str, T.List[str]]):
+ def update(self, language: str, variables: T.Dict[str, T.List[str]]) -> None:
if language not in self.__cache:
self.__cache[language] = {}
self.__cache[language].update(variables)
@@ -248,6 +220,7 @@ class CoreData:
'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'cpp': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
+ 'masm': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'test': '3AC096D0-A1C2-E12C-1390-A8335801FDAB',
'directory': '2150E333-8FDC-42A3-9474-1A3956D46DE8',
}
@@ -255,7 +228,7 @@ class CoreData:
self.regen_guid = str(uuid.uuid4()).upper()
self.install_guid = str(uuid.uuid4()).upper()
self.meson_command = meson_command
- self.target_guids = {}
+ self.target_guids: T.Dict[str, str] = {}
self.version = version
self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross')
self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict())
@@ -286,7 +259,7 @@ class CoreData:
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(cmd_options, scratch_dir, 'native')
self.builtin_options_libdir_cross_fixup()
- self.init_builtins()
+ self.optstore.init_builtins()
@staticmethod
def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]:
@@ -353,34 +326,6 @@ class CoreData:
if self.cross_files:
options.BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib'
- def init_builtins(self) -> None:
- # Create builtin options with default values
- for key, opt in options.BUILTIN_OPTIONS.items():
- self.add_builtin_option(self.optstore, key, opt)
- for for_machine in iter(MachineChoice):
- for key, opt in options.BUILTIN_OPTIONS_PER_MACHINE.items():
- self.add_builtin_option(self.optstore, key.evolve(machine=for_machine), opt)
-
- @staticmethod
- def add_builtin_option(optstore: options.OptionStore, key: OptionKey,
- opt: options.AnyOptionType) -> None:
- # Create a copy of the object, as we're going to mutate it
- opt = copy.copy(opt)
- if key.subproject:
- if opt.yielding:
- # This option is global and not per-subproject
- return
- else:
- new_value = options.argparse_prefixed_default(
- opt, key, default_prefix())
- opt.set_value(new_value)
-
- modulename = key.get_module_prefix()
- if modulename:
- optstore.add_module_option(modulename, key, opt)
- else:
- optstore.add_system_option(key, opt)
-
def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja':
self.optstore.add_system_option('backend_max_links', options.UserIntegerOption(
@@ -406,24 +351,14 @@ class CoreData:
# key and target have the same subproject for consistency.
# Now just do this to get things going.
newkey = newkey.evolve(subproject=target.subproject)
- (option_object, value) = self.optstore.get_value_object_and_value_for(newkey)
+ option_object, value = self.optstore.get_option_and_value_for(newkey)
override = target.get_override(newkey.name)
if override is not None:
return option_object.validate_value(override)
return value
- def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool:
- dirty = False
- try:
- changed = self.optstore.set_option(key, value, first_invocation)
- except KeyError:
- raise MesonException(f'Tried to set unknown builtin option {str(key)}')
- dirty |= changed
-
- if key.name == 'buildtype':
- dirty |= self._set_others_from_buildtype(value)
-
- return dirty
+ def set_from_configure_command(self, options: SharedCMDOptions) -> bool:
+ return self.optstore.set_from_configure_command(options.cmd_line_options)
def clear_cache(self) -> None:
self.deps.host.clear()
@@ -454,43 +389,18 @@ class CoreData:
return []
actual_opt = self.optstore.get_value_for('optimization')
actual_debug = self.optstore.get_value_for('debug')
+ assert isinstance(actual_opt, str) # for mypy
+ assert isinstance(actual_debug, bool) # for mypy
if actual_opt != opt:
result.append(('optimization', actual_opt, opt))
if actual_debug != debug:
result.append(('debug', actual_debug, debug))
return result
- def _set_others_from_buildtype(self, value: str) -> bool:
- dirty = False
-
- if value == 'plain':
- opt = 'plain'
- debug = False
- elif value == 'debug':
- opt = '0'
- debug = True
- elif value == 'debugoptimized':
- opt = '2'
- debug = True
- elif value == 'release':
- opt = '3'
- debug = False
- elif value == 'minsize':
- opt = 's'
- debug = True
- else:
- assert value == 'custom'
- return False
-
- dirty |= self.optstore.set_option(OptionKey('optimization'), opt)
- dirty |= self.optstore.set_option(OptionKey('debug'), debug)
-
- return dirty
-
def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]:
# mypy cannot analyze type of OptionKey
key = OptionKey(f'{lang}_args', machine=for_machine)
- return T.cast('T.List[str]', self.optstore.get_value(key))
+ return T.cast('T.List[str]', self.optstore.get_value_for(key))
@lru_cache(maxsize=None)
def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]:
@@ -503,92 +413,32 @@ class CoreData:
return False
return len(self.cross_files) > 0
- def copy_build_options_from_regular_ones(self, shut_up_pylint: bool = True) -> bool:
- # FIXME, needs cross compilation support.
- if shut_up_pylint:
- return False
- dirty = False
- assert not self.is_cross_build()
- for k in options.BUILTIN_OPTIONS_PER_MACHINE:
- o = self.optstore.get_value_object_for(k.name)
- dirty |= self.optstore.set_option(k, o.value, True)
- for bk, bv in self.optstore.items():
- if bk.machine is MachineChoice.BUILD:
- hk = bk.as_host()
- try:
- hv = self.optstore.get_value_object(hk)
- dirty |= bv.set_value(hv.value)
- except KeyError:
- continue
-
- return dirty
-
- def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = '', first_invocation: bool = False) -> bool:
- dirty = False
- if not self.is_cross_build():
- opts_to_set = {k: v for k, v in opts_to_set.items() if k.machine is not MachineChoice.BUILD}
- # Set prefix first because it's needed to sanitize other options
- pfk = OptionKey('prefix')
- if pfk in opts_to_set:
- prefix = self.optstore.sanitize_prefix(opts_to_set[pfk])
- for key in options.BUILTIN_DIR_NOPREFIX_OPTIONS:
- if key not in opts_to_set:
- val = options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix)
- dirty |= self.optstore.set_option(key, val)
-
- unknown_options: T.List[OptionKey] = []
- for k, v in opts_to_set.items():
- if k == pfk:
- continue
- elif k.evolve(subproject=None) in self.optstore:
- dirty |= self.set_option(k, v, first_invocation)
- elif k.machine != MachineChoice.BUILD and not self.optstore.is_compiler_option(k):
- unknown_options.append(k)
- if unknown_options:
- if subproject:
- # The subproject may have top-level options that should be used
- # when it is not a subproject. Ignore those for now. With option
- # refactor they will get per-subproject values.
- really_unknown = []
- for uo in unknown_options:
- topkey = uo.as_root()
- if topkey not in self.optstore:
- really_unknown.append(uo)
- unknown_options = really_unknown
- if unknown_options:
- unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
- sub = f'In subproject {subproject}: ' if subproject else ''
- raise MesonException(f'{sub}Unknown options: "{unknown_options_str}"')
-
- if not self.is_cross_build():
- dirty |= self.copy_build_options_from_regular_ones()
-
- return dirty
-
def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice,
- env: Environment, subproject: str) -> None:
+ subproject: str) -> None:
for k, o in c_options.items():
- comp_key = OptionKey(f'{k.name}', None, for_machine)
+ assert k.subproject is None and k.machine is for_machine
+ if subproject:
+ k = k.evolve(subproject=subproject)
if lang == 'objc' and k.name == 'c_std':
# For objective C, always fall back to c_std.
- self.optstore.add_compiler_option('c', comp_key, o)
+ self.optstore.add_compiler_option('c', k, o)
elif lang == 'objcpp' and k.name == 'cpp_std':
- self.optstore.add_compiler_option('cpp', comp_key, o)
+ self.optstore.add_compiler_option('cpp', k, o)
else:
- self.optstore.add_compiler_option(lang, comp_key, o)
+ self.optstore.add_compiler_option(lang, k, o)
- def add_lang_args(self, lang: str, comp: T.Type['Compiler'],
- for_machine: MachineChoice, env: 'Environment') -> None:
- """Add global language arguments that are needed before compiler/linker detection."""
- from .compilers import compilers
- # These options are all new at this point, because the compiler is
- # responsible for adding its own options, thus calling
- # `self.optstore.update()`` is perfectly safe.
- for gopt_key, gopt_valobj in compilers.get_global_options(lang, comp, for_machine, env).items():
- self.optstore.add_compiler_option(lang, gopt_key, gopt_valobj)
+ def process_compiler_options(self, lang: str, comp: Compiler, subproject: str) -> None:
+ self.add_compiler_options(comp.get_options(), lang, comp.for_machine, subproject)
- def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, subproject: str) -> None:
- self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env, subproject)
+ for key in [OptionKey(f'{lang}_args'), OptionKey(f'{lang}_link_args')]:
+ if self.is_cross_build():
+ key = key.evolve(machine=comp.for_machine)
+ # the global option is already there, but any augment is still
+ # sitting in pending_options has to be taken into account
+ assert key in self.optstore
+ if subproject:
+ skey = key.evolve(subproject=subproject)
+ self.optstore.add_compiler_option(lang, skey, self.optstore.get_value_object(key))
for key in comp.base_options:
if subproject:
@@ -598,71 +448,18 @@ class CoreData:
if skey not in self.optstore:
self.optstore.add_system_option(skey, copy.deepcopy(options.COMPILER_BASE_OPTIONS[key]))
+ comp.init_from_options()
+
self.emit_base_options_warnings()
def emit_base_options_warnings(self) -> None:
bcodekey = OptionKey('b_bitcode')
- if bcodekey in self.optstore and self.optstore.get_value(bcodekey):
+ if bcodekey in self.optstore and self.optstore.get_value_for(bcodekey):
msg = textwrap.dedent('''Base option 'b_bitcode' is enabled, which is incompatible with many linker options.
Incompatible options such as \'b_asneeded\' have been disabled.'
Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.''')
mlog.warning(msg, once=True, fatal=False)
-def get_cmd_line_file(build_dir: str) -> str:
- return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')
-
-def read_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
- filename = get_cmd_line_file(build_dir)
- if not os.path.isfile(filename):
- return
-
- config = CmdLineFileParser()
- config.read(filename)
-
- # Do a copy because config is not really a dict. options.cmd_line_options
- # overrides values from the file.
- d = {OptionKey.from_string(k): v for k, v in config['options'].items()}
- d.update(options.cmd_line_options)
- options.cmd_line_options = d
-
- properties = config['properties']
- if not options.cross_file:
- options.cross_file = ast.literal_eval(properties.get('cross_file', '[]'))
- if not options.native_file:
- # This will be a string in the form: "['first', 'second', ...]", use
- # literal_eval to get it into the list of strings.
- options.native_file = ast.literal_eval(properties.get('native_file', '[]'))
-
-def write_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
- filename = get_cmd_line_file(build_dir)
- config = CmdLineFileParser()
-
- properties: OrderedDict[str, str] = OrderedDict()
- if options.cross_file:
- properties['cross_file'] = options.cross_file
- if options.native_file:
- properties['native_file'] = options.native_file
-
- config['options'] = {str(k): str(v) for k, v in options.cmd_line_options.items()}
- config['properties'] = properties
- with open(filename, 'w', encoding='utf-8') as f:
- config.write(f)
-
-def update_cmd_line_file(build_dir: str, options: SharedCMDOptions) -> None:
- filename = get_cmd_line_file(build_dir)
- config = CmdLineFileParser()
- config.read(filename)
- config['options'].update({str(k): str(v) for k, v in options.cmd_line_options.items()})
- with open(filename, 'w', encoding='utf-8') as f:
- config.write(f)
-
-def format_cmd_line_options(options: SharedCMDOptions) -> str:
- cmdline = ['-D{}={}'.format(str(k), v) for k, v in options.cmd_line_options.items()]
- if options.cross_file:
- cmdline += [f'--cross-file={f}' for f in options.cross_file]
- if options.native_file:
- cmdline += [f'--native-file={f}' for f in options.native_file]
- return ' '.join([shlex.quote(x) for x in cmdline])
def major_versions_differ(v1: str, v2: str) -> bool:
v1_major, v1_minor = v1.rsplit('.', 1)
@@ -692,45 +489,6 @@ def save(obj: CoreData, build_dir: str) -> str:
return filename
-def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
- for n, b in options.BUILTIN_OPTIONS.items():
- options.option_to_argparse(b, n, parser, '')
- for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items():
- options.option_to_argparse(b, n, parser, ' (just for host machine)')
- options.option_to_argparse(b, n.as_build(), parser, ' (just for build machine)')
- parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
- help='Set the value of an option, can be used several times to set multiple options.')
-
-def create_options_dict(options: T.List[str], subproject: str = '') -> T.Dict[str, str]:
- result: T.OrderedDict[OptionKey, str] = OrderedDict()
- for o in options:
- try:
- (key, value) = o.split('=', 1)
- except ValueError:
- raise MesonException(f'Option {o!r} must have a value separated by equals sign.')
- result[key] = value
- return result
-
-def parse_cmd_line_options(args: SharedCMDOptions) -> None:
- args.cmd_line_options = create_options_dict(args.projectoptions)
-
- # Merge builtin options set with --option into the dict.
- for key in chain(
- options.BUILTIN_OPTIONS.keys(),
- (k.as_build() for k in options.BUILTIN_OPTIONS_PER_MACHINE.keys()),
- options.BUILTIN_OPTIONS_PER_MACHINE.keys(),
- ):
- name = str(key)
- value = getattr(args, name, None)
- if value is not None:
- if key in args.cmd_line_options:
- cmdline_name = options.argparse_name_to_arg(name)
- raise MesonException(
- f'Got argument {name} as both -D{name} and {cmdline_name}. Pick one.')
- args.cmd_line_options[key.name] = value
- delattr(args, name)
-
-
FORBIDDEN_TARGET_NAMES = frozenset({
'clean',
'clean-ctlist',
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py
index 95e6069..abe8335 100644
--- a/mesonbuild/dependencies/__init__.py
+++ b/mesonbuild/dependencies/__init__.py
@@ -78,7 +78,7 @@ class FooSystemDependency(ExternalDependency):
self.is_found = False
return
- lib = self.clib_compiler.find_library('foo', environment, [os.path.join(root, 'lib')])
+ lib = self.clib_compiler.find_library('foo', [os.path.join(root, 'lib')])
if lib is None:
mlog.debug('Could not find lib.')
self.is_found = False
@@ -114,7 +114,7 @@ class FooSystemDependency(ExternalDependency):
return
get_option = environment.coredata.get_option
- static_opt = kwargs.get('static', get_option(Mesonlib.OptionKey('prefer_static'))
+ static_opt = kwargs['static'] if kwargs.get('static') is not None else get_option(Mesonlib.OptionKey('prefer_static')
static = Mesonlib.LibType.STATIC if static_opt else Mesonlib.LibType.SHARED
lib = self.clib_compiler.find_library(
'foo', environment, [os.path.join(root, 'lib')], libtype=static)
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 38bfc08..4536746 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -21,6 +21,8 @@ from ..options import OptionKey
#from ..interpreterbase import FeatureDeprecated, FeatureNew
if T.TYPE_CHECKING:
+ from typing_extensions import Literal, TypedDict, TypeAlias
+
from ..compilers.compilers import Compiler
from ..environment import Environment
from ..interpreterbase import FeatureCheckBase
@@ -30,6 +32,42 @@ if T.TYPE_CHECKING:
)
from ..interpreter.type_checking import PkgConfigDefineType
+ IncludeType: TypeAlias = Literal['system', 'non-system', 'preserve']
+
+ class DependencyObjectKWs(TypedDict, total=False):
+
+ """Keyword arguments that the Dependency IR object accepts.
+
+ This is different than the arguments as taken by the Interpreter, since
+ it is expected to be clean.
+ """
+
+ cmake_args: T.List[str]
+ cmake_module_path: T.List[str]
+ cmake_package_version: str
+ components: T.List[str]
+ include_type: IncludeType
+ language: T.Optional[str]
+ main: bool
+ method: DependencyMethods
+ modules: T.List[str]
+ native: MachineChoice
+ optional_modules: T.List[str]
+ private_headers: bool
+ required: bool
+ static: T.Optional[bool]
+ version: T.List[str]
+
+ # Only in the python dependency
+ embed: bool
+
+ # Only passed internally, not part of the DSL API
+ paths: T.List[str]
+ returncode_value: int
+ silent: bool
+ tools: T.List[str]
+ version_arg: str
+
_MissingCompilerBase = Compiler
else:
_MissingCompilerBase = object
@@ -55,7 +93,7 @@ class MissingCompiler(_MissingCompilerBase):
def get_output_args(self, outputname: str) -> T.List[str]:
return []
- def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ def sanity_check(self, work_dir: str) -> None:
return None
def __getattr__(self, item: str) -> T.Any:
@@ -82,12 +120,6 @@ class DependencyMethods(Enum):
SYSCONFIG = 'sysconfig'
# Specify using a "program"-config style tool
CONFIG_TOOL = 'config-tool'
- # For backwards compatibility
- SDLCONFIG = 'sdlconfig'
- CUPSCONFIG = 'cups-config'
- PCAPCONFIG = 'pcap-config'
- LIBWMFCONFIG = 'libwmf-config'
- QMAKE = 'qmake'
# Misc
DUB = 'dub'
@@ -97,17 +129,7 @@ DependencyTypeName = T.NewType('DependencyTypeName', str)
class Dependency(HoldableObject):
- @classmethod
- def _process_include_type_kw(cls, kwargs: T.Dict[str, T.Any]) -> str:
- if 'include_type' not in kwargs:
- return 'preserve'
- if not isinstance(kwargs['include_type'], str):
- raise DependencyException('The include_type kwarg must be a string type')
- if kwargs['include_type'] not in ['preserve', 'system', 'non-system']:
- raise DependencyException("include_type may only be one of ['preserve', 'system', 'non-system']")
- return kwargs['include_type']
-
- def __init__(self, type_name: DependencyTypeName, kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, type_name: DependencyTypeName, kwargs: DependencyObjectKWs) -> None:
# This allows two Dependencies to be compared even after being copied.
# The purpose is to allow the name to be changed, but still have a proper comparison
self._id = uuid.uuid4().int
@@ -123,11 +145,12 @@ class Dependency(HoldableObject):
self.raw_link_args: T.Optional[T.List[str]] = None
self.sources: T.List[T.Union[mesonlib.File, GeneratedTypes, 'StructuredSources']] = []
self.extra_files: T.List[mesonlib.File] = []
- self.include_type = self._process_include_type_kw(kwargs)
+ self.include_type = kwargs.get('include_type', 'preserve')
self.ext_deps: T.List[Dependency] = []
self.d_features: T.DefaultDict[str, T.List[T.Any]] = collections.defaultdict(list)
self.featurechecks: T.List['FeatureCheckBase'] = []
self.feature_since: T.Optional[T.Tuple[str, str]] = None
+ self.meson_variables: T.List[str] = []
def __eq__(self, other: object) -> bool:
if not isinstance(other, Dependency):
@@ -143,6 +166,11 @@ class Dependency(HoldableObject):
def is_built(self) -> bool:
return False
+ def is_named(self) -> bool:
+ if self.name is None:
+ return False
+ return self.name != f'dep{self._id}'
+
def summary_value(self) -> T.Union[str, mlog.AnsiDecorator, mlog.AnsiText]:
if not self.found():
return mlog.red('NO')
@@ -256,9 +284,9 @@ class Dependency(HoldableObject):
return default_value
raise DependencyException(f'No default provided for dependency {self!r}, which is not pkg-config, cmake, or config-tool based.')
- def generate_system_dependency(self, include_type: str) -> 'Dependency':
+ def generate_system_dependency(self, include_type: IncludeType) -> 'Dependency':
new_dep = copy.deepcopy(self)
- new_dep.include_type = self._process_include_type_kw({'include_type': include_type})
+ new_dep.include_type = include_type
return new_dep
def get_as_static(self, recursive: bool) -> Dependency:
@@ -383,32 +411,23 @@ class InternalDependency(Dependency):
new_dep.ext_deps = [dep.get_as_shared(True) for dep in self.ext_deps]
return new_dep
-class HasNativeKwarg:
- def __init__(self, kwargs: T.Dict[str, T.Any]):
- self.for_machine = self.get_for_machine_from_kwargs(kwargs)
-
- def get_for_machine_from_kwargs(self, kwargs: T.Dict[str, T.Any]) -> MachineChoice:
- return MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST
-
-class ExternalDependency(Dependency, HasNativeKwarg):
- def __init__(self, type_name: DependencyTypeName, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None):
+class ExternalDependency(Dependency):
+ def __init__(self, type_name: DependencyTypeName, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None):
Dependency.__init__(self, type_name, kwargs)
self.env = environment
self.name = type_name # default
self.is_found = False
self.language = language
- version_reqs = kwargs.get('version', None)
- if isinstance(version_reqs, str):
- version_reqs = [version_reqs]
- self.version_reqs: T.Optional[T.List[str]] = version_reqs
+ self.version_reqs = kwargs.get('version', [])
self.required = kwargs.get('required', True)
self.silent = kwargs.get('silent', False)
- self.static = kwargs.get('static', self.env.coredata.optstore.get_value_for(OptionKey('prefer_static')))
+ static = kwargs.get('static')
+ if static is None:
+ static = T.cast('bool', self.env.coredata.optstore.get_value_for(OptionKey('prefer_static')))
+ self.static = static
self.libtype = LibType.STATIC if self.static else LibType.PREFER_SHARED
- if not isinstance(self.static, bool):
- raise DependencyException('Static keyword must be boolean')
# Is this dependency to be run on the build platform?
- HasNativeKwarg.__init__(self, kwargs)
+ self.for_machine = kwargs.get('native', MachineChoice.HOST)
self.clib_compiler = detect_compiler(self.name, environment, self.for_machine, self.language)
def get_compiler(self) -> T.Union['MissingCompiler', 'Compiler']:
@@ -597,36 +616,14 @@ def strip_system_includedirs(environment: 'Environment', for_machine: MachineCho
exclude = {f'-I{p}' for p in environment.get_compiler_system_include_dirs(for_machine)}
return [i for i in include_args if i not in exclude]
-def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs: T.Dict[str, T.Any]) -> T.List[DependencyMethods]:
- method: T.Union[DependencyMethods, str] = kwargs.get('method', 'auto')
- if isinstance(method, DependencyMethods):
- return [method]
- # TODO: try/except?
- if method not in [e.value for e in DependencyMethods]:
- raise DependencyException(f'method {method!r} is invalid')
- method = DependencyMethods(method)
-
- # Raise FeatureNew where appropriate
- if method is DependencyMethods.CONFIG_TOOL:
- # FIXME: needs to get a handle on the subproject
- # FeatureNew.single_use('Configuration method "config-tool"', '0.44.0')
- pass
- # This sets per-tool config methods which are deprecated to to the new
- # generic CONFIG_TOOL value.
- if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG,
- DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]:
- # FIXME: needs to get a handle on the subproject
- #FeatureDeprecated.single_use(f'Configuration method {method.value}', '0.44', 'Use "config-tool" instead.')
- method = DependencyMethods.CONFIG_TOOL
- if method is DependencyMethods.QMAKE:
- # FIXME: needs to get a handle on the subproject
- # FeatureDeprecated.single_use('Configuration method "qmake"', '0.58', 'Use "config-tool" instead.')
- method = DependencyMethods.CONFIG_TOOL
+def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs: DependencyObjectKWs) -> T.List[DependencyMethods]:
+ method = kwargs.get('method', DependencyMethods.AUTO)
# Set the detection method. If the method is set to auto, use any available method.
# If method is set to a specific string, allow only that detection method.
if method == DependencyMethods.AUTO:
- methods = list(possible)
+ # annotated for https://github.com/python/mypy/issues/19894
+ methods: T.List[DependencyMethods] = list(possible)
elif method in possible:
methods = [method]
else:
@@ -663,7 +660,7 @@ class SystemDependency(ExternalDependency):
"""Dependency base for System type dependencies."""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('system'), env, kwargs, language=language)
self.name = name
@@ -677,7 +674,7 @@ class BuiltinDependency(ExternalDependency):
"""Dependency base for Builtin type dependencies."""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('builtin'), env, kwargs, language=language)
self.name = name
diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py
index 662f985..1aeb451 100644
--- a/mesonbuild/dependencies/boost.py
+++ b/mesonbuild/dependencies/boost.py
@@ -21,6 +21,7 @@ from .misc import threads_factory
if T.TYPE_CHECKING:
from ..envconfig import Properties
from ..environment import Environment
+ from .base import DependencyObjectKWs
# On windows 3 directory layouts are supported:
# * The default layout (versioned) installed:
@@ -339,7 +340,7 @@ class BoostLibraryFile():
return [self.path.as_posix()]
class BoostDependency(SystemDependency):
- def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: Environment, kwargs: DependencyObjectKWs) -> None:
super().__init__('boost', environment, kwargs, language='cpp')
buildtype = environment.coredata.optstore.get_value_for(OptionKey('buildtype'))
assert isinstance(buildtype, str)
@@ -347,13 +348,11 @@ class BoostDependency(SystemDependency):
self.multithreading = kwargs.get('threading', 'multi') == 'multi'
self.boost_root: T.Optional[Path] = None
- self.explicit_static = 'static' in kwargs
+ self.explicit_static = kwargs.get('static') is not None
# Extract and validate modules
- self.modules: T.List[str] = mesonlib.extract_as_list(kwargs, 'modules')
+ self.modules = kwargs.get('modules', [])
for i in self.modules:
- if not isinstance(i, str):
- raise DependencyException('Boost module argument is not a string.')
if i.startswith('boost_'):
raise DependencyException('Boost modules must be passed without the boost_ prefix')
@@ -440,6 +439,8 @@ class BoostDependency(SystemDependency):
mlog.debug(' - potential library dirs: {}'.format([x.as_posix() for x in lib_dirs]))
mlog.debug(' - potential include dirs: {}'.format([x.path.as_posix() for x in inc_dirs]))
+ must_have_library = ['boost_python']
+
# 2. Find all boost libraries
libs: T.List[BoostLibraryFile] = []
for i in lib_dirs:
@@ -452,6 +453,10 @@ class BoostDependency(SystemDependency):
break
libs = sorted(set(libs))
+ any_libs_found = len(libs) > 0
+ if not any_libs_found:
+ return False
+
modules = ['boost_' + x for x in self.modules]
for inc in inc_dirs:
mlog.debug(f' - found boost {inc.version} include dir: {inc.path}')
@@ -462,7 +467,7 @@ class BoostDependency(SystemDependency):
mlog.debug(f' - {j}')
# 3. Select the libraries matching the requested modules
- not_found: T.List[str] = []
+ not_found_as_libs: T.List[str] = []
selected_modules: T.List[BoostLibraryFile] = []
for mod in modules:
found = False
@@ -472,7 +477,24 @@ class BoostDependency(SystemDependency):
found = True
break
if not found:
- not_found += [mod]
+ not_found_as_libs += [mod]
+
+ # If a lib is not found, but an include directory exists,
+ # assume it is a header only module.
+ not_found: T.List[str] = []
+ for boost_modulename in not_found_as_libs:
+ assert boost_modulename.startswith('boost_')
+ if boost_modulename in must_have_library:
+ not_found.append(boost_modulename)
+ continue
+ include_subdir = boost_modulename.replace('boost_', 'boost/', 1)
+ headerdir_found = False
+ for inc_dir in inc_dirs:
+ if (inc_dir.path / include_subdir).is_dir():
+ headerdir_found = True
+ break
+ if not headerdir_found:
+ not_found.append(boost_modulename)
# log the result
mlog.debug(' - found:')
@@ -535,7 +557,7 @@ class BoostDependency(SystemDependency):
# given root path
if use_system:
- system_dirs_t = self.clib_compiler.get_library_dirs(self.env)
+ system_dirs_t = self.clib_compiler.get_library_dirs()
system_dirs = [Path(x) for x in system_dirs_t]
system_dirs = [x.resolve() for x in system_dirs if x.exists()]
system_dirs = [x for x in system_dirs if mesonlib.path_is_in_root(x, root)]
@@ -581,9 +603,9 @@ class BoostDependency(SystemDependency):
# MSVC is very picky with the library tags
vscrt = ''
try:
- crt_val = self.env.coredata.optstore.get_value('b_vscrt')
+ crt_val = self.env.coredata.optstore.get_value_for('b_vscrt')
assert isinstance(crt_val, str)
- buildtype = self.env.coredata.optstore.get_value('buildtype')
+ buildtype = self.env.coredata.optstore.get_value_for('buildtype')
assert isinstance(buildtype, str)
vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0]
except (KeyError, IndexError, AttributeError):
diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py
index 4e44981..066bb29 100644
--- a/mesonbuild/dependencies/cmake.py
+++ b/mesonbuild/dependencies/cmake.py
@@ -4,7 +4,7 @@
from __future__ import annotations
from .base import ExternalDependency, DependencyException, DependencyTypeName
-from ..mesonlib import is_windows, MesonException, PerMachine, stringlistify, extract_as_list
+from ..mesonlib import is_windows, MesonException, PerMachine, MachineChoice
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args, resolve_cmake_trace_targets, cmake_is_debug
from .. import mlog
import importlib.resources
@@ -21,6 +21,7 @@ if T.TYPE_CHECKING:
from ..environment import Environment
from ..envconfig import MachineInfo
from ..interpreter.type_checking import PkgConfigDefineType
+ from .base import DependencyObjectKWs
class CMakeInfo(T.NamedTuple):
module_paths: T.List[str]
@@ -69,16 +70,12 @@ class CMakeDependency(ExternalDependency):
# one module
return module
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> None:
# Gather a list of all languages to support
self.language_list: T.List[str] = []
if language is None or force_use_global_compilers:
- compilers = None
- if kwargs.get('native', False):
- compilers = environment.coredata.compilers.build
- else:
- compilers = environment.coredata.compilers.host
-
+ for_machine = kwargs.get('native', MachineChoice.HOST)
+ compilers = environment.coredata.compilers[for_machine]
candidates = ['c', 'cpp', 'fortran', 'objc', 'objcxx']
self.language_list += [x for x in candidates if x in compilers]
else:
@@ -116,7 +113,7 @@ class CMakeDependency(ExternalDependency):
# Setup the trace parser
self.traceparser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir(), self.env)
- cm_args = stringlistify(extract_as_list(kwargs, 'cmake_args'))
+ cm_args = kwargs.get('cmake_args', [])
cm_args = check_cmake_args(cm_args)
if CMakeDependency.class_cmakeinfo[self.for_machine] is None:
CMakeDependency.class_cmakeinfo[self.for_machine] = self._get_cmake_info(cm_args)
@@ -126,13 +123,10 @@ class CMakeDependency(ExternalDependency):
self.cmakeinfo = cmakeinfo
package_version = kwargs.get('cmake_package_version', '')
- if not isinstance(package_version, str):
- raise DependencyException('Keyword "cmake_package_version" must be a string.')
- components = [(x, True) for x in stringlistify(extract_as_list(kwargs, 'components'))]
- modules = [(x, True) for x in stringlistify(extract_as_list(kwargs, 'modules'))]
- modules += [(x, False) for x in stringlistify(extract_as_list(kwargs, 'optional_modules'))]
- cm_path = stringlistify(extract_as_list(kwargs, 'cmake_module_path'))
- cm_path = [x if os.path.isabs(x) else os.path.join(environment.get_source_dir(), x) for x in cm_path]
+ components = [(x, True) for x in kwargs.get('components', [])]
+ modules = [(x, True) for x in kwargs.get('modules', [])]
+ modules += [(x, False) for x in kwargs.get('optional_modules', [])]
+ cm_path = [x if os.path.isabs(x) else os.path.join(environment.get_source_dir(), x) for x in kwargs.get('cmake_module_path', [])]
if cm_path:
cm_args.append('-DCMAKE_MODULE_PATH=' + ';'.join(cm_path))
if not self._preliminary_find_check(name, cm_path, self.cmakebin.get_cmake_prefix_paths(), environment.machines[self.for_machine]):
@@ -655,7 +649,7 @@ class CMakeDependencyFactory:
self.name = name
self.modules = modules
- def __call__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> CMakeDependency:
+ def __call__(self, name: str, env: Environment, kwargs: DependencyObjectKWs, language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> CMakeDependency:
if self.modules:
kwargs['modules'] = self.modules
return CMakeDependency(self.name or name, env, kwargs, language, force_use_global_compilers)
diff --git a/mesonbuild/dependencies/coarrays.py b/mesonbuild/dependencies/coarrays.py
index 7701c06..a4dbdc5 100644
--- a/mesonbuild/dependencies/coarrays.py
+++ b/mesonbuild/dependencies/coarrays.py
@@ -16,12 +16,13 @@ if T.TYPE_CHECKING:
from . factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
+ from .base import DependencyObjectKWs
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE, DependencyMethods.SYSTEM})
def coarray_factory(env: 'Environment',
for_machine: 'MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
fcid = detect_compiler('coarray', env, for_machine, 'fortran').get_id()
candidates: T.List['DependencyGenerator'] = []
@@ -34,8 +35,8 @@ def coarray_factory(env: 'Environment',
PkgConfigDependency, pkg, env, kwargs, language='fortran'))
if DependencyMethods.CMAKE in methods:
- if 'modules' not in kwargs:
- kwargs['modules'] = 'OpenCoarrays::caf_mpi'
+ if not kwargs.get('modules'):
+ kwargs['modules'] = ['OpenCoarrays::caf_mpi']
candidates.append(functools.partial(
CMakeDependency, 'OpenCoarrays', env, kwargs, language='fortran'))
@@ -55,7 +56,7 @@ class CoarrayDependency(SystemDependency):
Coarrays may be thought of as a high-level language abstraction of
low-level MPI calls.
"""
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__('coarray', environment, kwargs, language='fortran')
kwargs['required'] = False
kwargs['silent'] = True
diff --git a/mesonbuild/dependencies/configtool.py b/mesonbuild/dependencies/configtool.py
index 476f7ad..e2721fe 100644
--- a/mesonbuild/dependencies/configtool.py
+++ b/mesonbuild/dependencies/configtool.py
@@ -10,11 +10,11 @@ from .. import mlog
import re
import typing as T
-from mesonbuild import mesonlib
-
if T.TYPE_CHECKING:
from ..environment import Environment
from ..interpreter.type_checking import PkgConfigDefineType
+ from .base import DependencyObjectKWs
+
class ConfigToolDependency(ExternalDependency):
@@ -37,7 +37,7 @@ class ConfigToolDependency(ExternalDependency):
allow_default_for_cross = False
__strip_version = re.compile(r'^[0-9][0-9.]+')
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, exclude_paths: T.Optional[T.List[str]] = None):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None, exclude_paths: T.Optional[T.List[str]] = None):
super().__init__(DependencyTypeName('config-tool'), environment, kwargs, language=language)
self.name = name
# You may want to overwrite the class version in some cases
@@ -47,11 +47,7 @@ class ConfigToolDependency(ExternalDependency):
if 'version_arg' in kwargs:
self.version_arg = kwargs['version_arg']
- req_version_raw = kwargs.get('version', None)
- if req_version_raw is not None:
- req_version = mesonlib.stringlistify(req_version_raw)
- else:
- req_version = []
+ req_version = kwargs.get('version', [])
tool, version = self.find_config(req_version, kwargs.get('returncode_value', 0), exclude_paths=exclude_paths)
self.config = tool
self.is_found = self.report_config(version, req_version)
diff --git a/mesonbuild/dependencies/cuda.py b/mesonbuild/dependencies/cuda.py
index 82bf5ad..d80c62d 100644
--- a/mesonbuild/dependencies/cuda.py
+++ b/mesonbuild/dependencies/cuda.py
@@ -11,35 +11,40 @@ from pathlib import Path
from .. import mesonlib
from .. import mlog
-from ..environment import detect_cpu_family
from .base import DependencyException, SystemDependency
from .detect import packages
-
+from ..mesonlib import LibType
if T.TYPE_CHECKING:
from ..environment import Environment
from ..compilers import Compiler
+ from ..envconfig import MachineInfo
+ from .base import DependencyObjectKWs
TV_ResultTuple = T.Tuple[T.Optional[str], T.Optional[str], bool]
class CudaDependency(SystemDependency):
supported_languages = ['cpp', 'c', 'cuda'] # see also _default_language
+ targets_dir = 'targets' # Directory containing CUDA targets.
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
- compilers = environment.coredata.compilers[self.get_for_machine_from_kwargs(kwargs)]
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
+ for_machine = kwargs.get('native', mesonlib.MachineChoice.HOST)
+ compilers = environment.coredata.compilers[for_machine]
+ machine = environment.machines[for_machine]
language = self._detect_language(compilers)
+
if language not in self.supported_languages:
raise DependencyException(f'Language \'{language}\' is not supported by the CUDA Toolkit. Supported languages are {self.supported_languages}.')
super().__init__('cuda', environment, kwargs, language=language)
self.lib_modules: T.Dict[str, T.List[str]] = {}
- self.requested_modules = self.get_requested(kwargs)
+ self.requested_modules = kwargs.get('modules', [])
if not any(runtime in self.requested_modules for runtime in ['cudart', 'cudart_static']):
# By default, we prefer to link the static CUDA runtime, since this is what nvcc also does by default:
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#cudart-none-shared-static-cudart
req_modules = ['cudart']
- if kwargs.get('static', True):
+ if kwargs.get('static') is not False:
req_modules = ['cudart_static']
self.requested_modules = req_modules + self.requested_modules
@@ -50,16 +55,25 @@ class CudaDependency(SystemDependency):
if not os.path.isabs(self.cuda_path):
raise DependencyException(f'CUDA Toolkit path must be absolute, got \'{self.cuda_path}\'.')
+ # Cuda target directory relative to cuda path.
+ self.target_path = self._detect_target_path(machine)
+
# nvcc already knows where to find the CUDA Toolkit, but if we're compiling
# a mixed C/C++/CUDA project, we still need to make the include dir searchable
if self.language != 'cuda' or len(compilers) > 1:
- self.incdir = os.path.join(self.cuda_path, 'include')
+ self.incdir = os.path.join(self.cuda_path, self.target_path, 'include')
self.compile_args += [f'-I{self.incdir}']
arch_libdir = self._detect_arch_libdir()
- self.libdir = os.path.join(self.cuda_path, arch_libdir)
+ self.libdir = os.path.join(self.cuda_path, self.target_path, arch_libdir)
mlog.debug('CUDA library directory is', mlog.bold(self.libdir))
+ # For legacy reasons cuda ignores the `prefer_static` option, and treats
+ # anything short of `static : false` as `static : true`. This is the
+ # opposite behavior of all other languages.
+ if kwargs.get('static') is None:
+ self.libtype = LibType.PREFER_STATIC
+
self.is_found = self._find_requested_libraries()
@classmethod
@@ -128,6 +142,32 @@ class CudaDependency(SystemDependency):
mlog.warning(nvcc_warning)
return (None, None, False)
+ def _detect_target_path(self, machine: MachineInfo) -> str:
+ # Non-Linux hosts: nothing to detect.
+ if not machine.is_linux():
+ return '.'
+
+ # Canonical target: '<arch>-<system>', e.g. 'x86_64-linux'.
+ canonical_target = f'{machine.cpu_family}-{machine.system}'
+ rel_path = os.path.join(self.targets_dir, canonical_target)
+ abs_path = os.path.join(self.cuda_path, rel_path)
+
+ # AArch64 may need the SBSA fallback.
+ if machine.cpu_family == 'aarch64' and not os.path.exists(abs_path):
+ rel_path = os.path.join(self.targets_dir, f"sbsa-{machine.system}")
+ abs_path = os.path.join(self.cuda_path, rel_path)
+ mlog.debug(
+ f'Canonical CUDA target "{self.targets_dir}/{canonical_target}" missing; '
+ f'falling back to "{rel_path}".'
+ )
+
+ mlog.debug(f'CUDA target resolved to "{rel_path}".')
+
+ if not os.path.exists(abs_path):
+ mlog.error(f'CUDA target "{rel_path}" does not exist.')
+
+ return rel_path
+
def _default_path_env_var(self) -> T.Optional[str]:
env_vars = ['CUDA_PATH'] if self._is_windows() else ['CUDA_PATH', 'CUDA_HOME', 'CUDA_ROOT']
env_vars = [var for var in env_vars if var in os.environ]
@@ -211,8 +251,8 @@ class CudaDependency(SystemDependency):
return '.'.join(version.split('.')[:2])
def _detect_arch_libdir(self) -> str:
- arch = detect_cpu_family(self.env.coredata.compilers.host)
machine = self.env.machines[self.for_machine]
+ arch = machine.cpu_family
msg = '{} architecture is not supported in {} version of the CUDA Toolkit.'
if machine.is_windows():
libdirs = {'x86': 'Win32', 'x86_64': 'x64'}
@@ -220,10 +260,7 @@ class CudaDependency(SystemDependency):
raise DependencyException(msg.format(arch, 'Windows'))
return os.path.join('lib', libdirs[arch])
elif machine.is_linux():
- libdirs = {'x86_64': 'lib64', 'ppc64': 'lib', 'aarch64': 'lib64', 'loongarch64': 'lib64'}
- if arch not in libdirs:
- raise DependencyException(msg.format(arch, 'Linux'))
- return libdirs[arch]
+ return 'lib'
elif machine.is_darwin():
libdirs = {'x86_64': 'lib64'}
if arch not in libdirs:
@@ -236,13 +273,14 @@ class CudaDependency(SystemDependency):
all_found = True
for module in self.requested_modules:
- args = self.clib_compiler.find_library(module, self.env, [self.libdir])
- if module == 'cudart_static' and self.language != 'cuda':
- machine = self.env.machines[self.for_machine]
- if machine.is_linux():
- # extracted by running
- # nvcc -v foo.o
- args += ['-lrt', '-lpthread', '-ldl']
+ # You should only ever link to libraries inside the cuda tree, nothing outside of it.
+ # For instance, there is a
+ #
+ # - libnvidia-ml.so in stubs/ of the CUDA tree
+ # - libnvidia-ml.so in /usr/lib/ that is provided by the nvidia drivers
+ #
+ # Users should never link to the latter, since its ABI may change.
+ args = self.clib_compiler.find_library(module, [self.libdir, os.path.join(self.libdir, 'stubs')], self.libtype, ignore_system_dirs=True)
if args is None:
self._report_dependency_error(f'Couldn\'t find requested CUDA module \'{module}\'')
@@ -276,31 +314,27 @@ class CudaDependency(SystemDependency):
def log_info(self) -> str:
return self.cuda_path if self.cuda_path else ''
- def get_requested(self, kwargs: T.Dict[str, T.Any]) -> T.List[str]:
- candidates = mesonlib.extract_as_list(kwargs, 'modules')
- for c in candidates:
- if not isinstance(c, str):
- raise DependencyException('CUDA module argument is not a string.')
- return candidates
-
def get_link_args(self, language: T.Optional[str] = None, raw: bool = False) -> T.List[str]:
+ # when using nvcc to link, we should instead use the native driver options
+ REWRITE_MODULES = {
+ 'cudart': ['-cudart', 'shared'],
+ 'cudart_static': ['-cudart', 'static'],
+ 'cudadevrt': ['-cudadevrt'],
+ }
+
args: T.List[str] = []
for lib in self.requested_modules:
link_args = self.lib_modules[lib]
- # Turn canonical arguments like
- # /opt/cuda/lib64/libcublas.so
- # back into
- # -lcublas
- # since this is how CUDA modules were passed to nvcc since time immemorial
- if language == 'cuda':
- if lib in frozenset(['cudart', 'cudart_static']):
- # nvcc always links these unconditionally
- mlog.debug(f'Not adding \'{lib}\' to dependency, since nvcc will link it implicitly')
- link_args = []
- elif link_args and link_args[0].startswith(self.libdir):
- # module included with CUDA, nvcc knows how to find these itself
- mlog.debug(f'CUDA module \'{lib}\' found in CUDA libdir')
- link_args = ['-l' + lib]
+ if language == 'cuda' and lib in REWRITE_MODULES:
+ link_args = REWRITE_MODULES[lib]
+ mlog.debug(f'Rewriting module \'{lib}\' to \'{link_args}\'')
+ elif lib == 'cudart_static':
+ machine = self.env.machines[self.for_machine]
+ if machine.is_linux():
+ # extracted by running
+ # nvcc -v foo.o
+ link_args += ['-lrt', '-lpthread', '-ldl']
+
args += link_args
return args
diff --git a/mesonbuild/dependencies/detect.py b/mesonbuild/dependencies/detect.py
index aa62c66..f00075b 100644
--- a/mesonbuild/dependencies/detect.py
+++ b/mesonbuild/dependencies/detect.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import collections, functools, importlib
+import enum
import typing as T
from .base import ExternalDependency, DependencyException, DependencyMethods, NotFoundDependency
@@ -14,8 +15,9 @@ from .. import mlog
if T.TYPE_CHECKING:
from ..environment import Environment
from .factory import DependencyFactory, WrappedFactoryFunc, DependencyGenerator
+ from .base import DependencyObjectKWs
- TV_DepIDEntry = T.Union[str, bool, int, T.Tuple[str, ...]]
+ TV_DepIDEntry = T.Union[str, bool, int, None, T.Tuple[str, ...]]
TV_DepID = T.Tuple[T.Tuple[str, TV_DepIDEntry], ...]
PackageTypes = T.Union[T.Type[ExternalDependency], DependencyFactory, WrappedFactoryFunc]
@@ -38,12 +40,15 @@ class DependencyPackages(collections.UserDict):
packages = DependencyPackages()
_packages_accept_language: T.Set[str] = set()
-def get_dep_identifier(name: str, kwargs: T.Dict[str, T.Any]) -> 'TV_DepID':
+def get_dep_identifier(name: str, kwargs: DependencyObjectKWs) -> 'TV_DepID':
identifier: 'TV_DepID' = (('name', name), )
- from ..interpreter import permitted_dependency_kwargs
- assert len(permitted_dependency_kwargs) == 19, \
+ from ..interpreter.type_checking import DEPENDENCY_KWS
+ nkwargs = T.cast('DependencyObjectKWs', {k.name: k.default for k in DEPENDENCY_KWS})
+ nkwargs.update(kwargs)
+
+ assert len(DEPENDENCY_KWS) == 20, \
'Extra kwargs have been added to dependency(), please review if it makes sense to handle it here'
- for key, value in kwargs.items():
+ for key, value in nkwargs.items():
# 'version' is irrelevant for caching; the caller must check version matches
# 'native' is handled above with `for_machine`
# 'required' is irrelevant for caching; the caller handles it separately
@@ -61,8 +66,11 @@ def get_dep_identifier(name: str, kwargs: T.Dict[str, T.Any]) -> 'TV_DepID':
for i in value:
assert isinstance(i, str), i
value = tuple(frozenset(listify(value)))
+ elif isinstance(value, enum.Enum):
+ value = value.value
+ assert isinstance(value, str), 'for mypy'
else:
- assert isinstance(value, (str, bool, int)), value
+ assert value is None or isinstance(value, (str, bool, int)), value
identifier = (*identifier, (key, value),)
return identifier
@@ -80,24 +88,17 @@ display_name_map = {
'wxwidgets': 'WxWidgets',
}
-def find_external_dependency(name: str, env: 'Environment', kwargs: T.Dict[str, object], candidates: T.Optional[T.List['DependencyGenerator']] = None) -> T.Union['ExternalDependency', NotFoundDependency]:
+def find_external_dependency(name: str, env: 'Environment', kwargs: DependencyObjectKWs, candidates: T.Optional[T.List['DependencyGenerator']] = None) -> T.Union['ExternalDependency', NotFoundDependency]:
assert name
required = kwargs.get('required', True)
- if not isinstance(required, bool):
- raise DependencyException('Keyword "required" must be a boolean.')
- if not isinstance(kwargs.get('method', ''), str):
- raise DependencyException('Keyword "method" must be a string.')
lname = name.lower()
- if lname not in _packages_accept_language and 'language' in kwargs:
+ if lname not in _packages_accept_language and kwargs.get('language') is not None:
raise DependencyException(f'{name} dependency does not accept "language" keyword argument')
- if not isinstance(kwargs.get('version', ''), (str, list)):
- raise DependencyException('Keyword "Version" must be string or list.')
# display the dependency name with correct casing
display_name = display_name_map.get(lname, lname)
- for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST
-
+ for_machine = kwargs.get('native', MachineChoice.HOST)
type_text = PerMachine('Build-time', 'Run-time')[for_machine] + ' dependency'
# build a list of dependency methods to try
@@ -125,7 +126,7 @@ def find_external_dependency(name: str, env: 'Environment', kwargs: T.Dict[str,
details = d.log_details()
if details:
details = '(' + details + ') '
- if 'language' in kwargs:
+ if kwargs.get('language') is not None:
details += 'for ' + d.language + ' '
# if the dependency was found
@@ -169,11 +170,7 @@ def find_external_dependency(name: str, env: 'Environment', kwargs: T.Dict[str,
def _build_external_dependency_list(name: str, env: 'Environment', for_machine: MachineChoice,
- kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']:
- # First check if the method is valid
- if 'method' in kwargs and kwargs['method'] not in [e.value for e in DependencyMethods]:
- raise DependencyException('method {!r} is invalid'.format(kwargs['method']))
-
+ kwargs: DependencyObjectKWs) -> T.List['DependencyGenerator']:
# Is there a specific dependency detector for this dependency?
lname = name.lower()
if lname in packages:
@@ -192,25 +189,26 @@ def _build_external_dependency_list(name: str, env: 'Environment', for_machine:
candidates: T.List['DependencyGenerator'] = []
- if kwargs.get('method', 'auto') == 'auto':
+ method = kwargs.get('method', DependencyMethods.AUTO)
+ if method is DependencyMethods.AUTO:
# Just use the standard detection methods.
- methods = ['pkg-config', 'extraframework', 'cmake']
+ methods = [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK, DependencyMethods.CMAKE]
else:
# If it's explicitly requested, use that detection method (only).
- methods = [kwargs['method']]
+ methods = [method]
# Exclusive to when it is explicitly requested
- if 'dub' in methods:
+ if DependencyMethods.DUB in methods:
from .dub import DubDependency
candidates.append(functools.partial(DubDependency, name, env, kwargs))
# Preferred first candidate for auto.
- if 'pkg-config' in methods:
+ if DependencyMethods.PKGCONFIG in methods:
from .pkgconfig import PkgConfigDependency
candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs))
# On OSX only, try framework dependency detector.
- if 'extraframework' in methods:
+ if DependencyMethods.EXTRAFRAMEWORK in methods:
if env.machines[for_machine].is_darwin():
from .framework import ExtraFrameworkDependency
candidates.append(functools.partial(ExtraFrameworkDependency, name, env, kwargs))
@@ -218,7 +216,7 @@ def _build_external_dependency_list(name: str, env: 'Environment', for_machine:
# Only use CMake:
# - if it's explicitly requested
# - as a last resort, since it might not work 100% (see #6113)
- if 'cmake' in methods:
+ if DependencyMethods.CMAKE in methods:
from .cmake import CMakeDependency
candidates.append(functools.partial(CMakeDependency, name, env, kwargs))
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 8f0f1ba..4d219a5 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -15,8 +15,8 @@ import functools
from mesonbuild.interpreterbase.decorators import FeatureDeprecated
from .. import mesonlib, mlog
-from ..environment import get_llvm_tool_names
-from ..mesonlib import version_compare, version_compare_many, search_version, stringlistify, extract_as_list
+from ..tooldetect import get_llvm_tool_names
+from ..mesonlib import version_compare, version_compare_many, search_version
from .base import DependencyException, DependencyMethods, detect_compiler, strip_system_includedirs, strip_system_libdirs, SystemDependency, ExternalDependency, DependencyTypeName
from .cmake import CMakeDependency
from .configtool import ConfigToolDependency
@@ -30,14 +30,8 @@ if T.TYPE_CHECKING:
from ..environment import Environment
from ..compilers import Compiler
from ..mesonlib import MachineChoice
- from typing_extensions import TypedDict
from ..interpreter.type_checking import PkgConfigDefineType
-
- class JNISystemDependencyKW(TypedDict):
- modules: T.List[str]
- # FIXME: When dependency() moves to typed Kwargs, this should inherit
- # from its TypedDict type.
- version: T.Optional[str]
+ from .base import DependencyObjectKWs
def get_shared_library_suffix(environment: 'Environment', for_machine: MachineChoice) -> str:
@@ -53,7 +47,7 @@ def get_shared_library_suffix(environment: 'Environment', for_machine: MachineCh
class GTestDependencySystem(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__(name, environment, kwargs, language='cpp')
self.main = kwargs.get('main', False)
@@ -65,8 +59,8 @@ class GTestDependencySystem(SystemDependency):
self.detect()
def detect(self) -> None:
- gtest_detect = self.clib_compiler.find_library("gtest", self.env, [])
- gtest_main_detect = self.clib_compiler.find_library("gtest_main", self.env, [])
+ gtest_detect = self.clib_compiler.find_library("gtest", [])
+ gtest_main_detect = self.clib_compiler.find_library("gtest_main", [])
if gtest_detect and (not self.main or gtest_main_detect):
self.is_found = True
self.compile_args = []
@@ -110,7 +104,7 @@ class GTestDependencySystem(SystemDependency):
class GTestDependencyPC(PkgConfigDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
assert name == 'gtest'
if kwargs.get('main'):
name = 'gtest_main'
@@ -118,7 +112,7 @@ class GTestDependencyPC(PkgConfigDependency):
class GMockDependencySystem(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__(name, environment, kwargs, language='cpp')
self.main = kwargs.get('main', False)
if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
@@ -141,8 +135,8 @@ class GMockDependencySystem(SystemDependency):
# GMock may be a library or just source.
# Work with both.
- gmock_detect = self.clib_compiler.find_library("gmock", self.env, [])
- gmock_main_detect = self.clib_compiler.find_library("gmock_main", self.env, [])
+ gmock_detect = self.clib_compiler.find_library("gmock", [])
+ gmock_main_detect = self.clib_compiler.find_library("gmock_main", [])
if gmock_detect and (not self.main or gmock_main_detect):
self.is_found = True
self.link_args += gmock_detect
@@ -178,7 +172,7 @@ class GMockDependencySystem(SystemDependency):
class GMockDependencyPC(PkgConfigDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
assert name == 'gmock'
if kwargs.get('main'):
name = 'gmock_main'
@@ -193,14 +187,14 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
tool_name = 'llvm-config'
__cpp_blacklist = {'-DNDEBUG'}
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
self.tools = get_llvm_tool_names('llvm-config')
# Fedora starting with Fedora 30 adds a suffix of the number
# of bits in the isa that llvm targets, for example, on x86_64
# and aarch64 the name will be llvm-config-64, on x86 and arm
# it will be llvm-config-32.
- if environment.machines[self.get_for_machine_from_kwargs(kwargs)].is_64_bit:
+ if environment.machines[kwargs.get('native', mesonlib.MachineChoice.HOST)].is_64_bit:
self.tools.append('llvm-config-64')
else:
self.tools.append('llvm-config-32')
@@ -215,9 +209,9 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
return
self.provided_modules = self.get_config_value(['--components'], 'modules')
- modules = stringlistify(extract_as_list(kwargs, 'modules'))
+ modules = kwargs.get('modules', [])
self.check_components(modules)
- opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
+ opt_modules = kwargs.get('optional_modules', [])
self.check_components(opt_modules, required=False)
cargs = mesonlib.OrderedSet(self.get_config_value(['--cppflags'], 'compile_args'))
@@ -388,15 +382,12 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
return ''
class LLVMDependencyCMake(CMakeDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
- self.llvm_modules = stringlistify(extract_as_list(kwargs, 'modules'))
- self.llvm_opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs) -> None:
+ self.llvm_modules = kwargs.get('modules', [])
+ self.llvm_opt_modules = kwargs.get('optional_modules', [])
- compilers = None
- if kwargs.get('native', False):
- compilers = env.coredata.compilers.build
- else:
- compilers = env.coredata.compilers.host
+ for_machine = kwargs.get('native', mesonlib.MachineChoice.HOST)
+ compilers = env.coredata.compilers[for_machine]
if not compilers or not {'c', 'cpp'}.issubset(compilers):
# Initialize basic variables
ExternalDependency.__init__(self, DependencyTypeName('cmake'), env, kwargs)
@@ -515,7 +506,7 @@ class ValgrindDependency(PkgConfigDependency):
Consumers of Valgrind usually only need the compile args and do not want to
link to its (static) libraries.
'''
- def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__('valgrind', env, kwargs)
def get_link_args(self, language: T.Optional[str] = None, raw: bool = False) -> T.List[str]:
@@ -526,7 +517,7 @@ packages['valgrind'] = ValgrindDependency
class ZlibSystemDependency(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
from ..compilers.c import AppleClangCCompiler
from ..compilers.cpp import AppleClangCPPCompiler
@@ -549,8 +540,8 @@ class ZlibSystemDependency(SystemDependency):
else:
libs = ['z']
for lib in libs:
- l = self.clib_compiler.find_library(lib, environment, [], self.libtype)
- h = self.clib_compiler.has_header('zlib.h', '', environment, dependencies=[self])
+ l = self.clib_compiler.find_library(lib, [], self.libtype)
+ h = self.clib_compiler.has_header('zlib.h', '', dependencies=[self])
if l and h[0]:
self.is_found = True
self.link_args = l
@@ -558,13 +549,13 @@ class ZlibSystemDependency(SystemDependency):
else:
return
- v, _ = self.clib_compiler.get_define('ZLIB_VERSION', '#include <zlib.h>', self.env, [], [self])
+ v, _ = self.clib_compiler.get_define('ZLIB_VERSION', '#include <zlib.h>', [], [self])
self.version = v.strip('"')
class JNISystemDependency(SystemDependency):
- def __init__(self, environment: 'Environment', kwargs: JNISystemDependencyKW):
- super().__init__('jni', environment, T.cast('T.Dict[str, T.Any]', kwargs))
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs):
+ super().__init__('jni', environment, kwargs)
self.feature_since = ('0.62.0', '')
@@ -575,7 +566,7 @@ class JNISystemDependency(SystemDependency):
self.javac = environment.coredata.compilers[self.for_machine]['java']
self.version = self.javac.version
- modules: T.List[str] = mesonlib.listify(kwargs.get('modules', []))
+ modules = kwargs.get('modules', [])
for module in modules:
if module not in {'jvm', 'awt'}:
msg = f'Unknown JNI module ({module})'
@@ -586,7 +577,7 @@ class JNISystemDependency(SystemDependency):
self.is_found = False
return
- if 'version' in kwargs and not version_compare_many(self.version, kwargs['version'])[0]:
+ if kwargs.get('version') and not version_compare_many(self.version, kwargs['version'])[0]:
mlog.error(f'Incorrect JDK version found ({self.version}), wanted {kwargs["version"]}')
self.is_found = False
return
@@ -632,14 +623,14 @@ class JNISystemDependency(SystemDependency):
java_home_lib_server = java_home_lib / 'server'
if 'jvm' in modules:
- jvm = self.clib_compiler.find_library('jvm', environment, extra_dirs=[str(java_home_lib_server)])
+ jvm = self.clib_compiler.find_library('jvm', extra_dirs=[str(java_home_lib_server)])
if jvm is None:
mlog.debug('jvm library not found.')
self.is_found = False
else:
self.link_args.extend(jvm)
if 'awt' in modules:
- jawt = self.clib_compiler.find_library('jawt', environment, extra_dirs=[str(java_home_lib)])
+ jawt = self.clib_compiler.find_library('jawt', extra_dirs=[str(java_home_lib)])
if jawt is None:
mlog.debug('jawt library not found.')
self.is_found = False
@@ -691,7 +682,7 @@ packages['jni'] = JNISystemDependency
class JDKSystemDependency(JNISystemDependency):
- def __init__(self, environment: 'Environment', kwargs: JNISystemDependencyKW):
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(environment, kwargs)
self.feature_since = ('0.59.0', '')
@@ -754,10 +745,10 @@ class DiaSDKSystemDependency(SystemDependency):
# Check if compiler has a built-in macro defined
@staticmethod
def _has_define(compiler: 'Compiler', dname: str, env: 'Environment') -> bool:
- defval, _ = compiler.get_define(dname, '', env, [], [])
+ defval, _ = compiler.get_define(dname, '', [], [])
return defval is not None
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__('diasdk', environment, kwargs)
self.is_found = False
diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py
index ac137e3..2166a95 100644
--- a/mesonbuild/dependencies/dub.py
+++ b/mesonbuild/dependencies/dub.py
@@ -19,6 +19,7 @@ if T.TYPE_CHECKING:
from typing_extensions import TypedDict
from ..environment import Environment
+ from .base import DependencyObjectKWs
# Definition of what `dub describe` returns (only the fields used by Meson)
class DubDescription(TypedDict):
@@ -74,7 +75,7 @@ class DubDependency(ExternalDependency):
'llvm': 'ldc',
}
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(DependencyTypeName('dub'), environment, kwargs, language='d')
self.name = name
from ..compilers.d import DCompiler, d_feature_args
@@ -83,8 +84,8 @@ class DubDependency(ExternalDependency):
assert isinstance(_temp_comp, DCompiler)
self.compiler = _temp_comp
- if 'required' in kwargs:
- self.required = kwargs.get('required')
+ if kwargs.get('required') is not None:
+ self.required = kwargs['required']
if DubDependency.class_dubbin is None and not DubDependency.class_dubbin_searched:
DubDependency.class_dubbin = self._check_dub()
diff --git a/mesonbuild/dependencies/factory.py b/mesonbuild/dependencies/factory.py
index aac09ca..0c4ca81 100644
--- a/mesonbuild/dependencies/factory.py
+++ b/mesonbuild/dependencies/factory.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2021 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
+# Copyright © 2021-2025 Intel Corporation
from __future__ import annotations
@@ -15,7 +15,7 @@ from .framework import ExtraFrameworkDependency
from .pkgconfig import PkgConfigDependency
if T.TYPE_CHECKING:
- from .base import ExternalDependency
+ from .base import DependencyObjectKWs, ExternalDependency
from .configtool import ConfigToolDependency
from ..environment import Environment
from ..mesonlib import MachineChoice
@@ -25,7 +25,7 @@ if T.TYPE_CHECKING:
[
'Environment',
MachineChoice,
- T.Dict[str, T.Any],
+ DependencyObjectKWs,
T.List[DependencyMethods]
],
T.List[DependencyGenerator]
@@ -35,7 +35,7 @@ if T.TYPE_CHECKING:
[
'Environment',
MachineChoice,
- T.Dict[str, T.Any]
+ DependencyObjectKWs,
],
T.List[DependencyGenerator]
]
@@ -68,7 +68,7 @@ class DependencyFactory:
"""
def __init__(self, name: str, methods: T.List[DependencyMethods], *,
- extra_kwargs: T.Optional[T.Dict[str, T.Any]] = None,
+ extra_kwargs: T.Optional[DependencyObjectKWs] = None,
pkgconfig_name: T.Optional[str] = None,
pkgconfig_class: 'T.Type[PkgConfigDependency]' = PkgConfigDependency,
cmake_name: T.Optional[str] = None,
@@ -86,7 +86,7 @@ class DependencyFactory:
self.methods = methods
self.classes: T.Dict[
DependencyMethods,
- T.Callable[['Environment', T.Dict[str, T.Any]], ExternalDependency]
+ T.Callable[['Environment', DependencyObjectKWs], ExternalDependency]
] = {
# Just attach the correct name right now, either the generic name
# or the method specific name.
@@ -116,7 +116,7 @@ class DependencyFactory:
return True
def __call__(self, env: 'Environment', for_machine: MachineChoice,
- kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']:
+ kwargs: DependencyObjectKWs) -> T.List['DependencyGenerator']:
"""Return a list of Dependencies with the arguments already attached."""
methods = process_method_kw(self.methods, kwargs)
nwargs = self.extra_kwargs.copy()
@@ -131,14 +131,14 @@ def factory_methods(methods: T.Set[DependencyMethods]) -> T.Callable[['FactoryFu
This helps to make factory functions self documenting
>>> @factory_methods([DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE])
- >>> def factory(env: Environment, for_machine: MachineChoice, kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
+ >>> def factory(env: Environment, for_machine: MachineChoice, kwargs: DependencyObjectKWs, methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
>>> pass
"""
def inner(func: 'FactoryFunc') -> 'WrappedFactoryFunc':
@functools.wraps(func)
- def wrapped(env: 'Environment', for_machine: MachineChoice, kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']:
+ def wrapped(env: 'Environment', for_machine: MachineChoice, kwargs: DependencyObjectKWs) -> T.List['DependencyGenerator']:
return func(env, for_machine, kwargs, process_method_kw(methods, kwargs))
return wrapped
diff --git a/mesonbuild/dependencies/framework.py b/mesonbuild/dependencies/framework.py
index 1fbd628..a23b4a6 100644
--- a/mesonbuild/dependencies/framework.py
+++ b/mesonbuild/dependencies/framework.py
@@ -11,11 +11,12 @@ import typing as T
if T.TYPE_CHECKING:
from ..environment import Environment
+ from .base import DependencyObjectKWs
class ExtraFrameworkDependency(ExternalDependency):
system_framework_paths: T.Optional[T.List[str]] = None
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None) -> None:
paths = stringlistify(kwargs.get('paths', []))
super().__init__(DependencyTypeName('extraframeworks'), env, kwargs, language=language)
self.name = name
@@ -25,7 +26,7 @@ class ExtraFrameworkDependency(ExternalDependency):
raise DependencyException('No C-like compilers are available')
if self.system_framework_paths is None:
try:
- self.system_framework_paths = self.clib_compiler.find_framework_paths(self.env)
+ self.system_framework_paths = self.clib_compiler.find_framework_paths()
except MesonException as e:
if 'non-clang' in str(e):
# Apple frameworks can only be found (and used) with the
@@ -55,7 +56,7 @@ class ExtraFrameworkDependency(ExternalDependency):
# Python.framework. We need to know for sure that the framework was
# found in the path we expect.
allow_system = p in self.system_framework_paths
- args = self.clib_compiler.find_framework(framework_name, self.env, [p], allow_system)
+ args = self.clib_compiler.find_framework(framework_name, [p], allow_system)
if args is None:
continue
self.link_args = args
diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py
index 7595c7c..5894cfb 100644
--- a/mesonbuild/dependencies/hdf5.py
+++ b/mesonbuild/dependencies/hdf5.py
@@ -9,7 +9,7 @@ import os
import re
from pathlib import Path
-from ..mesonlib import OrderedSet, join_args
+from ..mesonlib import OrderedSet, join_args, MachineChoice
from .base import DependencyException, DependencyMethods
from .configtool import ConfigToolDependency
from .detect import packages
@@ -20,14 +20,14 @@ import typing as T
if T.TYPE_CHECKING:
from .factory import DependencyGenerator
from ..environment import Environment
- from ..mesonlib import MachineChoice
+ from .base import DependencyObjectKWs
class HDF5PkgConfigDependency(PkgConfigDependency):
"""Handle brokenness in the HDF5 pkg-config files."""
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None) -> None:
language = language or 'c'
if language not in {'c', 'cpp', 'fortran'}:
raise DependencyException(f'Language {language} is not supported with HDF5.')
@@ -78,7 +78,7 @@ class HDF5ConfigToolDependency(ConfigToolDependency):
version_arg = '-showconfig'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None) -> None:
language = language or 'c'
if language not in {'c', 'cpp', 'fortran'}:
raise DependencyException(f'Language {language} is not supported with HDF5.')
@@ -99,7 +99,7 @@ class HDF5ConfigToolDependency(ConfigToolDependency):
raise DependencyException('How did you get here?')
# We need this before we call super()
- for_machine = self.get_for_machine_from_kwargs(kwargs)
+ for_machine = kwargs.get('native', MachineChoice.HOST)
nkwargs = kwargs.copy()
nkwargs['tools'] = tools
@@ -144,7 +144,7 @@ class HDF5ConfigToolDependency(ConfigToolDependency):
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL})
def hdf5_factory(env: 'Environment', for_machine: 'MachineChoice',
- kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
+ kwargs: DependencyObjectKWs, methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
language = kwargs.get('language')
candidates: T.List['DependencyGenerator'] = []
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 3ab2194..b1b8b8e 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -21,13 +21,14 @@ from ..options import OptionKey
if T.TYPE_CHECKING:
from ..environment import Environment
+ from .base import DependencyObjectKWs
from .factory import DependencyGenerator
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE})
def netcdf_factory(env: 'Environment',
for_machine: 'mesonlib.MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
language = kwargs.get('language')
if language is None:
@@ -54,42 +55,42 @@ packages['netcdf'] = netcdf_factory
class AtomicBuiltinDependency(BuiltinDependency):
- def __init__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: Environment, kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('1.7.0', "consider checking for `atomic_flag_clear` with and without `find_library('atomic')`")
- if self.clib_compiler.has_function('atomic_flag_clear', '#include <stdatomic.h>', env)[0]:
+ if self.clib_compiler.has_function('atomic_flag_clear', '#include <stdatomic.h>')[0]:
self.is_found = True
class AtomicSystemDependency(SystemDependency):
- def __init__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: Environment, kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('1.7.0', "consider checking for `atomic_flag_clear` with and without `find_library('atomic')`")
- h = self.clib_compiler.has_header('stdatomic.h', '', env)
- self.link_args = self.clib_compiler.find_library('atomic', env, [], self.libtype)
+ h = self.clib_compiler.has_header('stdatomic.h', '')
+ self.link_args = self.clib_compiler.find_library('atomic', [], self.libtype)
if h[0] and self.link_args:
self.is_found = True
class DlBuiltinDependency(BuiltinDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.62.0', "consider checking for `dlopen` with and without `find_library('dl')`")
- if self.clib_compiler.has_function('dlopen', '#include <dlfcn.h>', env)[0]:
+ if self.clib_compiler.has_function('dlopen', '#include <dlfcn.h>')[0]:
self.is_found = True
class DlSystemDependency(SystemDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.62.0', "consider checking for `dlopen` with and without `find_library('dl')`")
- h = self.clib_compiler.has_header('dlfcn.h', '', env)
- self.link_args = self.clib_compiler.find_library('dl', env, [], self.libtype)
+ h = self.clib_compiler.has_header('dlfcn.h', '')
+ self.link_args = self.clib_compiler.find_library('dl', [], self.libtype)
if h[0] and self.link_args:
self.is_found = True
@@ -98,6 +99,7 @@ class DlSystemDependency(SystemDependency):
class OpenMPDependency(SystemDependency):
# Map date of specification release (which is the macro value) to a version.
VERSIONS = {
+ '202411': '6.0',
'202111': '5.2',
'202011': '5.1',
'201811': '5.0',
@@ -111,7 +113,7 @@ class OpenMPDependency(SystemDependency):
'199810': '1.0',
}
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
language = kwargs.get('language')
super().__init__('openmp', environment, kwargs, language=language)
self.is_found = False
@@ -119,26 +121,26 @@ class OpenMPDependency(SystemDependency):
# No macro defined for OpenMP, but OpenMP 3.1 is supported.
self.version = '3.1'
self.is_found = True
- self.compile_args = self.link_args = self.clib_compiler.openmp_flags(environment)
+ self.compile_args = self.link_args = self.clib_compiler.openmp_flags()
return
if self.clib_compiler.get_id() == 'pgi':
# through at least PGI 19.4, there is no macro defined for OpenMP, but OpenMP 3.1 is supported.
self.version = '3.1'
self.is_found = True
- self.compile_args = self.link_args = self.clib_compiler.openmp_flags(environment)
+ self.compile_args = self.link_args = self.clib_compiler.openmp_flags()
return
# Set these now so they're available for the following compiler checks
try:
- self.compile_args.extend(self.clib_compiler.openmp_flags(environment))
- self.link_args.extend(self.clib_compiler.openmp_link_flags(environment))
+ self.compile_args.extend(self.clib_compiler.openmp_flags())
+ self.link_args.extend(self.clib_compiler.openmp_link_flags())
except mesonlib.MesonException as e:
mlog.warning('OpenMP support not available because:', str(e), fatal=False)
return
try:
openmp_date = self.clib_compiler.get_define(
- '_OPENMP', '', self.env, [], [self], disable_cache=True)[0]
+ '_OPENMP', '', [], [self], disable_cache=True)[0]
except mesonlib.EnvironmentException as e:
mlog.debug('OpenMP support not available in the compiler')
mlog.debug(e)
@@ -155,7 +157,7 @@ class OpenMPDependency(SystemDependency):
# Flang has omp_lib.h
header_names = ('omp.h', 'omp_lib.h')
for name in header_names:
- if self.clib_compiler.has_header(name, '', self.env, dependencies=[self], disable_cache=True)[0]:
+ if self.clib_compiler.has_header(name, '', dependencies=[self], disable_cache=True)[0]:
self.is_found = True
break
else:
@@ -165,7 +167,7 @@ packages['openmp'] = OpenMPDependency
class ThreadDependency(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__(name, environment, kwargs)
self.is_found = True
# Happens if you are using a language with threads
@@ -174,12 +176,12 @@ class ThreadDependency(SystemDependency):
self.compile_args = []
self.link_args = []
else:
- self.compile_args = self.clib_compiler.thread_flags(environment)
- self.link_args = self.clib_compiler.thread_link_flags(environment)
+ self.compile_args = self.clib_compiler.thread_flags()
+ self.link_args = self.clib_compiler.thread_link_flags()
class BlocksDependency(SystemDependency):
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__('blocks', environment, kwargs)
self.name = 'blocks'
self.is_found = False
@@ -191,8 +193,8 @@ class BlocksDependency(SystemDependency):
self.compile_args = ['-fblocks']
self.link_args = ['-lBlocksRuntime']
- if not self.clib_compiler.has_header('Block.h', '', environment, disable_cache=True) or \
- not self.clib_compiler.find_library('BlocksRuntime', environment, []):
+ if not self.clib_compiler.has_header('Block.h', '', disable_cache=True) or \
+ not self.clib_compiler.find_library('BlocksRuntime', []):
mlog.log(mlog.red('ERROR:'), 'BlocksRuntime not found.')
return
@@ -222,7 +224,7 @@ class PcapDependencyConfigTool(ConfigToolDependency):
# version 1.10.3 will hopefully add actual support for --version
skip_version = '--help'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -240,7 +242,7 @@ class PcapDependencyConfigTool(ConfigToolDependency):
return None
v = self.clib_compiler.get_return_value('pcap_lib_version', 'string',
- '#include <pcap.h>', self.env, [], [self])
+ '#include <pcap.h>', [], [self])
v = re.sub(r'libpcap version ', '', str(v))
v = re.sub(r' -- Apple version.*$', '', v)
return v
@@ -251,7 +253,7 @@ class CupsDependencyConfigTool(ConfigToolDependency):
tools = ['cups-config']
tool_name = 'cups-config'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -264,7 +266,7 @@ class LibWmfDependencyConfigTool(ConfigToolDependency):
tools = ['libwmf-config']
tool_name = 'libwmf-config'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -277,7 +279,7 @@ class LibGCryptDependencyConfigTool(ConfigToolDependency):
tools = ['libgcrypt-config']
tool_name = 'libgcrypt-config'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -291,7 +293,7 @@ class GpgmeDependencyConfigTool(ConfigToolDependency):
tools = ['gpgme-config']
tool_name = 'gpg-config'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -302,7 +304,7 @@ class GpgmeDependencyConfigTool(ConfigToolDependency):
class ShadercDependency(SystemDependency):
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__('shaderc', environment, kwargs)
static_lib = 'shaderc_combined'
@@ -315,7 +317,7 @@ class ShadercDependency(SystemDependency):
cc = self.get_compiler()
for lib in libs:
- self.link_args = cc.find_library(lib, environment, [])
+ self.link_args = cc.find_library(lib, [])
if self.link_args is not None:
self.is_found = True
@@ -334,7 +336,7 @@ class CursesConfigToolDependency(ConfigToolDependency):
# ncurses5.4-config is for macOS Catalina
tools = ['ncursesw6-config', 'ncursesw5-config', 'ncurses6-config', 'ncurses5-config', 'ncurses5.4-config']
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None):
exclude_paths = None
# macOS mistakenly ships /usr/bin/ncurses5.4-config and a man page for
# it, but none of the headers or libraries. Ignore /usr/bin because it
@@ -358,7 +360,7 @@ class CursesSystemDependency(SystemDependency):
implementations, and the differences between them can be very annoying.
"""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
candidates = [
@@ -370,10 +372,10 @@ class CursesSystemDependency(SystemDependency):
# Not sure how else to elegantly break out of both loops
for lib, headers in candidates:
- l = self.clib_compiler.find_library(lib, env, [])
+ l = self.clib_compiler.find_library(lib, [])
if l:
for header in headers:
- h = self.clib_compiler.has_header(header, '', env)
+ h = self.clib_compiler.has_header(header, '')
if h[0]:
self.is_found = True
self.link_args = l
@@ -381,15 +383,15 @@ class CursesSystemDependency(SystemDependency):
# implementations. The one in illumos/OpenIndiana
# doesn't seem to have a version defined in the header.
if lib.startswith('ncurses'):
- v, _ = self.clib_compiler.get_define('NCURSES_VERSION', f'#include <{header}>', env, [], [self])
+ v, _ = self.clib_compiler.get_define('NCURSES_VERSION', f'#include <{header}>', [], [self])
self.version = v.strip('"')
if lib.startswith('pdcurses'):
- v_major, _ = self.clib_compiler.get_define('PDC_VER_MAJOR', f'#include <{header}>', env, [], [self])
- v_minor, _ = self.clib_compiler.get_define('PDC_VER_MINOR', f'#include <{header}>', env, [], [self])
+ v_major, _ = self.clib_compiler.get_define('PDC_VER_MAJOR', f'#include <{header}>', [], [self])
+ v_minor, _ = self.clib_compiler.get_define('PDC_VER_MINOR', f'#include <{header}>', [], [self])
self.version = f'{v_major}.{v_minor}'
# Check the version if possible, emit a warning if we can't
- req = kwargs.get('version')
+ req = kwargs.get('version', [])
if req:
if self.version:
self.is_found, *_ = mesonlib.version_compare_many(self.version, req)
@@ -405,44 +407,44 @@ class CursesSystemDependency(SystemDependency):
class IconvBuiltinDependency(BuiltinDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.60.0', "consider checking for `iconv_open` with and without `find_library('iconv')`")
code = '''#include <iconv.h>\n\nint main() {\n iconv_open("","");\n}''' # [ignore encoding] this is C, not python, Mr. Lint
- if self.clib_compiler.links(code, env)[0]:
+ if self.clib_compiler.links(code)[0]:
self.is_found = True
class IconvSystemDependency(SystemDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.60.0', "consider checking for `iconv_open` with and without find_library('iconv')")
- h = self.clib_compiler.has_header('iconv.h', '', env)
- self.link_args = self.clib_compiler.find_library('iconv', env, [], self.libtype)
+ h = self.clib_compiler.has_header('iconv.h', '')
+ self.link_args = self.clib_compiler.find_library('iconv', [], self.libtype)
if h[0] and self.link_args:
self.is_found = True
class IntlBuiltinDependency(BuiltinDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.59.0', "consider checking for `ngettext` with and without `find_library('intl')`")
code = '''#include <libintl.h>\n\nint main() {\n gettext("Hello world");\n}'''
- if self.clib_compiler.links(code, env)[0]:
+ if self.clib_compiler.links(code)[0]:
self.is_found = True
class IntlSystemDependency(SystemDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
self.feature_since = ('0.59.0', "consider checking for `ngettext` with and without `find_library('intl')`")
- h = self.clib_compiler.has_header('libintl.h', '', env)
- self.link_args = self.clib_compiler.find_library('intl', env, [], self.libtype)
+ h = self.clib_compiler.has_header('libintl.h', '')
+ self.link_args = self.clib_compiler.find_library('intl', [], self.libtype)
if h[0] and self.link_args:
self.is_found = True
@@ -453,21 +455,21 @@ class IntlSystemDependency(SystemDependency):
class OpensslSystemDependency(SystemDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
- dependency_kwargs = {
- 'method': 'system',
+ dependency_kwargs: DependencyObjectKWs = {
+ 'method': DependencyMethods.SYSTEM,
'static': self.static,
}
- if not self.clib_compiler.has_header('openssl/ssl.h', '', env)[0]:
+ if not self.clib_compiler.has_header('openssl/ssl.h', '')[0]:
return
# openssl >= 3 only
- self.version = self.clib_compiler.get_define('OPENSSL_VERSION_STR', '#include <openssl/opensslv.h>', env, [], [self])[0]
+ self.version = self.clib_compiler.get_define('OPENSSL_VERSION_STR', '#include <openssl/opensslv.h>', [], [self])[0]
# openssl < 3 only
if not self.version:
- version_hex = self.clib_compiler.get_define('OPENSSL_VERSION_NUMBER', '#include <openssl/opensslv.h>', env, [], [self])[0]
+ version_hex = self.clib_compiler.get_define('OPENSSL_VERSION_NUMBER', '#include <openssl/opensslv.h>', [], [self])[0]
if not version_hex:
return
version_hex = version_hex.rstrip('L')
@@ -481,7 +483,7 @@ class OpensslSystemDependency(SystemDependency):
self.is_found = True
return
else:
- self.link_args = self.clib_compiler.find_library(name.lstrip('lib'), env, [], self.libtype)
+ self.link_args = self.clib_compiler.find_library(name.lstrip('lib'), [], self.libtype)
if not self.link_args:
return
@@ -492,11 +494,11 @@ class OpensslSystemDependency(SystemDependency):
if self._add_sub_dependency(libcrypto_factory(env, self.for_machine, dependency_kwargs)):
self.is_found = True
elif name == 'libcrypto':
- use_threads = self.clib_compiler.has_header_symbol('openssl/opensslconf.h', 'OPENSSL_THREADS', '', env, dependencies=[self])[0]
+ use_threads = self.clib_compiler.has_header_symbol('openssl/opensslconf.h', 'OPENSSL_THREADS', '', dependencies=[self])[0]
if not use_threads or self._add_sub_dependency(threads_factory(env, self.for_machine, {})):
self.is_found = True
# only relevant on platforms where it is distributed with the libc, in which case it always succeeds
- sublib = self.clib_compiler.find_library('dl', env, [], self.libtype)
+ sublib = self.clib_compiler.find_library('dl', [], self.libtype)
if sublib:
self.link_args.extend(sublib)
@@ -506,7 +508,7 @@ class ObjFWDependency(ConfigToolDependency):
tools = ['objfw-config']
tool_name = 'objfw-config'
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__('objfw', environment, kwargs)
self.feature_since = ('1.5.0', '')
if not self.is_found:
@@ -516,7 +518,7 @@ class ObjFWDependency(ConfigToolDependency):
# TODO: Expose --framework-libs
extra_flags = []
- for module in mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules')):
+ for module in kwargs.get('modules', []):
extra_flags.append('--package')
extra_flags.append(module)
@@ -528,7 +530,7 @@ class ObjFWDependency(ConfigToolDependency):
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.SYSTEM})
def curses_factory(env: 'Environment',
for_machine: 'mesonlib.MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
candidates: T.List['DependencyGenerator'] = []
@@ -554,7 +556,7 @@ packages['curses'] = curses_factory
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM})
def shaderc_factory(env: 'Environment',
for_machine: 'mesonlib.MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
"""Custom DependencyFactory for ShaderC.
@@ -572,7 +574,10 @@ def shaderc_factory(env: 'Environment',
shared_libs = ['shaderc']
static_libs = ['shaderc_combined', 'shaderc_static']
- if kwargs.get('static', env.coredata.optstore.get_value_for(OptionKey('prefer_static'))):
+ static = kwargs.get('static')
+ if static is None:
+ static = T.cast('bool', env.coredata.optstore.get_value_for(OptionKey('prefer_static')))
+ if static:
c = [functools.partial(PkgConfigDependency, name, env, kwargs)
for name in static_libs + shared_libs]
else:
diff --git a/mesonbuild/dependencies/mpi.py b/mesonbuild/dependencies/mpi.py
index a259972..1eae1a4 100644
--- a/mesonbuild/dependencies/mpi.py
+++ b/mesonbuild/dependencies/mpi.py
@@ -8,7 +8,7 @@ import typing as T
import os
import re
-from ..environment import detect_cpu_family
+from ..envconfig import detect_cpu_family
from ..mesonlib import Popen_safe
from .base import DependencyException, DependencyMethods, detect_compiler, SystemDependency
from .configtool import ConfigToolDependency
@@ -20,12 +20,13 @@ if T.TYPE_CHECKING:
from .factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
+ from .base import DependencyObjectKWs
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.SYSTEM})
def mpi_factory(env: 'Environment',
for_machine: 'MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
language = kwargs.get('language')
if language is None:
@@ -104,7 +105,7 @@ packages['mpi'] = mpi_factory
class MPIConfigToolDependency(ConfigToolDependency):
"""Wrapper around mpicc, Intel's mpiicc and friends."""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
if not self.is_found:
@@ -214,7 +215,7 @@ class MSMPIDependency(SystemDependency):
"""The Microsoft MPI."""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
# MSMPI only supports the C API
diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py
index 94e0893..b628e00 100644
--- a/mesonbuild/dependencies/pkgconfig.py
+++ b/mesonbuild/dependencies/pkgconfig.py
@@ -25,12 +25,14 @@ if T.TYPE_CHECKING:
from ..environment import Environment
from ..utils.core import EnvironOrDict
from ..interpreter.type_checking import PkgConfigDefineType
+ from .base import DependencyObjectKWs
class PkgConfigInterface:
'''Base class wrapping a pkg-config implementation'''
- class_impl: PerMachine[T.Union[Literal[False], T.Optional[PkgConfigInterface]]] = PerMachine(False, False)
- class_cli_impl: PerMachine[T.Union[Literal[False], T.Optional[PkgConfigCLI]]] = PerMachine(False, False)
+ # keyed on machine and extra_paths
+ class_impl: PerMachine[T.Dict[T.Optional[T.Tuple[str, ...]], T.Union[Literal[False], T.Optional[PkgConfigInterface]]]] = PerMachine({}, {})
+ class_cli_impl: PerMachine[T.Dict[T.Optional[T.Tuple[str, ...]], T.Union[Literal[False], T.Optional[PkgConfigCLI]]]] = PerMachine({}, {})
pkg_bin_per_machine: PerMachine[T.Optional[ExternalProgram]] = PerMachine(None, None)
@staticmethod
@@ -41,21 +43,25 @@ class PkgConfigInterface:
PkgConfigInterface.pkg_bin_per_machine[for_machine] = pkg_bin
@staticmethod
- def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Optional[PkgConfigInterface]:
+ def instance(env: Environment, for_machine: MachineChoice, silent: bool,
+ extra_paths: T.Optional[T.List[str]] = None) -> T.Optional[PkgConfigInterface]:
'''Return a pkg-config implementation singleton'''
for_machine = for_machine if env.is_cross_build() else MachineChoice.HOST
- impl = PkgConfigInterface.class_impl[for_machine]
+ extra_paths_key = tuple(extra_paths) if extra_paths is not None else None
+ impl = PkgConfigInterface.class_impl[for_machine].get(extra_paths_key, False)
if impl is False:
- impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine])
+ impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine], extra_paths)
if not impl.found():
impl = None
if not impl and not silent:
mlog.log('Found pkg-config:', mlog.red('NO'))
- PkgConfigInterface.class_impl[for_machine] = impl
+ PkgConfigInterface.class_impl[for_machine][extra_paths_key] = impl
return impl
@staticmethod
- def _cli(env: Environment, for_machine: MachineChoice, silent: bool = False) -> T.Optional[PkgConfigCLI]:
+ def _cli(env: Environment, for_machine: MachineChoice,
+ extra_paths: T.Optional[T.List[str]] = None,
+ silent: bool = False) -> T.Optional[PkgConfigCLI]:
'''Return the CLI pkg-config implementation singleton
Even when we use another implementation internally, external tools might
still need the CLI implementation.
@@ -64,17 +70,19 @@ class PkgConfigInterface:
impl: T.Union[Literal[False], T.Optional[PkgConfigInterface]] # Help confused mypy
impl = PkgConfigInterface.instance(env, for_machine, silent)
if impl and not isinstance(impl, PkgConfigCLI):
- impl = PkgConfigInterface.class_cli_impl[for_machine]
+ extra_paths_key = tuple(extra_paths) if extra_paths is not None else None
+ impl = PkgConfigInterface.class_cli_impl[for_machine].get(extra_paths_key, False)
if impl is False:
- impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine])
+ impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine], extra_paths)
if not impl.found():
impl = None
- PkgConfigInterface.class_cli_impl[for_machine] = impl
+ PkgConfigInterface.class_cli_impl[for_machine][extra_paths_key] = impl
return T.cast('T.Optional[PkgConfigCLI]', impl) # Trust me, mypy
@staticmethod
- def get_env(env: Environment, for_machine: MachineChoice, uninstalled: bool = False) -> EnvironmentVariables:
- cli = PkgConfigInterface._cli(env, for_machine)
+ def get_env(env: Environment, for_machine: MachineChoice, uninstalled: bool = False,
+ extra_paths: T.Optional[T.List[str]] = None) -> EnvironmentVariables:
+ cli = PkgConfigInterface._cli(env, for_machine, extra_paths)
return cli._get_env(uninstalled) if cli else EnvironmentVariables()
@staticmethod
@@ -123,11 +131,13 @@ class PkgConfigCLI(PkgConfigInterface):
'''pkg-config CLI implementation'''
def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool,
- pkgbin: T.Optional[ExternalProgram] = None) -> None:
+ pkgbin: T.Optional[ExternalProgram] = None,
+ extra_paths: T.Optional[T.List[str]] = None) -> None:
super().__init__(env, for_machine)
self._detect_pkgbin(pkgbin)
if self.pkgbin and not silent:
mlog.log('Found pkg-config:', mlog.green('YES'), mlog.bold(f'({self.pkgbin.get_path()})'), mlog.blue(self.pkgbin_version))
+ self.extra_paths = extra_paths or []
def found(self) -> bool:
return bool(self.pkgbin)
@@ -258,7 +268,7 @@ class PkgConfigCLI(PkgConfigInterface):
key = OptionKey('pkg_config_path', machine=self.for_machine)
pathlist = self.env.coredata.optstore.get_value_for(key)
assert isinstance(pathlist, list)
- extra_paths: T.List[str] = pathlist[:]
+ extra_paths: T.List[str] = pathlist + self.extra_paths
if uninstalled:
bpath = self.env.get_build_dir()
if bpath is not None:
@@ -296,12 +306,14 @@ class PkgConfigCLI(PkgConfigInterface):
class PkgConfigDependency(ExternalDependency):
- def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any],
- language: T.Optional[str] = None) -> None:
+ def __init__(self, name: str, environment: Environment, kwargs: DependencyObjectKWs,
+ language: T.Optional[str] = None,
+ extra_paths: T.Optional[T.List[str]] = None) -> None:
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
self.name = name
self.is_libtool = False
- pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent)
+ self.extra_paths = extra_paths or []
+ pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent, self.extra_paths)
if not pkgconfig:
msg = f'Pkg-config for machine {self.for_machine} not found. Giving up.'
if self.required:
@@ -421,7 +433,7 @@ class PkgConfigDependency(ExternalDependency):
#
# Only prefix_libpaths are reordered here because there should not be
# too many system_libpaths to cause library version issues.
- pkg_config_path: T.List[str] = self.env.coredata.optstore.get_value(OptionKey('pkg_config_path', machine=self.for_machine)) # type: ignore[assignment]
+ pkg_config_path: T.List[str] = self.env.coredata.optstore.get_value_for(OptionKey('pkg_config_path', machine=self.for_machine)) # type: ignore[assignment]
pkg_config_path = self._convert_mingw_paths(pkg_config_path)
prefix_libpaths = OrderedSet(sort_libpaths(list(prefix_libpaths), pkg_config_path))
system_libpaths: OrderedSet[str] = OrderedSet()
@@ -476,9 +488,8 @@ class PkgConfigDependency(ExternalDependency):
if lib in libs_found:
continue
if self.clib_compiler:
- args = self.clib_compiler.find_library(lib[2:], self.env,
- libpaths, self.libtype,
- lib_prefix_warning=False)
+ args = self.clib_compiler.find_library(
+ lib[2:], libpaths, self.libtype, lib_prefix_warning=False)
# If the project only uses a non-clib language such as D, Rust,
# C#, Python, etc, all we can do is limp along by adding the
# arguments as-is and then adding the libpaths at the end.
diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py
index 335faed..49ec980 100644
--- a/mesonbuild/dependencies/platform.py
+++ b/mesonbuild/dependencies/platform.py
@@ -12,13 +12,12 @@ import typing as T
if T.TYPE_CHECKING:
from ..environment import Environment
+ from .base import DependencyObjectKWs
class AppleFrameworks(ExternalDependency):
- def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, env: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__(DependencyTypeName('appleframeworks'), env, kwargs)
modules = kwargs.get('modules', [])
- if isinstance(modules, str):
- modules = [modules]
if not modules:
raise DependencyException("AppleFrameworks dependency requires at least one module.")
self.frameworks = modules
@@ -27,7 +26,7 @@ class AppleFrameworks(ExternalDependency):
self.is_found = True
for f in self.frameworks:
try:
- args = self.clib_compiler.find_framework(f, env, [])
+ args = self.clib_compiler.find_framework(f, [])
except MesonException as e:
if 'non-clang' in str(e):
self.is_found = False
diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py
index 3dab31c..aa2e22c 100644
--- a/mesonbuild/dependencies/python.py
+++ b/mesonbuild/dependencies/python.py
@@ -1,29 +1,30 @@
-# SPDX-License-Identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.
# Copyright 2022 The Meson development team
from __future__ import annotations
-import functools, json, os, textwrap
+import functools, json, operator, os, textwrap
from pathlib import Path
import typing as T
from .. import mesonlib, mlog
-from .base import process_method_kw, DependencyException, DependencyMethods, DependencyTypeName, ExternalDependency, SystemDependency
+from .base import process_method_kw, DependencyException, DependencyMethods, ExternalDependency, SystemDependency
from .configtool import ConfigToolDependency
from .detect import packages
from .factory import DependencyFactory
from .framework import ExtraFrameworkDependency
from .pkgconfig import PkgConfigDependency
-from ..environment import detect_cpu_family
+from ..envconfig import detect_cpu_family
from ..programs import ExternalProgram
from ..options import OptionKey
if T.TYPE_CHECKING:
- from typing_extensions import TypedDict
+ from typing_extensions import Final, TypedDict
from .factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
+ from .base import DependencyObjectKWs
class PythonIntrospectionDict(TypedDict):
@@ -56,7 +57,7 @@ class Pybind11ConfigToolDependency(ConfigToolDependency):
# in the meantime
skip_version = '--pkgconfigdir'
- def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: Environment, kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -67,16 +68,101 @@ class NumPyConfigToolDependency(ConfigToolDependency):
tools = ['numpy-config']
- def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: Environment, kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
self.compile_args = self.get_config_value(['--cflags'], 'compile_args')
+class PythonBuildConfig:
+ """PEP 739 build-details.json config file."""
+
+ IMPLEMENTED_VERSION: Final[str] = '1.0'
+ """Schema version currently implemented."""
+ _PATH_KEYS = (
+ 'base_interpreter',
+ 'libpython.dynamic',
+ 'libpython.dynamic_stableabi',
+ 'libpython.static',
+ 'c_api.headers',
+ 'c_api.pkgconfig_path',
+ )
+ """Path keys — may be relative, need to be expanded."""
+
+ def __init__(self, path: str) -> None:
+ self._path = Path(path)
+
+ try:
+ self._data = json.loads(self._path.read_text(encoding='utf8'))
+ except OSError as e:
+ raise DependencyException(f'Failed to read python.build_config: {e}') from e
+
+ self._validate_data()
+ self._expand_paths()
+
+ def __getitem__(self, key: str) -> T.Any:
+ return functools.reduce(operator.getitem, key.split('.'), self._data)
+
+ def __contains__(self, key: str) -> bool:
+ try:
+ self[key]
+ except KeyError:
+ return False
+ else:
+ return True
+
+ def get(self, key: str, default: T.Any = None) -> T.Any:
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def _validate_data(self) -> None:
+ schema_version = self._data['schema_version']
+ if mesonlib.version_compare(schema_version, '< 1.0'):
+ raise DependencyException(f'Invalid schema_version in python.build_config: {schema_version}')
+ if mesonlib.version_compare(schema_version, '>= 2.0'):
+ raise DependencyException(
+ f'Unsupported schema_version {schema_version!r} in python.build_config, '
+ f'but we only implement support for {self.IMPLEMENTED_VERSION!r}'
+ )
+ # Schema version that we currently understand
+ if mesonlib.version_compare(schema_version, f'> {self.IMPLEMENTED_VERSION}'):
+ mlog.log(
+ f'python.build_config has schema_version {schema_version!r}, '
+ f'but we only implement support for {self.IMPLEMENTED_VERSION!r}, '
+ 'new functionality might be missing'
+ )
+
+ def _expand_paths(self) -> None:
+ """Expand relative path (they're relative to base_prefix)."""
+ for key in self._PATH_KEYS:
+ if key not in self:
+ continue
+ parent, _, child = key.rpartition('.')
+ container = self[parent] if parent else self._data
+ path = Path(container[child])
+ if not path.is_absolute():
+ container[child] = os.fspath(self.base_prefix / path)
+
+ @property
+ def config_path(self) -> Path:
+ return self._path
+
+ @mesonlib.lazy_property
+ def base_prefix(self) -> Path:
+ path = Path(self._data['base_prefix'])
+ if path.is_absolute():
+ return path
+ # Non-absolute paths are relative to the build config directory
+ return self.config_path.parent / path
+
+
class BasicPythonExternalProgram(ExternalProgram):
def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
- ext_prog: T.Optional[ExternalProgram] = None):
+ ext_prog: T.Optional[ExternalProgram] = None,
+ build_config_path: T.Optional[str] = None):
if ext_prog is None:
super().__init__(name, command=command, silent=True)
else:
@@ -86,6 +172,8 @@ class BasicPythonExternalProgram(ExternalProgram):
self.cached_version = None
self.version_arg = '--version'
+ self.build_config = PythonBuildConfig(build_config_path) if build_config_path else None
+
# We want strong key values, so we always populate this with bogus data.
# Otherwise to make the type checkers happy we'd have to do .get() for
# everycall, even though we know that the introspection data will be
@@ -106,6 +194,15 @@ class BasicPythonExternalProgram(ExternalProgram):
}
self.pure: bool = True
+ @property
+ def version(self) -> str:
+ if self.build_config:
+ value = self.build_config['language']['version']
+ else:
+ value = self.info['variables'].get('LDVERSION') or self.info['version']
+ assert isinstance(value, str)
+ return value
+
def _check_version(self, version: str) -> bool:
if self.name == 'python2':
return mesonlib.version_compare(version, '< 3.0')
@@ -116,6 +213,14 @@ class BasicPythonExternalProgram(ExternalProgram):
def sanity(self) -> bool:
# Sanity check, we expect to have something that at least quacks in tune
+ if self.build_config:
+ if not self.build_config['libpython']:
+ mlog.debug('This Python installation does not provide a libpython')
+ return False
+ if not self.build_config['c_api']:
+ mlog.debug('This Python installation does support the C API')
+ return False
+
import importlib.resources
with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
@@ -143,14 +248,32 @@ class BasicPythonExternalProgram(ExternalProgram):
class _PythonDependencyBase(_Base):
- def __init__(self, python_holder: 'BasicPythonExternalProgram', embed: bool):
+ def __init__(self, python_holder: 'BasicPythonExternalProgram', embed: bool,
+ for_machine: 'MachineChoice'):
+ self.for_machine = for_machine
self.embed = embed
- self.version: str = python_holder.info['version']
- self.platform = python_holder.info['platform']
- self.variables = python_holder.info['variables']
+ self.build_config = python_holder.build_config
+
+ if self.build_config:
+ self.version = self.build_config['language']['version']
+ self.platform = self.build_config['platform']
+ self.is_freethreaded = 't' in self.build_config['abi']['flags']
+ self.link_libpython = self.build_config['libpython']['link_extensions']
+ # TODO: figure out how to deal with frameworks
+ # see the logic at the bottom of PythonPkgConfigDependency.__init__()
+ if self.env.machines.host.is_darwin():
+ raise DependencyException('--python.build-config is not supported on Darwin')
+ else:
+ self.version = python_holder.info['version']
+ self.platform = python_holder.info['platform']
+ self.is_freethreaded = python_holder.info['is_freethreaded']
+ self.link_libpython = python_holder.info['link_libpython']
+ # This data shouldn't be needed when build_config is set
+ self.is_pypy = python_holder.info['is_pypy']
+ self.variables = python_holder.info['variables']
+
self.paths = python_holder.info['paths']
- self.is_pypy = python_holder.info['is_pypy']
- self.is_freethreaded = python_holder.info['is_freethreaded']
+
# The "-embed" version of python.pc / python-config was introduced in 3.8,
# and distutils extension linking was changed to be considered a non embed
# usage. Before then, this dependency always uses the embed=True handling
@@ -159,7 +282,9 @@ class _PythonDependencyBase(_Base):
# On macOS and some Linux distros (Debian) distutils doesn't link extensions
# against libpython, even on 3.7 and below. We call into distutils and
# mirror its behavior. See https://github.com/mesonbuild/meson/issues/4117
- self.link_libpython = python_holder.info['link_libpython'] or embed
+ if not self.link_libpython:
+ self.link_libpython = embed
+
self.info: T.Optional[T.Dict[str, str]] = None
if mesonlib.version_compare(self.version, '>= 3.0'):
self.major_version = 3
@@ -173,6 +298,18 @@ class _PythonDependencyBase(_Base):
self.compile_args += ['-DPy_GIL_DISABLED']
def find_libpy(self, environment: 'Environment') -> None:
+ if self.build_config:
+ path = self.build_config['libpython'].get('dynamic')
+ if not path:
+ raise DependencyException('Python does not provide a dynamic libpython library')
+ sysroot = environment.properties[self.for_machine].get_sys_root() or ''
+ path = sysroot + path
+ if not os.path.isfile(path):
+ raise DependencyException('Python dynamic library does not exist or is not a file')
+ self.link_args = [path]
+ self.is_found = True
+ return
+
if self.is_pypy:
if self.major_version == 3:
libname = 'pypy3-c'
@@ -188,7 +325,7 @@ class _PythonDependencyBase(_Base):
libname += self.variables['ABIFLAGS']
libdirs = []
- largs = self.clib_compiler.find_library(libname, environment, libdirs)
+ largs = self.clib_compiler.find_library(libname, libdirs)
if largs is not None:
self.link_args = largs
self.is_found = True
@@ -211,7 +348,17 @@ class _PythonDependencyBase(_Base):
return 'aarch64'
raise DependencyException('Unknown Windows Python platform {self.platform!r}')
- def get_windows_link_args(self, limited_api: bool) -> T.Optional[T.List[str]]:
+ def get_windows_link_args(self, limited_api: bool, environment: 'Environment') -> T.Optional[T.List[str]]:
+ if self.build_config:
+ if self.static:
+ key = 'static'
+ elif limited_api:
+ key = 'dynamic-stableabi'
+ else:
+ key = 'dynamic'
+ sysroot = environment.properties[self.for_machine].get_sys_root() or ''
+ return [sysroot + self.build_config['libpython'][key]]
+
if self.platform.startswith('win'):
vernum = self.variables.get('py_version_nodot')
verdot = self.variables.get('py_version_short')
@@ -251,7 +398,7 @@ class _PythonDependencyBase(_Base):
is_debug_build = debug or buildtype == 'debug'
vscrt_debug = False
if OptionKey('b_vscrt') in self.env.coredata.optstore:
- vscrt = self.env.coredata.optstore.get_value('b_vscrt')
+ vscrt = self.env.coredata.optstore.get_value_for('b_vscrt')
if vscrt in {'mdd', 'mtd', 'from_buildtype', 'static_from_buildtype'}:
vscrt_debug = True
if is_debug_build and vscrt_debug and not self.variables.get('Py_DEBUG'):
@@ -300,28 +447,49 @@ class _PythonDependencyBase(_Base):
self.is_found = False
return
# This can fail if the library is not found
- largs = self.get_windows_link_args(limited_api)
+ largs = self.get_windows_link_args(limited_api, env)
if largs is None:
self.is_found = False
return
self.link_args = largs
self.is_found = True
+
class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
- def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram',
- libpc: bool = False):
- if libpc:
- mlog.debug(f'Searching for {name!r} via pkgconfig lookup in LIBPC')
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs,
+ installation: 'BasicPythonExternalProgram', embed: bool,
+ for_machine: 'MachineChoice'):
+ pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
+ pkg_name = f'python-{installation.version}{pkg_embed}'
+
+ if installation.build_config:
+ pkg_libdir = installation.build_config.get('c_api.pkgconfig_path')
+ pkg_libdir_origin = 'c_api.pkgconfig_path from the Python build config'
else:
- mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths')
+ pkg_libdir = installation.info['variables'].get('LIBPC')
+ pkg_libdir_origin = 'LIBPC'
+ if pkg_libdir is None:
+ # we do not fall back to system directories, since this could lead
+ # to using pkg-config of another Python installation, for example
+ # we could end up using CPython .pc file for PyPy
+ mlog.debug(f'Skipping pkgconfig lookup, {pkg_libdir_origin} is unset')
+ self.is_found = False
+ return
+
+ sysroot = environment.properties[for_machine].get_sys_root() or ''
+ pkg_libdir = sysroot + pkg_libdir
- PkgConfigDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
+ mlog.debug(f'Searching for {pkg_libdir!r} via pkgconfig lookup in {pkg_libdir_origin}')
+ pkgconfig_paths = [pkg_libdir] if pkg_libdir else []
- 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')
+ PkgConfigDependency.__init__(self, pkg_name, environment, kwargs, extra_paths=pkgconfig_paths)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False), for_machine)
+
+ if pkg_libdir and not self.is_found:
+ mlog.debug(f'{pkg_name!r} could not be found in {pkg_libdir_origin}, '
+ 'this is likely due to a relocated python installation')
+ return
# pkg-config files are usually accurate starting with python 3.8
if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'):
@@ -330,28 +498,39 @@ class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
# But not Apple, because it's a framework
if self.env.machines.host.is_darwin() and 'PYTHONFRAMEWORKPREFIX' in self.variables:
framework_prefix = self.variables['PYTHONFRAMEWORKPREFIX']
- # Add rpath, will be de-duplicated if necessary
+ # Add rpath, will be de-duplicated if necessary
if framework_prefix.startswith('/Applications/Xcode.app/'):
self.link_args += ['-Wl,-rpath,' + framework_prefix]
- self.raw_link_args += ['-Wl,-rpath,' + framework_prefix]
+ if self.raw_link_args is not None:
+ # When None, self.link_args is used
+ self.raw_link_args += ['-Wl,-rpath,' + framework_prefix]
+
class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase):
def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
+ kwargs: DependencyObjectKWs, installation: 'BasicPythonExternalProgram',
+ for_machine: 'MachineChoice'):
ExtraFrameworkDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False), for_machine)
class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
+ kwargs: DependencyObjectKWs, installation: 'BasicPythonExternalProgram',
+ for_machine: 'MachineChoice'):
SystemDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
-
- # match pkg-config behavior
- if self.link_libpython:
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False), for_machine)
+
+ # For most platforms, match pkg-config behavior. iOS is a special case;
+ # check for that first, so that check takes priority over
+ # `link_libpython` (which *shouldn't* be set, but just in case)
+ if self.platform.startswith('ios-'):
+ # iOS doesn't use link_libpython - it links with the *framework*.
+ self.link_args = ['-framework', 'Python', '-F', self.variables.get('base_prefix')]
+ self.is_found = True
+ elif self.link_libpython:
# link args
if mesonlib.is_windows():
self.find_libpy_windows(environment, limited_api=False)
@@ -361,10 +540,14 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
self.is_found = True
# compile args
- inc_paths = mesonlib.OrderedSet([
- self.variables.get('INCLUDEPY'),
- self.paths.get('include'),
- self.paths.get('platinclude')])
+ if self.build_config:
+ sysroot = environment.properties[for_machine].get_sys_root() or ''
+ inc_paths = mesonlib.OrderedSet([sysroot + self.build_config['c_api']['headers']])
+ else:
+ inc_paths = mesonlib.OrderedSet([
+ self.variables.get('INCLUDEPY'),
+ self.paths.get('include'),
+ self.paths.get('platinclude')])
self.compile_args += ['-I' + path for path in inc_paths if path]
@@ -373,7 +556,7 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
if mesonlib.is_windows() and self.get_windows_python_arch().endswith('64') and mesonlib.version_compare(self.version, '<3.12'):
self.compile_args += ['-DMS_WIN64=']
- if not self.clib_compiler.has_header('Python.h', '', environment, extra_args=self.compile_args)[0]:
+ if not self.clib_compiler.has_header('Python.h', '', extra_args=self.compile_args)[0]:
self.is_found = False
@staticmethod
@@ -381,7 +564,7 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
return 'sysconfig'
def python_factory(env: 'Environment', for_machine: 'MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
installation: T.Optional['BasicPythonExternalProgram'] = None) -> T.List['DependencyGenerator']:
# We can't use the factory_methods decorator here, as we need to pass the
# extra installation argument
@@ -393,58 +576,23 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
if installation is None:
installation = BasicPythonExternalProgram('python3', mesonlib.python_command)
installation.sanity()
- pkg_version = installation.info['variables'].get('LDVERSION') or installation.info['version']
if DependencyMethods.PKGCONFIG in methods:
if from_installation:
- pkg_libdir = installation.info['variables'].get('LIBPC')
- pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
- pkg_name = f'python-{pkg_version}{pkg_embed}'
-
- # 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: 'BasicPythonExternalProgram') -> 'ExternalDependency':
- if not pkg_libdir:
- # there is no LIBPC, so we can't search in it
- empty = ExternalDependency(DependencyTypeName('pkgconfig'), env, {})
- empty.name = 'python'
- return empty
-
- old_pkg_libdir = os.environ.pop('PKG_CONFIG_LIBDIR', None)
- old_pkg_path = os.environ.pop('PKG_CONFIG_PATH', None)
- os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
- try:
- return PythonPkgConfigDependency(name, env, kwargs, installation, True)
- finally:
- def set_env(name: str, value: str) -> None:
- if value is not None:
- os.environ[name] = value
- elif name in os.environ:
- del os.environ[name]
- set_env('PKG_CONFIG_LIBDIR', old_pkg_libdir)
- set_env('PKG_CONFIG_PATH', old_pkg_path)
-
- # Otherwise this doesn't fulfill the interface requirements
- wrap_in_pythons_pc_dir.log_tried = PythonPkgConfigDependency.log_tried # type: ignore[attr-defined]
-
- 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))
+ candidates.append(functools.partial(PythonPkgConfigDependency, env, kwargs, installation, embed, for_machine))
else:
candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs))
if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(PythonSystemDependency, 'python', env, kwargs, installation))
+ candidates.append(functools.partial(PythonSystemDependency, 'python', env, kwargs, installation, for_machine))
if DependencyMethods.EXTRAFRAMEWORK in methods:
nkwargs = kwargs.copy()
- if mesonlib.version_compare(pkg_version, '>= 3'):
+ if mesonlib.version_compare(installation.version, '>= 3'):
# There is a python in /System/Library/Frameworks, but that's python 2.x,
# Python 3 will always be in /Library
nkwargs['paths'] = ['/Library/Frameworks']
- candidates.append(functools.partial(PythonFrameworkDependency, 'Python', env, nkwargs, installation))
+ candidates.append(functools.partial(PythonFrameworkDependency, 'Python', env, nkwargs, installation, for_machine))
return candidates
diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py
index a3a9388..c245e5c 100644
--- a/mesonbuild/dependencies/qt.py
+++ b/mesonbuild/dependencies/qt.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2017 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
+# Copyright © 2021-2025 Intel Corporation
from __future__ import annotations
@@ -9,6 +9,7 @@ from __future__ import annotations
import abc
import re
import os
+from pathlib import Path
import typing as T
from .base import DependencyException, DependencyMethods
@@ -25,6 +26,7 @@ if T.TYPE_CHECKING:
from ..envconfig import MachineInfo
from ..environment import Environment
from ..dependencies import MissingCompiler
+ from .base import DependencyObjectKWs
def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> T.List[str]:
@@ -50,7 +52,7 @@ def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) ->
if len(dirname.split('.')) == 3:
private_dir = dirname
break
- return [private_dir, os.path.join(private_dir, 'Qt' + module)]
+ return [private_dir, Path(private_dir, f'Qt{module}').as_posix()]
def get_qmake_host_bins(qvars: T.Dict[str, str]) -> str:
@@ -95,7 +97,7 @@ def _get_modules_lib_suffix(version: str, info: 'MachineInfo', is_debug: bool) -
class QtExtraFrameworkDependency(ExtraFrameworkDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], qvars: T.Dict[str, str], language: T.Optional[str] = None):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs, qvars: T.Dict[str, str], language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
self.mod_name = name[2:]
self.qt_extra_include_directory = qvars['QT_INSTALL_HEADERS']
@@ -122,7 +124,7 @@ class _QtBase:
libexecdir: T.Optional[str] = None
version: str
- def __init__(self, name: str, kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, kwargs: DependencyObjectKWs):
self.name = name
self.qtname = name.capitalize()
self.qtver = name[-1]
@@ -131,20 +133,20 @@ class _QtBase:
else:
self.qtpkgname = self.qtname
- self.private_headers = T.cast('bool', kwargs.get('private_headers', False))
+ self.private_headers = kwargs.get('private_headers', False)
- self.requested_modules = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules'))
+ self.requested_modules = kwargs.get('modules', [])
if not self.requested_modules:
raise DependencyException('No ' + self.qtname + ' modules specified.')
- self.qtmain = T.cast('bool', kwargs.get('main', False))
+ self.qtmain = kwargs.get('main', False)
if not isinstance(self.qtmain, bool):
raise DependencyException('"main" argument must be a boolean')
def _link_with_qt_winmain(self, is_debug: bool, libdir: T.Union[str, T.List[str]]) -> bool:
libdir = mesonlib.listify(libdir) # TODO: shouldn't be necessary
base_name = self.get_qt_winmain_base_name(is_debug)
- qt_winmain = self.clib_compiler.find_library(base_name, self.env, libdir)
+ qt_winmain = self.clib_compiler.find_library(base_name, libdir)
if qt_winmain:
self.link_args.append(qt_winmain[0])
return True
@@ -170,7 +172,7 @@ class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta)
"""Specialization of the PkgConfigDependency for Qt."""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
_QtBase.__init__(self, name, kwargs)
# Always use QtCore as the "main" dependency, since it has the extra
@@ -249,7 +251,7 @@ class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
version: str
version_arg = '-v'
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
_QtBase.__init__(self, name, kwargs)
self.tool_name = f'qmake{self.qtver}'
self.tools = [f'qmake{self.qtver}', f'qmake-{self.name}', 'qmake']
@@ -259,7 +261,7 @@ class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
# is requested, add "">= 5, < 6", but if the user has ">= 5.6", don't
# lose that.
kwargs = kwargs.copy()
- _vers = mesonlib.listify(kwargs.get('version', []))
+ _vers = kwargs.get('version', [])
_vers.extend([f'>= {self.qtver}', f'< {int(self.qtver) + 1}'])
kwargs['version'] = _vers
@@ -303,7 +305,7 @@ class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
modules_lib_suffix = _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], is_debug)
for module in self.requested_modules:
- mincdir = os.path.join(incdir, 'Qt' + module)
+ mincdir = Path(incdir, f'Qt{module}').as_posix()
self.compile_args.append('-I' + mincdir)
if module == 'QuickTest':
@@ -319,7 +321,7 @@ class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
for directory in priv_inc:
self.compile_args.append('-I' + directory)
libfiles = self.clib_compiler.find_library(
- self.qtpkgname + module + modules_lib_suffix, self.env,
+ self.qtpkgname + module + modules_lib_suffix,
mesonlib.listify(libdir)) # TODO: shouldn't be necessary
if libfiles:
libfile = libfiles[0]
@@ -348,12 +350,12 @@ class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
pass
- def _framework_detect(self, qvars: T.Dict[str, str], modules: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
+ def _framework_detect(self, qvars: T.Dict[str, str], modules: T.List[str], kwargs: DependencyObjectKWs) -> None:
libdir = qvars['QT_INSTALL_LIBS']
# ExtraFrameworkDependency doesn't support any methods
fw_kwargs = kwargs.copy()
- fw_kwargs.pop('method', None)
+ fw_kwargs.pop('method')
fw_kwargs['paths'] = [libdir]
for m in modules:
@@ -442,7 +444,7 @@ class Qt5PkgConfigDependency(QtPkgConfigDependency):
class Qt6PkgConfigDependency(Qt6WinMainMixin, QtPkgConfigDependency):
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, env, kwargs)
if not self.libexecdir:
mlog.debug(f'detected Qt6 {self.version} pkg-config dependency does not '
diff --git a/mesonbuild/dependencies/scalapack.py b/mesonbuild/dependencies/scalapack.py
index c04d1f5..26a6e39 100644
--- a/mesonbuild/dependencies/scalapack.py
+++ b/mesonbuild/dependencies/scalapack.py
@@ -9,7 +9,7 @@ import os
import typing as T
from ..options import OptionKey
-from .base import DependencyMethods
+from .base import DependencyException, DependencyMethods
from .cmake import CMakeDependency
from .detect import packages
from .pkgconfig import PkgConfigDependency
@@ -19,16 +19,17 @@ if T.TYPE_CHECKING:
from ..environment import Environment
from ..mesonlib import MachineChoice
from .factory import DependencyGenerator
+ from .base import DependencyObjectKWs
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE})
def scalapack_factory(env: 'Environment', for_machine: 'MachineChoice',
- kwargs: T.Dict[str, T.Any],
+ kwargs: DependencyObjectKWs,
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
candidates: T.List['DependencyGenerator'] = []
if DependencyMethods.PKGCONFIG in methods:
- static_opt = kwargs.get('static', env.coredata.optstore.get_value_for(OptionKey('prefer_static')))
+ static_opt = kwargs['static'] if kwargs.get('static') is not None else env.coredata.optstore.get_value_for(OptionKey('prefer_static'))
mkl = 'mkl-static-lp64-iomp' if static_opt else 'mkl-dynamic-lp64-iomp'
candidates.append(functools.partial(
MKLPkgConfigDependency, mkl, env, kwargs))
@@ -54,7 +55,7 @@ class MKLPkgConfigDependency(PkgConfigDependency):
bunch of fixups to make it work correctly.
"""
- def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ def __init__(self, name: str, env: 'Environment', kwargs: DependencyObjectKWs,
language: T.Optional[str] = None):
_m = os.environ.get('MKLROOT')
self.__mklroot = Path(_m).resolve() if _m else None
@@ -65,8 +66,7 @@ class MKLPkgConfigDependency(PkgConfigDependency):
super().__init__(name, env, kwargs, language=language)
# Doesn't work with gcc on windows, but does on Linux
- if (not self.__mklroot or (env.machines[self.for_machine].is_windows()
- and self.clib_compiler.id == 'gcc')):
+ if env.machines[self.for_machine].is_windows() and self.clib_compiler.id == 'gcc':
self.is_found = False
# This can happen either because we're using GCC, we couldn't find the
@@ -96,6 +96,9 @@ class MKLPkgConfigDependency(PkgConfigDependency):
self.version = v
def _set_libs(self) -> None:
+ if self.__mklroot is None:
+ raise DependencyException('MKLROOT not set')
+
super()._set_libs()
if self.env.machines[self.for_machine].is_windows():
@@ -133,6 +136,9 @@ class MKLPkgConfigDependency(PkgConfigDependency):
self.link_args.insert(i + 1, '-lmkl_blacs_intelmpi_lp64')
def _set_cargs(self) -> None:
+ if self.__mklroot is None:
+ raise DependencyException('MKLROOT not set')
+
allow_system = False
if self.language == 'fortran':
# gfortran doesn't appear to look in system paths for INCLUDE files,
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index fc44037..566ba52 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -13,9 +13,8 @@ import typing as T
from .. import mlog
from .. import mesonlib
from ..mesonlib import (
- Popen_safe, extract_as_list, version_compare_many
+ Popen_safe, version_compare_many
)
-from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods, DependencyTypeName, SystemDependency
from .configtool import ConfigToolDependency
@@ -24,10 +23,11 @@ from .factory import DependencyFactory
if T.TYPE_CHECKING:
from ..environment import Environment
+ from .base import DependencyObjectKWs
class GLDependencySystem(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__(name, environment, kwargs)
if self.env.machines[self.for_machine].is_darwin():
@@ -43,8 +43,8 @@ class GLDependencySystem(SystemDependency):
# FIXME: Detect version using self.clib_compiler
return
else:
- links = self.clib_compiler.find_library('GL', environment, [])
- has_header = self.clib_compiler.has_header('GL/gl.h', '', environment)[0]
+ links = self.clib_compiler.find_library('GL', [])
+ has_header = self.clib_compiler.has_header('GL/gl.h', '')[0]
if links and has_header:
self.is_found = True
self.link_args = links
@@ -56,7 +56,7 @@ class GnuStepDependency(ConfigToolDependency):
tools = ['gnustep-config']
tool_name = 'gnustep-config'
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None:
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs) -> None:
super().__init__('gnustep', environment, kwargs, language='objc')
if not self.is_found:
return
@@ -135,7 +135,7 @@ class SDL2DependencyConfigTool(ConfigToolDependency):
tools = ['sdl2-config']
tool_name = 'sdl2-config'
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__(name, environment, kwargs)
if not self.is_found:
return
@@ -148,11 +148,11 @@ class WxDependency(ConfigToolDependency):
tools = ['wx-config-3.0', 'wx-config-3.1', 'wx-config', 'wx-config-gtk3']
tool_name = 'wx-config'
- def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ def __init__(self, environment: 'Environment', kwargs: DependencyObjectKWs):
super().__init__('WxWidgets', environment, kwargs, language='cpp')
if not self.is_found:
return
- self.requested_modules = self.get_requested(kwargs)
+ self.requested_modules = kwargs.get('modules', [])
extra_args = []
if self.static:
@@ -170,29 +170,16 @@ class WxDependency(ConfigToolDependency):
self.compile_args = self.get_config_value(['--cxxflags'] + extra_args + self.requested_modules, 'compile_args')
self.link_args = self.get_config_value(['--libs'] + extra_args + self.requested_modules, 'link_args')
- @staticmethod
- def get_requested(kwargs: T.Dict[str, T.Any]) -> T.List[str]:
- if 'modules' not in kwargs:
- return []
- candidates = extract_as_list(kwargs, 'modules')
- for c in candidates:
- if not isinstance(c, str):
- raise DependencyException('wxwidgets module argument is not a string')
- return candidates
-
packages['wxwidgets'] = WxDependency
class VulkanDependencySystem(SystemDependency):
- def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
+ def __init__(self, name: str, environment: 'Environment', kwargs: DependencyObjectKWs, language: T.Optional[str] = None) -> None:
super().__init__(name, environment, kwargs, language=language)
- try:
- self.vulkan_sdk = os.environ.get('VULKAN_SDK', os.environ['VK_SDK_PATH'])
- if not os.path.isabs(self.vulkan_sdk):
- raise DependencyException('VULKAN_SDK must be an absolute path.')
- except KeyError:
- self.vulkan_sdk = None
+ self.vulkan_sdk = os.environ.get('VULKAN_SDK', os.environ.get('VK_SDK_PATH'))
+ if self.vulkan_sdk and not os.path.isabs(self.vulkan_sdk):
+ raise DependencyException('VULKAN_SDK must be an absolute path.')
if self.vulkan_sdk:
# TODO: this config might not work on some platforms, fix bugs as reported
@@ -200,18 +187,42 @@ class VulkanDependencySystem(SystemDependency):
lib_name = 'vulkan'
lib_dir = 'lib'
inc_dir = 'include'
- if mesonlib.is_windows():
+ if self.env.machines[self.for_machine].is_windows():
lib_name = 'vulkan-1'
- lib_dir = 'Lib32'
+ lib_dir = ''
inc_dir = 'Include'
- if detect_cpu_family(self.env.coredata.compilers.host) == 'x86_64':
- lib_dir = 'Lib'
+ build_cpu = self.env.machines.build.cpu_family
+ host_cpu = self.env.machines.host.cpu_family
+ if build_cpu == 'x86_64':
+ if host_cpu == build_cpu:
+ lib_dir = 'Lib'
+ elif host_cpu == 'aarch64':
+ lib_dir = 'Lib-ARM64'
+ elif host_cpu == 'x86':
+ lib_dir = 'Lib32'
+ elif build_cpu == 'aarch64':
+ if host_cpu == build_cpu:
+ lib_dir = 'Lib'
+ elif host_cpu == 'x86_64':
+ lib_dir = 'Lib-x64'
+ elif host_cpu == 'x86':
+ lib_dir = 'Lib32'
+ elif build_cpu == 'x86':
+ if host_cpu == build_cpu:
+ lib_dir = 'Lib32'
+ if host_cpu == 'aarch64':
+ lib_dir = 'Lib-ARM64'
+ elif host_cpu == 'x86_64':
+ lib_dir = 'Lib'
+
+ if lib_dir == '':
+ raise DependencyException(f'Target architecture \'{host_cpu}\' is not supported for this Vulkan SDK.')
# make sure header and lib are valid
inc_path = os.path.join(self.vulkan_sdk, inc_dir)
header = os.path.join(inc_path, 'vulkan', 'vulkan.h')
lib_path = os.path.join(self.vulkan_sdk, lib_dir)
- find_lib = self.clib_compiler.find_library(lib_name, environment, [lib_path])
+ find_lib = self.clib_compiler.find_library(lib_name, [lib_path])
if not find_lib:
raise DependencyException('VULKAN_SDK point to invalid directory (no lib)')
@@ -227,8 +238,8 @@ class VulkanDependencySystem(SystemDependency):
self.link_args.append('-l' + lib_name)
else:
# simply try to guess it, usually works on linux
- libs = self.clib_compiler.find_library('vulkan', environment, [])
- if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment, disable_cache=True)[0]:
+ libs = self.clib_compiler.find_library('vulkan', [])
+ if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', disable_cache=True)[0]:
self.is_found = True
for lib in libs:
self.link_args.append(lib)
@@ -241,8 +252,7 @@ class VulkanDependencySystem(SystemDependency):
components = [str(self.clib_compiler.compute_int(f'VK_VERSION_{c}(VK_HEADER_VERSION_COMPLETE)',
low=0, high=None, guess=e,
prefix='#include <vulkan/vulkan.h>',
- env=environment,
- extra_args=None,
+ extra_args=self.compile_args,
dependencies=None))
# list containing vulkan version components and their expected value
for c, e in [('MAJOR', 1), ('MINOR', 3), ('PATCH', None)]]
diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py
index 43fad0c..dd050b0 100644
--- a/mesonbuild/envconfig.py
+++ b/mesonbuild/envconfig.py
@@ -4,17 +4,22 @@
from __future__ import annotations
from dataclasses import dataclass
-import subprocess
import typing as T
from enum import Enum
+import os
+import platform
+import sys
from . import mesonlib
-from .mesonlib import EnvironmentException, HoldableObject
+from .mesonlib import EnvironmentException, HoldableObject, Popen_safe
+from .programs import ExternalProgram
from . import mlog
from pathlib import Path
if T.TYPE_CHECKING:
from .options import ElementaryOptionValues
+ from .compilers import Compiler
+ from .compilers.mixins.visualstudio import VisualStudioLikeCompiler
# These classes contains all the data pulled from configuration files (native
@@ -51,6 +56,7 @@ known_cpu_families = (
'msp430',
'parisc',
'pic24',
+ 'pic32',
'ppc',
'ppc64',
'riscv32',
@@ -376,11 +382,17 @@ class MachineInfo(HoldableObject):
"""Machine is IRIX?"""
return self.system.startswith('irix')
+ def is_os2(self) -> bool:
+ """
+ Machine is OS/2?
+ """
+ return self.system == 'os/2'
+
# Various prefixes and suffixes for import libraries, shared libraries,
# static libraries, and executables.
# Versioning is added to these names in the backends as-needed.
def get_exe_suffix(self) -> str:
- if self.is_windows() or self.is_cygwin():
+ if self.is_windows() or self.is_cygwin() or self.is_os2():
return 'exe'
else:
return ''
@@ -423,31 +435,23 @@ class BinaryTable:
del self.binaries['pkgconfig']
@staticmethod
- def detect_ccache() -> T.List[str]:
- try:
- subprocess.check_call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- except (OSError, subprocess.CalledProcessError):
- return []
- return ['ccache']
+ def detect_ccache() -> ExternalProgram:
+ return ExternalProgram('ccache', silent=True)
@staticmethod
- def detect_sccache() -> T.List[str]:
- try:
- subprocess.check_call(['sccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- except (OSError, subprocess.CalledProcessError):
- return []
- return ['sccache']
+ def detect_sccache() -> ExternalProgram:
+ return ExternalProgram('sccache', silent=True)
@staticmethod
- def detect_compiler_cache() -> T.List[str]:
+ def detect_compiler_cache() -> ExternalProgram:
# Sccache is "newer" so it is assumed that people would prefer it by default.
cache = BinaryTable.detect_sccache()
- if cache:
+ if cache.found():
return cache
return BinaryTable.detect_ccache()
@classmethod
- def parse_entry(cls, entry: T.Union[str, T.List[str]]) -> T.Tuple[T.List[str], T.List[str]]:
+ def parse_entry(cls, entry: T.Union[str, T.List[str]]) -> T.Tuple[T.List[str], T.Union[None, ExternalProgram]]:
parts = mesonlib.stringlistify(entry)
# Ensure ccache exists and remove it if it doesn't
if parts[0] == 'ccache':
@@ -458,7 +462,7 @@ class BinaryTable:
ccache = cls.detect_sccache()
else:
compiler = parts
- ccache = []
+ ccache = None
if not compiler:
raise EnvironmentException(f'Compiler cache specified without compiler: {parts[0]}')
# Return value has to be a list of compiler 'choices'
@@ -491,3 +495,259 @@ class CMakeVariables:
def get_variables(self) -> T.Dict[str, T.List[str]]:
return self.variables
+
+
+# Machine and platform detection functions
+# ========================================
+
+KERNEL_MAPPINGS: T.Mapping[str, str] = {'freebsd': 'freebsd',
+ 'openbsd': 'openbsd',
+ 'netbsd': 'netbsd',
+ 'windows': 'nt',
+ 'android': 'linux',
+ 'linux': 'linux',
+ 'cygwin': 'nt',
+ 'darwin': 'xnu',
+ 'ios': 'xnu',
+ 'tvos': 'xnu',
+ 'visionos': 'xnu',
+ 'watchos': 'xnu',
+ 'dragonfly': 'dragonfly',
+ 'haiku': 'haiku',
+ 'gnu': 'gnu',
+ }
+
+def detect_windows_arch(compilers: T.Dict[str, Compiler]) -> str:
+ """
+ Detecting the 'native' architecture of Windows is not a trivial task. We
+ cannot trust that the architecture that Python is built for is the 'native'
+ one because you can run 32-bit apps on 64-bit Windows using WOW64 and
+ people sometimes install 32-bit Python on 64-bit Windows.
+
+ We also can't rely on the architecture of the OS itself, since it's
+ perfectly normal to compile and run 32-bit applications on Windows as if
+ they were native applications. It's a terrible experience to require the
+ user to supply a cross-info file to compile 32-bit applications on 64-bit
+ Windows. Thankfully, the only way to compile things with Visual Studio on
+ Windows is by entering the 'msvc toolchain' environment, which can be
+ easily detected.
+
+ In the end, the sanest method is as follows:
+ 1. Check environment variables that are set by Windows and WOW64 to find out
+ if this is x86 (possibly in WOW64), if so use that as our 'native'
+ architecture.
+ 2. If the compiler toolchain target architecture is x86, use that as our
+ 'native' architecture.
+ 3. Otherwise, use the actual Windows architecture
+
+ """
+ os_arch = mesonlib.windows_detect_native_arch()
+ if os_arch == 'x86':
+ return os_arch
+ # If we're on 64-bit Windows, 32-bit apps can be compiled without
+ # cross-compilation. So if we're doing that, just set the native arch as
+ # 32-bit and pretend like we're running under WOW64. Else, return the
+ # actual Windows architecture that we deduced above.
+ for compiler in compilers.values():
+ compiler = T.cast('VisualStudioLikeCompiler', compiler)
+ if compiler.id == 'msvc' and (compiler.target in {'x86', '80x86'}):
+ return 'x86'
+ if compiler.id == 'clang-cl' and (compiler.target in {'x86', 'i686'}):
+ return 'x86'
+ if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
+ return 'x86'
+ return os_arch
+
+def any_compiler_has_define(compilers: T.Dict[str, Compiler], define: str) -> bool:
+ for c in compilers.values():
+ try:
+ if c.has_builtin_define(define):
+ return True
+ except mesonlib.MesonException:
+ # Ignore compilers that do not support has_builtin_define.
+ pass
+ return False
+
+def detect_cpu_family(compilers: T.Dict[str, Compiler]) -> str:
+ """
+ Python is inconsistent in its platform module.
+ It returns different values for the same cpu.
+ For x86 it might return 'x86', 'i686' or some such.
+ Do some canonicalization.
+ """
+ if mesonlib.is_windows():
+ trial = detect_windows_arch(compilers)
+ elif mesonlib.is_freebsd() or mesonlib.is_netbsd() or mesonlib.is_openbsd() or mesonlib.is_qnx() or mesonlib.is_aix():
+ trial = platform.processor().lower()
+ else:
+ trial = platform.machine().lower()
+ if trial.startswith('i') and trial.endswith('86'):
+ trial = 'x86'
+ elif trial == 'bepc':
+ trial = 'x86'
+ elif trial == 'arm64':
+ trial = 'aarch64'
+ elif trial.startswith('aarch64'):
+ # This can be `aarch64_be`
+ trial = 'aarch64'
+ elif trial.startswith('arm') or trial.startswith('earm'):
+ trial = 'arm'
+ elif trial.startswith(('powerpc64', 'ppc64')):
+ trial = 'ppc64'
+ elif trial.startswith(('powerpc', 'ppc')) or trial in {'macppc', 'power macintosh'}:
+ trial = 'ppc'
+ elif trial in {'amd64', 'x64', 'i86pc'}:
+ trial = 'x86_64'
+ elif trial in {'sun4u', 'sun4v'}:
+ trial = 'sparc64'
+ elif trial.startswith('mips'):
+ if '64' not in trial:
+ trial = 'mips'
+ else:
+ trial = 'mips64'
+ elif trial in {'ip30', 'ip35'}:
+ trial = 'mips64'
+
+ # On Linux (and maybe others) there can be any mixture of 32/64 bit code in
+ # the kernel, Python, system, 32-bit chroot on 64-bit host, etc. The only
+ # reliable way to know is to check the compiler defines.
+ if trial == 'x86_64':
+ if any_compiler_has_define(compilers, '__i386__'):
+ trial = 'x86'
+ elif trial == 'aarch64':
+ if any_compiler_has_define(compilers, '__arm__'):
+ trial = 'arm'
+ # Add more quirks here as bugs are reported. Keep in sync with detect_cpu()
+ # below.
+ elif trial == 'parisc64':
+ # ATM there is no 64 bit userland for PA-RISC. Thus always
+ # report it as 32 bit for simplicity.
+ trial = 'parisc'
+ elif trial == 'ppc':
+ # AIX always returns powerpc, check here for 64-bit
+ if any_compiler_has_define(compilers, '__64BIT__'):
+ trial = 'ppc64'
+ # MIPS64 is able to run MIPS32 code natively, so there is a chance that
+ # such mixture mentioned above exists.
+ elif trial == 'mips64':
+ if compilers and not any_compiler_has_define(compilers, '__mips64'):
+ trial = 'mips'
+
+ if trial not in known_cpu_families:
+ mlog.warning(f'Unknown CPU family {trial!r}, please report this at '
+ 'https://github.com/mesonbuild/meson/issues/new with the '
+ 'output of `uname -a` and `cat /proc/cpuinfo`')
+
+ return trial
+
+def detect_cpu(compilers: T.Dict[str, Compiler]) -> str:
+ if mesonlib.is_windows():
+ trial = detect_windows_arch(compilers)
+ elif mesonlib.is_freebsd() or mesonlib.is_netbsd() or mesonlib.is_openbsd() or mesonlib.is_aix():
+ trial = platform.processor().lower()
+ else:
+ trial = platform.machine().lower()
+
+ if trial in {'amd64', 'x64', 'i86pc'}:
+ trial = 'x86_64'
+ if trial == 'x86_64':
+ # Same check as above for cpu_family
+ if any_compiler_has_define(compilers, '__i386__'):
+ trial = 'i686' # All 64 bit cpus have at least this level of x86 support.
+ elif trial.startswith('aarch64') or trial.startswith('arm64'):
+ # Same check as above for cpu_family
+ if any_compiler_has_define(compilers, '__arm__'):
+ trial = 'arm'
+ else:
+ # for aarch64_be
+ trial = 'aarch64'
+ elif trial.startswith('earm'):
+ trial = 'arm'
+ elif trial == 'e2k':
+ # Make more precise CPU detection for Elbrus platform.
+ trial = platform.processor().lower()
+ elif trial.startswith('mips'):
+ if '64' not in trial:
+ trial = 'mips'
+ else:
+ if compilers and not any_compiler_has_define(compilers, '__mips64'):
+ trial = 'mips'
+ else:
+ trial = 'mips64'
+ elif trial == 'ppc':
+ # AIX always returns powerpc, check here for 64-bit
+ if any_compiler_has_define(compilers, '__64BIT__'):
+ trial = 'ppc64'
+
+ # Add more quirks here as bugs are reported. Keep in sync with
+ # detect_cpu_family() above.
+ return trial
+
+def detect_kernel(system: str) -> T.Optional[str]:
+ if system == 'sunos':
+ # Solaris 5.10 uname doesn't support the -o switch, and illumos started
+ # with version 5.11 so shortcut the logic to report 'solaris' in such
+ # cases where the version is 5.10 or below.
+ if mesonlib.version_compare(platform.uname().release, '<=5.10'):
+ return 'solaris'
+ # This needs to be /usr/bin/uname because gnu-uname could be installed and
+ # won't provide the necessary information
+ p, out, _ = Popen_safe(['/usr/bin/uname', '-o'])
+ if p.returncode != 0:
+ raise mesonlib.MesonException('Failed to run "/usr/bin/uname -o"')
+ out = out.lower().strip()
+ if out not in {'illumos', 'solaris'}:
+ mlog.warning(f'Got an unexpected value for kernel on a SunOS derived platform, expected either "illumos" or "solaris", but got "{out}".'
+ "Please open a Meson issue with the OS you're running and the value detected for your kernel.")
+ return None
+ return out
+ return KERNEL_MAPPINGS.get(system, None)
+
+def detect_subsystem(system: str) -> T.Optional[str]:
+ if system == 'darwin':
+ return 'macos'
+ return system
+
+def detect_system() -> str:
+ if sys.platform == 'cygwin':
+ return 'cygwin'
+ return platform.system().lower()
+
+def detect_msys2_arch() -> T.Optional[str]:
+ return os.environ.get('MSYSTEM_CARCH', None)
+
+def detect_machine_info(compilers: T.Optional[T.Dict[str, Compiler]] = None) -> MachineInfo:
+ """Detect the machine we're running on
+
+ If compilers are not provided, we cannot know as much. None out those
+ fields to avoid accidentally depending on partial knowledge. The
+ underlying ''detect_*'' method can be called to explicitly use the
+ partial information.
+ """
+ system = detect_system()
+ return MachineInfo(
+ system,
+ detect_cpu_family(compilers) if compilers is not None else None,
+ detect_cpu(compilers) if compilers is not None else None,
+ sys.byteorder,
+ detect_kernel(system),
+ detect_subsystem(system))
+
+# TODO make this compare two `MachineInfo`s purely. How important is the
+# `detect_cpu_family({})` distinction? It is the one impediment to that.
+def machine_info_can_run(machine_info: MachineInfo) -> bool:
+ """Whether we can run binaries for this machine on the current machine.
+
+ Can almost always run 32-bit binaries on 64-bit natively if the host
+ and build systems are the same. We don't pass any compilers to
+ detect_cpu_family() here because we always want to know the OS
+ architecture, not what the compiler environment tells us.
+ """
+ if machine_info.system != detect_system():
+ return False
+ true_build_cpu_family = detect_cpu_family({})
+ assert machine_info.cpu_family is not None, 'called on incomplete machine_info'
+ return \
+ (machine_info.cpu_family == true_build_cpu_family) or \
+ ((true_build_cpu_family == 'x86_64') and (machine_info.cpu_family == 'x86')) or \
+ ((true_build_cpu_family == 'mips64') and (machine_info.cpu_family == 'mips'))
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index f322cda..f1d55cc 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -5,50 +5,45 @@
from __future__ import annotations
import itertools
-import os, platform, re, sys, shutil
+import os, re
import typing as T
import collections
+from . import cmdline
from . import coredata
from . import mesonlib
from . import machinefile
-
-CmdLineFileParser = machinefile.CmdLineFileParser
+from . import options
from .mesonlib import (
MesonException, MachineChoice, Popen_safe, PerMachine,
- PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg,
- search_version, MesonBugException
+ PerMachineDefaultable, PerThreeMachineDefaultable, split_args,
+ MesonBugException
)
from .options import OptionKey
from . import mlog
from .programs import ExternalProgram
from .envconfig import (
- BinaryTable, MachineInfo, Properties, known_cpu_families, CMakeVariables,
+ BinaryTable, MachineInfo, Properties, CMakeVariables,
+ detect_machine_info, machine_info_can_run
)
from . import compilers
-from .compilers import (
- is_assembly,
- is_header,
- is_library,
- is_llvm_ir,
- is_object,
- is_source,
-)
-from functools import lru_cache
from mesonbuild import envconfig
if T.TYPE_CHECKING:
from .compilers import Compiler
- from .compilers.mixins.visualstudio import VisualStudioLikeCompiler
- from .options import ElementaryOptionValues
+ from .options import OptionDict, ElementaryOptionValues
from .wrap.wrap import Resolver
- from . import cargo
- CompilersDict = T.Dict[str, Compiler]
+NON_LANG_ENV_OPTIONS = [
+ ('PKG_CONFIG_PATH', 'pkg_config_path'),
+ ('CMAKE_PREFIX_PATH', 'cmake_prefix_path'),
+ ('LDFLAGS', 'ldflags'),
+ ('CPPFLAGS', 'cppflags'),
+]
build_filename = 'meson.build'
@@ -83,494 +78,12 @@ def _get_env_var(for_machine: MachineChoice, is_cross: bool, var_name: str) -> T
return value
-def detect_gcovr(gcovr_exe: str = 'gcovr', min_version: str = '3.3', log: bool = False) \
- -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
- try:
- p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- return None, None
- found = search_version(found)
- if p.returncode == 0 and mesonlib.version_compare(found, '>=' + min_version):
- if log:
- mlog.log('Found gcovr-{} at {}'.format(found, quote_arg(shutil.which(gcovr_exe))))
- return gcovr_exe, found
- return None, None
-
-def detect_lcov(lcov_exe: str = 'lcov', log: bool = False) \
- -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
- try:
- p, found = Popen_safe([lcov_exe, '--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- return None, None
- found = search_version(found)
- if p.returncode == 0 and found:
- if log:
- mlog.log('Found lcov-{} at {}'.format(found, quote_arg(shutil.which(lcov_exe))))
- return lcov_exe, found
- return None, None
-
-def detect_llvm_cov(suffix: T.Optional[str] = None) -> T.Optional[str]:
- # If there's a known suffix or forced lack of suffix, use that
- if suffix is not None:
- if suffix == '':
- tool = 'llvm-cov'
- else:
- tool = f'llvm-cov-{suffix}'
- if shutil.which(tool) is not None:
- return tool
- else:
- # Otherwise guess in the dark
- tools = get_llvm_tool_names('llvm-cov')
- for tool in tools:
- if shutil.which(tool):
- return tool
- return None
-
-def compute_llvm_suffix(coredata: coredata.CoreData) -> T.Optional[str]:
- # Check to see if the user is trying to do coverage for either a C or C++ project
- compilers = coredata.compilers[MachineChoice.BUILD]
- cpp_compiler_is_clang = 'cpp' in compilers and compilers['cpp'].id == 'clang'
- c_compiler_is_clang = 'c' in compilers and compilers['c'].id == 'clang'
- # Extract first the C++ compiler if available. If it's a Clang of some kind, compute the suffix if possible
- if cpp_compiler_is_clang:
- suffix = compilers['cpp'].version.split('.')[0]
- return suffix
-
- # Then the C compiler, again checking if it's some kind of Clang and computing the suffix
- if c_compiler_is_clang:
- suffix = compilers['c'].version.split('.')[0]
- return suffix
-
- # Neither compiler is a Clang, or no compilers are for C or C++
- return None
-
-def detect_lcov_genhtml(lcov_exe: str = 'lcov', genhtml_exe: str = 'genhtml') \
- -> T.Tuple[str, T.Optional[str], str]:
- lcov_exe, lcov_version = detect_lcov(lcov_exe)
- if shutil.which(genhtml_exe) is None:
- genhtml_exe = None
-
- return lcov_exe, lcov_version, genhtml_exe
-
-def find_coverage_tools(coredata: coredata.CoreData) -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]:
- gcovr_exe, gcovr_version = detect_gcovr()
-
- llvm_cov_exe = detect_llvm_cov(compute_llvm_suffix(coredata))
- # Some platforms may provide versioned clang but only non-versioned llvm utils
- if llvm_cov_exe is None:
- llvm_cov_exe = detect_llvm_cov('')
-
- lcov_exe, lcov_version, genhtml_exe = detect_lcov_genhtml()
-
- return gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe
-
-def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.Optional[T.List[str]]:
- r = detect_ninja_command_and_version(version, log)
- return r[0] if r else None
-
-def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) -> T.Optional[T.Tuple[T.List[str], str]]:
- env_ninja = os.environ.get('NINJA', None)
- for n in [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu']:
- prog = ExternalProgram(n, silent=True)
- if not prog.found():
- continue
- try:
- p, found = Popen_safe(prog.command + ['--version'])[0:2]
- except (FileNotFoundError, PermissionError):
- # Doesn't exist in PATH or isn't executable
- continue
- found = found.strip()
- # Perhaps we should add a way for the caller to know the failure mode
- # (not found or too old)
- if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version):
- if log:
- name = os.path.basename(n)
- if name.endswith('-' + found):
- name = name[0:-1 - len(found)]
- if name == 'ninja-build':
- name = 'ninja'
- if name == 'samu':
- name = 'samurai'
- mlog.log('Found {}-{} at {}'.format(name, found,
- ' '.join([quote_arg(x) for x in prog.command])))
- return (prog.command, found)
- return None
-
-def get_llvm_tool_names(tool: str) -> T.List[str]:
- # Ordered list of possible suffixes of LLVM executables to try. Start with
- # base, then try newest back to oldest (3.5 is arbitrary), and finally the
- # devel version. Please note that the development snapshot in Debian does
- # not have a distinct name. Do not move it to the beginning of the list
- # unless it becomes a stable release.
- suffixes = [
- '', # base (no suffix)
- '-20.1', '20.1',
- '-20', '20',
- '-19.1', '19.1',
- '-19', '19',
- '-18.1', '18.1',
- '-18', '18',
- '-17', '17',
- '-16', '16',
- '-15', '15',
- '-14', '14',
- '-13', '13',
- '-12', '12',
- '-11', '11',
- '-10', '10',
- '-9', '90',
- '-8', '80',
- '-7', '70',
- '-6.0', '60',
- '-5.0', '50',
- '-4.0', '40',
- '-3.9', '39',
- '-3.8', '38',
- '-3.7', '37',
- '-3.6', '36',
- '-3.5', '35',
- '-20', # Debian development snapshot
- '-devel', # FreeBSD development snapshot
- ]
- names: T.List[str] = []
- for suffix in suffixes:
- names.append(tool + suffix)
- return names
-
-def detect_scanbuild() -> T.List[str]:
- """ Look for scan-build binary on build platform
-
- First, if a SCANBUILD env variable has been provided, give it precedence
- on all platforms.
-
- For most platforms, scan-build is found is the PATH contains a binary
- named "scan-build". However, some distribution's package manager (FreeBSD)
- don't. For those, loop through a list of candidates to see if one is
- available.
-
- Return: a single-element list of the found scan-build binary ready to be
- passed to Popen()
- """
- exelist: T.List[str] = []
- if 'SCANBUILD' in os.environ:
- exelist = split_args(os.environ['SCANBUILD'])
-
- else:
- tools = get_llvm_tool_names('scan-build')
- for tool in tools:
- which = shutil.which(tool)
- if which is not None:
- exelist = [which]
- break
-
- if exelist:
- tool = exelist[0]
- if os.path.isfile(tool) and os.access(tool, os.X_OK):
- return [tool]
- return []
-
-def detect_clangformat() -> T.List[str]:
- """ Look for clang-format binary on build platform
-
- Do the same thing as detect_scanbuild to find clang-format except it
- currently does not check the environment variable.
-
- Return: a single-element list of the found clang-format binary ready to be
- passed to Popen()
- """
- tools = get_llvm_tool_names('clang-format')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-def detect_clangtidy() -> T.List[str]:
- """ Look for clang-tidy binary on build platform
-
- Return: a single-element list of the found clang-tidy binary ready to be
- passed to Popen()
- """
- tools = get_llvm_tool_names('clang-tidy')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-def detect_clangapply() -> T.List[str]:
- """ Look for clang-apply-replacements binary on build platform
-
- Return: a single-element list of the found clang-apply-replacements binary
- ready to be passed to Popen()
- """
- tools = get_llvm_tool_names('clang-apply-replacements')
- for tool in tools:
- path = shutil.which(tool)
- if path is not None:
- return [path]
- return []
-
-def detect_windows_arch(compilers: CompilersDict) -> str:
- """
- Detecting the 'native' architecture of Windows is not a trivial task. We
- cannot trust that the architecture that Python is built for is the 'native'
- one because you can run 32-bit apps on 64-bit Windows using WOW64 and
- people sometimes install 32-bit Python on 64-bit Windows.
-
- We also can't rely on the architecture of the OS itself, since it's
- perfectly normal to compile and run 32-bit applications on Windows as if
- they were native applications. It's a terrible experience to require the
- user to supply a cross-info file to compile 32-bit applications on 64-bit
- Windows. Thankfully, the only way to compile things with Visual Studio on
- Windows is by entering the 'msvc toolchain' environment, which can be
- easily detected.
-
- In the end, the sanest method is as follows:
- 1. Check environment variables that are set by Windows and WOW64 to find out
- if this is x86 (possibly in WOW64), if so use that as our 'native'
- architecture.
- 2. If the compiler toolchain target architecture is x86, use that as our
- 'native' architecture.
- 3. Otherwise, use the actual Windows architecture
-
- """
- os_arch = mesonlib.windows_detect_native_arch()
- if os_arch == 'x86':
- return os_arch
- # If we're on 64-bit Windows, 32-bit apps can be compiled without
- # cross-compilation. So if we're doing that, just set the native arch as
- # 32-bit and pretend like we're running under WOW64. Else, return the
- # actual Windows architecture that we deduced above.
- for compiler in compilers.values():
- compiler = T.cast('VisualStudioLikeCompiler', compiler)
- if compiler.id == 'msvc' and (compiler.target in {'x86', '80x86'}):
- return 'x86'
- if compiler.id == 'clang-cl' and (compiler.target in {'x86', 'i686'}):
- return 'x86'
- if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
- return 'x86'
- return os_arch
-
-def any_compiler_has_define(compilers: CompilersDict, define: str) -> bool:
- for c in compilers.values():
- try:
- if c.has_builtin_define(define):
- return True
- except mesonlib.MesonException:
- # Ignore compilers that do not support has_builtin_define.
- pass
- return False
-
-def detect_cpu_family(compilers: CompilersDict) -> str:
- """
- Python is inconsistent in its platform module.
- It returns different values for the same cpu.
- For x86 it might return 'x86', 'i686' or some such.
- Do some canonicalization.
- """
- if mesonlib.is_windows():
- trial = detect_windows_arch(compilers)
- elif mesonlib.is_freebsd() or mesonlib.is_netbsd() or mesonlib.is_openbsd() or mesonlib.is_qnx() or mesonlib.is_aix():
- trial = platform.processor().lower()
- else:
- trial = platform.machine().lower()
- if trial.startswith('i') and trial.endswith('86'):
- trial = 'x86'
- elif trial == 'bepc':
- trial = 'x86'
- elif trial == 'arm64':
- trial = 'aarch64'
- elif trial.startswith('aarch64'):
- # This can be `aarch64_be`
- trial = 'aarch64'
- elif trial.startswith('arm') or trial.startswith('earm'):
- trial = 'arm'
- elif trial.startswith(('powerpc64', 'ppc64')):
- trial = 'ppc64'
- elif trial.startswith(('powerpc', 'ppc')) or trial in {'macppc', 'power macintosh'}:
- trial = 'ppc'
- elif trial in {'amd64', 'x64', 'i86pc'}:
- trial = 'x86_64'
- elif trial in {'sun4u', 'sun4v'}:
- trial = 'sparc64'
- elif trial.startswith('mips'):
- if '64' not in trial:
- trial = 'mips'
- else:
- trial = 'mips64'
- elif trial in {'ip30', 'ip35'}:
- trial = 'mips64'
-
- # On Linux (and maybe others) there can be any mixture of 32/64 bit code in
- # the kernel, Python, system, 32-bit chroot on 64-bit host, etc. The only
- # reliable way to know is to check the compiler defines.
- if trial == 'x86_64':
- if any_compiler_has_define(compilers, '__i386__'):
- trial = 'x86'
- elif trial == 'aarch64':
- if any_compiler_has_define(compilers, '__arm__'):
- trial = 'arm'
- # Add more quirks here as bugs are reported. Keep in sync with detect_cpu()
- # below.
- elif trial == 'parisc64':
- # ATM there is no 64 bit userland for PA-RISC. Thus always
- # report it as 32 bit for simplicity.
- trial = 'parisc'
- elif trial == 'ppc':
- # AIX always returns powerpc, check here for 64-bit
- if any_compiler_has_define(compilers, '__64BIT__'):
- trial = 'ppc64'
- # MIPS64 is able to run MIPS32 code natively, so there is a chance that
- # such mixture mentioned above exists.
- elif trial == 'mips64':
- if compilers and not any_compiler_has_define(compilers, '__mips64'):
- trial = 'mips'
-
- if trial not in known_cpu_families:
- mlog.warning(f'Unknown CPU family {trial!r}, please report this at '
- 'https://github.com/mesonbuild/meson/issues/new with the '
- 'output of `uname -a` and `cat /proc/cpuinfo`')
-
- return trial
-
-def detect_cpu(compilers: CompilersDict) -> str:
- if mesonlib.is_windows():
- trial = detect_windows_arch(compilers)
- elif mesonlib.is_freebsd() or mesonlib.is_netbsd() or mesonlib.is_openbsd() or mesonlib.is_aix():
- trial = platform.processor().lower()
- else:
- trial = platform.machine().lower()
-
- if trial in {'amd64', 'x64', 'i86pc'}:
- trial = 'x86_64'
- if trial == 'x86_64':
- # Same check as above for cpu_family
- if any_compiler_has_define(compilers, '__i386__'):
- trial = 'i686' # All 64 bit cpus have at least this level of x86 support.
- elif trial.startswith('aarch64') or trial.startswith('arm64'):
- # Same check as above for cpu_family
- if any_compiler_has_define(compilers, '__arm__'):
- trial = 'arm'
- else:
- # for aarch64_be
- trial = 'aarch64'
- elif trial.startswith('earm'):
- trial = 'arm'
- elif trial == 'e2k':
- # Make more precise CPU detection for Elbrus platform.
- trial = platform.processor().lower()
- elif trial.startswith('mips'):
- if '64' not in trial:
- trial = 'mips'
- else:
- if compilers and not any_compiler_has_define(compilers, '__mips64'):
- trial = 'mips'
- else:
- trial = 'mips64'
- elif trial == 'ppc':
- # AIX always returns powerpc, check here for 64-bit
- if any_compiler_has_define(compilers, '__64BIT__'):
- trial = 'ppc64'
-
- # Add more quirks here as bugs are reported. Keep in sync with
- # detect_cpu_family() above.
- return trial
-
-KERNEL_MAPPINGS: T.Mapping[str, str] = {'freebsd': 'freebsd',
- 'openbsd': 'openbsd',
- 'netbsd': 'netbsd',
- 'windows': 'nt',
- 'android': 'linux',
- 'linux': 'linux',
- 'cygwin': 'nt',
- 'darwin': 'xnu',
- 'ios': 'xnu',
- 'tvos': 'xnu',
- 'visionos': 'xnu',
- 'watchos': 'xnu',
- 'dragonfly': 'dragonfly',
- 'haiku': 'haiku',
- 'gnu': 'gnu',
- }
-
-def detect_kernel(system: str) -> T.Optional[str]:
- if system == 'sunos':
- # Solaris 5.10 uname doesn't support the -o switch, and illumos started
- # with version 5.11 so shortcut the logic to report 'solaris' in such
- # cases where the version is 5.10 or below.
- if mesonlib.version_compare(platform.uname().release, '<=5.10'):
- return 'solaris'
- # This needs to be /usr/bin/uname because gnu-uname could be installed and
- # won't provide the necessary information
- p, out, _ = Popen_safe(['/usr/bin/uname', '-o'])
- if p.returncode != 0:
- raise MesonException('Failed to run "/usr/bin/uname -o"')
- out = out.lower().strip()
- if out not in {'illumos', 'solaris'}:
- mlog.warning(f'Got an unexpected value for kernel on a SunOS derived platform, expected either "illumos" or "solaris", but got "{out}".'
- "Please open a Meson issue with the OS you're running and the value detected for your kernel.")
- return None
- return out
- return KERNEL_MAPPINGS.get(system, None)
-
-def detect_subsystem(system: str) -> T.Optional[str]:
- if system == 'darwin':
- return 'macos'
- return system
-
-def detect_system() -> str:
- if sys.platform == 'cygwin':
- return 'cygwin'
- return platform.system().lower()
-
-def detect_msys2_arch() -> T.Optional[str]:
- return os.environ.get('MSYSTEM_CARCH', None)
-
-def detect_machine_info(compilers: T.Optional[CompilersDict] = None) -> MachineInfo:
- """Detect the machine we're running on
-
- If compilers are not provided, we cannot know as much. None out those
- fields to avoid accidentally depending on partial knowledge. The
- underlying ''detect_*'' method can be called to explicitly use the
- partial information.
- """
- system = detect_system()
- return MachineInfo(
- system,
- detect_cpu_family(compilers) if compilers is not None else None,
- detect_cpu(compilers) if compilers is not None else None,
- sys.byteorder,
- detect_kernel(system),
- detect_subsystem(system))
-
-# TODO make this compare two `MachineInfo`s purely. How important is the
-# `detect_cpu_family({})` distinction? It is the one impediment to that.
-def machine_info_can_run(machine_info: MachineInfo) -> bool:
- """Whether we can run binaries for this machine on the current machine.
-
- Can almost always run 32-bit binaries on 64-bit natively if the host
- and build systems are the same. We don't pass any compilers to
- detect_cpu_family() here because we always want to know the OS
- architecture, not what the compiler environment tells us.
- """
- if machine_info.system != detect_system():
- return False
- true_build_cpu_family = detect_cpu_family({})
- assert machine_info.cpu_family is not None, 'called on incomplete machine_info'
- return \
- (machine_info.cpu_family == true_build_cpu_family) or \
- ((true_build_cpu_family == 'x86_64') and (machine_info.cpu_family == 'x86')) or \
- ((true_build_cpu_family == 'mips64') and (machine_info.cpu_family == 'mips'))
-
class Environment:
private_dir = 'meson-private'
log_dir = 'meson-logs'
info_dir = 'meson-info'
- def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: coredata.SharedCMDOptions) -> None:
+ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cmdline.SharedCMDOptions) -> None:
self.source_dir = source_dir
# Do not try to create build directories when build_dir is none.
# This reduced mode is used by the --buildoptions introspector
@@ -590,15 +103,15 @@ class Environment:
except coredata.MesonVersionMismatchException as e:
# This is routine, but tell the user the update happened
mlog.log('Regenerating configuration from scratch:', str(e))
- coredata.read_cmd_line_file(self.build_dir, cmd_options)
+ cmdline.read_cmd_line_file(self.build_dir, cmd_options)
self.create_new_coredata(cmd_options)
except MesonException as e:
# If we stored previous command line options, we can recover from
# a broken/outdated coredata.
- if os.path.isfile(coredata.get_cmd_line_file(self.build_dir)):
+ if os.path.isfile(cmdline.get_cmd_line_file(self.build_dir)):
mlog.warning('Regenerating configuration from scratch.', fatal=False)
mlog.log('Reason:', mlog.red(str(e)))
- coredata.read_cmd_line_file(self.build_dir, cmd_options)
+ cmdline.read_cmd_line_file(self.build_dir, cmd_options)
self.create_new_coredata(cmd_options)
else:
raise MesonException(f'{str(e)} Try regenerating using "meson setup --wipe".')
@@ -639,7 +152,12 @@ class Environment:
#
# Note that order matters because of 'buildtype', if it is after
# 'optimization' and 'debug' keys, it override them.
- self.options: T.MutableMapping[OptionKey, ElementaryOptionValues] = collections.OrderedDict()
+ self.options: OptionDict = collections.OrderedDict()
+
+ # Environment variables with the name converted into an OptionKey type.
+ # These have subtly different behavior compared to machine files, so do
+ # not store them in self.options. See _set_default_options_from_env.
+ self.env_opts: OptionDict = {}
self.machinestore = machinefile.MachineFileStore(self.coredata.config_files, self.coredata.cross_files, self.source_dir)
@@ -711,18 +229,17 @@ class Environment:
self.default_cmake = ['cmake']
self.default_pkgconfig = ['pkg-config']
self.wrap_resolver: T.Optional['Resolver'] = None
- # Store a global state of Cargo dependencies
- self.cargo: T.Optional[cargo.Interpreter] = None
def mfilestr2key(self, machine_file_string: str, section: T.Optional[str], section_subproject: T.Optional[str], machine: MachineChoice) -> OptionKey:
key = OptionKey.from_string(machine_file_string)
- assert key.machine == MachineChoice.HOST
if key.subproject:
suggestion = section if section == 'project options' else 'built-in options'
raise MesonException(f'Do not set subproject options in [{section}] section, use [subproject:{suggestion}] instead.')
if section_subproject:
key = key.evolve(subproject=section_subproject)
if machine == MachineChoice.BUILD:
+ if key.machine == MachineChoice.BUILD:
+ mlog.deprecation('Setting build machine options in the native file does not need the "build." prefix', once=True)
return key.evolve(machine=machine)
return key
@@ -756,7 +273,7 @@ class Environment:
for section, values in config.items():
if ':' in section:
- section_subproject, section = section.split(':')
+ section_subproject, section = section.split(':', 1)
else:
section_subproject = ''
if section == 'built-in options':
@@ -773,16 +290,18 @@ class Environment:
# Project options are always for the host machine
key = self.mfilestr2key(strk, section, section_subproject, machine)
self.options[key] = v
+ elif ':' in section:
+ correct_subproject, correct_section = section.split(':')[-2:]
+ raise MesonException(
+ 'Subproject options should always be set as '
+ '`[subproject:section]`, even if the options are from a '
+ 'nested subproject. '
+ f'Replace `[{section_subproject}:{section}]` with `[{correct_subproject}:{correct_section}]`')
def _set_default_options_from_env(self) -> None:
opts: T.List[T.Tuple[str, str]] = (
[(v, f'{k}_args') for k, v in compilers.compilers.CFLAGS_MAPPING.items()] +
- [
- ('PKG_CONFIG_PATH', 'pkg_config_path'),
- ('CMAKE_PREFIX_PATH', 'cmake_prefix_path'),
- ('LDFLAGS', 'ldflags'),
- ('CPPFLAGS', 'cppflags'),
- ]
+ NON_LANG_ENV_OPTIONS
)
env_opts: T.DefaultDict[OptionKey, T.List[str]] = collections.defaultdict(list)
@@ -817,35 +336,32 @@ class Environment:
env_opts[key].extend(p_list)
elif keyname == 'cppflags':
for lang in compilers.compilers.LANGUAGES_USING_CPPFLAGS:
- key = OptionKey(f'{lang}_env_args', machine=for_machine)
+ key = OptionKey(f'{lang}_args', machine=for_machine)
env_opts[key].extend(p_list)
else:
key = OptionKey.from_string(keyname).evolve(machine=for_machine)
- if evar in compilers.compilers.CFLAGS_MAPPING.values():
- # If this is an environment variable, we have to
- # store it separately until the compiler is
- # instantiated, as we don't know whether the
- # compiler will want to use these arguments at link
- # time and compile time (instead of just at compile
- # time) until we're instantiating that `Compiler`
- # object. This is required so that passing
- # `-Dc_args=` on the command line and `$CFLAGS`
- # have subtly different behavior. `$CFLAGS` will be
- # added to the linker command line if the compiler
- # acts as a linker driver, `-Dc_args` will not.
- #
- # We still use the original key as the base here, as
- # we want to inherit the machine and the compiler
- # language
- lang = key.name.split('_', 1)[0]
- key = key.evolve(f'{lang}_env_args')
env_opts[key].extend(p_list)
- # Only store options that are not already in self.options,
- # otherwise we'd override the machine files
- for k, v in env_opts.items():
- if k not in self.options:
- self.options[k] = v
+ # If this is an environment variable, we have to
+ # store it separately until the compiler is
+ # instantiated, as we don't know whether the
+ # compiler will want to use these arguments at link
+ # time and compile time (instead of just at compile
+ # time) until we're instantiating that `Compiler`
+ # object. This is required so that passing
+ # `-Dc_args=` on the command line and `$CFLAGS`
+ # have subtly different behavior. `$CFLAGS` will be
+ # added to the linker command line if the compiler
+ # acts as a linker driver, `-Dc_args` will not.
+ for (_, keyname), for_machine in itertools.product(NON_LANG_ENV_OPTIONS, MachineChoice):
+ key = OptionKey.from_string(keyname).evolve(machine=for_machine)
+ # Only store options that are not already in self.options,
+ # otherwise we'd override the machine files
+ if key in env_opts and key not in self.options:
+ self.options[key] = env_opts[key]
+ del env_opts[key]
+
+ self.env_opts.update(env_opts)
def _set_default_binaries_from_env(self) -> None:
"""Set default binaries from the environment.
@@ -884,7 +400,7 @@ class Environment:
self.properties[for_machine].properties.setdefault(name, p_env)
break
- def create_new_coredata(self, options: coredata.SharedCMDOptions) -> None:
+ def create_new_coredata(self, options: cmdline.SharedCMDOptions) -> None:
# WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during
# build file parsing.
@@ -897,6 +413,17 @@ class Environment:
self.coredata = coredata.CoreData(options, self.scratch_dir, meson_command)
self.first_invocation = True
+ def init_backend_options(self, backend_name: str) -> None:
+ # Only init backend options on first invocation otherwise it would
+ # override values previously set from command line.
+ if not self.first_invocation:
+ return
+
+ self.coredata.init_backend_options(backend_name)
+ for k, v in self.options.items():
+ if self.coredata.optstore.is_backend_option(k):
+ self.coredata.optstore.set_option(k, v)
+
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
return self.coredata.is_cross_build(when_building_for)
@@ -919,25 +446,6 @@ class Environment:
cmd.insert(1, '-u')
return cmd
- def is_header(self, fname: 'mesonlib.FileOrString') -> bool:
- return is_header(fname)
-
- def is_source(self, fname: 'mesonlib.FileOrString') -> bool:
- return is_source(fname)
-
- def is_assembly(self, fname: 'mesonlib.FileOrString') -> bool:
- return is_assembly(fname)
-
- def is_llvm_ir(self, fname: 'mesonlib.FileOrString') -> bool:
- return is_llvm_ir(fname)
-
- def is_object(self, fname: 'mesonlib.FileOrString') -> bool:
- return is_object(fname)
-
- @lru_cache(maxsize=None)
- def is_library(self, fname: mesonlib.FileOrString) -> bool:
- return is_library(fname)
-
def lookup_binary_entry(self, for_machine: MachineChoice, name: str) -> T.Optional[T.List[str]]:
return self.binaries[for_machine].lookup_entry(name)
@@ -1064,3 +572,44 @@ class Environment:
if extra_paths:
env.prepend('PATH', list(extra_paths))
return env
+
+ def add_lang_args(self, lang: str, comp: T.Type['Compiler'],
+ for_machine: MachineChoice) -> None:
+ """Add global language arguments that are needed before compiler/linker detection."""
+ description = f'Extra arguments passed to the {lang}'
+ argkey = OptionKey(f'{lang}_args', machine=for_machine)
+ largkey = OptionKey(f'{lang}_link_args', machine=for_machine)
+
+ comp_args_from_envvar = False
+ comp_options = self.coredata.optstore.get_pending_value(argkey)
+ if comp_options is None:
+ comp_args_from_envvar = True
+ comp_options = self.env_opts.get(argkey, [])
+
+ link_options = self.coredata.optstore.get_pending_value(largkey)
+ if link_options is None:
+ link_options = self.env_opts.get(largkey, [])
+
+ assert isinstance(comp_options, (str, list)), 'for mypy'
+ assert isinstance(link_options, (str, list)), 'for mypy'
+
+ cargs = options.UserStringArrayOption(
+ argkey.name,
+ description + ' compiler',
+ comp_options, split_args=True, allow_dups=True)
+
+ largs = options.UserStringArrayOption(
+ largkey.name,
+ description + ' linker',
+ link_options, split_args=True, allow_dups=True)
+
+ self.coredata.optstore.add_compiler_option(lang, argkey, cargs)
+ self.coredata.optstore.add_compiler_option(lang, largkey, largs)
+
+ if comp.INVOKES_LINKER and comp_args_from_envvar:
+ # If the compiler acts as a linker driver, and we're using the
+ # environment variable flags for both the compiler and linker
+ # arguments, then put the compiler flags in the linker flags as well.
+ # This is how autotools works, and the env vars feature is for
+ # autotools compatibility.
+ largs.extend_value(comp_options)
diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py
index e2ccce4..600798f 100644
--- a/mesonbuild/interpreter/__init__.py
+++ b/mesonbuild/interpreter/__init__.py
@@ -1,12 +1,11 @@
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2021 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
+# Copyright © 2021-2024 Intel Corporation
"""Meson interpreter."""
__all__ = [
'Interpreter',
- 'permitted_dependency_kwargs',
'CompilerHolder',
@@ -30,7 +29,7 @@ __all__ = [
'StringHolder',
]
-from .interpreter import Interpreter, permitted_dependency_kwargs
+from .interpreter import Interpreter
from .compiler import CompilerHolder
from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder,
CustomTargetIndexHolder, MachineHolder, Test,
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 8aeac8a..d5b14af 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -19,11 +19,11 @@ from ..compilers import SUFFIX_TO_LANG, RunResult
from ..compilers.compilers import CompileCheckMode
from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs,
FeatureNew, FeatureNewKwargs, disablerIfNotFound,
- InterpreterException)
+ InterpreterException, InterpreterObject)
from ..interpreterbase.decorators import ContainerTypeInfo, typed_kwargs, KwargInfo, typed_pos_args
from ..options import OptionKey
from .interpreterobjects import (extract_required_kwarg, extract_search_dirs)
-from .type_checking import REQUIRED_KW, in_set_validator, NoneType
+from .type_checking import INCLUDE_DIRECTORIES, REQUIRED_KW, in_set_validator, NoneType
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
@@ -86,7 +86,7 @@ if T.TYPE_CHECKING:
# prepended to the key
header_args: T.List[str]
header_dependencies: T.List[dependencies.Dependency]
- header_include_directories: T.List[build.IncludeDirs]
+ header_include_directories: T.List[T.Union[build.IncludeDirs, str]]
header_no_builtin_args: bool
header_prefix: str
header_required: T.Union[bool, options.UserFeatureOption]
@@ -94,9 +94,9 @@ if T.TYPE_CHECKING:
class PreprocessKW(TypedDict):
output: str
compile_args: T.List[str]
- include_directories: T.List[build.IncludeDirs]
+ include_directories: T.List[T.Union[build.IncludeDirs, str]]
dependencies: T.List[dependencies.Dependency]
- depends: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]
+ depends: T.List[build.BuildTargetTypes]
class _TestMode(enum.Enum):
@@ -110,29 +110,28 @@ class _TestMode(enum.Enum):
class TryRunResultHolder(ObjectHolder['RunResult']):
def __init__(self, res: 'RunResult', interpreter: 'Interpreter'):
super().__init__(res, interpreter)
- self.methods.update({'returncode': self.returncode_method,
- 'compiled': self.compiled_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
@noPosargs
@noKwargs
+ @InterpreterObject.method('returncode')
def returncode_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> int:
return self.held_object.returncode
@noPosargs
@noKwargs
+ @InterpreterObject.method('compiled')
def compiled_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
return self.held_object.compiled
@noPosargs
@noKwargs
+ @InterpreterObject.method('stdout')
def stdout_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.held_object.stdout
@noPosargs
@noKwargs
+ @InterpreterObject.method('stderr')
def stderr_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.held_object.stderr
@@ -149,18 +148,12 @@ _DEPENDENCIES_KW: KwargInfo[T.List['dependencies.Dependency']] = KwargInfo(
listify=True,
default=[],
)
-_DEPENDS_KW: KwargInfo[T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]] = KwargInfo(
+_DEPENDS_KW: KwargInfo[T.List[build.BuildTargetTypes]] = KwargInfo(
'depends',
ContainerTypeInfo(list, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)),
listify=True,
default=[],
)
-_INCLUDE_DIRS_KW: KwargInfo[T.List[build.IncludeDirs]] = KwargInfo(
- 'include_directories',
- ContainerTypeInfo(list, build.IncludeDirs),
- default=[],
- listify=True,
-)
_PREFIX_KW: KwargInfo[str] = KwargInfo(
'prefix',
(str, ContainerTypeInfo(list, str)),
@@ -174,10 +167,10 @@ _WERROR_KW = KwargInfo('werror', bool, default=False, since='1.3.0')
# Many of the compiler methods take this kwarg signature exactly, this allows
# simplifying the `typed_kwargs` calls
-_COMMON_KWS: T.List[KwargInfo] = [_ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW, _PREFIX_KW, _NO_BUILTIN_ARGS_KW]
+_COMMON_KWS: T.List[KwargInfo] = [_ARGS_KW, _DEPENDENCIES_KW, INCLUDE_DIRECTORIES, _PREFIX_KW, _NO_BUILTIN_ARGS_KW]
# Common methods of compiles, links, runs, and similar
-_COMPILES_KWS: T.List[KwargInfo] = [_NAME_KW, _ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW, _NO_BUILTIN_ARGS_KW,
+_COMPILES_KWS: T.List[KwargInfo] = [_NAME_KW, _ARGS_KW, _DEPENDENCIES_KW, INCLUDE_DIRECTORIES, _NO_BUILTIN_ARGS_KW,
_WERROR_KW,
REQUIRED_KW.evolve(since='1.5.0', default=False)]
@@ -190,40 +183,6 @@ class CompilerHolder(ObjectHolder['Compiler']):
def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'):
super().__init__(compiler, interpreter)
self.environment = self.env
- self.methods.update({'compiles': self.compiles_method,
- 'links': self.links_method,
- 'get_id': self.get_id_method,
- 'get_linker_id': self.get_linker_id_method,
- 'compute_int': self.compute_int_method,
- 'sizeof': self.sizeof_method,
- 'get_define': self.get_define_method,
- 'has_define': self.has_define_method,
- 'check_header': self.check_header_method,
- 'has_header': self.has_header_method,
- 'has_header_symbol': self.has_header_symbol_method,
- 'run': self.run_method,
- 'has_function': self.has_function_method,
- 'has_member': self.has_member_method,
- 'has_members': self.has_members_method,
- 'has_type': self.has_type_method,
- 'alignment': self.alignment_method,
- 'version': self.version_method,
- 'cmd_array': self.cmd_array_method,
- 'find_library': self.find_library_method,
- 'has_argument': self.has_argument_method,
- 'has_function_attribute': self.has_func_attribute_method,
- 'get_supported_function_attributes': self.get_supported_function_attributes_method,
- 'has_multi_arguments': self.has_multi_arguments_method,
- 'get_supported_arguments': self.get_supported_arguments_method,
- 'first_supported_argument': self.first_supported_argument_method,
- 'has_link_argument': self.has_link_argument_method,
- 'has_multi_link_arguments': self.has_multi_link_arguments_method,
- 'get_supported_link_arguments': self.get_supported_link_arguments_method,
- 'first_supported_link_argument': self.first_supported_link_argument_method,
- 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
- 'get_argument_syntax': self.get_argument_syntax_method,
- 'preprocess': self.preprocess_method,
- })
@property
def compiler(self) -> 'Compiler':
@@ -254,25 +213,27 @@ class CompilerHolder(ObjectHolder['Compiler']):
@noPosargs
@noKwargs
+ @InterpreterObject.method('version')
def version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.version
@noPosargs
@noKwargs
+ @InterpreterObject.method('cmd_array')
def cmd_array_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.List[str]:
return self.compiler.exelist
def _determine_args(self, kwargs: BaseCompileKW,
mode: CompileCheckMode = CompileCheckMode.LINK) -> T.List[str]:
args: T.List[str] = []
- for i in kwargs['include_directories']:
+ for i in self.interpreter.extract_incdirs(kwargs, strings_since='1.10.0'):
for idir in i.to_string_list(self.environment.get_source_dir(), self.environment.get_build_dir()):
args.extend(self.compiler.get_include_args(idir, False))
if not kwargs['no_builtin_args']:
- args += self.compiler.get_option_compile_args(None, self.interpreter.environment, self.subproject)
- args += self.compiler.get_option_std_args(None, self.interpreter.environment, self.subproject)
+ args += self.compiler.get_option_compile_args(None, self.subproject)
+ args += self.compiler.get_option_std_args(None, self.subproject)
if mode is CompileCheckMode.LINK:
- args.extend(self.compiler.get_option_link_args(None, self.interpreter.environment, self.subproject))
+ args.extend(self.compiler.get_option_link_args(None, self.subproject))
if kwargs.get('werror', False):
args.extend(self.compiler.get_werror_args())
args.extend(kwargs['args'])
@@ -289,10 +250,11 @@ class CompilerHolder(ObjectHolder['Compiler']):
_ARGS_KW,
_DEPENDENCIES_KW,
)
+ @InterpreterObject.method('alignment')
def alignment_method(self, args: T.Tuple[str], kwargs: 'AlignmentKw') -> int:
typename = args[0]
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross)
- result, cached = self.compiler.alignment(typename, kwargs['prefix'], self.environment,
+ result, cached = self.compiler.alignment(typename, kwargs['prefix'],
extra_args=kwargs['args'],
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
@@ -302,6 +264,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.run', (str, mesonlib.File))
@typed_kwargs('compiler.run', *_COMPILES_KWS)
+ @InterpreterObject.method('run')
def run_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> 'RunResult':
if self.compiler.language not in {'d', 'c', 'cpp', 'objc', 'objcpp', 'fortran'}:
FeatureNew.single_use(f'compiler.run for {self.compiler.get_display_language()} language',
@@ -321,8 +284,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
code.rel_to_builddir(self.environment.source_dir))
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False, endl=None)
- result = self.compiler.run(code, self.environment, extra_args=extra_args,
- dependencies=deps)
+ result = self.compiler.run(code, extra_args=extra_args, dependencies=deps)
if required and result.returncode != 0:
raise InterpreterException(f'Could not run {testname if testname else "code"}')
@@ -338,26 +300,30 @@ class CompilerHolder(ObjectHolder['Compiler']):
@noPosargs
@noKwargs
+ @InterpreterObject.method('get_id')
def get_id_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.get_id()
@noPosargs
@noKwargs
@FeatureNew('compiler.get_linker_id', '0.53.0')
+ @InterpreterObject.method('get_linker_id')
def get_linker_id_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.get_linker_id()
@noPosargs
@noKwargs
+ @InterpreterObject.method('symbols_have_underscore_prefix')
def symbols_have_underscore_prefix_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
'''
Check if the compiler prefixes _ (underscore) to global C symbols
See: https://en.wikipedia.org/wiki/Name_mangling#C
'''
- return self.compiler.symbols_have_underscore_prefix(self.environment)
+ return self.compiler.symbols_have_underscore_prefix()
@typed_pos_args('compiler.has_member', str, str)
@typed_kwargs('compiler.has_member', _HAS_REQUIRED_KW, *_COMMON_KWS)
+ @InterpreterObject.method('has_member')
def has_member_method(self, args: T.Tuple[str, str], kwargs: 'HasKW') -> bool:
typename, membername = args
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
@@ -367,9 +333,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
had, cached = self.compiler.has_members(typename, [membername], kwargs['prefix'],
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
+ extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if required and not had:
raise InterpreterException(f'{self.compiler.get_display_language()} member {membername!r} of type {typename!r} not usable')
@@ -383,6 +347,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.has_members', str, varargs=str, min_varargs=1)
@typed_kwargs('compiler.has_members', _HAS_REQUIRED_KW, *_COMMON_KWS)
+ @InterpreterObject.method('has_members')
def has_members_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'HasKW') -> bool:
typename, membernames = args
members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
@@ -393,9 +358,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
had, cached = self.compiler.has_members(typename, membernames, kwargs['prefix'],
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
+ extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if required and not had:
# print members as array: ['member1', 'member2']
@@ -410,6 +373,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.has_function', str)
@typed_kwargs('compiler.has_function', _HAS_REQUIRED_KW, *_COMMON_KWS)
+ @InterpreterObject.method('has_function')
def has_function_method(self, args: T.Tuple[str], kwargs: 'HasKW') -> bool:
funcname = args[0]
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
@@ -418,7 +382,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
return False
extra_args = self._determine_args(kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False)
- had, cached = self.compiler.has_function(funcname, kwargs['prefix'], self.environment,
+ had, cached = self.compiler.has_function(funcname, kwargs['prefix'],
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
@@ -433,6 +397,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.has_type', str)
@typed_kwargs('compiler.has_type', _HAS_REQUIRED_KW, *_COMMON_KWS)
+ @InterpreterObject.method('has_type')
def has_type_method(self, args: T.Tuple[str], kwargs: 'HasKW') -> bool:
typename = args[0]
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
@@ -441,7 +406,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
return False
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
- had, cached = self.compiler.has_type(typename, kwargs['prefix'], self.environment,
+ had, cached = self.compiler.has_type(typename, kwargs['prefix'],
extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if required and not had:
@@ -462,24 +427,25 @@ class CompilerHolder(ObjectHolder['Compiler']):
KwargInfo('guess', (int, NoneType)),
*_COMMON_KWS,
)
+ @InterpreterObject.method('compute_int')
def compute_int_method(self, args: T.Tuple[str], kwargs: 'ComputeIntKW') -> int:
expression = args[0]
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross)
res = self.compiler.compute_int(expression, kwargs['low'], kwargs['high'],
kwargs['guess'], kwargs['prefix'],
- self.environment, extra_args=extra_args,
- dependencies=deps)
+ extra_args=extra_args, dependencies=deps)
mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
return res
@typed_pos_args('compiler.sizeof', str)
@typed_kwargs('compiler.sizeof', *_COMMON_KWS)
+ @InterpreterObject.method('sizeof')
def sizeof_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> int:
element = args[0]
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross)
- esize, cached = self.compiler.sizeof(element, kwargs['prefix'], self.environment,
+ esize, cached = self.compiler.sizeof(element, kwargs['prefix'],
extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
mlog.log('Checking for size of',
@@ -489,13 +455,13 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.get_define', '0.40.0')
@typed_pos_args('compiler.get_define', str)
@typed_kwargs('compiler.get_define', *_COMMON_KWS)
+ @InterpreterObject.method('get_define')
def get_define_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> str:
element = args[0]
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
- value, cached = self.compiler.get_define(element, kwargs['prefix'], self.environment,
- extra_args=extra_args,
- dependencies=deps)
+ value, cached = self.compiler.get_define(element, kwargs['prefix'],
+ extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
value_msg = '(undefined)' if value is None else value
mlog.log('Fetching value of define', mlog.bold(element, True), msg, value_msg, cached_msg)
@@ -504,13 +470,13 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.has_define', '1.3.0')
@typed_pos_args('compiler.has_define', str)
@typed_kwargs('compiler.has_define', *_COMMON_KWS)
+ @InterpreterObject.method('has_define')
def has_define_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
define_name = args[0]
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], endl=None)
- value, cached = self.compiler.get_define(define_name, kwargs['prefix'], self.environment,
- extra_args=extra_args,
- dependencies=deps)
+ value, cached = self.compiler.get_define(define_name, kwargs['prefix'],
+ extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
h = mlog.green('YES') if value is not None else mlog.red('NO')
mlog.log('Checking if define', mlog.bold(define_name, True), msg, 'exists:', h, cached_msg)
@@ -519,6 +485,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.compiles', (str, mesonlib.File))
@typed_kwargs('compiler.compiles', *_COMPILES_KWS)
+ @InterpreterObject.method('compiles')
def compiles_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> bool:
code = args[0]
testname = kwargs['name']
@@ -538,7 +505,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
code.absolute_path(self.environment.source_dir, self.environment.build_dir))
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], endl=None)
- result, cached = self.compiler.compiles(code, self.environment,
+ result, cached = self.compiler.compiles(code,
extra_args=extra_args,
dependencies=deps)
if required and not result:
@@ -555,6 +522,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.links', (str, mesonlib.File))
@typed_kwargs('compiler.links', *_COMPILES_KWS)
+ @InterpreterObject.method('links')
def links_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> bool:
code = args[0]
testname = kwargs['name']
@@ -587,7 +555,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False, endl=None)
- result, cached = self.compiler.links(code, self.environment,
+ result, cached = self.compiler.links(code,
compiler=compiler,
extra_args=extra_args,
dependencies=deps)
@@ -606,6 +574,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.check_header', '0.47.0')
@typed_pos_args('compiler.check_header', str)
@typed_kwargs('compiler.check_header', *_HEADER_KWS)
+ @InterpreterObject.method('check_header')
def check_header_method(self, args: T.Tuple[str], kwargs: 'HeaderKW') -> bool:
hname = args[0]
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
@@ -614,7 +583,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
return False
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
- haz, cached = self.compiler.check_header(hname, kwargs['prefix'], self.environment,
+ haz, cached = self.compiler.check_header(hname, kwargs['prefix'],
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
@@ -634,7 +603,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
return False
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
- haz, cached = self.compiler.has_header(hname, kwargs['prefix'], self.environment,
+ haz, cached = self.compiler.has_header(hname, kwargs['prefix'],
extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if required and not haz:
@@ -648,11 +617,13 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.has_header', str)
@typed_kwargs('compiler.has_header', *_HEADER_KWS)
+ @InterpreterObject.method('has_header')
def has_header_method(self, args: T.Tuple[str], kwargs: 'HeaderKW') -> bool:
return self._has_header_impl(args[0], kwargs)
@typed_pos_args('compiler.has_header_symbol', str, str)
@typed_kwargs('compiler.has_header_symbol', *_HEADER_KWS)
+ @InterpreterObject.method('has_header_symbol')
def has_header_symbol_method(self, args: T.Tuple[str, str], kwargs: 'HeaderKW') -> bool:
hname, symbol = args
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
@@ -661,7 +632,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
return False
extra_args = functools.partial(self._determine_args, kwargs)
deps, msg = self._determine_dependencies(kwargs['dependencies'])
- haz, cached = self.compiler.has_header_symbol(hname, symbol, kwargs['prefix'], self.environment,
+ haz, cached = self.compiler.has_header_symbol(hname, symbol, kwargs['prefix'],
extra_args=extra_args,
dependencies=deps)
if required and not haz:
@@ -692,6 +663,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
KwargInfo('dirs', ContainerTypeInfo(list, str), listify=True, default=[]),
*(k.evolve(name=f'header_{k.name}') for k in _HEADER_KWS)
)
+ @InterpreterObject.method('find_library')
def find_library_method(self, args: T.Tuple[str], kwargs: 'FindLibraryKW') -> 'dependencies.ExternalLibrary':
# TODO add dependencies support?
libname = args[0]
@@ -701,13 +673,15 @@ class CompilerHolder(ObjectHolder['Compiler']):
mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
return self.notfound_library(libname)
+ include_directories = self.interpreter.extract_incdirs(kwargs, key='header_include_directories', strings_since='1.10.0')
+
# This could be done with a comprehension, but that confuses the type
# checker, and having it check this seems valuable
has_header_kwargs: 'HeaderKW' = {
'required': required,
'args': kwargs['header_args'],
'dependencies': kwargs['header_dependencies'],
- 'include_directories': kwargs['header_include_directories'],
+ 'include_directories': include_directories,
'prefix': kwargs['header_prefix'],
'no_builtin_args': kwargs['header_no_builtin_args'],
}
@@ -726,7 +700,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
libtype = mesonlib.LibType.PREFER_STATIC
else:
libtype = mesonlib.LibType.PREFER_SHARED
- linkargs = self.compiler.find_library(libname, self.environment, search_dirs, libtype)
+ linkargs = self.compiler.find_library(libname, search_dirs, libtype)
if required and not linkargs:
if libtype == mesonlib.LibType.PREFER_SHARED:
libtype_s = 'shared or static'
@@ -759,7 +733,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
mlog.log(*logargs)
return False
test = self.compiler.has_multi_link_arguments if mode is _TestMode.LINKER else self.compiler.has_multi_arguments
- result, cached = test(arguments, self.environment)
+ result, cached = test(arguments)
if required and not result:
logargs += ['not usable']
raise InterpreterException(*logargs)
@@ -772,12 +746,14 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.has_argument', str)
@typed_kwargs('compiler.has_argument', _HAS_REQUIRED_KW)
+ @InterpreterObject.method('has_argument')
def has_argument_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl([args[0]], kwargs=kwargs)
@typed_pos_args('compiler.has_multi_arguments', varargs=str)
@typed_kwargs('compiler.has_multi_arguments', _HAS_REQUIRED_KW)
@FeatureNew('compiler.has_multi_arguments', '0.37.0')
+ @InterpreterObject.method('has_multi_arguments')
def has_multi_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl(args[0], kwargs=kwargs)
@@ -788,6 +764,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
KwargInfo('checked', str, default='off', since='0.59.0',
validator=in_set_validator({'warn', 'require', 'off'})),
)
+ @InterpreterObject.method('get_supported_arguments')
def get_supported_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'GetSupportedArgumentKw') -> T.List[str]:
supported_args: T.List[str] = []
checked = kwargs['checked']
@@ -805,6 +782,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@noKwargs
@typed_pos_args('compiler.first_supported_argument', varargs=str)
+ @InterpreterObject.method('first_supported_argument')
def first_supported_argument_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
for arg in args[0]:
if self._has_argument_impl([arg]):
@@ -816,18 +794,21 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.has_link_argument', '0.46.0')
@typed_pos_args('compiler.has_link_argument', str)
@typed_kwargs('compiler.has_link_argument', _HAS_REQUIRED_KW)
+ @InterpreterObject.method('has_link_argument')
def has_link_argument_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl([args[0]], mode=_TestMode.LINKER, kwargs=kwargs)
@FeatureNew('compiler.has_multi_link_argument', '0.46.0')
@typed_pos_args('compiler.has_multi_link_argument', varargs=str)
@typed_kwargs('compiler.has_multi_link_argument', _HAS_REQUIRED_KW)
+ @InterpreterObject.method('has_multi_link_arguments')
def has_multi_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl(args[0], mode=_TestMode.LINKER, kwargs=kwargs)
@FeatureNew('compiler.get_supported_link_arguments', '0.46.0')
@noKwargs
@typed_pos_args('compiler.get_supported_link_arguments', varargs=str)
+ @InterpreterObject.method('get_supported_link_arguments')
def get_supported_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
supported_args: T.List[str] = []
for arg in args[0]:
@@ -835,9 +816,10 @@ class CompilerHolder(ObjectHolder['Compiler']):
supported_args.append(arg)
return supported_args
- @FeatureNew('compiler.first_supported_link_argument_method', '0.46.0')
+ @FeatureNew('compiler.first_supported_link_argument', '0.46.0')
@noKwargs
@typed_pos_args('compiler.first_supported_link_argument', varargs=str)
+ @InterpreterObject.method('first_supported_link_argument')
def first_supported_link_argument_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
for arg in args[0]:
if self._has_argument_impl([arg], mode=_TestMode.LINKER):
@@ -857,7 +839,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
logargs += ['skipped: feature', mlog.bold(feature), 'disabled']
mlog.log(*logargs)
return False
- had, cached = self.compiler.has_func_attribute(attr, self.environment)
+ had, cached = self.compiler.has_func_attribute(attr)
if required and not had:
logargs += ['not usable']
raise InterpreterException(*logargs)
@@ -871,18 +853,21 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.has_function_attribute', '0.48.0')
@typed_pos_args('compiler.has_function_attribute', str)
@typed_kwargs('compiler.has_function_attribute', _HAS_REQUIRED_KW)
+ @InterpreterObject.method('has_function_attribute')
def has_func_attribute_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_function_attribute_impl(args[0], kwargs)
@FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
@noKwargs
@typed_pos_args('compiler.get_supported_function_attributes', varargs=str)
+ @InterpreterObject.method('get_supported_function_attributes')
def get_supported_function_attributes_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
return [a for a in args[0] if self._has_function_attribute_impl(a)]
- @FeatureNew('compiler.get_argument_syntax_method', '0.49.0')
+ @FeatureNew('compiler.get_argument_syntax', '0.49.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('get_argument_syntax')
def get_argument_syntax_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.get_argument_syntax()
@@ -893,10 +878,11 @@ class CompilerHolder(ObjectHolder['Compiler']):
'compiler.preprocess',
KwargInfo('output', str, default='@PLAINNAME@.i'),
KwargInfo('compile_args', ContainerTypeInfo(list, str), listify=True, default=[]),
- _INCLUDE_DIRS_KW,
+ INCLUDE_DIRECTORIES,
_DEPENDENCIES_KW.evolve(since='1.1.0'),
_DEPENDS_KW.evolve(since='1.4.0'),
)
+ @InterpreterObject.method('preprocess')
def preprocess_method(self, args: T.Tuple[T.List['mesonlib.FileOrString']], kwargs: 'PreprocessKW') -> T.List[build.CustomTargetIndex]:
compiler = self.compiler.get_preprocessor()
_sources: T.List[mesonlib.File] = self.interpreter.source_strings_to_files(args[0])
@@ -920,7 +906,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
compiler,
self.interpreter.backend,
kwargs['compile_args'],
- kwargs['include_directories'],
+ self.interpreter.extract_incdirs(kwargs, strings_since='1.10.0'),
kwargs['dependencies'],
kwargs['depends'])
self.interpreter.add_target(tg.name, tg)
diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py
index 0ebfe3b..baf5ea3 100644
--- a/mesonbuild/interpreter/dependencyfallbacks.py
+++ b/mesonbuild/interpreter/dependencyfallbacks.py
@@ -4,14 +4,11 @@
from __future__ import annotations
-import copy
-
-from .interpreterobjects import extract_required_kwarg
from .. import mlog
from .. import dependencies
from .. import build
from ..wrap import WrapMode
-from ..mesonlib import extract_as_list, stringlistify, version_compare_many, listify
+from ..mesonlib import stringlistify, version_compare_many, MachineChoice
from ..options import OptionKey
from ..dependencies import Dependency, DependencyException, NotFoundDependency
from ..interpreterbase import (MesonInterpreterObject, FeatureNew,
@@ -19,17 +16,22 @@ from ..interpreterbase import (MesonInterpreterObject, FeatureNew,
import typing as T
if T.TYPE_CHECKING:
+ from typing_extensions import TypeAlias
from .interpreter import Interpreter
- from ..interpreterbase import TYPE_nkwargs, TYPE_nvar
+ from .kwargs import DoSubproject
+ from ..dependencies.base import DependencyObjectKWs
+ from ..options import ElementaryOptionValues, OptionDict
from .interpreterobjects import SubprojectHolder
+ CandidateType: TypeAlias = T.Tuple[T.Callable[[DependencyObjectKWs, str, DoSubproject], T.Optional[Dependency]], str]
+
class DependencyFallbacksHolder(MesonInterpreterObject):
def __init__(self,
interpreter: 'Interpreter',
names: T.List[str],
allow_fallback: T.Optional[bool] = None,
- default_options: T.Optional[T.Dict[str, str]] = None) -> None:
+ default_options: T.Optional[T.Dict[OptionKey, ElementaryOptionValues]] = None) -> None:
super().__init__(subproject=interpreter.subproject)
self.interpreter = interpreter
self.subproject = interpreter.subproject
@@ -40,7 +42,7 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
self.allow_fallback = allow_fallback
self.subproject_name: T.Optional[str] = None
self.subproject_varname: T.Optional[str] = None
- self.subproject_kwargs = {'default_options': default_options or {}}
+ self.default_options = default_options or {}
self.names: T.List[str] = []
self.forcefallback: bool = False
self.nofallback: bool = False
@@ -80,36 +82,33 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
self.subproject_name = subp_name
self.subproject_varname = varname
- def _do_dependency_cache(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
- name = func_args[0]
+ def _do_dependency_cache(self, kwargs: DependencyObjectKWs, name: str, func_kwargs: DoSubproject) -> T.Optional[Dependency]:
cached_dep = self._get_cached_dep(name, kwargs)
if cached_dep:
self._verify_fallback_consistency(cached_dep)
return cached_dep
- def _do_dependency(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
+ def _do_dependency(self, kwargs: DependencyObjectKWs, name: str, func_kwargs: DoSubproject) -> T.Optional[Dependency]:
# Note that there is no df.dependency() method, this is called for names
# given as positional arguments to dependency_fallbacks(name1, ...).
# We use kwargs from the dependency() function, for things like version,
# module, etc.
- name = func_args[0]
self._handle_featurenew_dependencies(name)
dep = dependencies.find_external_dependency(name, self.environment, kwargs)
if dep.found():
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ for_machine = kwargs.get('native', MachineChoice.HOST)
identifier = dependencies.get_dep_identifier(name, kwargs)
self.coredata.deps[for_machine].put(identifier, dep)
return dep
return None
- def _do_existing_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
- subp_name = func_args[0]
+ def _do_existing_subproject(self, kwargs: DependencyObjectKWs, subp_name: str, func_kwargs: DoSubproject) -> T.Optional[Dependency]:
varname = self.subproject_varname
if subp_name and self._get_subproject(subp_name):
return self._get_subproject_dep(subp_name, varname, kwargs)
return None
- def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
+ def _do_subproject(self, kwargs: DependencyObjectKWs, name: str, func_kwargs: DoSubproject) -> T.Optional[Dependency]:
if self.forcefallback:
mlog.log('Looking for a fallback subproject for the dependency',
mlog.bold(self._display_name), 'because:\nUse of fallback dependencies is forced.')
@@ -124,21 +123,16 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
# dependency('foo', static: true) should implicitly add
# default_options: ['default_library=static']
static = kwargs.get('static')
- default_options = func_kwargs.get('default_options', {})
- if static is not None and 'default_library' not in default_options:
+ forced_options: OptionDict = {}
+ if static is not None:
default_library = 'static' if static else 'shared'
mlog.log(f'Building fallback subproject with default_library={default_library}')
- default_options = copy.copy(default_options)
- default_options['default_library'] = default_library
- func_kwargs['default_options'] = default_options
+ forced_options[OptionKey('default_library')] = default_library
# Configure the subproject
subp_name = self.subproject_name
varname = self.subproject_varname
- func_kwargs.setdefault('version', [])
- if 'default_options' in kwargs and isinstance(kwargs['default_options'], str):
- func_kwargs['default_options'] = listify(kwargs['default_options'])
- self.interpreter.do_subproject(subp_name, func_kwargs)
+ self.interpreter.do_subproject(subp_name, func_kwargs, forced_options=forced_options)
return self._get_subproject_dep(subp_name, varname, kwargs)
def _get_subproject(self, subp_name: str) -> T.Optional[SubprojectHolder]:
@@ -147,7 +141,7 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
return sub
return None
- def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
+ def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: DependencyObjectKWs) -> T.Optional[Dependency]:
# Verify the subproject is found
subproject = self._get_subproject(subp_name)
if not subproject:
@@ -210,12 +204,12 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
mlog.normal_cyan(found) if found else None)
return var_dep
- def _get_cached_dep(self, name: str, kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
+ def _get_cached_dep(self, name: str, kwargs: DependencyObjectKWs) -> T.Optional[Dependency]:
# Unlike other methods, this one returns not-found dependency instead
# of None in the case the dependency is cached as not-found, or if cached
# version does not match. In that case we don't want to continue with
# other candidates.
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ for_machine = kwargs.get('native', MachineChoice.HOST)
identifier = dependencies.get_dep_identifier(name, kwargs)
wanted_vers = stringlistify(kwargs.get('version', []))
@@ -298,35 +292,34 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
return True
return not (found == 'undefined' or not version_compare_many(found, wanted)[0])
- def _get_candidates(self) -> T.List[T.Tuple[T.Callable[[TYPE_nkwargs, TYPE_nvar, TYPE_nkwargs], T.Optional[Dependency]], TYPE_nvar, TYPE_nkwargs]]:
- candidates = []
+ def _get_candidates(self) -> T.List[CandidateType]:
+ candidates: T.List[CandidateType] = []
# 1. check if any of the names is cached already.
for name in self.names:
- candidates.append((self._do_dependency_cache, [name], {}))
+ candidates.append((self._do_dependency_cache, name))
# 2. check if the subproject fallback has already been configured.
if self.subproject_name:
- candidates.append((self._do_existing_subproject, [self.subproject_name], self.subproject_kwargs))
+ candidates.append((self._do_existing_subproject, self.subproject_name))
# 3. check external dependency if we are not forced to use subproject
if not self.forcefallback or not self.subproject_name:
for name in self.names:
- candidates.append((self._do_dependency, [name], {}))
+ candidates.append((self._do_dependency, name))
# 4. configure the subproject
if self.subproject_name:
- candidates.append((self._do_subproject, [self.subproject_name], self.subproject_kwargs))
+ candidates.append((self._do_subproject, self.subproject_name))
return candidates
- def lookup(self, kwargs: TYPE_nkwargs, force_fallback: bool = False) -> Dependency:
- mods = extract_as_list(kwargs, 'modules')
+ def lookup(self, kwargs: DependencyObjectKWs, force_fallback: bool = False) -> Dependency:
+ mods = kwargs.get('modules', [])
if mods:
self._display_name += ' (modules: {})'.format(', '.join(str(i) for i in mods))
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Dependency', mlog.bold(self._display_name), 'skipped: feature', mlog.bold(feature), 'disabled')
- return self._notfound_dependency()
+ required = kwargs.get('required', True)
# Check if usage of the subproject fallback is forced
- wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode')))
+ _wm = self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))
+ assert isinstance(_wm, str), 'for mypy'
+ wrap_mode = WrapMode.from_string(_wm)
force_fallback_for = self.coredata.optstore.get_value_for(OptionKey('force_fallback_for'))
assert isinstance(force_fallback_for, list), 'for mypy'
self.nofallback = wrap_mode == WrapMode.nofallback
@@ -359,15 +352,21 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
# Try all candidates, only the last one is really required.
last = len(candidates) - 1
for i, item in enumerate(candidates):
- func, func_args, func_kwargs = item
- func_kwargs['required'] = required and (i == last)
+ func, name = item
kwargs['required'] = required and (i == last)
- dep = func(kwargs, func_args, func_kwargs)
+ func_kwargs: DoSubproject = {
+ 'required': kwargs['required'],
+ 'cmake_options': [],
+ 'default_options': self.default_options,
+ 'options': None,
+ 'version': [],
+ }
+ dep = func(kwargs, name, func_kwargs)
if dep and dep.found():
# Override this dependency to have consistent results in subsequent
# dependency lookups.
for name in self.names:
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ for_machine = kwargs.get('native', MachineChoice.HOST)
identifier = dependencies.get_dep_identifier(name, kwargs)
if identifier not in self.build.dependency_overrides[for_machine]:
self.build.dependency_overrides[for_machine][identifier] = \
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index abdc889..e3d321e 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -54,7 +54,7 @@ from .type_checking import (
CT_BUILD_BY_DEFAULT,
CT_INPUT_KW,
CT_INSTALL_DIR_KW,
- _EXCLUSIVE_EXECUTABLE_KWS,
+ EXCLUSIVE_EXECUTABLE_KWS,
EXECUTABLE_KWS,
JAR_KWS,
LIBRARY_KWS,
@@ -62,6 +62,7 @@ from .type_checking import (
OUTPUT_KW,
DEFAULT_OPTIONS,
DEPENDENCIES_KW,
+ DEPENDENCY_KWS,
DEPENDS_KW,
DEPEND_FILES_KW,
DEPFILE_KW,
@@ -108,24 +109,25 @@ import typing as T
import textwrap
import importlib
import copy
+import itertools
if T.TYPE_CHECKING:
from typing_extensions import Literal
+ from .. import cargo
from . import kwargs as kwtypes
from ..backend.backends import Backend
from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs
+ from ..options import OptionDict
from ..programs import OverrideProgram
from .type_checking import SourcesVarargsType
# Input source types passed to Targets
- SourceInputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget, build.BothLibraries,
- build.CustomTargetIndex, build.CustomTarget, build.GeneratedList,
- build.ExtractedObjects, str]
+ SourceInputs = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.BuildTarget,
+ build.BothLibraries, build.ExtractedObjects]
# Input source types passed to the build.Target classes
- SourceOutputs = T.Union[mesonlib.File, build.GeneratedList,
- build.BuildTarget, build.CustomTargetIndex, build.CustomTarget,
- build.ExtractedObjects, build.GeneratedList, build.StructuredSources]
+ SourceOutputs = T.Union[mesonlib.File, build.GeneratedTypes, build.BuildTarget,
+ build.ExtractedObjects, build.StructuredSources]
BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]
@@ -234,28 +236,7 @@ class InterpreterRuleRelaxation(Enum):
'''
ALLOW_BUILD_DIR_FILE_REFERENCES = 1
-
-permitted_dependency_kwargs = {
- 'allow_fallback',
- 'cmake_args',
- 'cmake_module_path',
- 'cmake_package_version',
- 'components',
- 'default_options',
- 'fallback',
- 'include_type',
- 'language',
- 'main',
- 'method',
- 'modules',
- 'native',
- 'not_found_message',
- 'optional_modules',
- 'private_headers',
- 'required',
- 'static',
- 'version',
-}
+ CARGO_SUBDIR = 2
implicit_check_false_warning = """You should add the boolean check kwarg to the run_command call.
It currently defaults to false,
@@ -270,15 +251,17 @@ class Interpreter(InterpreterBase, HoldableObject):
subproject: str = '',
subdir: str = '',
subproject_dir: str = 'subprojects',
- default_project_options: T.Optional[T.Dict[OptionKey, str]] = None,
+ invoker_method_default_options: T.Optional[OptionDict] = None,
ast: T.Optional[mparser.CodeBlockNode] = None,
relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None,
user_defined_options: T.Optional[coredata.SharedCMDOptions] = None,
+ cargo: T.Optional[cargo.Interpreter] = None,
) -> None:
super().__init__(_build.environment.get_source_dir(), subdir, subproject, subproject_dir, _build.environment)
self.active_projectname = ''
self.build = _build
self.backend = backend
+ self.cargo = cargo
self.summary: T.Dict[str, 'Summary'] = {}
self.modules: T.Dict[str, NewExtensionModule] = {}
self.relaxations = relaxations or set()
@@ -295,13 +278,12 @@ class Interpreter(InterpreterBase, HoldableObject):
self.subproject_stack: T.List[str] = []
self.configure_file_outputs: T.Dict[str, int] = {}
# Passed from the outside, only used in subprojects.
- if default_project_options:
- self.default_project_options = default_project_options if isinstance(default_project_options, str) else default_project_options.copy()
- if isinstance(default_project_options, dict):
- pass
+ if invoker_method_default_options:
+ assert isinstance(invoker_method_default_options, dict)
+ self.invoker_method_default_options = invoker_method_default_options
else:
- self.default_project_options = {}
- self.project_default_options: T.List[str] = []
+ self.invoker_method_default_options = {}
+ self.project_default_options: OptionDict = {}
self.build_func_dict()
self.build_holder_map()
self.user_defined_options = user_defined_options
@@ -312,12 +294,23 @@ class Interpreter(InterpreterBase, HoldableObject):
def __getnewargs_ex__(self) -> T.Tuple[T.Tuple[object], T.Dict[str, object]]:
raise MesonBugException('This class is unpicklable')
+ def load_root_cargo_lock_file(self) -> None:
+ cargo_lock = os.path.join(self.source_root, self.subdir, 'Cargo.lock')
+ if not os.path.isfile(cargo_lock):
+ return
+ from .. import cargo
+ try:
+ self.cargo = cargo.Interpreter(self.environment, self.subdir, self.subproject_dir)
+ except cargo.TomlImplementationMissing as e:
+ # error delayed to actual usage of a Cargo subproject
+ mlog.warning(f'cannot load Cargo.lock: {e}', fatal=False)
+
def _redetect_machines(self) -> None:
# Re-initialize machine descriptions. We can do a better job now because we
# have the compilers needed to gain more knowledge, so wipe out old
# inference and start over.
machines = self.build.environment.machines.miss_defaulting()
- machines.build = environment.detect_machine_info(self.coredata.compilers.build)
+ machines.build = envconfig.detect_machine_info(self.coredata.compilers.build)
self.build.environment.machines = machines.default_missing()
assert self.build.environment.machines.build.cpu is not None
assert self.build.environment.machines.host.cpu is not None
@@ -426,6 +419,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.Generator: OBJ.GeneratorHolder,
build.GeneratedList: OBJ.GeneratedListHolder,
build.ExtractedObjects: OBJ.GeneratedObjectsHolder,
+ build.OverrideExecutable: OBJ.OverrideExecutableHolder,
build.RunTarget: OBJ.RunTargetHolder,
build.AliasTarget: OBJ.AliasTargetHolder,
build.Headers: OBJ.HeadersHolder,
@@ -521,8 +515,10 @@ class Interpreter(InterpreterBase, HoldableObject):
if isinstance(val, mparser.StringNode):
self.handle_meson_version(val.value, val)
- def get_build_def_files(self) -> mesonlib.OrderedSet[str]:
- return self.build_def_files
+ def get_build_def_files(self) -> T.List[str]:
+ if self.cargo:
+ self.build_def_files.update(self.cargo.get_build_def_files())
+ return list(self.build_def_files)
def add_build_def_file(self, f: mesonlib.FileOrString) -> None:
# Use relative path for files within source directory, and absolute path
@@ -575,8 +571,7 @@ class Interpreter(InterpreterBase, HoldableObject):
continue
if len(di) == 1:
FeatureNew.single_use('stdlib without variable name', '0.56.0', self.subproject, location=self.current_node)
- kwargs = {'native': for_machine is MachineChoice.BUILD,
- }
+ kwargs: dependencies.base.DependencyObjectKWs = {'native': for_machine}
name = l + '_stdlib'
df = DependencyFallbacksHolder(self, [name])
df.set_fallback(di)
@@ -690,7 +685,7 @@ class Interpreter(InterpreterBase, HoldableObject):
def func_declare_dependency(self, node: mparser.BaseNode, args: T.List[TYPE_var],
kwargs: kwtypes.FuncDeclareDependency) -> dependencies.Dependency:
deps = kwargs['dependencies']
- incs = self.extract_incdirs(kwargs)
+ incs = self.extract_incdirs(kwargs, strings_since='0.50.0')
libs = kwargs['link_with']
libs_whole = kwargs['link_whole']
objects = kwargs['objects']
@@ -868,17 +863,27 @@ class Interpreter(InterpreterBase, HoldableObject):
self.subprojects[subp_name] = sub
return sub
- def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None) -> SubprojectHolder:
+ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None,
+ forced_options: T.Optional[OptionDict] = None) -> SubprojectHolder:
if subp_name == 'sub_static':
pass
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
- assert feature, 'for mypy'
mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
return self.disabled_subproject(subp_name, disabled_feature=feature)
default_options = kwargs['default_options']
+ # This in practice is only used for default_library. forced_options is the
+ # only case in which a meson.build file overrides the machine file or the
+ # command line.
+ if forced_options:
+ for k, v in forced_options.items():
+ # FIXME: this should have no business poking at augments[],
+ # but set_option() does not do what we want
+ self.coredata.optstore.augments[k.evolve(subproject=subp_name)] = v
+ default_options = {**forced_options, **default_options}
+
if subp_name == '':
raise InterpreterException('Subproject name must not be empty.')
if subp_name[0] == '.':
@@ -930,7 +935,8 @@ class Interpreter(InterpreterBase, HoldableObject):
m += ['method', mlog.bold(method)]
mlog.log(*m, '\n', nested=False)
- methods_map: T.Dict[wrap.Method, T.Callable[[str, str, T.Dict[OptionKey, str, kwtypes.DoSubproject]], SubprojectHolder]] = {
+ methods_map: T.Dict[wrap.Method, T.Callable[[str, str, OptionDict, kwtypes.DoSubproject],
+ SubprojectHolder]] = {
'meson': self._do_subproject_meson,
'cmake': self._do_subproject_cmake,
'cargo': self._do_subproject_cargo,
@@ -951,30 +957,34 @@ class Interpreter(InterpreterBase, HoldableObject):
return self.disabled_subproject(subp_name, exception=e)
raise e
+ def _save_ast(self, subdir: str, ast: mparser.CodeBlockNode) -> None:
+ # Debug print the generated meson file
+ from ..ast import AstIndentationGenerator, AstPrinter
+ printer = AstPrinter(update_ast_line_nos=True)
+ ast.accept(AstIndentationGenerator())
+ ast.accept(printer)
+ printer.post_process()
+ meson_filename = os.path.join(self.build.environment.get_build_dir(), subdir, 'meson.build')
+ with open(meson_filename, "w", encoding='utf-8') as f:
+ f.write(printer.result)
+ mlog.log('Generated Meson AST:', meson_filename)
+ mlog.cmd_ci_include(meson_filename)
+
def _do_subproject_meson(self, subp_name: str, subdir: str,
- default_options: T.List[str],
+ default_options: OptionDict,
kwargs: kwtypes.DoSubproject,
ast: T.Optional[mparser.CodeBlockNode] = None,
build_def_files: T.Optional[T.List[str]] = None,
- relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None) -> SubprojectHolder:
+ relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None,
+ cargo: T.Optional[cargo.Interpreter] = None) -> SubprojectHolder:
with mlog.nested(subp_name):
if ast:
- # Debug print the generated meson file
- from ..ast import AstIndentationGenerator, AstPrinter
- printer = AstPrinter(update_ast_line_nos=True)
- ast.accept(AstIndentationGenerator())
- ast.accept(printer)
- printer.post_process()
- meson_filename = os.path.join(self.build.environment.get_build_dir(), subdir, 'meson.build')
- with open(meson_filename, "w", encoding='utf-8') as f:
- f.write(printer.result)
- mlog.log('Generated Meson AST:', meson_filename)
- mlog.cmd_ci_include(meson_filename)
-
+ self._save_ast(subdir, ast)
new_build = self.build.copy()
subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir,
default_options, ast=ast, relaxations=relaxations,
- user_defined_options=self.user_defined_options)
+ user_defined_options=self.user_defined_options,
+ cargo=cargo)
# Those lists are shared by all interpreters. That means that
# even if the subproject fails, any modification that the subproject
# made to those lists will affect the parent project.
@@ -1006,22 +1016,20 @@ class Interpreter(InterpreterBase, HoldableObject):
if build_def_files:
self.build_def_files.update(build_def_files)
# We always need the subi.build_def_files, to propagate sub-sub-projects
- self.build_def_files.update(subi.build_def_files)
+ self.build_def_files.update(subi.get_build_def_files())
self.build.merge(subi.build)
self.build.subprojects[subp_name] = subi.project_version
return self.subprojects[subp_name]
def _do_subproject_cmake(self, subp_name: str, subdir: str,
- default_options: T.List[str],
+ default_options: OptionDict,
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from ..cmake import CMakeInterpreter
with mlog.nested(subp_name):
- prefix = self.coredata.optstore.get_value_for('prefix')
-
from ..modules.cmake import CMakeSubprojectOptions
kw_opts = kwargs.get('options') or CMakeSubprojectOptions()
cmake_options = kwargs.get('cmake_options', []) + kw_opts.cmake_options
- cm_int = CMakeInterpreter(Path(subdir), Path(prefix), self.build.environment, self.backend)
+ cm_int = CMakeInterpreter(Path(subdir), self.build.environment, self.backend)
cm_int.initialise(cmake_options)
cm_int.analyse()
@@ -1039,20 +1047,24 @@ class Interpreter(InterpreterBase, HoldableObject):
return result
def _do_subproject_cargo(self, subp_name: str, subdir: str,
- default_options: T.List[str],
+ default_options: OptionDict,
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from .. import cargo
FeatureNew.single_use('Cargo subproject', '1.3.0', self.subproject, location=self.current_node)
mlog.warning('Cargo subproject is an experimental feature and has no backwards compatibility guarantees.',
once=True, location=self.current_node)
- if self.environment.cargo is None:
- self.environment.cargo = cargo.Interpreter(self.environment)
+ self.add_languages(['rust'], True, MachineChoice.HOST)
with mlog.nested(subp_name):
- ast = self.environment.cargo.interpret(subdir)
+ try:
+ cargo_int = self.cargo or cargo.Interpreter(self.environment, subdir, self.subproject_dir)
+ except cargo.TomlImplementationMissing as e:
+ raise MesonException(f'Failed to load Cargo.lock: {e!s}')
+
+ ast = cargo_int.interpret(subdir)
return self._do_subproject_meson(
subp_name, subdir, default_options, kwargs, ast,
- # FIXME: Are there other files used by cargo interpreter?
- [os.path.join(subdir, 'Cargo.toml')])
+ relaxations={InterpreterRuleRelaxation.CARGO_SUBDIR},
+ cargo=cargo_int)
@typed_pos_args('get_option', str)
@noKwargs
@@ -1067,35 +1079,28 @@ class Interpreter(InterpreterBase, HoldableObject):
if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None:
raise InterpreterException(f'Invalid option name {optname!r}')
- # Will be None only if the value comes from the default
- value_object: T.Optional[options.AnyOptionType]
+ option_object: T.Optional[options.AnyOptionType]
try:
- optkey = options.OptionKey(optname, self.subproject)
- value_object, value = self.coredata.optstore.get_value_object_and_value_for(optkey)
+ optkey = options.OptionKey.from_string(optname).evolve(subproject=self.subproject)
+ option_object, value = self.coredata.optstore.get_option_and_value_for(optkey)
except KeyError:
if self.coredata.optstore.is_base_option(optkey):
# Due to backwards compatibility return the default
# option for base options instead of erroring out.
- #
- # TODO: This will have issues if we expect to return a user FeatureOption
- # Of course, there's a bit of a layering violation here in
- # that we return a UserFeatureOption, but otherwise the held value
- # We probably need a lower level feature thing, or an enum
- # instead of strings
- value = self.coredata.optstore.get_default_for_b_option(optkey)
- value_object = None
+ option_object = options.COMPILER_BASE_OPTIONS[optkey.evolve(subproject=None, machine=MachineChoice.HOST)]
+ value = option_object.default
else:
if self.subproject:
raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.')
raise MesonException(f'Option {optname} does not exist.')
- if isinstance(value_object, options.UserFeatureOption):
- ocopy = copy.copy(value_object)
+ if isinstance(option_object, options.UserFeatureOption):
+ ocopy = copy.copy(option_object)
ocopy.name = optname
ocopy.value = value
return ocopy
elif optname == 'b_sanitize':
- assert value_object is None or isinstance(value_object, options.UserStringArrayOption)
+ assert isinstance(option_object, options.UserStringArrayOption)
# To ensure backwards compatibility this always returns a string.
# We may eventually want to introduce a new "format" kwarg that
# allows the user to modify this behaviour, but for now this is
@@ -1131,10 +1136,10 @@ class Interpreter(InterpreterBase, HoldableObject):
# Use of the '--genvslite vsxxxx' option ultimately overrides any '--backend xxx'
# option the user may specify.
backend_name = self.coredata.optstore.get_value_for(OptionKey('genvslite'))
- self.backend = backends.get_genvslite_backend(backend_name, self.build, self)
+ self.backend = backends.get_genvslite_backend(backend_name, self.build)
else:
backend_name = self.coredata.optstore.get_value_for(OptionKey('backend'))
- self.backend = backends.get_backend_from_name(backend_name, self.build, self)
+ self.backend = backends.get_backend_from_name(backend_name, self.build)
if self.backend is None:
raise InterpreterException(f'Unknown backend "{backend_name}".')
@@ -1143,15 +1148,9 @@ class Interpreter(InterpreterBase, HoldableObject):
mlog.log('Auto detected Visual Studio backend:', mlog.bold(self.backend.name))
if not self.environment.first_invocation:
raise MesonBugException(f'Backend changed from {backend_name} to {self.backend.name}')
- self.coredata.set_option(OptionKey('backend'), self.backend.name, first_invocation=True)
+ self.coredata.optstore.set_option(OptionKey('backend'), self.backend.name, first_invocation=True)
- # Only init backend options on first invocation otherwise it would
- # override values previously set from command line.
- if self.environment.first_invocation:
- self.coredata.init_backend_options(backend_name)
-
- options = {k: v for k, v in self.environment.options.items() if self.environment.coredata.optstore.is_backend_option(k)}
- self.coredata.set_options(options)
+ self.environment.init_backend_options(backend_name)
@typed_pos_args('project', str, varargs=str)
@typed_kwargs(
@@ -1184,20 +1183,17 @@ class Interpreter(InterpreterBase, HoldableObject):
self._load_option_file()
self.project_default_options = kwargs['default_options']
- if isinstance(self.project_default_options, str):
- self.project_default_options = [self.project_default_options]
- assert isinstance(self.project_default_options, (list, dict))
if self.environment.first_invocation or (self.subproject != '' and self.subproject not in self.coredata.initialized_subprojects):
if self.subproject == '':
self.coredata.optstore.initialize_from_top_level_project_call(self.project_default_options,
self.user_defined_options.cmd_line_options,
self.environment.options)
else:
- invoker_method_default_options = self.default_project_options
self.coredata.optstore.initialize_from_subproject_call(self.subproject,
- invoker_method_default_options,
+ self.invoker_method_default_options,
self.project_default_options,
- self.user_defined_options.cmd_line_options)
+ self.user_defined_options.cmd_line_options,
+ self.environment.options)
self.coredata.initialized_subprojects.add(self.subproject)
if not self.is_subproject():
@@ -1214,7 +1210,6 @@ class Interpreter(InterpreterBase, HoldableObject):
self.set_backend()
if not self.is_subproject():
- self.coredata.optstore.validate_cmd_line_options(self.user_defined_options.cmd_line_options)
self.build.project_name = proj_name
self.active_projectname = proj_name
@@ -1280,6 +1275,9 @@ class Interpreter(InterpreterBase, HoldableObject):
assert self.environment.wrap_resolver is not None, 'for mypy'
self.environment.wrap_resolver.load_and_merge(subprojects_dir, self.subproject)
+ if self.cargo is None:
+ self.load_root_cargo_lock_file()
+
self.build.projects[self.subproject] = proj_name
mlog.log('Project name:', mlog.bold(proj_name))
mlog.log('Project version:', mlog.bold(self.project_version))
@@ -1298,7 +1296,6 @@ class Interpreter(InterpreterBase, HoldableObject):
native = kwargs['native']
if disabled:
- assert feature, 'for mypy'
for lang in sorted(langs, key=compilers.sort_clink):
mlog.log('Compiler for language', mlog.bold(lang), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
@@ -1469,8 +1466,6 @@ class Interpreter(InterpreterBase, HoldableObject):
def add_languages(self, args: T.List[str], required: bool, for_machine: MachineChoice) -> bool:
success = self.add_languages_for(args, required, for_machine)
- if not self.coredata.is_cross_build():
- self.coredata.copy_build_options_from_regular_ones()
self._redetect_machines()
return success
@@ -1529,7 +1524,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.backend.allow_thin_archives[for_machine] = False
else:
# update new values from commandline, if it applies
- self.coredata.process_compiler_options(lang, comp, self.environment, self.subproject)
+ self.coredata.process_compiler_options(lang, comp, self.subproject)
if for_machine == MachineChoice.HOST or self.environment.is_cross_build():
logger_fun = mlog.log
@@ -1590,7 +1585,7 @@ class Interpreter(InterpreterBase, HoldableObject):
def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString],
extra_info: T.List['mlog.TV_Loggable']
- ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.Executable]]:
+ ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable]]:
for name in command_names:
if not isinstance(name, str):
continue
@@ -1605,7 +1600,7 @@ class Interpreter(InterpreterBase, HoldableObject):
if isinstance(name, str):
self.build.searched_programs.add(name)
- def add_find_program_override(self, name: str, exe: T.Union[build.Executable, ExternalProgram, 'OverrideProgram']) -> None:
+ def add_find_program_override(self, name: str, exe: T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']) -> None:
if name in self.build.searched_programs:
raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.')
if name in self.build.find_overrides:
@@ -1624,13 +1619,13 @@ class Interpreter(InterpreterBase, HoldableObject):
# the host machine.
def find_program_impl(self, args: T.List[mesonlib.FileOrString],
for_machine: MachineChoice = MachineChoice.HOST,
- default_options: T.Optional[T.Dict[OptionKey, options.ElementaryOptionValues]] = None,
+ default_options: T.Optional[OptionDict] = None,
required: bool = True, silent: bool = True,
wanted: T.Union[str, T.List[str]] = '',
search_dirs: T.Optional[T.List[str]] = None,
version_arg: T.Optional[str] = '',
version_func: T.Optional[ProgramVersionFunc] = None
- ) -> T.Union['ExternalProgram', 'build.Executable', 'OverrideProgram']:
+ ) -> T.Union['ExternalProgram', 'build.OverrideExecutable', 'OverrideProgram']:
args = mesonlib.listify(args)
extra_info: T.List[mlog.TV_Loggable] = []
@@ -1655,7 +1650,7 @@ class Interpreter(InterpreterBase, HoldableObject):
return progobj
def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: MachineChoice,
- default_options: T.Optional[T.Dict[OptionKey, options.ElementaryOptionValues]],
+ default_options: T.Optional[OptionDict],
required: bool,
search_dirs: T.Optional[T.List[str]],
wanted: T.Union[str, T.List[str]],
@@ -1723,7 +1718,7 @@ class Interpreter(InterpreterBase, HoldableObject):
return True
def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString],
- default_options: T.Dict[OptionKey, options.ElementaryOptionValues],
+ default_options: OptionDict,
required: bool, extra_info: T.List[mlog.TV_Loggable]
) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]:
mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program',
@@ -1755,7 +1750,6 @@ class Interpreter(InterpreterBase, HoldableObject):
) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
- assert feature, 'for mypy'
mlog.log('Program', mlog.bold(' '.join(args[0])), 'skipped: feature', mlog.bold(feature), 'disabled')
return self.notfound_program(args[0])
@@ -1766,34 +1760,32 @@ class Interpreter(InterpreterBase, HoldableObject):
search_dirs=search_dirs)
# When adding kwargs, please check if they make sense in dependencies.get_dep_identifier()
- @FeatureNewKwargs('dependency', '0.57.0', ['cmake_package_version'])
- @FeatureNewKwargs('dependency', '0.56.0', ['allow_fallback'])
- @FeatureNewKwargs('dependency', '0.54.0', ['components'])
- @FeatureNewKwargs('dependency', '0.52.0', ['include_type'])
- @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message', 'cmake_module_path', 'cmake_args'])
- @FeatureNewKwargs('dependency', '0.49.0', ['disabler'])
- @FeatureNewKwargs('dependency', '0.40.0', ['method'])
- @disablerIfNotFound
- @permittedKwargs(permitted_dependency_kwargs)
@typed_pos_args('dependency', varargs=str, min_varargs=1)
- @typed_kwargs('dependency', DEFAULT_OPTIONS.evolve(since='0.38.0'), allow_unknown=True)
- def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kwargs) -> Dependency:
+ @typed_kwargs('dependency', *DEPENDENCY_KWS)
+ @disablerIfNotFound
+ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kwargs: kwtypes.FuncDependency) -> Dependency:
# Replace '' by empty list of names
names = [n for n in args[0] if n]
if len(names) > 1:
FeatureNew('dependency with more than one name', '0.60.0').use(self.subproject)
- allow_fallback = kwargs.get('allow_fallback')
- if allow_fallback is not None and not isinstance(allow_fallback, bool):
- raise InvalidArguments('"allow_fallback" argument must be boolean')
- fallback = kwargs.get('fallback')
default_options = kwargs.get('default_options')
- df = DependencyFallbacksHolder(self, names, allow_fallback, default_options)
- df.set_fallback(fallback)
- not_found_message = kwargs.get('not_found_message', '')
- if not isinstance(not_found_message, str):
- raise InvalidArguments('The not_found_message must be a string.')
+ df = DependencyFallbacksHolder(self, names, kwargs['allow_fallback'], default_options)
+ df.set_fallback(kwargs['fallback'])
+ not_found_message = kwargs['not_found_message']
+
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
+ if disabled:
+ name = names[0]
+ if kwargs['modules']:
+ name = name + '(modules: {})'.format(', '.join(kwargs['modules']))
+ mlog.log('Dependency', mlog.bold(name), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return dependencies.NotFoundDependency(names[0], self.environment)
+
+ nkwargs = T.cast('dependencies.base.DependencyObjectKWs', kwargs.copy())
+ nkwargs['required'] = required # to replace a possible UserFeatureOption with a bool
+
try:
- d = df.lookup(kwargs)
+ d = df.lookup(nkwargs)
except Exception:
if not_found_message:
self.message_impl([not_found_message])
@@ -1802,10 +1794,8 @@ class Interpreter(InterpreterBase, HoldableObject):
if not d.found() and not_found_message:
self.message_impl([not_found_message])
# Ensure the correct include type
- if 'include_type' in kwargs:
+ if kwargs['include_type'] != 'preserve':
wanted = kwargs['include_type']
- if not isinstance(wanted, str):
- raise InvalidArguments('The `include_type` kwarg must be a string')
actual = d.get_include_type()
if wanted != actual:
mlog.debug(f'Current include type of {args[0]} is {actual}. Converting to requested {wanted}')
@@ -1823,22 +1813,26 @@ class Interpreter(InterpreterBase, HoldableObject):
def func_disabler(self, node, args, kwargs):
return Disabler()
- def _strip_exe_specific_kwargs(self, kwargs: kwtypes.Executable) -> kwtypes._BuildTarget:
- kwargs = kwargs.copy()
- for exe_kwarg in _EXCLUSIVE_EXECUTABLE_KWS:
- del kwargs[exe_kwarg.name]
- return kwargs
+ def _exe_to_shlib_kwargs(self, kwargs: kwtypes.Executable) -> kwtypes.SharedLibrary:
+ nkwargs = T.cast('kwtypes.SharedLibrary', kwargs.copy())
+ for exe_kwarg in EXCLUSIVE_EXECUTABLE_KWS:
+ del nkwargs[exe_kwarg.name] # type: ignore[misc]
+ for sh_kwarg in SHARED_LIB_KWS:
+ nkwargs.setdefault(sh_kwarg.name, sh_kwarg.default) # type: ignore[misc]
+ nkwargs['rust_abi'] = None
+ nkwargs['rust_crate_type'] = 'cdylib'
+ return nkwargs
@permittedKwargs(build.known_exe_kwargs)
@typed_pos_args('executable', str, varargs=SOURCES_VARARGS)
@typed_kwargs('executable', *EXECUTABLE_KWS, allow_unknown=True)
def func_executable(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
- kwargs: kwtypes.Executable) -> build.Executable:
+ kwargs: kwtypes.Executable) -> T.Union[build.Executable, build.SharedLibrary]:
for_machine = kwargs['native']
m = self.environment.machines[for_machine]
if m.is_android() and kwargs.get('android_exe_type') == 'application':
- holder = self.build_target(node, args, self._strip_exe_specific_kwargs(kwargs), build.SharedLibrary)
+ holder = self.build_target(node, args, self._exe_to_shlib_kwargs(kwargs), build.SharedLibrary)
holder.shared_library_only = True
return holder
return self.build_target(node, args, kwargs, build.Executable)
@@ -2048,6 +2042,7 @@ class Interpreter(InterpreterBase, HoldableObject):
KwargInfo('feed', bool, default=False, since='0.59.0'),
KwargInfo('capture', bool, default=False),
KwargInfo('console', bool, default=False, since='0.48.0'),
+ KwargInfo('build_subdir', str, default='', since='1.10.0'),
)
def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str],
kwargs: 'kwtypes.CustomTarget') -> build.CustomTarget:
@@ -2138,7 +2133,8 @@ class Interpreter(InterpreterBase, HoldableObject):
install_dir=kwargs['install_dir'],
install_mode=install_mode,
install_tag=kwargs['install_tag'],
- backend=self.backend)
+ backend=self.backend,
+ build_subdir=kwargs['build_subdir'])
self.add_target(tg.name, tg)
return tg
@@ -2158,6 +2154,10 @@ class Interpreter(InterpreterBase, HoldableObject):
raise InterpreterException(f'Tried to use non-existing executable {i.name!r}')
if isinstance(all_args[0], str):
all_args[0] = self.find_program_impl([all_args[0]])
+
+ if '@DEPFILE@' in all_args:
+ raise InterpreterException('run_target does not have support for @DEPFILE@')
+
name = args[0]
tg = build.RunTarget(name, all_args, kwargs['depends'], self.subdir, self.subproject, self.environment,
kwargs['env'])
@@ -2205,7 +2205,7 @@ class Interpreter(InterpreterBase, HoldableObject):
if '@OUTPUT@' in o:
raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.')
- return build.Generator(args[0], **kwargs)
+ return build.Generator(self.environment, args[0], **kwargs)
@typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex))
@typed_kwargs('benchmark', *TEST_KWS)
@@ -2456,7 +2456,12 @@ class Interpreter(InterpreterBase, HoldableObject):
os.makedirs(os.path.join(self.environment.build_dir, subdir), exist_ok=True)
- if not self._evaluate_subdir(self.environment.get_source_dir(), subdir):
+ if InterpreterRuleRelaxation.CARGO_SUBDIR in self.relaxations and \
+ os.path.exists(os.path.join(self.environment.get_source_dir(), subdir, 'Cargo.toml')):
+ codeblock = self.cargo.interpret(subdir, self.root_subdir)
+ self._save_ast(subdir, codeblock)
+ self._evaluate_codeblock(codeblock, subdir)
+ elif not self._evaluate_subdir(self.environment.get_source_dir(), subdir):
buildfilename = os.path.join(subdir, environment.build_filename)
raise InterpreterException(f"Nonexistent build file '{buildfilename!s}'")
@@ -2583,6 +2588,13 @@ class Interpreter(InterpreterBase, HoldableObject):
self.build.install_dirs.append(idir)
return idir
+ def validate_build_subdir(self, build_subdir: str, target: str):
+ if build_subdir and build_subdir != '.':
+ if os.path.exists(os.path.join(self.source_root, self.subdir, build_subdir)):
+ raise InvalidArguments(f'Build subdir "{build_subdir}" in "{target}" exists in source tree.')
+ if '..' in build_subdir:
+ raise InvalidArguments(f'Build subdir "{build_subdir}" in "{target}" contains ..')
+
@noPosargs
@typed_kwargs(
'configure_file',
@@ -2619,6 +2631,7 @@ class Interpreter(InterpreterBase, HoldableObject):
KwargInfo('output_format', str, default='c', since='0.47.0', since_values={'json': '1.3.0'},
validator=in_set_validator({'c', 'json', 'nasm'})),
KwargInfo('macro_name', (str, NoneType), default=None, since='1.3.0'),
+ KwargInfo('build_subdir', str, default='', since='1.10.0'),
)
def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var],
kwargs: kwtypes.ConfigureFile):
@@ -2674,8 +2687,14 @@ class Interpreter(InterpreterBase, HoldableObject):
mlog.warning('Output file', mlog.bold(ofile_rpath, True), 'for configure_file() at', current_call, 'overwrites configure_file() output at', first_call)
else:
self.configure_file_outputs[ofile_rpath] = self.current_node.lineno
- (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))
+
+ # Validate build_subdir
+ build_subdir = kwargs['build_subdir']
+ self.validate_build_subdir(build_subdir, output)
+
+ (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, build_subdir, output))
ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname)
+ os.makedirs(os.path.split(ofile_abs)[0], exist_ok=True)
# Perform the appropriate action
if kwargs['configuration'] is not None:
@@ -2691,7 +2710,6 @@ class Interpreter(InterpreterBase, HoldableObject):
if len(inputs) > 1:
raise InterpreterException('At most one input file can given in configuration mode')
if inputs:
- os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
file_encoding = kwargs['encoding']
missing_variables, confdata_useless = \
mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf,
@@ -2774,12 +2792,12 @@ class Interpreter(InterpreterBase, HoldableObject):
install_tag=install_tag, data_type='configure'))
return mesonlib.File.from_built_file(self.subdir, output)
- def extract_incdirs(self, kwargs, key: str = 'include_directories') -> T.List[build.IncludeDirs]:
+ def extract_incdirs(self, kwargs, key: str = 'include_directories', strings_since: T.Optional[str] = None) -> T.List[build.IncludeDirs]:
prospectives = extract_as_list(kwargs, key)
- if key == 'include_directories':
+ if strings_since:
for i in prospectives:
if isinstance(i, str):
- FeatureNew.single_use('include_directories kwarg of type string', '0.50.0', self.subproject,
+ FeatureNew.single_use(f'{key} kwarg of type string', strings_since, self.subproject,
f'Use include_directories({i!r}) instead', location=self.current_node)
break
@@ -3063,9 +3081,9 @@ class Interpreter(InterpreterBase, HoldableObject):
return
if OptionKey('b_sanitize') not in self.coredata.optstore:
return
- if (self.coredata.optstore.get_value('b_lundef') and
- self.coredata.optstore.get_value('b_sanitize')):
- value = self.coredata.optstore.get_value('b_sanitize')
+ if (self.coredata.optstore.get_value_for('b_lundef') and
+ self.coredata.optstore.get_value_for('b_sanitize')):
+ value = self.coredata.optstore.get_value_for('b_sanitize')
mlog.warning(textwrap.dedent(f'''\
Trying to use {value} sanitizer on Clang with b_lundef.
This will probably not work.
@@ -3214,11 +3232,17 @@ class Interpreter(InterpreterBase, HoldableObject):
To define a target that builds in that directory you must define it
in the meson.build file in that directory.
'''))
+
+ # Make sure build_subdir doesn't exist in the source tree and
+ # doesn't contain ..
+ build_subdir = tobj.get_build_subdir()
+ self.validate_build_subdir(build_subdir, name)
+
self.validate_forbidden_targets(name)
# To permit an executable and a shared library to have the
# same name, such as "foo.exe" and "libfoo.a".
idname = tobj.get_id()
- subdir = tobj.get_subdir()
+ subdir = tobj.get_builddir()
namedir = (name, subdir)
if idname in self.build.targets:
@@ -3244,9 +3268,9 @@ class Interpreter(InterpreterBase, HoldableObject):
def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.BothLibraries:
shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary)
static_lib = self.build_target(node, args, kwargs, build.StaticLibrary)
- preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_both_libraries'))
+ preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_both_libraries', subproject=self.subproject))
if preferred_library == 'auto':
- preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_library'))
+ preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_library', subproject=self.subproject))
if preferred_library == 'both':
preferred_library = 'shared'
@@ -3338,6 +3362,40 @@ class Interpreter(InterpreterBase, HoldableObject):
d.extend(deps)
kwargs['language_args'] = new_args
+ @staticmethod
+ def _handle_rust_abi(abi: T.Optional[Literal['c', 'rust']],
+ crate_type: T.Optional[build.RustCrateType],
+ default_rust_type: build.RustCrateType,
+ default_c_type: build.RustCrateType, typename: str,
+ extra_valid_types: T.Optional[T.Set[build.RustCrateType]] = None,
+ ) -> build.RustCrateType:
+ """Handle the interactions between the rust_abi and rust_crate_type keyword arguments.
+
+ :param abi: Is this using Rust ABI or C ABI
+ :param crate_type: Is there an explicit crate type set
+ :param default_rust_type: The default crate type to use for Rust ABI
+ :param default_c_type: the default crate type to use for C ABI
+ :param typename: The name of the type this argument is for
+ :param extra_valid_types: additional valid crate types, defaults to None
+ :raises InvalidArguments: If the crate_type argument is set, but not valid
+ :raises InvalidArguments: If both crate_type and abi are set
+ :return: The finalized crate type
+ """
+ if abi is not None:
+ if crate_type is not None:
+ raise InvalidArguments('rust_abi and rust_crate_type are mutually exclusive')
+ crate_type = default_rust_type if abi == 'rust' else default_c_type
+ elif crate_type is not None:
+ if crate_type == 'lib':
+ crate_type = default_rust_type
+ valid_types = {default_rust_type, default_c_type} | (extra_valid_types or set())
+ if crate_type not in valid_types:
+ choices = ", " .join(f'"{c}"' for c in sorted(valid_types))
+ raise InvalidArguments(f'Crate type for {typename} must be one of {choices}, but got "{crate_type}"')
+ else:
+ crate_type = default_rust_type
+ return crate_type
+
@T.overload
def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.Executable, targetclass: T.Type[build.Executable]) -> build.Executable: ...
@@ -3362,6 +3420,13 @@ class Interpreter(InterpreterBase, HoldableObject):
kwargs: T.Union[kwtypes.Executable, kwtypes.StaticLibrary, kwtypes.SharedLibrary, kwtypes.SharedModule, kwtypes.Jar],
targetclass: T.Type[T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]]
) -> T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]:
+ if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}:
+ mlog.debug('Unknown target type:', str(targetclass))
+ raise RuntimeError('Unreachable code')
+
+ # Because who owns this isn't clear
+ kwargs = kwargs.copy()
+
name, sources = args
for_machine = kwargs['native']
if kwargs.get('rust_crate_type') == 'proc-macro':
@@ -3382,35 +3447,48 @@ class Interpreter(InterpreterBase, HoldableObject):
# backwards compatibility anyway
sources = [s for s in sources
if not isinstance(s, (build.BuildTarget, build.ExtractedObjects))]
-
- # due to lack of type checking, these are "allowed" for legacy reasons
- if not isinstance(kwargs['install'], bool):
- FeatureBroken.single_use('install kwarg with non-boolean value', '1.3.0', self.subproject,
- 'This was never intended to work, and is essentially the same as using `install: true` regardless of value.',
- node)
-
sources = self.source_strings_to_files(sources)
objs = kwargs['objects']
kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies')
- kwargs['extra_files'] = self.source_strings_to_files(kwargs['extra_files'])
+ # TODO: When we can do strings -> Files in the typed_kwargs validator, do this there too
+ kwargs['extra_files'] = mesonlib.unique_list(self.source_strings_to_files(kwargs['extra_files']))
self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
- if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}:
- mlog.debug('Unknown target type:', str(targetclass))
- raise RuntimeError('Unreachable code')
self.__process_language_args(kwargs)
if targetclass is build.StaticLibrary:
+ kwargs = T.cast('kwtypes.StaticLibrary', kwargs)
for lang in compilers.all_languages - {'java'}:
deps, args = self.__convert_file_args(kwargs.get(f'{lang}_static_args', []))
kwargs['language_args'][lang].extend(args)
kwargs['depend_files'].extend(deps)
+ kwargs['rust_crate_type'] = self._handle_rust_abi(
+ kwargs['rust_abi'], kwargs['rust_crate_type'], 'rlib', 'staticlib', targetclass.typename)
+
elif targetclass is build.SharedLibrary:
+ kwargs = T.cast('kwtypes.SharedLibrary', kwargs)
for lang in compilers.all_languages - {'java'}:
deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', []))
kwargs['language_args'][lang].extend(args)
kwargs['depend_files'].extend(deps)
+ kwargs['rust_crate_type'] = self._handle_rust_abi(
+ kwargs['rust_abi'], kwargs['rust_crate_type'], 'dylib', 'cdylib', targetclass.typename,
+ extra_valid_types={'proc-macro'})
+
+ elif targetclass is build.Executable:
+ kwargs = T.cast('kwtypes.Executable', kwargs)
+ if kwargs['rust_crate_type'] not in {None, 'bin'}:
+ raise InvalidArguments('Crate type for executable must be "bin"')
+ kwargs['rust_crate_type'] = 'bin'
+
if targetclass is not build.Jar:
self.check_for_jar_sources(sources, targetclass)
kwargs['d_import_dirs'] = self.extract_incdirs(kwargs, 'd_import_dirs')
+ missing: T.List[str] = []
+ for each in itertools.chain(kwargs['c_pch'] or [], kwargs['cpp_pch'] or []):
+ if each is not None:
+ if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, each)):
+ missing.append(os.path.join(self.subdir, each))
+ if missing:
+ raise InvalidArguments('The following PCH files do not exist: {}'.format(', '.join(missing)))
# Filter out kwargs from other target types. For example 'soversion'
# passed to library() when default_library == 'static'.
@@ -3447,7 +3525,7 @@ class Interpreter(InterpreterBase, HoldableObject):
node=node)
outputs.update(o)
- kwargs['include_directories'] = self.extract_incdirs(kwargs)
+ kwargs['include_directories'] = self.extract_incdirs(kwargs, strings_since='0.50.0')
if targetclass is build.Executable:
kwargs = T.cast('kwtypes.Executable', kwargs)
@@ -3470,11 +3548,12 @@ class Interpreter(InterpreterBase, HoldableObject):
elif kwargs['export_dynamic']:
if kwargs['implib'] is False:
raise InvalidArguments('"implib" keyword" must not be false if "export_dynamic" is set and not false.')
- kwargs['implib'] = True
if kwargs['export_dynamic'] is None:
kwargs['export_dynamic'] = False
- if kwargs['implib'] is None:
- kwargs['implib'] = False
+ if isinstance(kwargs['implib'], bool):
+ kwargs['implib'] = None
+
+ kwargs['install_tag'] = [kwargs['install_tag']]
target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs,
self.environment, self.compilers[for_machine], kwargs)
@@ -3506,11 +3585,6 @@ class Interpreter(InterpreterBase, HoldableObject):
elif isinstance(s, build.StructuredSources):
self.check_for_jar_sources(s.as_list(), targetclass)
- # Only permit object extraction from the same subproject
- def validate_extraction(self, buildtarget: mesonlib.HoldableObject) -> None:
- if self.subproject != buildtarget.subproject:
- raise InterpreterException('Tried to extract objects from a different subproject.')
-
def is_subproject(self) -> bool:
return self.subproject != ''
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index a2fadbe..4dce3f5 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -15,7 +15,7 @@ from .. import mlog
from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule, NewExtensionModule
from ..backend.backends import TestProtocol
from ..interpreterbase import (
- ContainerTypeInfo, KwargInfo, MesonOperator,
+ ContainerTypeInfo, KwargInfo, InterpreterObject, MesonOperator,
MesonInterpreterObject, ObjectHolder, MutableInterpreterObject,
FeatureNew, FeatureDeprecated,
typed_pos_args, typed_kwargs, typed_operator,
@@ -31,11 +31,12 @@ import typing as T
if T.TYPE_CHECKING:
from . import kwargs
from ..cmake.interpreter import CMakeInterpreter
+ from ..dependencies.base import IncludeType
from ..envconfig import MachineInfo
- from ..interpreterbase import FeatureCheckBase, InterpreterObject, SubProject, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs
+ from ..interpreterbase import FeatureCheckBase, SubProject, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs
from .interpreter import Interpreter
- from typing_extensions import TypedDict
+ from typing_extensions import Literal, TypedDict
class EnvironmentSeparatorKW(TypedDict):
@@ -51,18 +52,36 @@ _ERROR_MSG_KW: KwargInfo[T.Optional[str]] = KwargInfo('error_message', (str, Non
def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
subproject: 'SubProject',
feature_check: T.Optional[FeatureCheckBase] = None,
- default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]:
+ default: bool = True
+ ) -> T.Union[T.Tuple[Literal[True], bool, str],
+ T.Tuple[Literal[False], bool, None]]:
+ """Check common keyword arguments for required status.
+
+ This handles booleans vs feature option.
+
+ :param kwargs:
+ keyword arguments from the Interpreter, containing a `required` argument
+ :param subproject: The subproject this is
+ :param feature_check:
+ A custom feature check for this use of `required` with a
+ `UserFeatureOption`, defaults to None.
+ :param default:
+ The default value is `required` is not set in `kwargs`, defaults to
+ True
+ :raises InterpreterException: If the type of `kwargs['required']` is invalid
+ :return:
+ a tuple of `disabled, required, feature_name`. If `disabled` is `True`
+ `feature_name` will be a string, otherwise it is `None`
+ """
val = kwargs.get('required', default)
- disabled = False
required = False
- feature: T.Optional[str] = None
if isinstance(val, options.UserFeatureOption):
if not feature_check:
feature_check = FeatureNew('User option "feature"', '0.47.0')
feature_check.use(subproject)
feature = val.name
if val.is_disabled():
- disabled = True
+ return True, required, feature
elif val.is_enabled():
required = True
elif isinstance(val, bool):
@@ -75,7 +94,7 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
# TODO: this should be removed, and those callers should learn about FeatureOptions
kwargs['required'] = required
- return disabled, required, feature
+ return False, required, None
def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]:
search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', []))
@@ -94,19 +113,9 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
super().__init__(option, interpreter)
if option and option.is_auto():
# TODO: we need to cast here because options is not a TypedDict
- auto = T.cast('options.UserFeatureOption', self.env.coredata.optstore.get_value_object_for('auto_features'))
+ auto = T.cast('options.UserFeatureOption', self.env.coredata.optstore.resolve_option('auto_features'))
self.held_object = copy.copy(auto)
self.held_object.name = option.name
- self.methods.update({'enabled': self.enabled_method,
- 'disabled': self.disabled_method,
- 'allowed': self.allowed_method,
- 'auto': self.auto_method,
- 'require': self.require_method,
- 'disable_auto_if': self.disable_auto_if_method,
- 'enable_auto_if': self.enable_auto_if_method,
- 'disable_if': self.disable_if_method,
- 'enable_if': self.enable_if_method,
- })
@property
def value(self) -> str:
@@ -124,22 +133,26 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('enabled')
def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'enabled'
@noPosargs
@noKwargs
+ @InterpreterObject.method('disabled')
def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'disabled'
@noPosargs
@noKwargs
@FeatureNew('feature_option.allowed()', '0.59.0')
+ @InterpreterObject.method('allowed')
def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value != 'disabled'
@noPosargs
@noKwargs
+ @InterpreterObject.method('auto')
def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'auto'
@@ -160,6 +173,7 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
'feature_option.require',
_ERROR_MSG_KW,
)
+ @InterpreterObject.method('require')
def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
return self._disable_if(not args[0], kwargs['error_message'])
@@ -169,6 +183,7 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
'feature_option.disable_if',
_ERROR_MSG_KW,
)
+ @InterpreterObject.method('disable_if')
def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
return self._disable_if(args[0], kwargs['error_message'])
@@ -178,6 +193,7 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
'feature_option.enable_if',
_ERROR_MSG_KW,
)
+ @InterpreterObject.method('enable_if')
def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
if not args[0]:
return copy.deepcopy(self.held_object)
@@ -192,12 +208,14 @@ class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
@FeatureNew('feature_option.disable_auto_if()', '0.59.0')
@noKwargs
@typed_pos_args('feature_option.disable_auto_if', bool)
+ @InterpreterObject.method('disable_auto_if')
def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption:
return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled()
@FeatureNew('feature_option.enable_auto_if()', '1.1.0')
@noKwargs
@typed_pos_args('feature_option.enable_auto_if', bool)
+ @InterpreterObject.method('enable_auto_if')
def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption:
return self.as_enabled() if self.value == 'auto' and args[0] else copy.deepcopy(self.held_object)
@@ -220,10 +238,6 @@ class RunProcess(MesonInterpreterObject):
raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
self.capture = capture
self.returncode, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
- self.methods.update({'returncode': self.returncode_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
def run_command(self,
cmd: ExternalProgram,
@@ -271,16 +285,19 @@ class RunProcess(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('returncode')
def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
return self.returncode
@noPosargs
@noKwargs
+ @InterpreterObject.method('stdout')
def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stdout
@noPosargs
@noKwargs
+ @InterpreterObject.method('stderr')
def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stderr
@@ -288,11 +305,6 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu
def __init__(self, obj: mesonlib.EnvironmentVariables, interpreter: 'Interpreter'):
super().__init__(obj, interpreter)
- self.methods.update({'set': self.set_method,
- 'unset': self.unset_method,
- 'append': self.append_method,
- 'prepend': self.prepend_method,
- })
def __repr__(self) -> str:
repr_str = "<{0}: {1}>"
@@ -310,6 +322,7 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu
@typed_pos_args('environment.set', str, varargs=str, min_varargs=1)
@typed_kwargs('environment.set', ENV_SEPARATOR_KW)
+ @InterpreterObject.method('set')
def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
name, values = args
self.held_object.set(name, values, kwargs['separator'])
@@ -317,11 +330,13 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu
@FeatureNew('environment.unset', '1.4.0')
@typed_pos_args('environment.unset', str)
@noKwargs
+ @InterpreterObject.method('unset')
def unset_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> None:
self.held_object.unset(args[0])
@typed_pos_args('environment.append', str, varargs=str, min_varargs=1)
@typed_kwargs('environment.append', ENV_SEPARATOR_KW)
+ @InterpreterObject.method('append')
def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
name, values = args
self.warn_if_has_name(name)
@@ -329,6 +344,7 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu
@typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1)
@typed_kwargs('environment.prepend', ENV_SEPARATOR_KW)
+ @InterpreterObject.method('prepend')
def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
name, values = args
self.warn_if_has_name(name)
@@ -342,15 +358,6 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
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,
- 'has': self.has_method,
- 'get': self.get_method,
- 'keys': self.keys_method,
- 'get_unquoted': self.get_unquoted_method,
- 'merge_from': self.merge_from_method,
- })
def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder':
return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter)
@@ -364,12 +371,14 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@typed_pos_args('configuration_data.set', str, (str, int, bool))
@typed_kwargs('configuration_data.set', _CONF_DATA_SET_KWS)
+ @InterpreterObject.method('set')
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)
+ @InterpreterObject.method('set_quoted')
def set_quoted_method(self, args: T.Tuple[str, str], kwargs: 'kwargs.ConfigurationDataSet') -> None:
self.__check_used()
escaped_val = '\\"'.join(args[1].split('"'))
@@ -377,6 +386,7 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@typed_pos_args('configuration_data.set10', str, (int, bool))
@typed_kwargs('configuration_data.set10', _CONF_DATA_SET_KWS)
+ @InterpreterObject.method('set10')
def set10_method(self, args: T.Tuple[str, T.Union[int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None:
self.__check_used()
# bool is a subclass of int, so we need to check for bool explicitly.
@@ -394,12 +404,14 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@typed_pos_args('configuration_data.has', (str, int, bool))
@noKwargs
+ @InterpreterObject.method('has')
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')
@typed_pos_args('configuration_data.get', str, optargs=[(str, int, bool)])
@noKwargs
+ @InterpreterObject.method('get')
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]
@@ -412,6 +424,7 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@FeatureNew('configuration_data.get_unquoted()', '0.44.0')
@typed_pos_args('configuration_data.get_unquoted', str, optargs=[(str, int, bool)])
@noKwargs
+ @InterpreterObject.method('get_unquoted')
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]
@@ -431,6 +444,7 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@FeatureNew('configuration_data.keys()', '0.57.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('keys')
def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
return sorted(self.keys())
@@ -439,6 +453,7 @@ class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInte
@typed_pos_args('configuration_data.merge_from', build.ConfigurationData)
@noKwargs
+ @InterpreterObject.method('merge_from')
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)
@@ -455,31 +470,19 @@ _PARTIAL_DEP_KWARGS = [
class DependencyHolder(ObjectHolder[Dependency]):
def __init__(self, dep: Dependency, interpreter: 'Interpreter'):
super().__init__(dep, interpreter)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'version': self.version_method,
- 'name': self.name_method,
- 'get_pkgconfig_variable': self.pkgconfig_method,
- 'get_configtool_variable': self.configtool_method,
- 'get_variable': self.variable_method,
- 'partial_dependency': self.partial_dependency_method,
- 'include_type': self.include_type_method,
- 'as_system': self.as_system_method,
- 'as_link_whole': self.as_link_whole_method,
- 'as_static': self.as_static_method,
- 'as_shared': self.as_shared_method,
- })
def found(self) -> bool:
return self.found_method([], {})
@noPosargs
@noKwargs
+ @InterpreterObject.method('type_name')
def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name
@noPosargs
@noKwargs
+ @InterpreterObject.method('found')
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
if self.held_object.type_name == 'internal':
return True
@@ -487,11 +490,13 @@ class DependencyHolder(ObjectHolder[Dependency]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('version')
def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_version()
@noPosargs
@noKwargs
+ @InterpreterObject.method('name')
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_name()
@@ -503,6 +508,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
KwargInfo('default', str, default=''),
PKGCONFIG_DEFINE_KW.evolve(name='define_variable')
)
+ @InterpreterObject.method('get_pkgconfig_variable')
def pkgconfig_method(self, args: T.Tuple[str], kwargs: 'kwargs.DependencyPkgConfigVar') -> str:
from ..dependencies.pkgconfig import PkgConfigDependency
if not isinstance(self.held_object, PkgConfigDependency):
@@ -521,6 +527,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
'use dependency.get_variable(configtool : ...) instead')
@noKwargs
@typed_pos_args('dependency.get_config_tool_variable', str)
+ @InterpreterObject.method('get_configtool_variable')
def configtool_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> str:
from ..dependencies.configtool import ConfigToolDependency
if not isinstance(self.held_object, ConfigToolDependency):
@@ -533,6 +540,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
@FeatureNew('dependency.partial_dependency', '0.46.0')
@noPosargs
@typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ @InterpreterObject.method('partial_dependency')
def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs)
return pdep
@@ -549,6 +557,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
KwargInfo('default_value', (str, NoneType)),
PKGCONFIG_DEFINE_KW,
)
+ @InterpreterObject.method('get_variable')
def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: 'kwargs.DependencyGetVariable') -> str:
default_varname = args[0]
if default_varname is not None:
@@ -570,18 +579,30 @@ class DependencyHolder(ObjectHolder[Dependency]):
@FeatureNew('dependency.include_type', '0.52.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('include_type')
def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_include_type()
@FeatureNew('dependency.as_system', '0.52.0')
@noKwargs
@typed_pos_args('dependency.as_system', optargs=[str])
+ @InterpreterObject.method('as_system')
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')
+ include_type: IncludeType
+ if args[0] is None:
+ include_type = 'system'
+ elif args[0] not in {'preserve', 'system', 'non-system'}:
+ raise InvalidArguments(
+ 'Dependency.as_system: if an argument is given it must be one '
+ f'of: "preserve", "system", "non-system", not: "{args[0]}"')
+ else:
+ include_type = T.cast('IncludeType', args[0])
+ return self.held_object.generate_system_dependency(include_type)
@FeatureNew('dependency.as_link_whole', '0.56.0')
@noKwargs
@noPosargs
+ @InterpreterObject.method('as_link_whole')
def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
if not isinstance(self.held_object, InternalDependency):
raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
@@ -594,6 +615,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
'dependency.as_static',
KwargInfo('recursive', bool, default=False),
)
+ @InterpreterObject.method('as_static')
def as_static_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency:
if not isinstance(self.held_object, InternalDependency):
raise InterpreterException('as_static method is only supported on declare_dependency() objects')
@@ -605,6 +627,7 @@ class DependencyHolder(ObjectHolder[Dependency]):
'dependency.as_shared',
KwargInfo('recursive', bool, default=False),
)
+ @InterpreterObject.method('as_shared')
def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency:
if not isinstance(self.held_object, InternalDependency):
raise InterpreterException('as_shared method is only supported on declare_dependency() objects')
@@ -615,13 +638,10 @@ _EXTPROG = T.TypeVar('_EXTPROG', bound=ExternalProgram)
class _ExternalProgramHolder(ObjectHolder[_EXTPROG]):
def __init__(self, ep: _EXTPROG, interpreter: 'Interpreter') -> None:
super().__init__(ep, interpreter)
- self.methods.update({'found': self.found_method,
- 'path': self.path_method,
- 'version': self.version_method,
- 'full_path': self.full_path_method})
@noPosargs
@noKwargs
+ @InterpreterObject.method('found')
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found()
@@ -629,12 +649,14 @@ class _ExternalProgramHolder(ObjectHolder[_EXTPROG]):
@noKwargs
@FeatureDeprecated('ExternalProgram.path', '0.55.0',
'use ExternalProgram.full_path() instead')
+ @InterpreterObject.method('path')
def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._full_path()
@noPosargs
@noKwargs
@FeatureNew('ExternalProgram.full_path', '0.55.0')
+ @InterpreterObject.method('full_path')
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._full_path()
@@ -647,7 +669,19 @@ class _ExternalProgramHolder(ObjectHolder[_EXTPROG]):
@noPosargs
@noKwargs
+ @FeatureNew('ExternalProgram.cmd_array', '1.10.0')
+ @InterpreterObject.method('cmd_array')
+ def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
+ if not self.found():
+ raise InterpreterException('Unable to get the path of a not-found external program')
+ cmd = self.held_object.get_command()
+ assert cmd is not None
+ return cmd
+
+ @noPosargs
+ @noKwargs
@FeatureNew('ExternalProgram.version', '0.62.0')
+ @InterpreterObject.method('version')
def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
if not self.found():
raise InterpreterException('Unable to get the version of a not-found external program')
@@ -665,25 +699,23 @@ class ExternalProgramHolder(_ExternalProgramHolder[ExternalProgram]):
class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
super().__init__(el, interpreter)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'partial_dependency': self.partial_dependency_method,
- 'name': self.name_method,
- })
@noPosargs
@noKwargs
+ @InterpreterObject.method('type_name')
def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name
@noPosargs
@noKwargs
+ @InterpreterObject.method('found')
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.held_object.found()
@FeatureNew('dependency.partial_dependency', '0.46.0')
@noPosargs
@typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ @InterpreterObject.method('partial_dependency')
def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs)
return pdep
@@ -691,6 +723,7 @@ class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
@FeatureNew('dependency.name', '1.5.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('name')
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.name
@@ -699,36 +732,34 @@ class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
class MachineHolder(ObjectHolder['MachineInfo']):
def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'):
super().__init__(machine_info, interpreter)
- self.methods.update({'system': self.system_method,
- 'cpu': self.cpu_method,
- 'cpu_family': self.cpu_family_method,
- 'endian': self.endian_method,
- 'kernel': self.kernel_method,
- 'subsystem': self.subsystem_method,
- })
@noPosargs
@noKwargs
+ @InterpreterObject.method('cpu_family')
def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu_family
@noPosargs
@noKwargs
+ @InterpreterObject.method('cpu')
def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu
@noPosargs
@noKwargs
+ @InterpreterObject.method('system')
def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.system
@noPosargs
@noKwargs
+ @InterpreterObject.method('endian')
def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.endian
@noPosargs
@noKwargs
+ @InterpreterObject.method('kernel')
def kernel_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
if self.held_object.kernel is not None:
return self.held_object.kernel
@@ -736,6 +767,7 @@ class MachineHolder(ObjectHolder['MachineInfo']):
@noPosargs
@noKwargs
+ @InterpreterObject.method('subsystem')
def subsystem_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
if self.held_object.subsystem is not None:
return self.held_object.subsystem
@@ -748,12 +780,11 @@ class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
class FileHolder(ObjectHolder[mesonlib.File]):
def __init__(self, file: mesonlib.File, interpreter: 'Interpreter'):
super().__init__(file, interpreter)
- self.methods.update({'full_path': self.full_path_method,
- })
@noPosargs
@noKwargs
@FeatureNew('file.full_path', '1.4.0')
+ @InterpreterObject.method('full_path')
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.absolute_path(self.env.source_dir, self.env.build_dir)
@@ -836,12 +867,10 @@ class SubprojectHolder(MesonInterpreterObject):
self.subdir = PurePath(subdir).as_posix()
self.cm_interpreter: T.Optional[CMakeInterpreter] = None
self.callstack = callstack
- self.methods.update({'get_variable': self.get_variable_method,
- 'found': self.found_method,
- })
@noPosargs
@noKwargs
+ @InterpreterObject.method('found')
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found()
@@ -863,6 +892,7 @@ class SubprojectHolder(MesonInterpreterObject):
@noKwargs
@typed_pos_args('subproject.get_variable', str, optargs=[object])
@noArgsFlattening
+ @InterpreterObject.method('get_variable')
def get_variable_method(self, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
return self.get_variable(args, kwargs)
@@ -905,16 +935,6 @@ _BuildTarget = T.TypeVar('_BuildTarget', bound=T.Union[build.BuildTarget, build.
class BuildTargetHolder(ObjectHolder[_BuildTarget]):
def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
super().__init__(target, interp)
- self.methods.update({'extract_objects': self.extract_objects_method,
- 'extract_all_objects': self.extract_all_objects_method,
- 'name': self.name_method,
- 'get_id': self.get_id_method,
- 'outdir': self.outdir_method,
- 'full_path': self.full_path_method,
- 'path': self.path_method,
- 'found': self.found_method,
- 'private_dir_include': self.private_dir_include_method,
- })
def __repr__(self) -> str:
r = '<{} {}: {}>'
@@ -934,6 +954,7 @@ class BuildTargetHolder(ObjectHolder[_BuildTarget]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('found')
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
if not (isinstance(self.held_object, build.Executable) and self.held_object.was_returned_by_find_program):
FeatureNew.single_use('BuildTarget.found', '0.59.0', subproject=self.held_object.subproject)
@@ -941,28 +962,35 @@ class BuildTargetHolder(ObjectHolder[_BuildTarget]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('private_dir_include')
def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs:
return build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self._target_object)])
@noPosargs
@noKwargs
+ @InterpreterObject.method('full_path')
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_filename_abs(self._target_object)
@noPosargs
@noKwargs
@FeatureDeprecated('BuildTarget.path', '0.55.0', 'Use BuildTarget.full_path instead')
+ @InterpreterObject.method('path')
def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_filename_abs(self._target_object)
@noPosargs
@noKwargs
+ @InterpreterObject.method('outdir')
def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_dir(self._target_object)
@noKwargs
@typed_pos_args('extract_objects', varargs=(mesonlib.File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
+ @InterpreterObject.method('extract_objects')
def extract_objects_method(self, args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects:
+ if self.subproject != self.held_object.subproject:
+ raise InterpreterException('Tried to extract objects from a different subproject.')
tobj = self._target_object
unity_value = self.interpreter.coredata.get_option_for_target(tobj, "unity")
is_unity = (unity_value == 'on' or (unity_value == 'subprojects' and tobj.subproject != ''))
@@ -981,6 +1009,7 @@ class BuildTargetHolder(ObjectHolder[_BuildTarget]):
''')
)
)
+ @InterpreterObject.method('extract_all_objects')
def extract_all_objects_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.BuildTargeMethodExtractAllObjects') -> build.ExtractedObjects:
return self._target_object.extract_all_objects(kwargs['recursive'])
@@ -989,15 +1018,54 @@ class BuildTargetHolder(ObjectHolder[_BuildTarget]):
@FeatureDeprecated('BuildTarget.get_id', '1.2.0',
'This was never formally documented and does not seem to have a real world use. ' +
'See https://github.com/mesonbuild/meson/pull/6061')
+ @InterpreterObject.method('get_id')
def get_id_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._target_object.get_id()
@FeatureNew('name', '0.54.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('name')
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._target_object.name
+ @FeatureNew('vala_header', '1.10.0')
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('vala_header')
+ def vala_header_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
+ if self._target_object.vala_header is None:
+ raise mesonlib.MesonException("Attempted to get a Vala header from a target that doesn't generate one")
+
+ assert self.interpreter.backend is not None, 'for mypy'
+ return mesonlib.File.from_built_file(
+ self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_header)
+
+ @FeatureNew('vala_vapi', '1.10.0')
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('vala_vapi')
+ def vala_vapi_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
+ if self._target_object.vala_vapi is None:
+ raise mesonlib.MesonException("Attempted to get a Vala VAPI from a target that doesn't generate one")
+
+ assert self.interpreter.backend is not None, 'for mypy'
+ return mesonlib.File.from_built_file(
+ self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_vapi)
+
+ @FeatureNew('vala_gir', '1.10.0')
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('vala_gir')
+ def vala_gir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
+ if self._target_object.vala_gir is None:
+ raise mesonlib.MesonException("Attempted to get a Vala GIR from a target that doesn't generate one")
+
+ assert self.interpreter.backend is not None, 'for mypy'
+ return mesonlib.File.from_built_file(
+ self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_gir)
+
+
class ExecutableHolder(BuildTargetHolder[build.Executable]):
pass
@@ -1010,9 +1078,6 @@ class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
super().__init__(libs, interp)
- self.methods.update({'get_shared_lib': self.get_shared_lib_method,
- 'get_static_lib': self.get_static_lib_method,
- })
def __repr__(self) -> str:
r = '<{} {}: {}, {}: {}>'
@@ -1022,6 +1087,7 @@ class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('get_shared_lib')
def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary:
lib = copy.copy(self.held_object.shared)
lib.both_lib = None
@@ -1029,6 +1095,7 @@ class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('get_static_lib')
def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary:
lib = copy.copy(self.held_object.static)
lib.both_lib = None
@@ -1043,12 +1110,11 @@ class JarHolder(BuildTargetHolder[build.Jar]):
class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]):
def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- })
@FeatureNew('custom_target[i].full_path', '0.54.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('full_path')
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
assert self.interpreter.backend is not None
return self.interpreter.backend.get_target_filename_abs(self.held_object)
@@ -1058,13 +1124,6 @@ _CT = T.TypeVar('_CT', bound=build.CustomTarget)
class _CustomTargetHolder(ObjectHolder[_CT]):
def __init__(self, target: _CT, interp: 'Interpreter'):
super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- 'to_list': self.to_list_method,
- })
-
- self.operators.update({
- MesonOperator.INDEX: self.op_index,
- })
def __repr__(self) -> str:
r = '<{} {}: {}>'
@@ -1073,20 +1132,20 @@ class _CustomTargetHolder(ObjectHolder[_CT]):
@noPosargs
@noKwargs
+ @InterpreterObject.method('full_path')
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_filename_abs(self.held_object)
@FeatureNew('custom_target.to_list', '0.54.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('to_list')
def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]:
- result = []
- for i in self.held_object:
- result.append(i)
- return result
+ return list(self.held_object)
@noKwargs
@typed_operator(MesonOperator.INDEX, int)
+ @InterpreterObject.operator(MesonOperator.INDEX)
def op_index(self, other: int) -> build.CustomTargetIndex:
try:
return self.held_object[other]
@@ -1108,7 +1167,6 @@ class GeneratedListHolder(ObjectHolder[build.GeneratedList]):
class GeneratorHolder(ObjectHolder[build.Generator]):
def __init__(self, gen: build.Generator, interpreter: 'Interpreter'):
super().__init__(gen, interpreter)
- self.methods.update({'process': self.process_method})
@typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
@typed_kwargs(
@@ -1117,6 +1175,7 @@ class GeneratorHolder(ObjectHolder[build.Generator]):
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
ENV_KW.evolve(since='1.3.0')
)
+ @InterpreterObject.method('process')
def process_method(self,
args: T.Tuple[T.List[T.Union[str, mesonlib.File, 'build.GeneratedTypes']]],
kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList:
@@ -1132,7 +1191,7 @@ class GeneratorHolder(ObjectHolder[build.Generator]):
'Calling generator.process with CustomTarget or Index of CustomTarget.',
'0.57.0', self.interpreter.subproject)
- gl = self.held_object.process_files(args[0], self.interpreter,
+ gl = self.held_object.process_files(args[0], self.interpreter.subdir,
preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env'])
return gl
@@ -1142,3 +1201,11 @@ class StructuredSourcesHolder(ObjectHolder[build.StructuredSources]):
def __init__(self, sources: build.StructuredSources, interp: 'Interpreter'):
super().__init__(sources, interp)
+
+class OverrideExecutableHolder(BuildTargetHolder[build.OverrideExecutable]):
+ @noPosargs
+ @noKwargs
+ @FeatureNew('OverrideExecutable.version', '1.9.0')
+ @InterpreterObject.method('version')
+ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.get_version(self.interpreter)
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index fb34bbb..1e20642 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -12,15 +12,15 @@ from typing_extensions import TypedDict, Literal, Protocol, NotRequired
from .. import build
from .. import options
from ..compilers import Compiler
-from ..dependencies.base import Dependency
+from ..dependencies.base import Dependency, DependencyMethods, IncludeType
from ..mesonlib import EnvironmentVariables, MachineChoice, File, FileMode, FileOrString
from ..options import OptionKey
from ..modules.cmake import CMakeSubprojectOptions
from ..programs import ExternalProgram
from .type_checking import PkgConfigDefineType, SourcesVarargsType
-if T.TYPE_CHECKING:
- TestArgs = T.Union[str, File, build.Target, ExternalProgram]
+TestArgs = T.Union[str, File, build.Target, ExternalProgram]
+RustAbi = Literal['rust', 'c']
class FuncAddProjectArgs(TypedDict):
@@ -181,17 +181,17 @@ class CustomTarget(TypedDict):
build_always: bool
build_always_stale: T.Optional[bool]
build_by_default: T.Optional[bool]
+ build_subdir: str
capture: bool
- command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget,
- build.CustomTargetIndex, ExternalProgram, File]]
+ command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, File]]
console: bool
depend_files: T.List[FileOrString]
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
depfile: T.Optional[str]
env: EnvironmentVariables
feed: bool
- input: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex,
- build.ExtractedObjects, build.GeneratedList, ExternalProgram, File]]
+ input: T.List[T.Union[str, build.BuildTarget, build.GeneratedTypes,
+ build.ExtractedObjects, ExternalProgram, File]]
install: bool
install_dir: T.List[T.Union[str, T.Literal[False]]]
install_mode: FileMode
@@ -282,11 +282,10 @@ class ConfigurationDataSet(TypedDict):
class VcsTag(TypedDict):
- command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget,
- build.CustomTargetIndex, ExternalProgram, File]]
+ command: T.List[T.Union[str, build.GeneratedTypes, ExternalProgram, File]]
fallback: T.Optional[str]
- input: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex,
- build.ExtractedObjects, build.GeneratedList, ExternalProgram, File]]
+ input: T.List[T.Union[str, build.BuildTarget, build.GeneratedTypes,
+ build.ExtractedObjects, ExternalProgram, File]]
output: T.List[str]
replace_string: str
install: bool
@@ -311,6 +310,7 @@ class ConfigureFile(TypedDict):
input: T.List[FileOrString]
configuration: T.Optional[T.Union[T.Dict[str, T.Union[str, int, bool]], build.ConfigurationData]]
macro_name: T.Optional[str]
+ build_subdir: str
class Subproject(ExtractRequired):
@@ -321,7 +321,7 @@ class Subproject(ExtractRequired):
class DoSubproject(ExtractRequired):
- default_options: T.List[str]
+ default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
version: T.List[str]
cmake_options: T.List[str]
options: T.Optional[CMakeSubprojectOptions]
@@ -341,17 +341,21 @@ class _BaseBuildTarget(TypedDict):
gnu_symbol_visibility: str
install: bool
install_mode: FileMode
+ install_tag: T.Optional[str]
install_rpath: str
implicit_include_directories: bool
- link_depends: T.List[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex, build.BuildTarget]]
+ link_depends: T.List[T.Union[str, File, build.GeneratedTypes]]
link_language: T.Optional[str]
name_prefix: T.Optional[str]
name_suffix: T.Optional[str]
native: MachineChoice
objects: T.List[build.ObjectTypes]
- override_options: T.Dict[OptionKey, options.ElementaryOptionValues]
+ override_options: T.Dict[str, options.ElementaryOptionValues]
depend_files: NotRequired[T.List[File]]
resources: T.List[str]
+ vala_header: T.Optional[str]
+ vala_vapi: T.Optional[str]
+ vala_gir: T.Optional[str]
class _BuildTarget(_BaseBuildTarget):
@@ -362,8 +366,13 @@ class _BuildTarget(_BaseBuildTarget):
d_import_dirs: T.List[T.Union[str, build.IncludeDirs]]
d_module_versions: T.List[T.Union[str, int]]
d_unittest: bool
+ rust_crate_type: T.Optional[Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro']]
rust_dependency_map: T.Dict[str, str]
+ swift_interoperability_mode: Literal['c', 'cpp']
+ swift_module_name: str
sources: SourcesVarargsType
+ c_pch: T.List[str]
+ cpp_pch: T.List[str]
c_args: T.List[str]
cpp_args: T.List[str]
cuda_args: T.List[str]
@@ -382,7 +391,7 @@ class _BuildTarget(_BaseBuildTarget):
class _LibraryMixin(TypedDict):
- rust_abi: T.Optional[Literal['c', 'rust']]
+ rust_abi: T.Optional[RustAbi]
class Executable(_BuildTarget):
@@ -467,7 +476,7 @@ class Jar(_BaseBuildTarget):
main_class: str
java_resources: T.Optional[build.StructuredSources]
- sources: T.Union[str, File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget]
+ sources: T.Union[str, File, build.GeneratedTypes, build.ExtractedObjects, build.BuildTarget]
java_args: T.List[str]
@@ -486,3 +495,25 @@ class FuncDeclareDependency(TypedDict):
sources: T.List[T.Union[FileOrString, build.GeneratedTypes]]
variables: T.Dict[str, str]
version: T.Optional[str]
+
+
+class FuncDependency(ExtractRequired):
+
+ allow_fallback: T.Optional[bool]
+ cmake_args: T.List[str]
+ cmake_module_path: T.List[str]
+ cmake_package_version: str
+ components: T.List[str]
+ default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
+ fallback: T.Union[str, T.List[str], None]
+ include_type: IncludeType
+ language: T.Optional[str]
+ main: bool
+ method: DependencyMethods
+ modules: T.List[str]
+ native: MachineChoice
+ not_found_message: str
+ optional_modules: T.List[str]
+ private_headers: bool
+ static: T.Optional[bool]
+ version: T.List[str]
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index 8ede691..7c3789f 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -9,16 +9,16 @@ import typing as T
from .. import mesonlib
from .. import dependencies
-from .. import build
-from .. import mlog, coredata
+from .. import build, cmdline
+from .. import mlog
from ..mesonlib import MachineChoice
from ..options import OptionKey
from ..programs import OverrideProgram, ExternalProgram
from ..interpreter.type_checking import ENV_KW, ENV_METHOD_KW, ENV_SEPARATOR_KW, env_convertor_with_method
-from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated,
+from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, FeatureBroken,
typed_pos_args, noArgsFlattening, noPosargs, noKwargs,
- typed_kwargs, KwargInfo, InterpreterException)
+ typed_kwargs, KwargInfo, InterpreterException, InterpreterObject)
from .primitives import MesonVersionString
from .type_checking import NATIVE_KW, NoneType
@@ -26,6 +26,7 @@ if T.TYPE_CHECKING:
from typing_extensions import Literal, TypedDict
from ..compilers import Compiler
+ from ..dependencies.base import DependencyObjectKWs
from ..interpreterbase import TYPE_kwargs, TYPE_var
from ..mesonlib import ExecutableSerialisation
from .interpreter import Interpreter
@@ -55,38 +56,6 @@ class MesonMain(MesonInterpreterObject):
super().__init__(subproject=interpreter.subproject)
self.build = build
self.interpreter = interpreter
- self.methods.update({'add_devenv': self.add_devenv_method,
- 'add_dist_script': self.add_dist_script_method,
- 'add_install_script': self.add_install_script_method,
- 'add_postconf_script': self.add_postconf_script_method,
- 'backend': self.backend_method,
- 'build_options': self.build_options_method,
- 'build_root': self.build_root_method,
- 'can_run_host_binaries': self.can_run_host_binaries_method,
- 'current_source_dir': self.current_source_dir_method,
- 'current_build_dir': self.current_build_dir_method,
- 'get_compiler': self.get_compiler_method,
- 'get_cross_property': self.get_cross_property_method,
- 'get_external_property': self.get_external_property_method,
- 'global_build_root': self.global_build_root_method,
- 'global_source_root': self.global_source_root_method,
- 'has_exe_wrapper': self.has_exe_wrapper_method,
- 'has_external_property': self.has_external_property_method,
- 'install_dependency_manifest': self.install_dependency_manifest_method,
- 'is_cross_build': self.is_cross_build_method,
- 'is_subproject': self.is_subproject_method,
- 'is_unity': self.is_unity_method,
- 'override_dependency': self.override_dependency_method,
- 'override_find_program': self.override_find_program_method,
- 'project_build_root': self.project_build_root_method,
- 'project_license': self.project_license_method,
- 'project_license_files': self.project_license_files_method,
- 'project_name': self.project_name_method,
- 'project_source_root': self.project_source_root_method,
- 'project_version': self.project_version_method,
- 'source_root': self.source_root_method,
- 'version': self.version_method,
- })
def _find_source_script(
self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram],
@@ -157,10 +126,11 @@ class MesonMain(MesonInterpreterObject):
KwargInfo('install_tag', (str, NoneType), since='0.60.0'),
KwargInfo('dry_run', bool, default=False, since='1.1.0'),
)
+ @InterpreterObject.method('add_install_script')
def add_install_script_method(
self,
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]]],
+ T.List[T.Union[str, mesonlib.File, build.BuildTargetTypes, ExternalProgram]]],
kwargs: 'AddInstallScriptKW') -> None:
script_args = self._process_script_args('add_install_script', args[1])
script = self._find_source_script('add_install_script', args[0], script_args)
@@ -175,6 +145,7 @@ class MesonMain(MesonInterpreterObject):
varargs=(str, mesonlib.File, ExternalProgram)
)
@noKwargs
+ @InterpreterObject.method('add_postconf_script')
def add_postconf_script_method(
self,
args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram],
@@ -191,6 +162,7 @@ class MesonMain(MesonInterpreterObject):
)
@noKwargs
@FeatureNew('meson.add_dist_script', '0.48.0')
+ @InterpreterObject.method('add_dist_script')
def add_dist_script_method(
self,
args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram],
@@ -208,6 +180,7 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('current_source_dir')
def current_source_dir_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
src = self.interpreter.environment.source_dir
sub = self.interpreter.subdir
@@ -217,6 +190,7 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('current_build_dir')
def current_build_dir_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
src = self.interpreter.environment.build_dir
sub = self.interpreter.subdir
@@ -226,24 +200,28 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('backend')
def backend_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.backend.name
@noPosargs
@noKwargs
@FeatureDeprecated('meson.source_root', '0.56.0', 'use meson.project_source_root() or meson.global_source_root() instead.')
+ @InterpreterObject.method('source_root')
def source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.environment.source_dir
@noPosargs
@noKwargs
@FeatureDeprecated('meson.build_root', '0.56.0', 'use meson.project_build_root() or meson.global_build_root() instead.')
+ @InterpreterObject.method('build_root')
def build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.environment.build_dir
@noPosargs
@noKwargs
@FeatureNew('meson.project_source_root', '0.56.0')
+ @InterpreterObject.method('project_source_root')
def project_source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
src = self.interpreter.environment.source_dir
sub = self.interpreter.root_subdir
@@ -254,6 +232,7 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
@FeatureNew('meson.project_build_root', '0.56.0')
+ @InterpreterObject.method('project_build_root')
def project_build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
src = self.interpreter.environment.build_dir
sub = self.interpreter.root_subdir
@@ -264,24 +243,28 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
@FeatureNew('meson.global_source_root', '0.58.0')
+ @InterpreterObject.method('global_source_root')
def global_source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.environment.source_dir
@noPosargs
@noKwargs
@FeatureNew('meson.global_build_root', '0.58.0')
+ @InterpreterObject.method('global_build_root')
def global_build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.environment.build_dir
@noPosargs
@noKwargs
@FeatureDeprecated('meson.has_exe_wrapper', '0.55.0', 'use meson.can_run_host_binaries instead.')
+ @InterpreterObject.method('has_exe_wrapper')
def has_exe_wrapper_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
return self._can_run_host_binaries_impl()
@noPosargs
@noKwargs
@FeatureNew('meson.can_run_host_binaries', '0.55.0')
+ @InterpreterObject.method('can_run_host_binaries')
def can_run_host_binaries_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
return self._can_run_host_binaries_impl()
@@ -294,39 +277,51 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('is_cross_build')
def is_cross_build_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
return self.build.environment.is_cross_build()
@typed_pos_args('meson.get_compiler', str)
@typed_kwargs('meson.get_compiler', NATIVE_KW)
+ @InterpreterObject.method('get_compiler')
def get_compiler_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> 'Compiler':
- cname = args[0]
+ lang = args[0]
for_machine = kwargs['native']
- clist = self.interpreter.coredata.compilers[for_machine]
try:
- return clist[cname]
+ return self.interpreter.compilers[for_machine][lang]
except KeyError:
- raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.')
+ try:
+ comp = self.interpreter.coredata.compilers[for_machine][lang]
+ except KeyError:
+ raise InterpreterException(f'Tried to access compiler for language "{lang}", not specified for {for_machine.get_lower_case_name()} machine.')
+
+ FeatureBroken.single_use('Using `meson.get_compiler()` for languages only initialized in another subproject', '1.11.0', self.subproject,
+ 'This is extremely fragile, as your project likely cannot be used outside of your environment.')
+ return comp
@noPosargs
@noKwargs
+ @InterpreterObject.method('is_unity')
def is_unity_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
optval = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('unity'))
return optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject())
@noPosargs
@noKwargs
+ @InterpreterObject.method('is_subproject')
def is_subproject_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
return self.interpreter.is_subproject()
@typed_pos_args('meson.install_dependency_manifest', str)
@noKwargs
+ @InterpreterObject.method('install_dependency_manifest')
def install_dependency_manifest_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> None:
self.build.dep_manifest_name = args[0]
@FeatureNew('meson.override_find_program', '0.46.0')
@typed_pos_args('meson.override_find_program', str, (mesonlib.File, ExternalProgram, build.Executable))
@noKwargs
+ @InterpreterObject.method('override_find_program')
def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, ExternalProgram, build.Executable]], kwargs: 'TYPE_kwargs') -> None:
name, exe = args
if isinstance(exe, mesonlib.File):
@@ -335,6 +330,8 @@ class MesonMain(MesonInterpreterObject):
if not os.path.exists(abspath):
raise InterpreterException(f'Tried to override {name} with a file that does not exist.')
exe = OverrideProgram(name, self.interpreter.project_version, command=[abspath])
+ elif isinstance(exe, build.Executable):
+ exe = build.OverrideExecutable(exe, self.interpreter.project_version)
self.interpreter.add_find_program_override(name, exe)
@typed_kwargs(
@@ -344,6 +341,7 @@ class MesonMain(MesonInterpreterObject):
)
@typed_pos_args('meson.override_dependency', str, dependencies.Dependency)
@FeatureNew('meson.override_dependency', '0.54.0')
+ @InterpreterObject.method('override_dependency')
def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency], kwargs: 'FuncOverrideDependency') -> None:
name, dep = args
if not name:
@@ -390,11 +388,8 @@ class MesonMain(MesonInterpreterObject):
static: T.Optional[bool], permissive: bool = False) -> None:
# We need the cast here as get_dep_identifier works on such a dict,
# which FuncOverrideDependency is, but mypy can't figure that out
- nkwargs = T.cast('T.Dict[str, T.Any]', kwargs.copy())
- if static is None:
- del nkwargs['static']
- else:
- nkwargs['static'] = static
+ nkwargs: DependencyObjectKWs = kwargs.copy() # type: ignore[assignment]
+ nkwargs['static'] = static
identifier = dependencies.get_dep_identifier(name, nkwargs)
for_machine = kwargs['native']
override = self.build.dependency_overrides[for_machine].get(identifier)
@@ -409,28 +404,33 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
+ @InterpreterObject.method('project_version')
def project_version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.build.dep_manifest[self.interpreter.active_projectname].version
@FeatureNew('meson.project_license()', '0.45.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('project_license')
def project_license_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.List[str]:
return self.build.dep_manifest[self.interpreter.active_projectname].license
@FeatureNew('meson.project_license_files()', '1.1.0')
@noPosargs
@noKwargs
+ @InterpreterObject.method('project_license_files')
def project_license_files_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[mesonlib.File]:
return [l[1] for l in self.build.dep_manifest[self.interpreter.active_projectname].license_files]
@noPosargs
@noKwargs
+ @InterpreterObject.method('version')
def version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> MesonVersionString:
return MesonVersionString(self.interpreter.coredata.version)
@noPosargs
@noKwargs
+ @InterpreterObject.method('project_name')
def project_name_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.interpreter.active_projectname
@@ -447,6 +447,7 @@ class MesonMain(MesonInterpreterObject):
@FeatureDeprecated('meson.get_cross_property', '0.58.0', 'Use meson.get_external_property() instead')
@typed_pos_args('meson.get_cross_property', str, optargs=[object])
@noKwargs
+ @InterpreterObject.method('get_cross_property')
def get_cross_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: 'TYPE_kwargs') -> object:
propname, fallback = args
return self.__get_external_property_impl(propname, fallback, MachineChoice.HOST)
@@ -455,6 +456,7 @@ class MesonMain(MesonInterpreterObject):
@FeatureNew('meson.get_external_property', '0.54.0')
@typed_pos_args('meson.get_external_property', str, optargs=[object])
@typed_kwargs('meson.get_external_property', NATIVE_KW)
+ @InterpreterObject.method('get_external_property')
def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: 'NativeKW') -> object:
propname, fallback = args
return self.__get_external_property_impl(propname, fallback, kwargs['native'])
@@ -462,6 +464,7 @@ class MesonMain(MesonInterpreterObject):
@FeatureNew('meson.has_external_property', '0.58.0')
@typed_pos_args('meson.has_external_property', str)
@typed_kwargs('meson.has_external_property', NATIVE_KW)
+ @InterpreterObject.method('has_external_property')
def has_external_property_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> bool:
prop_name = args[0]
return prop_name in self.interpreter.environment.properties[kwargs['native']]
@@ -469,6 +472,7 @@ class MesonMain(MesonInterpreterObject):
@FeatureNew('add_devenv', '0.58.0')
@typed_kwargs('environment', ENV_METHOD_KW, ENV_SEPARATOR_KW.evolve(since='0.62.0'))
@typed_pos_args('add_devenv', (str, list, dict, mesonlib.EnvironmentVariables))
+ @InterpreterObject.method('add_devenv')
def add_devenv_method(self, args: T.Tuple[T.Union[str, list, dict, mesonlib.EnvironmentVariables]],
kwargs: 'AddDevenvKW') -> None:
env = args[0]
@@ -482,8 +486,9 @@ class MesonMain(MesonInterpreterObject):
@noPosargs
@noKwargs
@FeatureNew('meson.build_options', '1.1.0')
+ @InterpreterObject.method('build_options')
def build_options_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
options = self.interpreter.user_defined_options
if options is None:
return ''
- return coredata.format_cmd_line_options(options)
+ return cmdline.format_cmd_line_options(options)
diff --git a/mesonbuild/interpreter/primitives/array.py b/mesonbuild/interpreter/primitives/array.py
index b42ddea..86c1ce2 100644
--- a/mesonbuild/interpreter/primitives/array.py
+++ b/mesonbuild/interpreter/primitives/array.py
@@ -5,13 +5,16 @@ from __future__ import annotations
import typing as T
from ...interpreterbase import (
- ObjectHolder,
+ InterpreterObject,
IterableObject,
+ KwargInfo,
MesonOperator,
+ ObjectHolder,
typed_operator,
noKwargs,
noPosargs,
noArgsFlattening,
+ typed_kwargs,
typed_pos_args,
FeatureNew,
@@ -22,31 +25,16 @@ from ...interpreterbase import (
from ...mparser import PlusAssignmentNode
if T.TYPE_CHECKING:
- # Object holders need the actual interpreter
- from ...interpreter import Interpreter
from ...interpreterbase import TYPE_kwargs
class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
- def __init__(self, obj: T.List[TYPE_var], interpreter: 'Interpreter') -> None:
- super().__init__(obj, interpreter)
- self.methods.update({
- 'contains': self.contains_method,
- 'length': self.length_method,
- 'get': self.get_method,
- })
-
- self.trivial_operators.update({
- MesonOperator.EQUALS: (list, lambda x: self.held_object == x),
- MesonOperator.NOT_EQUALS: (list, lambda x: self.held_object != x),
- MesonOperator.IN: (object, lambda x: x in self.held_object),
- MesonOperator.NOT_IN: (object, lambda x: x not in self.held_object),
- })
-
- # Use actual methods for functions that require additional checks
- self.operators.update({
- MesonOperator.PLUS: self.op_plus,
- MesonOperator.INDEX: self.op_index,
- })
+ # Operators that only require type checks
+ TRIVIAL_OPERATORS = {
+ MesonOperator.EQUALS: (list, lambda obj, x: obj.held_object == x),
+ MesonOperator.NOT_EQUALS: (list, lambda obj, x: obj.held_object != x),
+ MesonOperator.IN: (object, lambda obj, x: x in obj.held_object),
+ MesonOperator.NOT_IN: (object, lambda obj, x: x not in obj.held_object),
+ }
def display_name(self) -> str:
return 'array'
@@ -63,6 +51,7 @@ class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
@noArgsFlattening
@noKwargs
@typed_pos_args('array.contains', object)
+ @InterpreterObject.method('contains')
def contains_method(self, args: T.Tuple[object], kwargs: TYPE_kwargs) -> bool:
def check_contains(el: T.List[TYPE_var]) -> bool:
for element in el:
@@ -77,12 +66,14 @@ class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
@noKwargs
@noPosargs
+ @InterpreterObject.method('length')
def length_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
return len(self.held_object)
@noArgsFlattening
@noKwargs
@typed_pos_args('array.get', int, optargs=[object])
+ @InterpreterObject.method('get')
def get_method(self, args: T.Tuple[int, T.Optional[TYPE_var]], kwargs: TYPE_kwargs) -> TYPE_var:
index = args[0]
if index < -len(self.held_object) or index >= len(self.held_object):
@@ -91,7 +82,20 @@ class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
return args[1]
return self.held_object[index]
+ @FeatureNew('array.slice', '1.10.0')
+ @typed_kwargs('array.slice', KwargInfo('step', int, default=1))
+ @typed_pos_args('array.slice', optargs=[int, int])
+ @InterpreterObject.method('slice')
+ def slice_method(self, args: T.Tuple[T.Optional[int], T.Optional[int]], kwargs: T.Dict[str, int]) -> TYPE_var:
+ start, stop = args
+ if start is not None and stop is None:
+ raise InvalidArguments('Providing only one positional slice argument is ambiguous.')
+ if kwargs['step'] == 0:
+ raise InvalidArguments('Slice step cannot be zero.')
+ return self.held_object[start:stop:kwargs['step']]
+
@typed_operator(MesonOperator.PLUS, object)
+ @InterpreterObject.operator(MesonOperator.PLUS)
def op_plus(self, other: TYPE_var) -> T.List[TYPE_var]:
if not isinstance(other, list):
if not isinstance(self.current_node, PlusAssignmentNode):
@@ -101,8 +105,23 @@ class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
return self.held_object + other
@typed_operator(MesonOperator.INDEX, int)
+ @InterpreterObject.operator(MesonOperator.INDEX)
def op_index(self, other: int) -> TYPE_var:
try:
return self.held_object[other]
except IndexError:
raise InvalidArguments(f'Index {other} out of bounds of array of size {len(self.held_object)}.')
+
+ @noPosargs
+ @noKwargs
+ @FeatureNew('array.flatten', '1.9.0')
+ @InterpreterObject.method('flatten')
+ def flatten_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
+ def flatten(obj: TYPE_var) -> T.Iterable[TYPE_var]:
+ if isinstance(obj, list):
+ for o in obj:
+ yield from flatten(o)
+ else:
+ yield obj
+
+ return list(flatten(self.held_object))
diff --git a/mesonbuild/interpreter/primitives/boolean.py b/mesonbuild/interpreter/primitives/boolean.py
index 4b49caf..01521ac 100644
--- a/mesonbuild/interpreter/primitives/boolean.py
+++ b/mesonbuild/interpreter/primitives/boolean.py
@@ -1,10 +1,11 @@
# Copyright 2021 The Meson development team
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
from ...interpreterbase import (
- ObjectHolder,
+ InterpreterObject,
MesonOperator,
+ ObjectHolder,
typed_pos_args,
noKwargs,
noPosargs,
@@ -15,35 +16,28 @@ from ...interpreterbase import (
import typing as T
if T.TYPE_CHECKING:
- # Object holders need the actual interpreter
- from ...interpreter import Interpreter
from ...interpreterbase import TYPE_var, TYPE_kwargs
class BooleanHolder(ObjectHolder[bool]):
- def __init__(self, obj: bool, interpreter: 'Interpreter') -> None:
- super().__init__(obj, interpreter)
- self.methods.update({
- 'to_int': self.to_int_method,
- 'to_string': self.to_string_method,
- })
-
- self.trivial_operators.update({
- MesonOperator.BOOL: (None, lambda x: self.held_object),
- MesonOperator.NOT: (None, lambda x: not self.held_object),
- MesonOperator.EQUALS: (bool, lambda x: self.held_object == x),
- MesonOperator.NOT_EQUALS: (bool, lambda x: self.held_object != x),
- })
+ TRIVIAL_OPERATORS = {
+ MesonOperator.BOOL: (None, lambda obj, x: obj.held_object),
+ MesonOperator.NOT: (None, lambda obj, x: not obj.held_object),
+ MesonOperator.EQUALS: (bool, lambda obj, x: obj.held_object == x),
+ MesonOperator.NOT_EQUALS: (bool, lambda obj, x: obj.held_object != x),
+ }
def display_name(self) -> str:
return 'bool'
@noKwargs
@noPosargs
+ @InterpreterObject.method('to_int')
def to_int_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
return 1 if self.held_object else 0
@noKwargs
@typed_pos_args('bool.to_string', optargs=[str, str])
+ @InterpreterObject.method('to_string')
def to_string_method(self, args: T.Tuple[T.Optional[str], T.Optional[str]], kwargs: TYPE_kwargs) -> str:
true_str = args[0] or 'true'
false_str = args[1] or 'false'
diff --git a/mesonbuild/interpreter/primitives/dict.py b/mesonbuild/interpreter/primitives/dict.py
index ab4c15f..794c150 100644
--- a/mesonbuild/interpreter/primitives/dict.py
+++ b/mesonbuild/interpreter/primitives/dict.py
@@ -5,9 +5,11 @@ from __future__ import annotations
import typing as T
from ...interpreterbase import (
- ObjectHolder,
+ InterpreterObject,
IterableObject,
MesonOperator,
+ ObjectHolder,
+ FeatureNew,
typed_operator,
noKwargs,
noPosargs,
@@ -20,34 +22,20 @@ from ...interpreterbase import (
)
if T.TYPE_CHECKING:
- # Object holders need the actual interpreter
- from ...interpreter import Interpreter
from ...interpreterbase import TYPE_kwargs
class DictHolder(ObjectHolder[T.Dict[str, TYPE_var]], IterableObject):
- def __init__(self, obj: T.Dict[str, TYPE_var], interpreter: 'Interpreter') -> None:
- super().__init__(obj, interpreter)
- self.methods.update({
- 'has_key': self.has_key_method,
- 'keys': self.keys_method,
- 'get': self.get_method,
- })
-
- self.trivial_operators.update({
- # Arithmetic
- MesonOperator.PLUS: (dict, lambda x: {**self.held_object, **x}),
-
- # Comparison
- MesonOperator.EQUALS: (dict, lambda x: self.held_object == x),
- MesonOperator.NOT_EQUALS: (dict, lambda x: self.held_object != x),
- MesonOperator.IN: (str, lambda x: x in self.held_object),
- MesonOperator.NOT_IN: (str, lambda x: x not in self.held_object),
- })
-
- # Use actual methods for functions that require additional checks
- self.operators.update({
- MesonOperator.INDEX: self.op_index,
- })
+ # Operators that only require type checks
+ TRIVIAL_OPERATORS = {
+ # Arithmetic
+ MesonOperator.PLUS: (dict, lambda obj, x: {**obj.held_object, **x}),
+
+ # Comparison
+ MesonOperator.EQUALS: (dict, lambda obj, x: obj.held_object == x),
+ MesonOperator.NOT_EQUALS: (dict, lambda obj, x: obj.held_object != x),
+ MesonOperator.IN: (str, lambda obj, x: x in obj.held_object),
+ MesonOperator.NOT_IN: (str, lambda obj, x: x not in obj.held_object),
+ }
def display_name(self) -> str:
return 'dict'
@@ -61,19 +49,32 @@ class DictHolder(ObjectHolder[T.Dict[str, TYPE_var]], IterableObject):
def size(self) -> int:
return len(self.held_object)
+ def _keys_getter(self) -> T.List[str]:
+ return sorted(self.held_object)
+
@noKwargs
@typed_pos_args('dict.has_key', str)
+ @InterpreterObject.method('has_key')
def has_key_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
return args[0] in self.held_object
@noKwargs
@noPosargs
+ @InterpreterObject.method('keys')
def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
- return sorted(self.held_object)
+ return self._keys_getter()
+
+ @noKwargs
+ @noPosargs
+ @InterpreterObject.method('values')
+ @FeatureNew('dict.values', '1.10.0')
+ def values_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[TYPE_var]:
+ return [self.held_object[k] for k in self._keys_getter()]
@noArgsFlattening
@noKwargs
@typed_pos_args('dict.get', str, optargs=[object])
+ @InterpreterObject.method('get')
def get_method(self, args: T.Tuple[str, T.Optional[TYPE_var]], kwargs: TYPE_kwargs) -> TYPE_var:
if args[0] in self.held_object:
return self.held_object[args[0]]
@@ -82,6 +83,7 @@ class DictHolder(ObjectHolder[T.Dict[str, TYPE_var]], IterableObject):
raise InvalidArguments(f'Key {args[0]!r} is not in the dictionary.')
@typed_operator(MesonOperator.INDEX, str)
+ @InterpreterObject.operator(MesonOperator.INDEX)
def op_index(self, other: str) -> TYPE_var:
if other not in self.held_object:
raise InvalidArguments(f'Key {other} is not in the dictionary.')
diff --git a/mesonbuild/interpreter/primitives/integer.py b/mesonbuild/interpreter/primitives/integer.py
index cdf2355..c59ea6e 100644
--- a/mesonbuild/interpreter/primitives/integer.py
+++ b/mesonbuild/interpreter/primitives/integer.py
@@ -3,47 +3,33 @@
from __future__ import annotations
from ...interpreterbase import (
- FeatureBroken, InvalidArguments, MesonOperator, ObjectHolder, KwargInfo,
+ InterpreterObject, MesonOperator, ObjectHolder,
+ FeatureBroken, InvalidArguments, KwargInfo,
noKwargs, noPosargs, typed_operator, typed_kwargs
)
import typing as T
if T.TYPE_CHECKING:
- # Object holders need the actual interpreter
- from ...interpreter import Interpreter
from ...interpreterbase import TYPE_var, TYPE_kwargs
class IntegerHolder(ObjectHolder[int]):
- def __init__(self, obj: int, interpreter: 'Interpreter') -> None:
- super().__init__(obj, interpreter)
- self.methods.update({
- 'is_even': self.is_even_method,
- 'is_odd': self.is_odd_method,
- 'to_string': self.to_string_method,
- })
+ # Operators that only require type checks
+ TRIVIAL_OPERATORS = {
+ # Arithmetic
+ MesonOperator.UMINUS: (None, lambda obj, x: -obj.held_object),
+ MesonOperator.PLUS: (int, lambda obj, x: obj.held_object + x),
+ MesonOperator.MINUS: (int, lambda obj, x: obj.held_object - x),
+ MesonOperator.TIMES: (int, lambda obj, x: obj.held_object * x),
- self.trivial_operators.update({
- # Arithmetic
- MesonOperator.UMINUS: (None, lambda x: -self.held_object),
- MesonOperator.PLUS: (int, lambda x: self.held_object + x),
- MesonOperator.MINUS: (int, lambda x: self.held_object - x),
- MesonOperator.TIMES: (int, lambda x: self.held_object * x),
-
- # Comparison
- MesonOperator.EQUALS: (int, lambda x: self.held_object == x),
- MesonOperator.NOT_EQUALS: (int, lambda x: self.held_object != x),
- MesonOperator.GREATER: (int, lambda x: self.held_object > x),
- MesonOperator.LESS: (int, lambda x: self.held_object < x),
- MesonOperator.GREATER_EQUALS: (int, lambda x: self.held_object >= x),
- MesonOperator.LESS_EQUALS: (int, lambda x: self.held_object <= x),
- })
-
- # Use actual methods for functions that require additional checks
- self.operators.update({
- MesonOperator.DIV: self.op_div,
- MesonOperator.MOD: self.op_mod,
- })
+ # Comparison
+ MesonOperator.EQUALS: (int, lambda obj, x: obj.held_object == x),
+ MesonOperator.NOT_EQUALS: (int, lambda obj, x: obj.held_object != x),
+ MesonOperator.GREATER: (int, lambda obj, x: obj.held_object > x),
+ MesonOperator.LESS: (int, lambda obj, x: obj.held_object < x),
+ MesonOperator.GREATER_EQUALS: (int, lambda obj, x: obj.held_object >= x),
+ MesonOperator.LESS_EQUALS: (int, lambda obj, x: obj.held_object <= x),
+ }
def display_name(self) -> str:
return 'int'
@@ -57,11 +43,13 @@ class IntegerHolder(ObjectHolder[int]):
@noKwargs
@noPosargs
+ @InterpreterObject.method('is_even')
def is_even_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.held_object % 2 == 0
@noKwargs
@noPosargs
+ @InterpreterObject.method('is_odd')
def is_odd_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.held_object % 2 != 0
@@ -70,16 +58,19 @@ class IntegerHolder(ObjectHolder[int]):
KwargInfo('fill', int, default=0, since='1.3.0')
)
@noPosargs
+ @InterpreterObject.method('to_string')
def to_string_method(self, args: T.List[TYPE_var], kwargs: T.Dict[str, T.Any]) -> str:
return str(self.held_object).zfill(kwargs['fill'])
@typed_operator(MesonOperator.DIV, int)
+ @InterpreterObject.operator(MesonOperator.DIV)
def op_div(self, other: int) -> int:
if other == 0:
raise InvalidArguments('Tried to divide by 0')
return self.held_object // other
@typed_operator(MesonOperator.MOD, int)
+ @InterpreterObject.operator(MesonOperator.MOD)
def op_mod(self, other: int) -> int:
if other == 0:
raise InvalidArguments('Tried to divide by 0')
diff --git a/mesonbuild/interpreter/primitives/range.py b/mesonbuild/interpreter/primitives/range.py
index 23d5617..1aceb68 100644
--- a/mesonbuild/interpreter/primitives/range.py
+++ b/mesonbuild/interpreter/primitives/range.py
@@ -5,8 +5,9 @@ from __future__ import annotations
import typing as T
from ...interpreterbase import (
- MesonInterpreterObject,
+ InterpreterObject,
IterableObject,
+ MesonInterpreterObject,
MesonOperator,
InvalidArguments,
)
@@ -18,10 +19,8 @@ class RangeHolder(MesonInterpreterObject, IterableObject):
def __init__(self, start: int, stop: int, step: int, *, subproject: 'SubProject') -> None:
super().__init__(subproject=subproject)
self.range = range(start, stop, step)
- self.operators.update({
- MesonOperator.INDEX: self.op_index,
- })
+ @InterpreterObject.operator(MesonOperator.INDEX)
def op_index(self, other: int) -> int:
try:
return self.range[other]
diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py
index a224dfa..2adc58d 100644
--- a/mesonbuild/interpreter/primitives/string.py
+++ b/mesonbuild/interpreter/primitives/string.py
@@ -7,10 +7,12 @@ import os
import typing as T
-from ...mesonlib import version_compare, version_compare_many
+from ... import mlog
+from ...mesonlib import version_compare_many, underscorify
from ...interpreterbase import (
- ObjectHolder,
+ InterpreterObject,
MesonOperator,
+ ObjectHolder,
FeatureNew,
typed_operator,
noArgsFlattening,
@@ -24,73 +26,47 @@ from ...interpreterbase import (
if T.TYPE_CHECKING:
- # Object holders need the actual interpreter
- from ...interpreter import Interpreter
from ...interpreterbase import TYPE_var, TYPE_kwargs
class StringHolder(ObjectHolder[str]):
- def __init__(self, obj: str, interpreter: 'Interpreter') -> None:
- super().__init__(obj, interpreter)
- self.methods.update({
- 'contains': self.contains_method,
- 'startswith': self.startswith_method,
- 'endswith': self.endswith_method,
- 'format': self.format_method,
- 'join': self.join_method,
- 'replace': self.replace_method,
- 'split': self.split_method,
- 'splitlines': self.splitlines_method,
- 'strip': self.strip_method,
- 'substring': self.substring_method,
- 'to_int': self.to_int_method,
- 'to_lower': self.to_lower_method,
- 'to_upper': self.to_upper_method,
- 'underscorify': self.underscorify_method,
- 'version_compare': self.version_compare_method,
- })
-
- self.trivial_operators.update({
- # Arithmetic
- MesonOperator.PLUS: (str, lambda x: self.held_object + x),
-
- # Comparison
- MesonOperator.EQUALS: (str, lambda x: self.held_object == x),
- MesonOperator.NOT_EQUALS: (str, lambda x: self.held_object != x),
- MesonOperator.GREATER: (str, lambda x: self.held_object > x),
- MesonOperator.LESS: (str, lambda x: self.held_object < x),
- MesonOperator.GREATER_EQUALS: (str, lambda x: self.held_object >= x),
- MesonOperator.LESS_EQUALS: (str, lambda x: self.held_object <= x),
- })
-
- # Use actual methods for functions that require additional checks
- self.operators.update({
- MesonOperator.DIV: self.op_div,
- MesonOperator.INDEX: self.op_index,
- MesonOperator.IN: self.op_in,
- MesonOperator.NOT_IN: self.op_notin,
- })
+ TRIVIAL_OPERATORS = {
+ # Arithmetic
+ MesonOperator.PLUS: (str, lambda obj, x: obj.held_object + x),
+
+ # Comparison
+ MesonOperator.EQUALS: (str, lambda obj, x: obj.held_object == x),
+ MesonOperator.NOT_EQUALS: (str, lambda obj, x: obj.held_object != x),
+ MesonOperator.GREATER: (str, lambda obj, x: obj.held_object > x),
+ MesonOperator.LESS: (str, lambda obj, x: obj.held_object < x),
+ MesonOperator.GREATER_EQUALS: (str, lambda obj, x: obj.held_object >= x),
+ MesonOperator.LESS_EQUALS: (str, lambda obj, x: obj.held_object <= x),
+ }
def display_name(self) -> str:
return 'str'
@noKwargs
@typed_pos_args('str.contains', str)
+ @InterpreterObject.method('contains')
def contains_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
return self.held_object.find(args[0]) >= 0
@noKwargs
@typed_pos_args('str.startswith', str)
+ @InterpreterObject.method('startswith')
def startswith_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
return self.held_object.startswith(args[0])
@noKwargs
@typed_pos_args('str.endswith', str)
+ @InterpreterObject.method('endswith')
def endswith_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
return self.held_object.endswith(args[0])
@noArgsFlattening
@noKwargs
@typed_pos_args('str.format', varargs=object)
+ @InterpreterObject.method('format')
def format_method(self, args: T.Tuple[T.List[TYPE_var]], kwargs: TYPE_kwargs) -> str:
arg_strings: T.List[str] = []
for arg in args[0]:
@@ -111,27 +87,35 @@ class StringHolder(ObjectHolder[str]):
@noKwargs
@noPosargs
@FeatureNew('str.splitlines', '1.2.0')
+ @InterpreterObject.method('splitlines')
def splitlines_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
return self.held_object.splitlines()
@noKwargs
@typed_pos_args('str.join', varargs=str)
+ @InterpreterObject.method('join')
def join_method(self, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> str:
return self.held_object.join(args[0])
@noKwargs
@FeatureNew('str.replace', '0.58.0')
@typed_pos_args('str.replace', str, str)
+ @InterpreterObject.method('replace')
def replace_method(self, args: T.Tuple[str, str], kwargs: TYPE_kwargs) -> str:
return self.held_object.replace(args[0], args[1])
@noKwargs
@typed_pos_args('str.split', optargs=[str])
+ @InterpreterObject.method('split')
def split_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> T.List[str]:
- return self.held_object.split(args[0])
+ delimiter = args[0]
+ if delimiter == '':
+ raise InvalidArguments('str.split() delimitier must not be an empty string')
+ return self.held_object.split(delimiter)
@noKwargs
@typed_pos_args('str.strip', optargs=[str])
+ @InterpreterObject.method('strip')
def strip_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> str:
if args[0]:
FeatureNew.single_use('str.strip with a positional argument', '0.43.0', self.subproject, location=self.current_node)
@@ -140,6 +124,7 @@ class StringHolder(ObjectHolder[str]):
@noKwargs
@FeatureNew('str.substring', '0.56.0')
@typed_pos_args('str.substring', optargs=[int, int])
+ @InterpreterObject.method('substring')
def substring_method(self, args: T.Tuple[T.Optional[int], T.Optional[int]], kwargs: TYPE_kwargs) -> str:
start = args[0] if args[0] is not None else 0
end = args[1] if args[1] is not None else len(self.held_object)
@@ -147,6 +132,7 @@ class StringHolder(ObjectHolder[str]):
@noKwargs
@noPosargs
+ @InterpreterObject.method('to_int')
def to_int_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
try:
return int(self.held_object)
@@ -155,20 +141,24 @@ class StringHolder(ObjectHolder[str]):
@noKwargs
@noPosargs
+ @InterpreterObject.method('to_lower')
def to_lower_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.lower()
@noKwargs
@noPosargs
+ @InterpreterObject.method('to_upper')
def to_upper_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.upper()
@noKwargs
@noPosargs
+ @InterpreterObject.method('underscorify')
def underscorify_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
- return re.sub(r'[^a-zA-Z0-9]', '_', self.held_object)
+ return underscorify(self.held_object)
@noKwargs
+ @InterpreterObject.method('version_compare')
@typed_pos_args('str.version_compare', varargs=str, min_varargs=1)
def version_compare_method(self, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> bool:
if len(args[0]) > 1:
@@ -181,10 +171,12 @@ class StringHolder(ObjectHolder[str]):
@FeatureNew('/ with string arguments', '0.49.0')
@typed_operator(MesonOperator.DIV, str)
+ @InterpreterObject.operator(MesonOperator.DIV)
def op_div(self, other: str) -> str:
return self._op_div(self.held_object, other)
@typed_operator(MesonOperator.INDEX, int)
+ @InterpreterObject.operator(MesonOperator.INDEX)
def op_index(self, other: int) -> str:
try:
return self.held_object[other]
@@ -193,11 +185,13 @@ class StringHolder(ObjectHolder[str]):
@FeatureNew('"in" string operator', '1.0.0')
@typed_operator(MesonOperator.IN, str)
+ @InterpreterObject.operator(MesonOperator.IN)
def op_in(self, other: str) -> bool:
return other in self.held_object
@FeatureNew('"not in" string operator', '1.0.0')
@typed_operator(MesonOperator.NOT_IN, str)
+ @InterpreterObject.operator(MesonOperator.NOT_IN)
def op_notin(self, other: str) -> bool:
return other not in self.held_object
@@ -207,10 +201,26 @@ class MesonVersionString(str):
class MesonVersionStringHolder(StringHolder):
@noKwargs
- @typed_pos_args('str.version_compare', str)
- def version_compare_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
- self.interpreter.tmp_meson_version = args[0]
- return version_compare(self.held_object, args[0])
+ @InterpreterObject.method('version_compare')
+ @typed_pos_args('str.version_compare', varargs=str, min_varargs=1)
+ def version_compare_method(self, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> bool:
+ unsupported = []
+ for constraint in args[0]:
+ if not constraint.strip().startswith('>'):
+ unsupported.append('non-upper-bounds (> or >=) constraints')
+ if len(args[0]) > 1:
+ FeatureNew.single_use('meson.version().version_compare() with multiple arguments', '1.10.0',
+ self.subproject, 'From 1.8.0 - 1.9.* it failed to match str.version_compare',
+ location=self.current_node)
+ unsupported.append('multiple arguments')
+ else:
+ self.interpreter.tmp_meson_version = args[0][0]
+ if unsupported:
+ mlog.debug('meson.version().version_compare() with', ' or '.join(unsupported),
+ 'does not support overriding minimum meson_version checks.')
+
+ return version_compare_many(self.held_object, args[0])[0]
+
# These special subclasses of string exist to cover the case where a dependency
# exports a string variable interchangeable with a system dependency. This
@@ -221,6 +231,7 @@ class DependencyVariableString(str):
pass
class DependencyVariableStringHolder(StringHolder):
+ @InterpreterObject.operator(MesonOperator.DIV)
def op_div(self, other: str) -> T.Union[str, DependencyVariableString]:
ret = super().op_div(other)
if '..' in other:
@@ -243,6 +254,7 @@ class OptionString(str):
class OptionStringHolder(StringHolder):
held_object: OptionString
+ @InterpreterObject.operator(MesonOperator.DIV)
def op_div(self, other: str) -> T.Union[str, OptionString]:
ret = super().op_div(other)
name = self._op_div(self.held_object.optname, other)
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index 78938ba..743046c 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -11,10 +11,10 @@ from .. import compilers
from ..build import (CustomTarget, BuildTarget,
CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs,
BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources)
-from ..options import UserFeatureOption
-from ..dependencies import Dependency, InternalDependency
-from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo
-from ..mesonlib import (File, FileMode, MachineChoice, listify, has_path_sep,
+from ..options import OptionKey, UserFeatureOption
+from ..dependencies import Dependency, DependencyMethods, InternalDependency
+from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo, FeatureBroken, FeatureDeprecated
+from ..mesonlib import (File, FileMode, MachineChoice, has_path_sep, listify, stringlistify,
EnvironmentVariables)
from ..programs import ExternalProgram
@@ -24,14 +24,15 @@ NoneType: T.Type[None] = type(None)
if T.TYPE_CHECKING:
from typing_extensions import Literal
- from ..build import ObjectTypes
+ from ..build import ObjectTypes, GeneratedTypes, BuildTargetTypes
from ..interpreterbase import TYPE_var
from ..options import ElementaryOptionValues
from ..mesonlib import EnvInitValueType
+ from ..interpreterbase.decorators import FeatureCheckBase
_FullEnvInitValueType = T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], EnvInitValueType, str, None]
PkgConfigDefineType = T.Optional[T.Tuple[T.Tuple[str, str], ...]]
- SourcesVarargsType = T.List[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget]]
+ SourcesVarargsType = T.List[T.Union[str, File, GeneratedTypes, StructuredSources, ExtractedObjects, BuildTarget]]
def in_set_validator(choices: T.Set[str]) -> T.Callable[[str], T.Optional[str]]:
@@ -269,7 +270,7 @@ DEPFILE_KW: KwargInfo[T.Optional[str]] = KwargInfo(
validator=lambda x: 'Depfile must be a plain filename with a subdirectory' if has_path_sep(x) else None
)
-DEPENDS_KW: KwargInfo[T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]] = KwargInfo(
+DEPENDS_KW: KwargInfo[T.List[BuildTargetTypes]] = KwargInfo(
'depends',
ContainerTypeInfo(list, (BuildTarget, CustomTarget, CustomTargetIndex)),
listify=True,
@@ -284,7 +285,7 @@ DEPEND_FILES_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo(
default=[],
)
-COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File]]] = KwargInfo(
+COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTargetTypes, ExternalProgram, File]]] = KwargInfo(
'command',
ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File), allow_empty=False),
required=True,
@@ -293,11 +294,22 @@ COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTarget, CustomTarget, CustomTarge
)
-OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.Dict[str, ElementaryOptionValues], T.List[str]]] = KwargInfo(
+def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, ElementaryOptionValues]]) -> T.Dict[str, ElementaryOptionValues]:
+ if isinstance(raw, dict):
+ return raw
+ raw = stringlistify(raw)
+ output: T.Dict[str, ElementaryOptionValues] = {}
+ for each in raw:
+ k, v = split_equal_string(each)
+ output[k] = v
+ return output
+
+OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.List[str], T.Dict[str, ElementaryOptionValues]]] = KwargInfo(
'override_options',
(str, ContainerTypeInfo(list, str), ContainerTypeInfo(dict, (str, int, bool, list))),
default={},
validator=_options_validator,
+ convertor=_override_options_convertor,
since_values={dict: '1.2.0'},
)
@@ -338,7 +350,7 @@ OUTPUT_KW: KwargInfo[str] = KwargInfo(
validator=lambda x: _output_validator([x])
)
-CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, ExternalProgram, BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, GeneratedList]]] = KwargInfo(
+CT_INPUT_KW: KwargInfo[T.List[T.Union[str, File, ExternalProgram, BuildTarget, GeneratedTypes, ExtractedObjects]]] = KwargInfo(
'input',
ContainerTypeInfo(list, (str, File, ExternalProgram, BuildTarget, CustomTarget, CustomTargetIndex, ExtractedObjects, GeneratedList)),
listify=True,
@@ -394,7 +406,13 @@ INCLUDE_DIRECTORIES: KwargInfo[T.List[T.Union[str, IncludeDirs]]] = KwargInfo(
default=[],
)
-DEFAULT_OPTIONS = OVERRIDE_OPTIONS_KW.evolve(name='default_options')
+def _default_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, ElementaryOptionValues]]) -> T.Dict[OptionKey, ElementaryOptionValues]:
+ d = _override_options_convertor(raw)
+ return {OptionKey.from_string(k): v for k, v in d.items()}
+
+DEFAULT_OPTIONS = OVERRIDE_OPTIONS_KW.evolve(
+ name='default_options',
+ convertor=_default_options_convertor)
ENV_METHOD_KW = KwargInfo('method', str, default='set', since='0.62.0',
validator=in_set_validator({'set', 'prepend', 'append'}))
@@ -444,7 +462,7 @@ LINK_WHOLE_KW: KwargInfo[T.List[T.Union[BothLibraries, StaticLibrary, CustomTarg
validator=link_whole_validator,
)
-DEPENDENCY_SOURCES_KW: KwargInfo[T.List[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]]] = KwargInfo(
+DEPENDENCY_SOURCES_KW: KwargInfo[T.List[T.Union[str, File, GeneratedTypes]]] = KwargInfo(
'sources',
ContainerTypeInfo(list, (str, File, CustomTarget, CustomTargetIndex, GeneratedList)),
listify=True,
@@ -472,6 +490,12 @@ VARIABLES_KW: KwargInfo[T.Dict[str, str]] = KwargInfo(
PRESERVE_PATH_KW: KwargInfo[bool] = KwargInfo('preserve_path', bool, default=False, since='0.63.0')
+def suite_convertor(suite: T.List[str]) -> T.List[str]:
+ # Ensure we always have at least one suite.
+ if not suite:
+ return ['']
+ return suite
+
TEST_KWS_NO_ARGS: T.List[KwargInfo] = [
KwargInfo('should_fail', bool, default=False),
KwargInfo('timeout', int, default=30),
@@ -485,7 +509,7 @@ TEST_KWS_NO_ARGS: T.List[KwargInfo] = [
# TODO: env needs reworks of the way the environment variable holder itself works probably
ENV_KW,
DEPENDS_KW.evolve(since='0.46.0'),
- KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=['']), # yes, a list of empty string
+ KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=[], convertor=suite_convertor),
KwargInfo('verbose', bool, default=False, since='0.62.0'),
]
@@ -553,14 +577,44 @@ def _objects_validator(vals: T.List[ObjectTypes]) -> T.Optional[str]:
return None
+def _target_install_feature_validator(val: object) -> T.Iterable[FeatureCheckBase]:
+ # due to lack of type checking, these are "allowed" for legacy reasons
+ if not isinstance(val, bool):
+ yield FeatureBroken('install kwarg with non-boolean value', '1.3.0',
+ 'This was never intended to work, and is essentially the same as using `install: true` regardless of value.')
+
+
+def _target_install_convertor(val: object) -> bool:
+ return bool(val)
+
+
+def _extra_files_validator(args: T.List[T.Union[File, str]]) -> T.Optional[str]:
+ generated = [a for a in args if isinstance(a, File) and a.is_built]
+ if generated:
+ return 'extra_files contains generated files: {}'.format(', '.join(f"{f.fname}" for f in generated))
+ return None
+
+
# Applies to all build_target like classes
_ALL_TARGET_KWS: T.List[KwargInfo] = [
OVERRIDE_OPTIONS_KW,
KwargInfo('build_by_default', bool, default=True, since='0.38.0'),
- KwargInfo('extra_files', ContainerTypeInfo(list, (str, File)), default=[], listify=True),
- # Accursed. We allow this for backwards compat and warn in the interpreter.
- KwargInfo('install', object, default=False),
+ KwargInfo(
+ 'extra_files',
+ ContainerTypeInfo(list, (str, File)),
+ default=[],
+ listify=True,
+ validator=_extra_files_validator,
+ ),
+ KwargInfo(
+ 'install',
+ object,
+ default=False,
+ convertor=_target_install_convertor,
+ feature_validator=_target_install_feature_validator,
+ ),
INSTALL_MODE_KW,
+ INSTALL_TAG_KW,
KwargInfo('implicit_include_directories', bool, default=True, since='0.42.0'),
NATIVE_KW,
KwargInfo('resources', ContainerTypeInfo(list, str), default=[], listify=True),
@@ -575,6 +629,7 @@ _ALL_TARGET_KWS: T.List[KwargInfo] = [
('1.1.0', 'generated sources as positional "objects" arguments')
},
),
+ KwargInfo('build_subdir', str, default='', since='1.10.0')
]
@@ -598,6 +653,63 @@ _NAME_PREFIX_KW: KwargInfo[T.Optional[T.Union[str, T.List]]] = KwargInfo(
)
+def _pch_validator(args: T.List[str]) -> T.Optional[str]:
+ num_args = len(args)
+ if num_args == 1:
+ if not compilers.is_header(args[0]):
+ return f'PCH argument {args[0]} is not a header.'
+ elif num_args == 2:
+ if compilers.is_header(args[0]):
+ if not compilers.is_source(args[1]):
+ return 'PCH definition must contain one header and at most one source.'
+ elif compilers.is_source(args[0]):
+ if not compilers.is_header(args[1]):
+ return 'PCH definition must contain one header and at most one source.'
+ else:
+ return f'PCH argument {args[0]} has neither a known header or code extension.'
+
+ if os.path.dirname(args[0]) != os.path.dirname(args[1]):
+ return 'PCH files must be stored in the same folder.'
+ elif num_args > 2:
+ return 'A maximum of two elements are allowed for PCH arguments'
+ if num_args >= 1 and not has_path_sep(args[0]):
+ return f'PCH header {args[0]} must not be in the same directory as source files'
+ if num_args == 2 and not has_path_sep(args[1]):
+ return f'PCH source {args[0]} must not be in the same directory as source files'
+ return None
+
+
+def _pch_feature_validator(args: T.List[str]) -> T.Iterable[FeatureCheckBase]:
+ if len(args) > 1:
+ yield FeatureDeprecated('PCH source files', '0.50.0', 'Only a single header file should be used.')
+
+
+def _pch_convertor(args: T.List[str]) -> T.Optional[T.Tuple[str, T.Optional[str]]]:
+ num_args = len(args)
+
+ if num_args == 1:
+ return (args[0], None)
+
+ if num_args == 2:
+ if compilers.is_source(args[0]):
+ # Flip so that we always have [header, src]
+ return (args[1], args[0])
+ return (args[0], args[1])
+
+ return None
+
+
+_PCH_ARGS: KwargInfo[T.List[str]] = KwargInfo(
+ 'pch',
+ ContainerTypeInfo(list, str),
+ listify=True,
+ default=[],
+ validator=_pch_validator,
+ feature_validator=_pch_feature_validator,
+ convertor=_pch_convertor,
+)
+
+
# Applies to all build_target classes except jar
_BUILD_TARGET_KWS: T.List[KwargInfo] = [
*_ALL_TARGET_KWS,
@@ -607,6 +719,8 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [
_NAME_PREFIX_KW,
_NAME_PREFIX_KW.evolve(name='name_suffix', validator=_name_suffix_validator),
RUST_CRATE_TYPE_KW,
+ _PCH_ARGS.evolve(name='c_pch'),
+ _PCH_ARGS.evolve(name='cpp_pch'),
KwargInfo('d_debug', ContainerTypeInfo(list, (str, int)), default=[], listify=True),
D_MODULE_VERSIONS_KW,
KwargInfo('d_unittest', bool, default=False),
@@ -616,6 +730,8 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [
default={},
since='1.2.0',
),
+ KwargInfo('swift_interoperability_mode', str, default='c', validator=in_set_validator({'c', 'cpp'}), since='1.9.0'),
+ KwargInfo('swift_module_name', str, default='', since='1.9.0'),
KwargInfo('build_rpath', str, default='', since='0.42.0'),
KwargInfo(
'gnu_symbol_visibility',
@@ -626,6 +742,12 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [
),
KwargInfo('install_rpath', str, default=''),
KwargInfo(
+ 'link_args',
+ ContainerTypeInfo(list, str),
+ default=[],
+ listify=True,
+ ),
+ KwargInfo(
'link_depends',
ContainerTypeInfo(list, (str, File, CustomTarget, CustomTargetIndex, BuildTarget)),
default=[],
@@ -637,6 +759,9 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [
validator=in_set_validator(set(compilers.all_languages)),
since='0.51.0',
),
+ KwargInfo('vala_gir', (str, NoneType)),
+ KwargInfo('vala_header', (str, NoneType)),
+ KwargInfo('vala_vapi', (str, NoneType)),
]
def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]:
@@ -690,10 +815,17 @@ _DARWIN_VERSIONS_KW: KwargInfo[T.List[T.Union[str, int]]] = KwargInfo(
# Arguments exclusive to Executable. These are separated to make integrating
# them into build_target easier
-_EXCLUSIVE_EXECUTABLE_KWS: T.List[KwargInfo] = [
+EXCLUSIVE_EXECUTABLE_KWS: T.List[KwargInfo] = [
KwargInfo('export_dynamic', (bool, NoneType), since='0.45.0'),
KwargInfo('gui_app', (bool, NoneType), deprecated='0.56.0', deprecated_message="Use 'win_subsystem' instead"),
- KwargInfo('implib', (bool, str, NoneType), since='0.42.0'),
+ KwargInfo(
+ 'implib',
+ (bool, str, NoneType),
+ since='0.42.0',
+ deprecated_values={
+ bool: ('1.10.0', 'Use "export_dynamic" keyword instead'),
+ },
+ ),
KwargInfo('pie', (bool, NoneType)),
KwargInfo(
'win_subsystem',
@@ -712,7 +844,7 @@ _EXCLUSIVE_EXECUTABLE_KWS: T.List[KwargInfo] = [
# The total list of arguments used by Executable
EXECUTABLE_KWS = [
*_BUILD_TARGET_KWS,
- *_EXCLUSIVE_EXECUTABLE_KWS,
+ *EXCLUSIVE_EXECUTABLE_KWS,
_VS_MODULE_DEFS_KW.evolve(since='1.3.0', since_values=None),
_JAVA_LANG_KW,
]
@@ -737,16 +869,22 @@ STATIC_LIB_KWS = [
_JAVA_LANG_KW,
]
+def _shortname_validator(shortname: T.Optional[str]) -> T.Optional[str]:
+ if shortname is not None and len(shortname) > 8:
+ return 'must have a maximum of 8 characters'
+ return None
+
# Arguments exclusive to SharedLibrary. These are separated to make integrating
# them into build_target easier
_EXCLUSIVE_SHARED_LIB_KWS: T.List[KwargInfo] = [
_DARWIN_VERSIONS_KW,
KwargInfo('soversion', (str, int, NoneType), convertor=lambda x: str(x) if x is not None else None),
KwargInfo('version', (str, NoneType), validator=_validate_shlib_version),
+ KwargInfo('shortname', (str, NoneType), since='1.10.0', validator=_shortname_validator),
]
# The total list of arguments used by SharedLibrary
-SHARED_LIB_KWS = [
+SHARED_LIB_KWS: T.List[KwargInfo] = [
*_BUILD_TARGET_KWS,
*_EXCLUSIVE_SHARED_LIB_KWS,
*_EXCLUSIVE_LIB_KWS,
@@ -814,8 +952,9 @@ BUILD_TARGET_KWS = [
*_EXCLUSIVE_SHARED_LIB_KWS,
*_EXCLUSIVE_SHARED_MOD_KWS,
*_EXCLUSIVE_STATIC_LIB_KWS,
- *_EXCLUSIVE_EXECUTABLE_KWS,
+ *EXCLUSIVE_EXECUTABLE_KWS,
*_SHARED_STATIC_ARGS,
+ RUST_ABI_KW.evolve(since='1.10.0'),
*[a.evolve(deprecated='1.3.0', deprecated_message='The use of "jar" in "build_target()" is deprecated, and this argument is only used by jar()')
for a in _EXCLUSIVE_JAR_KWS],
KwargInfo(
@@ -848,3 +987,64 @@ PKGCONFIG_DEFINE_KW: KwargInfo = KwargInfo(
default=[],
convertor=_pkgconfig_define_convertor,
)
+
+INCLUDE_TYPE = KwargInfo(
+ 'include_type',
+ str,
+ default='preserve',
+ since='0.52.0',
+ validator=in_set_validator({'system', 'non-system', 'preserve'})
+)
+
+
+_DEPRECATED_DEPENDENCY_METHODS = frozenset(
+ {'sdlconfig', 'cups-config', 'pcap-config', 'libwmf-config', 'qmake'})
+
+
+def _dependency_method_convertor(value: str) -> DependencyMethods:
+ if value in _DEPRECATED_DEPENDENCY_METHODS:
+ return DependencyMethods.CONFIG_TOOL
+ return DependencyMethods(value)
+
+
+DEPENDENCY_METHOD_KW = KwargInfo(
+ 'method',
+ str,
+ default='auto',
+ since='0.40.0',
+ validator=in_set_validator(
+ {m.value for m in DependencyMethods} | _DEPRECATED_DEPENDENCY_METHODS),
+ convertor=_dependency_method_convertor,
+ deprecated_values={
+ 'sdlconfig': ('0.44.0', 'use config-tool instead'),
+ 'cups-config': ('0.44.0', 'use config-tool instead'),
+ 'pcap-config': ('0.44.0', 'use config-tool instead'),
+ 'libwmf-config': ('0.44.0', 'use config-tool instead'),
+ 'qmake': ('0.58.0', 'use config-tool instead'),
+ },
+)
+
+
+DEPENDENCY_KWS: T.List[KwargInfo] = [
+ DEFAULT_OPTIONS.evolve(since='0.38.0'),
+ DEPENDENCY_METHOD_KW,
+ DISABLER_KW.evolve(since='0.49.0'),
+ INCLUDE_TYPE,
+ NATIVE_KW,
+ REQUIRED_KW,
+ KwargInfo('allow_fallback', (bool, NoneType), since='0.56.0'),
+ KwargInfo('cmake_args', ContainerTypeInfo(list, str), listify=True, default=[], since='0.50.0'),
+ KwargInfo('cmake_module_path', ContainerTypeInfo(list, str), listify=True, default=[], since='0.50.0'),
+ KwargInfo('cmake_package_version', str, default='', since='0.57.0'),
+ KwargInfo('components', ContainerTypeInfo(list, str), listify=True, default=[], since='0.54.0'),
+ KwargInfo('fallback', (ContainerTypeInfo(list, str), str, NoneType), since='0.54.0'),
+ KwargInfo('language', (str, NoneType), convertor=lambda x: x.lower() if x is not None else x,
+ validator=lambda x: 'Must be a valid language if set' if (x is not None and x not in compilers.all_languages) else None),
+ KwargInfo('main', bool, default=False),
+ KwargInfo('modules', ContainerTypeInfo(list, str), listify=True, default=[]),
+ KwargInfo('not_found_message', str, default='', since='0.50.0'),
+ KwargInfo('optional_modules', ContainerTypeInfo(list, str), listify=True, default=[]),
+ KwargInfo('private_headers', bool, default=False),
+ KwargInfo('static', (bool, NoneType)),
+ KwargInfo('version', ContainerTypeInfo(list, str), listify=True, default=[]),
+]
diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py
index aa38e94..88fa706 100644
--- a/mesonbuild/interpreterbase/__init__.py
+++ b/mesonbuild/interpreterbase/__init__.py
@@ -59,6 +59,9 @@ __all__ = [
'TYPE_HoldableTypes',
'HoldableTypes',
+
+ 'UnknownValue',
+ 'UndefinedVariable',
]
from .baseobjects import (
@@ -81,6 +84,9 @@ from .baseobjects import (
SubProject,
HoldableTypes,
+
+ UnknownValue,
+ UndefinedVariable,
)
from .decorators import (
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
index a5cccce..c756761 100644
--- a/mesonbuild/interpreterbase/baseobjects.py
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -15,16 +15,11 @@ from abc import ABCMeta
from contextlib import AbstractContextManager
if T.TYPE_CHECKING:
- from typing_extensions import Protocol, TypeAlias
+ from typing_extensions import TypeAlias
# Object holders need the actual interpreter
from ..interpreter import Interpreter
- __T = T.TypeVar('__T', bound='TYPE_var', contravariant=True)
-
- class OperatorCall(Protocol[__T]):
- def __call__(self, other: __T) -> 'TYPE_var': ...
-
TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])
@@ -34,34 +29,85 @@ TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
TYPE_kwargs = T.Dict[str, TYPE_var]
TYPE_nkwargs = T.Dict[str, TYPE_nvar]
TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
+TYPE_op_arg = T.TypeVar('TYPE_op_arg', bound='TYPE_var', contravariant=True)
+TYPE_op_func = T.Callable[[TYPE_op_arg, TYPE_op_arg], TYPE_var]
+TYPE_method_func = T.Callable[['InterpreterObject', T.List[TYPE_var], TYPE_kwargs], TYPE_var]
+
SubProject = T.NewType('SubProject', str)
class InterpreterObject:
+ TRIVIAL_OPERATORS: T.Dict[
+ MesonOperator,
+ T.Tuple[
+ T.Union[T.Type, T.Tuple[T.Type, ...]],
+ TYPE_op_func
+ ]
+ ] = {}
+
+ OPERATORS: T.Dict[MesonOperator, TYPE_op_func] = {}
+
+ METHODS: T.Dict[
+ str,
+ TYPE_method_func,
+ ] = {}
+
+ def __init_subclass__(cls: T.Type[InterpreterObject], **kwargs: T.Any) -> None:
+ super().__init_subclass__(**kwargs)
+ saved_trivial_operators = cls.TRIVIAL_OPERATORS
+
+ cls.METHODS = {}
+ cls.OPERATORS = {}
+ cls.TRIVIAL_OPERATORS = {}
+
+ # Compute inherited operators and methods according to the Python resolution
+ # order. Reverse the result of mro() because update() will overwrite entries
+ # that are set by the superclass with those that are set by the subclass.
+ for superclass in reversed(cls.mro()[1:]):
+ if superclass is InterpreterObject:
+ # InterpreterObject cannot use @InterpreterObject.operator because
+ # __init_subclass__ does not operate on InterpreterObject itself
+ cls.OPERATORS.update({
+ MesonOperator.EQUALS: InterpreterObject.op_equals,
+ MesonOperator.NOT_EQUALS: InterpreterObject.op_not_equals
+ })
+
+ elif issubclass(superclass, InterpreterObject):
+ cls.METHODS.update(superclass.METHODS)
+ cls.OPERATORS.update(superclass.OPERATORS)
+ cls.TRIVIAL_OPERATORS.update(superclass.TRIVIAL_OPERATORS)
+
+ for name, method in cls.__dict__.items():
+ if hasattr(method, 'meson_method'):
+ cls.METHODS[method.meson_method] = method
+ if hasattr(method, 'meson_operator'):
+ cls.OPERATORS[method.meson_operator] = method
+ cls.TRIVIAL_OPERATORS.update(saved_trivial_operators)
+
+ @staticmethod
+ def method(name: str) -> T.Callable[[TV_func], TV_func]:
+ '''Decorator that tags a Python method as the implementation of a method
+ for the Meson interpreter'''
+ def decorator(f: TV_func) -> TV_func:
+ f.meson_method = name # type: ignore[attr-defined]
+ return f
+ return decorator
+
+ @staticmethod
+ def operator(op: MesonOperator) -> T.Callable[[TV_func], TV_func]:
+ '''Decorator that tags a method as the implementation of an operator
+ for the Meson interpreter'''
+ def decorator(f: TV_func) -> TV_func:
+ f.meson_operator = op # type: ignore[attr-defined]
+ return f
+ return decorator
+
def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None:
- self.methods: T.Dict[
- str,
- T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var]
- ] = {}
- self.operators: T.Dict[MesonOperator, 'OperatorCall'] = {}
- self.trivial_operators: T.Dict[
- MesonOperator,
- T.Tuple[
- T.Union[T.Type, T.Tuple[T.Type, ...]],
- 'OperatorCall'
- ]
- ] = {}
# Current node set during a method call. This can be used as location
# when printing a warning message during a method call.
self.current_node: mparser.BaseNode = None
self.subproject = subproject or SubProject('')
- # Some default operators supported by all objects
- self.operators.update({
- MesonOperator.EQUALS: self.op_equals,
- MesonOperator.NOT_EQUALS: self.op_not_equals,
- })
-
# The type of the object that can be printed to the user
def display_name(self) -> str:
return type(self).__name__
@@ -72,25 +118,26 @@ class InterpreterObject:
args: T.List[TYPE_var],
kwargs: TYPE_kwargs
) -> TYPE_var:
- if method_name in self.methods:
- method = self.methods[method_name]
+ if method_name in self.METHODS:
+ method = self.METHODS[method_name]
if not getattr(method, 'no-args-flattening', False):
args = flatten(args)
if not getattr(method, 'no-second-level-holder-flattening', False):
args, kwargs = resolve_second_level_holders(args, kwargs)
- return method(args, kwargs)
+ return method(self, args, kwargs)
raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.')
def operator_call(self, operator: MesonOperator, other: TYPE_var) -> TYPE_var:
- if operator in self.trivial_operators:
- op = self.trivial_operators[operator]
+ if operator in self.TRIVIAL_OPERATORS:
+ op = self.TRIVIAL_OPERATORS[operator]
if op[0] is None and other is not None:
raise MesonBugException(f'The unary operator `{operator.value}` of {self.display_name()} was passed the object {other} of type {type(other).__name__}')
if op[0] is not None and not isinstance(other, op[0]):
raise InvalidArguments(f'The `{operator.value}` operator of {self.display_name()} does not accept objects of type {type(other).__name__} ({other})')
- return op[1](other)
- if operator in self.operators:
- return self.operators[operator](other)
+ return op[1](self, other)
+ if operator in self.OPERATORS:
+ return self.OPERATORS[operator](self, other)
+
raise InvalidCode(f'Object {self} of type {self.display_name()} does not support the `{operator.value}` operator.')
# Default comparison operator support
@@ -121,6 +168,16 @@ class MesonInterpreterObject(InterpreterObject):
class MutableInterpreterObject:
''' Dummy class to mark the object type as mutable '''
+class UnknownValue(MesonInterpreterObject):
+ '''This class is only used for the rewriter/static introspection tool and
+ indicates that a value cannot be determined statically, either because of
+ limitations in our code or because the value differs from machine to
+ machine.'''
+
+class UndefinedVariable(MesonInterpreterObject):
+ '''This class is only used for the rewriter/static introspection tool and
+ represents the `value` a meson-variable has if it was never written to.'''
+
HoldableTypes = (HoldableObject, int, bool, str, list, dict)
TYPE_HoldableTypes = T.Union[TYPE_var, HoldableObject]
InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes)
@@ -142,12 +199,14 @@ class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]):
return type(self.held_object).__name__
# Override default comparison operators for the held object
+ @InterpreterObject.operator(MesonOperator.EQUALS)
def op_equals(self, other: TYPE_var) -> bool:
# See the comment from InterpreterObject why we are using `type()` here.
if type(self.held_object) is not type(other):
self._throw_comp_exception(other, '==')
return self.held_object == other
+ @InterpreterObject.operator(MesonOperator.NOT_EQUALS)
def op_not_equals(self, other: TYPE_var) -> bool:
if type(self.held_object) is not type(other):
self._throw_comp_exception(other, '!=')
diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py
index 06cac52..486a585 100644
--- a/mesonbuild/interpreterbase/decorators.py
+++ b/mesonbuild/interpreterbase/decorators.py
@@ -352,6 +352,7 @@ class KwargInfo(T.Generic[_T]):
added in.
:param not_set_warning: A warning message that is logged if the kwarg is not
set by the user.
+ :param feature_validator: A callable returning an iterable of FeatureNew | FeatureDeprecated objects.
"""
def __init__(self, name: str,
types: T.Union[T.Type[_T], T.Tuple[T.Union[T.Type[_T], ContainerTypeInfo], ...], ContainerTypeInfo],
@@ -363,6 +364,7 @@ class KwargInfo(T.Generic[_T]):
deprecated: T.Optional[str] = None,
deprecated_message: T.Optional[str] = None,
deprecated_values: T.Optional[T.Dict[T.Union[_T, ContainerTypeInfo, type], T.Union[str, T.Tuple[str, str]]]] = None,
+ feature_validator: T.Optional[T.Callable[[_T], T.Iterable[FeatureCheckBase]]] = None,
validator: T.Optional[T.Callable[[T.Any], T.Optional[str]]] = None,
convertor: T.Optional[T.Callable[[_T], object]] = None,
not_set_warning: T.Optional[str] = None):
@@ -374,6 +376,7 @@ class KwargInfo(T.Generic[_T]):
self.since = since
self.since_message = since_message
self.since_values = since_values
+ self.feature_validator = feature_validator
self.deprecated = deprecated
self.deprecated_message = deprecated_message
self.deprecated_values = deprecated_values
@@ -392,8 +395,9 @@ class KwargInfo(T.Generic[_T]):
deprecated: T.Union[str, None, _NULL_T] = _NULL,
deprecated_message: T.Union[str, None, _NULL_T] = _NULL,
deprecated_values: T.Union[T.Dict[T.Union[_T, ContainerTypeInfo, type], T.Union[str, T.Tuple[str, str]]], None, _NULL_T] = _NULL,
+ feature_validator: T.Union[T.Callable[[_T], T.Iterable[FeatureCheckBase]], None, _NULL_T] = _NULL,
validator: T.Union[T.Callable[[_T], T.Optional[str]], None, _NULL_T] = _NULL,
- convertor: T.Union[T.Callable[[_T], TYPE_var], None, _NULL_T] = _NULL) -> 'KwargInfo':
+ convertor: T.Union[T.Callable[[_T], object], None, _NULL_T] = _NULL) -> 'KwargInfo':
"""Create a shallow copy of this KwargInfo, with modifications.
This allows us to create a new copy of a KwargInfo with modifications.
@@ -417,6 +421,7 @@ class KwargInfo(T.Generic[_T]):
deprecated=deprecated if not isinstance(deprecated, _NULL_T) else self.deprecated,
deprecated_message=deprecated_message if not isinstance(deprecated_message, _NULL_T) else self.deprecated_message,
deprecated_values=deprecated_values if not isinstance(deprecated_values, _NULL_T) else self.deprecated_values,
+ feature_validator=feature_validator if not isinstance(feature_validator, _NULL_T) else self.feature_validator,
validator=validator if not isinstance(validator, _NULL_T) else self.validator,
convertor=convertor if not isinstance(convertor, _NULL_T) else self.convertor,
)
@@ -493,7 +498,7 @@ def typed_kwargs(name: str, *types: KwargInfo, allow_unknown: bool = False) -> T
if n in value:
warning = f'value "{n}" in list'
elif isinstance(value, dict):
- if n in value.keys():
+ if n in value:
warning = f'value "{n}" in dict keys'
elif n == value:
warning = f'value "{n}"'
@@ -532,6 +537,10 @@ def typed_kwargs(name: str, *types: KwargInfo, allow_unknown: bool = False) -> T
if msg is not None:
raise InvalidArguments(f'{name} keyword argument "{info.name}" {msg}')
+ if info.feature_validator is not None:
+ for each in info.feature_validator(value):
+ each.use(subproject, node)
+
if info.deprecated_values is not None:
emit_feature_change(info.deprecated_values, FeatureDeprecated)
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py
index b13bbae..a601e5c 100644
--- a/mesonbuild/interpreterbase/interpreterbase.py
+++ b/mesonbuild/interpreterbase/interpreterbase.py
@@ -28,6 +28,7 @@ from .exceptions import (
)
from .. import mlog
+from . import operator
from .decorators import FeatureNew
from .disabler import Disabler, is_disabled
from .helpers import default_resolve_key, flatten, resolve_second_level_holders, stringifyUserArguments
@@ -344,24 +345,14 @@ class InterpreterBase:
if isinstance(val2, Disabler):
return val2
- # New code based on InterpreterObjects
- operator = {
- 'in': MesonOperator.IN,
- 'notin': MesonOperator.NOT_IN,
- '==': MesonOperator.EQUALS,
- '!=': MesonOperator.NOT_EQUALS,
- '>': MesonOperator.GREATER,
- '<': MesonOperator.LESS,
- '>=': MesonOperator.GREATER_EQUALS,
- '<=': MesonOperator.LESS_EQUALS,
- }[node.ctype]
+ op = operator.MAPPING[node.ctype]
# Check if the arguments should be reversed for simplicity (this essentially converts `in` to `contains`)
- if operator in (MesonOperator.IN, MesonOperator.NOT_IN):
+ if op in (MesonOperator.IN, MesonOperator.NOT_IN):
val1, val2 = val2, val1
val1.current_node = node
- return self._holderify(val1.operator_call(operator, _unholder(val2)))
+ return self._holderify(val1.operator_call(op, _unholder(val2)))
def evaluate_andstatement(self, cur: mparser.AndNode) -> InterpreterObject:
l = self.evaluate_statement(cur.left)
@@ -414,15 +405,8 @@ class InterpreterBase:
if l is None or r is None:
raise InvalidCodeOnVoid(cur.operation)
- mapping: T.Dict[str, MesonOperator] = {
- 'add': MesonOperator.PLUS,
- 'sub': MesonOperator.MINUS,
- 'mul': MesonOperator.TIMES,
- 'div': MesonOperator.DIV,
- 'mod': MesonOperator.MOD,
- }
l.current_node = cur
- res = l.operator_call(mapping[cur.operation], _unholder(r))
+ res = l.operator_call(operator.MAPPING[cur.operation], _unholder(r))
return self._holderify(res)
def evaluate_ternary(self, node: mparser.TernaryNode) -> T.Optional[InterpreterObject]:
@@ -555,12 +539,6 @@ class InterpreterBase:
return Disabler()
if not isinstance(obj, InterpreterObject):
raise InvalidArguments(f'{object_display_name} is not callable.')
- # TODO: InterpreterBase **really** shouldn't be in charge of checking this
- if method_name == 'extract_objects':
- if isinstance(obj, ObjectHolder):
- self.validate_extraction(obj.held_object)
- elif not isinstance(obj, Disabler):
- raise InvalidArguments(f'Invalid operation "extract_objects" on {object_display_name} of type {type(obj).__name__}')
obj.current_node = self.current_node = node
res = obj.method_call(method_name, args, kwargs)
return self._holderify(res) if res is not None else None
@@ -675,9 +653,6 @@ class InterpreterBase:
return self.variables[varname]
raise InvalidCode(f'Unknown variable "{varname}".')
- def validate_extraction(self, buildtarget: mesonlib.HoldableObject) -> None:
- raise InterpreterException('validate_extraction is not implemented in this context (please file a bug)')
-
def _load_option_file(self) -> None:
from .. import optinterpreter # prevent circular import
@@ -733,6 +708,11 @@ class InterpreterBase:
except mesonlib.MesonException as me:
me.file = absname
raise me
+ self._evaluate_codeblock(codeblock, subdir, visitors)
+ return True
+
+ def _evaluate_codeblock(self, codeblock: mparser.CodeBlockNode, subdir: str,
+ visitors: T.Optional[T.Iterable[AstVisitor]] = None) -> None:
try:
prev_subdir = self.subdir
self.subdir = subdir
@@ -744,4 +724,3 @@ class InterpreterBase:
pass
finally:
self.subdir = prev_subdir
- return True
diff --git a/mesonbuild/interpreterbase/operator.py b/mesonbuild/interpreterbase/operator.py
index 3419c4b..d17cd37 100644
--- a/mesonbuild/interpreterbase/operator.py
+++ b/mesonbuild/interpreterbase/operator.py
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
from enum import Enum
+import typing as T
class MesonOperator(Enum):
# Arithmetic
@@ -30,3 +31,7 @@ class MesonOperator(Enum):
IN = 'in'
NOT_IN = 'not in'
INDEX = '[]'
+
+# Accessing this directly is about 9x faster than calling MesonOperator(s),
+# and about 3 times faster than a staticmethod
+MAPPING: T.Mapping[str, MesonOperator] = {x.value: x for x in MesonOperator}
diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py
index ee9bb08..d9be379 100644
--- a/mesonbuild/linkers/detect.py
+++ b/mesonbuild/linkers/detect.py
@@ -27,6 +27,7 @@ defaults['clang_cl_static_linker'] = ['llvm-lib']
defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']
+defaults['emxomf_static_linker'] = ['emxomfar']
def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
msg = 'Unable to detect linker for compiler `{}`\nstdout: {}\nstderr: {}'.format(
@@ -39,7 +40,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
use_linker_prefix: bool = True, invoked_directly: bool = True,
extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
from . import linkers
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
if invoked_directly or comp_class.get_argument_syntax() == 'msvc':
rsp_syntax = RSPFileSyntax.MSVC
@@ -72,11 +73,11 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
if 'LLD' in o.split('\n', maxsplit=1)[0]:
if 'compatible with GNU linkers' in o:
return linkers.LLVMDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX,
+ compiler, env, for_machine, comp_class.LINKER_PREFIX,
override, version=search_version(o))
elif not invoked_directly:
return linkers.ClangClDynamicLinker(
- for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
+ env, for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
version=search_version(o), direct=False, machine=None,
rsp_syntax=rsp_syntax)
@@ -87,13 +88,13 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
p, o, e = Popen_safe(compiler + check_args)
if 'LLD' in o.split('\n', maxsplit=1)[0]:
return linkers.ClangClDynamicLinker(
- for_machine, [],
+ env, for_machine, [],
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
exelist=compiler, version=search_version(o), direct=invoked_directly,
rsp_syntax=rsp_syntax)
elif 'OPTLINK' in o:
# Optlink's stdout *may* begin with a \r character.
- return linkers.OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
+ return linkers.OptlinkDynamicLinker(compiler, env, for_machine, version=search_version(o))
elif o.startswith('Microsoft') or e.startswith('Microsoft'):
out = o or e
match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
@@ -103,7 +104,7 @@ def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
target = 'x86'
return linkers.MSVCDynamicLinker(
- for_machine, [], machine=target, exelist=compiler,
+ env, for_machine, [], machine=target, exelist=compiler,
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
version=search_version(out), direct=invoked_directly,
rsp_syntax=rsp_syntax)
@@ -128,7 +129,8 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
:extra_args: Any additional arguments required (such as a source file)
"""
from . import linkers
- env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ from ..options import OptionKey
+ env.add_lang_args(comp_class.language, comp_class, for_machine)
extra_args = extra_args or []
system = env.machines[for_machine].system
@@ -146,6 +148,9 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
override = comp_class.use_linker_args(value[0], comp_version)
check_args += override
+ if env.machines[for_machine].is_os2() and env.coredata.optstore.get_value_for(OptionKey('os2_emxomf')):
+ check_args += ['-Zomf']
+
mlog.debug('-----')
p, o, e = Popen_safe_logged(compiler + check_args, msg='Detecting linker via')
@@ -165,10 +170,13 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
lld_cls = linkers.LLVMDynamicLinker
linker = lld_cls(
- compiler, for_machine, comp_class.LINKER_PREFIX, override, system=system, version=v)
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override, system=system, version=v)
+ elif o.startswith("eld"):
+ linker = linkers.ELDDynamicLinker(
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'Snapdragon' in e and 'LLVM' in e:
linker = linkers.QualcommLLVMDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif e.startswith('lld-link: '):
# The LLD MinGW frontend didn't respond to --version before version 9.0.0,
# and produced an error message about failing to link (when no object
@@ -186,7 +194,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
_, o, e = Popen_safe([linker_cmd, '--version'])
v = search_version(o)
- linker = linkers.LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ linker = linkers.LLVMDynamicLinker(compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'GNU' in o or 'GNU' in e:
gnu_cls: T.Type[GnuDynamicLinker]
# this is always the only thing on stdout, except for swift
@@ -197,7 +205,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
gnu_cls = linkers.MoldDynamicLinker
else:
gnu_cls = linkers.GnuBFDDynamicLinker
- linker = gnu_cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ linker = gnu_cls(compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'Solaris' in e or 'Solaris' in o:
for line in (o+e).split('\n'):
if 'ld: Software Generation Utilities' in line:
@@ -206,7 +214,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
else:
v = 'unknown version'
linker = linkers.SolarisDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
version=v)
elif 'ld: 0706-012 The -- flag is not recognized' in e:
if isinstance(comp_class.LINKER_PREFIX, str):
@@ -214,17 +222,17 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
else:
_, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
linker = linkers.AIXDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
version=search_version(e))
elif o.startswith('zig ld'):
linker = linkers.ZigCCDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override, version=v)
# detect xtools first, bug #10805
elif 'xtools-' in o.split('\n', maxsplit=1)[0]:
xtools = o.split(' ', maxsplit=1)[0]
v = xtools.split('-', maxsplit=2)[1]
linker = linkers.AppleDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
system=system, version=v
)
# detect linker on MacOS - must be after other platforms because the
@@ -246,9 +254,17 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty
else:
__failed_to_detect_linker(compiler, check_args, o, e)
linker = linkers.AppleDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
system=system, version=v
)
+ elif 'ld.exe: unrecognized option' in e:
+ linker = linkers.OS2AoutDynamicLinker(
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
+ version='none')
+ elif 'emxomfld: invalid option' in e:
+ linker = linkers.OS2OmfDynamicLinker(
+ compiler, env, for_machine, comp_class.LINKER_PREFIX, override,
+ version='none')
else:
__failed_to_detect_linker(compiler, check_args, o, e)
return linker
diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py
index 59f60e0..d40d5e2 100644
--- a/mesonbuild/linkers/linkers.py
+++ b/mesonbuild/linkers/linkers.py
@@ -25,8 +25,9 @@ class StaticLinker:
id: str
- def __init__(self, exelist: T.List[str]):
+ def __init__(self, exelist: T.List[str], env: Environment):
self.exelist = exelist
+ self.environment = env
def get_id(self) -> str:
return self.id
@@ -65,18 +66,21 @@ class StaticLinker:
def get_coverage_link_args(self) -> T.List[str]:
return []
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def gen_vs_module_defs_args(self) -> T.List[str]:
+ return []
+
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
- def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
- def openmp_flags(self, env: Environment) -> T.List[str]:
+ def openmp_flags(self) -> T.List[str]:
return []
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
@classmethod
@@ -139,11 +143,12 @@ class DynamicLinker(metaclass=abc.ABCMeta):
ret += self.prefix_arg + [arg]
return ret
- def __init__(self, exelist: T.List[str],
+ def __init__(self, exelist: T.List[str], env: Environment,
for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]],
always_args: T.List[str], *, system: str = 'unknown system',
version: str = 'unknown version'):
self.exelist = exelist
+ self.environment = env
self.for_machine = for_machine
self.system = system
self.version = version
@@ -190,10 +195,10 @@ class DynamicLinker(metaclass=abc.ABCMeta):
def get_option_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]:
+ def get_option_link_args(self, target: 'BuildTarget', subproject: T.Optional[str] = None) -> T.List[str]:
return []
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]:
raise EnvironmentException(f'Language {self.id} does not support has_multi_link_arguments.')
def get_debugfile_name(self, targetfile: str) -> T.Optional[str]:
@@ -232,6 +237,9 @@ class DynamicLinker(metaclass=abc.ABCMeta):
def get_thinlto_cache_args(self, path: str) -> T.List[str]:
return []
+ def get_lto_obj_cache_path(self, path: str) -> T.List[str]:
+ return []
+
def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
return []
@@ -253,11 +261,14 @@ class DynamicLinker(metaclass=abc.ABCMeta):
def get_coverage_args(self) -> T.List[str]:
raise EnvironmentException(f"Linker {self.id} doesn't implement coverage data generation.")
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ return []
+
@abc.abstractmethod
def get_search_args(self, dirname: str) -> T.List[str]:
pass
- def export_dynamic_args(self, env: 'Environment') -> T.List[str]:
+ def export_dynamic_args(self) -> T.List[str]:
return []
def import_library_args(self, implibname: str) -> T.List[str]:
@@ -267,7 +278,7 @@ class DynamicLinker(metaclass=abc.ABCMeta):
"""
return []
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return []
def no_undefined_args(self) -> T.List[str]:
@@ -297,12 +308,12 @@ class DynamicLinker(metaclass=abc.ABCMeta):
def bitcode_args(self) -> T.List[str]:
raise MesonException('This linker does not support bitcode bundles')
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ def get_soname_args(self, prefix: str, shlib_name: str,
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
return []
@@ -361,8 +372,8 @@ class VisualStudioLinker(VisualStudioLikeLinker, StaticLinker):
id = 'lib'
- def __init__(self, exelist: T.List[str], machine: str):
- StaticLinker.__init__(self, exelist)
+ def __init__(self, exelist: T.List[str], env: Environment, machine: str):
+ StaticLinker.__init__(self, exelist, env)
VisualStudioLikeLinker.__init__(self, machine)
@@ -372,16 +383,16 @@ class IntelVisualStudioLinker(VisualStudioLikeLinker, StaticLinker):
id = 'xilib'
- def __init__(self, exelist: T.List[str], machine: str):
- StaticLinker.__init__(self, exelist)
+ def __init__(self, exelist: T.List[str], env: Environment, machine: str):
+ StaticLinker.__init__(self, exelist, env)
VisualStudioLikeLinker.__init__(self, machine)
class ArLinker(ArLikeLinker, StaticLinker):
id = 'ar'
- def __init__(self, for_machine: mesonlib.MachineChoice, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, for_machine: mesonlib.MachineChoice, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
stdo = mesonlib.Popen_safe(self.exelist + ['-h'])[1]
# Enable deterministic builds if they are available.
stdargs = 'csr'
@@ -403,8 +414,10 @@ class ArLinker(ArLikeLinker, StaticLinker):
# on Mac OS X, Solaris, or illumos, so don't build them on those OSes.
# OS X ld rejects with: "file built for unknown-unsupported file format"
# illumos/Solaris ld rejects with: "unknown file type"
+ # OS/2 ld rejects with: "malformed input file (not rel or archive)"
if is_thin and not env.machines[self.for_machine].is_darwin() \
- and not env.machines[self.for_machine].is_sunos():
+ and not env.machines[self.for_machine].is_sunos() \
+ and not env.machines[self.for_machine].is_os2():
return self.std_thin_args
else:
return self.std_args
@@ -422,8 +435,8 @@ class ArmarLinker(ArLikeLinker, StaticLinker):
class DLinker(StaticLinker):
- def __init__(self, exelist: T.List[str], arch: str, *, rsp_syntax: RSPFileSyntax = RSPFileSyntax.GCC):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment, arch: str, *, rsp_syntax: RSPFileSyntax = RSPFileSyntax.GCC):
+ super().__init__(exelist, env)
self.id = exelist[0]
self.arch = arch
self.__rsp_syntax = rsp_syntax
@@ -449,8 +462,8 @@ class DLinker(StaticLinker):
class CcrxLinker(StaticLinker):
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
self.id = 'rlink'
def can_linker_accept_rsp(self) -> bool:
@@ -465,8 +478,8 @@ class CcrxLinker(StaticLinker):
class Xc16Linker(StaticLinker):
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
self.id = 'xc16-ar'
def can_linker_accept_rsp(self) -> bool:
@@ -478,10 +491,18 @@ class Xc16Linker(StaticLinker):
def get_linker_always_args(self) -> T.List[str]:
return ['rcs']
+
+class Xc32ArLinker(ArLinker):
+
+ """Static linker for Microchip XC32 compiler."""
+
+ id = 'xc32-ar'
+
+
class CompCertLinker(StaticLinker):
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
self.id = 'ccomp'
def can_linker_accept_rsp(self) -> bool:
@@ -493,8 +514,8 @@ class CompCertLinker(StaticLinker):
class TILinker(StaticLinker):
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
self.id = 'ti-ar'
def can_linker_accept_rsp(self) -> bool:
@@ -545,8 +566,8 @@ class MetrowerksStaticLinkerEmbeddedPowerPC(MetrowerksStaticLinker):
class TaskingStaticLinker(StaticLinker):
id = 'tasking'
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
def can_linker_accept_rsp(self) -> bool:
return True
@@ -560,6 +581,13 @@ class TaskingStaticLinker(StaticLinker):
def get_linker_always_args(self) -> T.List[str]:
return ['-r']
+
+class EmxomfArLinker(ArLinker):
+ id = 'emxomfar'
+
+ def get_std_link_args(self, env: 'Environment', is_thin: bool) -> T.List[str]:
+ return ['cr']
+
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
@@ -674,8 +702,17 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
def get_coverage_args(self) -> T.List[str]:
return ['--coverage']
- def export_dynamic_args(self, env: 'Environment') -> T.List[str]:
- m = env.machines[self.for_machine]
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ # On Windows targets, .def files may be specified on the linker command
+ # line like an object file.
+ m = self.environment.machines[self.for_machine]
+ if m.is_windows() or m.is_cygwin():
+ return [defsfile]
+ # For other targets, discard the .def file.
+ return []
+
+ def export_dynamic_args(self) -> T.List[str]:
+ m = self.environment.machines[self.for_machine]
if m.is_windows() or m.is_cygwin():
return self._apply_prefix('--export-all-symbols')
return self._apply_prefix('-export-dynamic')
@@ -683,8 +720,8 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
def import_library_args(self, implibname: str) -> T.List[str]:
return self._apply_prefix('--out-implib=' + implibname)
- def thread_flags(self, env: 'Environment') -> T.List[str]:
- if env.machines[self.for_machine].is_haiku():
+ def thread_flags(self) -> T.List[str]:
+ if self.environment.machines[self.for_machine].is_haiku():
return []
return ['-pthread']
@@ -694,22 +731,24 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
def fatal_warnings(self) -> T.List[str]:
return self._apply_prefix('--fatal-warnings')
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
- m = env.machines[self.for_machine]
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
+ m = self.environment.machines[self.for_machine]
if m.is_windows() or m.is_cygwin():
# For PE/COFF the soname argument has no effect
return []
sostr = '' if soversion is None else '.' + soversion
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: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
- m = env.machines[self.for_machine]
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ m = self.environment.machines[self.for_machine]
if m.is_windows() or m.is_cygwin():
return ([], set())
- if not rpath_paths and not install_rpath and not build_rpath:
+ rpath_paths = target.determine_rpath_dirs()
+ if not rpath_paths and not target.install_rpath and not target.build_rpath and not extra_paths:
return ([], set())
args: T.List[str] = []
origin_placeholder = '$ORIGIN'
@@ -722,10 +761,12 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
for p in all_paths:
rpath_dirs_to_remove.add(p.encode('utf8'))
# Build_rpath is used as-is (it is usually absolute).
- if build_rpath != '':
- all_paths.add(build_rpath)
- for p in build_rpath.split(':'):
+ if target.build_rpath != '':
+ all_paths.add(target.build_rpath)
+ for p in target.build_rpath.split(':'):
rpath_dirs_to_remove.add(p.encode('utf8'))
+ if extra_paths:
+ all_paths.update(extra_paths)
# TODO: should this actually be "for (dragonfly|open)bsd"?
if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd():
@@ -740,7 +781,7 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
# enough space in the ELF header to hold the final installation RPATH.
paths = ':'.join(all_paths)
paths_length = len(paths.encode('utf-8'))
- install_rpath_length = len(install_rpath.encode('utf-8'))
+ install_rpath_length = len(target.install_rpath.encode('utf-8'))
if paths_length < install_rpath_length:
padding = 'X' * (install_rpath_length - paths_length)
if not paths:
@@ -859,10 +900,13 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
return self._apply_prefix('-bitcode_bundle')
def fatal_warnings(self) -> T.List[str]:
- return self._apply_prefix('-fatal_warnings')
+ # no one else warns for duplicate libraries, and they're harmless;
+ # just make ld shup up when testing for supported flags
+ return self._apply_prefix('-fatal_warnings') + self._apply_prefix('-no_warn_duplicate_libraries')
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
install_name = ['@rpath/', prefix, shlib_name]
if soversion is not None:
install_name.append('.' + soversion)
@@ -873,10 +917,11 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
'-current_version', darwin_versions[1]])
return args
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: 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:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ rpath_paths = target.determine_rpath_dirs()
+ if not rpath_paths and not target.install_rpath and not target.build_rpath and not extra_paths:
return ([], set())
args: T.List[str] = []
rpath_dirs_to_remove: T.Set[bytes] = set()
@@ -885,8 +930,10 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
origin_placeholder = '@loader_path'
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths])
- if build_rpath != '':
- all_paths.update(build_rpath.split(':'))
+ if target.build_rpath != '':
+ all_paths.update(target.build_rpath.split(':'))
+ if extra_paths:
+ all_paths.update(extra_paths)
for rp in all_paths:
rpath_dirs_to_remove.add(rp.encode('utf8'))
args.extend(self._apply_prefix('-rpath,' + rp))
@@ -896,7 +943,11 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def get_thinlto_cache_args(self, path: str) -> T.List[str]:
return ["-Wl,-cache_path_lto," + path]
- def export_dynamic_args(self, env: 'Environment') -> T.List[str]:
+ def get_lto_obj_cache_path(self, path: str) -> T.List[str]:
+ # https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-flto
+ return ["-Wl,-object_path_lto," + path]
+
+ def export_dynamic_args(self) -> T.List[str]:
if mesonlib.version_compare(self.version, '>=224.1'):
return self._apply_prefix('-export_dynamic')
return []
@@ -943,11 +994,11 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna
id = 'ld.lld'
- def __init__(self, exelist: T.List[str],
+ def __init__(self, exelist: T.List[str], env: Environment,
for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]],
always_args: T.List[str], *, system: str = 'unknown system',
version: str = 'unknown version'):
- super().__init__(exelist, for_machine, prefix_arg, always_args, system=system, version=version)
+ super().__init__(exelist, env, for_machine, prefix_arg, always_args, system=system, version=version)
# Some targets don't seem to support this argument (windows, wasm, ...)
self.has_allow_shlib_undefined = self._supports_flag('--allow-shlib-undefined', always_args)
@@ -1015,16 +1066,17 @@ class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna
def no_undefined_args(self) -> T.List[str]:
return ['-sERROR_ON_UNDEFINED_SYMBOLS=1']
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
raise MesonException(f'{self.id} does not support shared libraries.')
def get_asneeded_args(self) -> T.List[str]:
return []
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
@@ -1034,9 +1086,9 @@ class CcrxDynamicLinker(DynamicLinker):
id = 'rlink'
- def __init__(self, for_machine: mesonlib.MachineChoice,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(['rlink.exe'], for_machine, '', [],
+ super().__init__(['rlink.exe'], env, for_machine, '', [],
version=version)
def get_accepts_rsp(self) -> bool:
@@ -1057,8 +1109,9 @@ class CcrxDynamicLinker(DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
return []
@@ -1068,9 +1121,9 @@ class Xc16DynamicLinker(DynamicLinker):
id = 'xc16-gcc'
- def __init__(self, for_machine: mesonlib.MachineChoice,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(['xc16-gcc'], for_machine, '', [],
+ super().__init__(['xc16-gcc'], env, for_machine, '', [],
version=version)
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
@@ -1096,24 +1149,45 @@ class Xc16DynamicLinker(DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
return []
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
+
+class Xc32DynamicLinker(GnuDynamicLinker):
+
+ """Linker for Microchip XC32 compiler."""
+
+ id = 'ld.xc32'
+
+ def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return DynamicLinker.get_coverage_args(self)
+
+ def get_pie_args(self) -> T.List[str]:
+ return DynamicLinker.get_pie_args(self)
+
+ def thread_flags(self) -> T.List[str]:
+ return []
+
+
class CompCertDynamicLinker(DynamicLinker):
"""Linker for CompCert C compiler."""
id = 'ccomp'
- def __init__(self, for_machine: mesonlib.MachineChoice,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(['ccomp'], for_machine, '', [],
+ super().__init__(['ccomp'], env, for_machine, '', [],
version=version)
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
@@ -1139,13 +1213,9 @@ class CompCertDynamicLinker(DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
- 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: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
return ([], set())
class TIDynamicLinker(DynamicLinker):
@@ -1154,9 +1224,9 @@ class TIDynamicLinker(DynamicLinker):
id = 'ti'
- def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
+ def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(exelist, for_machine, '', [],
+ super().__init__(exelist, env, for_machine, '', [],
version=version)
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
@@ -1200,9 +1270,9 @@ class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
id = 'armlink'
- def __init__(self, for_machine: mesonlib.MachineChoice,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(['armlink'], for_machine, '', [],
+ super().__init__(['armlink'], env, for_machine, '', [],
version=version)
def get_accepts_rsp(self) -> bool:
@@ -1223,7 +1293,7 @@ class ArmClangDynamicLinker(ArmDynamicLinker):
extends a few things as needed.
"""
- def export_dynamic_args(self, env: 'Environment') -> T.List[str]:
+ def export_dynamic_args(self) -> T.List[str]:
return ['--export_dynamic']
def import_library_args(self, implibname: str) -> T.List[str]:
@@ -1235,6 +1305,12 @@ class QualcommLLVMDynamicLinker(LLVMDynamicLinker):
id = 'ld.qcld'
+class ELDDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker):
+
+ """Qualcomm's opensource embedded linker"""
+
+ id = 'ld.eld'
+
class NAGDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
@@ -1249,17 +1325,20 @@ class NAGDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
id = 'nag'
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: 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:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ rpath_paths = target.determine_rpath_dirs()
+ if not rpath_paths and not target.install_rpath and not target.build_rpath and not extra_paths:
return ([], set())
args: T.List[str] = []
origin_placeholder = '$ORIGIN'
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths])
- if build_rpath != '':
- all_paths.add(build_rpath)
+ if target.build_rpath != '':
+ all_paths.add(target.build_rpath)
+ if extra_paths:
+ all_paths.update(extra_paths)
for rp in all_paths:
args.extend(self._apply_prefix('-Wl,-Wl,,-rpath,,' + rp))
@@ -1282,8 +1361,9 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
return []
def get_std_shared_lib_args(self) -> T.List[str]:
@@ -1294,10 +1374,11 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
return ['-shared']
return []
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: 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():
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ if not self.environment.machines[self.for_machine].is_windows():
+ rpath_paths = target.determine_rpath_dirs()
return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set())
return ([], set())
@@ -1305,8 +1386,8 @@ NvidiaHPC_DynamicLinker = PGIDynamicLinker
class PGIStaticLinker(StaticLinker):
- def __init__(self, exelist: T.List[str]):
- super().__init__(exelist)
+ def __init__(self, exelist: T.List[str], env: Environment):
+ super().__init__(exelist, env)
self.id = 'ar'
self.std_args = ['-r']
@@ -1339,12 +1420,12 @@ class VisualStudioLikeLinkerMixin(DynamicLinkerBase):
's': ['/INCREMENTAL:NO', '/OPT:REF'],
}
- def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
- prefix_arg: T.Union[str, T.List[str]], always_args: T.List[str], *,
- version: str = 'unknown version', direct: bool = True, machine: str = 'x86',
- rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC):
- # There's no way I can find to make mypy understand what's going on here
- super().__init__(exelist, for_machine, prefix_arg, always_args, version=version)
+ def __init__(self, exelist: T.List[str], env: Environment,
+ for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]],
+ always_args: T.List[str], *, version: str = 'unknown version',
+ direct: bool = True, machine: str = 'x86', rsp_syntax:
+ RSPFileSyntax = RSPFileSyntax.MSVC):
+ super().__init__(exelist, env, for_machine, prefix_arg, always_args, version=version)
self.machine = machine
self.direct = direct
self.rsp_syntax = rsp_syntax
@@ -1382,8 +1463,14 @@ class VisualStudioLikeLinkerMixin(DynamicLinkerBase):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ # With MSVC, DLLs only export symbols that are explicitly exported,
+ # so if a module defs file is specified, we use that to export symbols
+ return ['/DEF:' + defsfile]
+
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
return []
def import_library_args(self, implibname: str) -> T.List[str]:
@@ -1403,12 +1490,13 @@ class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
id = 'link'
- def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
+ always_args: T.List[str], *,
exelist: T.Optional[T.List[str]] = None,
prefix: T.Union[str, T.List[str]] = '',
machine: str = 'x86', version: str = 'unknown version',
direct: bool = True, rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC):
- super().__init__(exelist or ['link.exe'], for_machine,
+ super().__init__(exelist or ['link.exe'], env, for_machine,
prefix, always_args, machine=machine, version=version, direct=direct,
rsp_syntax=rsp_syntax)
@@ -1428,12 +1516,13 @@ class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
id = 'lld-link'
- def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
+ always_args: T.List[str], *,
exelist: T.Optional[T.List[str]] = None,
prefix: T.Union[str, T.List[str]] = '',
machine: str = 'x86', version: str = 'unknown version',
direct: bool = True, rsp_syntax: RSPFileSyntax = RSPFileSyntax.MSVC):
- super().__init__(exelist or ['lld-link.exe'], for_machine,
+ super().__init__(exelist or ['lld-link.exe'], env, for_machine,
prefix, always_args, machine=machine, version=version, direct=direct,
rsp_syntax=rsp_syntax)
@@ -1461,12 +1550,13 @@ class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
id = 'xilink'
- def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *,
+ def __init__(self, env: Environment, for_machine: mesonlib.MachineChoice,
+ always_args: T.List[str], *,
exelist: T.Optional[T.List[str]] = None,
prefix: T.Union[str, T.List[str]] = '',
machine: str = 'x86', version: str = 'unknown version',
direct: bool = True):
- super().__init__(['xilink.exe'], for_machine, '', always_args, version=version)
+ super().__init__(['xilink.exe'], env, for_machine, '', always_args, version=version)
def get_win_subsystem_args(self, value: str) -> T.List[str]:
return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}'])
@@ -1505,26 +1595,29 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def fatal_warnings(self) -> T.List[str]:
return ['-z', 'fatal-warnings']
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: 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:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ rpath_paths = target.determine_rpath_dirs()
+ if not rpath_paths and not target.install_rpath and not target.build_rpath and not extra_paths:
return ([], set())
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths])
rpath_dirs_to_remove: T.Set[bytes] = set()
for p in all_paths:
rpath_dirs_to_remove.add(p.encode('utf8'))
- if build_rpath != '':
- all_paths.add(build_rpath)
- for p in build_rpath.split(':'):
+ if target.build_rpath != '':
+ all_paths.add(target.build_rpath)
+ for p in target.build_rpath.split(':'):
rpath_dirs_to_remove.add(p.encode('utf8'))
+ if extra_paths:
+ all_paths.update(extra_paths)
# In order to avoid relinking for RPATH removal, the binary needs to contain just
# enough space in the ELF header to hold the final installation RPATH.
paths = ':'.join(all_paths)
paths_length = len(paths.encode('utf-8'))
- install_rpath_length = len(install_rpath.encode('utf-8'))
+ install_rpath_length = len(target.install_rpath.encode('utf-8'))
if paths_length < install_rpath_length:
padding = 'X' * (install_rpath_length - paths_length)
if not paths:
@@ -1533,8 +1626,9 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
paths = paths + ':' + padding
return (self._apply_prefix(f'-rpath,{paths}'), rpath_dirs_to_remove)
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
sostr = '' if soversion is None else '.' + soversion
return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}')
@@ -1575,20 +1669,20 @@ class AIXDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
# archives or not."
return args
- def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
- rpath_paths: T.Tuple[str, ...], build_rpath: str,
- install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ def build_rpath_args(self, build_dir: str, from_dir: str, target: BuildTarget,
+ extra_paths: T.Optional[T.List[str]] = None
+ ) -> T.Tuple[T.List[str], T.Set[bytes]]:
all_paths: mesonlib.OrderedSet[str] = mesonlib.OrderedSet()
# install_rpath first, followed by other paths, and the system path last
- if install_rpath != '':
- all_paths.add(install_rpath)
- if build_rpath != '':
- all_paths.add(build_rpath)
- for p in rpath_paths:
+ if target.install_rpath != '':
+ all_paths.add(target.install_rpath)
+ if target.build_rpath != '':
+ all_paths.add(target.build_rpath)
+ for p in target.determine_rpath_dirs():
all_paths.add(os.path.join(build_dir, p))
# We should consider allowing the $LIBPATH environment variable
# to override sys_path.
- sys_path = env.get_compiler_system_lib_dirs(self.for_machine)
+ sys_path = self.environment.get_compiler_system_lib_dirs(self.for_machine)
if len(sys_path) == 0:
# get_compiler_system_lib_dirs doesn't support our compiler.
# Use the default system library path
@@ -1598,9 +1692,11 @@ class AIXDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
for p in sys_path:
if os.path.isdir(p):
all_paths.add(p)
+ if extra_paths:
+ all_paths.update(extra_paths)
return (self._apply_prefix('-blibpath:' + ':'.join(all_paths)), set())
- def thread_flags(self, env: 'Environment') -> T.List[str]:
+ def thread_flags(self) -> T.List[str]:
return ['-pthread']
@@ -1610,11 +1706,11 @@ class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
id = 'optlink'
- def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
+ def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
# Use optlink instead of link so we don't interfere with other link.exe
# implementations.
- super().__init__(exelist, for_machine, '', [], version=version)
+ super().__init__(exelist, env, for_machine, '', [], version=version)
def get_allow_undefined_args(self) -> T.List[str]:
return []
@@ -1674,16 +1770,17 @@ class CudaLinker(PosixDynamicLinkerMixin, DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return []
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
return []
class MetrowerksLinker(DynamicLinker):
- def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
+ def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(exelist, for_machine, '', [],
+ super().__init__(exelist, env, for_machine, '', [],
version=version)
def fatal_warnings(self) -> T.List[str]:
@@ -1707,8 +1804,9 @@ class MetrowerksLinker(DynamicLinker):
def invoked_by_compiler(self) -> bool:
return False
- def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
- suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
raise MesonException(f'{self.id} does not support shared libraries.')
@@ -1732,9 +1830,9 @@ class TaskingLinker(DynamicLinker):
's': ['-Os'],
}
- def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
+ def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(exelist, for_machine, '', [],
+ super().__init__(exelist, env, for_machine, '', [],
version=version)
def get_accepts_rsp(self) -> bool:
@@ -1771,3 +1869,32 @@ class TaskingLinker(DynamicLinker):
for a in args:
l.extend(self._apply_prefix('-Wl--whole-archive=' + a))
return l
+
+
+class OS2DynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
+ """ld and emxomfld"""
+
+ def get_allow_undefined_args(self) -> T.List[str]:
+ return []
+
+ def thread_flags(self) -> T.List[str]:
+ return ['-lpthread']
+
+ def get_std_shared_lib_args(self) -> T.List[str]:
+ return ['-Zdll']
+
+ def get_soname_args(self, prefix: str, shlib_name: str, suffix: str,
+ soversion: str, darwin_versions: T.Tuple[str, str]
+ ) -> T.List[str]:
+ return []
+
+ def get_always_args(self) -> T.List[str]:
+ return ['-Zomf']
+
+
+class OS2AoutDynamicLinker(OS2DynamicLinker):
+ id = 'ld.os2'
+
+
+class OS2OmfDynamicLinker(OS2DynamicLinker):
+ id = 'emxomfld'
diff --git a/mesonbuild/machinefile.py b/mesonbuild/machinefile.py
index b39a472..19a0865 100644
--- a/mesonbuild/machinefile.py
+++ b/mesonbuild/machinefile.py
@@ -9,26 +9,12 @@ import os
from . import mparser
+from .cmdline import CmdLineFileParser
from .mesonlib import MesonException
if T.TYPE_CHECKING:
- from .coredata import StrOrBytesPath
from .options import ElementaryOptionValues
-class CmdLineFileParser(configparser.ConfigParser):
- def __init__(self) -> None:
- # We don't want ':' as key delimiter, otherwise it would break when
- # storing subproject options like "subproject:option=value"
- super().__init__(delimiters=['='], interpolation=None)
-
- def read(self, filenames: T.Union['StrOrBytesPath', T.Iterable['StrOrBytesPath']], encoding: T.Optional[str] = 'utf-8') -> T.List[str]:
- return super().read(filenames, encoding)
-
- def optionxform(self, optionstr: str) -> str:
- # Don't call str.lower() on keys
- return optionstr
-
-
class MachineFileParser():
def __init__(self, filenames: T.List[str], sourcedir: str) -> None:
self.parser = CmdLineFileParser()
@@ -97,12 +83,12 @@ class MachineFileParser():
elif isinstance(node, mparser.ArithmeticNode):
l = self._evaluate_statement(node.left)
r = self._evaluate_statement(node.right)
- if node.operation == 'add':
+ if node.operation == '+':
if isinstance(l, str) and isinstance(r, str):
return l + r
if isinstance(l, list) and isinstance(r, list):
return l + r
- elif node.operation == 'div':
+ elif node.operation == '/':
if isinstance(l, str) and isinstance(r, str):
return os.path.join(l, r)
raise MesonException('Unsupported node type')
diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py
index cfaeb79..7221958 100644
--- a/mesonbuild/mcompile.py
+++ b/mesonbuild/mcompile.py
@@ -18,7 +18,7 @@ from . import mlog
from . import mesonlib
from .options import OptionKey
from .mesonlib import MesonException, RealPathAction, join_args, listify_array_value, setup_vsenv
-from mesonbuild.environment import detect_ninja
+from mesonbuild.tooldetect import detect_ninja
from mesonbuild import build
if T.TYPE_CHECKING:
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 416caf1..4de7bc2 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -13,6 +13,7 @@ import typing as T
import collections
from . import build
+from . import cmdline
from . import coredata
from . import options
from . import environment
@@ -28,12 +29,11 @@ if T.TYPE_CHECKING:
from typing_extensions import Protocol
import argparse
- class CMDOptions(coredata.SharedCMDOptions, Protocol):
+ class CMDOptions(cmdline.SharedCMDOptions, Protocol):
builddir: str
clearcache: bool
pager: bool
- unset_opts: T.List[str]
# cannot be TV_Loggable, because non-ansidecorators do direct string concat
LOGLINE = T.Union[str, mlog.AnsiDecorator]
@@ -41,13 +41,13 @@ if T.TYPE_CHECKING:
# Note: when adding arguments, please also add them to the completion
# scripts in $MESONSRC/data/shell-completions/
def add_arguments(parser: 'argparse.ArgumentParser') -> None:
- coredata.register_builtin_arguments(parser)
+ cmdline.register_builtin_arguments(parser)
parser.add_argument('builddir', nargs='?', default='.')
parser.add_argument('--clearcache', action='store_true', default=False,
help='Clear cached state (e.g. found dependencies)')
parser.add_argument('--no-pager', action='store_false', dest='pager',
help='Do not redirect output to a pager')
- parser.add_argument('-U', action='append', dest='unset_opts', default=[],
+ parser.add_argument('-U', action=cmdline.KeyNoneAction, dest='cmd_line_options', default={},
help='Remove a subproject option.')
def stringify(val: T.Any) -> str:
@@ -73,6 +73,7 @@ class Conf:
self.build_dir = os.path.dirname(self.build_dir)
self.build = None
self.max_choices_line_length = 60
+ self.pending_section: T.Optional[str] = None
self.name_col: T.List[LOGLINE] = []
self.value_col: T.List[LOGLINE] = []
self.choices_col: T.List[LOGLINE] = []
@@ -125,9 +126,6 @@ class Conf:
def clear_cache(self) -> None:
self.coredata.clear_cache()
- def set_options(self, options: T.Dict[OptionKey, str]) -> bool:
- return self.coredata.set_options(options)
-
def save(self) -> None:
# Do nothing when using introspection
if self.default_values_only:
@@ -149,7 +147,7 @@ class Conf:
Each column will have a specific width, and will be line wrapped.
"""
total_width = shutil.get_terminal_size(fallback=(160, 0))[0]
- _col = max(total_width // 5, 20)
+ _col = max(total_width // 5, 24)
last_column = total_width - (3 * _col) - 3
four_column = (_col, _col, _col, last_column if last_column > 1 else _col)
@@ -194,7 +192,7 @@ class Conf:
) -> T.Dict[str, options.MutableKeyedOptionDictType]:
result: T.Dict[str, options.MutableKeyedOptionDictType] = {}
for k, o in opts.items():
- if k.subproject:
+ if k.subproject is not None:
self.all_subprojects.add(k.subproject)
result.setdefault(k.subproject, {})[k] = o
return result
@@ -209,12 +207,15 @@ class Conf:
self.choices_col.append(choices)
self.descr_col.append(descr)
- def add_option(self, name: str, descr: str, value: T.Any, choices: T.Any) -> None:
+ def add_option(self, key: OptionKey, descr: str, value: T.Any, choices: T.Any) -> None:
+ self._add_section()
value = stringify(value)
choices = stringify(choices)
- self._add_line(mlog.green(name), mlog.yellow(value), mlog.blue(choices), descr)
+ self._add_line(mlog.green(str(key.evolve(subproject=None))), mlog.yellow(value),
+ mlog.blue(choices), descr)
def add_title(self, title: str) -> None:
+ self._add_section()
newtitle = mlog.cyan(title)
descr = mlog.cyan('Description')
value = mlog.cyan('Default Value' if self.default_values_only else 'Current Value')
@@ -223,11 +224,17 @@ class Conf:
self._add_line(newtitle, value, choices, descr)
self._add_line('-' * len(newtitle), '-' * len(value), '-' * len(choices), '-' * len(descr))
- def add_section(self, section: str) -> None:
+ def _add_section(self) -> None:
+ if not self.pending_section:
+ return
self.print_margin = 0
self._add_line('', '', '', '')
- self._add_line(mlog.normal_yellow(section + ':'), '', '', '')
+ self._add_line(mlog.normal_yellow(self.pending_section + ':'), '', '', '')
self.print_margin = 2
+ self.pending_section = None
+
+ def add_section(self, section: str) -> None:
+ self.pending_section = section
def print_options(self, title: str, opts: T.Union[options.MutableKeyedOptionDictType, options.OptionStore]) -> None:
if not opts:
@@ -242,7 +249,7 @@ class Conf:
# printable_value = '<inherited from main project>'
#if isinstance(o, options.UserFeatureOption) and o.is_auto():
# printable_value = auto.printable_value()
- self.add_option(k.name, o.description, printable_value, o.printable_choices())
+ self.add_option(k, o.description, printable_value, o.printable_choices())
def print_conf(self, pager: bool) -> None:
if pager:
@@ -291,15 +298,15 @@ class Conf:
project_options = self.split_options_per_subproject({k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_project_option(k)})
show_build_options = self.default_values_only or self.build.environment.is_cross_build()
- self.add_section('Main project options')
+ self.add_section('Global build options')
self.print_options('Core options', host_core_options[None])
if show_build_options and build_core_options:
self.print_options('', build_core_options[None])
self.print_options('Backend options', {k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_backend_option(k)})
self.print_options('Base options', {k: v for k, v in self.coredata.optstore.items() if self.coredata.optstore.is_base_option(k)})
- self.print_options('Compiler options', host_compiler_options.get('', {}))
+ self.print_options('Compiler options', host_compiler_options.get(None, {}))
if show_build_options:
- self.print_options('', build_compiler_options.get('', {}))
+ self.print_options('', build_compiler_options.get(None, {}))
for mod, mod_options in module_options.items():
self.print_options(f'{mod} module options', mod_options)
self.print_options('Directories', dir_options)
@@ -307,8 +314,9 @@ class Conf:
self.print_options('Project options', project_options.get('', {}))
for subproject in sorted(self.all_subprojects):
if subproject == '':
- continue
- self.add_section('Subproject ' + subproject)
+ self.add_section('Main project')
+ else:
+ self.add_section('Subproject ' + subproject)
if subproject in host_core_options:
self.print_options('Core options', host_core_options[subproject])
if subproject in build_core_options and show_build_options:
@@ -317,7 +325,7 @@ class Conf:
self.print_options('Compiler options', host_compiler_options[subproject])
if subproject in build_compiler_options and show_build_options:
self.print_options('', build_compiler_options[subproject])
- if subproject in project_options:
+ if subproject != '' and subproject in project_options:
self.print_options('Project options', project_options[subproject])
self.print_aligned()
@@ -342,16 +350,12 @@ class Conf:
if self.coredata.optstore.augments:
mlog.log('\nCurrently set option augments:')
for k, v in self.coredata.optstore.augments.items():
- mlog.log(f'{k:21}{v:10}')
+ mlog.log(f'{k!s:21}{v:10}')
else:
mlog.log('\nThere are no option augments.')
def has_option_flags(options: CMDOptions) -> bool:
- if options.cmd_line_options:
- return True
- if options.unset_opts:
- return True
- return False
+ return bool(options.cmd_line_options)
def is_print_only(options: CMDOptions) -> bool:
if has_option_flags(options):
@@ -373,12 +377,8 @@ def run_impl(options: CMDOptions, builddir: str) -> int:
save = False
if has_option_flags(options):
- unset_opts = getattr(options, 'unset_opts', [])
- all_D = options.projectoptions[:]
- for keystr, valstr in options.cmd_line_options.items():
- all_D.append(f'{keystr}={valstr}')
- save |= c.coredata.optstore.set_from_configure_command(all_D, unset_opts)
- coredata.update_cmd_line_file(builddir, options)
+ save |= c.coredata.set_from_configure_command(options)
+ cmdline.update_cmd_line_file(builddir, options)
if options.clearcache:
c.clear_cache()
save = True
@@ -397,6 +397,6 @@ def run_impl(options: CMDOptions, builddir: str) -> int:
return 0
def run(options: CMDOptions) -> int:
- coredata.parse_cmd_line_options(options)
+ cmdline.parse_cmd_line_options(options)
builddir = os.path.abspath(os.path.realpath(options.builddir))
return run_impl(options, builddir)
diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py
index 4962d96..e6c4fad 100644
--- a/mesonbuild/mdevenv.py
+++ b/mesonbuild/mdevenv.py
@@ -4,13 +4,15 @@ import os, subprocess
import argparse
import tempfile
import shutil
+import sys
import itertools
+import signal
import typing as T
from pathlib import Path
from . import build, minstall
from .mesonlib import (EnvironmentVariables, MesonException, join_args, is_windows, setup_vsenv,
- get_wine_shortpath, MachineChoice, relpath)
+ get_wine_shortpath, MachineChoice, relpath, is_osx)
from .options import OptionKey
from . import mlog
@@ -82,7 +84,7 @@ def bash_completion_files(b: build.Build, install_data: 'InstallData') -> T.List
from .dependencies.pkgconfig import PkgConfigDependency
result = []
dep = PkgConfigDependency('bash-completion', b.environment,
- {'required': False, 'silent': True, 'version': '>=2.10'})
+ {'required': False, 'silent': True, 'version': ['>=2.10']})
if dep.found():
prefix = b.environment.coredata.optstore.get_value_for(OptionKey('prefix'))
assert isinstance(prefix, str), 'for mypy'
@@ -150,6 +152,14 @@ def write_gdb_script(privatedir: Path, install_data: 'InstallData', workdir: Pat
mlog.log(' - Change current workdir to', mlog.bold(str(rel_path.parent)),
'or use', mlog.bold(f'--init-command {rel_path}'))
+def macos_sip_enabled() -> bool:
+ if not is_osx():
+ return False
+ ret = subprocess.run(["csrutil", "status"], text=True, capture_output=True, encoding='utf-8')
+ if not ret.stdout:
+ return True
+ return 'enabled' in ret.stdout
+
def dump(devenv: T.Dict[str, str], varnames: T.Set[str], dump_format: T.Optional[str], output: T.Optional[T.TextIO] = None) -> None:
for name in varnames:
print(f'{name}="{devenv[name]}"', file=output)
@@ -192,6 +202,8 @@ def run(options: argparse.Namespace) -> int:
args = options.devcmd
if not args:
prompt_prefix = f'[{b.project_name}]'
+ if os.environ.get("MESON_DISABLE_PS1_OVERRIDE"):
+ prompt_prefix = None
shell_env = os.environ.get("SHELL")
# Prefer $SHELL in a MSYS2 bash despite it being Windows
if shell_env and os.path.exists(shell_env):
@@ -202,8 +214,9 @@ def run(options: argparse.Namespace) -> int:
mlog.warning('Failed to determine Windows shell, fallback to cmd.exe')
if shell in POWERSHELL_EXES:
args = [shell, '-NoLogo', '-NoExit']
- prompt = f'function global:prompt {{ "{prompt_prefix} PS " + $PWD + "> "}}'
- args += ['-Command', prompt]
+ if prompt_prefix:
+ prompt = f'function global:prompt {{ "{prompt_prefix} PS " + $PWD + "> "}}'
+ args += ['-Command', prompt]
else:
args = [os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe")]
args += ['/k', f'prompt {prompt_prefix} $P$G']
@@ -213,21 +226,51 @@ def run(options: argparse.Namespace) -> int:
# Let the GC remove the tmp file
tmprc = tempfile.NamedTemporaryFile(mode='w')
tmprc.write('[ -e ~/.bashrc ] && . ~/.bashrc\n')
- if not os.environ.get("MESON_DISABLE_PS1_OVERRIDE"):
+ if prompt_prefix:
tmprc.write(f'export PS1="{prompt_prefix} $PS1"\n')
for f in bash_completion_files(b, install_data):
tmprc.write(f'. "{f}"\n')
tmprc.flush()
args.append("--rcfile")
args.append(tmprc.name)
+ elif args[0].endswith('fish'):
+ # Ignore SIGINT while using fish as the shell to make it behave
+ # like other shells such as bash and zsh.
+ # See: https://gitlab.freedesktop.org/gstreamer/gst-build/issues/18
+ signal.signal(signal.SIGINT, lambda _, __: True)
+ if prompt_prefix:
+ args.append('--init-command')
+ prompt_cmd = f'''functions --copy fish_prompt original_fish_prompt
+ function fish_prompt
+ echo -n '[{prompt_prefix}] '(original_fish_prompt)
+ end'''
+ args.append(prompt_cmd)
+ elif args[0].endswith('zsh'):
+ # Let the GC remove the tmp file
+ tmpdir = tempfile.TemporaryDirectory()
+ with open(os.path.join(tmpdir.name, '.zshrc'), 'w') as zshrc: # pylint: disable=unspecified-encoding
+ zshrc.write('[ -e ~/.zshrc ] && . ~/.zshrc\n')
+ if prompt_prefix:
+ zshrc.write(f'export PROMPT="[{prompt_prefix}] $PROMPT"\n')
+ devenv['ZDOTDIR'] = tmpdir.name
+ if 'DYLD_LIBRARY_PATH' in devenv and macos_sip_enabled():
+ mlog.warning('macOS System Integrity Protection is enabled: DYLD_LIBRARY_PATH cannot be set in the subshell')
+ mlog.warning('To fix that, use `meson devenv --dump dev.env && source dev.env`')
+ del devenv['DYLD_LIBRARY_PATH']
else:
# Try to resolve executable using devenv's PATH
abs_path = shutil.which(args[0], path=devenv.get('PATH', None))
args[0] = abs_path or args[0]
try:
- os.chdir(workdir)
- os.execvpe(args[0], args, env=devenv)
+ if is_windows():
+ # execvpe doesn't return exit code on Windows
+ # see https://github.com/python/cpython/issues/63323
+ result = subprocess.run(args, env=devenv, cwd=workdir)
+ sys.exit(result.returncode)
+ else:
+ os.chdir(workdir)
+ os.execvpe(args[0], args, env=devenv)
except FileNotFoundError:
raise MesonException(f'Command not found: {args[0]}')
except OSError as e:
diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py
index 0361606..15718d2 100644
--- a/mesonbuild/mdist.py
+++ b/mesonbuild/mdist.py
@@ -7,7 +7,6 @@ from __future__ import annotations
import abc
import argparse
-import gzip
import os
import sys
import shlex
@@ -21,13 +20,14 @@ import typing as T
from dataclasses import dataclass
from glob import glob
from pathlib import Path
-from mesonbuild.environment import Environment, detect_ninja
+from mesonbuild.environment import Environment
+from mesonbuild.tooldetect import detect_ninja
from mesonbuild.mesonlib import (GIT, MesonException, RealPathAction, get_meson_command, quiet_git,
windows_proof_rmtree, setup_vsenv)
from .options import OptionKey
from mesonbuild.msetup import add_arguments as msetup_argparse
from mesonbuild.wrap import wrap
-from mesonbuild import mlog, build, coredata
+from mesonbuild import mlog, build, cmdline
from .scripts.meson_exe import run_exe
if T.TYPE_CHECKING:
@@ -41,6 +41,9 @@ archive_extension = {'bztar': '.tar.bz2',
'xztar': '.tar.xz',
'zip': '.zip'}
+if sys.version_info >= (3, 14):
+ tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter)
+
# Note: when adding arguments, please also add them to the completion
# scripts in $MESONSRC/data/shell-completions/
def add_arguments(parser: argparse.ArgumentParser) -> None:
@@ -294,6 +297,7 @@ class HgDist(Dist):
shutil.copyfileobj(tf, bf)
output_names.append(bz2name)
if 'gztar' in archives:
+ import gzip
with gzip.open(gzname, 'wb') as zf, open(tarname, 'rb') as tf:
shutil.copyfileobj(tf, zf)
output_names.append(gzname)
@@ -353,11 +357,11 @@ def check_dist(packagename: str, _meson_command: ImmutableListProtocol[str], ext
def create_cmdline_args(bld_root: str) -> T.List[str]:
parser = argparse.ArgumentParser()
msetup_argparse(parser)
- args = T.cast('coredata.SharedCMDOptions', parser.parse_args([]))
- coredata.parse_cmd_line_options(args)
- coredata.read_cmd_line_file(bld_root, args)
+ args = T.cast('cmdline.SharedCMDOptions', parser.parse_args([]))
+ cmdline.parse_cmd_line_options(args)
+ cmdline.read_cmd_line_file(bld_root, args)
args.cmd_line_options.pop(OptionKey('backend'), '')
- return shlex.split(coredata.format_cmd_line_options(args))
+ return shlex.split(cmdline.format_cmd_line_options(args))
def determine_archives_to_generate(options: argparse.Namespace) -> T.List[str]:
result = []
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 9da2786..89b3a1c 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -1,4 +1,4 @@
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2021 The Meson development team
# Copyright © 2021-2023 Intel Corporation
@@ -12,12 +12,4 @@ from .utils.core import *
from .utils.vsenv import *
from .utils.universal import *
-
-# Here we import either the posix implementations, the windows implementations,
-# or a generic no-op implementation
-if os.name == 'posix':
- from .utils.posix import *
-elif os.name == 'nt':
- from .utils.win32 import *
-else:
- from .utils.platform import *
+from .utils.platform import *
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index dd265c4..ca7a184 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -240,14 +240,14 @@ def validate_original_args(args):
def has_startswith(coll, target):
for entry in coll:
- if entry.startswith(target):
+ if entry.startswith(target + '=') or entry == target:
return True
return False
#ds = [x for x in args if x.startswith('-D')]
#longs = [x for x in args if x.startswith('--')]
for optionkey in itertools.chain(mesonbuild.options.BUILTIN_DIR_OPTIONS, mesonbuild.options.BUILTIN_CORE_OPTIONS):
longarg = mesonbuild.options.argparse_name_to_arg(optionkey.name)
- shortarg = f'-D{optionkey.name}='
+ shortarg = f'-D{optionkey.name}'
if has_startswith(args, longarg) and has_startswith(args, shortarg):
sys.exit(
f'Got argument {optionkey.name} as both {shortarg} and {longarg}. Pick one.')
diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py
index 1e134f5..0281ed5 100644
--- a/mesonbuild/mformat.py
+++ b/mesonbuild/mformat.py
@@ -3,6 +3,7 @@
from __future__ import annotations
+import difflib
import re
import typing as T
from configparser import ConfigParser, MissingSectionHeaderError, ParsingError
@@ -242,6 +243,10 @@ class MultilineArgumentDetector(FullAstVisitor):
if node.is_multiline:
self.is_multiline = True
+ nargs = len(node)
+ if nargs and nargs == len(node.commas):
+ self.is_multiline = True
+
if self.is_multiline:
return
@@ -251,6 +256,19 @@ class MultilineArgumentDetector(FullAstVisitor):
super().visit_ArgumentNode(node)
+class MultilineParenthesesDetector(FullAstVisitor):
+
+ def __init__(self) -> None:
+ self.last_whitespaces: T.Optional[mparser.WhitespaceNode] = None
+
+ def enter_node(self, node: mparser.BaseNode) -> None:
+ self.last_whitespaces = None
+
+ def exit_node(self, node: mparser.BaseNode) -> None:
+ if node.whitespaces and node.whitespaces.value:
+ self.last_whitespaces = node.whitespaces
+
+
class TrimWhitespaces(FullAstVisitor):
def __init__(self, config: FormatterConfig):
@@ -284,6 +302,8 @@ class TrimWhitespaces(FullAstVisitor):
def add_space_after(self, node: mparser.BaseNode) -> None:
if not node.whitespaces.value:
node.whitespaces.value = ' '
+ elif '#' not in node.whitespaces.value:
+ node.whitespaces.value = ' '
def add_nl_after(self, node: mparser.BaseNode, force: bool = False) -> None:
if not node.whitespaces.value:
@@ -340,6 +360,7 @@ class TrimWhitespaces(FullAstVisitor):
super().visit_SymbolNode(node)
if node.value in "([{" and node.whitespaces.value == '\n':
node.whitespaces.value = ''
+ node.whitespaces.accept(self)
def visit_StringNode(self, node: mparser.StringNode) -> None:
self.enter_node(node)
@@ -352,7 +373,7 @@ class TrimWhitespaces(FullAstVisitor):
if node.is_fstring and '@' not in node.value:
node.is_fstring = False
- self.exit_node(node)
+ node.whitespaces.accept(self)
def visit_UnaryOperatorNode(self, node: mparser.UnaryOperatorNode) -> None:
super().visit_UnaryOperatorNode(node)
@@ -536,18 +557,28 @@ class TrimWhitespaces(FullAstVisitor):
def visit_ParenthesizedNode(self, node: mparser.ParenthesizedNode) -> None:
self.enter_node(node)
- is_multiline = node.lpar.lineno != node.rpar.lineno
- if is_multiline:
+ if node.lpar.whitespaces and '#' in node.lpar.whitespaces.value:
+ node.is_multiline = True
+
+ elif not node.is_multiline:
+ ml_detector = MultilineParenthesesDetector()
+ node.inner.accept(ml_detector)
+ if ml_detector.last_whitespaces and '\n' in ml_detector.last_whitespaces.value:
+ # We keep it multiline if last parenthesis is on a separate line
+ node.is_multiline = True
+
+ if node.is_multiline:
self.indent_comments += self.config.indent_by
node.lpar.accept(self)
node.inner.accept(self)
- if is_multiline:
+ if node.is_multiline:
node.inner.whitespaces.value = self.dedent(node.inner.whitespaces.value)
self.indent_comments = self.dedent(self.indent_comments)
- if node.lpar.whitespaces and '\n' in node.lpar.whitespaces.value:
- self.add_nl_after(node.inner)
+ self.add_nl_after(node.inner)
+ else:
+ node.inner.whitespaces = None
node.rpar.accept(self)
self.move_whitespaces(node.rpar, node)
@@ -560,6 +591,7 @@ class ArgumentFormatter(FullAstVisitor):
self.level = 0
self.indent_after = False
self.is_function_arguments = False
+ self.par_level = 0
def add_space_after(self, node: mparser.BaseNode) -> None:
if not node.whitespaces.value:
@@ -570,7 +602,7 @@ class ArgumentFormatter(FullAstVisitor):
node.whitespaces.value = '\n'
indent_by = (node.condition_level + indent) * self.config.indent_by
if indent_by:
- node.whitespaces.value += indent_by
+ node.whitespaces.value = re.sub(rf'\n({self.config.indent_by})*', '\n' + indent_by, node.whitespaces.value)
def visit_ArrayNode(self, node: mparser.ArrayNode) -> None:
self.enter_node(node)
@@ -670,7 +702,7 @@ class ArgumentFormatter(FullAstVisitor):
if self.config.group_arg_value:
for arg in node.arguments[:-1]:
group_args = False
- if isinstance(arg, mparser.StringNode) and arg.value.startswith('--'):
+ if isinstance(arg, mparser.StringNode) and arg.value.startswith('--') and arg.value != '--':
next_arg = node.arguments[arg_index + 1]
if isinstance(next_arg, mparser.StringNode) and not next_arg.value.startswith('--'):
group_args = True
@@ -678,11 +710,11 @@ class ArgumentFormatter(FullAstVisitor):
# keep '--arg', 'value' on same line
self.add_space_after(node.commas[arg_index])
elif arg_index < len(node.commas):
- self.add_nl_after(node.commas[arg_index], self.level)
+ self.add_nl_after(node.commas[arg_index], self.level + self.par_level)
arg_index += 1
for comma in node.commas[arg_index:-1]:
- self.add_nl_after(comma, self.level)
+ self.add_nl_after(comma, self.level + self.par_level)
if node.arguments or node.kwargs:
self.add_nl_after(node, self.level - 1)
@@ -697,17 +729,38 @@ class ArgumentFormatter(FullAstVisitor):
def visit_ParenthesizedNode(self, node: mparser.ParenthesizedNode) -> None:
self.enter_node(node)
- is_multiline = '\n' in node.lpar.whitespaces.value
- if is_multiline:
+ if node.is_multiline:
+ self.par_level += 1
current_indent_after = self.indent_after
self.indent_after = True
node.lpar.accept(self)
+ if node.is_multiline:
+ self.add_nl_after(node.lpar, indent=self.level + self.par_level)
node.inner.accept(self)
- if is_multiline:
+ if node.is_multiline:
+ self.par_level -= 1
self.indent_after = current_indent_after
node.rpar.accept(self)
self.exit_node(node)
+ def visit_OrNode(self, node: mparser.OrNode) -> None:
+ self.enter_node(node)
+ node.left.accept(self)
+ if self.par_level:
+ self.add_nl_after(node.left, indent=self.level + self.par_level)
+ node.operator.accept(self)
+ node.right.accept(self)
+ self.exit_node(node)
+
+ def visit_AndNode(self, node: mparser.AndNode) -> None:
+ self.enter_node(node)
+ node.left.accept(self)
+ if self.par_level:
+ self.add_nl_after(node.left, indent=self.level + self.par_level)
+ node.operator.accept(self)
+ node.right.accept(self)
+ self.exit_node(node)
+
class ComputeLineLengths(FullAstVisitor):
@@ -807,6 +860,16 @@ class ComputeLineLengths(FullAstVisitor):
self.split_if_needed(node.args) # split if closing bracket is too far
self.exit_node(node)
+ def visit_ParenthesizedNode(self, node: mparser.ParenthesizedNode) -> None:
+ self.enter_node(node)
+ node.lpar.accept(self)
+ node.inner.accept(self)
+ node.rpar.accept(self)
+ if not node.is_multiline and self.length > self.config.max_line_length:
+ node.is_multiline = True
+ self.need_regenerate = True
+ self.exit_node(node)
+
class SubdirFetcher(FullAstVisitor):
@@ -837,7 +900,15 @@ class Formatter:
# See https://editorconfig.org/
config = EditorConfig()
- for p in source_file.parents:
+ if source_file == Path('STDIN'):
+ raise MesonException('Using editorconfig with stdin requires --source-file-path argument')
+
+ try:
+ source_file_path = source_file.resolve()
+ except FileNotFoundError:
+ raise MesonException(f'Unable to resolve path for "{source_file}"')
+
+ for p in source_file_path.parents:
editorconfig_file = p / '.editorconfig'
if not editorconfig_file.exists():
continue
@@ -926,7 +997,13 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
inplace_group.add_argument(
'-q', '--check-only',
action='store_true',
- help='exit with 1 if files would be modified by meson format'
+ help='silently exit with 1 if files would be modified by meson format'
+ )
+ inplace_group.add_argument(
+ '-d', '--check-diff',
+ action='store_true',
+ default=False,
+ help='exit with 1 and show diff if files would be modified by meson format'
)
inplace_group.add_argument(
'-i', '--inplace',
@@ -956,6 +1033,11 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
help='output file (implies having exactly one input)'
)
parser.add_argument(
+ '--source-file-path',
+ type=Path,
+ help='path to use, when reading from stdin'
+ )
+ parser.add_argument(
'sources',
nargs='*',
type=Path,
@@ -981,6 +1063,10 @@ def run(options: argparse.Namespace) -> int:
raise MesonException('--recursive argument is not compatible with stdin input')
if options.inplace and from_stdin:
raise MesonException('--inplace argument is not compatible with stdin input')
+ if options.source_file_path and not from_stdin:
+ raise MesonException('--source-file-path argument is only compatible with stdin input')
+ if from_stdin and options.editor_config and not options.source_file_path:
+ raise MesonException('using --editor-config with stdin input requires --source-file-path argument')
sources: T.List[Path] = options.sources.copy() or [Path(build_filename)]
@@ -996,7 +1082,7 @@ def run(options: argparse.Namespace) -> int:
try:
if from_stdin:
- src_file = Path('STDIN') # used for error messages and introspection
+ src_file = options.source_file_path or Path('STDIN') # used for error messages and introspection
code = sys.stdin.read()
else:
code = src_file.read_text(encoding='utf-8')
@@ -1013,9 +1099,14 @@ def run(options: argparse.Namespace) -> int:
sf.write(formatted)
except IOError as e:
raise MesonException(f'Unable to write to {src_file}') from e
- elif options.check_only:
- # TODO: add verbose output showing diffs
+ elif options.check_only or options.check_diff:
if code != formatted:
+ if options.check_diff:
+ diff = difflib.unified_diff(code.splitlines(), formatted.splitlines(),
+ str(src_file), str(src_file),
+ '(original)', '(reformatted)',
+ lineterm='')
+ print('\n'.join(diff))
return 1
elif options.output:
try:
diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py
index 192c75a..7d0d051 100644
--- a/mesonbuild/minit.py
+++ b/mesonbuild/minit.py
@@ -17,7 +17,7 @@ import typing as T
from mesonbuild import build, mesonlib, mlog
from mesonbuild.coredata import FORBIDDEN_TARGET_NAMES
-from mesonbuild.environment import detect_ninja
+from mesonbuild.tooldetect import detect_ninja
from mesonbuild.templates.mesontemplates import create_meson_build
from mesonbuild.templates.samplefactory import sample_generator
from mesonbuild.options import OptionKey
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index f65087c..f9c8480 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -15,7 +15,7 @@ import sys
import typing as T
import re
-from . import build, environment
+from . import build, tooldetect
from .backend.backends import InstallData
from .mesonlib import (MesonException, Popen_safe, RealPathAction, is_windows,
is_aix, setup_vsenv, pickle_load, is_osx)
@@ -441,15 +441,13 @@ class Installer:
append_to_log(self.lf, to_file)
return True
- def do_symlink(self, target: str, link: str, destdir: str, full_dst_dir: str, allow_missing: bool) -> bool:
+ def do_symlink(self, target: str, link: str, destdir: str, full_dst_dir: str) -> bool:
abs_target = target
if not os.path.isabs(target):
abs_target = os.path.join(full_dst_dir, target)
- elif not os.path.exists(abs_target) and not allow_missing:
+ elif not os.path.exists(abs_target):
abs_target = destdir_join(destdir, abs_target)
- if not os.path.exists(abs_target) and not allow_missing:
- raise MesonException(f'Tried to install symlink to missing file {abs_target}')
- if os.path.exists(link):
+ if os.path.lexists(link):
if not os.path.islink(link):
raise MesonException(f'Destination {link!r} already exists and is not a symlink')
self.remove(link)
@@ -656,7 +654,7 @@ class Installer:
full_dst_dir = get_destdir_path(destdir, fullprefix, s.install_path)
full_link_name = get_destdir_path(destdir, fullprefix, s.name)
dm.makedirs(full_dst_dir, exist_ok=True)
- if self.do_symlink(s.target, full_link_name, destdir, full_dst_dir, s.allow_missing):
+ if self.do_symlink(s.target, full_link_name, destdir, full_dst_dir):
self.did_install_something = True
def install_man(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
@@ -805,7 +803,7 @@ def rebuild_all(wd: str, backend: str) -> bool:
print('Only ninja backend is supported to rebuild the project before installation.')
return True
- ninja = environment.detect_ninja()
+ ninja = tooldetect.detect_ninja()
if not ninja:
print("Can't find ninja, can't rebuild test.")
return False
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index 462ee2f..b1e0941 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -19,20 +19,22 @@ from pathlib import Path, PurePath
import sys
import typing as T
-from . import build, mesonlib, options, coredata as cdata
-from .ast import IntrospectionInterpreter, BUILD_TARGET_FUNCTIONS, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstJSONPrinter
+from . import build, environment, mesonlib, options, coredata as cdata
+from .ast import IntrospectionInterpreter, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstJSONPrinter
from .backend import backends
-from .dependencies import Dependency
-from . import environment
-from .interpreterbase import ObjectHolder
+from .interpreterbase import UnknownValue
from .options import OptionKey
-from .mparser import FunctionNode, ArrayNode, ArgumentNode, StringNode
if T.TYPE_CHECKING:
import argparse
- from .interpreter import Interpreter
- from .mparser import BaseNode
+ from .dependencies import Dependency
+
+class IntrospectionEncoder(json.JSONEncoder):
+ def default(self, obj: T.Any) -> T.Any:
+ if isinstance(obj, UnknownValue):
+ return 'unknown'
+ return json.JSONEncoder.default(self, obj)
def get_meson_info_file(info_dir: str) -> str:
return os.path.join(info_dir, 'meson-info.json')
@@ -54,12 +56,11 @@ class IntroCommand:
def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None,
builddata: T.Optional[build.Build] = None,
- backend: T.Optional[backends.Backend] = None) -> 'T.Mapping[str, IntroCommand]':
+ backend: T.Optional[backends.Backend] = None) -> T.Mapping[str, IntroCommand]:
if backend and builddata:
benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks())
testdata = backend.create_test_serialisation(builddata.get_tests())
installdata = backend.create_install_data()
- interpreter = backend.interpreter
else:
benchmarkdata = testdata = installdata = None
@@ -68,7 +69,7 @@ def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None,
('ast', IntroCommand('Dump the AST of the meson file', no_bd=dump_ast)),
('benchmarks', IntroCommand('List all benchmarks', func=lambda: list_benchmarks(benchmarkdata))),
('buildoptions', IntroCommand('List all build options', func=lambda: list_buildoptions(coredata), no_bd=list_buildoptions_from_source)),
- ('buildsystem_files', IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata, interpreter))),
+ ('buildsystem_files', IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata))),
('compilers', IntroCommand('List used compilers', func=lambda: list_compilers(coredata))),
('dependencies', IntroCommand('List external dependencies', func=lambda: list_deps(coredata, backend), no_bd=list_deps_from_source)),
('scan_dependencies', IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source)),
@@ -122,14 +123,15 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]:
res[basename] = os.path.join(installdata.prefix, s.install_path, basename)
return res
-def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]]:
- plan: T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]] = {
+def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]]:
+ plan: T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]] = {
'targets': {
os.path.join(installdata.build_dir, target.fname): {
'destination': target.out_name,
'tag': target.tag or None,
'subproject': target.subproject or None,
- 'install_rpath': target.install_rpath or None
+ 'install_rpath': target.install_rpath or None,
+ 'build_rpaths': sorted(x.decode('utf8') for x in target.rpath_dirs_to_remove),
}
for target in installdata.targets
},
@@ -169,56 +171,35 @@ def get_target_dir(coredata: cdata.CoreData, subdir: str) -> str:
else:
return subdir
-def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
- tlist: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]] = []
- root_dir = Path(intr.source_root)
-
- def nodes_to_paths(node_list: T.List[BaseNode]) -> T.List[Path]:
- res: T.List[Path] = []
- for n in node_list:
- args: T.List[BaseNode] = []
- if isinstance(n, FunctionNode):
- args = list(n.args.arguments)
- if n.func_name.value in BUILD_TARGET_FUNCTIONS:
- args.pop(0)
- elif isinstance(n, ArrayNode):
- args = n.args.arguments
- elif isinstance(n, ArgumentNode):
- args = n.arguments
- for j in args:
- if isinstance(j, StringNode):
- assert isinstance(j.value, str)
- res += [Path(j.value)]
- elif isinstance(j, str):
- res += [Path(j)]
- res = [root_dir / i['subdir'] / x for x in res]
- res = [x.resolve() for x in res]
- return res
+def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, object]]:
+ tlist: T.List[T.Dict[str, object]] = []
+ root_dir = Path(intr.source_root).resolve()
for i in intr.targets:
- sources = nodes_to_paths(i['sources'])
- extra_f = nodes_to_paths(i['extra_files'])
- outdir = get_target_dir(intr.coredata, i['subdir'])
+ sources = intr.nodes_to_pretty_filelist(root_dir, i.subdir, i.source_nodes)
+ extra_files = intr.nodes_to_pretty_filelist(root_dir, i.subdir, [i.extra_files] if i.extra_files else [])
+
+ outdir = get_target_dir(intr.coredata, i.subdir)
tlist += [{
- 'name': i['name'],
- 'id': i['id'],
- 'type': i['type'],
- 'defined_in': i['defined_in'],
- 'filename': [os.path.join(outdir, x) for x in i['outputs']],
- 'build_by_default': i['build_by_default'],
+ 'name': i.name,
+ 'id': i.id,
+ 'type': i.typename,
+ 'defined_in': i.defined_in,
+ 'filename': [os.path.join(outdir, x) for x in i.outputs],
+ 'build_by_default': i.build_by_default,
'target_sources': [{
'language': 'unknown',
- 'machine': i['machine'],
+ 'machine': i.machine,
'compiler': [],
'parameters': [],
- 'sources': [str(x) for x in sources],
+ 'sources': sources,
'generated_sources': []
}],
'depends': [],
- 'extra_files': [str(x) for x in extra_f],
+ 'extra_files': extra_files,
'subproject': None, # Subprojects are not supported
- 'installed': i['installed']
+ 'installed': i.installed
}]
return tlist
@@ -357,10 +338,9 @@ def find_buildsystem_files_list(src_dir: str) -> T.List[str]:
for f in build_files.intersection(files))
return filelist
-def list_buildsystem_files(builddata: build.Build, interpreter: Interpreter) -> T.List[str]:
+def list_buildsystem_files(builddata: build.Build) -> T.List[str]:
src_dir = builddata.environment.get_source_dir()
- filelist = list(interpreter.get_build_def_files())
- filelist = [PurePath(src_dir, x).as_posix() for x in filelist]
+ filelist = [PurePath(src_dir, x).as_posix() for x in builddata.def_files]
return filelist
def list_compilers(coredata: cdata.CoreData) -> T.Dict[str, T.Dict[str, T.Dict[str, str]]]:
@@ -380,23 +360,22 @@ def list_compilers(coredata: cdata.CoreData) -> T.Dict[str, T.Dict[str, T.Dict[s
}
return compilers
-def list_deps_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool]]]:
- result: T.List[T.Dict[str, T.Union[str, bool]]] = []
+def list_deps_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool, T.List[str], UnknownValue]]]:
+ result: T.List[T.Dict[str, T.Union[str, bool, T.List[str], UnknownValue]]] = []
for i in intr.dependencies:
- keys = [
- 'name',
- 'required',
- 'version',
- 'has_fallback',
- 'conditional',
- ]
- result += [{k: v for k, v in i.items() if k in keys}]
+ result += [{
+ 'name': i.name,
+ 'required': i.required,
+ 'version': i.version,
+ 'has_fallback': i.has_fallback,
+ 'conditional': i.conditional,
+ }]
return result
def list_deps(coredata: cdata.CoreData, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[str, T.List[str]]]]:
result: T.Dict[str, T.Dict[str, T.Union[str, T.List[str]]]] = {}
- def _src_to_str(src_file: T.Union[mesonlib.FileOrString, build.CustomTarget, build.StructuredSources, build.CustomTargetIndex, build.GeneratedList]) -> T.List[str]:
+ def _src_to_str(src_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources]) -> T.List[str]:
if isinstance(src_file, str):
return [src_file]
if isinstance(src_file, mesonlib.File):
@@ -407,7 +386,7 @@ def list_deps(coredata: cdata.CoreData, backend: backends.Backend) -> T.List[T.D
return [f for s in src_file.as_list() for f in _src_to_str(s)]
raise mesonlib.MesonBugException(f'Invalid file type {type(src_file)}.')
- def _create_result(d: Dependency, varname: T.Optional[str] = None) -> T.Dict[str, T.Any]:
+ def _create_result(d: Dependency) -> T.Dict[str, T.Any]:
return {
'name': d.name,
'type': d.type_name,
@@ -419,22 +398,13 @@ def list_deps(coredata: cdata.CoreData, backend: backends.Backend) -> T.List[T.D
'extra_files': [f for s in d.get_extra_files() for f in _src_to_str(s)],
'dependencies': [e.name for e in d.ext_deps],
'depends': [lib.get_id() for lib in getattr(d, 'libraries', [])],
- 'meson_variables': [varname] if varname else [],
+ 'meson_variables': d.meson_variables,
}
for d in coredata.deps.host.values():
if d.found():
result[d.name] = _create_result(d)
- for varname, holder in backend.interpreter.variables.items():
- if isinstance(holder, ObjectHolder):
- d = holder.held_object
- if isinstance(d, Dependency) and d.found():
- if d.name in result:
- T.cast('T.List[str]', result[d.name]['meson_variables']).append(varname)
- else:
- result[d.name] = _create_result(d, varname)
-
return list(result.values())
def get_test_list(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]:
@@ -517,12 +487,12 @@ def print_results(options: argparse.Namespace, results: T.Sequence[T.Tuple[str,
return 1
elif len(results) == 1 and not options.force_dict:
# Make to keep the existing output format for a single option
- print(json.dumps(results[0][1], indent=indent))
+ print(json.dumps(results[0][1], indent=indent, cls=IntrospectionEncoder))
else:
out = {}
for i in results:
out[i[0]] = i[1]
- print(json.dumps(out, indent=indent))
+ print(json.dumps(out, indent=indent, cls=IntrospectionEncoder))
return 0
def get_infodir(builddir: T.Optional[str] = None) -> str:
@@ -546,10 +516,11 @@ def run(options: argparse.Namespace) -> int:
datadir = os.path.join(options.builddir, datadir)
indent = 4 if options.indent else None
results: T.List[T.Tuple[str, T.Union[dict, T.List[T.Any]]]] = []
- sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11]
intro_types = get_meson_introspection_types()
- if 'meson.build' in [os.path.basename(options.builddir), options.builddir]:
+ # TODO: This if clause is undocumented.
+ if os.path.basename(options.builddir) == environment.build_filename:
+ sourcedir = '.' if options.builddir == environment.build_filename else options.builddir[:-len(environment.build_filename)]
# Make sure that log entries in other parts of meson don't interfere with the JSON output
with redirect_stdout(sys.stderr):
backend = backends.get_backend_from_name(options.backend)
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index b43ac8a..e1d998a 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -67,6 +67,7 @@ class _Logger:
log_depth: T.List[str] = field(default_factory=list)
log_to_stderr: bool = False
log_file: T.Optional[T.TextIO] = None
+ slog_file: T.Optional[T.TextIO] = None
log_timestamp_start: T.Optional[float] = None
log_fatal_warnings = False
log_disable_stdout = False
@@ -76,6 +77,7 @@ class _Logger:
log_pager: T.Optional['subprocess.Popen'] = None
_LOG_FNAME: T.ClassVar[str] = 'meson-log.txt'
+ _SLOG_FNAME: T.ClassVar[str] = 'meson-setup.txt'
@contextmanager
def no_logging(self) -> T.Iterator[None]:
@@ -110,6 +112,12 @@ class _Logger:
self.log_file = None
exception_around_goer.close()
return path
+ if self.slog_file is not None:
+ path = self.slog_file.name
+ exception_around_goer = self.slog_file
+ self.slog_file = None
+ exception_around_goer.close()
+ return path
self.stop_pager()
return None
@@ -164,6 +172,7 @@ class _Logger:
def initialize(self, logdir: str, fatal_warnings: bool = False) -> None:
self.log_dir = logdir
self.log_file = open(os.path.join(logdir, self._LOG_FNAME), 'w', encoding='utf-8')
+ self.slog_file = open(os.path.join(logdir, self._SLOG_FNAME), 'w', encoding='utf-8')
self.log_fatal_warnings = fatal_warnings
def process_markup(self, args: T.Sequence[TV_Loggable], keep: bool, display_timestamp: bool = True) -> T.List[str]:
@@ -224,6 +233,9 @@ class _Logger:
if self.log_file is not None:
print(*arr, file=self.log_file, sep=sep, end=end)
self.log_file.flush()
+ if self.slog_file is not None:
+ print(*arr, file=self.slog_file, sep=sep, end=end)
+ self.slog_file.flush()
if self.colorize_console():
arr = process_markup(args, True, display_timestamp)
if not self.log_errors_only or is_error:
@@ -394,7 +406,7 @@ class _Logger:
_colorize_console = os.isatty(output.fileno()) and os.environ.get('TERM', 'dumb') != 'dumb'
except Exception:
_colorize_console = False
- output.colorize_console = _colorize_console # type: ignore[attr-defined]
+ output.colorize_console = _colorize_console # type: ignore
return _colorize_console
def setup_console(self) -> None:
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index 67d1666..3ff9368 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -41,8 +41,8 @@ class ModuleState:
self.root_subdir = interpreter.root_subdir
self.current_lineno = interpreter.current_node.lineno
self.environment = interpreter.environment
- self.project_name = interpreter.build.project_name
- self.project_version = interpreter.build.dep_manifest[interpreter.active_projectname].version
+ self.project_name = interpreter.active_projectname
+ self.project_version = interpreter.project_version
# The backend object is under-used right now, but we will need it:
# https://github.com/mesonbuild/meson/issues/1419
self.backend = interpreter.backend
@@ -75,14 +75,14 @@ class ModuleState:
required: bool = True,
version_func: T.Optional[ProgramVersionFunc] = None,
wanted: T.Union[str, T.List[str]] = '', silent: bool = False,
- for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.Executable, OverrideProgram]:
+ for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram]:
if not isinstance(prog, list):
prog = [prog]
return self._interpreter.find_program_impl(prog, required=required, version_func=version_func,
wanted=wanted, silent=silent, for_machine=for_machine)
def find_tool(self, name: str, depname: str, varname: str, required: bool = True,
- wanted: T.Optional[str] = None) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
+ wanted: T.Optional[str] = None) -> T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']:
# Look in overrides in case it's built as subproject
progobj = self._interpreter.program_from_overrides([name], [])
if progobj is not None:
diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py
index 7d52842..ae6e2d4 100644
--- a/mesonbuild/modules/_qt.py
+++ b/mesonbuild/modules/_qt.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2015 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
+# Copyright © 2021-2025 Intel Corporation
from __future__ import annotations
@@ -14,16 +14,17 @@ from . import ModuleReturnValue, ExtensionModule
from .. import build
from .. import options
from .. import mlog
-from ..dependencies import find_external_dependency, Dependency, ExternalLibrary, InternalDependency
+from ..dependencies import DependencyMethods, find_external_dependency, Dependency, ExternalLibrary, InternalDependency
from ..mesonlib import MesonException, File, FileMode, version_compare, Popen_safe
from ..interpreter import extract_required_kwarg
-from ..interpreter.type_checking import INSTALL_DIR_KW, INSTALL_KW, NoneType
+from ..interpreter.type_checking import DEPENDENCY_METHOD_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType
from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs, typed_pos_args
from ..programs import NonExistingExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency
+ from ..dependencies.base import DependencyObjectKWs
from ..interpreter import Interpreter
from ..interpreter import kwargs
from ..mesonlib import FileOrString
@@ -39,27 +40,27 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Resource Compiler method."""
name: T.Optional[str]
- sources: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
+ sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]
extra_args: T.List[str]
- method: str
+ method: DependencyMethods
class UICompilerKwArgs(TypedDict):
"""Keyword arguments for the Ui Compiler method."""
- sources: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
+ sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]
extra_args: T.List[str]
- method: str
+ method: DependencyMethods
preserve_paths: bool
class MocCompilerKwArgs(TypedDict):
"""Keyword arguments for the Moc Compiler method."""
- sources: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
- headers: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
+ sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]
+ headers: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]
extra_args: T.List[str]
- method: str
+ method: DependencyMethods
include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
preserve_paths: bool
@@ -78,12 +79,12 @@ if T.TYPE_CHECKING:
moc_output_json: bool
include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
- method: str
+ method: DependencyMethods
preserve_paths: bool
class HasToolKwArgs(kwargs.ExtractRequired):
- method: str
+ method: DependencyMethods
tools: T.List[Literal['moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar']]
class CompileTranslationsKwArgs(TypedDict):
@@ -91,10 +92,10 @@ if T.TYPE_CHECKING:
build_by_default: bool
install: bool
install_dir: T.Optional[str]
- method: str
+ method: DependencyMethods
qresource: T.Optional[str]
rcc_extra_arguments: T.List[str]
- ts_files: T.List[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
+ ts_files: T.List[T.Union[str, File, build.GeneratedTypes]]
class GenQrcKwArgs(TypedDict):
@@ -126,7 +127,7 @@ if T.TYPE_CHECKING:
qml_qrc: T.Union[FileOrString, build.GeneratedTypes]
extra_args: T.List[str]
module_prefix: str
- method: str
+ method: DependencyMethods
class GenQmlTypeRegistrarKwArgs(TypedDict):
@@ -139,7 +140,7 @@ if T.TYPE_CHECKING:
generate_qmltype: bool
collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]]
extra_args: T.List[str]
- method: str
+ method: DependencyMethods
install: bool
install_dir: T.Optional[str]
@@ -147,7 +148,7 @@ if T.TYPE_CHECKING:
target_name: str
moc_json: T.Sequence[build.GeneratedList]
- method: str
+ method: DependencyMethods
class QmlModuleKwArgs(TypedDict):
@@ -173,7 +174,7 @@ if T.TYPE_CHECKING:
generate_qmltype: bool
cachegen: bool
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
- method: str
+ method: DependencyMethods
preserve_paths: bool
install_dir: str
install: bool
@@ -264,12 +265,12 @@ class QtBaseModule(ExtensionModule):
if p.found():
self.tools[name] = p
- def _detect_tools(self, state: ModuleState, method: str, required: bool = True) -> None:
+ def _detect_tools(self, state: ModuleState, method: DependencyMethods, required: bool = True) -> None:
if self._tools_detected:
return
self._tools_detected = True
mlog.log(f'Detecting Qt{self.qt_version} tools')
- kwargs = {'required': required, 'modules': 'Core', 'method': method}
+ kwargs: DependencyObjectKWs = {'required': required, 'modules': ['Core'], 'method': method}
# Just pick one to make mypy happy
qt = T.cast('QtPkgConfigDependency', find_external_dependency(f'qt{self.qt_version}', state.environment, kwargs))
if qt.found():
@@ -325,7 +326,7 @@ class QtBaseModule(ExtensionModule):
raise MesonException(f'Unable to parse resource file {abspath}')
def _parse_qrc_deps(self, state: ModuleState,
- rcc_file_: T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]) -> T.List[File]:
+ rcc_file_: T.Union[FileOrString, build.GeneratedTypes]) -> T.List[File]:
result: T.List[File] = []
inputs: T.Sequence['FileOrString'] = []
if isinstance(rcc_file_, (str, File)):
@@ -365,15 +366,15 @@ class QtBaseModule(ExtensionModule):
@noPosargs
@typed_kwargs(
'qt.has_tools',
+ DEPENDENCY_METHOD_KW,
KwargInfo('required', (bool, options.UserFeatureOption), default=False),
- KwargInfo('method', str, default='auto'),
KwargInfo('tools', ContainerTypeInfo(list, str), listify=True,
default=['moc', 'uic', 'rcc', 'lrelease'],
validator=_list_in_set_validator(_set_of_qt_tools),
since='1.6.0'),
)
def has_tools(self, state: ModuleState, args: T.Tuple, kwargs: HasToolKwArgs) -> bool:
- method = kwargs.get('method', 'auto')
+ method = kwargs['method']
# We have to cast here because TypedDicts are invariant, even though
# ExtractRequiredKwArgs is a subset of HasToolKwArgs, type checkers
# will insist this is wrong
@@ -394,6 +395,7 @@ class QtBaseModule(ExtensionModule):
@noPosargs
@typed_kwargs(
'qt.compile_resources',
+ DEPENDENCY_METHOD_KW,
KwargInfo('name', (str, NoneType)),
KwargInfo(
'sources',
@@ -402,7 +404,6 @@ class QtBaseModule(ExtensionModule):
required=True,
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
- KwargInfo('method', str, default='auto')
)
def compile_resources(self, state: 'ModuleState', args: T.Tuple, kwargs: 'ResourceCompilerKwArgs') -> ModuleReturnValue:
"""Compile Qt resources files.
@@ -486,6 +487,7 @@ class QtBaseModule(ExtensionModule):
@noPosargs
@typed_kwargs(
'qt.compile_ui',
+ DEPENDENCY_METHOD_KW,
KwargInfo(
'sources',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList), allow_empty=False),
@@ -493,7 +495,6 @@ class QtBaseModule(ExtensionModule):
required=True,
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
- KwargInfo('method', str, default='auto'),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
)
def compile_ui(self, state: ModuleState, args: T.Tuple, kwargs: UICompilerKwArgs) -> ModuleReturnValue:
@@ -513,18 +514,19 @@ class QtBaseModule(ExtensionModule):
raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version))
preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None
- # TODO: This generator isn't added to the generator list in the Interpreter
gen = build.Generator(
+ state.environment,
self.tools['uic'],
kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'],
['ui_@BASENAME@.h'],
name=f'Qt{self.qt_version} ui')
- return gen.process_files(kwargs['sources'], state, preserve_path_from)
+ return gen.process_files(kwargs['sources'], state.subdir, preserve_path_from)
@FeatureNew('qt.compile_moc', '0.59.0')
@noPosargs
@typed_kwargs(
'qt.compile_moc',
+ DEPENDENCY_METHOD_KW,
KwargInfo(
'sources',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)),
@@ -538,7 +540,6 @@ class QtBaseModule(ExtensionModule):
default=[]
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
- KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
@@ -567,11 +568,15 @@ class QtBaseModule(ExtensionModule):
inc = state.get_include_args(include_dirs=kwargs['include_directories'])
compile_args: T.List[str] = []
+ sources: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = []
for dep in kwargs['dependencies']:
compile_args.extend(a for a in dep.get_all_compile_args() if a.startswith(('-I', '-D')))
if isinstance(dep, InternalDependency):
for incl in dep.include_directories:
compile_args.extend(f'-I{i}' for i in incl.to_string_list(self.interpreter.source_root, self.interpreter.environment.build_dir))
+ for src in dep.sources:
+ if isinstance(src, (build.CustomTarget, build.BuildTarget, build.CustomTargetIndex)):
+ sources.append(src)
output: T.List[build.GeneratedList] = []
@@ -590,25 +595,29 @@ class QtBaseModule(ExtensionModule):
if do_output_json:
header_gen_output.append('moc_@BASENAME@.cpp.json')
moc_gen = build.Generator(
+ state.environment,
self.tools['moc'], arguments, header_gen_output,
+ depends=sources,
depfile='moc_@BASENAME@.cpp.d',
name=f'Qt{self.qt_version} moc header')
- output.append(moc_gen.process_files(kwargs['headers'], state, preserve_path_from))
+ output.append(moc_gen.process_files(kwargs['headers'], state.subdir, preserve_path_from))
if kwargs['sources']:
source_gen_output: T.List[str] = ['@BASENAME@.moc']
if do_output_json:
source_gen_output.append('@BASENAME@.moc.json')
moc_gen = build.Generator(
+ state.environment,
self.tools['moc'], arguments, source_gen_output,
depfile='@BASENAME@.moc.d',
name=f'Qt{self.qt_version} moc source')
- output.append(moc_gen.process_files(kwargs['sources'], state, preserve_path_from))
+ output.append(moc_gen.process_files(kwargs['sources'], state.subdir, preserve_path_from))
return output
# We can't use typed_pos_args here, the signature is ambiguous
@typed_kwargs(
'qt.preprocess',
+ DEPENDENCY_METHOD_KW,
KwargInfo('sources', ContainerTypeInfo(list, (File, str)), listify=True, default=[], deprecated='0.59.0'),
KwargInfo('qresources', ContainerTypeInfo(list, (File, str)), listify=True, default=[]),
KwargInfo('ui_files', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]),
@@ -617,7 +626,6 @@ class QtBaseModule(ExtensionModule):
KwargInfo('moc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.44.0'),
KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'),
KwargInfo('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'),
- KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
@@ -674,9 +682,9 @@ class QtBaseModule(ExtensionModule):
@typed_kwargs(
'qt.compile_translations',
KwargInfo('build_by_default', bool, default=False),
+ DEPENDENCY_METHOD_KW,
INSTALL_KW,
INSTALL_DIR_KW,
- KwargInfo('method', str, default='auto'),
KwargInfo('qresource', (str, NoneType), since='0.56.0'),
KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.56.0'),
KwargInfo('ts_files', ContainerTypeInfo(list, (str, File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[]),
@@ -893,13 +901,14 @@ class QtBaseModule(ExtensionModule):
command_args.append('@INPUT@')
cache_gen = build.Generator(
+ state.environment,
self.tools['qmlcachegen'],
command_args,
[f'{target_name}_@BASENAME@.cpp'],
name=f'Qml cache generation for {target_name}')
output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = []
- output.append(cache_gen.process_files(kwargs['qml_sources'], state))
+ output.append(cache_gen.process_files(kwargs['qml_sources'], state.subdir))
cachegen_inputs: T.List[str] = []
qml_sources_paths = self._source_to_files(state, kwargs['qml_sources'])
@@ -1015,7 +1024,7 @@ class QtBaseModule(ExtensionModule):
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
INSTALL_DIR_KW,
INSTALL_KW,
- KwargInfo('method', str, default='auto'),
+ DEPENDENCY_METHOD_KW,
KwargInfo('preserve_paths', bool, default=False),
)
def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKwArgs) -> ModuleReturnValue:
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index e3154b0..ce22cef 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -13,7 +13,7 @@ from .. import build, mesonlib, mlog, dependencies
from ..options import OptionKey
from ..cmake import TargetOptions, cmake_defines_to_args
from ..interpreter import SubprojectHolder
-from ..interpreter.type_checking import REQUIRED_KW, INSTALL_DIR_KW, NoneType, in_set_validator
+from ..interpreter.type_checking import REQUIRED_KW, INSTALL_DIR_KW, INCLUDE_TYPE, NoneType, in_set_validator
from ..interpreterbase import (
FeatureNew,
@@ -34,6 +34,7 @@ if T.TYPE_CHECKING:
from . import ModuleState
from ..cmake.common import SingleTargetOptions
+ from ..dependencies.base import IncludeType
from ..environment import Environment
from ..interpreter import Interpreter, kwargs
from ..interpreterbase import TYPE_kwargs, TYPE_var, InterpreterObject
@@ -62,6 +63,10 @@ if T.TYPE_CHECKING:
target: T.Optional[str]
+ class DependencyKW(TypedDict):
+
+ include_type: IncludeType
+
_TARGET_KW = KwargInfo('target', (str, NoneType))
@@ -129,17 +134,8 @@ class CMakeSubproject(ModuleObject):
return self.subp.get_variable(args, kwargs)
@typed_pos_args('cmake.subproject.dependency', str)
- @typed_kwargs(
- 'cmake.subproject.dependency',
- KwargInfo(
- 'include_type',
- str,
- default='preserve',
- since='0.56.0',
- validator=in_set_validator({'preserve', 'system', 'non-system'})
- ),
- )
- def dependency(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, str]) -> dependencies.Dependency:
+ @typed_kwargs('cmake.subproject.dependency', INCLUDE_TYPE.evolve(since='0.56.0'))
+ def dependency(self, state: ModuleState, args: T.Tuple[str], kwargs: DependencyKW) -> dependencies.Dependency:
info = self._args_to_info(args[0])
if info['func'] == 'executable':
raise InvalidArguments(f'{args[0]} is an executable and does not support the dependency() method. Use target() instead.')
@@ -154,10 +150,11 @@ class CMakeSubproject(ModuleObject):
@noKwargs
@typed_pos_args('cmake.subproject.include_directories', str)
- def include_directories(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> build.IncludeDirs:
+ def include_directories(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> T.List[build.IncludeDirs]:
info = self._args_to_info(args[0])
inc = self.get_variable(state, [info['inc']], kwargs)
- assert isinstance(inc, build.IncludeDirs), 'for mypy'
+ assert isinstance(inc, list), 'for mypy'
+ assert isinstance(inc[0], build.IncludeDirs), 'for mypy'
return inc
@noKwargs
@@ -242,7 +239,7 @@ class CMakeSubprojectOptions(ModuleObject):
class CmakeModule(ExtensionModule):
cmake_detected = False
- cmake_root = None
+ cmake_root: str
INFO = ModuleInfo('cmake', '0.50.0')
@@ -264,7 +261,7 @@ class CmakeModule(ExtensionModule):
if not compiler:
raise mesonlib.MesonException('Requires a C or C++ compiler to compute sizeof(void *).')
- return compiler.sizeof('void *', '', env)[0]
+ return compiler.sizeof('void *', '')[0]
def detect_cmake(self, state: ModuleState) -> bool:
if self.cmake_detected:
@@ -438,7 +435,7 @@ class CmakeModule(ExtensionModule):
'required': kwargs_['required'],
'options': kwargs_['options'],
'cmake_options': kwargs_['cmake_options'],
- 'default_options': [],
+ 'default_options': {},
'version': [],
}
subp = self.interpreter.do_subproject(dirname, kw, force_method='cmake')
diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py
new file mode 100644
index 0000000..f37f964
--- /dev/null
+++ b/mesonbuild/modules/codegen.py
@@ -0,0 +1,445 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2024-2025 Intel Corporation
+
+from __future__ import annotations
+import dataclasses
+import os
+import typing as T
+
+from . import ExtensionModule, ModuleInfo
+from ..build import CustomTarget, CustomTargetIndex, GeneratedList
+from ..compilers.compilers import lang_suffixes
+from ..interpreter.interpreterobjects import extract_required_kwarg
+from ..interpreter.type_checking import NoneType, REQUIRED_KW, DISABLER_KW, NATIVE_KW
+from ..interpreterbase import (
+ ContainerTypeInfo, ObjectHolder, KwargInfo, typed_pos_args, typed_kwargs,
+ noPosargs, noKwargs, disablerIfNotFound, InterpreterObject
+)
+from ..mesonlib import File, MesonException, Popen_safe, version_compare
+from ..programs import ExternalProgram, NonExistingExternalProgram
+from ..utils.core import HoldableObject
+from .. import mlog
+
+if T.TYPE_CHECKING:
+ from typing_extensions import Literal, TypeAlias, TypedDict
+
+ from . import ModuleState
+ from .._typing import ImmutableListProtocol
+ from ..build import Executable
+ from ..interpreter import Interpreter
+ from ..interpreter.kwargs import ExtractRequired
+ from ..interpreterbase import TYPE_var, TYPE_kwargs
+ from ..mesonlib import MachineChoice
+ from ..programs import OverrideProgram
+
+ Program: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram]
+ LexImpls = Literal['lex', 'flex', 'reflex', 'win_flex']
+ YaccImpls = Literal['yacc', 'byacc', 'bison', 'win_bison']
+
+ class LexGenerateKwargs(TypedDict):
+
+ args: T.List[str]
+ source: T.Optional[str]
+ header: T.Optional[str]
+ table: T.Optional[str]
+ plainname: bool
+
+ class FindLexKwargs(ExtractRequired):
+
+ lex_version: T.List[str]
+ flex_version: T.List[str]
+ reflex_version: T.List[str]
+ win_flex_version: T.List[str]
+ implementations: T.List[LexImpls]
+ native: MachineChoice
+
+ class YaccGenerateKWargs(TypedDict):
+
+ args: T.List[str]
+ source: T.Optional[str]
+ header: T.Optional[str]
+ locations: T.Optional[str]
+ plainname: bool
+
+ class FindYaccKwargs(ExtractRequired):
+
+ yacc_version: T.List[str]
+ byacc_version: T.List[str]
+ bison_version: T.List[str]
+ win_bison_version: T.List[str]
+ implementations: T.List[YaccImpls]
+ native: MachineChoice
+
+
+def is_subset_validator(choices: T.Set[str]) -> T.Callable[[T.List[str]], T.Optional[str]]:
+
+ def inner(check: T.List[str]) -> T.Optional[str]:
+ if not set(check).issubset(choices):
+ invalid = ', '.join(sorted(set(check).difference(choices)))
+ valid = ', '.join(sorted(choices))
+ return f"valid members are '{valid}', not '{invalid}'"
+ return None
+
+ return inner
+
+
+@dataclasses.dataclass
+class _CodeGenerator(HoldableObject):
+
+ name: str
+ program: Program
+ arguments: ImmutableListProtocol[str] = dataclasses.field(default_factory=list)
+
+ def command(self) -> T.List[T.Union[Program, str]]:
+ return (T.cast('T.List[T.Union[Program, str]]', [self.program]) +
+ T.cast('T.List[T.Union[Program, str]]', self.arguments))
+
+ def found(self) -> bool:
+ return self.program.found()
+
+
+@dataclasses.dataclass
+class LexGenerator(_CodeGenerator):
+ pass
+
+
+class LexHolder(ObjectHolder[LexGenerator]):
+
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('implementation')
+ def implementation_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.name
+
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('found')
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.found()
+
+ @typed_pos_args('codegen.lex.generate', (str, File, GeneratedList, CustomTarget, CustomTargetIndex))
+ @typed_kwargs(
+ 'codegen.lex.generate',
+ KwargInfo('args', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('source', (str, NoneType)),
+ KwargInfo('header', (str, NoneType)),
+ KwargInfo('table', (str, NoneType)),
+ KwargInfo('plainname', bool, default=False),
+ )
+ @InterpreterObject.method('generate')
+ def generate_method(self, args: T.Tuple[T.Union[str, File, GeneratedList, CustomTarget, CustomTargetIndex]], kwargs: LexGenerateKwargs) -> CustomTarget:
+ if not self.held_object.found():
+ raise MesonException('Attempted to call generate without finding a lex implementation')
+
+ input = self.interpreter.source_strings_to_files([args[0]])[0]
+ if isinstance(input, File):
+ is_cpp = input.endswith(".ll")
+ name = os.path.splitext(input.fname)[0]
+ else:
+ gen_input = input.get_outputs()
+ if len(gen_input) != 1:
+ raise MesonException('codegen.lex.generate: generated type inputs must have exactly one output, index into them to select the correct input')
+ is_cpp = gen_input[0].endswith('.ll')
+ name = os.path.splitext(gen_input[0])[0]
+ name = os.path.basename(name)
+
+ # If an explicit source was given, use that to determine whether the
+ # user expects this to be a C or C++ source.
+ if kwargs['source'] is not None:
+ ext = kwargs['source'].rsplit('.', 1)[1]
+ is_cpp = ext in lang_suffixes['cpp']
+
+ for_machine = self.held_object.program.for_machine
+
+ # Flex uses FlexLexer.h for C++ code
+ if is_cpp and self.held_object.name in {'flex', 'win_flex'}:
+ try:
+ comp = self.interpreter.environment.coredata.compilers[for_machine]['cpp']
+ except KeyError:
+ raise MesonException(f"Could not find a C++ compiler for {for_machine} to search for FlexLexer.h")
+ found, _ = comp.has_header('FlexLexer.h', '')
+ if not found:
+ raise MesonException('Could not find FlexLexer.h, which is required for Flex with C++')
+
+ if kwargs['source'] is None:
+ outputs = ['@{}@.{}'.format(
+ 'PLAINNAME' if kwargs['plainname'] else 'BASENAME',
+ 'cpp' if is_cpp else 'c')]
+ else:
+ outputs = [kwargs['source']]
+
+ command = self.held_object.command()
+ if kwargs['header'] is not None:
+ outputs.append(kwargs['header'])
+ command.append(f'--header-file=@OUTPUT{len(outputs) - 1}@')
+ if kwargs['table'] is not None:
+ outputs.append(kwargs['table'])
+ command.append(f'--tables-file=@OUTPUT{len(outputs) - 1}@')
+ command.extend(kwargs['args'])
+ # Flex, at least, seems to require that input be the last argument given
+ command.append('@INPUT@')
+
+ target = CustomTarget(
+ f'codegen-lex-{name}-{for_machine.get_lower_case_name()}',
+ self.interpreter.subdir,
+ self.interpreter.subproject,
+ self.interpreter.environment,
+ command,
+ [input],
+ outputs,
+ backend=self.interpreter.backend,
+ description='Generating lexer {{}} with {}'.format(self.held_object.name),
+ )
+ self.interpreter.add_target(target.name, target)
+
+ return target
+
+
+@dataclasses.dataclass
+class YaccGenerator(_CodeGenerator):
+ pass
+
+
+class YaccHolder(ObjectHolder[YaccGenerator]):
+
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('implementation')
+ def implementation_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.name
+
+ @noPosargs
+ @noKwargs
+ @InterpreterObject.method('found')
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.found()
+
+ @typed_pos_args('codegen.yacc.generate', (str, File, GeneratedList, CustomTarget, CustomTargetIndex))
+ @typed_kwargs(
+ 'codegen.yacc.generate',
+ KwargInfo('args', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('source', (str, NoneType)),
+ KwargInfo('header', (str, NoneType)),
+ KwargInfo('locations', (str, NoneType)),
+ KwargInfo('plainname', bool, default=False),
+ )
+ @InterpreterObject.method('generate')
+ def generate_method(self, args: T.Tuple[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]], kwargs: YaccGenerateKWargs) -> CustomTarget:
+ if not self.held_object.found():
+ raise MesonException('Attempted to call generate without finding a yacc implementation')
+
+ input = self.interpreter.source_strings_to_files([args[0]])[0]
+ if isinstance(input, File):
+ is_cpp = input.endswith(".yy")
+ name = os.path.splitext(input.fname)[0]
+ else:
+ gen_input = input.get_outputs()
+ if len(gen_input) != 1:
+ raise MesonException('codegen.lex.generate: generated type inputs must have exactly one output, index into them to select the correct input')
+ is_cpp = gen_input[0].endswith('.yy')
+ name = os.path.splitext(gen_input[0])[0]
+ name = os.path.basename(name)
+
+ command = self.held_object.command()
+ command.extend(kwargs['args'])
+
+ source_ext = 'cpp' if is_cpp else 'c'
+ header_ext = 'hpp' if is_cpp else 'h'
+
+ base = '@PLAINNAME@' if kwargs['plainname'] else '@BASENAME@'
+ outputs: T.List[str] = []
+ outputs.append(f'{base}.{source_ext}' if kwargs['source'] is None else kwargs['source'])
+ outputs.append(f'{base}.{header_ext}' if kwargs['header'] is None else kwargs['header'])
+ if kwargs['locations'] is not None:
+ outputs.append(kwargs['locations'])
+
+ for_machine = self.held_object.program.for_machine
+ target = CustomTarget(
+ f'codegen-yacc-{name}-{for_machine.get_lower_case_name()}',
+ self.interpreter.subdir,
+ self.interpreter.subproject,
+ self.interpreter.environment,
+ command,
+ [input],
+ outputs,
+ backend=self.interpreter.backend,
+ description='Generating parser {{}} with {}'.format(self.held_object.name),
+ )
+ self.interpreter.add_target(target.name, target)
+ return target
+
+
+class CodeGenModule(ExtensionModule):
+
+ """Module with helpers for codegen wrappers."""
+
+ INFO = ModuleInfo('codegen', '1.10.0', unstable=True)
+
+ def __init__(self, interpreter: Interpreter) -> None:
+ super().__init__(interpreter)
+ self.methods.update({
+ 'lex': self.lex_method,
+ 'yacc': self.yacc_method,
+ })
+
+ @noPosargs
+ @typed_kwargs(
+ 'codegen.lex',
+ KwargInfo('lex_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('flex_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('reflex_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('win_flex_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo(
+ 'implementations',
+ ContainerTypeInfo(list, str),
+ default=[],
+ listify=True,
+ validator=is_subset_validator({'lex', 'flex', 'reflex', 'win_flex'})
+ ),
+ REQUIRED_KW,
+ DISABLER_KW,
+ NATIVE_KW
+ )
+ @disablerIfNotFound
+ def lex_method(self, state: ModuleState, args: T.Tuple, kwargs: FindLexKwargs) -> LexGenerator:
+ disabled, required, feature = extract_required_kwarg(kwargs, state.subproject)
+ if disabled:
+ mlog.log('generator lex skipped: feature', mlog.bold(feature), 'disabled')
+ return LexGenerator('lex', NonExistingExternalProgram('lex'))
+
+ names: T.List[LexImpls] = []
+ if kwargs['implementations']:
+ names = kwargs['implementations']
+ else:
+ assert state.environment.machines[kwargs['native']] is not None, 'for mypy'
+ if state.environment.machines[kwargs['native']].system == 'windows':
+ names.append('win_flex')
+ names.extend(['flex', 'reflex', 'lex'])
+
+ versions: T.Mapping[str, T.List[str]] = {
+ 'lex': kwargs['lex_version'],
+ 'flex': kwargs['flex_version'],
+ 'reflex': kwargs['reflex_version'],
+ 'win_flex': kwargs['win_flex_version']
+ }
+
+ for name in names:
+ bin = state.find_program(
+ name, wanted=versions[name], for_machine=kwargs['native'], required=False)
+ if bin.found():
+ # If you're building reflex as a subproject, we consider that you
+ # know what you're doing.
+ if name == 'reflex' and isinstance(bin, ExternalProgram):
+ # there are potentially 3 programs called "reflex":
+ # 1. https://invisible-island.net/reflex/, an alternate fork
+ # of the original flex, this is supported
+ # 2. https://www.genivia.com/doc/reflex/html/, an
+ # alternative implementation for generating C++ scanners.
+ # Not supported
+ # 3. https://github.com/cespare/reflex, which is not a lex
+ # implementation at all, but a file watcher
+ _, out, err = Popen_safe(bin.get_command() + ['--version'])
+ if 'unknown flag: --version' in err:
+ mlog.debug('Skipping cespare/reflex, which is not a lexer and is not supported')
+ continue
+ if 'Written by Robert van Engelen' in out:
+ mlog.debug('Skipping RE/flex, which is not compatible with POSIX lex.')
+ continue
+ break
+ else:
+ if required:
+ raise MesonException.from_node(
+ 'Could not find a lex implementation. Tried: ', ", ".join(names),
+ node=state.current_node)
+ return LexGenerator(name, bin)
+
+ lex_args: T.List[str] = []
+ # This option allows compiling with MSVC
+ # https://github.com/lexxmark/winflexbison/blob/master/UNISTD_ERROR.readme
+ if bin.name == 'win_flex' and state.environment.machines[kwargs['native']].is_windows():
+ lex_args.append('--wincompat')
+ lex_args.extend(['-o', '@OUTPUT0@'])
+ return LexGenerator(name, bin, T.cast('ImmutableListProtocol[str]', lex_args))
+
+ @noPosargs
+ @typed_kwargs(
+ 'codegen.yacc',
+ KwargInfo('yacc_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('byacc_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('bison_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo('win_bison_version', ContainerTypeInfo(list, str), default=[], listify=True),
+ KwargInfo(
+ 'implementations',
+ ContainerTypeInfo(list, str),
+ default=[],
+ listify=True,
+ validator=is_subset_validator({'yacc', 'byacc', 'bison', 'win_bison'})
+ ),
+ REQUIRED_KW,
+ DISABLER_KW,
+ NATIVE_KW,
+ )
+ @disablerIfNotFound
+ def yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) -> YaccGenerator:
+ disabled, required, feature = extract_required_kwarg(kwargs, state.subproject)
+ if disabled:
+ mlog.log('generator yacc skipped: feature', mlog.bold(feature), 'disabled')
+ return YaccGenerator('yacc', NonExistingExternalProgram('yacc'))
+ names: T.List[YaccImpls]
+ if kwargs['implementations']:
+ names = kwargs['implementations']
+ else:
+ assert state.environment.machines[kwargs['native']] is not None, 'for mypy'
+ if state.environment.machines[kwargs['native']].system == 'windows':
+ names = ['win_bison', 'bison', 'yacc']
+ else:
+ names = ['bison', 'byacc', 'yacc']
+
+ versions: T.Mapping[YaccImpls, T.List[str]] = {
+ 'yacc': kwargs['yacc_version'],
+ 'byacc': kwargs['byacc_version'],
+ 'bison': kwargs['bison_version'],
+ 'win_bison': kwargs['win_bison_version'],
+ }
+
+ for name in names:
+ bin = state.find_program(
+ name, wanted=versions[name], for_machine=kwargs['native'], required=False)
+ if bin.found():
+ break
+ else:
+ if required:
+ raise MesonException.from_node(
+ 'Could not find a yacc implementation. Tried: ', ", ".join(names),
+ node=state.current_node)
+ return YaccGenerator(name, bin)
+
+ yacc_args: T.List[str] = ['@INPUT@', '-o', '@OUTPUT0@']
+
+ impl = T.cast('YaccImpls', bin.name)
+ if impl == 'yacc' and isinstance(bin, ExternalProgram):
+ _, out, _ = Popen_safe(bin.get_command() + ['--version'])
+ if 'GNU Bison' in out:
+ impl = 'bison'
+ elif out.startswith('yacc - 2'):
+ impl = 'byacc'
+
+ if impl in {'bison', 'win_bison'}:
+ yacc_args.append('--defines=@OUTPUT1@')
+ if isinstance(bin, ExternalProgram) and version_compare(bin.get_version(), '>= 3.4'):
+ yacc_args.append('--color=always')
+ elif impl == 'byacc':
+ yacc_args.extend(['-H', '@OUTPUT1@'])
+ else:
+ mlog.warning('This yacc does not appear to be bison or byacc, the '
+ 'POSIX specification does not require that header '
+ 'output location be configurable, and may not work.',
+ fatal=False)
+ yacc_args.append('-H')
+ return YaccGenerator(name, bin, T.cast('ImmutableListProtocol[str]', yacc_args))
+
+
+def initialize(interpreter: Interpreter) -> CodeGenModule:
+ interpreter.append_holder_map(LexGenerator, LexHolder)
+ interpreter.append_holder_map(YaccGenerator, YaccHolder)
+ return CodeGenModule(interpreter)
diff --git a/mesonbuild/modules/cuda.py b/mesonbuild/modules/cuda.py
index eb73a57..df1af07 100644
--- a/mesonbuild/modules/cuda.py
+++ b/mesonbuild/modules/cuda.py
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
-# Copyright 2017 The Meson development team
+# Copyright 2017-2025 The Meson development team
from __future__ import annotations
+import dataclasses
import re
import typing as T
@@ -31,6 +32,78 @@ if T.TYPE_CHECKING:
DETECTED_KW: KwargInfo[T.Union[None, T.List[str]]] = KwargInfo('detected', (ContainerTypeInfo(list, str), NoneType), listify=True)
+
+@dataclasses.dataclass
+class _CudaVersion:
+
+ meson: str
+ windows: str
+ linux: str
+
+ def compare(self, version: str, machine: str) -> T.Optional[str]:
+ if version_compare(version, f'>={self.meson}'):
+ return self.windows if machine == 'windows' else self.linux
+ return None
+
+
+# Copied from: https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#id7
+_DRIVER_TABLE_VERSION: T.List[_CudaVersion] = [
+ _CudaVersion('13.0.2', 'unknown', '580.95.05'),
+ _CudaVersion('13.0.1', 'unknown', '580.82.07'),
+ _CudaVersion('13.0.0', 'unknown', '580.65.06'),
+ _CudaVersion('12.9.1', '576.57', '575.57.08'),
+ _CudaVersion('12.9.0', '576.02', '575.51.03'),
+ _CudaVersion('12.8.1', '572.61', '570.124.06'),
+ _CudaVersion('12.8.0', '570.65', '570.26'),
+ _CudaVersion('12.6.3', '561.17', '560.35.05'),
+ _CudaVersion('12.6.2', '560.94', '560.35.03'),
+ _CudaVersion('12.6.1', '560.94', '560.35.03'),
+ _CudaVersion('12.6.0', '560.76', '560.28.03'),
+ _CudaVersion('12.5.1', '555.85', '555.42.06'),
+ _CudaVersion('12.5.0', '555.85', '555.42.02'),
+ _CudaVersion('12.4.1', '551.78', '550.54.15'),
+ _CudaVersion('12.4.0', '551.61', '550.54.14'),
+ _CudaVersion('12.3.1', '546.12', '545.23.08'),
+ _CudaVersion('12.3.0', '545.84', '545.23.06'),
+ _CudaVersion('12.2.2', '537.13', '535.104.05'),
+ _CudaVersion('12.2.1', '536.67', '535.86.09'),
+ _CudaVersion('12.2.0', '536.25', '535.54.03'),
+ _CudaVersion('12.1.1', '531.14', '530.30.02'),
+ _CudaVersion('12.1.0', '531.14', '530.30.02'),
+ _CudaVersion('12.0.1', '528.33', '525.85.11'),
+ _CudaVersion('12.0.0', '527.41', '525.60.13'),
+ _CudaVersion('11.8.0', '522.06', '520.61.05'),
+ _CudaVersion('11.7.1', '516.31', '515.48.07'),
+ _CudaVersion('11.7.0', '516.01', '515.43.04'),
+ _CudaVersion('11.6.1', '511.65', '510.47.03'), # 11.6.2 is identical
+ _CudaVersion('11.6.0', '511.23', '510.39.01'),
+ _CudaVersion('11.5.1', '496.13', '495.29.05'), # 11.5.2 is identical
+ _CudaVersion('11.5.0', '496.04', '495.29.05'),
+ _CudaVersion('11.4.3', '472.50', '470.82.01'), # 11.4.4 is identical
+ _CudaVersion('11.4.1', '471.41', '470.57.02'), # 11.4.2 is identical
+ _CudaVersion('11.4.0', '471.11', '470.42.01'),
+ _CudaVersion('11.3.0', '465.89', '465.19.01'), # 11.3.1 is identical
+ _CudaVersion('11.2.2', '461.33', '460.32.03'),
+ _CudaVersion('11.2.1', '461.09', '460.32.03'),
+ _CudaVersion('11.2.0', '460.82', '460.27.03'),
+ _CudaVersion('11.1.1', '456.81', '455.32'),
+ _CudaVersion('11.1.0', '456.38', '455.23'),
+ _CudaVersion('11.0.3', '451.82', '450.51.06'), # 11.0.3.1 is identical
+ _CudaVersion('11.0.2', '451.48', '450.51.05'),
+ _CudaVersion('11.0.1', '451.22', '450.36.06'),
+ _CudaVersion('10.2.89', '441.22', '440.33'),
+ _CudaVersion('10.1.105', '418.96', '418.39'),
+ _CudaVersion('10.0.130', '411.31', '410.48'),
+ _CudaVersion('9.2.148', '398.26', '396.37'),
+ _CudaVersion('9.2.88', '397.44', '396.26'),
+ _CudaVersion('9.1.85', '391.29', '390.46'),
+ _CudaVersion('9.0.76', '385.54', '384.81'),
+ _CudaVersion('8.0.61', '376.51', '375.26'),
+ _CudaVersion('8.0.44', '369.30', '367.48'),
+ _CudaVersion('7.5.16', '353.66', '352.31'),
+ _CudaVersion('7.0.28', '347.62', '346.46'),
+]
+
class CudaModule(NewExtensionModule):
INFO = ModuleInfo('CUDA', '0.50.0', unstable=True)
@@ -51,52 +124,16 @@ class CudaModule(NewExtensionModule):
'a CUDA Toolkit version string. Beware that, since CUDA 11.0, ' +
'the CUDA Toolkit\'s components (including NVCC) are versioned ' +
'independently from each other (and the CUDA Toolkit as a whole).')
-
if len(args) != 1 or not isinstance(args[0], str):
raise argerror
cuda_version = args[0]
- driver_version_table = [
- {'cuda_version': '>=12.0.0', 'windows': '527.41', 'linux': '525.60.13'},
- {'cuda_version': '>=11.8.0', 'windows': '522.06', 'linux': '520.61.05'},
- {'cuda_version': '>=11.7.1', 'windows': '516.31', 'linux': '515.48.07'},
- {'cuda_version': '>=11.7.0', 'windows': '516.01', 'linux': '515.43.04'},
- {'cuda_version': '>=11.6.1', 'windows': '511.65', 'linux': '510.47.03'},
- {'cuda_version': '>=11.6.0', 'windows': '511.23', 'linux': '510.39.01'},
- {'cuda_version': '>=11.5.1', 'windows': '496.13', 'linux': '495.29.05'},
- {'cuda_version': '>=11.5.0', 'windows': '496.04', 'linux': '495.29.05'},
- {'cuda_version': '>=11.4.3', 'windows': '472.50', 'linux': '470.82.01'},
- {'cuda_version': '>=11.4.1', 'windows': '471.41', 'linux': '470.57.02'},
- {'cuda_version': '>=11.4.0', 'windows': '471.11', 'linux': '470.42.01'},
- {'cuda_version': '>=11.3.0', 'windows': '465.89', 'linux': '465.19.01'},
- {'cuda_version': '>=11.2.2', 'windows': '461.33', 'linux': '460.32.03'},
- {'cuda_version': '>=11.2.1', 'windows': '461.09', 'linux': '460.32.03'},
- {'cuda_version': '>=11.2.0', 'windows': '460.82', 'linux': '460.27.03'},
- {'cuda_version': '>=11.1.1', 'windows': '456.81', 'linux': '455.32'},
- {'cuda_version': '>=11.1.0', 'windows': '456.38', 'linux': '455.23'},
- {'cuda_version': '>=11.0.3', 'windows': '451.82', 'linux': '450.51.06'},
- {'cuda_version': '>=11.0.2', 'windows': '451.48', 'linux': '450.51.05'},
- {'cuda_version': '>=11.0.1', 'windows': '451.22', 'linux': '450.36.06'},
- {'cuda_version': '>=10.2.89', 'windows': '441.22', 'linux': '440.33'},
- {'cuda_version': '>=10.1.105', 'windows': '418.96', 'linux': '418.39'},
- {'cuda_version': '>=10.0.130', 'windows': '411.31', 'linux': '410.48'},
- {'cuda_version': '>=9.2.148', 'windows': '398.26', 'linux': '396.37'},
- {'cuda_version': '>=9.2.88', 'windows': '397.44', 'linux': '396.26'},
- {'cuda_version': '>=9.1.85', 'windows': '391.29', 'linux': '390.46'},
- {'cuda_version': '>=9.0.76', 'windows': '385.54', 'linux': '384.81'},
- {'cuda_version': '>=8.0.61', 'windows': '376.51', 'linux': '375.26'},
- {'cuda_version': '>=8.0.44', 'windows': '369.30', 'linux': '367.48'},
- {'cuda_version': '>=7.5.16', 'windows': '353.66', 'linux': '352.31'},
- {'cuda_version': '>=7.0.28', 'windows': '347.62', 'linux': '346.46'},
- ]
-
- driver_version = 'unknown'
- for d in driver_version_table:
- if version_compare(cuda_version, d['cuda_version']):
- driver_version = d.get(state.environment.machines.host.system, d['linux'])
- break
-
- return driver_version
+
+ for d in _DRIVER_TABLE_VERSION:
+ driver_version = d.compare(cuda_version, state.environment.machines.host.system)
+ if driver_version is not None:
+ return driver_version
+ return 'unknown'
@typed_pos_args('cuda.nvcc_arch_flags', (str, CudaCompiler), varargs=str)
@typed_kwargs('cuda.nvcc_arch_flags', DETECTED_KW)
@@ -178,42 +215,50 @@ class CudaModule(NewExtensionModule):
# except that a bug with cuda_arch_list="All" is worked around by
# tracking both lower and upper limits on GPU architectures.
- cuda_known_gpu_architectures = ['Fermi', 'Kepler', 'Maxwell'] # noqa: E221
+ cuda_known_gpu_architectures = [] # noqa: E221
cuda_common_gpu_architectures = ['3.0', '3.5', '5.0'] # noqa: E221
cuda_hi_limit_gpu_architecture = None # noqa: E221
cuda_lo_limit_gpu_architecture = '2.0' # noqa: E221
cuda_all_gpu_architectures = ['3.0', '3.2', '3.5', '5.0'] # noqa: E221
- if version_compare(cuda_version, '<7.0'):
- cuda_hi_limit_gpu_architecture = '5.2'
+ # Fermi and Kepler support have been dropped since 12.0
+ if version_compare(cuda_version, '<12.0'):
+ cuda_known_gpu_architectures.extend(['Fermi', 'Kepler'])
- if version_compare(cuda_version, '>=7.0'):
- cuda_known_gpu_architectures += ['Kepler+Tegra', 'Kepler+Tesla', 'Maxwell+Tegra'] # noqa: E221
- cuda_common_gpu_architectures += ['5.2'] # noqa: E221
+ # Everything older than Turing is dropped by 13.0
+ if version_compare(cuda_version, '<13.0'):
+ cuda_known_gpu_architectures.append('Maxwell')
- if version_compare(cuda_version, '<8.0'):
- cuda_common_gpu_architectures += ['5.2+PTX'] # noqa: E221
- cuda_hi_limit_gpu_architecture = '6.0' # noqa: E221
+ if version_compare(cuda_version, '<7.0'):
+ cuda_hi_limit_gpu_architecture = '5.2'
- if version_compare(cuda_version, '>=8.0'):
- cuda_known_gpu_architectures += ['Pascal', 'Pascal+Tegra'] # noqa: E221
- cuda_common_gpu_architectures += ['6.0', '6.1'] # noqa: E221
- cuda_all_gpu_architectures += ['6.0', '6.1', '6.2'] # noqa: E221
+ if version_compare(cuda_version, '>=7.0'):
+ cuda_known_gpu_architectures += ['Kepler+Tegra', 'Kepler+Tesla', 'Maxwell+Tegra'] # noqa: E221
+ cuda_common_gpu_architectures += ['5.2'] # noqa: E221
- if version_compare(cuda_version, '<9.0'):
- cuda_common_gpu_architectures += ['6.1+PTX'] # noqa: E221
- cuda_hi_limit_gpu_architecture = '7.0' # noqa: E221
+ if version_compare(cuda_version, '<8.0'):
+ cuda_common_gpu_architectures += ['5.2+PTX'] # noqa: E221
+ cuda_hi_limit_gpu_architecture = '6.0' # noqa: E221
- if version_compare(cuda_version, '>=9.0'):
- cuda_known_gpu_architectures += ['Volta', 'Xavier'] # noqa: E221
- cuda_common_gpu_architectures += ['7.0'] # noqa: E221
- cuda_all_gpu_architectures += ['7.0', '7.2'] # noqa: E221
- # https://docs.nvidia.com/cuda/archive/9.0/cuda-toolkit-release-notes/index.html#unsupported-features
- cuda_lo_limit_gpu_architecture = '3.0' # noqa: E221
+ if version_compare(cuda_version, '>=8.0'):
+ cuda_known_gpu_architectures += ['Pascal', 'Pascal+Tegra'] # noqa: E221
+ cuda_common_gpu_architectures += ['6.0', '6.1'] # noqa: E221
+ cuda_all_gpu_architectures += ['6.0', '6.1', '6.2'] # noqa: E221
- if version_compare(cuda_version, '<10.0'):
- cuda_common_gpu_architectures += ['7.2+PTX'] # noqa: E221
- cuda_hi_limit_gpu_architecture = '8.0' # noqa: E221
+ if version_compare(cuda_version, '<9.0'):
+ cuda_common_gpu_architectures += ['6.1+PTX'] # noqa: E221
+ cuda_hi_limit_gpu_architecture = '7.0' # noqa: E221
+
+ if version_compare(cuda_version, '>=9.0'):
+ cuda_known_gpu_architectures += ['Volta', 'Xavier'] # noqa: E221
+ cuda_common_gpu_architectures += ['7.0'] # noqa: E221
+ cuda_all_gpu_architectures += ['7.0', '7.2'] # noqa: E221
+ # https://docs.nvidia.com/cuda/archive/9.0/cuda-toolkit-release-notes/index.html#unsupported-features
+ cuda_lo_limit_gpu_architecture = '3.0' # noqa: E221
+
+ if version_compare(cuda_version, '<10.0'):
+ cuda_common_gpu_architectures += ['7.2+PTX'] # noqa: E221
+ cuda_hi_limit_gpu_architecture = '8.0' # noqa: E221
if version_compare(cuda_version, '>=10.0'):
cuda_known_gpu_architectures += ['Turing'] # noqa: E221
@@ -266,6 +311,29 @@ class CudaModule(NewExtensionModule):
if version_compare(cuda_version, '<13'):
cuda_hi_limit_gpu_architecture = '10.0' # noqa: E221
+ if version_compare(cuda_version, '>=12.8'):
+ cuda_known_gpu_architectures.append('Blackwell')
+ cuda_common_gpu_architectures.extend(['10.0', '12.0'])
+ cuda_all_gpu_architectures.extend(['10.0', '12.0'])
+
+ if version_compare(cuda_version, '<13'):
+ # Yes, 12.8 and 12.9 support 10.1, but 13.0 doesn't
+ cuda_common_gpu_architectures.append('10.1')
+ cuda_all_gpu_architectures.append('10.1')
+ cuda_hi_limit_gpu_architecture = '12.1'
+
+ if version_compare(cuda_version, '>=12.9'):
+ cuda_common_gpu_architectures.extend(['10.3', '12.1'])
+ cuda_all_gpu_architectures.extend(['10.3', '12.1'])
+
+ if version_compare(cuda_version, '>=13.0'):
+ cuda_common_gpu_architectures.append('11.0')
+ cuda_all_gpu_architectures.append('11.0')
+ cuda_lo_limit_gpu_architecture = '7.5'
+
+ if version_compare(cuda_version, '<14'):
+ cuda_hi_limit_gpu_architecture = '12.1'
+
if not cuda_arch_list:
cuda_arch_list = 'Auto'
@@ -318,6 +386,7 @@ class CudaModule(NewExtensionModule):
'Orin': (['8.7'], []),
'Lovelace': (['8.9'], ['8.9']),
'Hopper': (['9.0'], ['9.0']),
+ 'Blackwell': (['10.0'], ['10.0']),
}.get(arch_name, (None, None))
if arch_bin is None:
diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py
index 966b070..35ce86b 100644
--- a/mesonbuild/modules/dlang.py
+++ b/mesonbuild/modules/dlang.py
@@ -7,27 +7,45 @@ from __future__ import annotations
import json
import os
+import typing as T
+
from . import ExtensionModule, ModuleInfo
from .. import mlog
+from ..build import InvalidArguments
from ..dependencies import Dependency
from ..dependencies.dub import DubDependency
from ..interpreterbase import typed_pos_args
from ..mesonlib import Popen_safe, MesonException, listify
+if T.TYPE_CHECKING:
+ from typing_extensions import Literal, TypeAlias
+
+ from . import ModuleState
+ from ..build import OverrideExecutable
+ from ..interpreter.interpreter import Interpreter
+ from ..interpreterbase.baseobjects import TYPE_kwargs
+ from ..programs import ExternalProgram, OverrideProgram
+
+ _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram]
+ _JSONTypes: TypeAlias = T.Union[str, int, bool, None, T.List['_JSONTypes'], T.Dict[str, '_JSONTypes']]
+
+
class DlangModule(ExtensionModule):
- class_dubbin = None
+ class_dubbin: T.Union[_AnyProgram, Literal[False], None] = None
init_dub = False
+ dubbin: T.Union[_AnyProgram, Literal[False], None]
+
INFO = ModuleInfo('dlang', '0.48.0')
- def __init__(self, interpreter):
+ def __init__(self, interpreter: Interpreter):
super().__init__(interpreter)
self.methods.update({
'generate_dub_file': self.generate_dub_file,
})
- def _init_dub(self, state):
+ def _init_dub(self, state: ModuleState) -> None:
if DlangModule.class_dubbin is None and DubDependency.class_dubbin is not None:
self.dubbin = DubDependency.class_dubbin[0]
DlangModule.class_dubbin = self.dubbin
@@ -45,11 +63,11 @@ class DlangModule(ExtensionModule):
raise MesonException('DUB not found.')
@typed_pos_args('dlang.generate_dub_file', str, str)
- def generate_dub_file(self, state, args, kwargs):
+ def generate_dub_file(self, state: ModuleState, args: T.Tuple[str, str], kwargs: TYPE_kwargs) -> None:
if not DlangModule.init_dub:
self._init_dub(state)
- config = {
+ config: T.Dict[str, _JSONTypes] = {
'name': args[0]
}
@@ -70,7 +88,7 @@ class DlangModule(ExtensionModule):
for key, value in kwargs.items():
if key == 'dependencies':
values = listify(value, flatten=False)
- config[key] = {}
+ data: T.Dict[str, _JSONTypes] = {}
for dep in values:
if isinstance(dep, Dependency):
name = dep.get_name()
@@ -78,20 +96,33 @@ class DlangModule(ExtensionModule):
if ret == 0:
version = dep.get_version()
if version is None:
- config[key][name] = ''
+ data[name] = ''
else:
- config[key][name] = version
+ data[name] = version
+ config[key] = data
else:
- config[key] = value
+ def _do_validate(v: object) -> _JSONTypes:
+ if not isinstance(v, (str, int, bool, list, dict)):
+ raise InvalidArguments('keyword arguments must be strings, numbers, booleans, arrays, or dictionaries of such')
+ if isinstance(v, list):
+ for e in v:
+ _do_validate(e)
+ if isinstance(v, dict):
+ for e in v.values():
+ _do_validate(e)
+ return T.cast('_JSONTypes', v)
+
+ config[key] = _do_validate(value)
with open(config_path, 'w', encoding='utf-8') as ofile:
ofile.write(json.dumps(config, indent=4, ensure_ascii=False))
- def _call_dubbin(self, args, env=None):
+ def _call_dubbin(self, args: T.List[str], env: T.Optional[T.Mapping[str, str]] = None) -> T.Tuple[int, str]:
+ assert self.dubbin is not None and self.dubbin is not False, 'for mypy'
p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2]
return p.returncode, out.strip()
- def check_dub(self, state):
+ def check_dub(self, state: ModuleState) -> T.Union[_AnyProgram, Literal[False]]:
dubbin = state.find_program('dub', silent=True)
if dubbin.found():
try:
@@ -101,17 +132,14 @@ class DlangModule(ExtensionModule):
''.format(' '.join(dubbin.get_command())))
# Set to False instead of None to signify that we've already
# searched for it and not found it
- dubbin = False
+ else:
+ mlog.log('Found DUB:', mlog.green('YES'), ':', mlog.bold(dubbin.get_path() or ''),
+ '({})'.format(out.strip()))
+ return dubbin
except (FileNotFoundError, PermissionError):
- dubbin = False
- else:
- dubbin = False
- if dubbin:
- mlog.log('Found DUB:', mlog.bold(dubbin.get_path()),
- '(%s)' % out.strip())
- else:
- mlog.log('Found DUB:', mlog.red('NO'))
- return dubbin
+ pass
+ mlog.log('Found DUB:', mlog.red('NO'))
+ return False
-def initialize(*args, **kwargs):
- return DlangModule(*args, **kwargs)
+def initialize(interp: Interpreter) -> DlangModule:
+ return DlangModule(interp)
diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py
index 339d000..0881df9 100644
--- a/mesonbuild/modules/external_project.py
+++ b/mesonbuild/modules/external_project.py
@@ -169,7 +169,7 @@ class ExternalProject(NewExtensionModule):
def _quote_and_join(self, array: T.List[str]) -> str:
return ' '.join([shlex.quote(i) for i in array])
- def _validate_configure_options(self, variables: T.List[T.Tuple[str, str, str]], state: 'ModuleState') -> None:
+ def _validate_configure_options(self, variables: T.Sequence[T.Tuple[str, T.Optional[str], str]], state: 'ModuleState') -> None:
# Ensure the user at least try to pass basic info to the build system,
# like the prefix, libdir, etc.
for key, default, val in variables:
@@ -183,7 +183,7 @@ class ExternalProject(NewExtensionModule):
FeatureNew('Default configure_option', '0.57.0').use(self.subproject, state.current_node)
self.configure_options.append(default)
- def _format_options(self, options: T.List[str], variables: T.List[T.Tuple[str, str, str]]) -> T.List[str]:
+ def _format_options(self, options: T.List[str], variables: T.Sequence[T.Tuple[str, T.Optional[str], str]]) -> T.List[str]:
out: T.List[str] = []
missing = set()
regex = get_variable_regex('meson')
@@ -201,10 +201,10 @@ class ExternalProject(NewExtensionModule):
def _run(self, step: str, command: T.List[str], workdir: Path) -> None:
mlog.log(f'External project {self.name}:', mlog.bold(step))
m = 'Running command ' + str(command) + ' in directory ' + str(workdir) + '\n'
- log_filename = Path(mlog.get_log_dir(), f'{self.name}-{step}.log')
+ logfile = Path(mlog.get_log_dir(), f'{self.name}-{step}.log')
output = None
if not self.verbose:
- output = open(log_filename, 'w', encoding='utf-8')
+ output = open(logfile, 'w', encoding='utf-8')
output.write(m + '\n')
output.flush()
else:
@@ -215,7 +215,10 @@ class ExternalProject(NewExtensionModule):
if p.returncode != 0:
m = f'{step} step returned error code {p.returncode}.'
if not self.verbose:
- m += '\nSee logs: ' + str(log_filename)
+ m += '\nSee logs: ' + str(logfile)
+ contents = mlog.ci_fold_file(logfile, f'CI platform detected, click here for {os.path.basename(logfile)} contents.')
+ if contents:
+ print(contents)
raise MesonException(m)
def _create_targets(self, extra_depends: T.List[T.Union['BuildTarget', 'CustomTarget']]) -> T.List['TYPE_var']:
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index 1fa368e..57a6b6d 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -2,7 +2,9 @@
# Copyright 2019 The Meson development team
from __future__ import annotations
-from pathlib import Path, PurePath, PureWindowsPath
+from ntpath import sep as ntsep
+from pathlib import Path
+from posixpath import sep as posixsep
import hashlib
import os
import typing as T
@@ -12,7 +14,7 @@ from .. import mlog
from ..build import BuildTarget, CustomTarget, CustomTargetIndex, InvalidArguments
from ..interpreter.type_checking import INSTALL_KW, INSTALL_MODE_KW, INSTALL_TAG_KW, NoneType
from ..interpreterbase import FeatureNew, KwargInfo, typed_kwargs, typed_pos_args, noKwargs
-from ..mesonlib import File, MesonException, has_path_sep, path_is_in_root, relpath
+from ..mesonlib import File, MesonException, has_path_sep, is_windows, path_is_in_root, relpath
if T.TYPE_CHECKING:
from . import ModuleState
@@ -42,7 +44,7 @@ class FSModule(ExtensionModule):
INFO = ModuleInfo('fs', '0.53.0')
- def __init__(self, interpreter: 'Interpreter') -> None:
+ def __init__(self, interpreter: Interpreter) -> None:
super().__init__(interpreter)
self.methods.update({
'as_posix': self.as_posix,
@@ -62,29 +64,30 @@ class FSModule(ExtensionModule):
'replace_suffix': self.replace_suffix,
'size': self.size,
'stem': self.stem,
+ 'suffix': self.suffix,
})
- def _absolute_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
+ def _absolute_dir(self, state: ModuleState, arg: FileOrString) -> str:
"""
make an absolute path from a relative path, WITHOUT resolving symlinks
"""
if isinstance(arg, File):
- return Path(arg.absolute_path(state.source_root, state.environment.get_build_dir()))
- return Path(state.source_root) / Path(state.subdir) / Path(arg).expanduser()
+ return arg.absolute_path(state.source_root, state.environment.get_build_dir())
+ return os.path.join(state.source_root, state.subdir, os.path.expanduser(arg))
@staticmethod
- def _obj_to_path(feature_new_prefix: str, obj: T.Union[FileOrString, BuildTargetTypes], state: ModuleState) -> PurePath:
+ def _obj_to_pathstr(feature_new_prefix: str, obj: T.Union[FileOrString, BuildTargetTypes], state: ModuleState) -> str:
if isinstance(obj, str):
- return PurePath(obj)
+ return obj
if isinstance(obj, File):
FeatureNew(f'{feature_new_prefix} with file', '0.59.0').use(state.subproject, location=state.current_node)
- return PurePath(str(obj))
+ return str(obj)
FeatureNew(f'{feature_new_prefix} with build_tgt, custom_tgt, and custom_idx', '1.4.0').use(state.subproject, location=state.current_node)
- return PurePath(state.backend.get_target_filename(obj))
+ return state.backend.get_target_filename(obj)
- def _resolve_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
+ def _resolve_dir(self, state: ModuleState, arg: FileOrString) -> str:
"""
resolves symlinks and makes absolute a directory relative to calling meson.build,
if not already absolute
@@ -92,7 +95,7 @@ class FSModule(ExtensionModule):
path = self._absolute_dir(state, arg)
try:
# accommodate unresolvable paths e.g. symlink loops
- path = path.resolve()
+ path = os.path.realpath(path)
except Exception:
# return the best we could do
pass
@@ -101,123 +104,139 @@ class FSModule(ExtensionModule):
@noKwargs
@FeatureNew('fs.expanduser', '0.54.0')
@typed_pos_args('fs.expanduser', str)
- def expanduser(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
- return str(Path(args[0]).expanduser())
+ def expanduser(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
+ return os.path.expanduser(args[0])
@noKwargs
@FeatureNew('fs.is_absolute', '0.54.0')
@typed_pos_args('fs.is_absolute', (str, File))
- def is_absolute(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
- if isinstance(args[0], File):
+ def is_absolute(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
+ path = args[0]
+ if isinstance(path, File):
FeatureNew('fs.is_absolute with file', '0.59.0').use(state.subproject, location=state.current_node)
- return PurePath(str(args[0])).is_absolute()
+ path = str(path)
+ if is_windows():
+ # os.path.isabs was broken for Windows before Python 3.13, so we implement it ourselves
+ path = path[:3].replace(posixsep, ntsep)
+ return path.startswith(ntsep * 2) or path.startswith(':' + ntsep, 1)
+ return path.startswith(posixsep)
@noKwargs
@FeatureNew('fs.as_posix', '0.54.0')
@typed_pos_args('fs.as_posix', str)
- def as_posix(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
+ def as_posix(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
r"""
this function assumes you are passing a Windows path, even if on a Unix-like system
and so ALL '\' are turned to '/', even if you meant to escape a character
"""
- return PureWindowsPath(args[0]).as_posix()
+ return args[0].replace(ntsep, posixsep)
@noKwargs
@typed_pos_args('fs.exists', str)
- def exists(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).exists()
+ def exists(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.exists(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_symlink', (str, File))
- def is_symlink(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
+ def is_symlink(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
if isinstance(args[0], File):
FeatureNew('fs.is_symlink with file', '0.59.0').use(state.subproject, location=state.current_node)
- return self._absolute_dir(state, args[0]).is_symlink()
+ return os.path.islink(self._absolute_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_file', str)
- def is_file(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).is_file()
+ def is_file(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.isfile(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_dir', str)
- def is_dir(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).is_dir()
+ def is_dir(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.isdir(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.hash', (str, File), str)
- def hash(self, state: 'ModuleState', args: T.Tuple['FileOrString', str], kwargs: T.Dict[str, T.Any]) -> str:
+ def hash(self, state: ModuleState, args: T.Tuple[FileOrString, str], kwargs: T.Dict[str, T.Any]) -> str:
if isinstance(args[0], File):
FeatureNew('fs.hash with file', '0.59.0').use(state.subproject, location=state.current_node)
file = self._resolve_dir(state, args[0])
- if not file.is_file():
+ if not os.path.isfile(file):
raise MesonException(f'{file} is not a file and therefore cannot be hashed')
try:
h = hashlib.new(args[1])
except ValueError:
raise MesonException('hash algorithm {} is not available'.format(args[1]))
- mlog.debug('computing {} sum of {} size {} bytes'.format(args[1], file, file.stat().st_size))
- h.update(file.read_bytes())
+ mlog.debug('computing {} sum of {} size {} bytes'.format(args[1], file, os.stat(file).st_size))
+ with open(file, mode='rb', buffering=0) as f:
+ h.update(f.read())
return h.hexdigest()
@noKwargs
@typed_pos_args('fs.size', (str, File))
- def size(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> int:
+ def size(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> int:
if isinstance(args[0], File):
FeatureNew('fs.size with file', '0.59.0').use(state.subproject, location=state.current_node)
file = self._resolve_dir(state, args[0])
- if not file.is_file():
+ if not os.path.isfile(file):
raise MesonException(f'{file} is not a file and therefore cannot be sized')
try:
- return file.stat().st_size
+ return os.stat(file).st_size
except ValueError:
raise MesonException('{} size could not be determined'.format(args[0]))
@noKwargs
@typed_pos_args('fs.is_samepath', (str, File), (str, File))
- def is_samepath(self, state: 'ModuleState', args: T.Tuple['FileOrString', 'FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
+ def is_samepath(self, state: ModuleState, args: T.Tuple[FileOrString, FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
if isinstance(args[0], File) or isinstance(args[1], File):
FeatureNew('fs.is_samepath with file', '0.59.0').use(state.subproject, location=state.current_node)
file1 = self._resolve_dir(state, args[0])
file2 = self._resolve_dir(state, args[1])
- if not file1.exists():
+ if not os.path.exists(file1):
return False
- if not file2.exists():
+ if not os.path.exists(file2):
return False
try:
- return file1.samefile(file2)
+ return os.path.samefile(file1, file2)
except OSError:
return False
@noKwargs
@typed_pos_args('fs.replace_suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget), str)
- def replace_suffix(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], str], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.replace_suffix', args[0], state)
- return str(path.with_suffix(args[1]))
+ def replace_suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], str], kwargs: T.Dict[str, T.Any]) -> str:
+ if args[1] and not args[1].startswith('.'):
+ raise ValueError(f"Invalid suffix {args[1]!r}")
+ path = self._obj_to_pathstr('fs.replace_suffix', args[0], state)
+ return os.path.splitext(path)[0] + args[1]
@noKwargs
@typed_pos_args('fs.parent', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
- def parent(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.parent', args[0], state)
- return str(path.parent)
+ def parent(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.parent', args[0], state)
+ return os.path.split(path)[0] or '.'
@noKwargs
@typed_pos_args('fs.name', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
- def name(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.name', args[0], state)
- return str(path.name)
+ def name(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.name', args[0], state)
+ return os.path.basename(path)
@noKwargs
@typed_pos_args('fs.stem', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
@FeatureNew('fs.stem', '0.54.0')
- def stem(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.stem', args[0], state)
- return str(path.stem)
+ def stem(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.name', args[0], state)
+ return os.path.splitext(os.path.basename(path))[0]
+
+ @noKwargs
+ @typed_pos_args('fs.suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
+ @FeatureNew('fs.suffix', '1.9.0')
+ def suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.suffix', args[0], state)
+ return os.path.splitext(path)[1]
@FeatureNew('fs.read', '0.57.0')
@typed_pos_args('fs.read', (str, File))
@typed_kwargs('fs.read', KwargInfo('encoding', str, default='utf-8'))
- def read(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: 'ReadKwArgs') -> str:
+ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwArgs) -> str:
"""Read a file from the source tree and return its value as a decoded
string.
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 6764133..0f522c1 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2015-2016 The Meson development team
-# Copyright © 2023-2024 Intel Corporation
+# Copyright © 2023-2025 Intel Corporation
'''This module provides helper functions for Gnome/GLib related
functionality such as gobject-introspection, gresources and gtk-doc'''
@@ -37,7 +37,7 @@ from ..programs import OverrideProgram
from ..scripts.gettext import read_linguas
if T.TYPE_CHECKING:
- from typing_extensions import Literal, TypedDict
+ from typing_extensions import Literal, TypeAlias, TypedDict
from . import ModuleState
from ..build import BuildTarget
@@ -137,6 +137,8 @@ if T.TYPE_CHECKING:
install_header: bool
install_dir: T.Optional[str]
docbook: T.Optional[str]
+ rst: T.Optional[str]
+ markdown: T.Optional[str]
autocleanup: Literal['all', 'none', 'objects', 'default']
class GenMarshal(TypedDict):
@@ -196,7 +198,7 @@ if T.TYPE_CHECKING:
vtail: T.Optional[str]
depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]]
- ToolType = T.Union[Executable, ExternalProgram, OverrideProgram]
+ ToolType: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram]
# Differs from the CustomTarget version in that it straight defaults to True
@@ -254,8 +256,8 @@ class GnomeModule(ExtensionModule):
def __init__(self, interpreter: 'Interpreter') -> None:
super().__init__(interpreter)
self.gir_dep: T.Optional[Dependency] = None
- self.giscanner: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None
- self.gicompiler: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None
+ self.giscanner: T.Optional[ToolType] = None
+ self.gicompiler: T.Optional[ToolType] = None
self.install_glib_compile_schemas = False
self.install_gio_querymodules: T.List[str] = []
self.install_gtk_update_icon_cache = False
@@ -281,7 +283,7 @@ class GnomeModule(ExtensionModule):
def _get_native_glib_version(self, state: 'ModuleState') -> str:
if self.native_glib_version is None:
glib_dep = PkgConfigDependency('glib-2.0', state.environment,
- {'native': True, 'required': False})
+ {'native': MachineChoice.BUILD, 'required': False})
if glib_dep.found():
self.native_glib_version = glib_dep.get_version()
else:
@@ -634,7 +636,7 @@ class GnomeModule(ExtensionModule):
# https://github.com/mesonbuild/meson/issues/1911
# However, g-ir-scanner does not understand -Wl,-rpath
# so we need to use -L instead
- for d in state.backend.determine_rpath_dirs(lib):
+ for d in lib.determine_rpath_dirs():
d = os.path.join(state.environment.get_build_dir(), d)
link_command.append('-L' + d)
if include_rpath:
@@ -646,7 +648,7 @@ class GnomeModule(ExtensionModule):
return link_command, new_depends
def _get_dependencies_flags_raw(
- self, deps: T.Sequence[T.Union['Dependency', build.BuildTarget, CustomTarget, CustomTargetIndex]],
+ self, deps: T.Sequence[T.Union['Dependency', build.BuildTargetTypes]],
state: 'ModuleState',
depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]],
include_rpath: bool,
@@ -739,7 +741,7 @@ class GnomeModule(ExtensionModule):
return cflags, internal_ldflags, external_ldflags, gi_includes, depends
def _get_dependencies_flags(
- self, deps: T.Sequence[T.Union['Dependency', build.BuildTarget, CustomTarget, CustomTargetIndex]],
+ self, deps: T.Sequence[T.Union['Dependency', build.BuildTargetTypes]],
state: 'ModuleState',
depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]],
include_rpath: bool = False,
@@ -773,9 +775,7 @@ class GnomeModule(ExtensionModule):
STATIC_BUILD_REQUIRED_VERSION = ">=1.58.1"
if isinstance(girtarget, (build.StaticLibrary)) and \
- not mesonlib.version_compare(
- self._get_gir_dep(state)[0].get_version(),
- STATIC_BUILD_REQUIRED_VERSION):
+ not self._giscanner_version_compare(state, STATIC_BUILD_REQUIRED_VERSION):
raise MesonException('Static libraries can only be introspected with GObject-Introspection ' + STATIC_BUILD_REQUIRED_VERSION)
return girtarget
@@ -797,10 +797,20 @@ class GnomeModule(ExtensionModule):
self.gicompiler = self._find_tool(state, 'g-ir-compiler')
return self.gir_dep, self.giscanner, self.gicompiler
+ def _giscanner_version_compare(self, state: 'ModuleState', cmp: str) -> bool:
+ # Support for --version was introduced in g-i 1.58, but Ubuntu
+ # Bionic shipped 1.56.1. As all our version checks are greater
+ # than 1.58, we can just return False if get_version fails.
+ try:
+ giscanner, _, _ = self._get_gir_dep(state)
+ return mesonlib.version_compare(giscanner.get_version(), cmp)
+ except MesonException:
+ return False
+
@functools.lru_cache(maxsize=None)
def _gir_has_option(self, option: str) -> bool:
exe = self.giscanner
- if isinstance(exe, OverrideProgram):
+ if isinstance(exe, (Executable, OverrideProgram)):
# Handle overridden g-ir-scanner
assert option in {'--extra-library', '--sources-top-dirs'}
return True
@@ -865,7 +875,7 @@ class GnomeModule(ExtensionModule):
# https://github.com/mesonbuild/meson/issues/1911
# However, g-ir-scanner does not understand -Wl,-rpath
# so we need to use -L instead
- for d in state.backend.determine_rpath_dirs(girtarget):
+ for d in girtarget.determine_rpath_dirs():
d = os.path.join(state.environment.get_build_dir(), d)
ret.append('-L' + d)
@@ -885,8 +895,8 @@ class GnomeModule(ExtensionModule):
@staticmethod
def _get_gir_targets_deps(girtargets: T.Sequence[build.BuildTarget]
- ) -> T.List[T.Union[build.BuildTarget, CustomTarget, CustomTargetIndex, Dependency]]:
- ret: T.List[T.Union[build.BuildTarget, CustomTarget, CustomTargetIndex, Dependency]] = []
+ ) -> T.List[T.Union[build.BuildTargetTypes, Dependency]]:
+ ret: T.List[T.Union[build.BuildTargetTypes, Dependency]] = []
for girtarget in girtargets:
ret += girtarget.get_all_link_deps()
ret += girtarget.get_external_deps()
@@ -912,9 +922,9 @@ class GnomeModule(ExtensionModule):
if state.project_args.get(lang):
cflags += state.project_args[lang]
if OptionKey('b_sanitize') in compiler.base_options:
- sanitize = state.environment.coredata.optstore.get_value('b_sanitize')
+ sanitize = state.environment.coredata.optstore.get_value_for('b_sanitize')
assert isinstance(sanitize, list)
- cflags += compiler.sanitizer_compile_args(sanitize)
+ cflags += compiler.sanitizer_compile_args(None, sanitize)
# These must be first in ldflags
if 'address' in sanitize:
internal_ldflags += ['-lasan']
@@ -924,7 +934,7 @@ class GnomeModule(ExtensionModule):
internal_ldflags += ['-lubsan']
# FIXME: Linking directly to lib*san is not recommended but g-ir-scanner
# does not understand -f LDFLAGS. https://bugzilla.gnome.org/show_bug.cgi?id=783892
- # ldflags += compiler.sanitizer_link_args(sanitize)
+ # ldflags += compiler.sanitizer_link_args(None, state.environment, sanitize)
return cflags, internal_ldflags, external_ldflags
@@ -957,12 +967,12 @@ class GnomeModule(ExtensionModule):
return gir_filelist_filename
- @staticmethod
def _make_gir_target(
+ self,
state: 'ModuleState',
girfile: str,
scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]],
- generated_files: T.Sequence[T.Union[str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList]],
+ generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]],
depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]],
env_flags: T.Sequence[str],
kwargs: T.Dict[str, T.Any]) -> GirTarget:
@@ -988,6 +998,9 @@ class GnomeModule(ExtensionModule):
run_env.set('CFLAGS', [quote_arg(x) for x in env_flags], ' ')
run_env.merge(kwargs['env'])
+ # response file supported?
+ rspable = self._giscanner_version_compare(state, '>= 1.85.0')
+
return GirTarget(
girfile,
state.subdir,
@@ -1002,12 +1015,13 @@ class GnomeModule(ExtensionModule):
install_dir=[install_dir],
install_tag=['devel'],
env=run_env,
+ rspable=rspable,
)
@staticmethod
def _make_typelib_target(state: 'ModuleState', typelib_output: str,
typelib_cmd: T.Sequence[T.Union[str, Executable, ExternalProgram, CustomTarget]],
- generated_files: T.Sequence[T.Union[str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList]],
+ generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]],
kwargs: T.Dict[str, T.Any]) -> TypelibTarget:
install = kwargs['install_typelib']
if install is None:
@@ -1037,7 +1051,7 @@ class GnomeModule(ExtensionModule):
@staticmethod
def _gather_typelib_includes_and_update_depends(
state: 'ModuleState',
- deps: T.Sequence[T.Union[Dependency, build.BuildTarget, CustomTarget, CustomTargetIndex]],
+ deps: T.Sequence[T.Union[Dependency, build.BuildTargetTypes]],
depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]
) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]:
# Need to recursively add deps on GirTarget sources from our
@@ -1168,13 +1182,13 @@ class GnomeModule(ExtensionModule):
scan_cflags += list(self._get_scanner_cflags(self._get_external_args_for_langs(state, [lc[0] for lc in langs_compilers])))
scan_internal_ldflags = []
scan_external_ldflags = []
- scan_env_ldflags = []
+ scan_env_ldflags = state.environment.coredata.get_external_link_args(MachineChoice.HOST, 'c')
for cli_flags, env_flags in (self._get_scanner_ldflags(internal_ldflags), self._get_scanner_ldflags(dep_internal_ldflags)):
scan_internal_ldflags += cli_flags
- scan_env_ldflags = env_flags
+ scan_env_ldflags += env_flags
for cli_flags, env_flags in (self._get_scanner_ldflags(external_ldflags), self._get_scanner_ldflags(dep_external_ldflags)):
scan_external_ldflags += cli_flags
- scan_env_ldflags = env_flags
+ scan_env_ldflags += env_flags
girtargets_inc_dirs = self._get_gir_targets_inc_dirs(girtargets)
inc_dirs = kwargs['include_directories']
@@ -1619,6 +1633,8 @@ class GnomeModule(ExtensionModule):
),
KwargInfo('install_header', bool, default=False, since='0.46.0'),
KwargInfo('docbook', (str, NoneType)),
+ KwargInfo('rst', (str, NoneType), since='1.9.0'),
+ KwargInfo('markdown', (str, NoneType), since='1.9.0'),
KwargInfo(
'autocleanup', str, default='default', since='0.47.0',
validator=in_set_validator({'all', 'none', 'objects'})),
@@ -1675,6 +1691,26 @@ class GnomeModule(ExtensionModule):
cmd += ['--generate-docbook', docbook]
+ if kwargs['rst'] is not None:
+ if not mesonlib.version_compare(glib_version, '>= 2.71.1'):
+ mlog.error(f'Glib version ({glib_version}) is too old to '
+ 'support the \'rst\' kwarg, need 2.71.1 or '
+ 'newer')
+
+ rst = kwargs['rst']
+
+ cmd += ['--generate-rst', rst]
+
+ if kwargs['markdown'] is not None:
+ if not mesonlib.version_compare(glib_version, '>= 2.75.2'):
+ mlog.error(f'Glib version ({glib_version}) is too old to '
+ 'support the \'markdown\' kwarg, need 2.75.2 '
+ 'or newer')
+
+ markdown = kwargs['markdown']
+
+ cmd += ['--generate-md', markdown]
+
# https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a
if mesonlib.version_compare(glib_version, '>= 2.51.3'):
cmd += ['--output-directory', '@OUTDIR@', '--generate-c-code', namebase, '@INPUT@']
@@ -1750,6 +1786,48 @@ class GnomeModule(ExtensionModule):
)
targets.append(docbook_custom_target)
+ if kwargs['rst'] is not None:
+ rst = kwargs['rst']
+ # The rst output is always ${rst}-${name_of_xml_file}
+ output = namebase + '-rst'
+ outputs = []
+ for f in xml_files:
+ outputs.append('{}-{}'.format(rst, os.path.basename(str(f))))
+
+ rst_custom_target = CustomTarget(
+ output,
+ state.subdir,
+ state.subproject,
+ state.environment,
+ cmd + ['--output-directory', '@OUTDIR@', '--generate-rst', rst, '@INPUT@'],
+ xml_files,
+ outputs,
+ build_by_default=build_by_default,
+ description='Generating gdbus reStructuredText {}',
+ )
+ targets.append(rst_custom_target)
+
+ if kwargs['markdown'] is not None:
+ markdown = kwargs['markdown']
+ # The markdown output is always ${markdown}-${name_of_xml_file}
+ output = namebase + '-markdown'
+ outputs = []
+ for f in xml_files:
+ outputs.append('{}-{}'.format(markdown, os.path.basename(str(f))))
+
+ markdown_custom_target = CustomTarget(
+ output,
+ state.subdir,
+ state.subproject,
+ state.environment,
+ cmd + ['--output-directory', '@OUTDIR@', '--generate-md', markdown, '@INPUT@'],
+ xml_files,
+ outputs,
+ build_by_default=build_by_default,
+ description='Generating gdbus markdown {}',
+ )
+ targets.append(markdown_custom_target)
+
return ModuleReturnValue(targets, targets)
@typed_pos_args('gnome.mkenums', str)
@@ -1963,13 +2041,13 @@ class GnomeModule(ExtensionModule):
def _make_mkenum_impl(
self,
state: 'ModuleState',
- sources: T.Sequence[T.Union[str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList]],
+ sources: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]],
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
+ depends: T.Optional[T.Sequence[build.BuildTargetTypes]] = None
) -> build.CustomTarget:
real_cmd: T.List[T.Union[str, 'ToolType']] = [self._find_tool(state, 'glib-mkenums')]
real_cmd.extend(cmd)
@@ -1991,6 +2069,7 @@ class GnomeModule(ExtensionModule):
extra_depends=depends,
# https://github.com/mesonbuild/meson/issues/973
absolute_paths=True,
+ rspable=mesonlib.is_windows() or mesonlib.is_cygwin(),
description='Generating GObject enum file {}',
)
@@ -2130,7 +2209,7 @@ class GnomeModule(ExtensionModule):
with open(fname, 'w', encoding='utf-8') as ofile:
for package in packages:
ofile.write(package + '\n')
- return build.Data([mesonlib.File(True, outdir, fname)], install_dir, install_dir, mesonlib.FileMode(), state.subproject)
+ return build.Data([mesonlib.File(True, outdir, fname)], install_dir, install_dir, mesonlib.FileMode(), state.subproject, install_tag='devel')
def _get_vapi_link_with(self, target: CustomTarget) -> T.List[build.LibTypes]:
link_with: T.List[build.LibTypes] = []
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 5099b41..a72bf73 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -14,7 +14,7 @@ from ..build import CustomTarget, CustomTargetIndex
from ..dependencies import Dependency, InternalDependency
from ..interpreterbase import (
InvalidArguments, noPosargs, noKwargs, typed_kwargs, FeatureDeprecated,
- ContainerTypeInfo, KwargInfo, typed_pos_args
+ ContainerTypeInfo, KwargInfo, typed_pos_args, InterpreterObject
)
from ..interpreter.interpreterobjects import _CustomTargetHolder
from ..interpreter.type_checking import NoneType
@@ -146,31 +146,6 @@ class HotdocTargetBuilder:
self.check_extra_arg_type(arg, value)
self.set_arg_value(option, value)
- def get_value(self, types, argname, default=None, value_processor=None,
- mandatory=False, force_list=False):
- if not isinstance(types, list):
- types = [types]
- try:
- uvalue = value = self.kwargs.pop(argname)
- if value_processor:
- value = value_processor(value)
-
- for t in types:
- if isinstance(value, t):
- if force_list and not isinstance(value, list):
- return [value], uvalue
- return value, uvalue
- raise MesonException(f"{argname} field value {value} is not valid,"
- f" valid types are {types}")
- except KeyError:
- if mandatory:
- raise MesonException(f"{argname} mandatory field not found")
-
- if default is not None:
- return default, default
-
- return None, None
-
def add_extension_paths(self, paths: T.Union[T.List[str], T.Set[str]]) -> None:
for path in paths:
if path in self._extra_extension_paths:
@@ -383,12 +358,9 @@ class HotdocTargetBuilder:
class HotdocTargetHolder(_CustomTargetHolder['HotdocTarget']):
- def __init__(self, target: HotdocTarget, interp: Interpreter):
- super().__init__(target, interp)
- self.methods.update({'config_path': self.config_path_method})
-
@noPosargs
@noKwargs
+ @InterpreterObject.method('config_path')
def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str:
conf = self.held_object.hotdoc_conf.absolute_path(self.interpreter.environment.source_dir,
self.interpreter.environment.build_dir)
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index 87baab2..2d8d04d 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -56,16 +56,15 @@ if T.TYPE_CHECKING:
class ItsJoinFile(TypedDict):
input: T.List[T.Union[
- str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex,
- build.ExtractedObjects, build.GeneratedList, ExternalProgram,
- mesonlib.File]]
+ str, build.BuildTarget, build.GeneratedTypes,
+ build.ExtractedObjects, ExternalProgram, mesonlib.File]]
output: str
build_by_default: bool
install: bool
install_dir: T.Optional[str]
install_tag: T.Optional[str]
its_files: T.List[str]
- mo_targets: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]]
+ mo_targets: T.List[build.BuildTargetTypes]
class XgettextProgramT(TypedDict):
@@ -75,7 +74,7 @@ if T.TYPE_CHECKING:
install_dir: T.Optional[str]
install_tag: T.Optional[str]
- SourcesType = T.Union[str, mesonlib.File, build.BuildTarget, build.BothLibraries, build.CustomTarget]
+ SourcesType = T.Union[str, mesonlib.File, build.BuildTargetTypes, build.BothLibraries]
_ARGS: KwargInfo[T.List[str]] = KwargInfo(
@@ -202,6 +201,8 @@ class XgettextProgram:
source_files.update(source.get_sources())
elif isinstance(source, build.BothLibraries):
source_files.update(source.get('shared').get_sources())
+ elif isinstance(source, (build.CustomTarget, build.CustomTargetIndex)):
+ source_files.update(mesonlib.File.from_built_file(source.get_subdir(), f) for f in source.get_outputs())
return source_files
def _get_depends(self, sources: T.Iterable[SourcesType]) -> T.Set[build.CustomTarget]:
@@ -237,7 +238,7 @@ class XgettextProgram:
return mesonlib.File.from_built_file(self.interpreter.subdir, rsp_file.name)
@staticmethod
- def _get_source_id(sources: T.Iterable[T.Union[SourcesType, build.CustomTargetIndex]]) -> T.Iterable[str]:
+ def _get_source_id(sources: T.Iterable[SourcesType]) -> T.Iterable[str]:
for source in sources:
if isinstance(source, build.Target):
yield source.get_id()
@@ -307,8 +308,7 @@ class I18nModule(ExtensionModule):
ddirs = self._get_data_dirs(state, kwargs['data_dirs'])
datadirs = '--datadirs=' + ':'.join(ddirs) if ddirs else None
- command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget,
- build.CustomTargetIndex, 'ExternalProgram', mesonlib.File]] = []
+ command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, mesonlib.File]] = []
command.extend(state.environment.get_build_command())
command.extend([
'--internal', 'msgfmthelper',
@@ -487,8 +487,7 @@ class I18nModule(ExtensionModule):
for target in mo_targets:
mo_fnames.append(path.join(target.get_subdir(), target.get_outputs()[0]))
- command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget,
- build.CustomTargetIndex, 'ExternalProgram', mesonlib.File]] = []
+ command: T.List[T.Union[str, build.BuildTargetTypes, ExternalProgram, mesonlib.File]] = []
command.extend(state.environment.get_build_command())
itstool_cmd = self.tools['itstool'].get_command()
@@ -531,7 +530,7 @@ class I18nModule(ExtensionModule):
return ModuleReturnValue(ct, [ct])
@FeatureNew('i18n.xgettext', '1.8.0')
- @typed_pos_args('i18n.xgettext', str, varargs=(str, mesonlib.File, build.BuildTarget, build.BothLibraries, build.CustomTarget), min_varargs=1)
+ @typed_pos_args('i18n.xgettext', str, varargs=(str, mesonlib.File, build.BuildTarget, build.BothLibraries, build.CustomTarget, build.CustomTargetIndex), min_varargs=1)
@typed_kwargs(
'i18n.xgettext',
_ARGS,
@@ -541,6 +540,11 @@ class I18nModule(ExtensionModule):
INSTALL_TAG_KW,
)
def xgettext(self, state: ModuleState, args: T.Tuple[str, T.List[SourcesType]], kwargs: XgettextProgramT) -> build.CustomTarget:
+ if any(isinstance(a, build.CustomTarget) for a in args[1]):
+ FeatureNew.single_use('i18n.xgettext with custom_target is broken until 1.10', '1.10.0', self.interpreter.subproject, location=self.interpreter.current_node)
+ if any(isinstance(a, build.CustomTargetIndex) for a in args[1]):
+ FeatureNew.single_use('i18n.xgettext with custom_target index', '1.10.0', self.interpreter.subproject, location=self.interpreter.current_node)
+
toolname = 'xgettext'
if self.tools[toolname] is None or not self.tools[toolname].found():
self.tools[toolname] = state.find_program(toolname, required=True, for_machine=mesonlib.MachineChoice.BUILD)
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index cc0450a..7d5bc91 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -38,6 +38,7 @@ if T.TYPE_CHECKING:
filebase: T.Optional[str]
description: T.Optional[str]
url: str
+ license: str
subdirs: T.List[str]
conflicts: T.List[str]
dataonly: bool
@@ -149,16 +150,30 @@ class DependenciesHelper:
self.add_version_reqs(obj.name, obj.version_reqs)
elif isinstance(obj, str):
name, version_req = self.split_version_req(obj)
+ if name is None:
+ continue
processed_reqs.append(name)
self.add_version_reqs(name, [version_req] if version_req is not None else None)
elif isinstance(obj, dependencies.Dependency) and not obj.found():
pass
elif isinstance(obj, dependencies.ExternalDependency) and obj.name == 'threads':
pass
+ elif isinstance(obj, dependencies.InternalDependency) and all(lib.get_id() in self.metadata for lib in obj.libraries):
+ FeatureNew.single_use('pkgconfig.generate requirement from internal dependency', '1.9.0',
+ self.state.subproject, location=self.state.current_node)
+ # Ensure BothLibraries are resolved:
+ if self.pub_libs and isinstance(self.pub_libs[0], build.StaticLibrary):
+ obj = obj.get_as_static(recursive=True)
+ else:
+ obj = obj.get_as_shared(recursive=True)
+ for lib in obj.libraries:
+ processed_reqs.append(self.metadata[lib.get_id()].filebase)
else:
raise mesonlib.MesonException('requires argument not a string, '
- 'library with pkgconfig-generated file '
- f'or pkgconfig-dependency object, got {obj!r}')
+ 'library with pkgconfig-generated file, '
+ 'pkgconfig-dependency object, or '
+ 'internal-dependency object with '
+ f'pkgconfig-generated file, got {obj!r}')
return processed_reqs
def add_cflags(self, cflags: T.List[str]) -> None:
@@ -285,12 +300,22 @@ class DependenciesHelper:
# foo, bar' is ok, but 'foo,bar' is not.
self.version_reqs[name].update(version_reqs)
- def split_version_req(self, s: str) -> T.Tuple[str, T.Optional[str]]:
+ def split_version_req(self, s: str) -> T.Tuple[T.Optional[str], T.Optional[str]]:
+ stripped_str = s.strip()
+ if not stripped_str:
+ mlog.warning('Required dependency was found to be an empty string. Did you mean to pass an empty array?')
+ return None, None
for op in ['>=', '<=', '!=', '==', '=', '>', '<']:
- pos = s.find(op)
- if pos > 0:
- return s[0:pos].strip(), s[pos:].strip()
- return s, None
+ pos = stripped_str.find(op)
+ if pos < 0:
+ continue
+ if pos == 0:
+ raise mesonlib.MesonException(f'required versioned dependency "{s}" is missing the dependency\'s name.')
+ stripped_str, version = stripped_str[0:pos].strip(), stripped_str[pos:].strip()
+ if not stripped_str:
+ raise mesonlib.MesonException(f'required versioned dependency "{s}" is missing the dependency\'s name.')
+ return stripped_str, version
+ return stripped_str, None
def format_vreq(self, vreq: str) -> str:
# vreq are '>=1.0' and pkgconfig wants '>= 1.0'
@@ -441,6 +466,7 @@ class PkgConfigModule(NewExtensionModule):
def _generate_pkgconfig_file(self, state: ModuleState, deps: DependenciesHelper,
subdirs: T.List[str], name: str,
description: str, url: str, version: str,
+ license: str,
pcfile: str, conflicts: T.List[str],
variables: T.List[T.Tuple[str, str]],
unescaped_variables: T.List[T.Tuple[str, str]],
@@ -519,18 +545,20 @@ class PkgConfigModule(NewExtensionModule):
ofile.write(f'{k}={v}\n')
ofile.write('\n')
ofile.write(f'Name: {name}\n')
- if len(description) > 0:
+ if description:
ofile.write(f'Description: {description}\n')
- if len(url) > 0:
+ if url:
ofile.write(f'URL: {url}\n')
+ if license:
+ ofile.write(f'License: {license}\n')
ofile.write(f'Version: {version}\n')
reqs_str = deps.format_reqs(deps.pub_reqs)
- if len(reqs_str) > 0:
+ if reqs_str:
ofile.write(f'Requires: {reqs_str}\n')
reqs_str = deps.format_reqs(deps.priv_reqs)
- if len(reqs_str) > 0:
+ if reqs_str:
ofile.write(f'Requires.private: {reqs_str}\n')
- if len(conflicts) > 0:
+ if conflicts:
ofile.write('Conflicts: {}\n'.format(' '.join(conflicts)))
def generate_libs_flags(libs: T.List[LIBS]) -> T.Iterable[str]:
@@ -571,9 +599,9 @@ class PkgConfigModule(NewExtensionModule):
if isinstance(l, (build.CustomTarget, build.CustomTargetIndex)) or 'cs' not in l.compilers:
yield f'-l{lname}'
- if len(deps.pub_libs) > 0:
+ if deps.pub_libs:
ofile.write('Libs: {}\n'.format(' '.join(generate_libs_flags(deps.pub_libs))))
- if len(deps.priv_libs) > 0:
+ if deps.priv_libs:
ofile.write('Libs.private: {}\n'.format(' '.join(generate_libs_flags(deps.priv_libs))))
cflags: T.List[str] = []
@@ -605,6 +633,7 @@ class PkgConfigModule(NewExtensionModule):
KwargInfo('name', (str, NoneType), validator=lambda x: 'must not be an empty string' if x == '' else None),
KwargInfo('subdirs', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('url', str, default=''),
+ KwargInfo('license', str, default='', since='1.9.0'),
KwargInfo('version', (str, NoneType)),
VARIABLES_KW.evolve(name="unescaped_uninstalled_variables", since='0.59.0'),
VARIABLES_KW.evolve(name="unescaped_variables", since='0.59.0'),
@@ -659,6 +688,7 @@ class PkgConfigModule(NewExtensionModule):
filebase = kwargs['filebase'] if kwargs['filebase'] is not None else name
description = kwargs['description'] if kwargs['description'] is not None else default_description
url = kwargs['url']
+ license = kwargs['license']
conflicts = kwargs['conflicts']
# Prepend the main library to public libraries list. This is required
@@ -713,7 +743,7 @@ class PkgConfigModule(NewExtensionModule):
pkgroot_name = os.path.join('{libdir}', 'pkgconfig')
relocatable = state.get_option('pkgconfig.relocatable')
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
- version, pcfile, conflicts, variables,
+ version, license, pcfile, conflicts, variables,
unescaped_variables, False, dataonly,
pkgroot=pkgroot if relocatable else None)
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, pkgroot_name, None, state.subproject, install_tag='devel')
@@ -722,7 +752,7 @@ class PkgConfigModule(NewExtensionModule):
pcfile = filebase + '-uninstalled.pc'
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
- version, pcfile, conflicts, variables,
+ version, license, pcfile, conflicts, variables,
unescaped_variables, uninstalled=True, dataonly=dataonly)
# Associate the main library with this generated pc file. If the library
# is used in any subsequent call to the generated, it will generate a
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 2a7e685..99602c0 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -14,13 +14,13 @@ from ..build import known_shmod_kwargs, CustomTarget, CustomTargetIndex, BuildTa
from ..dependencies import NotFoundDependency
from ..dependencies.detect import get_dep_identifier, find_external_dependency
from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase
-from ..interpreter import extract_required_kwarg, permitted_dependency_kwargs, primitives as P_OBJ
+from ..interpreter import extract_required_kwarg, primitives as P_OBJ
from ..interpreter.interpreterobjects import _ExternalProgramHolder
-from ..interpreter.type_checking import NoneType, PRESERVE_PATH_KW, SHARED_MOD_KWS
+from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS
from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo,
InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo,
- FeatureNew, FeatureNewKwargs, disablerIfNotFound
+ FeatureNew, disablerIfNotFound, InterpreterObject
)
from ..mesonlib import MachineChoice
from ..options import OptionKey
@@ -31,10 +31,10 @@ if T.TYPE_CHECKING:
from . import ModuleState
from ..build import Build, Data
- from ..dependencies import Dependency
+ from ..dependencies.base import Dependency, DependencyObjectKWs
from ..interpreter import Interpreter
from ..interpreter.interpreter import BuildTargetSource
- from ..interpreter.kwargs import ExtractRequired, SharedModule as SharedModuleKw
+ from ..interpreter.kwargs import ExtractRequired, SharedModule as SharedModuleKw, FuncDependency
from ..interpreterbase.baseobjects import TYPE_var, TYPE_kwargs
class PyInstallKw(TypedDict):
@@ -115,34 +115,32 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
info = python.info
prefix = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('prefix'))
assert isinstance(prefix, str), 'for mypy'
+
+ if python.build_config:
+ self.version = python.build_config['language']['version']
+ self.platform = python.build_config['platform']
+ self.suffix = python.build_config['abi']['extension_suffix']
+ self.limited_api_suffix = python.build_config['abi']['stable_abi_suffix']
+ self.link_libpython = python.build_config['libpython']['link_extensions']
+ self.is_pypy = python.build_config['implementation']['name'] == 'pypy'
+ else:
+ self.version = info['version']
+ self.platform = info['platform']
+ self.suffix = info['suffix']
+ self.limited_api_suffix = info['limited_api_suffix']
+ self.link_libpython = info['link_libpython']
+ self.is_pypy = info['is_pypy']
+
self.variables = info['variables']
- self.suffix = info['suffix']
- self.limited_api_suffix = info['limited_api_suffix']
self.paths = info['paths']
self.pure = python.pure
self.platlib_install_path = os.path.join(prefix, python.platlib)
self.purelib_install_path = os.path.join(prefix, python.purelib)
- self.version = info['version']
- self.platform = info['platform']
- self.is_pypy = info['is_pypy']
- self.link_libpython = info['link_libpython']
- self.methods.update({
- 'extension_module': self.extension_module_method,
- 'dependency': self.dependency_method,
- 'install_sources': self.install_sources_method,
- 'get_install_dir': self.get_install_dir_method,
- 'language_version': self.language_version_method,
- 'found': self.found_method,
- 'has_path': self.has_path_method,
- 'get_path': self.get_path_method,
- 'has_variable': self.has_variable_method,
- 'get_variable': self.get_variable_method,
- 'path': self.path_method,
- })
@permittedKwargs(mod_kwargs)
@typed_pos_args('python.extension_module', str, varargs=(str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget))
@typed_kwargs('python.extension_module', *_MOD_KWARGS, _DEFAULTABLE_SUBDIR_KW, _LIMITED_API_KW, allow_unknown=True)
+ @InterpreterObject.method('extension_module')
def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule':
if 'install_dir' in kwargs:
if kwargs['subdir'] is not None:
@@ -208,7 +206,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
new_link_args = mesonlib.extract_as_list(kwargs, 'link_args')
- is_debug = self.interpreter.environment.coredata.optstore.get_value('debug')
+ is_debug = self.interpreter.environment.coredata.optstore.get_value_for('debug')
if is_debug:
new_link_args.append(python_windows_debug_link_exception)
else:
@@ -248,33 +246,43 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
return '0x{:02x}{:02x}0000'.format(major, minor)
- def _dependency_method_impl(self, kwargs: TYPE_kwargs) -> Dependency:
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ def _dependency_method_impl(self, kwargs: DependencyObjectKWs) -> Dependency:
+ for_machine = kwargs.get('native', MachineChoice.HOST)
identifier = get_dep_identifier(self._full_path(), kwargs)
dep = self.interpreter.coredata.deps[for_machine].get(identifier)
if dep is not None:
return dep
+ build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config'))
+
new_kwargs = kwargs.copy()
new_kwargs['required'] = False
+ if build_config:
+ new_kwargs['build_config'] = build_config
candidates = python_factory(self.interpreter.environment, for_machine, new_kwargs, self.held_object)
dep = find_external_dependency('python', self.interpreter.environment, new_kwargs, candidates)
self.interpreter.coredata.deps[for_machine].put(identifier, dep)
return dep
- @disablerIfNotFound
- @permittedKwargs(permitted_dependency_kwargs | {'embed'})
- @FeatureNewKwargs('python_installation.dependency', '0.53.0', ['embed'])
@noPosargs
- def dependency_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'Dependency':
+ @typed_kwargs(
+ 'python_installation.dependency',
+ *DEPENDENCY_KWS,
+ KwargInfo('embed', bool, default=False, since='0.53.0'),
+ )
+ @disablerIfNotFound
+ @InterpreterObject.method('dependency')
+ def dependency_method(self, args: T.List['TYPE_var'], kwargs: FuncDependency) -> 'Dependency':
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
+ nkwargs = T.cast('DependencyObjectKWs', kwargs.copy())
+ nkwargs['required'] = required
if disabled:
mlog.log('Dependency', mlog.bold('python'), 'skipped: feature', mlog.bold(feature), 'disabled')
return NotFoundDependency('python', self.interpreter.environment)
else:
- dep = self._dependency_method_impl(kwargs)
+ dep = self._dependency_method_impl(nkwargs)
if required and not dep.found():
raise mesonlib.MesonException('Python dependency not found')
return dep
@@ -287,6 +295,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
PRESERVE_PATH_KW,
KwargInfo('install_tag', (str, NoneType), since='0.60.0')
)
+ @InterpreterObject.method('install_sources')
def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]],
kwargs: 'PyInstallKw') -> 'Data':
self.held_object.run_bytecompile[self.version] = True
@@ -301,6 +310,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
@noPosargs
@typed_kwargs('python_installation.install_dir', _PURE_KW, _SUBDIR_KW)
+ @InterpreterObject.method('get_install_dir')
def get_install_dir_method(self, args: T.List['TYPE_var'], kwargs: 'PyInstallKw') -> str:
self.held_object.run_bytecompile[self.version] = True
pure = kwargs['pure'] if kwargs['pure'] is not None else self.pure
@@ -318,16 +328,19 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
@noPosargs
@noKwargs
+ @InterpreterObject.method('language_version')
def language_version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.version
@typed_pos_args('python_installation.has_path', str)
@noKwargs
+ @InterpreterObject.method('has_path')
def has_path_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
return args[0] in self.paths
@typed_pos_args('python_installation.get_path', str, optargs=[object])
@noKwargs
+ @InterpreterObject.method('get_path')
def get_path_method(self, args: T.Tuple[str, T.Optional['TYPE_var']], kwargs: 'TYPE_kwargs') -> 'TYPE_var':
path_name, fallback = args
try:
@@ -339,11 +352,13 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
@typed_pos_args('python_installation.has_variable', str)
@noKwargs
+ @InterpreterObject.method('has_variable')
def has_variable_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
return args[0] in self.variables
@typed_pos_args('python_installation.get_variable', str, optargs=[object])
@noKwargs
+ @InterpreterObject.method('get_variable')
def get_variable_method(self, args: T.Tuple[str, T.Optional['TYPE_var']], kwargs: 'TYPE_kwargs') -> 'TYPE_var':
var_name, fallback = args
try:
@@ -356,6 +371,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
@noPosargs
@noKwargs
@FeatureNew('Python module path method', '0.50.0')
+ @InterpreterObject.method('path')
def path_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return super().path_method(args, kwargs)
@@ -441,11 +457,13 @@ class PythonModule(ExtensionModule):
return None
def _find_installation_impl(self, state: 'ModuleState', display_name: str, name_or_path: str, required: bool) -> MaybePythonProg:
+ build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config'))
+
if not name_or_path:
- python = PythonExternalProgram('python3', mesonlib.python_command)
+ python = PythonExternalProgram('python3', mesonlib.python_command, build_config_path=build_config)
else:
tmp_python = ExternalProgram.from_entry(display_name, name_or_path)
- python = PythonExternalProgram(display_name, ext_prog=tmp_python)
+ python = PythonExternalProgram(display_name, ext_prog=tmp_python, build_config_path=build_config)
if not python.found() and mesonlib.is_windows():
pythonpath = self._get_win_pythonpath(name_or_path)
@@ -459,7 +477,7 @@ class PythonModule(ExtensionModule):
# it
if not python.found() and name_or_path in {'python2', 'python3'}:
tmp_python = ExternalProgram.from_entry(display_name, 'python')
- python = PythonExternalProgram(name_or_path, ext_prog=tmp_python)
+ python = PythonExternalProgram(name_or_path, ext_prog=tmp_python, build_config_path=build_config)
if python.found():
if python.sanity(state):
diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py
index 2e6779a..69ad1ee 100644
--- a/mesonbuild/modules/python3.py
+++ b/mesonbuild/modules/python3.py
@@ -50,7 +50,7 @@ class Python3Module(ExtensionModule):
# On Windows the extension is pyd for some unexplainable reason.
suffix = 'pyd'
else:
- suffix = []
+ suffix = None
kwargs['name_prefix'] = ''
kwargs['name_suffix'] = suffix
return self.interpreter.build_target(state.current_node, args, kwargs, SharedModule)
diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py
index f43a0ed..a8fcc86 100644
--- a/mesonbuild/modules/rust.py
+++ b/mesonbuild/modules/rust.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import itertools
import os
import re
+import textwrap
import typing as T
from mesonbuild.interpreterbase.decorators import FeatureNew
@@ -20,12 +21,12 @@ from ..interpreter.type_checking import (
)
from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noPosargs, permittedKwargs
from ..interpreter.interpreterobjects import Doctest
-from ..mesonlib import File, MesonException, PerMachine
+from ..mesonlib import File, MachineChoice, MesonException, PerMachine
from ..programs import ExternalProgram, NonExistingExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
- from ..build import IncludeDirs, LibTypes
+ from ..build import BuildTargetTypes, ExecutableKeywordArguments, IncludeDirs, LibTypes
from ..compilers.rust import RustCompiler
from ..dependencies import Dependency, ExternalLibrary
from ..interpreter import Interpreter
@@ -44,7 +45,7 @@ if T.TYPE_CHECKING:
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
is_parallel: bool
link_with: T.List[LibTypes]
- link_whole: T.List[LibTypes]
+ link_whole: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]
rust_args: T.List[str]
FuncTest = FuncRustTest[_kwargs.TestArgs]
@@ -178,17 +179,16 @@ class RustModule(ExtensionModule):
tkwargs['args'] = extra_args + ['--test', '--format', 'pretty']
tkwargs['protocol'] = 'rust'
- new_target_kwargs = base_target.original_kwargs.copy()
- # Don't mutate the shallow copied list, instead replace it with a new
- # one
+ new_target_kwargs = T.cast('ExecutableKeywordArguments', base_target.original_kwargs.copy())
+ del new_target_kwargs['rust_crate_type']
+ for kw in ('pic', 'prelink', 'rust_abi', 'version', 'soversion', 'darwin_versions', 'shortname'):
+ if kw in new_target_kwargs:
+ del new_target_kwargs[kw] # type: ignore[misc]
+
new_target_kwargs['install'] = False
new_target_kwargs['dependencies'] = new_target_kwargs.get('dependencies', []) + kwargs['dependencies']
- new_target_kwargs['link_with'] = new_target_kwargs.get('link_with', []) + kwargs['link_with']
+ new_target_kwargs['link_with'] = new_target_kwargs.get('link_with', []) + T.cast('T.List[BuildTargetTypes]', kwargs['link_with'])
new_target_kwargs['link_whole'] = new_target_kwargs.get('link_whole', []) + kwargs['link_whole']
- del new_target_kwargs['rust_crate_type']
- for kw in ['pic', 'prelink', 'rust_abi', 'version', 'soversion', 'darwin_versions']:
- if kw in new_target_kwargs:
- del new_target_kwargs[kw]
lang_args = base_target.extra_args.copy()
lang_args['rust'] = base_target.extra_args['rust'] + kwargs['rust_args'] + ['--test']
@@ -201,8 +201,7 @@ class RustModule(ExtensionModule):
name, base_target.subdir, state.subproject, base_target.for_machine,
sources, base_target.structured_sources,
base_target.objects, base_target.environment, base_target.compilers,
- new_target_kwargs
- )
+ new_target_kwargs)
return new_target, tkwargs
@typed_pos_args('rust.test', str, BuildTarget)
@@ -242,6 +241,14 @@ class RustModule(ExtensionModule):
def doctest(self, state: ModuleState, args: T.Tuple[str, T.Union[SharedLibrary, StaticLibrary]], kwargs: FuncDoctest) -> ModuleReturnValue:
name, base_target = args
+ if not base_target.uses_rust():
+ raise MesonException('doc tests are only supported for Rust targets')
+ if not base_target.uses_rust_abi():
+ raise MesonException("doc tests are not supported for rust_abi: 'c'")
+ if state.environment.is_cross_build() and state.environment.need_exe_wrapper(base_target.for_machine):
+ mlog.notice('skipping Rust doctests due to cross compilation', once=True)
+ return ModuleReturnValue(None, [])
+
# Link the base target's crate into the tests
kwargs['link_with'].append(base_target)
kwargs['depends'].append(base_target)
@@ -265,7 +272,7 @@ class RustModule(ExtensionModule):
if self.rustdoc[base_target.for_machine] is None:
rustc = T.cast('RustCompiler', base_target.compilers['rust'])
- rustdoc = rustc.get_rustdoc(state.environment)
+ rustdoc = rustc.get_rustdoc()
if rustdoc:
self.rustdoc[base_target.for_machine] = ExternalProgram(rustdoc.get_exe())
else:
@@ -329,6 +336,24 @@ class RustModule(ExtensionModule):
# TODO: if we want this to be per-machine we'll need a native kwarg
clang_args = state.environment.properties.host.get_bindgen_clang_args().copy()
+ # Find the first C'ish compiler to fetch the default compiler flags
+ # from. Append those to the bindgen flags to ensure we use a compatible
+ # environment.
+ comp = mesonlib.first(
+ [state.environment.coredata.compilers.host.get(l) for l in ['c', 'cpp', 'objc', 'objcpp']],
+ lambda x: x is not None,
+ )
+ if comp:
+ clang_args.extend(comp.get_always_args())
+ else:
+ mlog.warning(textwrap.dedent('''\
+ Using `rust.bindgen` without configuring C (or a C-like)
+ language in Meson will skip compiler detection and can cause
+ ABI incompatibilities due to missing crucial compiler flags.
+ Consider calling `add_languages('c')` in your Meson build
+ files.
+ '''))
+
for i in state.process_include_dirs(kwargs['include_directories']):
# bindgen always uses clang, so it's safe to hardcode -I here
clang_args.extend([f'-I{x}' for x in i.to_string_list(
@@ -435,7 +460,7 @@ class RustModule(ExtensionModule):
if self._bindgen_rust_target and '--rust-target' not in cmd:
cmd.extend(['--rust-target', self._bindgen_rust_target])
if self._bindgen_set_std and '--rust-edition' not in cmd:
- rust_std = state.environment.coredata.optstore.get_value('rust_std')
+ rust_std = state.environment.coredata.optstore.get_value_for('rust_std')
assert isinstance(rust_std, str), 'for mypy'
if rust_std != 'none':
cmd.extend(['--rust-edition', rust_std])
@@ -469,8 +494,8 @@ class RustModule(ExtensionModule):
@typed_pos_args('rust.proc_macro', str, varargs=SOURCES_VARARGS)
@typed_kwargs('rust.proc_macro', *SHARED_LIB_KWS, allow_unknown=True)
def proc_macro(self, state: ModuleState, args: T.Tuple[str, SourcesVarargsType], kwargs: _kwargs.SharedLibrary) -> SharedLibrary:
- kwargs['native'] = True # type: ignore
- kwargs['rust_crate_type'] = 'proc-macro' # type: ignore
+ kwargs['native'] = MachineChoice.BUILD
+ kwargs['rust_crate_type'] = 'proc-macro'
kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro']
target = state._interpreter.build_target(state.current_node, args, kwargs, SharedLibrary)
return target
diff --git a/mesonbuild/modules/simd.py b/mesonbuild/modules/simd.py
index bfdc0c2..2519d53 100644
--- a/mesonbuild/modules/simd.py
+++ b/mesonbuild/modules/simd.py
@@ -88,7 +88,7 @@ class SimdModule(ExtensionModule):
mlog.log(f'Compiler supports {iset}:', mlog.red('NO'))
continue
- if not compiler.has_multi_arguments(compile_args, state.environment)[0]:
+ if not compiler.has_multi_arguments(compile_args)[0]:
mlog.log(f'Compiler supports {iset}:', mlog.red('NO'))
continue
mlog.log(f'Compiler supports {iset}:', mlog.green('YES'))
diff --git a/mesonbuild/modules/snippets.py b/mesonbuild/modules/snippets.py
new file mode 100644
index 0000000..bfc8d7a
--- /dev/null
+++ b/mesonbuild/modules/snippets.py
@@ -0,0 +1,104 @@
+# Copyright 2025 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import annotations
+import textwrap
+import typing as T
+
+from pathlib import Path
+
+from . import NewExtensionModule, ModuleInfo
+from ..interpreterbase import KwargInfo, typed_kwargs, typed_pos_args
+from ..interpreter.type_checking import NoneType
+from .. import mesonlib
+
+if T.TYPE_CHECKING:
+ from typing_extensions import TypedDict
+ from . import ModuleState
+
+ class SymbolVisibilityHeaderKW(TypedDict):
+ namespace: T.Optional[str]
+ api: T.Optional[str]
+ compilation: T.Optional[str]
+ static_compilation: T.Optional[str]
+ static_only: bool
+
+
+class SnippetsModule(NewExtensionModule):
+
+ INFO = ModuleInfo('snippets', '1.10.0')
+
+ def __init__(self) -> None:
+ super().__init__()
+ self.methods.update({
+ 'symbol_visibility_header': self.symbol_visibility_header_method,
+ })
+
+ @typed_kwargs('snippets.symbol_visibility_header',
+ KwargInfo('namespace', (str, NoneType)),
+ KwargInfo('api', (str, NoneType)),
+ KwargInfo('compilation', (str, NoneType)),
+ KwargInfo('static_compilation', (str, NoneType)),
+ KwargInfo('static_only', (bool, NoneType)))
+ @typed_pos_args('snippets.symbol_visibility_header', str)
+ def symbol_visibility_header_method(self, state: ModuleState, args: T.Tuple[str], kwargs: 'SymbolVisibilityHeaderKW') -> mesonlib.File:
+ header_name = args[0]
+ namespace = kwargs['namespace'] or state.project_name
+ namespace = mesonlib.underscorify(namespace).upper()
+ if namespace[0].isdigit():
+ namespace = f'_{namespace}'
+ api = kwargs['api'] or f'{namespace}_API'
+ compilation = kwargs['compilation'] or f'{namespace}_COMPILATION'
+ static_compilation = kwargs['static_compilation'] or f'{namespace}_STATIC_COMPILATION'
+ static_only = kwargs['static_only']
+ if static_only is None:
+ default_library = state.get_option('default_library')
+ static_only = default_library == 'static'
+ content = textwrap.dedent('''\
+ // SPDX-license-identifier: 0BSD OR CC0-1.0 OR WTFPL OR Apache-2.0 OR LGPL-2.0-or-later
+ #pragma once
+ ''')
+ if static_only:
+ content += textwrap.dedent(f'''
+ #ifndef {static_compilation}
+ # define {static_compilation}
+ #endif /* {static_compilation} */
+ ''')
+ content += textwrap.dedent(f'''
+ #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined({static_compilation})
+ # define {api}_EXPORT __declspec(dllexport)
+ # define {api}_IMPORT __declspec(dllimport)
+ #elif defined(__OS2__) && !defined({static_compilation})
+ # define {api}_EXPORT __declspec(dllexport)
+ # define {api}_IMPORT
+ #elif __GNUC__ >= 4
+ # define {api}_EXPORT __attribute__((visibility("default")))
+ # define {api}_IMPORT
+ #else
+ # define {api}_EXPORT
+ # define {api}_IMPORT
+ #endif
+
+ #ifdef {compilation}
+ # define {api} {api}_EXPORT extern
+ #else
+ # define {api} {api}_IMPORT extern
+ #endif
+ ''')
+ header_path = Path(state.environment.get_build_dir(), state.subdir, header_name)
+ header_path.write_text(content, encoding='utf-8')
+ return mesonlib.File.from_built_file(state.subdir, header_name)
+
+def initialize(*args: T.Any, **kwargs: T.Any) -> SnippetsModule:
+ return SnippetsModule()
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 29ae96b..a24c405 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -75,14 +75,20 @@ class WindowsModule(ExtensionModule):
rescomp = ExternalProgram.from_bin_list(state.environment, for_machine, 'windres')
if not rescomp or not rescomp.found():
+ def search_programs(names: T.List[str]) -> T.Optional[ExternalProgram]:
+ for name in names:
+ program = ExternalProgram(name, silent=True)
+ if program.found():
+ return program
+ return None
+
comp = self.detect_compiler(state.environment.coredata.compilers[for_machine])
if comp.id in {'msvc', 'clang-cl', 'intel-cl'} or (comp.linker and comp.linker.id in {'link', 'lld-link'}):
- # Microsoft compilers uses rc irrespective of the frontend
- rescomp = ExternalProgram('rc', silent=True)
+ rescomp = search_programs(['rc', 'llvm-rc'])
else:
- rescomp = ExternalProgram('windres', silent=True)
+ rescomp = search_programs(['windres', 'llvm-windres'])
- if not rescomp.found():
+ if not rescomp:
raise MesonException('Could not find Windows resource compiler')
for (arg, match, rc_type) in [
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index f1c6071..b5792d2 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -119,36 +119,39 @@ class Lexer:
('id', IDENT_RE),
('number', re.compile(r'0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|0|[1-9]\d*')),
('eol_cont', re.compile(r'\\[ \t]*(#.*)?\n')),
- ('eol', re.compile(r'\n')),
('multiline_string', re.compile(r"'''(.|\n)*?'''", re.M)),
('comment', re.compile(r'#.*')),
- ('lparen', re.compile(r'\(')),
- ('rparen', re.compile(r'\)')),
- ('lbracket', re.compile(r'\[')),
- ('rbracket', re.compile(r'\]')),
- ('lcurl', re.compile(r'\{')),
- ('rcurl', re.compile(r'\}')),
- ('dblquote', re.compile(r'"')),
('string', re.compile(r"'([^'\\]|(\\.))*'")),
- ('comma', re.compile(r',')),
('plusassign', re.compile(r'\+=')),
- ('dot', re.compile(r'\.')),
- ('plus', re.compile(r'\+')),
- ('dash', re.compile(r'-')),
- ('star', re.compile(r'\*')),
- ('percent', re.compile(r'%')),
- ('fslash', re.compile(r'/')),
- ('colon', re.compile(r':')),
('equal', re.compile(r'==')),
('nequal', re.compile(r'!=')),
- ('assign', re.compile(r'=')),
('le', re.compile(r'<=')),
- ('lt', re.compile(r'<')),
('ge', re.compile(r'>=')),
- ('gt', re.compile(r'>')),
- ('questionmark', re.compile(r'\?')),
]
+ self.single_char_tokens = {
+ '\n': 'eol',
+ '(': 'lparen',
+ ')': 'rparen',
+ '[': 'lbracket',
+ ']': 'rbracket',
+ '{': 'lcurl',
+ '}': 'rcurl',
+ '"': 'dblquote',
+ ',': 'comma',
+ '.': 'dot',
+ '+': 'plus',
+ '-': 'dash',
+ '*': 'star',
+ '%': 'percent',
+ '/': 'fslash',
+ ':': 'colon',
+ '=': 'assign',
+ '<': 'lt',
+ '>': 'gt',
+ '?': 'questionmark',
+ }
+
def getline(self, line_start: int) -> str:
return self.code[line_start:self.code.find('\n', line_start)]
@@ -159,22 +162,25 @@ class Lexer:
par_count = 0
bracket_count = 0
curl_count = 0
- col = 0
- while loc < len(self.code):
- matched = False
- value: str = ''
- for (tid, reg) in self.token_specification:
- mo = reg.match(self.code, loc)
- if mo:
- curline = lineno
- curline_start = line_start
- col = mo.start() - line_start
- matched = True
- span_start = loc
- loc = mo.end()
- span_end = loc
- bytespan = (span_start, span_end)
- value = mo.group()
+ try:
+ while loc < len(self.code):
+ value: str
+ span_start = loc
+ col = loc - line_start
+ curline = lineno
+ curline_start = line_start
+ for (tid, reg) in self.token_specification:
+ mo = reg.match(self.code, loc)
+ if mo:
+ value = mo.group()
+ loc = mo.end()
+ break
+ else:
+ # lex single characters and raise an exception for invalid tokens
+ value = self.code[loc]
+ tid = self.single_char_tokens[value]
+ loc += 1
+
if tid == 'lparen':
par_count += 1
elif tid == 'rparen':
@@ -189,39 +195,41 @@ class Lexer:
curl_count -= 1
elif tid == 'dblquote':
raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
- elif tid in {'string', 'fstring'}:
- if value.find("\n") != -1:
- msg = ("Newline character in a string detected, use ''' (three single quotes) "
- "for multiline strings instead.\n"
- "This will become a hard error in a future Meson release.")
- mlog.warning(mlog.code_line(msg, self.getline(line_start), col), location=BaseNode(lineno, col, filename))
- value = value[2 if tid == 'fstring' else 1:-1]
- elif tid in {'multiline_string', 'multiline_fstring'}:
- value = value[4 if tid == 'multiline_fstring' else 3:-3]
- lines = value.split('\n')
- if len(lines) > 1:
- lineno += len(lines) - 1
- line_start = mo.end() - len(lines[-1])
- elif tid == 'eol_cont':
- lineno += 1
- line_start = loc
- tid = 'whitespace'
elif tid == 'eol':
lineno += 1
line_start = loc
if par_count > 0 or bracket_count > 0 or curl_count > 0:
tid = 'whitespace'
- elif tid == 'id':
- if value in self.keywords:
- tid = value
- else:
- if value in self.future_keywords:
- mlog.warning(f"Identifier '{value}' will become a reserved keyword in a future release. Please rename it.",
- location=BaseNode(lineno, col, filename))
- yield Token(tid, filename, curline_start, curline, col, bytespan, value)
- break
- if not matched:
- raise ParseException('lexer', self.getline(line_start), lineno, col)
+
+ if tid == 'id':
+ if value in self.keywords:
+ tid = value
+ else:
+ if value in self.future_keywords:
+ mlog.warning(f"Identifier '{value}' will become a reserved keyword in a future release. Please rename it.",
+ location=BaseNode(lineno, col, filename))
+ elif tid in {'string', 'fstring'}:
+ if value.find("\n") != -1:
+ msg = ("Newline character in a string detected, use ''' (three single quotes) "
+ "for multiline strings instead.\n"
+ "This will become a hard error in a future Meson release.")
+ mlog.warning(mlog.code_line(msg, self.getline(line_start), col), location=BaseNode(lineno, col, filename))
+ value = value[2 if tid == 'fstring' else 1:-1]
+ elif tid in {'multiline_string', 'multiline_fstring'}:
+ value = value[4 if tid == 'multiline_fstring' else 3:-3]
+ lines = value.split('\n')
+ if len(lines) > 1:
+ lineno += len(lines) - 1
+ line_start = loc - len(lines[-1]) - 3
+ elif tid == 'eol_cont':
+ lineno += 1
+ line_start = loc
+ tid = 'whitespace'
+ bytespan = (span_start, loc)
+ yield Token(tid, filename, curline_start, curline, col, bytespan, value)
+
+ except KeyError:
+ raise ParseException(f'lexer: unrecognized token {self.code[loc]!r}', self.getline(line_start), lineno, loc - line_start)
@dataclass
class BaseNode:
@@ -369,6 +377,13 @@ class ArgumentNode(BaseNode):
mlog.warning('This will be an error in Meson 2.0.')
self.kwargs[name] = value
+ def get_kwarg_or_default(self, name: str, default: BaseNode) -> BaseNode:
+ for k, v in self.kwargs.items():
+ assert isinstance(k, IdNode)
+ if k.value == name:
+ return v
+ return default
+
def set_kwarg_no_check(self, name: BaseNode, value: BaseNode) -> None:
self.kwargs[name] = value
@@ -444,10 +459,9 @@ class ComparisonNode(BinaryOperatorNode):
@dataclass(unsafe_hash=True)
class ArithmeticNode(BinaryOperatorNode):
- # TODO: use a Literal for operation
- operation: str
+ operation: ARITH_OPERATORS
- def __init__(self, operation: str, left: BaseNode, operator: SymbolNode, right: BaseNode):
+ def __init__(self, operation: ARITH_OPERATORS, left: BaseNode, operator: SymbolNode, right: BaseNode):
super().__init__(left, operator, right)
self.operation = operation
@@ -649,18 +663,22 @@ class ParenthesizedNode(BaseNode):
lpar: SymbolNode = field(hash=False)
inner: BaseNode
rpar: SymbolNode = field(hash=False)
+ is_multiline: bool
def __init__(self, lpar: SymbolNode, inner: BaseNode, rpar: SymbolNode):
super().__init__(lpar.lineno, lpar.colno, inner.filename, end_lineno=rpar.lineno, end_colno=rpar.colno+1)
self.lpar = lpar
self.inner = inner
self.rpar = rpar
-
+ self.is_multiline = False
if T.TYPE_CHECKING:
- COMPARISONS = Literal['==', '!=', '<', '<=', '>=', '>', 'in', 'notin']
+ COMPARISONS = Literal['==', '!=', '<', '<=', '>=', '>', 'in', 'not in']
+ ARITH_OPERATORS = Literal['+', '-', '*', '/', '%']
-comparison_map: T.Mapping[str, COMPARISONS] = {
+ALL_STRINGS = frozenset({'string', 'fstring', 'multiline_string', 'multiline_fstring'})
+
+COMPARISON_MAP: T.Mapping[str, COMPARISONS] = {
'equal': '==',
'nequal': '!=',
'lt': '<',
@@ -668,7 +686,18 @@ comparison_map: T.Mapping[str, COMPARISONS] = {
'gt': '>',
'ge': '>=',
'in': 'in',
- 'not in': 'notin',
+ 'not in': 'not in',
+}
+
+ADDSUB_MAP: T.Mapping[str, ARITH_OPERATORS] = {
+ 'plus': '+',
+ 'dash': '-',
+}
+
+MULDIV_MAP: T.Mapping[str, ARITH_OPERATORS] = {
+ 'percent': '%',
+ 'star': '*',
+ 'fslash': '/',
}
# Recursive descent parser for Meson's definition language.
@@ -676,15 +705,16 @@ comparison_map: T.Mapping[str, COMPARISONS] = {
# levels so there are not enough words to describe them all.
# Enter numbering:
#
-# 1 assignment
-# 2 or
-# 3 and
-# 4 comparison
-# 5 arithmetic
-# 6 negation
-# 7 funcall, method call
-# 8 parentheses
-# 9 plain token
+# 1 assignment
+# 2 or
+# 3 and
+# 4 comparison
+# 5 addition and subtraction
+# 6 multiplication, division and modulus
+# 7 negation
+# 8 funcall, method call
+# 9 parentheses
+# 10 plain token
class Parser:
def __init__(self, code: str, filename: str):
@@ -727,7 +757,7 @@ class Parser:
return True
return False
- def accept_any(self, tids: T.Tuple[str, ...]) -> str:
+ def accept_any(self, tids: T.Union[T.AbstractSet[str], T.Mapping[str, object]]) -> str:
tid = self.current.tid
if tid in tids:
self.getsym()
@@ -810,10 +840,10 @@ class Parser:
def e4(self) -> BaseNode:
left = self.e5()
- for nodename, operator_type in comparison_map.items():
- if self.accept(nodename):
- operator = self.create_node(SymbolNode, self.previous)
- return self.create_node(ComparisonNode, operator_type, left, operator, self.e5())
+ op = self.accept_any(COMPARISON_MAP)
+ if op:
+ operator = self.create_node(SymbolNode, self.previous)
+ return self.create_node(ComparisonNode, COMPARISON_MAP[op], left, operator, self.e5())
if self.accept('not'):
ws = self.current_ws.copy()
not_token = self.previous
@@ -827,54 +857,42 @@ class Parser:
not_token.bytespan = (not_token.bytespan[0], in_token.bytespan[1])
not_token.value += temp_node.whitespaces.value + in_token.value
operator = self.create_node(SymbolNode, not_token)
- return self.create_node(ComparisonNode, 'notin', left, operator, self.e5())
+ return self.create_node(ComparisonNode, 'not in', left, operator, self.e5())
return left
def e5(self) -> BaseNode:
- return self.e5addsub()
-
- def e5addsub(self) -> BaseNode:
- op_map = {
- 'plus': 'add',
- 'dash': 'sub',
- }
- left = self.e5muldiv()
+ left = self.e6()
while True:
- op = self.accept_any(tuple(op_map.keys()))
+ op = self.accept_any(ADDSUB_MAP)
if op:
operator = self.create_node(SymbolNode, self.previous)
- left = self.create_node(ArithmeticNode, op_map[op], left, operator, self.e5muldiv())
+ left = self.create_node(ArithmeticNode, ADDSUB_MAP[op], left, operator, self.e6())
else:
break
return left
- def e5muldiv(self) -> BaseNode:
- op_map = {
- 'percent': 'mod',
- 'star': 'mul',
- 'fslash': 'div',
- }
- left = self.e6()
+ def e6(self) -> BaseNode:
+ left = self.e7()
while True:
- op = self.accept_any(tuple(op_map.keys()))
+ op = self.accept_any(MULDIV_MAP)
if op:
operator = self.create_node(SymbolNode, self.previous)
- left = self.create_node(ArithmeticNode, op_map[op], left, operator, self.e6())
+ left = self.create_node(ArithmeticNode, MULDIV_MAP[op], left, operator, self.e7())
else:
break
return left
- def e6(self) -> BaseNode:
+ def e7(self) -> BaseNode:
if self.accept('not'):
operator = self.create_node(SymbolNode, self.previous)
- return self.create_node(NotNode, self.current, operator, self.e7())
+ return self.create_node(NotNode, self.current, operator, self.e8())
if self.accept('dash'):
operator = self.create_node(SymbolNode, self.previous)
- return self.create_node(UMinusNode, self.current, operator, self.e7())
- return self.e7()
+ return self.create_node(UMinusNode, self.current, operator, self.e8())
+ return self.e8()
- def e7(self) -> BaseNode:
- left = self.e8()
+ def e8(self) -> BaseNode:
+ left = self.e9()
block_start = self.current
if self.accept('lparen'):
lpar = self.create_node(SymbolNode, block_start)
@@ -897,7 +915,7 @@ class Parser:
left = self.index_call(left)
return left
- def e8(self) -> BaseNode:
+ def e9(self) -> BaseNode:
block_start = self.current
if self.accept('lparen'):
lpar = self.create_node(SymbolNode, block_start)
@@ -918,9 +936,9 @@ class Parser:
rcurl = self.create_node(SymbolNode, self.previous)
return self.create_node(DictNode, lcurl, key_values, rcurl)
else:
- return self.e9()
+ return self.e10()
- def e9(self) -> BaseNode:
+ def e10(self) -> BaseNode:
t = self.current
if self.accept('true'):
t.value = True
@@ -932,7 +950,7 @@ class Parser:
return self.create_node(IdNode, t)
if self.accept('number'):
return self.create_node(NumberNode, t)
- if self.accept_any(('string', 'fstring', 'multiline_string', 'multiline_fstring')):
+ if self.accept_any(ALL_STRINGS):
return self.create_node(StringNode, t)
return EmptyNode(self.current.lineno, self.current.colno, self.current.filename)
@@ -978,7 +996,7 @@ class Parser:
def method_call(self, source_object: BaseNode) -> MethodNode:
dot = self.create_node(SymbolNode, self.previous)
- methodname = self.e9()
+ methodname = self.e10()
if not isinstance(methodname, IdNode):
if isinstance(source_object, NumberNode) and isinstance(methodname, NumberNode):
raise ParseException('meson does not support float numbers',
diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py
index 81dd183..df06245 100644
--- a/mesonbuild/msetup.py
+++ b/mesonbuild/msetup.py
@@ -9,13 +9,16 @@ import cProfile as profile
from pathlib import Path
import typing as T
-from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog
+from . import build, cmdline, coredata, environment, interpreter, mesonlib, mintro, mlog
+from .dependencies import Dependency
from .mesonlib import MesonException
-from .options import COMPILER_BASE_OPTIONS, OptionKey
+from .interpreterbase import ObjectHolder
+from .options import OptionKey
if T.TYPE_CHECKING:
from typing_extensions import Protocol
- from .coredata import SharedCMDOptions
+ from .cmdline import SharedCMDOptions
+ from .interpreter import SubprojectHolder
class CMDOptions(SharedCMDOptions, Protocol):
@@ -27,7 +30,6 @@ if T.TYPE_CHECKING:
builddir: str
sourcedir: str
pager: bool
- unset_opts: T.List[str]
git_ignore_file = '''# This file is autogenerated by Meson. If you change or delete it, it won't be recreated.
*
@@ -42,7 +44,7 @@ syntax: glob
# Note: when adding arguments, please also add them to the completion
# scripts in $MESONSRC/data/shell-completions/
def add_arguments(parser: argparse.ArgumentParser) -> None:
- coredata.register_builtin_arguments(parser)
+ cmdline.register_builtin_arguments(parser)
parser.add_argument('--native-file',
default=[],
action='append',
@@ -80,7 +82,7 @@ class MesonApp:
# configuration fails we need to be able to wipe again.
restore = []
with tempfile.TemporaryDirectory() as d:
- for filename in [coredata.get_cmd_line_file(self.build_dir)] + glob.glob(os.path.join(self.build_dir, environment.Environment.private_dir, '*.ini')):
+ for filename in [cmdline.get_cmd_line_file(self.build_dir)] + glob.glob(os.path.join(self.build_dir, environment.Environment.private_dir, '*.ini')):
try:
restore.append((shutil.copy(filename, d), filename))
except FileNotFoundError:
@@ -88,7 +90,7 @@ class MesonApp:
# a partial build or is empty.
pass
- coredata.read_cmd_line_file(self.build_dir, options)
+ cmdline.read_cmd_line_file(self.build_dir, options)
try:
# Don't delete the whole tree, just all of the files and
@@ -180,45 +182,48 @@ class MesonApp:
# See class Backend's 'generate' for comments on capture args and returned dictionary.
def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) -> T.Optional[dict]:
env = environment.Environment(self.source_dir, self.build_dir, self.options)
+ if not env.first_invocation:
+ assert self.options.reconfigure
+ env.coredata.set_from_configure_command(self.options)
mlog.initialize(env.get_log_dir(), self.options.fatal_warnings)
if self.options.profile:
mlog.set_timestamp_start(time.monotonic())
if self.options.clearcache:
env.coredata.clear_cache()
- with mesonlib.BuildDirLock(self.build_dir):
+ with mesonlib.DirectoryLock(self.build_dir, 'meson-private/meson.lock',
+ mesonlib.DirectoryLockAction.FAIL,
+ 'Some other Meson process is already using this build directory. Exiting.'):
return self._generate(env, capture, vslite_ctx)
- def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Any, all_subprojects: T.Any) -> None:
- pending = coredata.optstore.pending_options
+ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Dict[OptionKey, str], all_subprojects: T.Mapping[str, SubprojectHolder]) -> None:
errlist: T.List[str] = []
- for opt in pending:
- # It is not an error to set wrong option for unknown subprojects or
- # language because we don't have control on which one will be selected.
- if opt.subproject and opt.subproject not in all_subprojects:
+ known_subprojects = [name for name, obj in all_subprojects.items() if obj.found()]
+ for opt in cmd_line_options:
+ # Accept options that exist or could appear in subsequent reconfigurations,
+ # including options for subprojects that were not used
+ if opt in coredata.optstore or \
+ opt.evolve(subproject=None) in coredata.optstore or \
+ coredata.optstore.accept_as_pending_option(opt):
continue
- if coredata.optstore.is_compiler_option(opt):
+ if opt.subproject and opt.subproject not in known_subprojects:
continue
- if (coredata.optstore.is_base_option(opt) and
- opt.evolve(subproject=None, machine=mesonlib.MachineChoice.HOST) in COMPILER_BASE_OPTIONS):
+ # "foo=true" may also refer to toplevel project option ":foo"
+ if opt.subproject is None and coredata.optstore.is_project_option(opt.as_root()):
continue
- keystr = str(opt)
- if keystr in cmd_line_options:
- errlist.append(f'"{keystr}"')
+ errlist.append(f'"{opt}"')
if errlist:
errstr = ', '.join(errlist)
raise MesonException(f'Unknown options: {errstr}')
- coredata.optstore.clear_pending()
-
def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.Optional[dict]) -> T.Optional[dict]:
# Get all user defined options, including options that have been defined
# during a previous invocation or using meson configure.
user_defined_options = T.cast('CMDOptions', argparse.Namespace(**vars(self.options)))
- coredata.read_cmd_line_file(self.build_dir, user_defined_options)
+ cmdline.read_cmd_line_file(self.build_dir, user_defined_options)
mlog.debug('Build started at', datetime.datetime.now().isoformat())
mlog.debug('Main binary:', sys.executable)
- mlog.debug('Build Options:', coredata.format_cmd_line_options(user_defined_options))
+ mlog.debug('Build Options:', cmdline.format_cmd_line_options(user_defined_options))
mlog.debug('Python system:', platform.system())
mlog.log(mlog.bold('The Meson build system'))
mlog.log('Version:', coredata.version)
@@ -284,9 +289,9 @@ class MesonApp:
# read from a pipe and wrote into a private file.
self.options.cross_file = env.coredata.cross_files
self.options.native_file = env.coredata.config_files
- coredata.write_cmd_line_file(self.build_dir, self.options)
+ cmdline.write_cmd_line_file(self.build_dir, self.options)
else:
- coredata.update_cmd_line_file(self.build_dir, self.options)
+ cmdline.update_cmd_line_file(self.build_dir, self.options)
# Generate an IDE introspection file with the same syntax as the already existing API
if self.options.profile:
@@ -334,9 +339,16 @@ class MesonApp:
return captured_compile_args
def finalize_postconf_hooks(self, b: build.Build, intr: interpreter.Interpreter) -> None:
+ for varname, holder in intr.variables.items():
+ if isinstance(holder, ObjectHolder):
+ d = holder.held_object
+ if isinstance(d, Dependency) and d.found():
+ d.meson_variables.append(varname)
+
b.devenv.append(intr.backend.get_devenv())
for mod in intr.modules.values():
mod.postconf_hook(b)
+ b.def_files = intr.get_build_def_files()
def run_genvslite_setup(options: CMDOptions) -> None:
# With --genvslite, we essentially want to invoke multiple 'setup' iterations. I.e. -
@@ -347,17 +359,18 @@ def run_genvslite_setup(options: CMDOptions) -> None:
# invoke the appropriate 'meson compile ...' build commands upon the normal visual studio build/rebuild/clean actions, instead of using
# the native VS/msbuild system.
builddir_prefix = options.builddir
- genvsliteval = options.cmd_line_options.pop('genvslite') # type: ignore [call-overload]
+ k_genvslite = OptionKey('genvslite')
+ genvsliteval = options.cmd_line_options.pop(k_genvslite)
# The command line may specify a '--backend' option, which doesn't make sense in conjunction with
# '--genvslite', where we always want to use a ninja back end -
- k_backend = 'backend'
- if k_backend in options.cmd_line_options.keys():
- if options.cmd_line_options[k_backend] != 'ninja': # type: ignore [index]
+ k_backend = OptionKey('backend')
+ if k_backend in options.cmd_line_options:
+ if options.cmd_line_options[k_backend] != 'ninja':
raise MesonException('Explicitly specifying a backend option with \'genvslite\' is not necessary '
'(the ninja backend is always used) but specifying a non-ninja backend '
'conflicts with a \'genvslite\' setup')
else:
- options.cmd_line_options[k_backend] = 'ninja' # type: ignore [index]
+ options.cmd_line_options[k_backend] = 'ninja'
buildtypes_list = coredata.get_genvs_default_buildtype_list()
vslite_ctx = {}
@@ -368,7 +381,7 @@ def run_genvslite_setup(options: CMDOptions) -> None:
vslite_ctx[buildtypestr] = app.generate(capture=True)
#Now for generating the 'lite' solution and project files, which will use these builds we've just set up, above.
options.builddir = f'{builddir_prefix}_vs'
- options.cmd_line_options[OptionKey('genvslite')] = genvsliteval
+ options.cmd_line_options[k_genvslite] = genvsliteval
app = MesonApp(options)
app.generate(capture=False, vslite_ctx=vslite_ctx)
@@ -377,14 +390,14 @@ def run(options: T.Union[CMDOptions, T.List[str]]) -> int:
parser = argparse.ArgumentParser()
add_arguments(parser)
options = T.cast('CMDOptions', parser.parse_args(options))
- coredata.parse_cmd_line_options(options)
+ cmdline.parse_cmd_line_options(options)
# Msetup doesn't actually use this option, but we pass msetup options to
# mconf, and it does. We won't actually hit the path that uses it, but don't
# lie
options.pager = False
- if 'genvslite' in options.cmd_line_options.keys():
+ if OptionKey('genvslite') in options.cmd_line_options:
run_genvslite_setup(options)
else:
app = MesonApp(options)
diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py
index c74283c..f4b4405 100755
--- a/mesonbuild/msubprojects.py
+++ b/mesonbuild/msubprojects.py
@@ -1,9 +1,10 @@
from __future__ import annotations
from dataclasses import dataclass, InitVar
-import os, subprocess
+import sys, os, subprocess
import argparse
import asyncio
+import fnmatch
import threading
import copy
import shutil
@@ -60,6 +61,9 @@ if T.TYPE_CHECKING:
ALL_TYPES_STRING = ', '.join(ALL_TYPES)
+if sys.version_info >= (3, 14):
+ tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter)
+
def read_archive_files(path: Path, base_path: Path) -> T.Set[Path]:
if path.suffix == '.zip':
with zipfile.ZipFile(path, 'r') as zip_archive:
@@ -640,9 +644,14 @@ def add_common_arguments(p: argparse.ArgumentParser) -> None:
p.add_argument('--allow-insecure', default=False, action='store_true',
help='Allow insecure server connections.')
-def add_subprojects_argument(p: argparse.ArgumentParser) -> None:
- p.add_argument('subprojects', nargs='*',
- help='List of subprojects (default: all)')
+def add_subprojects_argument(p: argparse.ArgumentParser, name: str = None) -> None:
+ helpstr = 'Patterns of subprojects to operate on (default: all)'
+ if name:
+ p.add_argument(name, dest='subprojects', metavar='pattern', nargs=1, action='append',
+ default=[], help=helpstr)
+ else:
+ p.add_argument('subprojects', metavar='pattern', nargs='*', default=[],
+ help=helpstr)
def add_wrap_update_parser(subparsers: 'SubParsers') -> argparse.ArgumentParser:
p = subparsers.add_parser('update', help='Update wrap files from WrapDB (Since 0.63.0)')
@@ -692,7 +701,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
p.add_argument('args', nargs=argparse.REMAINDER,
help=argparse.SUPPRESS)
add_common_arguments(p)
- p.set_defaults(subprojects=[])
+ add_subprojects_argument(p, '--filter')
p.set_defaults(subprojects_func=Runner.foreach)
p = subparsers.add_parser('purge', help='Remove all wrap-based subproject artifacts')
@@ -724,7 +733,8 @@ def run(options: 'Arguments') -> int:
return 0
r = Resolver(source_dir, subproject_dir, wrap_frontend=True, allow_insecure=options.allow_insecure, silent=True)
if options.subprojects:
- wraps = [wrap for name, wrap in r.wraps.items() if name in options.subprojects]
+ wraps = [wrap for name, wrap in r.wraps.items()
+ if any(fnmatch.fnmatch(name, pat) for pat in options.subprojects)]
else:
wraps = list(r.wraps.values())
types = [t.strip() for t in options.types.split(',')] if options.types else []
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 4a907d8..95ca11d 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -29,7 +29,7 @@ import unicodedata
import xml.etree.ElementTree as et
from . import build
-from . import environment
+from . import tooldetect
from . import mlog
from .coredata import MesonVersionMismatchException, major_versions_differ
from .coredata import version as coredata_version
@@ -221,11 +221,14 @@ def returncode_to_status(retcode: int) -> str:
return f'exit status {retcode}'
signum = retcode - 128
- try:
- signame = signal.Signals(signum).name
- except ValueError:
- signame = 'SIGinvalid'
- return f'(exit status {retcode} or signal {signum} {signame})'
+ if signum < 32:
+ try:
+ signame = signal.Signals(signum).name
+ except ValueError:
+ signame = 'SIGinvalid'
+ return f'(exit status {retcode} or signal {signum} {signame})'
+
+ return f'(exit status {retcode} or {hex(retcode)})'
# TODO for Windows
sh_quote: T.Callable[[str], str] = lambda x: x
@@ -790,6 +793,7 @@ class JsonLogfileBuilder(TestFileLogger):
'name': result.name,
'stdout': result.stdo,
'result': result.res.value,
+ 'is_fail': result.res.is_bad(),
'starttime': result.starttime,
'duration': result.duration,
'returncode': result.returncode,
@@ -1699,7 +1703,7 @@ class TestHarness:
if self.options.no_rebuild:
return
- self.ninja = environment.detect_ninja()
+ self.ninja = tooldetect.detect_ninja()
if not self.ninja:
print("Can't find ninja, can't rebuild test.")
# If ninja can't be found return exit code 127, indicating command
@@ -1887,7 +1891,12 @@ class TestHarness:
raise RuntimeError('Test harness object can only be used once.')
self.is_run = True
tests = self.get_tests()
- rebuild_only_tests = tests if self.options.args else []
+ # NOTE: If all tests are selected anyway, we pass
+ # an empty list to `rebuild_deps`, which then will execute
+ # the "meson-test-prereq" ninja target as a fallback.
+ # This prevents situations, where ARG_MAX may overflow
+ # if there are many targets.
+ rebuild_only_tests = tests if tests != self.tests else []
if not tests:
return 0
if not self.options.no_rebuild and not rebuild_deps(self.ninja, self.options.wd, rebuild_only_tests, self.options.benchmark):
@@ -1918,7 +1927,7 @@ class TestHarness:
self.run_tests(runners)
finally:
os.chdir(startdir)
- return self.total_failure_count()
+ return 1 if self.total_failure_count() > 0 else 0
@staticmethod
def split_suite_string(suite: str) -> T.Tuple[str, str]:
@@ -1936,29 +1945,22 @@ class TestHarness:
for prjst in test.suite:
(prj, st) = TestHarness.split_suite_string(prjst)
- # the SUITE can be passed as
- # suite_name
- # or
- # project_name:suite_name
- # so we need to select only the test belonging to project_name
-
- # this if handle the first case (i.e., SUITE == suite_name)
-
- # in this way we can run tests belonging to different
- # (sub)projects which share the same suite_name
- if not st_match and st == prj_match:
- return True
-
- # these two conditions are needed to handle the second option
- # i.e., SUITE == project_name:suite_name
-
- # in this way we select the only the tests of
- # project_name with suite_name
- if prj_match and prj != prj_match:
- continue
- if st_match and st != st_match:
- continue
- return True
+ # The SUITE can be passed as
+ # - `name` - We select tests belonging to (sub)project OR suite
+ # with the given name.
+ # - `:suite_name` - We select tests belonging to any (sub)projects
+ # and in suite_name.
+ # - `project_name:suite_name` - We select tests belonging
+ # to project_name and in suite_name.
+ if not st_match:
+ if prj_match in {prj, st}:
+ return True
+ elif not prj_match:
+ if st == st_match:
+ return True
+ else:
+ if prj == prj_match and st == st_match:
+ return True
return False
def test_suitable(self, test: TestSerialisation) -> bool:
@@ -2077,21 +2079,25 @@ class TestHarness:
return wrap
def get_pretty_suite(self, test: TestSerialisation) -> str:
- if len(self.suites) > 1 and test.suite:
- rv = TestHarness.split_suite_string(test.suite[0])[0]
- s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite)
+ assert test.suite, 'Interpreter should ensure there is always at least one suite'
+ prj = TestHarness.split_suite_string(test.suite[0])[0]
+ suites: T.List[str] = []
+ for i in test.suite:
+ s = TestHarness.split_suite_string(i)[1]
if s:
- rv += ":"
- return rv + s + " / " + test.name
- else:
- return test.name
+ suites.append(s)
+ name = f'{prj}:{test.name}'
+ if suites:
+ s = '+'.join(suites)
+ name = f'{s} - {name}'
+ return name
def run_tests(self, runners: T.List[SingleTestRunner]) -> None:
try:
self.open_logfiles()
# TODO: this is the default for python 3.8
- if sys.platform == 'win32':
+ if sys.platform == 'win32' and sys.version_info < (3, 8):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
asyncio.run(self._run_tests(runners))
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 892d4d5..1127cc9 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -138,7 +138,7 @@ class OptionInterpreter:
elif isinstance(arg, mparser.ArithmeticNode):
l = self.reduce_single(arg.left)
r = self.reduce_single(arg.right)
- if not (arg.operation == 'add' and isinstance(l, str) and isinstance(r, str)):
+ if not (arg.operation == '+' and isinstance(l, str) and isinstance(r, str)):
raise OptionException('Only string concatenation with the "+" operator is allowed')
FeatureNew.single_use('string concatenation in meson_options.txt', '0.55.0', self.subproject)
return l + r
diff --git a/mesonbuild/options.py b/mesonbuild/options.py
index 3b7d8b2..8e29f29 100644
--- a/mesonbuild/options.py
+++ b/mesonbuild/options.py
@@ -60,8 +60,8 @@ DEFAULT_YIELDING = False
# Can't bind this near the class method it seems, sadly.
_T = T.TypeVar('_T')
-backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode', 'none']
-genvslitelist = ['vs2022']
+backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'vs2026', 'xcode', 'none']
+genvslitelist = ['vs2022', 'vs2026']
buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
# This is copied from coredata. There is no way to share this, because this
@@ -105,6 +105,7 @@ _BUILTIN_NAMES = {
'pkg_config_path',
'cmake_prefix_path',
'vsenv',
+ 'os2_emxomf',
}
_BAD_VALUE = 'Qwert Zuiopü'
@@ -310,7 +311,7 @@ class OptionKey:
return self.machine is MachineChoice.BUILD
if T.TYPE_CHECKING:
- OptionStringLikeDict: TypeAlias = T.Dict[T.Union[OptionKey, str], str]
+ OptionDict: TypeAlias = T.Dict[OptionKey, ElementaryOptionValues]
@dataclasses.dataclass
class UserOption(T.Generic[_T], HoldableObject):
@@ -321,13 +322,20 @@ class UserOption(T.Generic[_T], HoldableObject):
yielding: bool = DEFAULT_YIELDING
deprecated: DeprecatedType = False
readonly: bool = dataclasses.field(default=False)
+ parent: T.Optional[UserOption] = None
def __post_init__(self, value_: _T) -> None:
self.value = self.validate_value(value_)
# Final isn't technically allowed in a __post_init__ method
self.default: Final[_T] = self.value # type: ignore[misc]
- def listify(self, value: T.Any) -> T.List[T.Any]:
+ def listify(self, value: ElementaryOptionValues) -> T.List[str]:
+ if isinstance(value, list):
+ return value
+ if isinstance(value, bool):
+ return ['true'] if value else ['false']
+ if isinstance(value, int):
+ return [str(value)]
return [value]
def printable_value(self) -> ElementaryOptionValues:
@@ -340,10 +348,10 @@ class UserOption(T.Generic[_T], HoldableObject):
# Check that the input is a valid value and return the
# "cleaned" or "native" version. For example the Boolean
# option could take the string "true" and return True.
- def validate_value(self, value: T.Any) -> _T:
+ def validate_value(self, value: object) -> _T:
raise RuntimeError('Derived option class did not override validate_value.')
- def set_value(self, newvalue: T.Any) -> bool:
+ def set_value(self, newvalue: object) -> bool:
oldvalue = self.value
self.value = self.validate_value(newvalue)
return self.value != oldvalue
@@ -361,7 +369,7 @@ class EnumeratedUserOption(UserOption[_T]):
class UserStringOption(UserOption[str]):
- def validate_value(self, value: T.Any) -> str:
+ def validate_value(self, value: object) -> str:
if not isinstance(value, str):
raise MesonException(f'The value of option "{self.name}" is "{value}", which is not a string.')
return value
@@ -374,7 +382,7 @@ class UserBooleanOption(EnumeratedUserOption[bool]):
def __bool__(self) -> bool:
return self.value
- def validate_value(self, value: T.Any) -> bool:
+ def validate_value(self, value: object) -> bool:
if isinstance(value, bool):
return value
if not isinstance(value, str):
@@ -406,7 +414,7 @@ class _UserIntegerBase(UserOption[_T]):
def printable_choices(self) -> T.Optional[T.List[str]]:
return [self.__choices]
- def validate_value(self, value: T.Any) -> _T:
+ def validate_value(self, value: object) -> _T:
if isinstance(value, str):
value = T.cast('_T', self.toint(value))
if not isinstance(value, int):
@@ -450,7 +458,7 @@ class UserUmaskOption(_UserIntegerBase[T.Union["Literal['preserve']", OctalInt]]
return format(self.value, '04o')
return self.value
- def validate_value(self, value: T.Any) -> T.Union[Literal['preserve'], OctalInt]:
+ def validate_value(self, value: object) -> T.Union[Literal['preserve'], OctalInt]:
if value == 'preserve':
return 'preserve'
return OctalInt(super().validate_value(value))
@@ -465,7 +473,7 @@ class UserUmaskOption(_UserIntegerBase[T.Union["Literal['preserve']", OctalInt]]
@dataclasses.dataclass
class UserComboOption(EnumeratedUserOption[str]):
- def validate_value(self, value: T.Any) -> str:
+ def validate_value(self, value: object) -> str:
if value not in self.choices:
if isinstance(value, bool):
_type = 'boolean'
@@ -503,13 +511,13 @@ class UserArrayOption(UserOption[T.List[_T]]):
@dataclasses.dataclass
class UserStringArrayOption(UserArrayOption[str]):
- def listify(self, value: T.Any) -> T.List[T.Any]:
+ def listify(self, value: object) -> T.List[str]:
try:
return listify_array_value(value, self.split_args)
except MesonException as e:
raise MesonException(f'error in option "{self.name}": {e!s}')
- def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]:
+ def validate_value(self, value: object) -> T.List[str]:
newvalue = self.listify(value)
if not self.allow_dups and len(set(newvalue)) != len(newvalue):
@@ -606,11 +614,14 @@ class UserStdOption(UserComboOption):
else:
self.choices += gnu_stds_map.keys()
- def validate_value(self, value: T.Union[str, T.List[str]]) -> str:
+ def validate_value(self, value: object) -> str:
try:
candidates = listify_array_value(value)
except MesonException as e:
raise MesonException(f'error in option "{self.name}": {e!s}')
+ for std in candidates:
+ if not isinstance(std, str):
+ raise MesonException(f'String array element "{candidates!s}" for option "{self.name}" is not a string.')
unknown = ','.join(std for std in candidates if std not in self.all_stds)
if unknown:
raise MesonException(f'Unknown option "{self.name}" value {unknown}. Possible values are {self.all_stds}.')
@@ -705,25 +716,25 @@ BUILTIN_CORE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = {
),
UserComboOption('buildtype', 'Build type to use', 'debug', choices=buildtypelist),
UserBooleanOption('debug', 'Enable debug symbols and other information', True),
- UserComboOption('default_library', 'Default library type', 'shared', choices=['shared', 'static', 'both'],
- yielding=False),
+ UserComboOption('default_library', 'Default library type', 'shared', choices=['shared', 'static', 'both']),
UserComboOption('default_both_libraries', 'Default library type for both_libraries', 'shared',
choices=['shared', 'static', 'auto']),
UserBooleanOption('errorlogs', "Whether to print the logs from failing tests", True),
UserUmaskOption('install_umask', 'Default umask to apply on permissions of installed files', OctalInt(0o022)),
UserComboOption('layout', 'Build directory layout', 'mirror', choices=['mirror', 'flat']),
+ UserComboOption('namingscheme', 'How target file names are formed', 'classic', choices=['platform', 'classic']),
UserComboOption('optimization', 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's']),
UserBooleanOption('prefer_static', 'Whether to try static linking before shared linking', False),
UserBooleanOption('stdsplit', 'Split stdout and stderr in test logs', True),
UserBooleanOption('strip', 'Strip targets on install', False),
UserComboOption('unity', 'Unity build', 'off', choices=['on', 'off', 'subprojects']),
UserIntegerOption('unity_size', 'Unity block size', 4, min_value=2),
- UserComboOption('warning_level', 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'],
- yielding=False),
- UserBooleanOption('werror', 'Treat warnings as errors', False, yielding=False),
+ UserComboOption('warning_level', 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything']),
+ UserBooleanOption('werror', 'Treat warnings as errors', False),
UserComboOption('wrap_mode', 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote']),
UserStringArrayOption('force_fallback_for', 'Force fallback for those subprojects', []),
UserBooleanOption('vsenv', 'Activate Visual Studio environment', False, readonly=True),
+ UserBooleanOption('os2_emxomf', 'Use OMF format on OS/2', False),
# Pkgconfig module
UserBooleanOption('pkgconfig.relocatable', 'Generate pkgconfig files as relocatable', False),
@@ -735,6 +746,7 @@ BUILTIN_CORE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = {
UserStringOption('python.platlibdir', 'Directory for site-specific, platform-specific files.', ''),
UserStringOption('python.purelibdir', 'Directory for site-specific, non-platform-specific files.', ''),
UserBooleanOption('python.allow_limited_api', 'Whether to allow use of the Python Limited API', True),
+ UserStringOption('python.build_config', 'Config file containing the build details for the target Python installation.', ''),
])
}
@@ -796,21 +808,19 @@ class OptionStore:
def __init__(self, is_cross: bool) -> None:
self.options: T.Dict['OptionKey', 'AnyOptionType'] = {}
+ self.subprojects: T.Set[str] = set()
self.project_options: T.Set[OptionKey] = set()
self.module_options: T.Set[OptionKey] = set()
from .compilers import all_languages
self.all_languages = set(all_languages)
- self.project_options = set()
- self.augments: T.Dict[str, str] = {}
+ self.augments: OptionDict = {}
self.is_cross = is_cross
- # Pending options are options that need to be initialized later, either
- # configuration dependent options like compiler options, or options for
- # a different subproject
- self.pending_options: T.Dict[OptionKey, ElementaryOptionValues] = {}
-
- def clear_pending(self) -> None:
- self.pending_options = {}
+ # Pending options are configuration dependent options that could be
+ # initialized later, such as compiler options
+ self.pending_options: OptionDict = {}
+ # Subproject options from toplevel project()
+ self.pending_subproject_options: OptionDict = {}
def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey:
if isinstance(key, str):
@@ -829,51 +839,47 @@ class OptionStore:
key = key.as_host()
return key
- def get_value(self, key: T.Union[OptionKey, str]) -> ElementaryOptionValues:
- return self.get_value_object(key).value
+ def get_pending_value(self, key: T.Union[OptionKey, str], default: T.Optional[ElementaryOptionValues] = None) -> ElementaryOptionValues:
+ key = self.ensure_and_validate_key(key)
+ if key in self.options:
+ return self.options[key].value
+ return self.pending_options.get(key, default)
def __len__(self) -> int:
return len(self.options)
- def get_value_object_for(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType:
+ def resolve_option(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType:
key = self.ensure_and_validate_key(key)
potential = self.options.get(key, None)
if self.is_project_option(key):
assert key.subproject is not None
- if potential is not None and potential.yielding:
- parent_key = key.as_root()
- try:
- parent_option = self.options[parent_key]
- except KeyError:
- # Subproject is set to yield, but top level
- # project does not have an option of the same
- # name. Return the subproject option.
- return potential
- # If parent object has different type, do not yield.
- # This should probably be an error.
- if type(parent_option) is type(potential):
- return parent_option
- return potential
if potential is None:
raise KeyError(f'Tried to access nonexistant project option {key}.')
- return potential
else:
if potential is None:
parent_key = OptionKey(key.name, subproject=None, machine=key.machine)
if parent_key not in self.options:
raise KeyError(f'Tried to access nonexistant project parent option {parent_key}.')
+ # This is a global option but it can still have per-project
+ # augment, so return the subproject key.
return self.options[parent_key]
- return potential
+ return potential
- def get_value_object_and_value_for(self, key: OptionKey) -> T.Tuple[AnyOptionType, ElementaryOptionValues]:
+ def get_option_and_value_for(self, key: OptionKey) -> T.Tuple[AnyOptionType, ElementaryOptionValues]:
assert isinstance(key, OptionKey)
- vobject = self.get_value_object_for(key)
- computed_value = vobject.value
- if key.subproject is not None:
- keystr = str(key)
- if keystr in self.augments:
- computed_value = vobject.validate_value(self.augments[keystr])
- return (vobject, computed_value)
+ key = self.ensure_and_validate_key(key)
+ option_object = self.resolve_option(key)
+ computed_value = option_object.value
+ if key in self.augments:
+ assert key.subproject is not None
+ computed_value = self.augments[key]
+ elif option_object.yielding:
+ computed_value = option_object.parent.value
+ return (option_object, computed_value)
+
+ def option_has_value(self, key: OptionKey, value: ElementaryOptionValues) -> bool:
+ option_object, current_value = self.get_option_and_value_for(key)
+ return option_object.validate_value(value) == current_value
def get_value_for(self, name: 'T.Union[OptionKey, str]', subproject: T.Optional[str] = None) -> ElementaryOptionValues:
if isinstance(name, str):
@@ -881,7 +887,7 @@ class OptionStore:
else:
assert subproject is None
key = name
- vobject, resolved_value = self.get_value_object_and_value_for(key)
+ _, resolved_value = self.get_option_and_value_for(key)
return resolved_value
def add_system_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None:
@@ -897,14 +903,12 @@ class OptionStore:
if key in self.options:
return
- self.options[key] = valobj
pval = self.pending_options.pop(key, None)
if key.subproject:
proj_key = key.evolve(subproject=None)
self.add_system_option_internal(proj_key, valobj)
- if pval is None:
- pval = self.options[proj_key].value
-
+ else:
+ self.options[key] = valobj
if pval is not None:
self.set_option(key, pval)
@@ -919,12 +923,23 @@ class OptionStore:
assert key.subproject is not None
if key in self.options:
raise MesonException(f'Internal error: tried to add a project option {key} that already exists.')
+ if valobj.yielding and key.subproject:
+ parent_key = key.as_root()
+ try:
+ parent_option = self.options[parent_key]
+ # If parent object has different type, do not yield.
+ # This should probably be an error.
+ if type(parent_option) is type(valobj):
+ valobj.parent = parent_option
+ except KeyError:
+ # Subproject is set to yield, but top level
+ # project does not have an option of the same
+ pass
+ valobj.yielding = valobj.parent is not None
self.options[key] = valobj
self.project_options.add(key)
- pval = self.pending_options.pop(key, None)
- if pval is not None:
- self.set_option(key, pval)
+ assert key not in self.pending_options
def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None:
key = self.ensure_and_validate_key(key)
@@ -935,6 +950,27 @@ class OptionStore:
self.add_system_option_internal(key, valobj)
self.module_options.add(key)
+ def add_builtin_option(self, key: OptionKey, opt: AnyOptionType) -> None:
+ # Create a copy of the object, as we're going to mutate it
+ opt = copy.copy(opt)
+ assert key.subproject is None
+ new_value = argparse_prefixed_default(opt, key, default_prefix())
+ opt.set_value(new_value)
+
+ modulename = key.get_module_prefix()
+ if modulename:
+ self.add_module_option(modulename, key, opt)
+ else:
+ self.add_system_option(key, opt)
+
+ def init_builtins(self) -> None:
+ # Create builtin options with default values
+ for key, opt in BUILTIN_OPTIONS.items():
+ self.add_builtin_option(key, opt)
+ for for_machine in iter(MachineChoice):
+ for key, opt in BUILTIN_OPTIONS_PER_MACHINE.items():
+ self.add_builtin_option(key.evolve(machine=for_machine), opt)
+
def sanitize_prefix(self, prefix: str) -> str:
prefix = os.path.expanduser(prefix)
if not os.path.isabs(prefix):
@@ -985,6 +1021,11 @@ class OptionStore:
return value.as_posix()
def set_option(self, key: OptionKey, new_value: ElementaryOptionValues, first_invocation: bool = False) -> bool:
+ changed = False
+ error_key = key
+ if error_key.subproject == '':
+ error_key = error_key.evolve(subproject=None)
+
if key.name == 'prefix':
assert isinstance(new_value, str), 'for mypy'
new_value = self.sanitize_prefix(new_value)
@@ -994,85 +1035,119 @@ class OptionStore:
new_value = self.sanitize_dir_option_value(prefix, key, new_value)
try:
- opt = self.get_value_object_for(key)
+ opt = self.resolve_option(key)
except KeyError:
- raise MesonException(f'Unknown options: "{key!s}" not found.')
+ raise MesonException(f'Unknown option: "{error_key}".')
if opt.deprecated is True:
- mlog.deprecation(f'Option {key.name!r} is deprecated')
+ mlog.deprecation(f'Option "{error_key}" is deprecated')
elif isinstance(opt.deprecated, list):
for v in opt.listify(new_value):
if v in opt.deprecated:
- mlog.deprecation(f'Option {key.name!r} value {v!r} is deprecated')
+ mlog.deprecation(f'Option "{error_key}" value {v!r} is deprecated')
elif isinstance(opt.deprecated, dict):
- def replace(v: T.Any) -> T.Any:
+ def replace(v: str) -> str:
assert isinstance(opt.deprecated, dict) # No, Mypy can not tell this from two lines above
newvalue = opt.deprecated.get(v)
if newvalue is not None:
- mlog.deprecation(f'Option {key.name!r} value {v!r} is replaced by {newvalue!r}')
+ mlog.deprecation(f'Option "{error_key}" value {v!r} is replaced by {newvalue!r}')
return newvalue
return v
valarr = [replace(v) for v in opt.listify(new_value)]
new_value = ','.join(valarr)
elif isinstance(opt.deprecated, str):
- mlog.deprecation(f'Option {key.name!r} is replaced by {opt.deprecated!r}')
+ mlog.deprecation(f'Option "{error_key}" is replaced by {opt.deprecated!r}')
# Change both this aption and the new one pointed to.
- dirty = self.set_option(key.evolve(name=opt.deprecated), new_value)
- dirty |= opt.set_value(new_value)
- return dirty
+ changed |= self.set_option(key.evolve(name=opt.deprecated), new_value, first_invocation)
- old_value = opt.value
- changed = opt.set_value(new_value)
+ new_value = opt.validate_value(new_value)
+ if key in self.options:
+ old_value = opt.value
+ opt.set_value(new_value)
+ opt.yielding = False
+ else:
+ assert key.subproject is not None
+ old_value = self.augments.get(key, opt.value)
+ self.augments[key] = new_value
+ changed |= old_value != new_value
if opt.readonly and changed and not first_invocation:
- raise MesonException(f'Tried to modify read only option {str(key)!r}')
+ raise MesonException(f'Tried to modify read only option "{error_key}"')
if key.name == 'prefix' and first_invocation and changed:
assert isinstance(old_value, str), 'for mypy'
assert isinstance(new_value, str), 'for mypy'
self.reset_prefixed_options(old_value, new_value)
- if changed and key.name == 'buildtype':
+ if changed and key.name == 'buildtype' and new_value != 'custom':
assert isinstance(new_value, str), 'for mypy'
optimization, debug = self.DEFAULT_DEPENDENTS[new_value]
dkey = key.evolve(name='debug')
optkey = key.evolve(name='optimization')
- self.options[dkey].set_value(debug)
- self.options[optkey].set_value(optimization)
+ self.set_option(dkey, debug, first_invocation)
+ self.set_option(optkey, optimization, first_invocation)
return changed
- def set_option_from_string(self, keystr: T.Union[OptionKey, str], new_value: str) -> bool:
- if isinstance(keystr, OptionKey):
- o = keystr
- else:
- o = OptionKey.from_string(keystr)
+ def set_user_option(self, o: OptionKey, new_value: ElementaryOptionValues, first_invocation: bool = False) -> bool:
+ if not self.is_cross and o.is_for_build():
+ return False
+
+ # This is complicated by the fact that a string can have two meanings:
+ #
+ # default_options: 'foo=bar'
+ #
+ # can be either
+ #
+ # A) a system option in which case the subproject is None
+ # B) a project option, in which case the subproject is ''
+ #
+ # The key parsing function can not handle the difference between the two
+ # and defaults to A.
if o in self.options:
- return self.set_option(o, new_value)
- o = o.as_root()
- return self.set_option(o, new_value)
+ return self.set_option(o, new_value, first_invocation)
+
+ # could also be an augment...
+ global_option = o.evolve(subproject=None)
+ if o.subproject is not None and global_option in self.options:
+ return self.set_option(o, new_value, first_invocation)
+
+ if self.accept_as_pending_option(o, first_invocation=first_invocation):
+ old_value = self.pending_options.get(o, None)
+ self.pending_options[o] = new_value
+ return old_value is None or str(old_value) != new_value
+ elif o.subproject is None:
+ o = o.as_root()
+ return self.set_option(o, new_value, first_invocation)
+ else:
+ raise MesonException(f'Unknown option: "{o}".')
- def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool:
+ def set_from_configure_command(self, D_args: T.Dict[OptionKey, T.Optional[str]]) -> bool:
dirty = False
- D_args = [] if D_args is None else D_args
- (global_options, perproject_global_options, project_options) = self.classify_D_arguments(D_args)
- U_args = [] if U_args is None else U_args
- for key, valstr in global_options:
- dirty |= self.set_option_from_string(key, valstr)
- for key, valstr in project_options:
- dirty |= self.set_option_from_string(key, valstr)
- for keystr, valstr in perproject_global_options:
- if keystr in self.augments:
- if self.augments[keystr] != valstr:
- self.augments[keystr] = valstr
- dirty = True
- else:
- self.augments[keystr] = valstr
- dirty = True
- for delete in U_args:
- if delete in self.augments:
- del self.augments[delete]
+ for key, valstr in D_args.items():
+ if valstr is not None:
+ dirty |= self.set_user_option(key, valstr)
+ continue
+
+ if key in self.augments:
+ del self.augments[key]
dirty = True
+ else:
+ if key not in self.options:
+ raise MesonException(f"Unknown option: {key}")
+
+ # TODO: For project options, "dropping an augment" means going
+ # back to the superproject's value. However, it's confusing
+ # that -U does not simply remove the option from the stored
+ # cmd_line_options. This may cause "meson setup --wipe" to
+ # have surprising behavior. For this to work, UserOption
+ # should only store the default value and the option values
+ # should be stored with their source (project(), subproject(),
+ # machine file, command line). This way the effective value
+ # can be easily recomputed.
+ opt = self.get_value_object(key)
+ dirty |= not opt.yielding and bool(opt.parent)
+ opt.yielding = bool(opt.parent)
return dirty
def reset_prefixed_options(self, old_prefix: str, new_prefix: str) -> None:
@@ -1090,22 +1165,10 @@ class OptionStore:
new_value = prefix_mapping[new_prefix]
valobj.set_value(new_value)
- # FIXME, this should be removed.or renamed to "change_type_of_existing_object" or something like that
- def set_value_object(self, key: T.Union[OptionKey, str], new_object: AnyOptionType) -> None:
- key = self.ensure_and_validate_key(key)
- self.options[key] = new_object
-
def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType:
key = self.ensure_and_validate_key(key)
return self.options[key]
- def get_default_for_b_option(self, key: OptionKey) -> ElementaryOptionValues:
- assert self.is_base_option(key)
- try:
- return T.cast('ElementaryOptionValues', COMPILER_BASE_OPTIONS[key.evolve(subproject=None)].default)
- except KeyError:
- raise MesonBugException(f'Requested base option {key} which does not exist.')
-
def remove(self, key: OptionKey) -> None:
del self.options[key]
try:
@@ -1129,16 +1192,6 @@ class OptionStore:
def items(self) -> T.ItemsView['OptionKey', 'AnyOptionType']:
return self.options.items()
- # FIXME: this method must be deleted and users moved to use "add_xxx_option"s instead.
- def update(self, **kwargs: AnyOptionType) -> None:
- self.options.update(**kwargs)
-
- def setdefault(self, k: OptionKey, o: AnyOptionType) -> AnyOptionType:
- return self.options.setdefault(k, o)
-
- def get(self, o: OptionKey, default: T.Optional[AnyOptionType] = None, **kwargs: T.Any) -> T.Optional[AnyOptionType]:
- return self.options.get(o, default, **kwargs)
-
def is_project_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a project option."""
return key in self.project_options
@@ -1169,7 +1222,8 @@ class OptionStore:
def is_base_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a base option."""
- return key.name.startswith('b_')
+ # The "startswith" check is just an optimization
+ return key.name.startswith('b_') and key.evolve(subproject=None, machine=MachineChoice.HOST) in COMPILER_BASE_OPTIONS
def is_backend_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a backend option."""
@@ -1193,64 +1247,25 @@ class OptionStore:
def is_module_option(self, key: OptionKey) -> bool:
return key in self.module_options
- def classify_D_arguments(self, D: T.List[str]) -> T.Tuple[T.List[T.Tuple[OptionKey, str]],
- T.List[T.Tuple[str, str]],
- T.List[T.Tuple[OptionKey, str]]]:
- global_options = []
- project_options = []
- perproject_global_options = []
- for setval in D:
- keystr, valstr = setval.split('=', 1)
- key = OptionKey.from_string(keystr)
- valuetuple = (key, valstr)
- if self.is_project_option(key):
- project_options.append(valuetuple)
- elif key.subproject is None:
- global_options.append(valuetuple)
- else:
- # FIXME, augments are currently stored as strings, not OptionKeys
- strvaluetuple = (keystr, valstr)
- perproject_global_options.append(strvaluetuple)
- return (global_options, perproject_global_options, project_options)
-
- def optlist2optdict(self, optlist: T.List[str]) -> T.Dict[str, str]:
- optdict = {}
- for p in optlist:
- k, v = p.split('=', 1)
- optdict[k] = v
- return optdict
-
- def prefix_split_options(self, coll: T.Union[T.List[str], OptionStringLikeDict]) -> T.Tuple[str, T.Union[T.List[str], OptionStringLikeDict]]:
+ def prefix_split_options(self, coll: OptionDict) -> T.Tuple[T.Optional[str], OptionDict]:
prefix = None
- if isinstance(coll, list):
- others: T.List[str] = []
- for e in coll:
- if e.startswith('prefix='):
- prefix = e.split('=', 1)[1]
- else:
- others.append(e)
- return (prefix, others)
- else:
- others_d: OptionStringLikeDict = {}
- for k, v in coll.items():
- if isinstance(k, OptionKey) and k.name == 'prefix':
- prefix = v
- elif k == 'prefix':
- prefix = v
- else:
- others_d[k] = v
- return (prefix, others_d)
+ others_d: OptionDict = {}
+ for k, v in coll.items():
+ if k.name == 'prefix':
+ if not isinstance(v, str):
+ raise MesonException('Incorrect type for prefix option (expected string)')
+ prefix = v
+ else:
+ others_d[k] = v
+ return (prefix, others_d)
def first_handle_prefix(self,
- project_default_options: T.Union[T.List[str], OptionStringLikeDict],
- cmd_line_options: OptionStringLikeDict,
- machine_file_options: T.Mapping[OptionKey, ElementaryOptionValues]) \
- -> T.Tuple[T.Union[T.List[str], OptionStringLikeDict],
- T.Union[T.List[str], OptionStringLikeDict],
- T.MutableMapping[OptionKey, ElementaryOptionValues]]:
+ project_default_options: OptionDict,
+ cmd_line_options: OptionDict,
+ machine_file_options: OptionDict) \
+ -> T.Tuple[OptionDict, OptionDict, OptionDict]:
# Copy to avoid later mutation
- nopref_machine_file_options = T.cast(
- 'T.MutableMapping[OptionKey, ElementaryOptionValues]', copy.copy(machine_file_options))
+ nopref_machine_file_options = copy.copy(machine_file_options)
prefix = None
(possible_prefix, nopref_project_default_options) = self.prefix_split_options(project_default_options)
@@ -1281,160 +1296,115 @@ class OptionStore:
self.options[OptionKey('prefix')].set_value(prefix)
def initialize_from_top_level_project_call(self,
- project_default_options_in: T.Union[T.List[str], OptionStringLikeDict],
- cmd_line_options_in: OptionStringLikeDict,
- machine_file_options_in: T.Mapping[OptionKey, ElementaryOptionValues]) -> None:
- first_invocation = True
+ project_default_options_in: OptionDict,
+ cmd_line_options_in: OptionDict,
+ machine_file_options_in: OptionDict) -> None:
(project_default_options, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in,
cmd_line_options_in,
machine_file_options_in)
- if isinstance(project_default_options, str):
- project_default_options = [project_default_options]
- if isinstance(project_default_options, list):
- project_default_options = self.optlist2optdict(project_default_options) # type: ignore [assignment]
- if project_default_options is None:
- project_default_options = {}
- assert isinstance(machine_file_options, dict)
- for keystr, valstr in machine_file_options.items():
- if isinstance(keystr, str):
- # FIXME, standardise on Key or string.
- key = OptionKey.from_string(keystr)
- else:
- key = keystr
- # Due to backwards compatibility we ignore all build-machine options
- # when building natively.
- if not self.is_cross and key.is_for_build():
- continue
- if key.subproject:
- augstr = str(key)
- self.augments[augstr] = valstr
- elif key in self.options:
- self.set_option(key, valstr, first_invocation)
- else:
- proj_key = key.as_root()
- if proj_key in self.options:
- self.set_option(proj_key, valstr, first_invocation)
- else:
- self.pending_options[key] = valstr
- assert isinstance(project_default_options, dict)
- for keystr, valstr in project_default_options.items():
- # Ths is complicated by the fact that a string can have two meanings:
- #
- # default_options: 'foo=bar'
- #
- # can be either
- #
- # A) a system option in which case the subproject is None
- # B) a project option, in which case the subproject is '' (this method is only called from top level)
- #
- # The key parsing function can not handle the difference between the two
- # and defaults to A.
- if isinstance(keystr, str):
- key = OptionKey.from_string(keystr)
- else:
- key = keystr
+ for key, valstr in project_default_options.items():
# Due to backwards compatibility we ignore build-machine options
# when building natively.
if not self.is_cross and key.is_for_build():
continue
if key.subproject:
- self.pending_options[key] = valstr
- elif key in self.options:
- self.set_option(key, valstr, first_invocation)
+ # Subproject options from toplevel project() have low priority
+ # and will be processed when the subproject is found
+ self.pending_subproject_options[key] = valstr
else:
- # Setting a project option with default_options.
- # Argubly this should be a hard error, the default
+ # Setting a project option with default_options
+ # should arguably be a hard error; the default
# value of project option should be set in the option
# file, not in the project call.
- proj_key = key.as_root()
- if self.is_project_option(proj_key):
- self.set_option(proj_key, valstr)
- else:
- self.pending_options[key] = valstr
- assert isinstance(cmd_line_options, dict)
- for keystr, valstr in cmd_line_options.items():
- if isinstance(keystr, str):
- key = OptionKey.from_string(keystr)
- else:
- key = keystr
+ self.set_user_option(key, valstr, True)
+
+ # ignore subprojects for now for machine file and command line
+ # options; they are applied later
+ for key, valstr in itertools.chain(machine_file_options.items(), cmd_line_options.items()):
# Due to backwards compatibility we ignore all build-machine options
# when building natively.
if not self.is_cross and key.is_for_build():
continue
- if key.subproject:
- self.pending_options[key] = valstr
- elif key in self.options:
- self.set_option(key, valstr, True)
- else:
- proj_key = key.as_root()
- if proj_key in self.options:
- self.set_option(proj_key, valstr, True)
- else:
- self.pending_options[key] = valstr
+ if not key.subproject:
+ self.set_user_option(key, valstr, True)
- def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> None:
- unknown_options = []
- for keystr, valstr in cmd_line_options.items():
- if isinstance(keystr, str):
- key = OptionKey.from_string(keystr)
- else:
- key = keystr
- # Fail on unknown options that we can know must exist at this point in time.
- # Subproject and compiler options are resolved later.
- #
- # Some base options (sanitizers etc) might get added later.
- # Permitting them all is not strictly correct.
- if key.subproject is None and not self.is_compiler_option(key) and not self.is_base_option(key) and \
- key in self.pending_options:
- unknown_options.append(f'"{key}"')
-
- if unknown_options:
- keys = ', '.join(unknown_options)
- raise MesonException(f'Unknown options: {keys}')
-
- def hacky_mchackface_back_to_list(self, optdict: T.Dict[str, str]) -> T.List[str]:
- if isinstance(optdict, dict):
- return [f'{k}={v}' for k, v in optdict.items()]
- return optdict
+ def accept_as_pending_option(self, key: OptionKey, first_invocation: bool = False) -> bool:
+ # Some base options (sanitizers etc) might get added later.
+ # Permitting them all is not strictly correct.
+ if self.is_compiler_option(key):
+ return True
+ if first_invocation and self.is_backend_option(key):
+ return True
+ return self.is_base_option(key)
def initialize_from_subproject_call(self,
subproject: str,
- spcall_default_options: T.Union[T.List[str], OptionStringLikeDict],
- project_default_options: T.Union[T.List[str], OptionStringLikeDict],
- cmd_line_options: T.Union[T.List[str], OptionStringLikeDict]) -> None:
- is_first_invocation = True
- spcall_default_options = self.hacky_mchackface_back_to_list(spcall_default_options) # type: ignore [arg-type]
- project_default_options = self.hacky_mchackface_back_to_list(project_default_options) # type: ignore [arg-type]
- if isinstance(spcall_default_options, str):
- spcall_default_options = [spcall_default_options]
- for o in itertools.chain(project_default_options, spcall_default_options):
- keystr, valstr = o.split('=', 1)
- key = OptionKey.from_string(keystr)
- assert key.subproject is None
- key = key.evolve(subproject=subproject)
- # If the key points to a project option, set the value from that.
- # Otherwise set an augment.
- if key in self.project_options:
- self.set_option(key, valstr, is_first_invocation)
- else:
- self.pending_options.pop(key, None)
- aug_str = f'{subproject}:{keystr}'
- self.augments[aug_str] = valstr
- # Check for pending options
- assert isinstance(cmd_line_options, dict)
- for key, valstr in cmd_line_options.items(): # type: ignore [assignment]
- if not isinstance(key, OptionKey):
- key = OptionKey.from_string(key)
+ spcall_default_options: OptionDict,
+ project_default_options: OptionDict,
+ cmd_line_options: OptionDict,
+ machine_file_options: OptionDict) -> None:
+
+ options: OptionDict = {}
+
+ # project() default_options
+ for key, valstr in project_default_options.items():
+ if key.subproject == subproject:
+ without_subp = key.evolve(subproject=None)
+ raise MesonException(f'subproject name not needed in default_options; use "{without_subp}" instead of "{key}"')
+
+ if key.subproject is None:
+ key = key.evolve(subproject=subproject)
+ options[key] = valstr
+
+ # then global settings from machine file and command line
+ # **but not if they are toplevel project options**
+ for key, valstr in itertools.chain(machine_file_options.items(), cmd_line_options.items()):
+ if key.subproject is None and not self.is_project_option(key.as_root()):
+ subp_key = key.evolve(subproject=subproject)
+ # just leave in place the value that was set for the toplevel project
+ options.pop(subp_key, None)
+
+ # augments from the toplevel project() default_options
+ for key, valstr in self.pending_subproject_options.items():
+ if key.subproject == subproject:
+ options[key] = valstr
+
+ # subproject() default_options
+ for key, valstr in spcall_default_options.items():
+ if key.subproject == subproject:
+ without_subp = key.evolve(subproject=None)
+ raise MesonException(f'subproject name not needed in default_options; use "{without_subp}" instead of "{key}"')
+
+ if key.subproject is None:
+ key = key.evolve(subproject=subproject)
+ options[key] = valstr
+
+ # then finally per project augments from machine file and command line
+ for key, valstr in itertools.chain(machine_file_options.items(), cmd_line_options.items()):
+ if key.subproject == subproject:
+ options[key] = valstr
+
+ # merge everything that has been computed above, while giving self.augments priority
+ for key, valstr in options.items():
if key.subproject != subproject:
+ if key.subproject in self.subprojects and not self.option_has_value(key, valstr):
+ mlog.warning('option {key} is set in subproject {subproject} but has already been processed')
+ continue
+
+ # Subproject options from project() will be processed when the subproject is found
+ self.pending_subproject_options[key] = valstr
continue
+
+ self.pending_subproject_options.pop(key, None)
self.pending_options.pop(key, None)
- if key in self.options:
- self.set_option(key, valstr, is_first_invocation)
- else:
- self.augments[str(key)] = valstr
+ if key not in self.augments:
+ self.set_user_option(key, valstr, True)
+
+ self.subprojects.add(subproject)
def update_project_options(self, project_options: MutableKeyedOptionDictType, subproject: SubProject) -> None:
for key, value in project_options.items():
+ assert key.machine is MachineChoice.HOST
if key not in self.options:
self.add_project_option(key, value)
continue
@@ -1448,7 +1418,7 @@ class OptionStore:
# If the choices have changed, use the new value, but attempt
# to keep the old options. If they are not valid keep the new
# defaults but warn.
- self.set_value_object(key, value)
+ self.options[key] = value
try:
value.set_value(oldval.value)
except MesonException:
diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py
index d01440c..a1cfc06 100644
--- a/mesonbuild/programs.py
+++ b/mesonbuild/programs.py
@@ -322,6 +322,11 @@ class ExternalProgram(mesonlib.HoldableObject):
paths = OrderedSet(path.split(os.pathsep)).difference(exclude_paths)
path = os.pathsep.join(paths)
command = shutil.which(name, path=path)
+ if not command and mesonlib.is_os2():
+ for ext in ['exe', 'cmd']:
+ command = shutil.which(f'{name}.{ext}', path=path)
+ if command:
+ return [command]
if mesonlib.is_windows():
return self._search_windows_special_cases(name, command, exclude_paths)
# On UNIX-like platforms, shutil.which() is enough to find
@@ -364,9 +369,9 @@ class OverrideProgram(ExternalProgram):
def __init__(self, name: str, version: str, command: T.Optional[T.List[str]] = None,
silent: bool = False, search_dirs: T.Optional[T.List[T.Optional[str]]] = None,
exclude_paths: T.Optional[T.List[str]] = None):
- self.cached_version = version
super().__init__(name, command=command, silent=silent,
search_dirs=search_dirs, exclude_paths=exclude_paths)
+ self.cached_version = version
def find_external_program(env: 'Environment', for_machine: MachineChoice, name: str,
display_name: str, default_names: T.List[str],
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index 919bd38..ac23bd4 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -10,23 +10,29 @@
from __future__ import annotations
from .ast import IntrospectionInterpreter, BUILD_TARGET_FUNCTIONS, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstPrinter
-from mesonbuild.mesonlib import MesonException, setup_vsenv
+from .ast.interpreter import IntrospectionBuildTarget, IntrospectionDependency, _symbol
+from .interpreterbase import UnknownValue, TV_func
+from .interpreterbase.helpers import flatten
+from mesonbuild.mesonlib import MesonException, setup_vsenv, relpath
from . import mlog, environment
from functools import wraps
-from .mparser import Token, ArrayNode, ArgumentNode, AssignmentNode, StringNode, BooleanNode, ElementaryNode, IdNode, FunctionNode, SymbolNode
-import json, os, re, sys
+from .mparser import Token, ArrayNode, ArgumentNode, ArithmeticNode, AssignmentNode, BaseNode, StringNode, BooleanNode, DictNode, ElementaryNode, IdNode, FunctionNode, PlusAssignmentNode
+from .mintro import IntrospectionEncoder
+import json, os, re, sys, codecs
import typing as T
+from pathlib import Path
if T.TYPE_CHECKING:
- from argparse import ArgumentParser, HelpFormatter
- from .mparser import BaseNode
+ import argparse
+ from argparse import ArgumentParser, _FormatterClass
+ from .mlog import AnsiDecorator
class RewriterException(MesonException):
pass
# Note: when adding arguments, please also add them to the completion
# scripts in $MESONSRC/data/shell-completions/
-def add_arguments(parser: ArgumentParser, formatter: T.Callable[[str], HelpFormatter]) -> None:
+def add_arguments(parser: ArgumentParser, formatter: _FormatterClass) -> None:
parser.add_argument('-s', '--sourcedir', type=str, default='.', metavar='SRCDIR', help='Path to source directory.')
parser.add_argument('-V', '--verbose', action='store_true', default=False, help='Enable verbose output')
parser.add_argument('-S', '--skip-errors', dest='skip', action='store_true', default=False, help='Skip errors instead of aborting')
@@ -48,26 +54,28 @@ def add_arguments(parser: ArgumentParser, formatter: T.Callable[[str], HelpForma
help='Action to execute')
kw_parser.add_argument('function', choices=list(rewriter_func_kwargs.keys()),
help='Function type to modify')
- kw_parser.add_argument('id', help='ID of the function to modify (can be anything for "project")')
- kw_parser.add_argument('kwargs', nargs='*', help='Pairs of keyword and value')
+ kw_parser.add_argument('id', help='ID of the function to modify (must be "/" for "project")')
+ kw_parser.add_argument('kwargs', nargs='*', help='<keyword> <value> pairs, or list of <keyword> for "delete"')
# Default options
def_parser = subparsers.add_parser('default-options', aliases=['def'], help='Modify the project default options', formatter_class=formatter)
def_parser.add_argument('operation', choices=rewriter_keys['default_options']['operation'][2],
help='Action to execute')
- def_parser.add_argument('options', nargs='*', help='Key, value pairs of configuration option')
+ def_parser.add_argument('options', nargs='*', help='<key> <value> pairs for "set"; list of <key> for "delete"')
# JSON file/command
cmd_parser = subparsers.add_parser('command', aliases=['cmd'], help='Execute a JSON array of commands', formatter_class=formatter)
cmd_parser.add_argument('json', help='JSON string or file to execute')
class RequiredKeys:
- def __init__(self, keys):
+ keys: T.Dict[str, T.Any]
+
+ def __init__(self, keys: T.Dict[str, T.Any]):
self.keys = keys
- def __call__(self, f):
+ def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
- def wrapped(*wrapped_args, **wrapped_kwargs):
+ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
assert len(wrapped_args) >= 2
cmd = wrapped_args[1]
for key, val in self.keys.items():
@@ -90,12 +98,11 @@ class RequiredKeys:
.format(key, choices, cmd[key]))
return f(*wrapped_args, **wrapped_kwargs)
- return wrapped
-
-def _symbol(val: str) -> SymbolNode:
- return SymbolNode(Token('', '', 0, 0, 0, (0, 0), val))
+ return T.cast('TV_func', wrapped)
class MTypeBase:
+ node: BaseNode
+
def __init__(self, node: T.Optional[BaseNode] = None):
if node is None:
self.node = self.new_node()
@@ -107,30 +114,30 @@ class MTypeBase:
self.node_type = i
@classmethod
- def new_node(cls, value=None):
+ def new_node(cls, value: T.Any = None) -> BaseNode:
# Overwrite in derived class
raise RewriterException('Internal error: new_node of MTypeBase was called')
@classmethod
- def supported_nodes(cls):
+ def supported_nodes(cls) -> T.List[type]:
# Overwrite in derived class
return []
- def can_modify(self):
+ def can_modify(self) -> bool:
return self.node_type is not None
- def get_node(self):
+ def get_node(self) -> BaseNode:
return self.node
- def add_value(self, value):
+ def add_value(self, value: T.Any) -> None:
# Overwrite in derived class
mlog.warning('Cannot add a value of type', mlog.bold(type(self).__name__), '--> skipping')
- def remove_value(self, value):
+ def remove_value(self, value: T.Any) -> None:
# Overwrite in derived class
mlog.warning('Cannot remove a value of type', mlog.bold(type(self).__name__), '--> skipping')
- def remove_regex(self, value):
+ def remove_regex(self, value: T.Any) -> None:
# Overwrite in derived class
mlog.warning('Cannot remove a regex in type', mlog.bold(type(self).__name__), '--> skipping')
@@ -139,13 +146,13 @@ class MTypeStr(MTypeBase):
super().__init__(node)
@classmethod
- def new_node(cls, value=None):
+ def new_node(cls, value: T.Optional[str] = None) -> BaseNode:
if value is None:
value = ''
return StringNode(Token('string', '', 0, 0, 0, None, str(value)))
@classmethod
- def supported_nodes(cls):
+ def supported_nodes(cls) -> T.List[type]:
return [StringNode]
class MTypeBool(MTypeBase):
@@ -153,11 +160,11 @@ class MTypeBool(MTypeBase):
super().__init__(node)
@classmethod
- def new_node(cls, value=None):
+ def new_node(cls, value: T.Optional[str] = None) -> BaseNode:
return BooleanNode(Token('', '', 0, 0, 0, None, bool(value)))
@classmethod
- def supported_nodes(cls):
+ def supported_nodes(cls) -> T.List[type]:
return [BooleanNode]
class MTypeID(MTypeBase):
@@ -165,21 +172,23 @@ class MTypeID(MTypeBase):
super().__init__(node)
@classmethod
- def new_node(cls, value=None):
+ def new_node(cls, value: T.Optional[str] = None) -> BaseNode:
if value is None:
value = ''
return IdNode(Token('', '', 0, 0, 0, None, str(value)))
@classmethod
- def supported_nodes(cls):
+ def supported_nodes(cls) -> T.List[type]:
return [IdNode]
class MTypeList(MTypeBase):
+ node: ArrayNode
+
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
@classmethod
- def new_node(cls, value=None):
+ def new_node(cls, value: T.Optional[T.List[T.Any]] = None) -> ArrayNode:
if value is None:
value = []
elif not isinstance(value, list):
@@ -189,50 +198,52 @@ class MTypeList(MTypeBase):
return ArrayNode(_symbol('['), args, _symbol(']'))
@classmethod
- def _new_element_node(cls, value):
+ def _new_element_node(cls, value: T.Any) -> BaseNode:
# Overwrite in derived class
raise RewriterException('Internal error: _new_element_node of MTypeList was called')
- def _ensure_array_node(self):
+ def _ensure_array_node(self) -> None:
if not isinstance(self.node, ArrayNode):
tmp = self.node
self.node = self.new_node()
self.node.args.arguments = [tmp]
@staticmethod
- def _check_is_equal(node, value) -> bool:
+ def _check_is_equal(node: BaseNode, value: str) -> bool:
# Overwrite in derived class
return False
@staticmethod
- def _check_regex_matches(node, regex: str) -> bool:
+ def _check_regex_matches(node: BaseNode, regex: str) -> bool:
# Overwrite in derived class
return False
- def get_node(self):
+ def get_node(self) -> BaseNode:
if isinstance(self.node, ArrayNode):
if len(self.node.args.arguments) == 1:
return self.node.args.arguments[0]
return self.node
@classmethod
- def supported_element_nodes(cls):
+ def supported_element_nodes(cls) -> T.List[T.Type]:
# Overwrite in derived class
return []
@classmethod
- def supported_nodes(cls):
+ def supported_nodes(cls) -> T.List[T.Type]:
return [ArrayNode] + cls.supported_element_nodes()
- def add_value(self, value):
+ def add_value(self, value: T.Any) -> None:
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
for i in value:
+ assert hasattr(self.node, 'args') # For mypy
+ assert isinstance(self.node.args, ArgumentNode) # For mypy
self.node.args.arguments += [self._new_element_node(i)]
- def _remove_helper(self, value, equal_func):
- def check_remove_node(node):
+ def _remove_helper(self, value: T.Any, equal_func: T.Callable[[T.Any, T.Any], bool]) -> None:
+ def check_remove_node(node: BaseNode) -> bool:
for j in value:
if equal_func(i, j):
return True
@@ -241,16 +252,18 @@ class MTypeList(MTypeBase):
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
+ assert hasattr(self.node, 'args') # For mypy
+ assert isinstance(self.node.args, ArgumentNode) # For mypy
removed_list = []
for i in self.node.args.arguments:
if not check_remove_node(i):
removed_list += [i]
self.node.args.arguments = removed_list
- def remove_value(self, value):
+ def remove_value(self, value: T.Any) -> None:
self._remove_helper(value, self._check_is_equal)
- def remove_regex(self, regex: str):
+ def remove_regex(self, regex: str) -> None:
self._remove_helper(regex, self._check_regex_matches)
class MTypeStrList(MTypeList):
@@ -258,23 +271,23 @@ class MTypeStrList(MTypeList):
super().__init__(node)
@classmethod
- def _new_element_node(cls, value):
+ def _new_element_node(cls, value: str) -> StringNode:
return StringNode(Token('string', '', 0, 0, 0, None, str(value)))
@staticmethod
- def _check_is_equal(node, value) -> bool:
+ def _check_is_equal(node: BaseNode, value: str) -> bool:
if isinstance(node, StringNode):
- return node.value == value
+ return bool(node.value == value)
return False
@staticmethod
- def _check_regex_matches(node, regex: str) -> bool:
+ def _check_regex_matches(node: BaseNode, regex: str) -> bool:
if isinstance(node, StringNode):
return re.match(regex, node.value) is not None
return False
@classmethod
- def supported_element_nodes(cls):
+ def supported_element_nodes(cls) -> T.List[T.Type]:
return [StringNode]
class MTypeIDList(MTypeList):
@@ -282,26 +295,26 @@ class MTypeIDList(MTypeList):
super().__init__(node)
@classmethod
- def _new_element_node(cls, value):
+ def _new_element_node(cls, value: str) -> IdNode:
return IdNode(Token('', '', 0, 0, 0, None, str(value)))
@staticmethod
- def _check_is_equal(node, value) -> bool:
+ def _check_is_equal(node: BaseNode, value: str) -> bool:
if isinstance(node, IdNode):
- return node.value == value
+ return bool(node.value == value)
return False
@staticmethod
- def _check_regex_matches(node, regex: str) -> bool:
+ def _check_regex_matches(node: BaseNode, regex: str) -> bool:
if isinstance(node, StringNode):
return re.match(regex, node.value) is not None
return False
@classmethod
- def supported_element_nodes(cls):
+ def supported_element_nodes(cls) -> T.List[T.Type]:
return [IdNode]
-rewriter_keys = {
+rewriter_keys: T.Dict[str, T.Dict[str, T.Any]] = {
'default_options': {
'operation': (str, None, ['set', 'delete']),
'options': (dict, {}, None)
@@ -349,19 +362,22 @@ rewriter_func_kwargs = {
'default_options': MTypeStrList,
'meson_version': MTypeStr,
'license': MTypeStrList,
+ 'license_files': MTypeStrList,
'subproject_dir': MTypeStr,
'version': MTypeStr
}
}
class Rewriter:
+ info_dump: T.Optional[T.Dict[str, T.Dict[str, T.Any]]]
+
def __init__(self, sourcedir: str, generator: str = 'ninja', skip_errors: bool = False):
self.sourcedir = sourcedir
self.interpreter = IntrospectionInterpreter(sourcedir, '', generator, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()])
self.skip_errors = skip_errors
- self.modified_nodes = []
- self.to_remove_nodes = []
- self.to_add_nodes = []
+ self.modified_nodes: T.List[BaseNode] = []
+ self.to_remove_nodes: T.List[BaseNode] = []
+ self.to_add_nodes: T.List[BaseNode] = []
self.functions = {
'default_options': self.process_default_options,
'kwargs': self.process_kwargs,
@@ -369,89 +385,99 @@ class Rewriter:
}
self.info_dump = None
- def analyze_meson(self):
+ def analyze_meson(self) -> None:
mlog.log('Analyzing meson file:', mlog.bold(os.path.join(self.sourcedir, environment.build_filename)))
self.interpreter.analyze()
mlog.log(' -- Project:', mlog.bold(self.interpreter.project_data['descriptive_name']))
mlog.log(' -- Version:', mlog.cyan(self.interpreter.project_data['version']))
- def add_info(self, cmd_type: str, cmd_id: str, data: dict):
+ def add_info(self, cmd_type: str, cmd_id: str, data: dict) -> None:
if self.info_dump is None:
self.info_dump = {}
if cmd_type not in self.info_dump:
self.info_dump[cmd_type] = {}
self.info_dump[cmd_type][cmd_id] = data
- def print_info(self):
+ def print_info(self) -> None:
if self.info_dump is None:
return
- sys.stdout.write(json.dumps(self.info_dump, indent=2))
+ sys.stdout.write(json.dumps(self.info_dump, indent=2, cls=IntrospectionEncoder))
- def on_error(self):
+ def on_error(self) -> T.Tuple[AnsiDecorator, AnsiDecorator]:
if self.skip_errors:
return mlog.cyan('-->'), mlog.yellow('skipping')
return mlog.cyan('-->'), mlog.red('aborting')
- def handle_error(self):
+ def handle_error(self) -> None:
if self.skip_errors:
return None
raise MesonException('Rewriting the meson.build failed')
- def find_target(self, target: str):
- def check_list(name: str) -> T.List[BaseNode]:
- result = []
- for i in self.interpreter.targets:
- if name in {i['name'], i['id']}:
- result += [i]
- return result
-
- targets = check_list(target)
- if targets:
- if len(targets) == 1:
- return targets[0]
- else:
- mlog.error('There are multiple targets matching', mlog.bold(target))
- for i in targets:
- mlog.error(' -- Target name', mlog.bold(i['name']), 'with ID', mlog.bold(i['id']))
- mlog.error('Please try again with the unique ID of the target', *self.on_error())
- self.handle_error()
- return None
-
- # Check the assignments
- tgt = None
- if target in self.interpreter.assignments:
- node = self.interpreter.assignments[target]
- if isinstance(node, FunctionNode):
- if node.func_name.value in {'executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries'}:
- tgt = self.interpreter.assign_vals[target]
-
- return tgt
-
- def find_dependency(self, dependency: str):
- def check_list(name: str):
- for i in self.interpreter.dependencies:
- if name == i['name']:
- return i
+ def all_assignments(self, varname: str) -> T.List[BaseNode]:
+ assigned_values = []
+ for ass in self.interpreter.all_assignment_nodes[varname]:
+ if isinstance(ass, PlusAssignmentNode):
+ continue
+ assert isinstance(ass, AssignmentNode)
+ assigned_values.append(ass.value)
+ return assigned_values
+
+ def find_target(self, target: str) -> T.Optional[IntrospectionBuildTarget]:
+ for i in self.interpreter.targets:
+ if target == i.id:
+ return i
+
+ potential_tgts = []
+ for i in self.interpreter.targets:
+ if target == i.name:
+ potential_tgts.append(i)
+
+ if not potential_tgts:
+ potenial_tgts_1 = self.all_assignments(target)
+ potenial_tgts_1 = [self.interpreter.node_to_runtime_value(el) for el in potenial_tgts_1]
+ potential_tgts = [el for el in potenial_tgts_1 if isinstance(el, IntrospectionBuildTarget)]
+
+ if not potential_tgts:
+ return None
+ elif len(potential_tgts) == 1:
+ return potential_tgts[0]
+ else:
+ mlog.error('There are multiple targets matching', mlog.bold(target))
+ for i in potential_tgts:
+ mlog.error(' -- Target name', mlog.bold(i.name), 'with ID', mlog.bold(i.id))
+ mlog.error('Please try again with the unique ID of the target', *self.on_error())
+ self.handle_error()
return None
- dep = check_list(dependency)
- if dep is not None:
- return dep
+ def find_dependency(self, dependency: str) -> T.Optional[IntrospectionDependency]:
+ potential_deps = []
+ for i in self.interpreter.dependencies:
+ if i.name == dependency:
+ potential_deps.append(i)
- # Check the assignments
- if dependency in self.interpreter.assignments:
- node = self.interpreter.assignments[dependency]
- if isinstance(node, FunctionNode):
- if node.func_name.value == 'dependency':
- name = self.interpreter.flatten_args(node.args)[0]
- dep = check_list(name)
+ checking_varnames = len(potential_deps) == 0
- return dep
+ if checking_varnames:
+ potential_deps1 = self.all_assignments(dependency)
+ potential_deps = [self.interpreter.node_to_runtime_value(el) for el in potential_deps1 if isinstance(el, FunctionNode) and el.func_name.value == 'dependency']
+
+ if not potential_deps:
+ return None
+ elif len(potential_deps) == 1:
+ return potential_deps[0]
+ else:
+ mlog.error('There are multiple dependencies matching', mlog.bold(dependency))
+ for i in potential_deps:
+ mlog.error(' -- Dependency name', i)
+ if checking_varnames:
+ mlog.error('Please try again with the name of the dependency', *self.on_error())
+ self.handle_error()
+ return None
@RequiredKeys(rewriter_keys['default_options'])
- def process_default_options(self, cmd):
+ def process_default_options(self, cmd: T.Dict[str, T.Any]) -> None:
# First, remove the old values
- kwargs_cmd = {
+ kwargs_cmd: T.Dict[str, T.Any] = {
'function': 'project',
'id': "/",
'operation': 'remove_regex',
@@ -471,10 +497,6 @@ class Rewriter:
cdata = self.interpreter.coredata
options = {
**{str(k): v for k, v in cdata.optstore.items()},
- **{str(k): v for k, v in cdata.optstore.items()},
- **{str(k): v for k, v in cdata.optstore.items()},
- **{str(k): v for k, v in cdata.optstore.items()},
- **{str(k): v for k, v in cdata.optstore.items()},
}
for key, val in sorted(cmd['options'].items()):
@@ -495,7 +517,7 @@ class Rewriter:
self.process_kwargs(kwargs_cmd)
@RequiredKeys(rewriter_keys['kwargs'])
- def process_kwargs(self, cmd):
+ def process_kwargs(self, cmd: T.Dict[str, T.Any]) -> None:
mlog.log('Processing function type', mlog.bold(cmd['function']), 'with id', mlog.cyan("'" + cmd['id'] + "'"))
if cmd['function'] not in rewriter_func_kwargs:
mlog.error('Unknown function type', cmd['function'], *self.on_error())
@@ -516,26 +538,31 @@ class Rewriter:
node = self.interpreter.project_node
arg_node = node.args
elif cmd['function'] == 'target':
- tmp = self.find_target(cmd['id'])
- if tmp:
- node = tmp['node']
- arg_node = node.args
+ tmp_tgt = self.find_target(cmd['id'])
+ if not tmp_tgt:
+ mlog.error('Unable to find the target', mlog.bold(cmd['id']), *self.on_error())
+ return self.handle_error()
+ node = tmp_tgt.node
+ arg_node = node.args
elif cmd['function'] == 'dependency':
- tmp = self.find_dependency(cmd['id'])
- if tmp:
- node = tmp['node']
- arg_node = node.args
+ tmp_dep = self.find_dependency(cmd['id'])
+ if not tmp_dep:
+ mlog.error('Unable to find the dependency', mlog.bold(cmd['id']), *self.on_error())
+ return self.handle_error()
+ node = tmp_dep.node
+ arg_node = node.args
if not node:
- mlog.error('Unable to find the function node')
+ mlog.error('Unable to find the function node', *self.on_error())
+ return self.handle_error()
assert isinstance(node, FunctionNode)
assert isinstance(arg_node, ArgumentNode)
# Transform the key nodes to plain strings
- arg_node.kwargs = {k.value: v for k, v in arg_node.kwargs.items()}
+ kwargs = {T.cast(IdNode, k).value: v for k, v in arg_node.kwargs.items()}
# Print kwargs info
if cmd['operation'] == 'info':
- info_data = {}
- for key, val in sorted(arg_node.kwargs.items()):
+ info_data: T.Dict[str, T.Any] = {}
+ for key, val in sorted(kwargs.items()):
info_data[key] = None
if isinstance(val, ElementaryNode):
info_data[key] = val.value
@@ -547,6 +574,16 @@ class Rewriter:
element = i.value
data_list += [element]
info_data[key] = data_list
+ elif isinstance(val, DictNode):
+ data_dict = {}
+ for k, v in val.args.kwargs.items():
+ if not isinstance(k, StringNode):
+ continue
+ value = None
+ if isinstance(v, ElementaryNode):
+ value = v.value
+ data_dict[k.value] = value
+ info_data[key] = data_dict
self.add_info('kwargs', '{}#{}'.format(cmd['function'], cmd['id']), info_data)
return # Nothing else to do
@@ -561,21 +598,21 @@ class Rewriter:
if cmd['operation'] == 'delete':
# Remove the key from the kwargs
- if key not in arg_node.kwargs:
+ if key not in kwargs:
mlog.log(' -- Key', mlog.bold(key), 'is already deleted')
continue
mlog.log(' -- Deleting', mlog.bold(key), 'from the kwargs')
- del arg_node.kwargs[key]
+ del kwargs[key]
elif cmd['operation'] == 'set':
# Replace the key from the kwargs
mlog.log(' -- Setting', mlog.bold(key), 'to', mlog.yellow(str(val)))
- arg_node.kwargs[key] = kwargs_def[key].new_node(val)
+ kwargs[key] = kwargs_def[key].new_node(val)
else:
# Modify the value from the kwargs
- if key not in arg_node.kwargs:
- arg_node.kwargs[key] = None
- modifier = kwargs_def[key](arg_node.kwargs[key])
+ if key not in kwargs:
+ kwargs[key] = None
+ modifier = kwargs_def[key](kwargs[key])
if not modifier.can_modify():
mlog.log(' -- Skipping', mlog.bold(key), 'because it is too complex to modify')
continue
@@ -593,24 +630,251 @@ class Rewriter:
modifier.remove_regex(val)
# Write back the result
- arg_node.kwargs[key] = modifier.get_node()
+ kwargs[key] = modifier.get_node()
num_changed += 1
# Convert the keys back to IdNode's
- arg_node.kwargs = {IdNode(Token('', '', 0, 0, 0, None, k)): v for k, v in arg_node.kwargs.items()}
+ arg_node.kwargs = {IdNode(Token('', '', 0, 0, 0, None, k)): v for k, v in kwargs.items()}
for k, v in arg_node.kwargs.items():
k.level = v.level
if num_changed > 0 and node not in self.modified_nodes:
self.modified_nodes += [node]
- def find_assignment_node(self, node: BaseNode) -> AssignmentNode:
- if node.ast_id and node.ast_id in self.interpreter.reverse_assignment:
- return self.interpreter.reverse_assignment[node.ast_id]
+ def find_assignment_node(self, node: BaseNode) -> T.Optional[AssignmentNode]:
+ for k, v in self.interpreter.all_assignment_nodes.items():
+ for ass in v:
+ if ass.value == node:
+ return ass
return None
+ def affects_no_other_targets(self, candidate: BaseNode) -> bool:
+ affected = self.interpreter.dataflow_dag.reachable({candidate}, False)
+ affected_targets = [x for x in affected if isinstance(x, FunctionNode) and x.func_name.value in BUILD_TARGET_FUNCTIONS]
+ return len(affected_targets) == 1
+
+ def get_relto(self, target_node: BaseNode, node: BaseNode) -> Path:
+ cwd = Path(os.getcwd())
+ all_paths = self.interpreter.dataflow_dag.find_all_paths(node, target_node)
+ # len(all_paths) == 0 would imply that data does not flow from node to
+ # target_node. This would imply that adding sources to node would not
+ # add the source to the target.
+ assert all_paths
+ if len(all_paths) > 1:
+ return None
+ return (cwd / next(x for x in all_paths[0] if isinstance(x, FunctionNode)).filename).parent
+
+ def add_src_or_extra(self, op: str, target: IntrospectionBuildTarget, newfiles: T.List[str], to_sort_nodes: T.List[T.Union[FunctionNode, ArrayNode]]) -> None:
+ assert op in {'src_add', 'extra_files_add'}
+
+ if op == 'src_add':
+ old: T.Set[T.Union[BaseNode, UnknownValue]] = set(target.source_nodes)
+ elif op == 'extra_files_add':
+ if target.extra_files is None:
+ old = set()
+ else:
+ old = {target.extra_files}
+ tgt_function: FunctionNode = target.node
+
+ cwd = Path(os.getcwd())
+ target_dir_abs = cwd / os.path.dirname(target.node.filename)
+ source_root_abs = cwd / self.interpreter.source_root
+
+ candidates1 = self.interpreter.dataflow_dag.reachable(old, True)
+ # A node is a member of the set `candidates1` exactly if data from this node
+ # flow into one of the `dest` nodes. We assume that this implies that if we
+ # add `foo.c` to this node, then 'foo.c' will be added to one of these
+ # nodes. This assumption is not always true:
+ # ar = ['a.c', 'b.c']
+ # srcs = ar[1]
+ # executable('name', srcs)
+ # Data flows from `ar` to `srcs`, but if we add 'foo.c':
+ # ar = ['a.c', 'b.c', 'foo.c']
+ # srcs = ar[1]
+ # executable('name', srcs)
+ # this does not add 'foo.c' to `srcs`. This is a known bug/limitation of
+ # the meson rewriter that could be fixed by replacing `reachable` with a
+ # more advanced analysis. But this is a lot of work and I think e.g.
+ # `srcs = ar[1]` is rare in real-world projects, so I will just leave
+ # this for now.
+
+ candidates2 = {x for x in candidates1 if isinstance(x, (FunctionNode, ArrayNode))}
+
+ # If we have this meson.build file:
+ # shared = ['shared.c']
+ # executable('foo', shared + ['foo.c'])
+ # executable('bar', shared + ['bar.c'])
+ # and we are tasked with adding 'new.c' to 'foo', we should do e.g this:
+ # shared = ['shared.c']
+ # executable('foo', shared + ['foo.c', 'new.c'])
+ # executable('bar', shared + ['bar.c'])
+ # but never this:
+ # shared = ['shared.c', 'new.c']
+ # executable('foo', shared + ['foo.c'])
+ # executable('bar', shared + ['bar.c'])
+ # We do this by removing the `['shared.c']`-node from `candidates2`.
+ candidates2 = {x for x in candidates2 if self.affects_no_other_targets(x)}
+
+ def path_contains_unknowns(candidate: BaseNode) -> bool:
+ all_paths = self.interpreter.dataflow_dag.find_all_paths(candidate, target.node)
+ for path in all_paths:
+ for el in path:
+ if isinstance(el, UnknownValue):
+ return True
+ return False
+
+ candidates2 = {x for x in candidates2 if not path_contains_unknowns(x)}
+
+ candidates2 = {x for x in candidates2 if self.get_relto(target.node, x) is not None}
+
+ chosen: T.Union[FunctionNode, ArrayNode] = None
+ new_kwarg_flag = False
+ if len(candidates2) > 0:
+ # So that files(['a', 'b']) gets modified to files(['a', 'b', 'c']) instead of files(['a', 'b'], 'c')
+ if len({x for x in candidates2 if isinstance(x, ArrayNode)}) > 0:
+ candidates2 = {x for x in candidates2 if isinstance(x, ArrayNode)}
+
+ # We choose one more or less arbitrary candidate
+ chosen = min(candidates2, key=lambda x: (x.lineno, x.colno))
+ elif op == 'src_add':
+ chosen = target.node
+ elif op == 'extra_files_add':
+ chosen = ArrayNode(_symbol('['), ArgumentNode(Token('', tgt_function.filename, 0, 0, 0, None, '[]')), _symbol(']'))
+
+ # this is fundamentally error prone
+ self.interpreter.dataflow_dag.add_edge(chosen, target.node)
+
+ extra_files_idnode = IdNode(Token('string', tgt_function.filename, 0, 0, 0, None, 'extra_files'))
+ if tgt_function not in self.modified_nodes:
+ self.modified_nodes += [tgt_function]
+ new_extra_files_node: BaseNode
+ if target.node.args.get_kwarg_or_default('extra_files', None) is None:
+ # Target has no extra_files kwarg, create one
+ new_kwarg_flag = True
+ new_extra_files_node = chosen
+ else:
+ new_kwarg_flag = True
+ old_extra_files = target.node.args.get_kwarg_or_default('extra_files', None)
+ target.node.args.kwargs = {k: v for k, v in target.node.args.kwargs.items() if not (isinstance(k, IdNode) and k.value == 'extra_files')}
+ new_extra_files_node = ArithmeticNode('+', old_extra_files, _symbol('+'), chosen)
+
+ tgt_function.args.kwargs[extra_files_idnode] = new_extra_files_node
+
+ newfiles_relto = self.get_relto(target.node, chosen)
+ old_src_list: T.List[T.Any] = flatten([self.interpreter.node_to_runtime_value(sn) for sn in old])
+
+ if op == 'src_add':
+ name = 'Source'
+ elif op == 'extra_files_add':
+ name = 'Extra file'
+ # Generate the new String nodes
+ to_append = []
+ added = []
+
+ old_src_list = [(target_dir_abs / x).resolve() if isinstance(x, str) else x.to_abs_path(source_root_abs) for x in old_src_list if not isinstance(x, UnknownValue)]
+ for _newf in sorted(set(newfiles)):
+ newf = Path(_newf)
+ if os.path.isabs(newf):
+ newf = Path(newf)
+ else:
+ newf = source_root_abs / newf
+ if newf in old_src_list:
+ mlog.log(' -- ', name, mlog.green(str(newf)), 'is already defined for the target --> skipping')
+ continue
+
+ mlog.log(' -- Adding ', name.lower(), mlog.green(str(newf)), 'at',
+ mlog.yellow(f'{chosen.filename}:{chosen.lineno}'))
+ added.append(newf)
+ mocktarget = self.interpreter.funcvals[target.node]
+ assert isinstance(mocktarget, IntrospectionBuildTarget)
+ # print("adding ", str(newf), 'to', mocktarget.name) todo: should we write something to stderr?
+
+ path = relpath(newf, newfiles_relto)
+ path = codecs.encode(path, 'unicode_escape').decode() # Because the StringNode constructor does the inverse
+ token = Token('string', chosen.filename, 0, 0, 0, None, path)
+ to_append += [StringNode(token)]
+
+ assert isinstance(chosen, (FunctionNode, ArrayNode))
+ arg_node = chosen.args
+ # Append to the AST at the right place
+ arg_node.arguments += to_append
+
+ # Mark the node as modified
+ if chosen not in to_sort_nodes:
+ to_sort_nodes += [chosen]
+ # If the extra_files array is newly created, i.e. if new_kwarg_flag is
+ # True, don't mark it as its parent function node already is, otherwise
+ # this would cause double modification.
+ if chosen not in self.modified_nodes and not new_kwarg_flag:
+ self.modified_nodes += [chosen]
+
+ # Utility function to get a list of the sources from a node
+ def arg_list_from_node(self, n: BaseNode) -> T.List[BaseNode]:
+ args = []
+ if isinstance(n, FunctionNode):
+ args = list(n.args.arguments)
+ if n.func_name.value in BUILD_TARGET_FUNCTIONS:
+ args.pop(0)
+ elif isinstance(n, ArrayNode):
+ args = n.args.arguments
+ elif isinstance(n, ArgumentNode):
+ args = n.arguments
+ return args
+
+ def rm_src_or_extra(self, op: str, target: IntrospectionBuildTarget, to_be_removed: T.List[str], to_sort_nodes: T.List[T.Union[FunctionNode, ArrayNode]]) -> None:
+ assert op in {'src_rm', 'extra_files_rm'}
+ cwd = Path(os.getcwd())
+ source_root_abs = cwd / self.interpreter.source_root
+
+ # Helper to find the exact string node and its parent
+ def find_node(src: str) -> T.Tuple[T.Optional[BaseNode], T.Optional[StringNode]]:
+ if op == 'src_rm':
+ nodes = self.interpreter.dataflow_dag.reachable(set(target.source_nodes), True).union({target.node})
+ elif op == 'extra_files_rm':
+ nodes = self.interpreter.dataflow_dag.reachable({target.extra_files}, True)
+ for i in nodes:
+ if isinstance(i, UnknownValue):
+ continue
+ relto = self.get_relto(target.node, i)
+ if relto is not None:
+ for j in self.arg_list_from_node(i):
+ if isinstance(j, StringNode):
+ if os.path.normpath(relto / j.value) == os.path.normpath(source_root_abs / src):
+ return i, j
+ return None, None
+
+ if op == 'src_rm':
+ name = 'source'
+ elif op == 'extra_files_rm':
+ name = 'extra file'
+
+ for i in to_be_removed:
+ # Try to find the node with the source string
+ root, string_node = find_node(i)
+ if root is None:
+ mlog.warning(' -- Unable to find', name, mlog.green(i), 'in the target')
+ continue
+ if not self.affects_no_other_targets(string_node):
+ mlog.warning(' -- Removing the', name, mlog.green(i), 'is too compilicated')
+ continue
+
+ if not isinstance(root, (FunctionNode, ArrayNode)):
+ raise NotImplementedError # I'm lazy
+
+ # Remove the found string node from the argument list
+ arg_node = root.args
+ mlog.log(' -- Removing', name, mlog.green(i), 'from',
+ mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
+ arg_node.arguments.remove(string_node)
+
+ # Mark the node as modified
+ if root not in to_sort_nodes:
+ to_sort_nodes += [root]
+ if root not in self.modified_nodes:
+ self.modified_nodes += [root]
+
@RequiredKeys(rewriter_keys['target'])
- def process_target(self, cmd):
+ def process_target(self, cmd: T.Dict[str, T.Any]) -> None:
mlog.log('Processing target', mlog.bold(cmd['target']), 'operation', mlog.cyan(cmd['operation']))
target = self.find_target(cmd['target'])
if target is None and cmd['operation'] != 'target_add':
@@ -619,7 +883,7 @@ class Rewriter:
# Make source paths relative to the current subdir
def rel_source(src: str) -> str:
- subdir = os.path.abspath(os.path.join(self.sourcedir, target['subdir']))
+ subdir = os.path.abspath(os.path.join(self.sourcedir, target.subdir))
if os.path.isabs(src):
return os.path.relpath(src, subdir)
elif not os.path.exists(src):
@@ -630,180 +894,13 @@ class Rewriter:
if target is not None:
cmd['sources'] = [rel_source(x) for x in cmd['sources']]
- # Utility function to get a list of the sources from a node
- def arg_list_from_node(n):
- args = []
- if isinstance(n, FunctionNode):
- args = list(n.args.arguments)
- if n.func_name.value in BUILD_TARGET_FUNCTIONS:
- args.pop(0)
- elif isinstance(n, ArrayNode):
- args = n.args.arguments
- elif isinstance(n, ArgumentNode):
- args = n.arguments
- return args
-
- to_sort_nodes = []
-
- if cmd['operation'] == 'src_add':
- node = None
- if target['sources']:
- node = target['sources'][0]
- else:
- node = target['node']
- assert node is not None
-
- # Generate the current source list
- src_list = []
- for i in target['sources']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- src_list += [j.value]
-
- # Generate the new String nodes
- to_append = []
- for i in sorted(set(cmd['sources'])):
- if i in src_list:
- mlog.log(' -- Source', mlog.green(i), 'is already defined for the target --> skipping')
- continue
- mlog.log(' -- Adding source', mlog.green(i), 'at',
- mlog.yellow(f'{node.filename}:{node.lineno}'))
- token = Token('string', node.filename, 0, 0, 0, None, i)
- to_append += [StringNode(token)]
-
- # Append to the AST at the right place
- arg_node = None
- if isinstance(node, (FunctionNode, ArrayNode)):
- arg_node = node.args
- elif isinstance(node, ArgumentNode):
- arg_node = node
- assert arg_node is not None
- arg_node.arguments += to_append
-
- # Mark the node as modified
- if arg_node not in to_sort_nodes and not isinstance(node, FunctionNode):
- to_sort_nodes += [arg_node]
- if node not in self.modified_nodes:
- self.modified_nodes += [node]
-
- elif cmd['operation'] == 'src_rm':
- # Helper to find the exact string node and its parent
- def find_node(src):
- for i in target['sources']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- if j.value == src:
- return i, j
- return None, None
-
- for i in cmd['sources']:
- # Try to find the node with the source string
- root, string_node = find_node(i)
- if root is None:
- mlog.warning(' -- Unable to find source', mlog.green(i), 'in the target')
- continue
-
- # Remove the found string node from the argument list
- arg_node = None
- if isinstance(root, (FunctionNode, ArrayNode)):
- arg_node = root.args
- elif isinstance(root, ArgumentNode):
- arg_node = root
- assert arg_node is not None
- mlog.log(' -- Removing source', mlog.green(i), 'from',
- mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
- arg_node.arguments.remove(string_node)
-
- # Mark the node as modified
- if arg_node not in to_sort_nodes and not isinstance(root, FunctionNode):
- to_sort_nodes += [arg_node]
- if root not in self.modified_nodes:
- self.modified_nodes += [root]
-
- elif cmd['operation'] == 'extra_files_add':
- tgt_function: FunctionNode = target['node']
- mark_array = True
- try:
- node = target['extra_files'][0]
- except IndexError:
- # Specifying `extra_files` with a list that flattens to empty gives an empty
- # target['extra_files'] list, account for that.
- try:
- extra_files_key = next(k for k in tgt_function.args.kwargs.keys() if isinstance(k, IdNode) and k.value == 'extra_files')
- node = tgt_function.args.kwargs[extra_files_key]
- except StopIteration:
- # Target has no extra_files kwarg, create one
- node = ArrayNode(_symbol('['), ArgumentNode(Token('', tgt_function.filename, 0, 0, 0, None, '[]')), _symbol(']'))
- tgt_function.args.kwargs[IdNode(Token('string', tgt_function.filename, 0, 0, 0, None, 'extra_files'))] = node
- mark_array = False
- if tgt_function not in self.modified_nodes:
- self.modified_nodes += [tgt_function]
- target['extra_files'] = [node]
- if isinstance(node, IdNode):
- node = self.interpreter.assignments[node.value]
- target['extra_files'] = [node]
- if not isinstance(node, ArrayNode):
- mlog.error('Target', mlog.bold(cmd['target']), 'extra_files argument must be a list', *self.on_error())
- return self.handle_error()
-
- # Generate the current extra files list
- extra_files_list = []
- for i in target['extra_files']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- extra_files_list += [j.value]
-
- # Generate the new String nodes
- to_append = []
- for i in sorted(set(cmd['sources'])):
- if i in extra_files_list:
- mlog.log(' -- Extra file', mlog.green(i), 'is already defined for the target --> skipping')
- continue
- mlog.log(' -- Adding extra file', mlog.green(i), 'at',
- mlog.yellow(f'{node.filename}:{node.lineno}'))
- token = Token('string', node.filename, 0, 0, 0, None, i)
- to_append += [StringNode(token)]
-
- # Append to the AST at the right place
- arg_node = node.args
- arg_node.arguments += to_append
-
- # Mark the node as modified
- if arg_node not in to_sort_nodes:
- to_sort_nodes += [arg_node]
- # If the extra_files array is newly created, don't mark it as its parent function node already is,
- # otherwise this would cause double modification.
- if mark_array and node not in self.modified_nodes:
- self.modified_nodes += [node]
-
- elif cmd['operation'] == 'extra_files_rm':
- # Helper to find the exact string node and its parent
- def find_node(src):
- for i in target['extra_files']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- if j.value == src:
- return i, j
- return None, None
+ to_sort_nodes: T.List[T.Union[FunctionNode, ArrayNode]] = []
- for i in cmd['sources']:
- # Try to find the node with the source string
- root, string_node = find_node(i)
- if root is None:
- mlog.warning(' -- Unable to find extra file', mlog.green(i), 'in the target')
- continue
-
- # Remove the found string node from the argument list
- arg_node = root.args
- mlog.log(' -- Removing extra file', mlog.green(i), 'from',
- mlog.yellow(f'{string_node.filename}:{string_node.lineno}'))
- arg_node.arguments.remove(string_node)
+ if cmd['operation'] in {'src_add', 'extra_files_add'}:
+ self.add_src_or_extra(cmd['operation'], target, cmd['sources'], to_sort_nodes)
- # Mark the node as modified
- if arg_node not in to_sort_nodes and not isinstance(root, FunctionNode):
- to_sort_nodes += [arg_node]
- if root not in self.modified_nodes:
- self.modified_nodes += [root]
+ elif cmd['operation'] in {'src_rm', 'extra_files_rm'}:
+ self.rm_src_or_extra(cmd['operation'], target, cmd['sources'], to_sort_nodes)
elif cmd['operation'] == 'target_add':
if target is not None:
@@ -813,7 +910,7 @@ class Rewriter:
id_base = re.sub(r'[- ]', '_', cmd['target'])
target_id = id_base + '_exe' if cmd['target_type'] == 'executable' else '_lib'
source_id = id_base + '_sources'
- filename = os.path.join(cmd['subdir'], environment.build_filename)
+ filename = os.path.join(os.getcwd(), self.interpreter.source_root, cmd['subdir'], environment.build_filename)
# Build src list
src_arg_node = ArgumentNode(Token('string', filename, 0, 0, 0, None, ''))
@@ -838,44 +935,55 @@ class Rewriter:
self.to_add_nodes += [src_ass_node, tgt_ass_node]
elif cmd['operation'] == 'target_rm':
- to_remove = self.find_assignment_node(target['node'])
+ to_remove: BaseNode = self.find_assignment_node(target.node)
if to_remove is None:
- to_remove = target['node']
+ to_remove = target.node
self.to_remove_nodes += [to_remove]
mlog.log(' -- Removing target', mlog.green(cmd['target']), 'at',
mlog.yellow(f'{to_remove.filename}:{to_remove.lineno}'))
elif cmd['operation'] == 'info':
# T.List all sources in the target
- src_list = []
- for i in target['sources']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- src_list += [j.value]
- extra_files_list = []
- for i in target['extra_files']:
- for j in arg_list_from_node(i):
- if isinstance(j, StringNode):
- extra_files_list += [j.value]
+
+ cwd = Path(os.getcwd())
+ source_root_abs = cwd / self.interpreter.source_root
+
+ src_list = self.interpreter.nodes_to_pretty_filelist(source_root_abs, target.subdir, target.source_nodes)
+ extra_files_list = self.interpreter.nodes_to_pretty_filelist(source_root_abs, target.subdir, [target.extra_files] if target.extra_files else [])
+
+ src_list = ['unknown' if isinstance(x, UnknownValue) else relpath(x, source_root_abs) for x in src_list]
+ extra_files_list = ['unknown' if isinstance(x, UnknownValue) else relpath(x, source_root_abs) for x in extra_files_list]
+
test_data = {
- 'name': target['name'],
+ 'name': target.name,
'sources': src_list,
'extra_files': extra_files_list
}
- self.add_info('target', target['id'], test_data)
+ self.add_info('target', target.id, test_data)
# Sort files
for i in to_sort_nodes:
- convert = lambda text: int(text) if text.isdigit() else text.lower()
- alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
- path_sorter = lambda key: ([(key.count('/') <= idx, alphanum_key(x)) for idx, x in enumerate(key.split('/'))])
+ def convert(text: str) -> T.Union[int, str]:
+ return int(text) if text.isdigit() else text.lower()
+
+ def alphanum_key(key: str) -> T.List[T.Union[int, str]]:
+ return [convert(c) for c in re.split('([0-9]+)', key)]
+
+ def path_sorter(key: str) -> T.List[T.Tuple[bool, T.List[T.Union[int, str]]]]:
+ return [(key.count('/') <= idx, alphanum_key(x)) for idx, x in enumerate(key.split('/'))]
- unknown = [x for x in i.arguments if not isinstance(x, StringNode)]
- sources = [x for x in i.arguments if isinstance(x, StringNode)]
+ if isinstance(i, FunctionNode) and i.func_name.value in BUILD_TARGET_FUNCTIONS:
+ src_args = i.args.arguments[1:]
+ target_name = [i.args.arguments[0]]
+ else:
+ src_args = i.args.arguments
+ target_name = []
+ unknown: T.List[BaseNode] = [x for x in src_args if not isinstance(x, StringNode)]
+ sources: T.List[StringNode] = [x for x in src_args if isinstance(x, StringNode)]
sources = sorted(sources, key=lambda x: path_sorter(x.value))
- i.arguments = unknown + sources
+ i.args.arguments = target_name + unknown + T.cast(T.List[BaseNode], sources)
- def process(self, cmd):
+ def process(self, cmd: T.Dict[str, T.Any]) -> None:
if 'type' not in cmd:
raise RewriterException('Command has no key "type"')
if cmd['type'] not in self.functions:
@@ -883,7 +991,7 @@ class Rewriter:
.format(cmd['type'], list(self.functions.keys())))
self.functions[cmd['type']](cmd)
- def apply_changes(self):
+ def apply_changes(self) -> None:
assert all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'filename') for x in self.modified_nodes)
assert all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'filename') for x in self.to_remove_nodes)
assert all(isinstance(x, (ArrayNode, FunctionNode)) for x in self.modified_nodes)
@@ -891,7 +999,7 @@ class Rewriter:
# Sort based on line and column in reversed order
work_nodes = [{'node': x, 'action': 'modify'} for x in self.modified_nodes]
work_nodes += [{'node': x, 'action': 'rm'} for x in self.to_remove_nodes]
- work_nodes = sorted(work_nodes, key=lambda x: (x['node'].lineno, x['node'].colno), reverse=True)
+ work_nodes = sorted(work_nodes, key=lambda x: (T.cast(BaseNode, x['node']).lineno, T.cast(BaseNode, x['node']).colno), reverse=True)
work_nodes += [{'node': x, 'action': 'add'} for x in self.to_add_nodes]
# Generating the new replacement string
@@ -900,11 +1008,11 @@ class Rewriter:
new_data = ''
if i['action'] == 'modify' or i['action'] == 'add':
printer = AstPrinter()
- i['node'].accept(printer)
+ T.cast(BaseNode, i['node']).accept(printer)
printer.post_process()
new_data = printer.result.strip()
data = {
- 'file': i['node'].filename,
+ 'file': T.cast(BaseNode, i['node']).filename,
'str': new_data,
'node': i['node'],
'action': i['action']
@@ -912,11 +1020,11 @@ class Rewriter:
str_list += [data]
# Load build files
- files = {}
+ files: T.Dict[str, T.Any] = {}
for i in str_list:
if i['file'] in files:
continue
- fpath = os.path.realpath(os.path.join(self.sourcedir, i['file']))
+ fpath = os.path.realpath(T.cast(str, i['file']))
fdata = ''
# Create an empty file if it does not exist
if not os.path.exists(fpath):
@@ -933,14 +1041,14 @@ class Rewriter:
line_offsets += [offset]
offset += len(j)
- files[i['file']] = {
+ files[T.cast(str, i['file'])] = {
'path': fpath,
'raw': fdata,
'offsets': line_offsets
}
# Replace in source code
- def remove_node(i):
+ def remove_node(i: T.Dict[str, T.Any]) -> None:
offsets = files[i['file']]['offsets']
raw = files[i['file']]['raw']
node = i['node']
@@ -968,7 +1076,7 @@ class Rewriter:
if i['action'] in {'modify', 'rm'}:
remove_node(i)
elif i['action'] == 'add':
- files[i['file']]['raw'] += i['str'] + '\n'
+ files[T.cast(str, i['file'])]['raw'] += T.cast(str, i['str']) + '\n'
# Write the files back
for key, val in files.items():
@@ -996,10 +1104,17 @@ def list_to_dict(in_list: T.List[str]) -> T.Dict[str, str]:
# key value pairs.
result[i] = next(it)
except StopIteration:
- raise TypeError('in_list parameter of list_to_dict must have an even length.')
+ raise RewriterException('List of key/value pairs must have an even length.')
return result
-def generate_target(options) -> T.List[dict]:
+def list_to_dict_for_delete(args: T.List[str]) -> T.Dict[str, T.Optional[str]]:
+ if len(args) % 2 == 0 and all(a == '' for a in args[1::2]):
+ mlog.deprecation('Even-numbered arguments are all blank; '
+ 'ignoring these for compatibility with Meson < 1.10')
+ args = args[::2]
+ return {a: None for a in args}
+
+def generate_target(options: argparse.Namespace) -> T.List[T.Dict[str, T.Any]]:
return [{
'type': 'target',
'target': options.target,
@@ -1009,28 +1124,36 @@ def generate_target(options) -> T.List[dict]:
'target_type': options.tgt_type,
}]
-def generate_kwargs(options) -> T.List[dict]:
+def generate_kwargs(options: argparse.Namespace) -> T.List[T.Dict[str, T.Any]]:
+ if options.operation == 'delete':
+ kwargs = list_to_dict_for_delete(options.kwargs)
+ else:
+ kwargs = list_to_dict(options.kwargs)
return [{
'type': 'kwargs',
'function': options.function,
'id': options.id,
'operation': options.operation,
- 'kwargs': list_to_dict(options.kwargs),
+ 'kwargs': kwargs,
}]
-def generate_def_opts(options) -> T.List[dict]:
+def generate_def_opts(options: argparse.Namespace) -> T.List[T.Dict[str, T.Any]]:
+ if options.operation == 'delete':
+ kwargs = list_to_dict_for_delete(options.options)
+ else:
+ kwargs = list_to_dict(options.options)
return [{
'type': 'default_options',
'operation': options.operation,
- 'options': list_to_dict(options.options),
+ 'options': kwargs,
}]
-def generate_cmd(options) -> T.List[dict]:
+def generate_cmd(options: argparse.Namespace) -> T.List[T.Dict[str, T.Any]]:
if os.path.exists(options.json):
with open(options.json, encoding='utf-8') as fp:
- return json.load(fp)
+ return T.cast(T.List[T.Dict[str, T.Any]], json.load(fp))
else:
- return json.loads(options.json)
+ return T.cast(T.List[T.Dict[str, T.Any]], json.loads(options.json))
# Map options.type to the actual type name
cli_type_map = {
@@ -1043,7 +1166,7 @@ cli_type_map = {
'cmd': generate_cmd,
}
-def run(options):
+def run(options: argparse.Namespace) -> int:
mlog.redirect(True)
if not options.verbose:
mlog.set_quiet()
@@ -1062,12 +1185,22 @@ def run(options):
if not isinstance(commands, list):
raise TypeError('Command is not a list')
- for i in commands:
- if not isinstance(i, object):
+ for i, cmd in enumerate(commands):
+ if not isinstance(cmd, object):
raise TypeError('Command is not an object')
- rewriter.process(i)
+ rewriter.process(cmd)
+ rewriter.apply_changes()
+
+ if i == len(commands) - 1: # Improves the performance, is not necessary for correctness.
+ break
+
+ rewriter.modified_nodes = []
+ rewriter.to_remove_nodes = []
+ rewriter.to_add_nodes = []
+ # The AST changed, so we need to update every information that was derived from the AST
+ rewriter.interpreter = IntrospectionInterpreter(rewriter.sourcedir, '', rewriter.interpreter.backend, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()])
+ rewriter.analyze_meson()
- rewriter.apply_changes()
rewriter.print_info()
return 0
except Exception as e:
diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py
index a3c19e9..281cb10 100644
--- a/mesonbuild/scripts/clangformat.py
+++ b/mesonbuild/scripts/clangformat.py
@@ -8,7 +8,7 @@ from pathlib import Path
import sys
from .run_tool import run_clang_tool, run_with_buffered_output
-from ..environment import detect_clangformat
+from ..tooldetect import detect_clangformat
from ..mesonlib import version_compare
from ..programs import ExternalProgram
import typing as T
diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py
index 550faee..da4d454 100644
--- a/mesonbuild/scripts/clangtidy.py
+++ b/mesonbuild/scripts/clangtidy.py
@@ -11,8 +11,8 @@ import os
import shutil
import sys
-from .run_tool import run_clang_tool, run_with_buffered_output
-from ..environment import detect_clangtidy, detect_clangapply
+from .run_tool import run_with_buffered_output, run_clang_tool_on_sources
+from ..tooldetect import detect_clangtidy, detect_clangapply
import typing as T
async def run_clang_tidy(fname: Path, tidyexe: list, builddir: Path, fixesdir: T.Optional[Path]) -> int:
@@ -56,7 +56,7 @@ def run(args: T.List[str]) -> int:
fixesdir.unlink()
fixesdir.mkdir(parents=True)
- tidyret = run_clang_tool('clang-tidy', srcdir, builddir, run_clang_tidy, tidyexe, builddir, fixesdir)
+ tidyret = run_clang_tool_on_sources('clang-tidy', srcdir, builddir, run_clang_tidy, tidyexe, builddir, fixesdir)
if fixesdir is not None:
print('Applying fix-its...')
applyret = subprocess.run(applyexe + ['-format', '-style=file', '-ignore-insert-conflict', fixesdir]).returncode
diff --git a/mesonbuild/scripts/clippy.py b/mesonbuild/scripts/clippy.py
index 6d282e4..0ea7a34 100644
--- a/mesonbuild/scripts/clippy.py
+++ b/mesonbuild/scripts/clippy.py
@@ -23,7 +23,7 @@ class ClippyDriver:
compilers = build.environment.coredata.compilers[machine]
if 'rust' in compilers:
compiler = T.cast('RustCompiler', compilers['rust'])
- self.tools[machine] = compiler.get_rust_tool('clippy-driver', build.environment)
+ self.tools[machine] = compiler.get_rust_tool('clippy-driver')
def warn_missing_clippy(self, machine: str) -> None:
if self.warned[machine]:
diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py
index a4dfebf..f515363 100644
--- a/mesonbuild/scripts/coverage.py
+++ b/mesonbuild/scripts/coverage.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from mesonbuild import environment, mesonlib
+from mesonbuild import tooldetect, mesonlib
import argparse, re, sys, os, subprocess, pathlib, stat, shutil
import typing as T
@@ -16,11 +16,11 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build
if gcovr_exe == '':
gcovr_exe = None
else:
- gcovr_exe, gcovr_version = environment.detect_gcovr(gcovr_exe)
+ gcovr_exe, gcovr_version = tooldetect.detect_gcovr(gcovr_exe)
if llvm_cov_exe == '' or shutil.which(llvm_cov_exe) is None:
llvm_cov_exe = None
- lcov_exe, lcov_version, genhtml_exe = environment.detect_lcov_genhtml()
+ lcov_exe, lcov_version, genhtml_exe = tooldetect.detect_lcov_genhtml()
# load config files for tools if available in the source tree
# - lcov requires manually specifying a per-project config
diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py
index db9c97d..8adc35c 100644
--- a/mesonbuild/scripts/depfixer.py
+++ b/mesonbuild/scripts/depfixer.py
@@ -31,8 +31,12 @@ class DataSizes:
p = '<'
else:
p = '>'
+ self.Char = p + 'c'
+ self.CharSize = 1
self.Half = p + 'h'
self.HalfSize = 2
+ self.Section = p + 'h'
+ self.SectionSize = 2
self.Word = p + 'I'
self.WordSize = 4
self.Sword = p + 'i'
@@ -71,6 +75,24 @@ class DynamicEntry(DataSizes):
ofile.write(struct.pack(self.Sword, self.d_tag))
ofile.write(struct.pack(self.Word, self.val))
+class DynsymEntry(DataSizes):
+ def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None:
+ super().__init__(ptrsize, is_le)
+ is_64 = ptrsize == 64
+ self.st_name = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+ if is_64:
+ self.st_info = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_other = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_shndx = struct.unpack(self.Section, ifile.read(self.SectionSize))[0]
+ self.st_value = struct.unpack(self.Addr, ifile.read(self.AddrSize))[0]
+ self.st_size = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.st_value = struct.unpack(self.Addr, ifile.read(self.AddrSize))[0]
+ self.st_size = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+ self.st_info = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_other = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_shndx = struct.unpack(self.Section, ifile.read(self.SectionSize))[0]
+
class SectionHeader(DataSizes):
def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None:
super().__init__(ptrsize, is_le)
@@ -115,6 +137,8 @@ class Elf(DataSizes):
self.verbose = verbose
self.sections: T.List[SectionHeader] = []
self.dynamic: T.List[DynamicEntry] = []
+ self.dynsym: T.List[DynsymEntry] = []
+ self.dynsym_strings: T.List[str] = []
self.open_bf(bfile)
try:
(self.ptrsize, self.is_le) = self.detect_elf_type()
@@ -122,6 +146,8 @@ class Elf(DataSizes):
self.parse_header()
self.parse_sections()
self.parse_dynamic()
+ self.parse_dynsym()
+ self.parse_dynsym_strings()
except (struct.error, RuntimeError):
self.close_bf()
raise
@@ -232,6 +258,23 @@ class Elf(DataSizes):
if e.d_tag == 0:
break
+ def parse_dynsym(self) -> None:
+ sec = self.find_section(b'.dynsym')
+ if sec is None:
+ return
+ self.bf.seek(sec.sh_offset)
+ for i in range(sec.sh_size // sec.sh_entsize):
+ e = DynsymEntry(self.bf, self.ptrsize, self.is_le)
+ self.dynsym.append(e)
+
+ def parse_dynsym_strings(self) -> None:
+ sec = self.find_section(b'.dynstr')
+ if sec is None:
+ return
+ for i in self.dynsym:
+ self.bf.seek(sec.sh_offset + i.st_name)
+ self.dynsym_strings.append(self.read_str().decode())
+
@generate_list
def get_section_names(self) -> T.Generator[str, None, None]:
section_names = self.sections[self.e_shstrndx]
@@ -353,12 +396,48 @@ class Elf(DataSizes):
self.bf.write(new_rpath)
self.bf.write(b'\0')
+ def clean_rpath_entry_string(self, entrynum: int) -> None:
+ # Get the rpath string
+ offset = self.get_entry_offset(entrynum)
+ self.bf.seek(offset)
+ rpath_string = self.read_str().decode()
+ reused_str = ''
+
+ # Inspect the dyn strings and check if our rpath string
+ # ends with one of them.
+ # This is to handle a subtle optimization of the linker
+ # where one of the dyn function name offset in the dynstr
+ # table might be set at the an offset of the rpath string.
+ # Example:
+ #
+ # rpath offset = 1314 string = /usr/lib/foo
+ # dym function offset = 1322 string = foo
+ #
+ # In the following case, the dym function string offset is
+ # placed at the offset +10 of the rpath.
+ # To correctly clear the rpath entry AND keep normal
+ # functionality of this optimization (and the binary),
+ # parse the maximum string we can remove from the rpath entry.
+ #
+ # Since strings MUST be null terminated, we can always check
+ # if the rpath string ends with the dyn function string and
+ # calculate what we can actually remove accordingly.
+ for dynsym_string in self.dynsym_strings:
+ if rpath_string.endswith(dynsym_string):
+ if len(dynsym_string) > len(reused_str):
+ reused_str = dynsym_string
+
+ # Seek back to start of string
+ self.bf.seek(offset)
+ self.bf.write(b'X' * (len(rpath_string) - len(reused_str)))
+
def remove_rpath_entry(self, entrynum: int) -> None:
sec = self.find_section(b'.dynamic')
if sec is None:
return None
for (i, entry) in enumerate(self.dynamic):
if entry.d_tag == entrynum:
+ self.clean_rpath_entry_string(entrynum)
rpentry = self.dynamic[i]
rpentry.d_tag = 0
self.dynamic = self.dynamic[:i] + self.dynamic[i + 1:] + [rpentry]
diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py
index 16051a8..c529577 100755
--- a/mesonbuild/scripts/env2mfile.py
+++ b/mesonbuild/scripts/env2mfile.py
@@ -5,6 +5,7 @@ from __future__ import annotations
from dataclasses import dataclass, field
import sys, os, subprocess, shutil
+import pathlib
import shlex
import typing as T
@@ -24,11 +25,13 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None:
parser.add_argument('--gccsuffix', default="",
help='A particular gcc version suffix if necessary.')
parser.add_argument('-o', required=True, dest='outfile',
- help='The output file.')
+ help='The output file or directory (for Android).')
parser.add_argument('--cross', default=False, action='store_true',
help='Generate a cross compilation file.')
parser.add_argument('--native', default=False, action='store_true',
help='Generate a native compilation file.')
+ parser.add_argument('--android', default=False, action='store_true',
+ help='Generate cross files for Android toolchains.')
parser.add_argument('--use-for-build', default=False, action='store_true',
help='Use _FOR_BUILD envvars.')
parser.add_argument('--system', default=None,
@@ -234,6 +237,7 @@ def dpkg_architecture_to_machine_info(output: str, options: T.Any) -> MachineInf
'g-ir-inspect',
'g-ir-scanner',
'pkg-config',
+ 'vapigen',
]:
try:
infos.binaries[tool] = locate_path("%s-%s" % (host_arch, tool))
@@ -429,11 +433,108 @@ def detect_native_env(options: T.Any) -> MachineInfo:
detect_properties_from_envvars(infos, esuffix)
return infos
+ANDROID_CPU_TO_MESON_CPU_FAMILY: dict[str, str] = {
+ 'aarch64': 'aarch64',
+ 'armv7a': 'arm',
+ 'i686': 'x86',
+ 'x86_64': 'x86_64',
+ 'riscv64': 'riscv64',
+}
+
+class AndroidDetector:
+ def __init__(self, options: T.Any):
+ import platform
+ self.platform = platform.system().lower()
+ self.options = options
+
+ if self.platform == 'windows':
+ self.build_machine_id = 'windows-X86_64'
+ self.command_suffix = '.cmd'
+ self.exe_suffix = '.exe'
+ elif self.platform == 'darwin':
+ self.build_machine_id = 'darwin-x86_64' # Yes, even on aarch64 for some reason
+ self.command_suffix = ''
+ self.exe_suffix = ''
+ elif self.platform == 'linux':
+ self.build_machine_id = 'linux-x86_64'
+ self.command_suffix = ''
+ self.exe_suffix = ''
+ else:
+ sys.exit('Android lookup only supported on Linux, Windows and macOS. Patches welcome.')
+ self.outdir = pathlib.Path(options.outfile)
+
+ def detect_android_sdk_root(self) -> None:
+ home = pathlib.Path.home()
+ if self.platform == 'windows':
+ sdk_root = home / 'AppData/Local/Android/Sdk'
+ elif self.platform == 'darwin':
+ sdk_root = home / 'Library/Android/Sdk'
+ elif self.platform == 'linux':
+ sdk_root = home / 'Android/Sdk'
+ else:
+ sys.exit('Unsupported platform.')
+ if not sdk_root.is_dir():
+ sys.exit(f'Could not locate Android SDK root in {sdk_root}.')
+ ndk_root = sdk_root / 'ndk'
+ if not ndk_root.is_dir():
+ sys.exit(f'Could not locate Android ndk in {ndk_root}')
+ self.ndk_root = ndk_root
+
+ def detect_toolchains(self) -> None:
+ self.detect_android_sdk_root()
+ if not self.outdir.is_dir():
+ self.outdir.mkdir()
+ for ndk in self.ndk_root.glob('*'):
+ if not ndk.is_dir():
+ continue
+ self.process_ndk(ndk)
+
+ def process_ndk(self, ndk: pathlib.Path) -> None:
+ ndk_version = ndk.parts[-1]
+ toolchain_root = ndk / f'toolchains/llvm/prebuilt/{self.build_machine_id}'
+ bindir = toolchain_root / 'bin'
+ if not bindir.is_dir():
+ sys.exit(f'Could not detect toolchain in {toolchain_root}.')
+ ar_path = bindir / f'llvm-ar{self.exe_suffix}'
+ if not ar_path.is_file():
+ sys.exit(f'Could not detect llvm-ar in {toolchain_root}.')
+ ar_str = str(ar_path).replace('\\', '/')
+ strip_path = bindir / f'llvm-strip{self.exe_suffix}'
+ if not strip_path.is_file():
+ sys.exit(f'Could not detect llvm-strip n {toolchain_root}.')
+ strip_str = str(strip_path).replace('\\', '/')
+ for compiler in bindir.glob('*-clang++'):
+ parts = compiler.parts[-1].split('-')
+ assert len(parts) == 4
+ cpu = parts[0]
+ assert parts[1] == 'linux'
+ android_version = parts[2]
+ cpp_compiler_str = str(compiler).replace('\\', '/')
+ c_compiler_str = cpp_compiler_str[:-2]
+ cpp_compiler_str += self.command_suffix
+ c_compiler_str += self.command_suffix
+ crossfile_name = f'android-{ndk_version}-{android_version}-{cpu}-cross.txt'
+ with open(pathlib.Path(self.options.outfile) / crossfile_name, 'w', encoding='utf-8') as ofile:
+ ofile.write('[binaries]\n')
+ ofile.write(f"c = '{c_compiler_str}'\n")
+ ofile.write(f"cpp = '{cpp_compiler_str}'\n")
+ ofile.write(f"ar = '{ar_str}'\n")
+ ofile.write(f"strip = '{strip_str}'\n")
+
+ ofile.write('\n[host_machine]\n')
+ ofile.write("system = 'android'\n")
+ ofile.write(f"cpu_family = '{ANDROID_CPU_TO_MESON_CPU_FAMILY[cpu]}'\n")
+ ofile.write(f"cpu = '{cpu}'\n")
+ ofile.write("endian = 'little'\n")
+
+
def run(options: T.Any) -> None:
if options.cross and options.native:
sys.exit('You can only specify either --cross or --native, not both.')
- if not options.cross and not options.native:
- sys.exit('You must specify --cross or --native.')
+ if (options.cross or options.native) and options.android:
+ sys.exit('You can not specify either --cross or --native with --android.')
+ if not options.cross and not options.native and not options.android:
+ sys.exit('You must specify --cross, --native or --android.')
mlog.notice('This functionality is experimental and subject to change.')
detect_cross = options.cross
if detect_cross:
@@ -441,7 +542,11 @@ def run(options: T.Any) -> None:
sys.exit('--use-for-build only makes sense for --native, not --cross')
infos = detect_cross_env(options)
write_system_info = True
- else:
+ write_machine_file(infos, options.outfile, write_system_info)
+ elif options.android is None:
infos = detect_native_env(options)
write_system_info = False
- write_machine_file(infos, options.outfile, write_system_info)
+ write_machine_file(infos, options.outfile, write_system_info)
+ else:
+ ad = AndroidDetector(options)
+ ad.detect_toolchains()
diff --git a/mesonbuild/scripts/externalproject.py b/mesonbuild/scripts/externalproject.py
index 4013b0a..b782a85 100644
--- a/mesonbuild/scripts/externalproject.py
+++ b/mesonbuild/scripts/externalproject.py
@@ -10,6 +10,7 @@ from pathlib import Path
import typing as T
from ..mesonlib import Popen_safe, split_args, determine_worker_count
+from .. import mlog
class ExternalProject:
def __init__(self, options: argparse.Namespace):
@@ -67,10 +68,10 @@ class ExternalProject:
def _run(self, step: str, command: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> int:
m = 'Running command ' + str(command) + ' in directory ' + str(self.build_dir) + '\n'
- log_filename = Path(self.log_dir, f'{self.name}-{step}.log')
+ logfile = Path(self.log_dir, f'{self.name}-{step}.log')
output = None
if not self.verbose:
- output = open(log_filename, 'w', encoding='utf-8')
+ output = open(logfile, 'w', encoding='utf-8')
output.write(m + '\n')
output.flush()
else:
@@ -84,8 +85,11 @@ class ExternalProject:
if p.returncode != 0:
m = f'{step} step returned error code {p.returncode}.'
if not self.verbose:
- m += '\nSee logs: ' + str(log_filename)
+ m += '\nSee logs: ' + str(logfile)
print(m)
+ contents = mlog.ci_fold_file(logfile, f'CI platform detected, click here for {os.path.basename(logfile)} contents.')
+ if contents:
+ print(contents)
return p.returncode
def run(args: T.List[str]) -> int:
diff --git a/mesonbuild/scripts/pickle_env.py b/mesonbuild/scripts/pickle_env.py
new file mode 100644
index 0000000..79e613f
--- /dev/null
+++ b/mesonbuild/scripts/pickle_env.py
@@ -0,0 +1,8 @@
+import os
+import pickle
+import typing as T
+
+def run(args: T.List[str]) -> int:
+ with open(args[0], "wb") as f:
+ pickle.dump(dict(os.environ), f)
+ return 0
diff --git a/mesonbuild/scripts/run_tool.py b/mesonbuild/scripts/run_tool.py
index e206ff7..4b3878c 100644
--- a/mesonbuild/scripts/run_tool.py
+++ b/mesonbuild/scripts/run_tool.py
@@ -19,14 +19,14 @@ import typing as T
Info = T.TypeVar("Info")
-async def run_with_buffered_output(cmdlist: T.List[str]) -> int:
+async def run_with_buffered_output(cmdlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> int:
"""Run the command in cmdlist, buffering the output so that it is
not mixed for multiple child processes. Kill the child on
cancellation."""
quoted_cmdline = join_args(cmdlist)
p: T.Optional[asyncio.subprocess.Process] = None
try:
- p = await asyncio.create_subprocess_exec(*cmdlist,
+ p = await asyncio.create_subprocess_exec(*cmdlist, env=env,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT)
@@ -98,7 +98,7 @@ def parse_pattern_file(fname: Path) -> T.List[str]:
def all_clike_files(name: str, srcdir: Path, builddir: Path) -> T.Iterable[Path]:
patterns = parse_pattern_file(srcdir / f'.{name}-include')
- globs: T.Union[T.List[T.List[Path]], T.List[T.Generator[Path, None, None]]]
+ globs: T.Sequence[T.Union[T.List[Path], T.Iterator[Path], T.Generator[Path, None, None]]]
if patterns:
globs = [srcdir.glob(p) for p in patterns]
else:
@@ -121,16 +121,36 @@ def all_clike_files(name: str, srcdir: Path, builddir: Path) -> T.Iterable[Path]
yield f
def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., T.Coroutine[None, None, int]], *args: T.Any) -> int:
- if sys.platform == 'win32':
+ if sys.platform == 'win32' and sys.version_info < (3, 8):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
def wrapper(path: Path) -> T.Iterable[T.Coroutine[None, None, int]]:
yield fn(path, *args)
return asyncio.run(_run_workers(all_clike_files(name, srcdir, builddir), wrapper))
+def run_clang_tool_on_sources(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., T.Coroutine[None, None, int]], *args: T.Any) -> int:
+ if sys.platform == 'win32' and sys.version_info < (3, 8):
+ asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
+
+ source_files = set()
+ with open('meson-info/intro-targets.json', encoding='utf-8') as fp:
+ targets = json.load(fp)
+
+ for target in targets:
+ for target_source in target.get('target_sources') or []:
+ for source in target_source.get('sources') or []:
+ source_files.add(Path(source))
+
+ clike_files = set(all_clike_files(name, srcdir, builddir))
+ source_files = source_files.intersection(clike_files)
+
+ def wrapper(path: Path) -> T.Iterable[T.Coroutine[None, None, int]]:
+ yield fn(path, *args)
+ return asyncio.run(_run_workers(source_files, wrapper))
+
def run_tool_on_targets(fn: T.Callable[[T.Dict[str, T.Any]],
T.Iterable[T.Coroutine[None, None, int]]]) -> int:
- if sys.platform == 'win32':
+ if sys.platform == 'win32' and sys.version_info < (3, 8):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
with open('meson-info/intro-targets.json', encoding='utf-8') as fp:
diff --git a/mesonbuild/scripts/rustdoc.py b/mesonbuild/scripts/rustdoc.py
index f5f74c4..c1c0d8c 100644
--- a/mesonbuild/scripts/rustdoc.py
+++ b/mesonbuild/scripts/rustdoc.py
@@ -15,8 +15,8 @@ from ..wrap import WrapMode, wrap
if T.TYPE_CHECKING:
from ..compilers.rust import RustCompiler
-async def run_and_confirm_success(cmdlist: T.List[str], crate: str) -> int:
- returncode = await run_with_buffered_output(cmdlist)
+async def run_and_confirm_success(cmdlist: T.List[str], environ: T.Dict[str, str], crate: str) -> int:
+ returncode = await run_with_buffered_output(cmdlist, environ)
if returncode == 0:
print(mlog.green('Generated'), os.path.join('doc', crate))
return returncode
@@ -31,7 +31,7 @@ class Rustdoc:
compilers = build.environment.coredata.compilers[machine]
if 'rust' in compilers:
compiler = T.cast('RustCompiler', compilers['rust'])
- self.tools[machine] = compiler.get_rust_tool('rustdoc', build.environment)
+ self.tools[machine] = compiler.get_rust_tool('rustdoc')
def warn_missing_rustdoc(self, machine: str) -> None:
if self.warned[machine]:
@@ -54,18 +54,25 @@ class Rustdoc:
prev = None
crate_name = None
is_test = False
+ environ = dict(os.environ)
for arg in src_block['parameters']:
if prev:
if prev == '--crate-name':
cmdlist.extend((prev, arg))
crate_name = arg
+ elif prev == '--env-set':
+ try:
+ key, val = arg.split('=', maxsplit=1)
+ environ[key] = val
+ except ValueError:
+ pass
prev = None
continue
if arg == '--test':
is_test = True
break
- elif arg in {'--crate-name', '--emit', '--out-dir', '-l'}:
+ elif arg in {'--crate-name', '--emit', '--out-dir', '-l', '--env-set'}:
prev = arg
elif arg != '-g' and not arg.startswith('-l'):
cmdlist.append(arg)
@@ -80,7 +87,7 @@ class Rustdoc:
cmdlist.append('--document-private-items')
cmdlist.append('-o')
cmdlist.append('doc')
- yield run_and_confirm_success(cmdlist, crate_name)
+ yield run_and_confirm_success(cmdlist, environ, crate_name)
else:
print(mlog.yellow('Skipping'), target['name'], '(no crate name)')
diff --git a/mesonbuild/scripts/scanbuild.py b/mesonbuild/scripts/scanbuild.py
index b738aee..3bf7918 100644
--- a/mesonbuild/scripts/scanbuild.py
+++ b/mesonbuild/scripts/scanbuild.py
@@ -6,10 +6,9 @@ from __future__ import annotations
import subprocess
import shutil
import tempfile
-from ..environment import detect_ninja, detect_scanbuild
-from ..coredata import get_cmd_line_file
-from ..machinefile import CmdLineFileParser
-from ..mesonlib import windows_proof_rmtree
+from ..cmdline import get_cmd_line_file, CmdLineFileParser
+from ..tooldetect import detect_ninja, detect_scanbuild
+from ..mesonlib import windows_proof_rmtree, determine_worker_count
from pathlib import Path
import typing as T
from ast import literal_eval
@@ -20,7 +19,7 @@ def scanbuild(exelist: T.List[str], srcdir: Path, blddir: Path, privdir: Path, l
# so it can be debugged.
scandir = tempfile.mkdtemp(dir=str(privdir))
meson_cmd = exelist + args
- build_cmd = exelist + ['--exclude', str(subprojdir), '-o', str(logdir)] + detect_ninja() + ['-C', scandir]
+ build_cmd = exelist + ['--exclude', str(subprojdir), '-o', str(logdir)] + detect_ninja() + ['-C', scandir, f'-j{determine_worker_count()}']
rc = subprocess.call(meson_cmd + [str(srcdir), scandir])
if rc != 0:
return rc
diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py
index b0a07d9..3ceea4b 100644
--- a/mesonbuild/scripts/symbolextractor.py
+++ b/mesonbuild/scripts/symbolextractor.py
@@ -23,7 +23,7 @@ parser.add_argument('--cross-host', default=None, dest='cross_host',
help='cross compilation host platform')
parser.add_argument('args', nargs='+')
-TOOL_WARNING_FILE = None
+TOOL_WARNING_FILE: str
RELINKING_WARNING = 'Relinking will always happen on source changes.'
def dummy_syms(outfilename: str) -> None:
@@ -273,7 +273,7 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host
windows_syms(impfilename, outfilename)
else:
dummy_syms(outfilename)
- elif mesonlib.is_linux() or mesonlib.is_hurd():
+ elif mesonlib.is_linux() or mesonlib.is_hurd() or mesonlib.is_haiku():
gnu_syms(libfilename, outfilename)
elif mesonlib.is_osx():
osx_syms(libfilename, outfilename)
diff --git a/mesonbuild/scripts/vcstagger.py b/mesonbuild/scripts/vcstagger.py
index 26c1e81..dacf231 100644
--- a/mesonbuild/scripts/vcstagger.py
+++ b/mesonbuild/scripts/vcstagger.py
@@ -8,8 +8,8 @@ import typing as T
def config_vcs_tag(infile: str, outfile: str, fallback: str, source_dir: str, replace_string: str, regex_selector: str, cmd: T.List[str]) -> None:
try:
- output = subprocess.check_output(cmd, cwd=source_dir)
- new_string = re.search(regex_selector, output.decode()).group(1).strip()
+ output = subprocess.check_output(cmd, cwd=source_dir, stderr=subprocess.DEVNULL)
+ new_string = re.search(regex_selector, output.decode()).group(1).rstrip('\r\n')
except Exception:
new_string = fallback
diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py
index 1bfa2ae..79f88a5 100644
--- a/mesonbuild/templates/cpptemplates.py
+++ b/mesonbuild/templates/cpptemplates.py
@@ -16,7 +16,7 @@ hello_cpp_template = '''#include <iostream>
int main(int argc, char **argv) {{
if (argc != 1) {{
- std::cout << argv[0] << "takes no arguments.\\n";
+ std::cout << argv[0] << " takes no arguments.\\n";
return 1;
}}
std::cout << "This is project " << PROJECT_NAME << ".\\n";
@@ -35,9 +35,12 @@ hello_cpp_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
install : true,
dependencies : dependencies,
)
@@ -52,6 +55,12 @@ lib_hpp_template = '''#pragma once
#else
#define {utoken}_PUBLIC __declspec(dllimport)
#endif
+#elif defined __OS2__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC
+ #endif
#else
#ifdef BUILDING_{utoken}
#define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
@@ -121,9 +130,12 @@ dependencies = [{dependencies}
# not the executables that use the library.
lib_args = ['-DBUILDING_{utoken}']
+sources = [{source_files}
+]
+
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
cpp_shared_args : lib_args,
gnu_symbol_visibility : 'hidden',
diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py
index 59c7189..e44cf6f 100644
--- a/mesonbuild/templates/cstemplates.py
+++ b/mesonbuild/templates/cstemplates.py
@@ -35,9 +35,12 @@ hello_cs_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
install : true,
dependencies : dependencies,
)
@@ -83,9 +86,12 @@ lib_cs_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
stlib = shared_library(
'{lib_name}',
- '{source_file}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py
index 559cef9..3db6477 100644
--- a/mesonbuild/templates/ctemplates.py
+++ b/mesonbuild/templates/ctemplates.py
@@ -18,6 +18,12 @@ lib_h_template = '''#pragma once
#else
#define {utoken}_PUBLIC __declspec(dllimport)
#endif
+#elif defined __OS2__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC
+ #endif
#else
#ifdef BUILDING_{utoken}
#define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
@@ -71,9 +77,12 @@ lib_args = ['-DBUILDING_{utoken}']
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
c_shared_args : lib_args,
gnu_symbol_visibility : 'hidden',
@@ -133,9 +142,12 @@ hello_c_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py
index 252f44a..d10fb76 100644
--- a/mesonbuild/templates/cudatemplates.py
+++ b/mesonbuild/templates/cudatemplates.py
@@ -36,9 +36,12 @@ hello_cuda_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
@@ -122,9 +125,12 @@ lib_args = ['-DBUILDING_{utoken}']
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
cpp_shared_args : lib_args,
gnu_symbol_visibility : 'hidden',
diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py
index db3bdbf..727ee20 100644
--- a/mesonbuild/templates/dlangtemplates.py
+++ b/mesonbuild/templates/dlangtemplates.py
@@ -35,9 +35,12 @@ hello_d_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
@@ -84,10 +87,12 @@ lib_d_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
stlib = static_library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
gnu_symbol_visibility : 'hidden',
dependencies : dependencies,
diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py
index 7aaa9d3..03f0ff9 100644
--- a/mesonbuild/templates/fortrantemplates.py
+++ b/mesonbuild/templates/fortrantemplates.py
@@ -56,9 +56,12 @@ lib_args = ['-DBUILDING_{utoken}']
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
fortran_shared_args : lib_args,
gnu_symbol_visibility : 'hidden',
@@ -110,9 +113,12 @@ hello_fortran_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py
index c30c7f7..ed32194 100644
--- a/mesonbuild/templates/javatemplates.py
+++ b/mesonbuild/templates/javatemplates.py
@@ -35,9 +35,12 @@ hello_java_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = jar(
'{exe_name}',
- '{source_name}',
+ sources,
main_class : '{exe_name}',
dependencies : dependencies,
install : true,
@@ -86,9 +89,12 @@ lib_java_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
jarlib = jar(
'{class_name}',
- '{source_file}',
+ sources,
dependencies : dependencies,
main_class : '{class_name}',
install : true,
diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py
index 1fdfa06..d80c374 100644
--- a/mesonbuild/templates/objcpptemplates.py
+++ b/mesonbuild/templates/objcpptemplates.py
@@ -18,6 +18,12 @@ lib_h_template = '''#pragma once
#else
#define {utoken}_PUBLIC __declspec(dllimport)
#endif
+#elif defined __OS2__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC
+ #endif
#else
#ifdef BUILDING_{utoken}
#define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
@@ -67,13 +73,16 @@ lib_objcpp_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
# These arguments are only used to build the shared library
# not the executables that use the library.
lib_args = ['-DBUILDING_{utoken}']
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
objcpp_shared_args : lib_args,
dependencies : dependencies,
@@ -133,9 +142,12 @@ hello_objcpp_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py
index 5603bae..7dc3d4f 100644
--- a/mesonbuild/templates/objctemplates.py
+++ b/mesonbuild/templates/objctemplates.py
@@ -18,6 +18,12 @@ lib_h_template = '''#pragma once
#else
#define {utoken}_PUBLIC __declspec(dllimport)
#endif
+#elif defined __OS2__
+ #ifdef BUILDING_{utoken}
+ #define {utoken}_PUBLIC __declspec(dllexport)
+ #else
+ #define {utoken}_PUBLIC
+ #endif
#else
#ifdef BUILDING_{utoken}
#define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
@@ -67,13 +73,16 @@ lib_objc_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
# These arguments are only used to build the shared library
# not the executables that use the library.
lib_args = ['-DBUILDING_{utoken}']
lib = library(
'{lib_name}',
- '{source_file}',
+ sources,
install : true,
objc_shared_args : lib_args,
dependencies : dependencies,
@@ -132,9 +141,12 @@ hello_objc_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py
index ee1f008..b415be7 100644
--- a/mesonbuild/templates/rusttemplates.py
+++ b/mesonbuild/templates/rusttemplates.py
@@ -50,9 +50,12 @@ rust = import('rust')
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
lib = static_library(
'{lib_name}',
- '{source_file}',
+ sources,
dependencies : dependencies,
install : true,
)
@@ -86,9 +89,12 @@ hello_rust_meson_template = '''project(
dependencies = [{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py
index d033f3c..46f9a70 100644
--- a/mesonbuild/templates/sampleimpl.py
+++ b/mesonbuild/templates/sampleimpl.py
@@ -3,6 +3,7 @@
# Copyright © 2023-2025 Intel Corporation
from __future__ import annotations
+from pathlib import Path
import abc
import os
@@ -17,6 +18,7 @@ class SampleImpl(metaclass=abc.ABCMeta):
def __init__(self, args: Arguments):
self.name = args.name
+ self.executable_name = args.executable if args.executable else None
self.version = args.version
self.lowercase_token = re.sub(r'[^a-z0-9]', '_', self.name.lower())
self.uppercase_token = self.lowercase_token.upper()
@@ -24,6 +26,7 @@ class SampleImpl(metaclass=abc.ABCMeta):
self.meson_version = '1.0.0'
self.force = args.force
self.dependencies = args.deps.split(',') if args.deps else []
+ self.sources: list[Path] = []
@abc.abstractmethod
def create_executable(self) -> None:
@@ -66,11 +69,18 @@ class SampleImpl(metaclass=abc.ABCMeta):
def _format_dependencies(self) -> str:
return ''.join(f"\n dependency('{d}')," for d in self.dependencies)
+ def _format_sources(self) -> str:
+ return ''.join(f"\n '{x}'," for x in self.sources)
+
class ClassImpl(SampleImpl):
"""For Class based languages, like Java and C#"""
+ def __init__(self, args: Arguments):
+ super().__init__(args)
+ self.sources = args.srcfiles if args.srcfiles else [Path(f'{self.capitalized_token}.{self.source_ext}')]
+
def create_executable(self) -> None:
source_name = f'{self.capitalized_token}.{self.source_ext}'
if not os.path.exists(source_name):
@@ -80,29 +90,32 @@ class ClassImpl(SampleImpl):
if self.force or not os.path.exists('meson.build'):
with open('meson.build', 'w', encoding='utf-8') as f:
f.write(self.exe_meson_template.format(project_name=self.name,
- exe_name=self.name,
+ exe_name=self.executable_name,
source_name=source_name,
version=self.version,
meson_version=self.meson_version,
- dependencies=self._format_dependencies()))
+ dependencies=self._format_dependencies(),
+ source_files=self._format_sources()))
def create_library(self) -> None:
lib_name = f'{self.capitalized_token}.{self.source_ext}'
test_name = f'{self.capitalized_token}_test.{self.source_ext}'
- kwargs = {'utoken': self.uppercase_token,
- 'ltoken': self.lowercase_token,
- 'class_test': f'{self.capitalized_token}_test',
- 'class_name': self.capitalized_token,
- 'source_file': lib_name,
- 'test_source_file': test_name,
- 'test_exe_name': f'{self.lowercase_token}_test',
- 'project_name': self.name,
- 'lib_name': self.lowercase_token,
- 'test_name': self.lowercase_token,
- 'version': self.version,
- 'meson_version': self.meson_version,
- 'dependencies': self._format_dependencies(),
- }
+ kwargs = {
+ 'utoken': self.uppercase_token,
+ 'ltoken': self.lowercase_token,
+ 'class_test': f'{self.capitalized_token}_test',
+ 'class_name': self.capitalized_token,
+ 'source_file': lib_name,
+ 'test_source_file': test_name,
+ 'test_exe_name': f'{self.lowercase_token}_test',
+ 'project_name': self.name,
+ 'lib_name': self.lowercase_token,
+ 'test_name': self.lowercase_token,
+ 'version': self.version,
+ 'meson_version': self.meson_version,
+ 'dependencies': self._format_dependencies(),
+ 'source_files': self._format_sources(),
+ }
if not os.path.exists(lib_name):
with open(lib_name, 'w', encoding='utf-8') as f:
f.write(self.lib_template.format(**kwargs))
@@ -118,6 +131,10 @@ class FileImpl(SampleImpl):
"""File based languages without headers"""
+ def __init__(self, args: Arguments):
+ super().__init__(args)
+ self.sources = args.srcfiles if args.srcfiles else [Path(f'{self.name}.{self.source_ext}')]
+
def create_executable(self) -> None:
source_name = f'{self.lowercase_token}.{self.source_ext}'
if not os.path.exists(source_name):
@@ -126,11 +143,12 @@ class FileImpl(SampleImpl):
if self.force or not os.path.exists('meson.build'):
with open('meson.build', 'w', encoding='utf-8') as f:
f.write(self.exe_meson_template.format(project_name=self.name,
- exe_name=self.name,
+ exe_name=self.executable_name,
source_name=source_name,
version=self.version,
meson_version=self.meson_version,
- dependencies=self._format_dependencies()))
+ dependencies=self._format_dependencies(),
+ source_files=self._format_sources()))
def lib_kwargs(self) -> T.Dict[str, str]:
"""Get Language specific keyword arguments
@@ -153,6 +171,7 @@ class FileImpl(SampleImpl):
'version': self.version,
'meson_version': self.meson_version,
'dependencies': self._format_dependencies(),
+ 'source_files': self._format_sources(),
}
def create_library(self) -> None:
diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py
index b2aab3f..de31877 100644
--- a/mesonbuild/templates/valatemplates.py
+++ b/mesonbuild/templates/valatemplates.py
@@ -24,9 +24,12 @@ dependencies = [
dependency('gobject-2.0'),{dependencies}
]
+sources = [{source_files}
+]
+
exe = executable(
'{exe_name}',
- '{source_name}',
+ sources,
dependencies : dependencies,
install : true,
)
@@ -67,11 +70,14 @@ dependencies = [
dependency('gobject-2.0'),{dependencies}
]
+sources = [{source_files}
+]
+
# These arguments are only used to build the shared library
# not the executables that use the library.
lib = shared_library(
'foo',
- '{source_file}',
+ sources,
dependencies : dependencies,
install : true,
install_dir : [true, true, true],
diff --git a/mesonbuild/tooldetect.py b/mesonbuild/tooldetect.py
new file mode 100644
index 0000000..198a6ee
--- /dev/null
+++ b/mesonbuild/tooldetect.py
@@ -0,0 +1,248 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2012-2020 The Meson development team
+# Copyright © 2023-2025 Intel Corporation
+
+from __future__ import annotations
+
+import os
+import shutil
+import typing as T
+
+from . import coredata
+from . import mesonlib
+from . import mlog
+from .mesonlib import MachineChoice, Popen_safe, search_version, quote_arg, split_args
+from .programs import ExternalProgram
+
+
+def detect_gcovr(gcovr_exe: str = 'gcovr', min_version: str = '3.3', log: bool = False) \
+ -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
+ try:
+ p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
+ except (FileNotFoundError, PermissionError):
+ # Doesn't exist in PATH or isn't executable
+ return None, None
+ found = search_version(found)
+ if p.returncode == 0 and mesonlib.version_compare(found, '>=' + min_version):
+ if log:
+ mlog.log('Found gcovr-{} at {}'.format(found, quote_arg(shutil.which(gcovr_exe))))
+ return gcovr_exe, found
+ return None, None
+
+def detect_lcov(lcov_exe: str = 'lcov', log: bool = False) \
+ -> T.Union[T.Tuple[None, None], T.Tuple[str, str]]:
+ try:
+ p, found = Popen_safe([lcov_exe, '--version'])[0:2]
+ except (FileNotFoundError, PermissionError):
+ # Doesn't exist in PATH or isn't executable
+ return None, None
+ found = search_version(found)
+ if p.returncode == 0 and found:
+ if log:
+ mlog.log('Found lcov-{} at {}'.format(found, quote_arg(shutil.which(lcov_exe))))
+ return lcov_exe, found
+ return None, None
+
+def detect_llvm_cov(suffix: T.Optional[str] = None) -> T.Optional[str]:
+ # If there's a known suffix or forced lack of suffix, use that
+ if suffix is not None:
+ if suffix == '':
+ tool = 'llvm-cov'
+ else:
+ tool = f'llvm-cov-{suffix}'
+ if shutil.which(tool) is not None:
+ return tool
+ else:
+ # Otherwise guess in the dark
+ tools = get_llvm_tool_names('llvm-cov')
+ for tool in tools:
+ if shutil.which(tool):
+ return tool
+ return None
+
+def compute_llvm_suffix(coredata: coredata.CoreData) -> T.Optional[str]:
+ # Check to see if the user is trying to do coverage for either a C or C++ project
+ compilers = coredata.compilers[MachineChoice.BUILD]
+ cpp_compiler_is_clang = 'cpp' in compilers and compilers['cpp'].id == 'clang'
+ c_compiler_is_clang = 'c' in compilers and compilers['c'].id == 'clang'
+ # Extract first the C++ compiler if available. If it's a Clang of some kind, compute the suffix if possible
+ if cpp_compiler_is_clang:
+ suffix = compilers['cpp'].version.split('.')[0]
+ return suffix
+
+ # Then the C compiler, again checking if it's some kind of Clang and computing the suffix
+ if c_compiler_is_clang:
+ suffix = compilers['c'].version.split('.')[0]
+ return suffix
+
+ # Neither compiler is a Clang, or no compilers are for C or C++
+ return None
+
+def detect_lcov_genhtml(lcov_exe: str = 'lcov', genhtml_exe: str = 'genhtml') \
+ -> T.Tuple[str, T.Optional[str], str]:
+ lcov_exe, lcov_version = detect_lcov(lcov_exe)
+ if shutil.which(genhtml_exe) is None:
+ genhtml_exe = None
+
+ return lcov_exe, lcov_version, genhtml_exe
+
+def find_coverage_tools(coredata: coredata.CoreData) -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]:
+ gcovr_exe, gcovr_version = detect_gcovr()
+
+ llvm_cov_exe = detect_llvm_cov(compute_llvm_suffix(coredata))
+ # Some platforms may provide versioned clang but only non-versioned llvm utils
+ if llvm_cov_exe is None:
+ llvm_cov_exe = detect_llvm_cov('')
+
+ lcov_exe, lcov_version, genhtml_exe = detect_lcov_genhtml()
+
+ return gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe
+
+def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.Optional[T.List[str]]:
+ r = detect_ninja_command_and_version(version, log)
+ return r[0] if r else None
+
+def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) -> T.Optional[T.Tuple[T.List[str], str]]:
+ env_ninja = os.environ.get('NINJA', None)
+ for n in [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu']:
+ prog = ExternalProgram(n, silent=True)
+ if not prog.found():
+ continue
+ try:
+ p, found = Popen_safe(prog.command + ['--version'])[0:2]
+ except (FileNotFoundError, PermissionError):
+ # Doesn't exist in PATH or isn't executable
+ continue
+ found = found.strip()
+ # Perhaps we should add a way for the caller to know the failure mode
+ # (not found or too old)
+ if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version):
+ if log:
+ name = os.path.basename(n)
+ if name.endswith('-' + found):
+ name = name[0:-1 - len(found)]
+ if name == 'ninja-build':
+ name = 'ninja'
+ if name == 'samu':
+ name = 'samurai'
+ mlog.log('Found {}-{} at {}'.format(name, found,
+ ' '.join([quote_arg(x) for x in prog.command])))
+ return (prog.command, found)
+ return None
+
+def get_llvm_tool_names(tool: str) -> T.List[str]:
+ # Ordered list of possible suffixes of LLVM executables to try. Start with
+ # base, then try newest back to oldest (3.5 is arbitrary), and finally the
+ # devel version. Please note that the development snapshot in Debian does
+ # not have a distinct name. Do not move it to the beginning of the list
+ # unless it becomes a stable release.
+ suffixes = [
+ '', # base (no suffix)
+ '-21.1', '21.1',
+ '-21', '21',
+ '-20.1', '20.1',
+ '-20', '20',
+ '-19.1', '19.1',
+ '-19', '19',
+ '-18.1', '18.1',
+ '-18', '18',
+ '-17', '17',
+ '-16', '16',
+ '-15', '15',
+ '-14', '14',
+ '-13', '13',
+ '-12', '12',
+ '-11', '11',
+ '-10', '10',
+ '-9', '90',
+ '-8', '80',
+ '-7', '70',
+ '-6.0', '60',
+ '-5.0', '50',
+ '-4.0', '40',
+ '-3.9', '39',
+ '-3.8', '38',
+ '-3.7', '37',
+ '-3.6', '36',
+ '-3.5', '35',
+ '-20', # Debian development snapshot
+ '-devel', # FreeBSD development snapshot
+ ]
+ names: T.List[str] = []
+ for suffix in suffixes:
+ names.append(tool + suffix)
+ return names
+
+def detect_scanbuild() -> T.List[str]:
+ """ Look for scan-build binary on build platform
+
+ First, if a SCANBUILD env variable has been provided, give it precedence
+ on all platforms.
+
+ For most platforms, scan-build is found is the PATH contains a binary
+ named "scan-build". However, some distribution's package manager (FreeBSD)
+ don't. For those, loop through a list of candidates to see if one is
+ available.
+
+ Return: a single-element list of the found scan-build binary ready to be
+ passed to Popen()
+ """
+ exelist: T.List[str] = []
+ if 'SCANBUILD' in os.environ:
+ exelist = split_args(os.environ['SCANBUILD'])
+
+ else:
+ tools = get_llvm_tool_names('scan-build')
+ for tool in tools:
+ which = shutil.which(tool)
+ if which is not None:
+ exelist = [which]
+ break
+
+ if exelist:
+ tool = exelist[0]
+ if os.path.isfile(tool) and os.access(tool, os.X_OK):
+ return [tool]
+ return []
+
+def detect_clangformat() -> T.List[str]:
+ """ Look for clang-format binary on build platform
+
+ Do the same thing as detect_scanbuild to find clang-format except it
+ currently does not check the environment variable.
+
+ Return: a single-element list of the found clang-format binary ready to be
+ passed to Popen()
+ """
+ tools = get_llvm_tool_names('clang-format')
+ for tool in tools:
+ path = shutil.which(tool)
+ if path is not None:
+ return [path]
+ return []
+
+def detect_clangtidy() -> T.List[str]:
+ """ Look for clang-tidy binary on build platform
+
+ Return: a single-element list of the found clang-tidy binary ready to be
+ passed to Popen()
+ """
+ tools = get_llvm_tool_names('clang-tidy')
+ for tool in tools:
+ path = shutil.which(tool)
+ if path is not None:
+ return [path]
+ return []
+
+def detect_clangapply() -> T.List[str]:
+ """ Look for clang-apply-replacements binary on build platform
+
+ Return: a single-element list of the found clang-apply-replacements binary
+ ready to be passed to Popen()
+ """
+ tools = get_llvm_tool_names('clang-apply-replacements')
+ for tool in tools:
+ path = shutil.which(tool)
+ if path is not None:
+ return [path]
+ return []
diff --git a/mesonbuild/utils/platform.py b/mesonbuild/utils/platform.py
index 8e762b6..1a2780f 100644
--- a/mesonbuild/utils/platform.py
+++ b/mesonbuild/utils/platform.py
@@ -2,26 +2,113 @@
# Copyright 2012-2021 The Meson development team
# Copyright © 2021-2023 Intel Corporation
-from __future__ import annotations
+"""Utility functions with platform specific implementations."""
-"""base classes providing no-op functionality.."""
+from __future__ import annotations
+import enum
import os
+import sys
import typing as T
from .. import mlog
+from .core import MesonException
+
+__all__ = ['DirectoryLock', 'DirectoryLockAction']
+
+class DirectoryLockAction(enum.Enum):
+ IGNORE = 0
+ WAIT = 1
+ FAIL = 2
-__all__ = ['BuildDirLock']
+class DirectoryLockBase:
-# This needs to be inherited by the specific implementations to make type
-# checking happy
-class BuildDirLock:
+ lockfile: T.TextIO
- def __init__(self, builddir: str) -> None:
- self.lockfilename = os.path.join(builddir, 'meson-private/meson.lock')
+ def __init__(self, directory: str, lockfile: str, action: DirectoryLockAction, err: str,
+ optional: bool = False) -> None:
+ self.action = action
+ self.err = err
+ self.lockpath = os.path.join(directory, lockfile)
+ self.optional = optional
def __enter__(self) -> None:
- mlog.debug('Calling the no-op version of BuildDirLock')
+ mlog.debug('Calling the no-op version of DirectoryLock')
def __exit__(self, *args: T.Any) -> None:
pass
+
+
+if sys.platform == 'win32':
+ import msvcrt
+
+ class DirectoryLock(DirectoryLockBase):
+
+ def __enter__(self) -> None:
+ try:
+ self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
+ except (FileNotFoundError, IsADirectoryError):
+ # For FileNotFoundError, there is nothing to lock.
+ # For IsADirectoryError, something is seriously wrong.
+ raise
+ except OSError:
+ if self.action == DirectoryLockAction.IGNORE or self.optional:
+ return
+
+ try:
+ mode = msvcrt.LK_LOCK
+ if self.action != DirectoryLockAction.WAIT:
+ mode = msvcrt.LK_NBLCK
+ msvcrt.locking(self.lockfile.fileno(), mode, 1)
+ except BlockingIOError:
+ self.lockfile.close()
+ if self.action == DirectoryLockAction.IGNORE:
+ return
+ raise MesonException(self.err)
+ except PermissionError:
+ self.lockfile.close()
+ raise MesonException(self.err)
+
+ def __exit__(self, *args: T.Any) -> None:
+ if self.lockfile is None or self.lockfile.closed:
+ return
+ msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
+ self.lockfile.close()
+else:
+ import fcntl
+
+ class DirectoryLock(DirectoryLockBase):
+
+ def __enter__(self) -> None:
+ try:
+ self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
+ except (FileNotFoundError, IsADirectoryError):
+ # For FileNotFoundError, there is nothing to lock.
+ # For IsADirectoryError, something is seriously wrong.
+ raise
+ except OSError:
+ if self.action == DirectoryLockAction.IGNORE or self.optional:
+ return
+
+ try:
+ flags = fcntl.LOCK_EX
+ if self.action != DirectoryLockAction.WAIT:
+ flags = flags | fcntl.LOCK_NB
+ fcntl.flock(self.lockfile, flags)
+ except BlockingIOError:
+ self.lockfile.close()
+ if self.action == DirectoryLockAction.IGNORE:
+ return
+ raise MesonException(self.err)
+ except PermissionError:
+ self.lockfile.close()
+ raise MesonException(self.err)
+ except OSError as e:
+ self.lockfile.close()
+ raise MesonException(f'Failed to lock directory {self.lockpath}: {e.strerror}')
+
+ def __exit__(self, *args: T.Any) -> None:
+ if self.lockfile is None or self.lockfile.closed:
+ return
+ fcntl.flock(self.lockfile, fcntl.LOCK_UN)
+ self.lockfile.close()
diff --git a/mesonbuild/utils/posix.py b/mesonbuild/utils/posix.py
deleted file mode 100644
index e8387ba..0000000
--- a/mesonbuild/utils/posix.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-# Copyright 2012-2021 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
-
-from __future__ import annotations
-
-"""Posix specific implementations of mesonlib functionality."""
-
-import fcntl
-import typing as T
-
-from .core import MesonException
-from .platform import BuildDirLock as BuildDirLockBase
-
-__all__ = ['BuildDirLock']
-
-class BuildDirLock(BuildDirLockBase):
-
- def __enter__(self) -> None:
- self.lockfile = open(self.lockfilename, 'w', encoding='utf-8')
- try:
- fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except (BlockingIOError, PermissionError):
- self.lockfile.close()
- raise MesonException('Some other Meson process is already using this build directory. Exiting.')
- except OSError as e:
- self.lockfile.close()
- raise MesonException(f'Failed to lock the build directory: {e.strerror}')
-
- def __exit__(self, *args: T.Any) -> None:
- fcntl.flock(self.lockfile, fcntl.LOCK_UN)
- self.lockfile.close()
diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py
index 5b3f131..467d4f5 100644
--- a/mesonbuild/utils/universal.py
+++ b/mesonbuild/utils/universal.py
@@ -34,10 +34,11 @@ if T.TYPE_CHECKING:
from .._typing import ImmutableListProtocol
from ..build import ConfigurationData
- from ..coredata import StrOrBytesPath
+ from ..cmdline import StrOrBytesPath
from ..environment import Environment
from ..compilers.compilers import Compiler
from ..interpreterbase.baseobjects import SubProject
+ from .. import programs
class _EnvPickleLoadable(Protocol):
@@ -107,6 +108,7 @@ __all__ = [
'get_compiler_for_source',
'get_filenames_templates_dict',
'get_rsp_threshold',
+ 'get_subproject_dir',
'get_variable_regex',
'get_wine_shortpath',
'git',
@@ -124,6 +126,7 @@ __all__ = [
'is_linux',
'is_netbsd',
'is_openbsd',
+ 'is_os2',
'is_osx',
'is_parent_path',
'is_qnx',
@@ -150,9 +153,11 @@ __all__ = [
'set_meson_command',
'split_args',
'stringlistify',
+ 'underscorify',
'substitute_values',
'substring_is_in_list',
'typeslistify',
+ 'unique_list',
'verbose_git',
'version_compare',
'version_compare_condition_with_min',
@@ -432,7 +437,7 @@ class File(HoldableObject):
absdir = srcdir
if self.is_built:
absdir = builddir
- return os.path.join(absdir, self.relative_name())
+ return os.path.normpath(os.path.join(absdir, self.relative_name()))
@property
def suffix(self) -> str:
@@ -680,6 +685,9 @@ def is_qnx() -> bool:
def is_aix() -> bool:
return platform.system().lower() == 'aix'
+def is_os2() -> bool:
+ return platform.system().lower() == 'os/2'
+
@lru_cache(maxsize=None)
def darwin_get_object_archs(objpath: str) -> 'ImmutableListProtocol[str]':
'''
@@ -756,6 +764,20 @@ class VcsData:
rev_regex: str
dep: str
wc_dir: T.Optional[str] = None
+ repo_can_be_file: bool = False
+
+ def repo_exists(self, curdir: Path) -> bool:
+ if not shutil.which(self.cmd):
+ return False
+
+ repo = curdir / self.repo_dir
+ if repo.is_dir():
+ return True
+ if repo.is_file() and self.repo_can_be_file:
+ return True
+
+ return False
+
def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[VcsData]:
vcs_systems = [
@@ -766,6 +788,7 @@ def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[VcsData]:
get_rev = ['git', 'describe', '--dirty=+', '--always'],
rev_regex = '(.*)',
dep = '.git/logs/HEAD',
+ repo_can_be_file=True,
),
VcsData(
name = 'mercurial',
@@ -801,9 +824,7 @@ def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[VcsData]:
parent_paths_and_self.appendleft(source_dir)
for curdir in parent_paths_and_self:
for vcs in vcs_systems:
- repodir = vcs.repo_dir
- cmd = vcs.cmd
- if curdir.joinpath(repodir).is_dir() and shutil.which(cmd):
+ if vcs.repo_exists(curdir):
vcs.wc_dir = str(curdir)
return vcs
return None
@@ -1226,7 +1247,7 @@ def do_replacement(regex: T.Pattern[str], line: str,
if variable_format == 'meson':
return do_replacement_meson(regex, line, confdata)
elif variable_format in {'cmake', 'cmake@'}:
- return do_replacement_cmake(regex, line, variable_format == 'cmake@', confdata)
+ return do_replacement_cmake(line, variable_format == 'cmake@', confdata)
else:
raise MesonException('Invalid variable format')
@@ -1251,6 +1272,9 @@ def do_replacement_meson(regex: T.Pattern[str], line: str,
if isinstance(var, str):
var_str = var
elif isinstance(var, int):
+ if isinstance(var, bool):
+ msg = f'Variable substitution with boolean value {varname!r} is deprecated.'
+ mlog.deprecation(msg)
var_str = str(var)
else:
msg = f'Tried to replace variable {varname!r} value with ' \
@@ -1261,44 +1285,92 @@ def do_replacement_meson(regex: T.Pattern[str], line: str,
return var_str
return re.sub(regex, variable_replace, line), missing_variables
-def do_replacement_cmake(regex: T.Pattern[str], line: str, at_only: bool,
+def do_replacement_cmake(line: str, at_only: bool,
confdata: T.Union[T.Dict[str, T.Tuple[str, T.Optional[str]]], 'ConfigurationData']) -> T.Tuple[str, T.Set[str]]:
missing_variables: T.Set[str] = set()
- def variable_replace(match: T.Match[str]) -> str:
- # Pairs of escape characters before '@', '\@', '${' or '\${'
- if match.group(0).endswith('\\'):
- num_escapes = match.end(0) - match.start(0)
- return '\\' * (num_escapes // 2)
- # Handle cmake escaped \${} tags
- elif not at_only and match.group(0) == '\\${':
- return '${'
- # \@escaped\@ variables
- elif match.groupdict().get('escaped') is not None:
- return match.group('escaped')[1:-2]+'@'
+ character_regex = re.compile(r'''
+ [^a-zA-Z0-9_/.+\-]
+ ''', re.VERBOSE)
+
+ def variable_get(varname: str) -> str:
+ var_str = ''
+ if varname in confdata:
+ var, _ = confdata.get(varname)
+ if isinstance(var, str):
+ var_str = var
+ elif isinstance(var, bool):
+ var_str = str(int(var))
+ elif isinstance(var, int):
+ var_str = str(var)
+ else:
+ msg = f'Tried to replace variable {varname!r} value with ' \
+ f'something other than a string or int: {var!r}'
+ raise MesonException(msg)
else:
- # Template variable to be replaced
- varname = match.group('variable')
- if not varname:
- varname = match.group('cmake_variable')
-
- var_str = ''
- if varname in confdata:
- var, _ = confdata.get(varname)
- if isinstance(var, str):
- var_str = var
- elif isinstance(var, bool):
- var_str = str(int(var))
- elif isinstance(var, int):
- var_str = str(var)
- else:
- msg = f'Tried to replace variable {varname!r} value with ' \
- f'something other than a string or int: {var!r}'
+ missing_variables.add(varname)
+ return var_str
+
+ def parse_line(line: str) -> str:
+ index = 0
+ while len(line) > index:
+ if line[index] == '@':
+ next_at = line.find("@", index+1)
+ if next_at > index+1:
+ varname = line[index+1:next_at]
+ match = character_regex.search(varname)
+
+ # at substituion doesn't occur if they key isn't valid
+ # however it also doesn't raise an error
+ if not match:
+ value = variable_get(varname)
+ line = line[:index] + value + line[next_at+1:]
+
+ elif not at_only and line[index:index+2] == '${':
+ bracket_count = 1
+ end_bracket = index + 2
+ try:
+ while bracket_count > 0:
+ if line[end_bracket:end_bracket+2] == "${":
+ end_bracket += 2
+ bracket_count += 1
+ elif line[end_bracket] == "}":
+ end_bracket += 1
+ bracket_count -= 1
+ elif line[end_bracket] in {"@", "\n"}:
+ # these aren't valid variable characters
+ # but they are inconsequential at this point
+ end_bracket += 1
+ elif character_regex.search(line[end_bracket]):
+ invalid_character = line[end_bracket]
+ variable = line[index+2:end_bracket]
+ msg = f'Found invalid character {invalid_character!r}' \
+ f' in variable {variable!r}'
+ raise MesonException(msg)
+ else:
+ end_bracket += 1
+ except IndexError:
+ msg = f'Found incomplete variable {line[index:-1]!r}'
raise MesonException(msg)
- else:
- missing_variables.add(varname)
- return var_str
- return re.sub(regex, variable_replace, line), missing_variables
+
+ if bracket_count == 0:
+ varname = parse_line(line[index+2:end_bracket-1])
+ match = character_regex.search(varname)
+ if match:
+ invalid_character = line[end_bracket-2]
+ variable = line[index+2:end_bracket-3]
+ msg = f'Found invalid character {invalid_character!r}' \
+ f' in variable {variable!r}'
+ raise MesonException(msg)
+
+ value = variable_get(varname)
+ line = line[:index] + value + line[end_bracket:]
+
+ index += 1
+
+ return line
+
+ return parse_line(line), missing_variables
def do_define_meson(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData',
subproject: T.Optional[SubProject] = None) -> str:
@@ -1327,12 +1399,12 @@ def do_define_meson(regex: T.Pattern[str], line: str, confdata: 'ConfigurationDa
else:
raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)
-def do_define_cmake(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData', at_only: bool,
+def do_define_cmake(line: str, confdata: 'ConfigurationData', at_only: bool,
subproject: T.Optional[SubProject] = None) -> str:
cmake_bool_define = 'cmakedefine01' in line
def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str:
- arr = line.split()
+ arr = line[1:].split()
if cmake_bool_define:
(v, desc) = confdata.get(arr[1])
@@ -1347,7 +1419,7 @@ def do_define_cmake(regex: T.Pattern[str], line: str, confdata: 'ConfigurationDa
define_value += [token]
return ' '.join(define_value)
- arr = line.split()
+ arr = line[1:].split()
if len(arr) != 2 and subproject is not None:
from ..interpreterbase.decorators import FeatureNew
@@ -1367,12 +1439,12 @@ def do_define_cmake(regex: T.Pattern[str], line: str, confdata: 'ConfigurationDa
result = get_cmake_define(line, confdata)
result = f'#define {varname} {result}'.strip() + '\n'
- result, _ = do_replacement_cmake(regex, result, at_only, confdata)
+ result, _ = do_replacement_cmake(result, at_only, confdata)
return result
def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'meson') -> T.Pattern[str]:
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
- if variable_format in {'meson', 'cmake@'}:
+ if variable_format == 'meson':
# Also allow escaping pairs of '@' with '\@'
regex = re.compile(r'''
(?:\\\\)+(?=\\?@) # Matches multiple backslashes followed by an @ symbol
@@ -1381,17 +1453,13 @@ def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'm
| # OR
(?P<escaped>\\@[-a-zA-Z0-9_]+\\@) # Match an escaped variable enclosed in @ symbols
''', re.VERBOSE)
- else:
+ elif variable_format == 'cmake@':
regex = re.compile(r'''
- (?:\\\\)+(?=\\?(\$|@)) # Match multiple backslashes followed by a dollar sign or an @ symbol
- | # OR
- \\\${ # Match a backslash followed by a dollar sign and an opening curly brace
- | # OR
- \${(?P<cmake_variable>[-a-zA-Z0-9_]+)} # Match a variable enclosed in curly braces and capture the variable name
- | # OR
(?<!\\)@(?P<variable>[-a-zA-Z0-9_]+)@ # Match a variable enclosed in @ symbols and capture the variable name; no matches beginning with '\@'
- | # OR
- (?P<escaped>\\@[-a-zA-Z0-9_]+\\@) # Match an escaped variable enclosed in @ symbols
+ ''', re.VERBOSE)
+ elif variable_format == "cmake":
+ regex = re.compile(r'''
+ \${(?P<variable>[-a-zA-Z0-9_]*)} # Match a variable enclosed in curly braces and capture the variable name
''', re.VERBOSE)
return regex
@@ -1439,9 +1507,7 @@ def do_conf_str_cmake(src: str, data: T.List[str], confdata: 'ConfigurationData'
if at_only:
variable_format = 'cmake@'
- regex = get_variable_regex(variable_format)
-
- search_token = '#cmakedefine'
+ search_token = 'cmakedefine'
result: T.List[str] = []
missing_variables: T.Set[str] = set()
@@ -1449,13 +1515,15 @@ def do_conf_str_cmake(src: str, data: T.List[str], confdata: 'ConfigurationData'
# during substitution so we can warn the user to use the `copy:` kwarg.
confdata_useless = not confdata.keys()
for line in data:
- if line.lstrip().startswith(search_token):
+ stripped_line = line.lstrip()
+ if len(stripped_line) >= 2 and stripped_line[0] == '#' and stripped_line[1:].lstrip().startswith(search_token):
confdata_useless = False
- line = do_define_cmake(regex, line, confdata, at_only, subproject)
+
+ line = do_define_cmake(line, confdata, at_only, subproject)
else:
if '#mesondefine' in line:
raise MesonException(f'Format error in {src}: saw "{line.strip()}" when format set to "{variable_format}"')
- line, missing = do_replacement_cmake(regex, line, at_only, confdata)
+ line, missing = do_replacement_cmake(line, at_only, confdata)
missing_variables.update(missing)
if missing:
confdata_useless = False
@@ -1578,7 +1646,7 @@ def listify(item: T.Any, flatten: bool = True) -> T.List[T.Any]:
result.append(i)
return result
-def listify_array_value(value: T.Union[str, T.List[str]], shlex_split_args: bool = False) -> T.List[str]:
+def listify_array_value(value: object, shlex_split_args: bool = False) -> T.List[str]:
if isinstance(value, str):
if value.startswith('['):
try:
@@ -1629,6 +1697,8 @@ def typeslistify(item: 'T.Union[_T, T.Sequence[_T]]',
def stringlistify(item: T.Union[T.Any, T.Sequence[T.Any]]) -> T.List[str]:
return typeslistify(item, str)
+def underscorify(item: str) -> str:
+ return re.sub(r'[^a-zA-Z0-9]', '_', item)
def expand_arguments(args: T.Iterable[str]) -> T.Optional[T.List[str]]:
expended_args: T.List[str] = []
@@ -1738,7 +1808,7 @@ def Popen_safe_logged(args: T.List[str], msg: str = 'Called', **kwargs: T.Any) -
return p, o, e
-def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str]) -> T.Optional[str]:
+def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str | programs.ExternalProgram]) -> T.Optional[str]:
'''
Takes each regular expression in @regexiter and tries to search for it in
every item in @initer. If there is a match, returns that match.
@@ -1754,7 +1824,7 @@ def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str]) -> T.
return None
-def _substitute_values_check_errors(command: T.List[str], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None:
+def _substitute_values_check_errors(command: T.List[str | programs.ExternalProgram], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None:
# Error checking
inregex: T.List[str] = ['@INPUT([0-9]+)?@', '@PLAINNAME@', '@BASENAME@']
outregex: T.List[str] = ['@OUTPUT([0-9]+)?@', '@OUTDIR@']
@@ -1794,7 +1864,7 @@ def _substitute_values_check_errors(command: T.List[str], values: T.Dict[str, T.
raise MesonException(m.format(match2.group(), len(values['@OUTPUT@'])))
-def substitute_values(command: T.List[str], values: T.Dict[str, T.Union[str, T.List[str]]]) -> T.List[str]:
+def substitute_values(command: T.List[str | programs.ExternalProgram], values: T.Dict[str, T.Union[str, T.List[str]]]) -> T.List[str | programs.ExternalProgram]:
'''
Substitute the template strings in the @values dict into the list of
strings @command and return a new list. For a full list of the templates,
@@ -1821,7 +1891,7 @@ def substitute_values(command: T.List[str], values: T.Dict[str, T.Union[str, T.L
_substitute_values_check_errors(command, values)
# Substitution
- outcmd: T.List[str] = []
+ outcmd: T.List[str | programs.ExternalProgram] = []
rx_keys = [re.escape(key) for key in values if key not in ('@INPUT@', '@OUTPUT@')]
value_rx = re.compile('|'.join(rx_keys)) if rx_keys else None
for vv in command:
@@ -2003,6 +2073,8 @@ def detect_subprojects(spdir_name: str, current_dir: str = '',
continue
append_this = True
if os.path.isdir(trial):
+ spdir_name = get_subproject_dir(trial) or 'subprojects'
+
detect_subprojects(spdir_name, trial, result)
elif trial.endswith('.wrap') and os.path.isfile(trial):
basename = os.path.splitext(basename)[0]
@@ -2023,6 +2095,10 @@ def substring_is_in_list(substr: str, strlist: T.List[str]) -> bool:
return False
+def unique_list(x: T.Iterable[_T]) -> T.List[_T]:
+ return list(dict.fromkeys(x))
+
+
class OrderedSet(T.MutableSet[_T]):
"""A set that preserves the order in which items are added, by first
insertion.
@@ -2167,6 +2243,7 @@ _BUILTIN_NAMES = {
'pkg_config_path',
'cmake_prefix_path',
'vsenv',
+ 'os2_emxomf',
}
@@ -2436,3 +2513,23 @@ class lazy_property(T.Generic[_T]):
value = self.__func(instance)
setattr(instance, self.__name, value)
return value
+
+
+def get_subproject_dir(directory: str = '.') -> T.Optional[str]:
+ """Get the name of the subproject directory for a specific project.
+
+ If the subproject does not have a meson.build file, it is called in an
+ invalid directory, it returns None
+
+ :param directory: Where to search, defaults to current working directory
+ :return: the name of the subproject directory or None.
+ """
+ from ..ast import IntrospectionInterpreter
+ from ..interpreterbase.exceptions import InvalidArguments
+ intr = IntrospectionInterpreter(directory, '', 'none')
+ try:
+ intr.load_root_meson_file()
+ except InvalidArguments: # Root meson file cannot be found
+ return None
+
+ return intr.extract_subproject_dir() or 'subprojects'
diff --git a/mesonbuild/utils/vsenv.py b/mesonbuild/utils/vsenv.py
index 5a02379..3cc9b4b 100644
--- a/mesonbuild/utils/vsenv.py
+++ b/mesonbuild/utils/vsenv.py
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2012-2025 The Meson development team
+
from __future__ import annotations
import os
@@ -6,11 +9,12 @@ import json
import pathlib
import shutil
import tempfile
-import locale
+import pickle
from .. import mlog
from .core import MesonException
-from .universal import is_windows, windows_detect_native_arch
+from .universal import (is_windows, windows_detect_native_arch, windows_proof_rm,
+ get_meson_command, join_args)
__all__ = [
@@ -18,14 +22,6 @@ __all__ = [
]
-bat_template = '''@ECHO OFF
-
-call "{}"
-
-ECHO {}
-SET
-'''
-
# If on Windows and VS is installed but not set up in the environment,
# set it to be runnable. In this way Meson can be directly invoked
# from any shell, VS Code etc.
@@ -88,32 +84,24 @@ def _setup_vsenv(force: bool) -> bool:
raise MesonException(f'Could not find {bat_path}')
mlog.log('Activating VS', bat_info[0]['catalog']['productDisplayVersion'])
- bat_separator = '---SPLIT---'
- bat_contents = bat_template.format(bat_path, bat_separator)
- bat_file = tempfile.NamedTemporaryFile('w', suffix='.bat', encoding='utf-8', delete=False)
- bat_file.write(bat_contents)
- bat_file.flush()
- bat_file.close()
- bat_output = subprocess.check_output(bat_file.name, universal_newlines=True,
- encoding=locale.getpreferredencoding(False))
- os.unlink(bat_file.name)
- bat_lines = bat_output.split('\n')
- bat_separator_seen = False
- for bat_line in bat_lines:
- if bat_line == bat_separator:
- bat_separator_seen = True
- continue
- if not bat_separator_seen:
- continue
- if not bat_line:
- continue
- try:
- k, v = bat_line.split('=', 1)
- except ValueError:
- # there is no "=", ignore junk data
- pass
- else:
+ # Write a bat file that first activates VS environment, and then calls
+ # a Meson script that pickles the environment into a temp file.
+ with tempfile.NamedTemporaryFile(delete=False) as env_file:
+ pass
+ vcvars_cmd = ['call', str(bat_path)]
+ pickle_cmd = get_meson_command() + ['--internal', 'pickle_env', env_file.name]
+ with tempfile.NamedTemporaryFile('w', suffix='.bat', encoding='utf-8', delete=False) as bat_file:
+ bat_file.write(join_args(vcvars_cmd) + '\n')
+ bat_file.write(join_args(pickle_cmd))
+ try:
+ subprocess.check_call([bat_file.name], stdout=subprocess.DEVNULL)
+ with open(env_file.name, 'rb') as f:
+ vsenv = pickle.load(f)
+ for k, v in vsenv.items():
os.environ[k] = v
+ finally:
+ windows_proof_rm(env_file.name)
+ windows_proof_rm(bat_file.name)
return True
def setup_vsenv(force: bool = False) -> bool:
diff --git a/mesonbuild/utils/win32.py b/mesonbuild/utils/win32.py
deleted file mode 100644
index 4fcb8ed..0000000
--- a/mesonbuild/utils/win32.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-# Copyright 2012-2021 The Meson development team
-# Copyright © 2021-2023 Intel Corporation
-
-from __future__ import annotations
-
-"""Windows specific implementations of mesonlib functionality."""
-
-import msvcrt
-import typing as T
-
-from .core import MesonException
-from .platform import BuildDirLock as BuildDirLockBase
-
-__all__ = ['BuildDirLock']
-
-class BuildDirLock(BuildDirLockBase):
-
- def __enter__(self) -> None:
- self.lockfile = open(self.lockfilename, 'w', encoding='utf-8')
- try:
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
- except (BlockingIOError, PermissionError):
- self.lockfile.close()
- raise MesonException('Some other Meson process is already using this build directory. Exiting.')
-
- def __exit__(self, *args: T.Any) -> None:
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
- self.lockfile.close()
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 9af1f39..67ca96b 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -21,6 +21,7 @@ import time
import typing as T
import textwrap
import json
+import gzip
from base64 import b64encode
from netrc import netrc
@@ -29,7 +30,10 @@ from functools import lru_cache
from . import WrapMode
from .. import coredata
-from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree, Popen_safe
+from ..mesonlib import (
+ DirectoryLock, DirectoryLockAction, quiet_git, GIT, ProgressBar, MesonException,
+ windows_proof_rmtree, Popen_safe
+)
from ..interpreterbase import FeatureNew
from ..interpreterbase import SubProject
from .. import mesonlib
@@ -53,7 +57,42 @@ WHITELIST_SUBDOMAIN = 'wrapdb.mesonbuild.com'
ALL_TYPES = ['file', 'git', 'hg', 'svn', 'redirect']
-PATCH = shutil.which('patch')
+if sys.version_info >= (3, 14):
+ import tarfile
+ tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter)
+
+if mesonlib.is_windows():
+ from ..programs import ExternalProgram
+ from ..mesonlib import version_compare
+ _exclude_paths: T.List[str] = []
+ while True:
+ _patch = ExternalProgram('patch', silent=True, exclude_paths=_exclude_paths)
+ if not _patch.found():
+ break
+ if version_compare(_patch.get_version(), '>=2.6.1'):
+ break
+ _exclude_paths.append(os.path.dirname(_patch.get_path()))
+ PATCH = _patch.get_path() if _patch.found() else None
+else:
+ PATCH = shutil.which('patch')
+
+
+truststore_message = '''
+
+ If you believe the connection should be secure, but python cannot see the
+ correct SSL certificates, install https://truststore.readthedocs.io/ and
+ try again.'''
+
+@lru_cache(maxsize=None)
+def ssl_truststore() -> T.Optional[ssl.SSLContext]:
+ """ Provide a default context=None for urlopen, but use truststore if installed. """
+ try:
+ import truststore
+ except ImportError:
+ # use default
+ return None
+ else:
+ return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult:
""" raises WrapException if not whitelisted subdomain """
@@ -66,21 +105,30 @@ def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult:
raise WrapException(f'WrapDB did not have expected SSL https url, instead got {urlstr}')
return url
-def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool = False) -> 'http.client.HTTPResponse':
+def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool = False, allow_compression: bool = False) -> http.client.HTTPResponse:
if have_opt:
insecure_msg = '\n\n To allow connecting anyway, pass `--allow-insecure`.'
else:
insecure_msg = ''
+ def do_urlopen(url: urllib.parse.ParseResult) -> http.client.HTTPResponse:
+ headers = {}
+ if allow_compression:
+ headers['Accept-Encoding'] = 'gzip'
+ req = urllib.request.Request(urllib.parse.urlunparse(url), headers=headers)
+ return T.cast('http.client.HTTPResponse', urllib.request.urlopen(req, timeout=REQ_TIMEOUT, context=ssl_truststore()))
+
url = whitelist_wrapdb(urlstring)
if has_ssl:
try:
- return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(url), timeout=REQ_TIMEOUT))
+ return do_urlopen(url)
except OSError as excp:
msg = f'WrapDB connection failed to {urlstring} with error {excp}.'
if isinstance(excp, urllib.error.URLError) and isinstance(excp.reason, ssl.SSLCertVerificationError):
if allow_insecure:
mlog.warning(f'{msg}\n\n Proceeding without authentication.')
+ elif ssl_truststore() is None:
+ raise WrapException(f'{msg}{insecure_msg}{truststore_message}')
else:
raise WrapException(f'{msg}{insecure_msg}')
else:
@@ -92,15 +140,24 @@ def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool
mlog.warning(f'SSL module not available in {sys.executable}: WrapDB traffic not authenticated.', once=True)
# If we got this far, allow_insecure was manually passed
- nossl_url = url._replace(scheme='http')
try:
- return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(nossl_url), timeout=REQ_TIMEOUT))
+ return do_urlopen(url._replace(scheme='http'))
except OSError as excp:
raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}')
+def read_and_decompress(resp: http.client.HTTPResponse) -> bytes:
+ data = resp.read()
+ encoding = resp.headers['Content-Encoding']
+ if encoding == 'gzip':
+ return gzip.decompress(data)
+ elif encoding:
+ raise WrapException(f'Unexpected Content-Encoding for {resp.url}: {encoding}')
+ else:
+ return data
+
def get_releases_data(allow_insecure: bool) -> bytes:
- url = open_wrapdburl('https://wrapdb.mesonbuild.com/v2/releases.json', allow_insecure, True)
- return url.read()
+ url = open_wrapdburl('https://wrapdb.mesonbuild.com/v2/releases.json', allow_insecure, True, True)
+ return read_and_decompress(url)
@lru_cache(maxsize=None)
def get_releases(allow_insecure: bool) -> T.Dict[str, T.Any]:
@@ -109,9 +166,9 @@ def get_releases(allow_insecure: bool) -> T.Dict[str, T.Any]:
def update_wrap_file(wrapfile: str, name: str, new_version: str, new_revision: str, allow_insecure: bool) -> None:
url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{name}_{new_version}-{new_revision}/{name}.wrap',
- allow_insecure, True)
+ allow_insecure, True, True)
with open(wrapfile, 'wb') as f:
- f.write(url.read())
+ f.write(read_and_decompress(url))
def parse_patch_url(patch_url: str) -> T.Tuple[str, str]:
u = urllib.parse.urlparse(patch_url)
@@ -130,6 +187,7 @@ def parse_patch_url(patch_url: str) -> T.Tuple[str, str]:
else:
raise WrapException(f'Invalid wrapdb URL {patch_url}')
+
class WrapException(MesonException):
pass
@@ -213,6 +271,15 @@ class PackageDefinition:
wrap.original_filename = filename
wrap.parse_provide_section(config)
+ patch_url = values.get('patch_url')
+ if patch_url and patch_url.startswith('https://wrapdb.mesonbuild.com/v1'):
+ if name == 'sqlite':
+ mlog.deprecation('sqlite wrap has been renamed to sqlite3, update using `meson wrap install sqlite3`')
+ elif name == 'libjpeg':
+ mlog.deprecation('libjpeg wrap has been renamed to libjpeg-turbo, update using `meson wrap install libjpeg-turbo`')
+ else:
+ mlog.deprecation(f'WrapDB v1 is deprecated, updated using `meson wrap update {name}`')
+
with open(filename, 'r', encoding='utf-8') as file:
wrap.wrapfile_hash = hashlib.sha256(file.read().encode('utf-8')).hexdigest()
@@ -275,6 +342,9 @@ class PackageDefinition:
with open(self.get_hashfile(subproject_directory), 'w', encoding='utf-8') as file:
file.write(self.wrapfile_hash + '\n')
+ def add_provided_dep(self, name: str) -> None:
+ self.provided_deps[name] = None
+
def get_directory(subdir_root: str, packagename: str) -> str:
fname = os.path.join(subdir_root, packagename + '.wrap')
if os.path.isfile(fname):
@@ -311,6 +381,7 @@ class Resolver:
self.wrapdb: T.Dict[str, T.Any] = {}
self.wrapdb_provided_deps: T.Dict[str, str] = {}
self.wrapdb_provided_programs: T.Dict[str, str] = {}
+ self.loaded_dirs: T.Set[str] = set()
self.load_wraps()
self.load_netrc()
self.load_wrapdb()
@@ -324,12 +395,6 @@ class Resolver:
mlog.warning(f'failed to process netrc file: {e}.', fatal=False)
def load_wraps(self) -> None:
- # Load Cargo.lock at the root of source tree
- source_dir = os.path.dirname(self.subdir_root)
- if os.path.exists(os.path.join(source_dir, 'Cargo.lock')):
- from .. import cargo
- for wrap in cargo.load_wraps(source_dir, self.subdir_root):
- self.wraps[wrap.name] = wrap
# Load subprojects/*.wrap
if os.path.isdir(self.subdir_root):
root, dirs, files = next(os.walk(self.subdir_root))
@@ -352,6 +417,7 @@ class Resolver:
# Add provided deps and programs into our lookup tables
for wrap in self.wraps.values():
self.add_wrap(wrap)
+ self.loaded_dirs.add(self.subdir)
def add_wrap(self, wrap: PackageDefinition) -> None:
for k in wrap.provided_deps.keys():
@@ -384,28 +450,37 @@ class Resolver:
self.check_can_download()
latest_version = info['versions'][0]
version, revision = latest_version.rsplit('-', 1)
- url = urllib.request.urlopen(f'https://wrapdb.mesonbuild.com/v2/{subp_name}_{version}-{revision}/{subp_name}.wrap')
+ url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{subp_name}_{version}-{revision}/{subp_name}.wrap', allow_compression=True)
fname = Path(self.subdir_root, f'{subp_name}.wrap')
with fname.open('wb') as f:
- f.write(url.read())
+ f.write(read_and_decompress(url))
mlog.log(f'Installed {subp_name} version {version} revision {revision}')
wrap = PackageDefinition.from_wrap_file(str(fname))
self.wraps[wrap.name] = wrap
self.add_wrap(wrap)
return wrap
- def _merge_wraps(self, other_resolver: 'Resolver') -> None:
- for k, v in other_resolver.wraps.items():
- self.wraps.setdefault(k, v)
- for k, v in other_resolver.provided_deps.items():
- self.provided_deps.setdefault(k, v)
- for k, v in other_resolver.provided_programs.items():
- self.provided_programs.setdefault(k, v)
+ def merge_wraps(self, wraps: T.Dict[str, PackageDefinition]) -> None:
+ for k, v in wraps.items():
+ prev_wrap = self.wraps.get(v.directory)
+ if prev_wrap and prev_wrap.type is None and v.type is not None:
+ # This happens when a subproject has been previously downloaded
+ # using a wrap from another subproject and the wrap-redirect got
+ # deleted. In that case, the main project created a bare wrap
+ # for the download directory, but now we have a proper wrap.
+ # It also happens for wraps coming from Cargo.lock files, which
+ # don't create wrap-redirect.
+ del self.wraps[v.directory]
+ del self.provided_deps[v.directory.lower()]
+ if k not in self.wraps:
+ self.wraps[k] = v
+ self.add_wrap(v)
def load_and_merge(self, subdir: str, subproject: SubProject) -> None:
- if self.wrap_mode != WrapMode.nopromote:
+ if self.wrap_mode != WrapMode.nopromote and subdir not in self.loaded_dirs:
other_resolver = Resolver(self.source_dir, subdir, subproject, self.wrap_mode, self.wrap_frontend, self.allow_insecure, self.silent)
- self._merge_wraps(other_resolver)
+ self.merge_wraps(other_resolver.wraps)
+ self.loaded_dirs.add(subdir)
def find_dep_provider(self, packagename: str) -> T.Tuple[T.Optional[str], T.Optional[str]]:
# Python's ini parser converts all key values to lowercase.
@@ -432,7 +507,7 @@ class Resolver:
return wrap_name
return None
- def resolve(self, packagename: str, force_method: T.Optional[Method] = None) -> T.Tuple[str, Method]:
+ def _resolve(self, packagename: str, force_method: T.Optional[Method] = None) -> T.Tuple[str, Method]:
wrap = self.wraps.get(packagename)
if wrap is None:
wrap = self.get_from_wrapdb(packagename)
@@ -530,6 +605,15 @@ class Resolver:
self.wrap.update_hash_cache(self.dirname)
return rel_path, method
+ def resolve(self, packagename: str, force_method: T.Optional[Method] = None) -> T.Tuple[str, Method]:
+ try:
+ with DirectoryLock(self.subdir_root, '.wraplock',
+ DirectoryLockAction.WAIT,
+ 'Failed to lock subprojects directory', optional=True):
+ return self._resolve(packagename, force_method)
+ except FileNotFoundError:
+ raise WrapNotFoundException('Attempted to resolve subproject without subprojects directory present.')
+
def check_can_download(self) -> None:
# Don't download subproject data based on wrap file if requested.
# Git submodules are ok (see above)!
@@ -691,6 +775,23 @@ class Resolver:
resp = open_wrapdburl(urlstring, allow_insecure=self.allow_insecure, have_opt=self.wrap_frontend)
elif WHITELIST_SUBDOMAIN in urlstring:
raise WrapException(f'{urlstring} may be a WrapDB-impersonating URL')
+ elif url.scheme == 'sftp':
+ sftp = shutil.which('sftp')
+ if sftp is None:
+ raise WrapException('Scheme sftp is not available. Install sftp to enable it.')
+ with tempfile.TemporaryDirectory() as workdir, \
+ tempfile.NamedTemporaryFile(mode='wb', dir=self.cachedir, delete=False) as tmpfile:
+ args = []
+ # Older versions of the sftp client cannot handle URLs, hence the splitting of url below
+ if url.port:
+ args += ['-P', f'{url.port}']
+ user = f'{url.username}@' if url.username else ''
+ command = [sftp, '-o', 'KbdInteractiveAuthentication=no', *args, f'{user}{url.hostname}:{url.path[1:]}']
+ subprocess.run(command, cwd=workdir, check=True)
+ downloaded = os.path.join(workdir, os.path.basename(url.path))
+ tmpfile.close()
+ shutil.move(downloaded, tmpfile.name)
+ return self.hash_file(tmpfile.name), tmpfile.name
else:
headers = {
'User-Agent': f'mesonbuild/{coredata.version}',
@@ -712,10 +813,13 @@ class Resolver:
try:
req = urllib.request.Request(urlstring, headers=headers)
- resp = urllib.request.urlopen(req, timeout=REQ_TIMEOUT)
+ resp = urllib.request.urlopen(req, timeout=REQ_TIMEOUT, context=ssl_truststore())
except OSError as e:
mlog.log(str(e))
- raise WrapException(f'could not get {urlstring} is the internet available?')
+ if isinstance(e, urllib.error.URLError) and isinstance(e.reason, ssl.SSLCertVerificationError) and ssl_truststore() is None:
+ raise WrapException(f'could not get {urlstring}; is the internet available?{truststore_message}')
+ else:
+ raise WrapException(f'could not get {urlstring}; is the internet available?')
with contextlib.closing(resp) as resp, tmpfile as tmpfile:
try:
dlsize = int(resp.info()['Content-Length'])
@@ -746,14 +850,17 @@ class Resolver:
hashvalue = h.hexdigest()
return hashvalue, tmpfile.name
+ def hash_file(self, path: str) -> str:
+ h = hashlib.sha256()
+ with open(path, 'rb') as f:
+ h.update(f.read())
+ return h.hexdigest()
+
def check_hash(self, what: str, path: str, hash_required: bool = True) -> None:
if what + '_hash' not in self.wrap.values and not hash_required:
return
expected = self.wrap.get(what + '_hash').lower()
- h = hashlib.sha256()
- with open(path, 'rb') as f:
- h.update(f.read())
- dhash = h.hexdigest()
+ dhash = self.hash_file(path)
if dhash != expected:
raise WrapException(f'Incorrect hash for {what}:\n {expected} expected\n {dhash} actual.')
diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py
index 5486a26..8517511 100644
--- a/mesonbuild/wrap/wraptool.py
+++ b/mesonbuild/wrap/wraptool.py
@@ -9,10 +9,8 @@ import shutil
import typing as T
from glob import glob
-from .wrap import (open_wrapdburl, WrapException, get_releases, get_releases_data,
- parse_patch_url)
-from pathlib import Path
-
+from .wrap import (open_wrapdburl, read_and_decompress, WrapException, get_releases,
+ get_releases_data, parse_patch_url)
from .. import mesonlib, msubprojects
if T.TYPE_CHECKING:
@@ -91,17 +89,18 @@ def get_latest_version(name: str, allow_insecure: bool) -> T.Tuple[str, str]:
def install(options: 'argparse.Namespace') -> None:
name = options.name
- if not os.path.isdir('subprojects'):
+ subproject_dir_name = mesonlib.get_subproject_dir()
+ if subproject_dir_name is None or not os.path.isdir(subproject_dir_name):
raise SystemExit('Subprojects dir not found. Run this script in your source root directory.')
- if os.path.isdir(os.path.join('subprojects', name)):
+ if os.path.isdir(os.path.join(subproject_dir_name, name)):
raise SystemExit('Subproject directory for this project already exists.')
- wrapfile = os.path.join('subprojects', name + '.wrap')
+ wrapfile = os.path.join(subproject_dir_name, name + '.wrap')
if os.path.exists(wrapfile):
raise SystemExit('Wrap file already exists.')
(version, revision) = get_latest_version(name, options.allow_insecure)
- url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{name}_{version}-{revision}/{name}.wrap', options.allow_insecure, True)
+ url = open_wrapdburl(f'https://wrapdb.mesonbuild.com/v2/{name}_{version}-{revision}/{name}.wrap', options.allow_insecure, True, True)
with open(wrapfile, 'wb') as f:
- f.write(url.read())
+ f.write(read_and_decompress(url))
print(f'Installed {name} version {version} revision {revision}')
def get_current_version(wrapfile: str) -> T.Tuple[str, str, str, str, T.Optional[str]]:
@@ -143,11 +142,20 @@ def do_promotion(from_path: str, spdir_name: str) -> None:
outputdir = os.path.join(spdir_name, sproj_name)
if os.path.exists(outputdir):
raise SystemExit(f'Output dir {outputdir} already exists. Will not overwrite.')
- shutil.copytree(from_path, outputdir, ignore=shutil.ignore_patterns('subprojects'))
+
+ subpdir = mesonlib.get_subproject_dir()
+ if subpdir is not None:
+ ignore = shutil.ignore_patterns(subpdir)
+ else:
+ ignore = None
+
+ shutil.copytree(from_path, outputdir, ignore=ignore)
def promote(options: 'argparse.Namespace') -> None:
argument = options.project_path
- spdir_name = 'subprojects'
+ spdir_name = mesonlib.get_subproject_dir()
+ if spdir_name is None:
+ raise SystemExit('Subproject dir not found. Run this script in your source root directory.')
sprojs = mesonlib.detect_subprojects(spdir_name)
# check if the argument is a full path to a subproject directory or wrap file
@@ -170,7 +178,9 @@ def promote(options: 'argparse.Namespace') -> None:
def status(options: 'argparse.Namespace') -> None:
print('Subproject status')
- for w in glob('subprojects/*.wrap'):
+ subdir = mesonlib.get_subproject_dir()
+ assert subdir is not None, "This should only happen in a non-native subproject"
+ for w in glob(f'{subdir}/*.wrap'):
name = os.path.basename(w)[:-5]
try:
(latest_branch, latest_revision) = get_latest_version(name, options.allow_insecure)
@@ -189,8 +199,12 @@ def status(options: 'argparse.Namespace') -> None:
def update_db(options: 'argparse.Namespace') -> None:
data = get_releases_data(options.allow_insecure)
- Path('subprojects').mkdir(exist_ok=True)
- with Path('subprojects/wrapdb.json').open('wb') as f:
+ subproject_dir_name = mesonlib.get_subproject_dir()
+ if subproject_dir_name is None:
+ raise SystemExit('Subproject dir not found. Run this script in your source root directory.')
+
+ os.makedirs(subproject_dir_name, exist_ok=True)
+ with open(os.path.join(subproject_dir_name, 'wrapdb.json'), 'wb') as f:
f.write(data)
def run(options: 'argparse.Namespace') -> int:
diff --git a/packaging/builddist.py b/packaging/builddist.py
new file mode 100755
index 0000000..5cf3b02
--- /dev/null
+++ b/packaging/builddist.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2025 The Meson development team
+
+# This script must be run from the source root.
+
+import pathlib, shutil, subprocess
+
+gendir = pathlib.Path('distgendir')
+distdir = pathlib.Path('dist')
+gitdir = pathlib.Path('.git')
+
+if distdir.is_dir():
+ shutil.rmtree(distdir)
+distdir.mkdir()
+
+if gendir.is_dir():
+ shutil.rmtree(gendir)
+gendir.mkdir()
+
+shutil.copytree(gitdir, gendir / '.git')
+
+subprocess.check_call(['git', 'reset', '--hard'],
+ cwd=gendir)
+subprocess.check_call(['python3', 'setup.py', 'sdist', 'bdist_wheel'],
+ cwd=gendir)
+for f in (gendir / 'dist').glob('*'):
+ shutil.copy(f, distdir)
+
+shutil.rmtree(gendir)
+
diff --git a/packaging/builddist.sh b/packaging/builddist.sh
deleted file mode 100755
index edcf3ec..0000000
--- a/packaging/builddist.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/zsh
-
-# This script must be run from the source root.
-
-set -e
-
-GENDIR=distgendir
-
-rm -rf dist
-rm -rf $GENDIR
-mkdir dist
-mkdir $GENDIR
-cp -r .git $GENDIR
-cd $GENDIR
-git reset --hard
-python3 setup.py sdist bdist_wheel
-cp dist/* ../dist
-cd ..
-rm -rf $GENDIR
diff --git a/packaging/mpackage.py b/packaging/mpackage.py
index a075e06..e457e55 100755
--- a/packaging/mpackage.py
+++ b/packaging/mpackage.py
@@ -25,16 +25,19 @@ with tarfile.open(infile , 'r') as tf:
fname = os.path.split(infile)[1]
tmp = fname.replace('-', '_')
-if '0rc' in fname:
- version = tmp[6:-7]
- base_version = tmp[6:-10]
+
+assert fname.endswith('.tar.gz')
+version_part = fname.split('-', 1)[1][:-7]
+
+if 'rc' in version_part:
+ base_version, rcnum = version_part.split('rc')
+ version = base_version + 'rc' + rcnum
extension = tmp[-7:]
- rcnum = tmp[-8:-7]
dchversion = base_version + '~rc' + rcnum
- origname = tmp[:11] + '~rc' + rcnum + '.orig' + extension
+ origname = tmp.split('rc', 1)[0] + '~rc' + rcnum + '.orig' + extension
else:
- origname = tmp[:11] + '.orig.' + tmp[-6:]
- version = tmp[6:-7]
+ origname = tmp[:-7] + '.orig.' + tmp[-6:]
+ version = version_part
dchversion = version
version_lines = pathlib.Path(relfile).read_text().split('\n')[:-1]
prev_ver = version_lines[-1]
diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py
index f9faca9..c2a621b 100755
--- a/run_meson_command_tests.py
+++ b/run_meson_command_tests.py
@@ -46,6 +46,11 @@ def get_pybindir():
return sysconfig.get_path('scripts', scheme=scheme, vars={'base': ''}).strip('\\/')
return sysconfig.get_path('scripts', vars={'base': ''}).strip('\\/')
+def has_python_module(module: str) -> bool:
+ result = subprocess.run(python_command + ['-c', f'import {module}'])
+ return result.returncode == 0
+
+
class CommandTests(unittest.TestCase):
'''
Test that running meson in various ways works as expected by checking the
@@ -79,9 +84,13 @@ class CommandTests(unittest.TestCase):
# If this call hangs CI will just abort. It is very hard to distinguish
# between CI issue and test bug in that case. Set timeout and fail loud
# instead.
- p = subprocess.run(command, stdout=subprocess.PIPE,
- env=env, text=True,
- cwd=workdir, timeout=60 * 5)
+ p = subprocess.run(command,
+ stdout=subprocess.PIPE,
+ env=env,
+ encoding='utf-8',
+ text=True,
+ cwd=workdir,
+ timeout=60 * 5)
print(p.stdout)
if p.returncode != 0:
raise subprocess.CalledProcessError(p.returncode, command)
@@ -141,11 +150,17 @@ class CommandTests(unittest.TestCase):
# distutils complains that prefix isn't contained in PYTHONPATH
os.environ['PYTHONPATH'] = os.path.join(str(pylibdir), '')
os.environ['PATH'] = str(bindir) + os.pathsep + os.environ['PATH']
- self._run(python_command + ['setup.py', 'install', '--prefix', str(prefix)])
- # Fix importlib-metadata by appending all dirs in pylibdir
- PYTHONPATHS = [pylibdir] + [x for x in pylibdir.iterdir()]
- PYTHONPATHS = [os.path.join(str(x), '') for x in PYTHONPATHS]
- os.environ['PYTHONPATH'] = os.pathsep.join(PYTHONPATHS)
+ if has_python_module('gpep517'):
+ self._run(python_command + ['-m', 'gpep517', 'install-from-source', '--destdir', '/', '--prefix', str(prefix)])
+ elif has_python_module('pip'):
+ self._run(python_command + ['-m', 'pip', 'install', '--prefix', str(prefix), '.'])
+ else:
+ # Legacy deprecated setuptools command used as fallback
+ self._run(python_command + ['setup.py', 'install', '--prefix', str(prefix)])
+ # Fix importlib-metadata by appending all dirs in pylibdir
+ PYTHONPATHS = [pylibdir] + [x for x in pylibdir.iterdir() if x.name.endswith('.egg')]
+ PYTHONPATHS = [os.path.join(str(x), '') for x in PYTHONPATHS]
+ os.environ['PYTHONPATH'] = os.pathsep.join(PYTHONPATHS)
# Check that all the files were installed correctly
self.assertTrue(bindir.is_dir())
self.assertTrue(pylibdir.is_dir())
diff --git a/run_mypy.py b/run_mypy.py
index d7d3aaa..5823e09 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -13,7 +13,7 @@ from mesonbuild.mesonlib import version_compare
modules = [
# fully typed submodules
- # 'mesonbuild/ast/',
+ 'mesonbuild/ast/',
'mesonbuild/cargo/',
'mesonbuild/cmake/',
'mesonbuild/compilers/',
@@ -23,31 +23,26 @@ modules = [
'mesonbuild/linkers/',
'mesonbuild/scripts/',
'mesonbuild/templates/',
+ 'mesonbuild/utils/',
'mesonbuild/wrap/',
# specific files
- 'mesonbuild/ast/introspection.py',
- 'mesonbuild/ast/printer.py',
- 'mesonbuild/ast/postprocess.py',
- 'mesonbuild/ast/visitor.py',
'mesonbuild/arglist.py',
'mesonbuild/backend/backends.py',
'mesonbuild/backend/nonebackend.py',
- # 'mesonbuild/coredata.py',
+ 'mesonbuild/cmdline.py',
+ 'mesonbuild/coredata.py',
'mesonbuild/depfile.py',
'mesonbuild/envconfig.py',
'mesonbuild/environment.py',
'mesonbuild/interpreter/compiler.py',
+ 'mesonbuild/interpreter/dependencyfallbacks.py',
'mesonbuild/interpreter/mesonmain.py',
'mesonbuild/interpreter/interpreterobjects.py',
'mesonbuild/interpreter/type_checking.py',
'mesonbuild/machinefile.py',
'mesonbuild/mcompile.py',
'mesonbuild/mdevenv.py',
- 'mesonbuild/utils/core.py',
- 'mesonbuild/utils/platform.py',
- 'mesonbuild/utils/universal.py',
- 'mesonbuild/utils/vsenv.py',
'mesonbuild/mconf.py',
'mesonbuild/mdist.py',
'mesonbuild/mformat.py',
@@ -58,7 +53,9 @@ modules = [
'mesonbuild/msubprojects.py',
'mesonbuild/modules/__init__.py',
'mesonbuild/modules/cmake.py',
+ 'mesonbuild/modules/codegen.py',
'mesonbuild/modules/cuda.py',
+ 'mesonbuild/modules/dlang.py',
'mesonbuild/modules/external_project.py',
'mesonbuild/modules/fs.py',
'mesonbuild/modules/gnome.py',
@@ -74,6 +71,7 @@ modules = [
'mesonbuild/modules/qt6.py',
'mesonbuild/modules/rust.py',
'mesonbuild/modules/simd.py',
+ 'mesonbuild/modules/snippets.py',
'mesonbuild/modules/sourceset.py',
'mesonbuild/modules/wayland.py',
'mesonbuild/modules/windows.py',
@@ -83,6 +81,8 @@ modules = [
'mesonbuild/optinterpreter.py',
'mesonbuild/options.py',
'mesonbuild/programs.py',
+ 'mesonbuild/rewriter.py',
+ 'mesonbuild/tooldetect.py',
]
additional = [
'run_mypy.py',
@@ -94,11 +94,6 @@ additional = [
'unittests/helpers.py',
]
-if os.name == 'posix':
- modules.append('mesonbuild/utils/posix.py')
-elif os.name == 'nt':
- modules.append('mesonbuild/utils/win32.py')
-
def check_mypy() -> None:
try:
import mypy
diff --git a/run_project_tests.py b/run_project_tests.py
index fa7c8a6..62fe73a 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -79,7 +79,7 @@ ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'fai
'keyval', 'platform-osx', 'platform-windows', 'platform-linux', 'platform-android',
'java', 'C#', 'vala', 'cython', 'rust', 'd', 'objective c', 'objective c++',
'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm', 'wayland',
- 'format',
+ 'format', 'snippets',
]
@@ -355,15 +355,15 @@ def setup_commands(optbackend: str) -> None:
def platform_fix_name(fname: str, canonical_compiler: str, env: environment.Environment) -> str:
if '?lib' in fname:
if env.machines.host.is_windows() and canonical_compiler == 'msvc':
- fname = re.sub(r'lib/\?lib(.*)\.', r'bin/\1.', fname)
+ fname = re.sub(r'lib/\?lib(.*)$', r'bin/\1', fname)
fname = re.sub(r'/\?lib/', r'/bin/', fname)
elif env.machines.host.is_windows():
- fname = re.sub(r'lib/\?lib(.*)\.', r'bin/lib\1.', fname)
+ fname = re.sub(r'lib/\?lib(.*)$', r'bin/lib\1', fname)
fname = re.sub(r'\?lib(.*)\.dll$', r'lib\1.dll', fname)
fname = re.sub(r'/\?lib/', r'/bin/', fname)
elif env.machines.host.is_cygwin():
fname = re.sub(r'lib/\?lib(.*)\.so$', r'bin/cyg\1.dll', fname)
- fname = re.sub(r'lib/\?lib(.*)\.', r'bin/cyg\1.', fname)
+ fname = re.sub(r'lib/\?lib(.*)$', r'bin/cyg\1', fname)
fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname)
fname = re.sub(r'/\?lib/', r'/bin/', fname)
else:
@@ -555,8 +555,8 @@ def clear_internal_caches() -> None:
from mesonbuild.mesonlib import PerMachine
mesonbuild.interpreterbase.FeatureNew.feature_registry = {}
CMakeDependency.class_cmakeinfo = PerMachine(None, None)
- PkgConfigInterface.class_impl = PerMachine(False, False)
- PkgConfigInterface.class_cli_impl = PerMachine(False, False)
+ PkgConfigInterface.class_impl = PerMachine({}, {})
+ PkgConfigInterface.class_cli_impl = PerMachine({}, {})
PkgConfigInterface.pkg_bin_per_machine = PerMachine(None, None)
@@ -997,9 +997,9 @@ def have_working_compiler(lang: str, use_tmp: bool) -> bool:
return False
if not compiler:
return False
- env.coredata.process_compiler_options(lang, compiler, env, '')
+ env.coredata.process_compiler_options(lang, compiler, '')
try:
- compiler.sanity_check(env.get_scratch_dir(), env)
+ compiler.sanity_check(env.get_scratch_dir())
except mesonlib.MesonException:
return False
return True
@@ -1145,6 +1145,7 @@ def detect_tests_to_run(only: T.Dict[str, T.List[str]], use_tmp: bool) -> T.List
TestCategory('wasm', 'wasm', shutil.which('emcc') is None or backend is not Backend.ninja),
TestCategory('wayland', 'wayland', should_skip_wayland()),
TestCategory('format', 'format'),
+ TestCategory('snippets', 'snippets'),
]
categories = [t.category for t in all_tests]
@@ -1577,11 +1578,11 @@ def detect_tools(report: bool = True) -> None:
print('{0:<{2}}: {1}'.format(tool.tool, get_version(tool), max_width))
print()
-symlink_test_dir1 = None
-symlink_test_dir2 = None
-symlink_file1 = None
-symlink_file2 = None
-symlink_file3 = None
+symlink_test_dir1: T.Optional[Path] = None
+symlink_test_dir2: T.Optional[Path] = None
+symlink_file1: T.Optional[Path] = None
+symlink_file2: T.Optional[Path] = None
+symlink_file3: T.Optional[Path] = None
def scan_test_data_symlinks() -> None:
global symlink_test_dir1, symlink_test_dir2, symlink_file1, symlink_file2, symlink_file3
diff --git a/run_shell_checks.py b/run_shell_checks.py
new file mode 100755
index 0000000..f929d80
--- /dev/null
+++ b/run_shell_checks.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2025 The Meson development team
+
+import pathlib
+import sys
+
+# DO NOT ADD FILES IN THIS LIST!
+# They are here because they got added
+# in the past before this was properly checked.
+# Instead you should consider removing things
+# from this list by rewriting them to Python.
+#
+# The CI scripts probably need to remain shell
+# scripts due to the way the CI systems work.
+
+permitted_files = (
+ 'ci/ciimage/common.sh',
+ 'ci/intel-scripts/cache_exclude_windows.sh',
+ 'ci/ciimage/opensuse/install.sh',
+ 'ci/ciimage/ubuntu-rolling/install.sh',
+ 'ci/ciimage/ubuntu-rolling/test.sh',
+ 'ci/ciimage/cuda-cross/install.sh',
+ 'ci/ciimage/cuda/install.sh',
+ 'ci/ciimage/bionic/install.sh',
+ 'ci/ciimage/fedora/install.sh',
+ 'ci/ciimage/arch/install.sh',
+ 'ci/ciimage/gentoo/install.sh',
+ 'manual tests/4 standalone binaries/myapp.sh',
+ 'manual tests/4 standalone binaries/osx_bundler.sh',
+ 'manual tests/4 standalone binaries/linux_bundler.sh',
+ 'manual tests/4 standalone binaries/build_osx_package.sh',
+ 'manual tests/4 standalone binaries/build_linux_package.sh',
+ 'test cases/failing test/3 ambiguous/test_runner.sh',
+ 'test cases/common/190 install_mode/runscript.sh',
+ 'test cases/common/48 file grabber/grabber.sh',
+ 'test cases/common/12 data/runscript.sh',
+ 'test cases/common/33 run program/scripts/hello.sh',
+ )
+
+
+def check_bad_files(filename_glob):
+ num_errors = 0
+ for f in pathlib.Path('.').glob(f'**/{filename_glob}'):
+ if str(f) not in permitted_files:
+ print('Forbidden file type:', f)
+ num_errors += 1
+ return num_errors
+
+def check_deletions():
+ num_errors = 0
+ for f in permitted_files:
+ p = pathlib.Path(f)
+ if not p.is_file():
+ print('Exception list has a file that does not exist:', f)
+ num_errors += 1
+ return num_errors
+
+def check_shell_usage():
+ total_errors = 0
+ total_errors += check_bad_files('Makefile')
+ total_errors += check_bad_files('*.sh')
+ total_errors += check_bad_files('*.awk')
+ total_errors += check_deletions()
+ return total_errors
+
+if __name__ == '__main__':
+ sys.exit(check_shell_usage())
+
diff --git a/run_tests.py b/run_tests.py
index 4e22028..0548671 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -32,7 +32,9 @@ from mesonbuild import mesonlib
from mesonbuild import mesonmain
from mesonbuild import mtest
from mesonbuild import mlog
-from mesonbuild.environment import Environment, detect_ninja, detect_machine_info
+from mesonbuild.environment import Environment
+from mesonbuild.envconfig import detect_machine_info
+from mesonbuild.tooldetect import detect_ninja
from mesonbuild.coredata import version as meson_version
from mesonbuild.options import backendlist
from mesonbuild.mesonlib import setup_vsenv
@@ -135,10 +137,6 @@ class FakeBuild:
def __init__(self, env):
self.environment = env
-class FakeCompilerOptions:
- def __init__(self):
- self.value = []
-
def get_fake_options(prefix: str = '') -> SharedCMDOptions:
opts = T.cast('SharedCMDOptions', argparse.Namespace())
opts.native_file = []
@@ -153,7 +151,6 @@ def get_fake_env(sdir: str = '', bdir: T.Optional[str] = None, prefix: str = '',
if opts is None:
opts = get_fake_options(prefix)
env = Environment(sdir, bdir, opts)
- env.coredata.optstore.set_value_object(OptionKey('c_args'), FakeCompilerOptions())
env.machines.host.cpu_family = 'x86_64' # Used on macOS inside find_library
# Invalidate cache when using a different Environment object.
clear_meson_configure_class_caches()
@@ -294,7 +291,7 @@ def run_mtest_inprocess(commandlist: T.List[str]) -> T.Tuple[int, str]:
def clear_meson_configure_class_caches() -> None:
CCompiler.find_library_cache.clear()
CCompiler.find_framework_cache.clear()
- PkgConfigInterface.class_impl.assign(False, False)
+ PkgConfigInterface.class_impl.assign({}, {})
mesonlib.project_meson_versions.clear()
def run_configure_inprocess(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None, catch_exception: bool = False) -> T.Tuple[int, str, str]:
diff --git a/run_unittests.py b/run_unittests.py
index 84edb34..6a84f5c 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -11,43 +11,12 @@ import time
import subprocess
import os
import unittest
+import importlib
+import typing as T
-import mesonbuild.mlog
-import mesonbuild.depfile
-import mesonbuild.dependencies.base
-import mesonbuild.dependencies.factory
-import mesonbuild.compilers
-import mesonbuild.envconfig
-import mesonbuild.environment
import mesonbuild.coredata
-import mesonbuild.modules.gnome
from mesonbuild.mesonlib import python_command, setup_vsenv
-import mesonbuild.modules.pkgconfig
-from unittests.allplatformstests import AllPlatformTests
-from unittests.cargotests import CargoVersionTest, CargoCfgTest, CargoLockTest
-from unittests.darwintests import DarwinTests
-from unittests.failuretests import FailureTests
-from unittests.linuxcrosstests import LinuxCrossArmTests, LinuxCrossMingwTests
-from unittests.machinefiletests import NativeFileTests, CrossFileTests
-from unittests.rewritetests import RewriterTests
-from unittests.taptests import TAPParserTests
-from unittests.datatests import DataTests
-from unittests.internaltests import InternalTests
-from unittests.linuxliketests import LinuxlikeTests
-from unittests.pythontests import PythonTests
-from unittests.subprojectscommandtests import SubprojectsCommandTests
-from unittests.windowstests import WindowsTests
-from unittests.platformagnostictests import PlatformAgnosticTests
-
-def unset_envs():
- # For unit tests we must fully control all command lines
- # so that there are no unexpected changes coming from the
- # environment, for example when doing a package build.
- varnames = ['CPPFLAGS', 'LDFLAGS'] + list(mesonbuild.compilers.compilers.CFLAGS_MAPPING.values())
- for v in varnames:
- if v in os.environ:
- del os.environ[v]
def convert_args(argv):
# If we got passed a list of tests, pass it on
@@ -100,15 +69,33 @@ def setup_backend():
os.environ['MESON_UNIT_TEST_BACKEND'] = be
sys.argv = filtered
+def import_test_cases(suite: unittest.TestSuite) -> T.Set[str]:
+ '''
+ Imports all test classes into the current module and returns their names
+ '''
+ classes = set()
+ for test in suite:
+ if isinstance(test, unittest.TestSuite):
+ classes.update(import_test_cases(test))
+ elif isinstance(test, unittest.TestCase):
+ mod = importlib.import_module(test.__module__)
+ class_name = test.__class__.__name__
+ test_class = getattr(mod, class_name)
+ classes.add(class_name)
+ setattr(sys.modules[__name__], class_name, test_class)
+ return classes
+
+def discover_test_cases() -> T.Set[str]:
+ current_dir = os.path.dirname(os.path.realpath(__file__))
+ loader = unittest.TestLoader()
+ suite = loader.discover(os.path.join(current_dir, 'unittests'), '*tests.py', current_dir)
+ if loader.errors:
+ raise SystemExit(loader.errors)
+ return import_test_cases(suite)
+
def main():
- unset_envs()
+ cases = sorted(discover_test_cases())
setup_backend()
- cases = ['InternalTests', 'DataTests', 'AllPlatformTests', 'FailureTests',
- 'PythonTests', 'NativeFileTests', 'RewriterTests', 'CrossFileTests',
- 'TAPParserTests', 'SubprojectsCommandTests', 'PlatformAgnosticTests',
-
- 'LinuxlikeTests', 'LinuxCrossArmTests', 'LinuxCrossMingwTests',
- 'WindowsTests', 'DarwinTests']
try:
import pytest # noqa: F401
diff --git a/test cases/cmake/13 system includes/main2.cpp b/test cases/cmake/13 system includes/main2.cpp
new file mode 100644
index 0000000..a94a116
--- /dev/null
+++ b/test cases/cmake/13 system includes/main2.cpp
@@ -0,0 +1,5 @@
+#include <triggerWarn.hpp>
+
+int main(void) {
+ return 0;
+}
diff --git a/test cases/cmake/13 system includes/meson.build b/test cases/cmake/13 system includes/meson.build
index 1265d46..fe71580 100644
--- a/test cases/cmake/13 system includes/meson.build
+++ b/test cases/cmake/13 system includes/meson.build
@@ -13,6 +13,10 @@ endif
cm = import('cmake')
sub_pro = cm.subproject('cmMod')
sub_dep = sub_pro.dependency('cmModLib')
+sub_inc = sub_pro.include_directories('cmModLib')
exe1 = executable('main1', ['main.cpp'], dependencies: [sub_dep])
test('test1', exe1)
+
+exe2 = executable('main2', ['main2.cpp'], include_directories: sub_inc)
+test('test2', exe1)
diff --git a/test cases/cmake/2 advanced/meson.build b/test cases/cmake/2 advanced/meson.build
index 39da0c6..43b5f8b 100644
--- a/test cases/cmake/2 advanced/meson.build
+++ b/test cases/cmake/2 advanced/meson.build
@@ -27,3 +27,21 @@ test('test3', sub_pro.target('testEXE'))
# Test that we can add a new target with the same name as the CMake subproject
exe4 = executable('testEXE', ['main.cpp'], dependencies: [sub_sta])
test('test4', exe4)
+
+# Test if libraries are named correctly
+assert(sub_pro.target_type('cmModLib') == 'shared_library', 'Type should be shared_library')
+expected_soversion_str = '1'
+expected_version_str = '1.0.1'
+lib_file_name = import('fs').name(sub_pro.target('cmModLib').full_path())
+if host_machine.system() == 'linux'
+ # Linux shared libraries end with their version: libcmModLib.so.1.0.1
+ lib_version_ok = lib_file_name.endswith(expected_version_str)
+elif host_machine.system() == 'darwin'
+ # MacOS shared libraries are suffixed with their soversion: libcmModLib.1.dylib
+ lib_version_ok = lib_file_name.split('.')[1] == expected_soversion_str
+else
+ # Don't try to assert anything about the library version, either unknown host
+ # system or host system doesn't support versioning of shared libraries
+ lib_version_ok = true
+endif
+assert(lib_version_ok, f'Shared library version @lib_file_name@ not picked up correctly')
diff --git a/test cases/common/104 has arg/meson.build b/test cases/common/104 has arg/meson.build
index 466bed9..500b8a9 100644
--- a/test cases/common/104 has arg/meson.build
+++ b/test cases/common/104 has arg/meson.build
@@ -56,9 +56,22 @@ if cpp.get_id() == 'gcc' and cpp.version().version_compare('>=12.1.0')
# Handle special -Wno-attributes=foo where -Wattributes=foo is invalid
# i.e. our usual -Wno-foo -Wfoo hack doesn't work for -Wattributes=foo.
assert(cpp.has_argument('-Wno-attributes=meson::i_do_not_exist'))
- # Likewise, the positive counterpart to -Wno-vla-larger-than is
- # -Wvla-larger-than=N
- assert(cpp.has_argument('-Wno-vla-larger-than'))
+endif
+
+if cpp.get_id() == 'gcc'
+ # Handle negative flags whose positive counterparts require a value to be
+ # specified.
+ if cpp.version().version_compare('>=4.4.0')
+ assert(cpp.has_argument('-Wno-frame-larger-than'))
+ endif
+ if cpp.version().version_compare('>=4.7.0')
+ assert(cpp.has_argument('-Wno-stack-usage'))
+ endif
+ if cpp.version().version_compare('>=7.1.0')
+ assert(cpp.has_argument('-Wno-alloc-size-larger-than'))
+ assert(cpp.has_argument('-Wno-alloca-larger-than'))
+ assert(cpp.has_argument('-Wno-vla-larger-than'))
+ endif
endif
if cc.get_id() == 'clang' and cc.version().version_compare('<=4.0.0')
diff --git a/test cases/common/109 custom target capture/meson.build b/test cases/common/109 custom target capture/meson.build
index b762201..9fd7e22 100644
--- a/test cases/common/109 custom target capture/meson.build
+++ b/test cases/common/109 custom target capture/meson.build
@@ -22,3 +22,13 @@ if not os.path.exists(sys.argv[1]):
'''
test('capture-wrote', python3, args : ['-c', ct_output_exists, mytarget])
+
+mytarget = custom_target('bindat',
+ output : 'data.dat',
+ input : 'data_source.txt',
+ build_subdir : 'subdir2',
+ capture : true,
+ command : [python3, comp, '@INPUT@'],
+ install : true,
+ install_dir : 'subdir2'
+)
diff --git a/test cases/common/109 custom target capture/test.json b/test cases/common/109 custom target capture/test.json
index ba66b02..663a8f3 100644
--- a/test cases/common/109 custom target capture/test.json
+++ b/test cases/common/109 custom target capture/test.json
@@ -1,5 +1,6 @@
{
"installed": [
- {"type": "file", "file": "usr/subdir/data.dat"}
+ {"type": "file", "file": "usr/subdir/data.dat"},
+ {"type": "file", "file": "usr/subdir2/data.dat"}
]
}
diff --git a/test cases/common/117 shared module/meson.build b/test cases/common/117 shared module/meson.build
index 94d17a7..494ce42 100644
--- a/test cases/common/117 shared module/meson.build
+++ b/test cases/common/117 shared module/meson.build
@@ -34,6 +34,14 @@ test('import test', e, args : m)
m2 = build_target('mymodule2', 'module.c', target_type: 'shared_module')
test('import test 2', e, args : m2)
+# Same as above, but built and installed in a sub directory
+m2_subdir = build_target('mymodule2', 'module.c',
+ target_type: 'shared_module',
+ build_subdir: 'subdir',
+ install: true,
+ install_dir: join_paths(get_option('libdir'), 'modules/subdir'))
+test('import test 2 subdir', e, args : m2_subdir)
+
# Shared module that does not export any symbols
shared_module('nosyms', 'nosyms.c',
override_options: ['werror=false'],
diff --git a/test cases/common/117 shared module/test.json b/test cases/common/117 shared module/test.json
index 33bfeff..b24149c 100644
--- a/test cases/common/117 shared module/test.json
+++ b/test cases/common/117 shared module/test.json
@@ -2,6 +2,9 @@
"installed": [
{"type": "expr", "file": "usr/lib/modules/libnosyms?so"},
{"type": "implibempty", "file": "usr/lib/modules/libnosyms"},
- {"type": "pdb", "file": "usr/lib/modules/nosyms"}
+ {"type": "pdb", "file": "usr/lib/modules/nosyms"},
+ {"type": "expr", "file": "usr/lib/modules/subdir/libmymodule2?so"},
+ {"type": "implib", "file": "usr/lib/modules/subdir/libmymodule2"},
+ {"type": "pdb", "file": "usr/lib/modules/subdir/mymodule2"}
]
}
diff --git a/test cases/common/14 configure file/CMakeLists.txt b/test cases/common/14 configure file/CMakeLists.txt
new file mode 100644
index 0000000..6a894b0
--- /dev/null
+++ b/test cases/common/14 configure file/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.12)
+
+project("configure file test" LANGUAGES C)
+
+set("var1" "foo")
+set("var2" "bar")
+configure_file("config7.h.in" "config7.h")
+
+set("var" "foo")
+configure_file("config10.h.in" "config10.h")
diff --git a/test cases/common/14 configure file/config7.h.in b/test cases/common/14 configure file/config7.h.in
index edd0bb3..5180c2f 100644
--- a/test cases/common/14 configure file/config7.h.in
+++ b/test cases/common/14 configure file/config7.h.in
@@ -1,16 +1,11 @@
-/* No escape */
+/* cmake substitions cannot be escaped */
#define MESSAGE1 "${var1}"
-
-/* Single escape means no replace */
#define MESSAGE2 "\${var1}"
-
-/* Replace pairs of escapes before '@' or '\@' with escape characters
- * (note we have to double number of pairs due to C string escaping)
- */
#define MESSAGE3 "\\\\${var1}"
-
-/* Pairs of escapes and then single escape to avoid replace */
#define MESSAGE4 "\\\\\${var1}"
+#define MESSAGE5 "@var1@"
+#define MESSAGE6 "\\@var1@"
+#define MESSAGE7 "\\\\@var1@"
-/* Check escape character outside variables */
-#define MESSAGE5 "\\ ${ \${ \\\\${ \\\\\${"
+/* backslash is an invalid variable character */
+#define MESSAGE8 "@var1\@"
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
index 3a4ff4d..80a5d82 100644
--- a/test cases/common/14 configure file/meson.build
+++ b/test cases/common/14 configure file/meson.build
@@ -30,6 +30,13 @@ configure_file(input : files('config.h.in'),
output : 'config2.h',
configuration : conf)
+# Test if build_subdir works
+configure_file(input : files('config.h.in'),
+ output : 'config2.h',
+ build_subdir : 'config-subdir',
+ install_dir : 'share/appdir/config-subdir',
+ configuration : conf)
+
# Now generate a header file with an external script.
genprog = import('python3').find_python()
scriptfile = '@0@/generator.py'.format(meson.current_source_dir())
diff --git a/test cases/common/14 configure file/prog7.c b/test cases/common/14 configure file/prog7.c
index 802bc46..900522c 100644
--- a/test cases/common/14 configure file/prog7.c
+++ b/test cases/common/14 configure file/prog7.c
@@ -3,8 +3,12 @@
int main(void) {
return strcmp(MESSAGE1, "foo")
- || strcmp(MESSAGE2, "${var1}")
- || strcmp(MESSAGE3, "\\foo")
- || strcmp(MESSAGE4, "\\${var1}")
- || strcmp(MESSAGE5, "\\ ${ ${ \\${ \\${");
+ || strcmp(MESSAGE2, "\foo")
+ || strcmp(MESSAGE3, "\\\\foo")
+ || strcmp(MESSAGE4, "\\\\\foo")
+ || strcmp(MESSAGE5, "foo")
+ || strcmp(MESSAGE6, "\\foo")
+ || strcmp(MESSAGE7, "\\\\foo")
+ || strcmp(MESSAGE8, "@var1\@")
+ || 0;
}
diff --git a/test cases/common/14 configure file/test.json b/test cases/common/14 configure file/test.json
index 5a6ccd5..51e6770 100644
--- a/test cases/common/14 configure file/test.json
+++ b/test cases/common/14 configure file/test.json
@@ -4,6 +4,7 @@
{"type": "file", "file": "usr/share/appdir/config2b.h"},
{"type": "file", "file": "usr/share/appdireh/config2-1.h"},
{"type": "file", "file": "usr/share/appdirok/config2-2.h"},
- {"type": "file", "file": "usr/share/configure file test/invalid-utf8-1.bin"}
+ {"type": "file", "file": "usr/share/configure file test/invalid-utf8-1.bin"},
+ {"type": "file", "file": "usr/share/appdir/config-subdir/config2.h"}
]
}
diff --git a/test cases/common/182 find override/broken.py b/test cases/common/182 find override/broken.py
new file mode 100755
index 0000000..e7c2af0
--- /dev/null
+++ b/test cases/common/182 find override/broken.py
@@ -0,0 +1,3 @@
+#! /usr/bin/env python3
+
+raise SystemExit(1)
diff --git a/test cases/common/182 find override/meson.build b/test cases/common/182 find override/meson.build
index edb1687..7f68991 100644
--- a/test cases/common/182 find override/meson.build
+++ b/test cases/common/182 find override/meson.build
@@ -1,4 +1,4 @@
-project('find program override', 'c')
+project('find program override', 'c', version: '1.0.0')
gencodegen = find_program('gencodegen', required : false)
six_prog = find_program('six_meson_exe', required : false)
@@ -29,3 +29,11 @@ assert(six_prog.full_path() == six_prog.path())
prog = find_program('prog-version.py', 'prog-version', version: '>= 2.0')
assert(prog.found())
assert(prog.version() == '2.0')
+
+# Meson should not invoke the script to get its version, it uses current project
+# version.
+script = files('broken.py')
+meson.override_find_program('broken', script)
+prog = find_program('broken', version: '>= 1.0')
+assert(prog.found())
+assert(prog.version() == '1.0.0')
diff --git a/test cases/common/188 dict/meson.build b/test cases/common/188 dict/meson.build
index 2f4c4c9..020ae3c 100644
--- a/test cases/common/188 dict/meson.build
+++ b/test cases/common/188 dict/meson.build
@@ -16,12 +16,24 @@ endforeach
assert(i == 3, 'There should be three elements in that dictionary')
+# Test keys() works as expected (note that it sorts the keys)
+assert(dict.keys() == ['baz', 'foo', 'foo bar'], 'Keys returned unexpected list')
+
+# Test values() works as expected (note that it sorts first, to match keys())
+assert(dict.values() == ['foo', 'bar', 'baz'])
+
empty_dict = {}
foreach key, value : empty_dict
assert(false, 'This dict should be empty')
endforeach
+# Test keys() returns an empty list
+assert(empty_dict.keys() == [], 'Keys for an empty dict should be an empty list')
+
+# Test values() returns an empty list
+assert(empty_dict.values() == [], 'Values for an empty dict should be an empty list')
+
d1 = empty_dict + {'a' : 'b'}
assert(d1 == {'a' : 'b'}, 'dict addition is not working')
diff --git a/test cases/common/197 function attributes/meson.build b/test cases/common/197 function attributes/meson.build
index db7e3af..a2dc5b7 100644
--- a/test cases/common/197 function attributes/meson.build
+++ b/test cases/common/197 function attributes/meson.build
@@ -117,7 +117,16 @@ if ['gcc', 'intel'].contains(c.get_id())
endif
endif
+if c.get_id() == 'clang'
+ if c.version().version_compare('>= 18.0.0')
+ attributes += 'counted_by'
+ endif
+endif
+
if c.get_id() == 'gcc'
+ if c.version().version_compare('>= 15.1')
+ attributes += 'counted_by'
+ endif
if c.version().version_compare('>= 14.1')
attributes += 'null_terminated_string_arg'
endif
@@ -128,19 +137,27 @@ if get_option('mode') == 'single'
x = c.has_function_attribute(a)
expected_result = expected.get(a, expected_default)
assert(x == expected_result, '@0@: @1@'.format(c.get_id(), a))
- x = cpp.has_function_attribute(a)
- assert(x == expected_result, '@0@: @1@'.format(cpp.get_id(), a))
+ # counted_by is not currently supported by GCC or clang on C++
+ if a != 'counted_by'
+ x = cpp.has_function_attribute(a)
+ assert(x == expected_result, '@0@: @1@'.format(cpp.get_id(), a))
+ endif
endforeach
else
- multi_expected = []
+ multi_expected_c = []
+ multi_expected_cpp = []
foreach a : attributes
if expected.get(a, expected_default)
- multi_expected += a
+ multi_expected_c += a
+ # counted_by is not currently supported by GCC or clang on C++
+ if a != 'counted_by'
+ multi_expected_cpp += a
+ endif
endif
endforeach
multi_check = c.get_supported_function_attributes(attributes)
- assert(multi_check == multi_expected, 'get_supported_function_arguments works (C)')
+ assert(multi_check == multi_expected_c, 'get_supported_function_arguments works (C)')
multi_check = cpp.get_supported_function_attributes(attributes)
- assert(multi_check == multi_expected, 'get_supported_function_arguments works (C++)')
+ assert(multi_check == multi_expected_cpp, 'get_supported_function_arguments works (C++)')
endif
diff --git a/test cases/common/22 object extraction/meson.build b/test cases/common/22 object extraction/meson.build
index 37ac66d..cc6de6f 100644
--- a/test cases/common/22 object extraction/meson.build
+++ b/test cases/common/22 object extraction/meson.build
@@ -1,50 +1,57 @@
project('object extraction', 'c')
if meson.is_unity()
- message('Skipping extraction test because this is a Unity build.')
-else
- lib1 = library('somelib', 'src/lib.c')
- lib2 = library('somelib2', 'lib.c', 'header.h', 'lib2.c')
-
- obj1 = lib1.extract_objects('src/lib.c')
- obj2 = lib2.extract_objects(['lib.c'])
- obj3 = lib2.extract_objects(files('lib.c'))
- obj4 = lib2.extract_objects(['lib.c', 'lib.c'])
- obj5 = lib2.extract_objects(['lib.c', 'header.h'])
- obj6 = lib2.extract_all_objects(recursive: true)
-
- e1 = executable('main1', 'main.c', objects : obj1)
- e2 = executable('main2', 'main.c', objects : obj2)
- e3 = executable('main3', 'main.c', objects : obj3)
- e4 = executable('main4', 'main.c', objects : obj4)
- e5 = executable('main5', 'main.c', objects : obj5)
- e6 = executable('main6', 'main.c', objects : obj6)
-
- ct_src = custom_target('lib3.c', output: 'lib3.c', capture: true,
- command: [find_program('create-source.py'), 'lib.c'])
- lib3 = library('somelib3', ct_src)
- e7 = executable('main7', 'main.c', objects: lib3.extract_objects(ct_src[0]))
- e8 = executable('main8', 'main.c', objects: lib3.extract_objects(ct_src))
+ error('MESON_SKIP_TEST, Skipping extraction test because this is a Unity build.')
+endif
- gen = generator(find_program('create-source.py'), arguments: ['@INPUT@'],
- output: '@BASENAME@4.c', capture: true)
- gen_src = gen.process('lib.c')
- lib4 = library('somelib4', gen_src)
- e9 = executable('main9', 'main.c', objects: lib4.extract_objects(gen_src))
+lib1 = library('somelib', 'src/lib.c')
+lib2 = library('somelib2', 'lib.c', 'header.h', 'lib2.c')
+obj1 = lib1.extract_objects('src/lib.c')
+obj2 = lib2.extract_objects(['lib.c'])
+obj3 = lib2.extract_objects(files('lib.c'))
+obj4 = lib2.extract_objects(['lib.c', 'lib.c'])
+obj5 = lib2.extract_objects(['lib.c', 'header.h'])
+obj6 = lib2.extract_all_objects(recursive: true)
+e1 = executable('main1', 'main.c', objects : obj1)
+e2 = executable('main2', 'main.c', objects : obj2)
+e3 = executable('main3', 'main.c', objects : obj3)
+e4 = executable('main4', 'main.c', objects : obj4)
+e5 = executable('main5', 'main.c', objects : obj5)
+e6 = executable('main6', 'main.c', objects : obj6)
+ct_src = custom_target('lib3.c', output: 'lib3.c', capture: true,
+ command: [find_program('create-source.py'), 'lib.c'])
+lib3 = library('somelib3', ct_src)
+e7 = executable('main7', 'main.c', objects: lib3.extract_objects(ct_src[0]))
+e8 = executable('main8', 'main.c', objects: lib3.extract_objects(ct_src))
+gen = generator(find_program('create-source.py'), arguments: ['@INPUT@'],
+ output: '@BASENAME@4.c', capture: true)
+gen_src = gen.process('lib.c')
+lib4 = library('somelib4', gen_src)
+e9 = executable('main9', 'main.c', objects: lib4.extract_objects(gen_src))
+custom_target('custom_target with object inputs', output: 'objs',
+ input: [obj1, obj2, obj3, obj5, obj6],
+ build_by_default: true,
+ command: [find_program('check-obj.py'), meson.backend(), '@INPUT@'],
+ capture: true)
+test('extraction test 1', e1)
+test('extraction test 2', e2)
+test('extraction test 3', e3)
+test('extraction test 4', e4)
+test('extraction test 5', e5)
+test('extraction test 6', e6)
+test('extraction test 7', e7)
+test('extraction test 8', e8)
+test('extraction test 9', e9)
- custom_target('custom_target with object inputs', output: 'objs',
- input: [obj1, obj2, obj3, obj5, obj6],
- build_by_default: true,
- command: [find_program('check-obj.py'), meson.backend(), '@INPUT@'],
- capture: true)
+lib = subproject('sub').get_variable('lib')
+testcase expect_error('Tried to extract objects from a different subproject.')
+ lib.extract_objects()
+endtestcase
- test('extraction test 1', e1)
- test('extraction test 2', e2)
- test('extraction test 3', e3)
- test('extraction test 4', e4)
- test('extraction test 5', e5)
- test('extraction test 6', e6)
- test('extraction test 7', e7)
- test('extraction test 8', e8)
- test('extraction test 9', e9)
+cc = meson.get_compiler('c')
+lib = cc.find_library('z', required: false)
+if lib.found()
+ testcase expect_error('Unknown method .*', how: 're')
+ lib.extract_objects()
+ endtestcase
endif
diff --git a/test cases/common/22 object extraction/subprojects/sub/meson.build b/test cases/common/22 object extraction/subprojects/sub/meson.build
new file mode 100644
index 0000000..e7fbb28
--- /dev/null
+++ b/test cases/common/22 object extraction/subprojects/sub/meson.build
@@ -0,0 +1,4 @@
+project('sub', 'c')
+
+lib = static_library('a', 'source.c')
+lib.extract_objects()
diff --git a/test cases/common/22 object extraction/subprojects/sub/source.c b/test cases/common/22 object extraction/subprojects/sub/source.c
new file mode 100644
index 0000000..0584ef4
--- /dev/null
+++ b/test cases/common/22 object extraction/subprojects/sub/source.c
@@ -0,0 +1,5 @@
+int func(void);
+
+int func(void) {
+ return 1;
+}
diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build
index e5397ee..383b263 100644
--- a/test cases/common/220 fs module/meson.build
+++ b/test cases/common/220 fs module/meson.build
@@ -52,6 +52,7 @@ unixabs = '/foo'
if is_windows
assert(fs.is_absolute(winabs), 'is_absolute windows not detected')
assert(not fs.is_absolute(unixabs), 'is_absolute unix false positive')
+ assert(fs.is_absolute('//foo'), 'is_absolute failed on incomplete UNC path')
else
assert(fs.is_absolute(unixabs), 'is_absolute unix not detected')
assert(not fs.is_absolute(winabs), 'is_absolute windows false positive')
@@ -84,7 +85,7 @@ assert(new == 'foo', 'replace_suffix did not only delete last suffix')
# `/` on windows is interpreted like `.drive` which in general may not be `c:/`
# the files need not exist for fs.replace_suffix()
-original = is_windows ? 'j:/foo/bar.txt' : '/foo/bar.txt'
+original = is_windows ? 'j:\\foo\\bar.txt' : '/foo/bar.txt'
new_check = is_windows ? 'j:\\foo\\bar.ini' : '/foo/bar.ini'
new = fs.replace_suffix(original, '.ini')
@@ -139,11 +140,7 @@ endif
# parts of path
assert(fs.parent('foo/bar') == 'foo', 'failed to get dirname')
-if not is_windows
-assert(fs.parent(f[1]) == 'subdir/..', 'failed to get dirname')
-else
-assert(fs.parent(f[1]) == 'subdir\..', 'failed to get dirname')
-endif
+assert(fs.parent(f[1]) == 'subdir/..', 'failed to get dirname for file')
assert(fs.parent(btgt) == '.', 'failed to get dirname for build target')
assert(fs.parent(ctgt) == '.', 'failed to get dirname for custom target')
assert(fs.parent(ctgt[0]) == '.', 'failed to get dirname for custom target index')
@@ -153,8 +150,10 @@ assert(fs.name(f[1]) == 'meson.build', 'failed to get basename')
assert(fs.name('foo/bar/baz.dll.a') == 'baz.dll.a', 'failed to get basename with compound suffix')
if host_machine.system() in ['cygwin', 'windows']
assert(fs.name(btgt) == 'btgt.exe', 'failed to get basename of build target')
+ assert(fs.suffix(btgt) == '.exe', 'failed to get build target suffix')
else
assert(fs.name(btgt) == 'btgt', 'failed to get basename of build target')
+ assert(fs.suffix(btgt) == '', 'failed to get build target suffix')
endif
assert(fs.name(ctgt) == 'ctgt.txt', 'failed to get basename of custom target')
assert(fs.name(ctgt[0]) == 'ctgt.txt', 'failed to get basename of custom target index')
@@ -163,6 +162,12 @@ assert(fs.stem('foo/bar/baz.dll.a') == 'baz.dll', 'failed to get stem with compo
assert(fs.stem(btgt) == 'btgt', 'failed to get stem of build target')
assert(fs.stem(ctgt) == 'ctgt', 'failed to get stem of custom target')
assert(fs.stem(ctgt[0]) == 'ctgt', 'failed to get stem of custom target index')
+assert(fs.suffix('foo/bar/baz') == '', 'failed to get missing suffix')
+assert(fs.suffix('foo/bar/baz.') == '.', 'failed to get empty suffix')
+assert(fs.suffix('foo/bar/baz.dll') == '.dll', 'failed to get plain suffix')
+assert(fs.suffix('foo/bar/baz.dll.a') == '.a', 'failed to get final suffix')
+assert(fs.suffix(ctgt) == '.txt', 'failed to get suffix of custom target')
+assert(fs.suffix(ctgt[0]) == '.txt', 'failed to get suffix of custom target index')
# relative_to
if build_machine.system() == 'windows'
diff --git a/test cases/common/247 deprecated option/test.json b/test cases/common/247 deprecated option/test.json
index a644b04..4b9a475 100644
--- a/test cases/common/247 deprecated option/test.json
+++ b/test cases/common/247 deprecated option/test.json
@@ -26,77 +26,77 @@
},
"stdout": [
{
- "line": ".*DEPRECATION: Option 'o1' is deprecated",
+ "line": ".*DEPRECATION: Option \"o1\" is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'o2' value 'a' is deprecated",
+ "line": ".*DEPRECATION: Option \"o2\" value 'a' is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'o3' value 'a' is replaced by 'c'",
+ "line": ".*DEPRECATION: Option \"o3\" value 'a' is replaced by 'c'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'o4' value 'true' is replaced by 'enabled'",
+ "line": ".*DEPRECATION: Option \"o4\" value 'true' is replaced by 'enabled'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'o5' value 'auto' is replaced by 'false'",
+ "line": ".*DEPRECATION: Option \"o5\" value 'auto' is replaced by 'false'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'p1' is deprecated",
+ "line": ".*DEPRECATION: Option \"p1\" is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'p2' value 'a' is deprecated",
+ "line": ".*DEPRECATION: Option \"p2\" value 'a' is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'p3' value 'a' is replaced by 'c'",
+ "line": ".*DEPRECATION: Option \"p3\" value 'a' is replaced by 'c'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'p4' value 'true' is replaced by 'enabled'",
+ "line": ".*DEPRECATION: Option \"p4\" value 'true' is replaced by 'enabled'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'p5' value 'auto' is replaced by 'false'",
+ "line": ".*DEPRECATION: Option \"p5\" value 'auto' is replaced by 'false'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'c1' is deprecated",
+ "line": ".*DEPRECATION: Option \"c1\" is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'c2' value 'a' is deprecated",
+ "line": ".*DEPRECATION: Option \"c2\" value 'a' is deprecated",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'c3' value 'a' is replaced by 'c'",
+ "line": ".*DEPRECATION: Option \"c3\" value 'a' is replaced by 'c'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'c4' value 'true' is replaced by 'enabled'",
+ "line": ".*DEPRECATION: Option \"c4\" value 'true' is replaced by 'enabled'",
"match": "re",
"count": 1
},
{
- "line": ".*DEPRECATION: Option 'c5' value 'auto' is replaced by 'false'",
+ "line": ".*DEPRECATION: Option \"c5\" value 'auto' is replaced by 'false'",
"match": "re",
"count": 1
}
diff --git a/test cases/common/26 find program/meson.build b/test cases/common/26 find program/meson.build
index a20f6b4..72d311c 100644
--- a/test cases/common/26 find program/meson.build
+++ b/test cases/common/26 find program/meson.build
@@ -1,4 +1,4 @@
-project('find program')
+project('find program', meson_version: '>= 1.10.0',)
if build_machine.system() == 'windows'
# Things Windows does not provide:
@@ -40,3 +40,21 @@ assert(not prog.found(), 'Program should not be found')
prog = find_program('test_subdir.py', dirs : ['/nonexistent', meson.current_source_dir() / 'scripts'])
assert(prog.found(), 'Program should be found')
+
+prog = find_program('print-version.py')
+if build_machine.system() != 'cygwin'
+ assert(prog.cmd_array() != [prog.full_path()])
+ assert(prog.cmd_array().length() == 2)
+endif
+
+ret = run_command(prog.cmd_array(),'--version', check: true)
+assert(ret.returncode() == 0)
+assert(ret.stdout().strip() == '1.0')
+
+if build_machine.system() == 'windows'
+ prog = find_program('cmd.exe')
+ assert(prog.cmd_array() == [prog.full_path()])
+else
+ prog = find_program('ld')
+ assert(prog.cmd_array() == [prog.full_path()])
+endif \ No newline at end of file
diff --git a/test cases/common/280 pkgconfig-gen/meson.build b/test cases/common/280 pkgconfig-gen/meson.build
index 3f15888..42ce128 100644
--- a/test cases/common/280 pkgconfig-gen/meson.build
+++ b/test cases/common/280 pkgconfig-gen/meson.build
@@ -1,4 +1,4 @@
-project('pkgconfig-get', 'c')
+project('pkgconfig-get', 'c', meson_version: '>=1.9.0')
pkgg = import('pkgconfig')
@@ -16,4 +16,5 @@ pkgg.generate(
filebase : 'simple',
description : 'A simple demo library.',
libraries: [lib_dep],
-) \ No newline at end of file
+ license: 'Apache-2.0',
+)
diff --git a/test cases/common/281 subproj options/meson.build b/test cases/common/281 subproj options/meson.build
index 55fb109..d450004 100644
--- a/test cases/common/281 subproj options/meson.build
+++ b/test cases/common/281 subproj options/meson.build
@@ -1,4 +1,3 @@
-project('pkg_opt_test')
+project('pkg_opt_test', default_options: ['werror=false', 'sub:from_toplevel=true', 'sub:werror=true'])
-subproject('sub')
-subproject('sub2')
+subproject('sub', default_options: ['sub2:from_subp=true'])
diff --git a/test cases/common/281 subproj options/subprojects/sub/meson.build b/test cases/common/281 subproj options/subprojects/sub/meson.build
index 82cd386..6cc4906 100644
--- a/test cases/common/281 subproj options/subprojects/sub/meson.build
+++ b/test cases/common/281 subproj options/subprojects/sub/meson.build
@@ -1,8 +1,12 @@
project('subproject', 'c')
assert(get_option('bar') == true)
+assert(get_option('werror') == true)
+assert(get_option('from_toplevel') == true)
# b_lto is only initialized if used, see test "common/40 options"
cc = meson.get_compiler('c')
if cc.get_id() in ['gcc', 'clang', 'clang-cl']
assert(get_option('b_lto') == true)
endif
+
+subproject('sub2')
diff --git a/test cases/common/281 subproj options/subprojects/sub/meson_options.txt b/test cases/common/281 subproj options/subprojects/sub/meson_options.txt
index 129a7d4..7f94d02 100644
--- a/test cases/common/281 subproj options/subprojects/sub/meson_options.txt
+++ b/test cases/common/281 subproj options/subprojects/sub/meson_options.txt
@@ -1 +1,2 @@
option('bar', type: 'boolean', value: false)
+option('from_toplevel', type: 'boolean', value: false)
diff --git a/test cases/common/281 subproj options/subprojects/sub2/meson.build b/test cases/common/281 subproj options/subprojects/sub2/meson.build
index 3b0ed92..65f3e5a 100644
--- a/test cases/common/281 subproj options/subprojects/sub2/meson.build
+++ b/test cases/common/281 subproj options/subprojects/sub2/meson.build
@@ -1,5 +1,7 @@
project('subproject', 'c')
+assert(get_option('from_subp') == true)
+
# b_lto is only initialized if used, see test "common/40 options"
cc = meson.get_compiler('c')
if cc.get_id() in ['gcc', 'clang', 'clang-cl']
diff --git a/test cases/common/281 subproj options/subprojects/sub2/meson_options.txt b/test cases/common/281 subproj options/subprojects/sub2/meson_options.txt
new file mode 100644
index 0000000..d645182
--- /dev/null
+++ b/test cases/common/281 subproj options/subprojects/sub2/meson_options.txt
@@ -0,0 +1 @@
+option('from_subp', type: 'boolean', value: false)
diff --git a/test cases/common/282 test args and depends in path/libs/a/lib_a.c b/test cases/common/282 test args and depends in path/libs/a/lib_a.c
new file mode 100644
index 0000000..7191a69
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/a/lib_a.c
@@ -0,0 +1,5 @@
+char
+func_a (void)
+{
+ return 'a';
+}
diff --git a/test cases/common/282 test args and depends in path/libs/a/lib_a.def b/test cases/common/282 test args and depends in path/libs/a/lib_a.def
new file mode 100644
index 0000000..4af3bdb
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/a/lib_a.def
@@ -0,0 +1,3 @@
+LIBRARY LIBA
+EXPORTS
+ func_a
diff --git a/test cases/common/282 test args and depends in path/libs/a/meson.build b/test cases/common/282 test args and depends in path/libs/a/meson.build
new file mode 100644
index 0000000..0b4b6a4
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/a/meson.build
@@ -0,0 +1,5 @@
+lib_a = shared_library('a',
+ ['lib_a.c'],
+ name_prefix: 'lib',
+ gnu_symbol_visibility: 'default',
+ vs_module_defs: 'lib_a.def')
diff --git a/test cases/common/282 test args and depends in path/libs/b/lib_b.c b/test cases/common/282 test args and depends in path/libs/b/lib_b.c
new file mode 100644
index 0000000..17e5730
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/b/lib_b.c
@@ -0,0 +1,5 @@
+char
+func_b (void)
+{
+ return 'b';
+}
diff --git a/test cases/common/282 test args and depends in path/libs/b/lib_b.def b/test cases/common/282 test args and depends in path/libs/b/lib_b.def
new file mode 100644
index 0000000..403a731
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/b/lib_b.def
@@ -0,0 +1,3 @@
+LIBRARY LIBB
+EXPORTS
+ func_b
diff --git a/test cases/common/282 test args and depends in path/libs/b/meson.build b/test cases/common/282 test args and depends in path/libs/b/meson.build
new file mode 100644
index 0000000..766125d
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/b/meson.build
@@ -0,0 +1,5 @@
+lib_b = shared_library('b',
+ ['lib_b.c'],
+ name_prefix: 'lib',
+ gnu_symbol_visibility: 'default',
+ vs_module_defs: 'lib_b.def')
diff --git a/test cases/common/282 test args and depends in path/libs/meson.build b/test cases/common/282 test args and depends in path/libs/meson.build
new file mode 100644
index 0000000..b00ea8a
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/libs/meson.build
@@ -0,0 +1,2 @@
+subdir('a')
+subdir('b')
diff --git a/test cases/common/282 test args and depends in path/meson.build b/test cases/common/282 test args and depends in path/meson.build
new file mode 100644
index 0000000..d9dd9ad
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/meson.build
@@ -0,0 +1,19 @@
+project('test-args-and-depends-in-path', 'c')
+
+subdir('libs')
+
+dl_dep = dependency('dl', required: false)
+
+fs = import('fs')
+
+test_exe = executable('test-exe',
+ c_args: [
+ '-DLIBA=' + fs.name(lib_a.full_path()),
+ '-DLIBB=' + fs.name(lib_b.full_path()),
+ ],
+ sources: ['test.c'],
+ dependencies: [dl_dep])
+
+test ('test', test_exe,
+ args: [lib_a],
+ depends: [lib_b])
diff --git a/test cases/common/282 test args and depends in path/test.c b/test cases/common/282 test args and depends in path/test.c
new file mode 100644
index 0000000..82452ba
--- /dev/null
+++ b/test cases/common/282 test args and depends in path/test.c
@@ -0,0 +1,67 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <assert.h>
+
+#ifndef _WIN32
+#include <dlfcn.h>
+#else
+#include <windows.h>
+#endif
+
+typedef struct {
+ const char *library_name;
+ const char *func_name;
+ char expected_result;
+} test_t;
+
+static void
+load (test_t *test)
+{
+#ifndef _WIN32
+ void *h = dlopen (test->library_name, RTLD_NOW | RTLD_LOCAL);
+ if (h == NULL) {
+ fprintf (stderr, "dlopen (%s) failed: %s\n",
+ test->library_name, dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ typedef char (*func_t)(void);
+ func_t func = (func_t) dlsym (h, test->func_name);
+ assert (func != NULL);
+
+ assert (func () == test->expected_result);
+ dlclose (h);
+#else /* _WIN32 */
+ HMODULE h = LoadLibraryA (test->library_name);
+ if (h == NULL) {
+ fprintf (stderr, "LoadLibrary (%s) failed with error code %u\n",
+ test->library_name, (unsigned int) GetLastError ());
+ exit (EXIT_FAILURE);
+ }
+
+ typedef char (*func_t)(void);
+ func_t func = (func_t) GetProcAddress (h, test->func_name);
+ assert (func != NULL);
+
+ assert (func () == test->expected_result);
+ FreeLibrary (h);
+#endif
+}
+
+#define STRINGIFY_HELPER(x) #x
+#define STRINGIFY(x) STRINGIFY_HELPER(x)
+
+int
+main (void)
+{
+ test_t tests[] = {
+ {STRINGIFY (LIBA), "func_a", 'a'},
+ {STRINGIFY (LIBB), "func_b", 'b'},
+ };
+
+ for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
+ load (&tests[i]);
+
+ return 0;
+}
diff --git a/test cases/common/283 wrap override/meson.build b/test cases/common/283 wrap override/meson.build
new file mode 100644
index 0000000..76c84d6
--- /dev/null
+++ b/test cases/common/283 wrap override/meson.build
@@ -0,0 +1,8 @@
+project('wrap override')
+
+
+subproject('sub')
+
+# sub has subsub.wrap that provides subsub-1.0 dependency. Even if sub itself
+# does not use it, that dependency should now be available.
+dependency('subsub-1.0')
diff --git a/test cases/common/283 wrap override/subprojects/sub/meson.build b/test cases/common/283 wrap override/subprojects/sub/meson.build
new file mode 100644
index 0000000..abefb30
--- /dev/null
+++ b/test cases/common/283 wrap override/subprojects/sub/meson.build
@@ -0,0 +1,7 @@
+project('sub')
+
+# Simulate an optional feature that requires subsub.wrap, but that feature is
+# not enabled.
+if false
+ dependency('subsub-1.0')
+endif
diff --git a/test cases/common/283 wrap override/subprojects/sub/subprojects/subsub.wrap b/test cases/common/283 wrap override/subprojects/sub/subprojects/subsub.wrap
new file mode 100644
index 0000000..85a1a7c
--- /dev/null
+++ b/test cases/common/283 wrap override/subprojects/sub/subprojects/subsub.wrap
@@ -0,0 +1,5 @@
+[wrap-file]
+
+
+[provide]
+dependency_names = subsub-1.0
diff --git a/test cases/common/283 wrap override/subprojects/subsub/meson.build b/test cases/common/283 wrap override/subprojects/subsub/meson.build
new file mode 100644
index 0000000..668dcb3
--- /dev/null
+++ b/test cases/common/283 wrap override/subprojects/subsub/meson.build
@@ -0,0 +1,5 @@
+project('sub')
+
+# This simulates a subproject we previously downloaded using
+# subproject/sub/subproject/subsub.wrap
+meson.override_dependency('subsub-1.0', declare_dependency())
diff --git a/test cases/common/284 pkgconfig subproject/meson.build b/test cases/common/284 pkgconfig subproject/meson.build
new file mode 100644
index 0000000..3b1335b
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/meson.build
@@ -0,0 +1,13 @@
+project('simple', 'c', meson_version: '>=1.9.0')
+pkgg = import('pkgconfig')
+
+simple2_dep = dependency('simple2')
+
+simple_lib = library('simple',
+ 'simple.c',
+ dependencies: [simple2_dep]
+)
+
+pkgg.generate(simple_lib,
+requires: simple2_dep,
+)
diff --git a/test cases/common/284 pkgconfig subproject/simple.c b/test cases/common/284 pkgconfig subproject/simple.c
new file mode 100644
index 0000000..da1d909
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/simple.c
@@ -0,0 +1,6 @@
+#include"simple.h"
+#include <simple2.h>
+
+int simple_function(void) {
+ return simple_simple_function();
+}
diff --git a/test cases/common/284 pkgconfig subproject/simple.h b/test cases/common/284 pkgconfig subproject/simple.h
new file mode 100644
index 0000000..6896bfd
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/simple.h
@@ -0,0 +1,6 @@
+#ifndef SIMPLE_H_
+#define SIMPLE_H_
+
+int simple_function(void);
+
+#endif
diff --git a/test cases/common/284 pkgconfig subproject/subprojects/simple2/exports.def b/test cases/common/284 pkgconfig subproject/subprojects/simple2/exports.def
new file mode 100644
index 0000000..42c911b
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/subprojects/simple2/exports.def
@@ -0,0 +1,2 @@
+EXPORTS
+ simple_simple_function @1
diff --git a/test cases/common/284 pkgconfig subproject/subprojects/simple2/meson.build b/test cases/common/284 pkgconfig subproject/subprojects/simple2/meson.build
new file mode 100644
index 0000000..199fea6
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/subprojects/simple2/meson.build
@@ -0,0 +1,9 @@
+project('simple2', 'c', meson_version: '>=1.9.0')
+pkgg = import('pkgconfig')
+
+lib2 = library('simple2', 'simple2.c', vs_module_defs: 'exports.def')
+lib_dep = declare_dependency(link_with: lib2, include_directories: include_directories('.'))
+
+pkgg.generate(lib2)
+
+meson.override_dependency('simple2', lib_dep)
diff --git a/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.c b/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.c
new file mode 100644
index 0000000..215b2ae
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.c
@@ -0,0 +1,5 @@
+#include"simple2.h"
+
+int simple_simple_function(void) {
+ return 42;
+}
diff --git a/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.h b/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.h
new file mode 100644
index 0000000..472e135
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/subprojects/simple2/simple2.h
@@ -0,0 +1,6 @@
+#ifndef SIMPLE2_H_
+#define SIMPLE2_H_
+
+int simple_simple_function(void);
+
+#endif
diff --git a/test cases/common/284 pkgconfig subproject/test.json b/test cases/common/284 pkgconfig subproject/test.json
new file mode 100644
index 0000000..db6b52f
--- /dev/null
+++ b/test cases/common/284 pkgconfig subproject/test.json
@@ -0,0 +1,15 @@
+{
+ "installed": [
+ { "type": "file", "file": "usr/lib/pkgconfig/simple.pc"},
+ { "type": "file", "file": "usr/lib/pkgconfig/simple2.pc"}
+ ],
+ "matrix": {
+ "options": {
+ "default_library": [
+ { "val": "shared" },
+ { "val": "static" },
+ { "val": "both" }
+ ]
+ }
+ }
+}
diff --git a/test cases/failing/76 override exe config/foo.c b/test cases/common/285 atomic/a.c
index 03b2213..03b2213 100644
--- a/test cases/failing/76 override exe config/foo.c
+++ b/test cases/common/285 atomic/a.c
diff --git a/test cases/common/285 atomic/meson.build b/test cases/common/285 atomic/meson.build
new file mode 100644
index 0000000..323b9cc
--- /dev/null
+++ b/test cases/common/285 atomic/meson.build
@@ -0,0 +1,23 @@
+project('meson system dependency', 'c', meson_version: '>=1.7.0')
+
+cc = meson.get_compiler('c')
+
+# We could check if dependency('atomic') actually finds something when
+# we 'know' it exists (MESON_SKIP_TEST) but that's likely to be brittle,
+# so don't bother (for now, at least).
+atomic = dependency('atomic', required : false)
+
+# If the dependency provider says it found something, make sure it can
+# be linked against (https://github.com/mesonbuild/meson/issues/14946).
+dependencies = [
+ atomic
+]
+
+exe = executable(
+ 'a',
+ 'a.c',
+ dependencies : dependencies,
+ install : false,
+)
+
+test('basic', exe)
diff --git a/test cases/common/286 importstd/meson.build b/test cases/common/286 importstd/meson.build
new file mode 100644
index 0000000..06e0504
--- /dev/null
+++ b/test cases/common/286 importstd/meson.build
@@ -0,0 +1,26 @@
+project('importstd', 'cpp', default_options: {'cpp_importstd': 'true',
+ 'cpp_std': 'c++latest,c++23,c++20'})
+
+cpp = meson.get_compiler('cpp')
+
+cpp_id = cpp.get_id()
+cpp_version = cpp.version()
+
+if cpp_id == 'gcc' and cpp_version.version_compare('>=15.3')
+ # Versions between 15.0 and 15.2 are too unreliable to test.
+ # The same version number of GCC works in some distros
+ # but fails in others.
+ istd_supported = true
+elif cpp_id == 'msvc' and cpp_version.version_compare('>=19.44.35219')
+ istd_supported = get_option('backend') == 'ninja'
+else
+ istd_supported = false
+endif
+
+if istd_supported
+ useistd = executable('useistd', 'useistd.cpp')
+ test('useistd', useistd)
+
+else
+ message('Compiler not yet supported, import std test not run.')
+endif
diff --git a/test cases/common/286 importstd/useistd.cpp b/test cases/common/286 importstd/useistd.cpp
new file mode 100644
index 0000000..70f62ec
--- /dev/null
+++ b/test cases/common/286 importstd/useistd.cpp
@@ -0,0 +1,6 @@
+import std;
+
+int main(int, char**) {
+ std::print("Import STD is working.\n");
+ return 0;
+}
diff --git a/test cases/common/32 has header/meson.build b/test cases/common/32 has header/meson.build
index e6f6efb..7358be7 100644
--- a/test cases/common/32 has header/meson.build
+++ b/test cases/common/32 has header/meson.build
@@ -1,6 +1,7 @@
project('has header', 'c', 'cpp')
host_system = host_machine.system()
+cpp = meson.get_compiler('cpp')
non_existent_header = 'ouagadougou.h'
@@ -9,16 +10,20 @@ configure_file(input : non_existent_header,
output : non_existent_header,
configuration : configuration_data())
+is_libcxx = cpp.has_header_symbol('ciso646', '_LIBCPP_VERSION')
+
# Test that the fallback to __has_include also works on all compilers
-if host_system != 'darwin'
+#
+# darwin: can't redefine builtin macros so the above doesn't work
+# libcxx: Undefining __has_include() breaks LLVM libcxx platform.h
+if host_system != 'darwin' and not is_libcxx
fallbacks = ['', '\n#undef __has_include']
else
- # On Darwin's clang you can't redefine builtin macros so the above doesn't work
fallbacks = ['']
endif
foreach fallback : fallbacks
- foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')]
+ foreach comp : [meson.get_compiler('c'), cpp]
assert(comp.has_header('stdio.h', prefix : fallback), 'Stdio missing.')
# stdio.h doesn't actually need stdlib.h, but just test that setting the
diff --git a/test cases/common/35 string operations/meson.build b/test cases/common/35 string operations/meson.build
index ab77b49..8cc5b0c 100644
--- a/test cases/common/35 string operations/meson.build
+++ b/test cases/common/35 string operations/meson.build
@@ -67,6 +67,12 @@ assert(''.join(['a', 'b', 'c']) == 'abc', 'empty join() broken')
assert(' '.join(['a']) == 'a', 'single join broken')
assert(' '.join(['a'], ['b', ['c']], 'd') == 'a b c d', 'varargs join broken')
+# meson.version() has it's own cmp method
+assert(meson.version().version_compare('>1.1.0'), 'meson version compare 1 arg broken')
+assert(meson.version().version_compare('>1.1.0', '<10.0.0'), 'meson version compare 2 args broken')
+assert(not meson.version().version_compare('<1.1.0', '<1.0.0'), 'meson version compare 2 args broken')
+assert(meson.version().version_compare('!= 1.9.1', '> 1.8.0'), 'meson version compare neq broken')
+
version_number = '1.2.8'
assert(version_number.version_compare('>=1.2.8'), 'Version_compare gt broken')
diff --git a/test cases/common/40 options/meson.build b/test cases/common/40 options/meson.build
index 3849d54..f41265a 100644
--- a/test cases/common/40 options/meson.build
+++ b/test cases/common/40 options/meson.build
@@ -40,8 +40,7 @@ if get_option('integer_opt') != 3
endif
negint = get_option('neg_int_opt')
-
-if negint != -3 and negint != -10
+if negint not in [-2, -3, -10]
error('Incorrect value @0@ in negative integer option.'.format(negint))
endif
diff --git a/test cases/common/42 subproject/meson.build b/test cases/common/42 subproject/meson.build
index bc24adb..be2d90f 100644
--- a/test cases/common/42 subproject/meson.build
+++ b/test cases/common/42 subproject/meson.build
@@ -8,6 +8,7 @@ project('subproj user', 'c',
assert(meson.project_name() == 'subproj user', 'Incorrect project name')
sub = subproject('sublib', version : '1.0.0')
+assert(sub.found())
if meson.project_version() != '2.3.4'
error('Incorrect master project version string:' + meson.project_version())
@@ -29,3 +30,6 @@ unknown_var = sub.get_variable('does-not-exist', [])
if unknown_var != []
error ('unexpected fallback value for subproject.get_variable()')
endif
+
+unusedsub = subproject('subunused', required: get_option('disabled_feature'))
+assert(not unusedsub.found())
diff --git a/test cases/common/42 subproject/meson.options b/test cases/common/42 subproject/meson.options
new file mode 100644
index 0000000..ae9a39f
--- /dev/null
+++ b/test cases/common/42 subproject/meson.options
@@ -0,0 +1 @@
+option('disabled_feature', type: 'feature', value: 'disabled')
diff --git a/test cases/common/42 subproject/subprojects/subunused/meson.build b/test cases/common/42 subproject/subprojects/subunused/meson.build
new file mode 100644
index 0000000..a42fed3
--- /dev/null
+++ b/test cases/common/42 subproject/subprojects/subunused/meson.build
@@ -0,0 +1 @@
+syntax error because the subproject is unused
diff --git a/test cases/common/42 subproject/subprojects/subunused/meson.options b/test cases/common/42 subproject/subprojects/subunused/meson.options
new file mode 100644
index 0000000..a42fed3
--- /dev/null
+++ b/test cases/common/42 subproject/subprojects/subunused/meson.options
@@ -0,0 +1 @@
+syntax error because the subproject is unused
diff --git a/test cases/common/42 subproject/test.json b/test cases/common/42 subproject/test.json
index e469052..7400ff4 100644
--- a/test cases/common/42 subproject/test.json
+++ b/test cases/common/42 subproject/test.json
@@ -7,5 +7,12 @@
{"type": "file", "file": "usr/share/sublib/COPYING"},
{"type": "file", "file": "usr/share/sublib/subprojects/sublib/sublicense1.txt"},
{"type": "file", "file": "usr/share/sublib/subprojects/sublib/sublicense2.txt"}
- ]
+ ],
+ "matrix": {
+ "options": {
+ "subunused:opt1": [
+ { "val": "42" }
+ ]
+ }
+ }
}
diff --git a/test cases/common/43 subproject options/meson.build b/test cases/common/43 subproject options/meson.build
index a905272..d274fbf 100644
--- a/test cases/common/43 subproject options/meson.build
+++ b/test cases/common/43 subproject options/meson.build
@@ -1,4 +1,4 @@
-project('suboptions')
+project('suboptions', default_options: {'c_args': ['-O']})
subproject('subproject')
diff --git a/test cases/common/43 subproject options/subprojects/subproject/meson.build b/test cases/common/43 subproject options/subprojects/subproject/meson.build
index d00a024..24a9dc0 100644
--- a/test cases/common/43 subproject options/subprojects/subproject/meson.build
+++ b/test cases/common/43 subproject options/subprojects/subproject/meson.build
@@ -1,4 +1,11 @@
-project('subproject')
+project('subproject', 'c',
+ default_options: {'c_std': 'c11', 'c_args': ['-O2']})
+
+assert(get_option('c_std') == 'c11')
+# could be -O2 as above, or for some cross compilation tests in
+# CI it could come from the machine file. but it's certainly
+# not the value in the top-level project.
+assert(get_option('c_args') != ['-O'])
if get_option('opt')
error('option set when it should be unset.')
diff --git a/test cases/common/44 pkgconfig-gen/meson.build b/test cases/common/44 pkgconfig-gen/meson.build
index fd6371e..c312873 100644
--- a/test cases/common/44 pkgconfig-gen/meson.build
+++ b/test cases/common/44 pkgconfig-gen/meson.build
@@ -193,3 +193,11 @@ simple7 = library('simple7', include_directories: 'inc1')
dep = declare_dependency(include_directories: 'inc2')
install_headers('inc1/inc1.h', 'inc2/inc2.h')
pkgg.generate(simple7, libraries: dep)
+
+# Regression test: empty string passed to requires.private should not
+# fail the build
+pkgg.generate(
+ name : 'emptytest',
+ description : 'Check that requires.private can be an empty string',
+ requires_private: '',
+)
diff --git a/test cases/common/44 pkgconfig-gen/test.json b/test cases/common/44 pkgconfig-gen/test.json
index 01786d4..36c7e95 100644
--- a/test cases/common/44 pkgconfig-gen/test.json
+++ b/test cases/common/44 pkgconfig-gen/test.json
@@ -16,6 +16,7 @@
{"type": "file", "file": "usr/lib/pkgconfig/simple5.pc"},
{"type": "file", "file": "usr/lib/pkgconfig/simple6.pc"},
{"type": "file", "file": "usr/lib/pkgconfig/simple7.pc"},
+ {"type": "file", "file": "usr/lib/pkgconfig/emptytest.pc"},
{"type": "file", "file": "usr/lib/pkgconfig/ct.pc"},
{"type": "file", "file": "usr/lib/pkgconfig/ct0.pc"},
{"type": "file", "file": "usr/share/pkgconfig/libhello_nolib.pc"}
diff --git a/test cases/common/56 array methods/meson.build b/test cases/common/56 array methods/meson.build
index e9e4969..3cc1067 100644
--- a/test cases/common/56 array methods/meson.build
+++ b/test cases/common/56 array methods/meson.build
@@ -1,4 +1,4 @@
-project('array methods')
+project('array methods', meson_version : '>= 1.9')
empty = []
one = ['abc']
@@ -68,3 +68,20 @@ endif
if not combined.contains('ghi')
error('Combined claims not to contain ghi.')
endif
+
+# test array slicing
+assert(['a', 'b', 'c'].slice() == ['a', 'b', 'c'])
+assert(['a', 'b', 'c'].slice(step : 2) == ['a', 'c'])
+assert(['a', 'b', 'c'].slice(step : -1) == ['c', 'b', 'a'])
+assert(['a', 'b', 'c'].slice(step : -2) == ['c', 'a'])
+assert(['a', 'b', 'c'].slice(1, 2) == ['b'])
+assert(['a', 'b', 'c'].slice(2, -2) == [])
+assert(['a', 'b', 'c'].slice(-9876543, 2) == ['a', 'b'])
+assert(['a', 'b', 'c', 'd', 'e'].slice(1, 12, step : 2) == ['b', 'd'])
+
+# test array flattening
+x = ['a', ['b'], [[[[[[['c'], 'd']]], 'e']]]]
+assert(x.length() == 3)
+assert(x.flatten().length() == 5)
+assert(x.flatten() == ['a', 'b', 'c', 'd', 'e'])
+assert(['a', ['b', 'c']].flatten() == ['a', 'b', 'c'])
diff --git a/test cases/common/98 subproject subdir/meson.build b/test cases/common/98 subproject subdir/meson.build
index d2bafed..5d92772 100644
--- a/test cases/common/98 subproject subdir/meson.build
+++ b/test cases/common/98 subproject subdir/meson.build
@@ -83,7 +83,7 @@ d = dependency('subsubsub')
assert(d.found(), 'Should be able to fallback to sub-sub-subproject')
# Verify that `static: true` implies 'default_library=static'.
-d = dependency('sub_static', static: true)
+d = dependency('sub_static', static: true, default_options: ['bar=true'])
assert(d.found())
# Verify that when not specifying static kwarg we can still get fallback dep.
d = dependency('sub_static')
diff --git a/test cases/common/98 subproject subdir/subprojects/sub_static/meson.build b/test cases/common/98 subproject subdir/subprojects/sub_static/meson.build
index 6c00623..8de7cb4 100644
--- a/test cases/common/98 subproject subdir/subprojects/sub_static/meson.build
+++ b/test cases/common/98 subproject subdir/subprojects/sub_static/meson.build
@@ -1,6 +1,7 @@
project('sub_static')
assert(get_option('default_library') == 'static')
+assert(get_option('bar') == true)
meson.override_dependency('sub_static', declare_dependency())
meson.override_dependency('sub_static2', declare_dependency(), static: true)
meson.override_dependency('sub_static3', declare_dependency(variables: {'static': 'true'}), static: true)
diff --git a/test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt b/test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt
new file mode 100644
index 0000000..129a7d4
--- /dev/null
+++ b/test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt
@@ -0,0 +1 @@
+option('bar', type: 'boolean', value: false)
diff --git a/test cases/csharp/4 external dep/meson.build b/test cases/csharp/4 external dep/meson.build
index 019d618..e1c3a7c 100644
--- a/test cases/csharp/4 external dep/meson.build
+++ b/test cases/csharp/4 external dep/meson.build
@@ -1,9 +1,9 @@
project('C# external library', 'cs')
-glib_sharp_2 = dependency('glib-sharp-2.0', required : false)
+glib_sharp_3 = dependency('glib-sharp-3.0', required : false)
-if not glib_sharp_2.found()
+if not glib_sharp_3.found()
error('MESON_SKIP_TEST glib# not found.')
endif
-e = executable('prog', 'prog.cs', dependencies: glib_sharp_2, install : true)
+e = executable('prog', 'prog.cs', dependencies: glib_sharp_3, install : true)
test('libtest', e, args: [join_paths(meson.current_source_dir(), 'hello.txt')])
diff --git a/test cases/cuda/10 cuda dependency/modules/meson.build b/test cases/cuda/10 cuda dependency/modules/meson.build
index 0da43f2..b934c6b 100644
--- a/test cases/cuda/10 cuda dependency/modules/meson.build
+++ b/test cases/cuda/10 cuda dependency/modules/meson.build
@@ -1,2 +1,2 @@
-exe = executable('prog', 'prog.cc', dependencies: dependency('cuda', modules: ['cublas']))
+exe = executable('prog', 'prog.cc', dependencies: dependency('cuda', modules: ['cublas', 'nvidia-ml']))
test('cudatest', exe)
diff --git a/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build b/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build
index c0fed83..e36d877 100644
--- a/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build
+++ b/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build
@@ -1,2 +1,2 @@
-exe = executable('prog', 'prog.cu', dependencies: dependency('cuda', modules: ['cublas']))
+exe = executable('prog', 'prog.cu', dependencies: dependency('cuda', modules: ['cublas', 'nvidia-ml']))
test('cudatest', exe)
diff --git a/test cases/cuda/12 cuda dependency (mixed)/meson.build b/test cases/cuda/12 cuda dependency (mixed)/meson.build
index 44a49db..4519b28 100644
--- a/test cases/cuda/12 cuda dependency (mixed)/meson.build
+++ b/test cases/cuda/12 cuda dependency (mixed)/meson.build
@@ -1,4 +1,4 @@
project('cuda dependency', 'cpp', 'cuda')
-exe = executable('prog', 'prog.cpp', 'kernel.cu', dependencies: dependency('cuda', modules: ['cublas']), link_language: 'cpp')
+exe = executable('prog', 'prog.cpp', 'kernel.cu', dependencies: dependency('cuda', modules: ['cublas', 'nvidia-ml']), link_language: 'cpp')
test('cudatest', exe)
diff --git a/test cases/cython/2 generated sources/meson.build b/test cases/cython/2 generated sources/meson.build
index d438d80..3d2837d 100644
--- a/test cases/cython/2 generated sources/meson.build
+++ b/test cases/cython/2 generated sources/meson.build
@@ -79,13 +79,20 @@ stuff_pxi = fs.copyfile(
'stuff.pxi'
)
+stuff_pxi_2 = configure_file(
+ input: 'stuff.pxi.in',
+ output: 'stuff.pxi',
+ configuration: configuration_data(),
+ install: false
+)
+
# Need to copy the cython source to the build directory
# since meson can only generate the .pxi there
includestuff_pyx = fs.copyfile(
'includestuff.pyx'
)
-stuff_pxi_dep = declare_dependency(sources: stuff_pxi)
+stuff_pxi_dep = declare_dependency(sources: [stuff_pxi, stuff_pxi_2])
includestuff_ext = py3.extension_module(
'includestuff',
diff --git a/test cases/failing/100 compiler no lang/test.json b/test cases/failing/100 compiler no lang/test.json
deleted file mode 100644
index 935d79d..0000000
--- a/test cases/failing/100 compiler no lang/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/100 compiler no lang/meson.build:2:6: ERROR: Tried to access compiler for language \"c\", not specified for host machine."
- }
- ]
-}
diff --git a/test cases/failing/101 no fallback/meson.build b/test cases/failing/100 no fallback/meson.build
index 91d6b93..91d6b93 100644
--- a/test cases/failing/101 no fallback/meson.build
+++ b/test cases/failing/100 no fallback/meson.build
diff --git a/test cases/failing/101 no fallback/subprojects/foob/meson.build b/test cases/failing/100 no fallback/subprojects/foob/meson.build
index b2c4814..b2c4814 100644
--- a/test cases/failing/101 no fallback/subprojects/foob/meson.build
+++ b/test cases/failing/100 no fallback/subprojects/foob/meson.build
diff --git a/test cases/failing/101 no fallback/test.json b/test cases/failing/100 no fallback/test.json
index 5fbffe3..5fbffe3 100644
--- a/test cases/failing/101 no fallback/test.json
+++ b/test cases/failing/100 no fallback/test.json
diff --git a/test cases/failing/102 feature require/meson.build b/test cases/failing/101 feature require/meson.build
index 8c47e37..8c47e37 100644
--- a/test cases/failing/102 feature require/meson.build
+++ b/test cases/failing/101 feature require/meson.build
diff --git a/test cases/failing/102 feature require/meson_options.txt b/test cases/failing/101 feature require/meson_options.txt
index 5910a87..5910a87 100644
--- a/test cases/failing/102 feature require/meson_options.txt
+++ b/test cases/failing/101 feature require/meson_options.txt
diff --git a/test cases/failing/102 feature require/test.json b/test cases/failing/101 feature require/test.json
index 2da5494..2da5494 100644
--- a/test cases/failing/102 feature require/test.json
+++ b/test cases/failing/101 feature require/test.json
diff --git a/test cases/failing/103 feature require.bis/meson.build b/test cases/failing/102 feature require.bis/meson.build
index 4ea65e5..4ea65e5 100644
--- a/test cases/failing/103 feature require.bis/meson.build
+++ b/test cases/failing/102 feature require.bis/meson.build
diff --git a/test cases/failing/103 feature require.bis/meson_options.txt b/test cases/failing/102 feature require.bis/meson_options.txt
index 5910a87..5910a87 100644
--- a/test cases/failing/103 feature require.bis/meson_options.txt
+++ b/test cases/failing/102 feature require.bis/meson_options.txt
diff --git a/test cases/failing/103 feature require.bis/test.json b/test cases/failing/102 feature require.bis/test.json
index ed488af..ed488af 100644
--- a/test cases/failing/103 feature require.bis/test.json
+++ b/test cases/failing/102 feature require.bis/test.json
diff --git a/test cases/failing/104 no build get_external_property/meson.build b/test cases/failing/103 no build get_external_property/meson.build
index 8a4215c..8a4215c 100644
--- a/test cases/failing/104 no build get_external_property/meson.build
+++ b/test cases/failing/103 no build get_external_property/meson.build
diff --git a/test cases/failing/104 no build get_external_property/test.json b/test cases/failing/103 no build get_external_property/test.json
index 5792213..74ecb75 100644
--- a/test cases/failing/104 no build get_external_property/test.json
+++ b/test cases/failing/103 no build get_external_property/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/104 no build get_external_property/meson.build:3:14: ERROR: Unknown property for build machine: nonexisting"
+ "line": "test cases/failing/103 no build get_external_property/meson.build:3:14: ERROR: Unknown property for build machine: nonexisting"
}
]
}
diff --git a/test cases/failing/105 enter subdir twice/meson.build b/test cases/failing/104 enter subdir twice/meson.build
index d9bf9f5..d9bf9f5 100644
--- a/test cases/failing/105 enter subdir twice/meson.build
+++ b/test cases/failing/104 enter subdir twice/meson.build
diff --git a/test cases/failing/105 enter subdir twice/sub/meson.build b/test cases/failing/104 enter subdir twice/sub/meson.build
index d036a3f..d036a3f 100644
--- a/test cases/failing/105 enter subdir twice/sub/meson.build
+++ b/test cases/failing/104 enter subdir twice/sub/meson.build
diff --git a/test cases/failing/105 enter subdir twice/test.json b/test cases/failing/104 enter subdir twice/test.json
index 80e8c10..9d63889 100644
--- a/test cases/failing/105 enter subdir twice/test.json
+++ b/test cases/failing/104 enter subdir twice/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/105 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited."
+ "line": "test cases/failing/104 enter subdir twice/meson.build:3:0: ERROR: Tried to enter directory \"sub\", which has already been visited."
}
]
}
diff --git a/test cases/failing/106 invalid fstring/109 invalid fstring/meson.build b/test cases/failing/105 invalid fstring/109 invalid fstring/meson.build
index dd22f56..dd22f56 100644
--- a/test cases/failing/106 invalid fstring/109 invalid fstring/meson.build
+++ b/test cases/failing/105 invalid fstring/109 invalid fstring/meson.build
diff --git a/test cases/failing/106 invalid fstring/109 invalid fstring/test.json b/test cases/failing/105 invalid fstring/109 invalid fstring/test.json
index 71d8f59..71d8f59 100644
--- a/test cases/failing/106 invalid fstring/109 invalid fstring/test.json
+++ b/test cases/failing/105 invalid fstring/109 invalid fstring/test.json
diff --git a/test cases/failing/106 invalid fstring/meson.build b/test cases/failing/105 invalid fstring/meson.build
index 7eb3ccf..7eb3ccf 100644
--- a/test cases/failing/106 invalid fstring/meson.build
+++ b/test cases/failing/105 invalid fstring/meson.build
diff --git a/test cases/failing/106 invalid fstring/test.json b/test cases/failing/105 invalid fstring/test.json
index 8d53428..ae2a531 100644
--- a/test cases/failing/106 invalid fstring/test.json
+++ b/test cases/failing/105 invalid fstring/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/106 invalid fstring/meson.build:3:4: ERROR: Identifier \"foo\" does not name a variable."
+ "line": "test cases/failing/105 invalid fstring/meson.build:3:4: ERROR: Identifier \"foo\" does not name a variable."
}
]
}
diff --git a/test cases/failing/107 compiler argument checking/meson.build b/test cases/failing/106 compiler argument checking/meson.build
index bb1f447..bb1f447 100644
--- a/test cases/failing/107 compiler argument checking/meson.build
+++ b/test cases/failing/106 compiler argument checking/meson.build
diff --git a/test cases/failing/107 compiler argument checking/test.json b/test cases/failing/106 compiler argument checking/test.json
index a1576aa..262db69 100644
--- a/test cases/failing/107 compiler argument checking/test.json
+++ b/test cases/failing/106 compiler argument checking/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/107 compiler argument checking/meson.build:4:25: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\""
+ "line": "test cases/failing/106 compiler argument checking/meson.build:4:25: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\""
}
]
}
diff --git a/test cases/failing/108 empty fallback/meson.build b/test cases/failing/107 empty fallback/meson.build
index f4eb5fe..f4eb5fe 100644
--- a/test cases/failing/108 empty fallback/meson.build
+++ b/test cases/failing/107 empty fallback/meson.build
diff --git a/test cases/failing/108 empty fallback/subprojects/foo/meson.build b/test cases/failing/107 empty fallback/subprojects/foo/meson.build
index c9e134b..c9e134b 100644
--- a/test cases/failing/108 empty fallback/subprojects/foo/meson.build
+++ b/test cases/failing/107 empty fallback/subprojects/foo/meson.build
diff --git a/test cases/failing/108 empty fallback/test.json b/test cases/failing/107 empty fallback/test.json
index c631882..3d69a58 100644
--- a/test cases/failing/108 empty fallback/test.json
+++ b/test cases/failing/107 empty fallback/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/108 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found.*"
+ "line": "test cases/failing/107 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found.*"
}
]
}
diff --git a/test cases/failing/109 cmake executable dependency/meson.build b/test cases/failing/108 cmake executable dependency/meson.build
index 0fc0f9b..0fc0f9b 100644
--- a/test cases/failing/109 cmake executable dependency/meson.build
+++ b/test cases/failing/108 cmake executable dependency/meson.build
diff --git a/test cases/failing/109 cmake executable dependency/subprojects/cmlib/CMakeLists.txt b/test cases/failing/108 cmake executable dependency/subprojects/cmlib/CMakeLists.txt
index e927eae..e927eae 100644
--- a/test cases/failing/109 cmake executable dependency/subprojects/cmlib/CMakeLists.txt
+++ b/test cases/failing/108 cmake executable dependency/subprojects/cmlib/CMakeLists.txt
diff --git a/test cases/failing/109 cmake executable dependency/subprojects/cmlib/main.c b/test cases/failing/108 cmake executable dependency/subprojects/cmlib/main.c
index 9b6bdc2..9b6bdc2 100644
--- a/test cases/failing/109 cmake executable dependency/subprojects/cmlib/main.c
+++ b/test cases/failing/108 cmake executable dependency/subprojects/cmlib/main.c
diff --git a/test cases/failing/109 cmake executable dependency/test.json b/test cases/failing/108 cmake executable dependency/test.json
index 92a6ee1..5a88b0d 100644
--- a/test cases/failing/109 cmake executable dependency/test.json
+++ b/test cases/failing/108 cmake executable dependency/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/109 cmake executable dependency/meson.build:5:14: ERROR: main is an executable and does not support the dependency() method. Use target() instead."
+ "line": "test cases/failing/108 cmake executable dependency/meson.build:5:14: ERROR: main is an executable and does not support the dependency() method. Use target() instead."
}
],
"tools": {
diff --git a/test cases/failing/110 allow_fallback with fallback/meson.build b/test cases/failing/109 allow_fallback with fallback/meson.build
index 2874e42..2874e42 100644
--- a/test cases/failing/110 allow_fallback with fallback/meson.build
+++ b/test cases/failing/109 allow_fallback with fallback/meson.build
diff --git a/test cases/failing/110 allow_fallback with fallback/test.json b/test cases/failing/109 allow_fallback with fallback/test.json
index b558ecc..76917b1 100644
--- a/test cases/failing/110 allow_fallback with fallback/test.json
+++ b/test cases/failing/109 allow_fallback with fallback/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/110 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive"
+ "line": "test cases/failing/109 allow_fallback with fallback/meson.build:3:0: ERROR: \"fallback\" and \"allow_fallback\" arguments are mutually exclusive"
}
]
}
diff --git a/test cases/failing/111 nonsensical bindgen/meson.build b/test cases/failing/110 nonsensical bindgen/meson.build
index 6995f67..40e9a2d 100644
--- a/test cases/failing/111 nonsensical bindgen/meson.build
+++ b/test cases/failing/110 nonsensical bindgen/meson.build
@@ -1,4 +1,4 @@
-# SPDX-license-identifer: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation
project('rustmod bindgen', 'c')
diff --git a/test cases/failing/111 nonsensical bindgen/src/header.h b/test cases/failing/110 nonsensical bindgen/src/header.h
index 750621f..5299f13 100644
--- a/test cases/failing/111 nonsensical bindgen/src/header.h
+++ b/test cases/failing/110 nonsensical bindgen/src/header.h
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#pragma once
diff --git a/test cases/failing/111 nonsensical bindgen/src/source.c b/test cases/failing/110 nonsensical bindgen/src/source.c
index d652d28..d968ba4 100644
--- a/test cases/failing/111 nonsensical bindgen/src/source.c
+++ b/test cases/failing/110 nonsensical bindgen/src/source.c
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#include "header.h"
diff --git a/test cases/failing/111 nonsensical bindgen/test.json b/test cases/failing/110 nonsensical bindgen/test.json
index f2dc92c..43cfa3a 100644
--- a/test cases/failing/111 nonsensical bindgen/test.json
+++ b/test cases/failing/110 nonsensical bindgen/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/111 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/110 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/112 run_target in test/meson.build b/test cases/failing/111 run_target in test/meson.build
index 117009e..117009e 100644
--- a/test cases/failing/112 run_target in test/meson.build
+++ b/test cases/failing/111 run_target in test/meson.build
diff --git a/test cases/failing/112 run_target in test/test.json b/test cases/failing/111 run_target in test/test.json
index 5158978..c0ec0e6 100644
--- a/test cases/failing/112 run_target in test/test.json
+++ b/test cases/failing/111 run_target in test/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/112 run_target in test/meson.build:7:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex | ExternalProgram]"
+ "line": "test cases/failing/111 run_target in test/meson.build:7:0: ERROR: test keyword argument 'args' was of type array[RunTarget] but should have been array[str | File | BuildTarget | CustomTarget | CustomTargetIndex | ExternalProgram]"
}
]
}
diff --git a/test cases/failing/112 run_target in test/trivial.c b/test cases/failing/111 run_target in test/trivial.c
index 96612d4..96612d4 100644
--- a/test cases/failing/112 run_target in test/trivial.c
+++ b/test cases/failing/111 run_target in test/trivial.c
diff --git a/test cases/failing/113 run_target in add_install_script/meson.build b/test cases/failing/112 run_target in add_install_script/meson.build
index d3634bf..d3634bf 100644
--- a/test cases/failing/113 run_target in add_install_script/meson.build
+++ b/test cases/failing/112 run_target in add_install_script/meson.build
diff --git a/test cases/failing/113 run_target in add_install_script/test.json b/test cases/failing/112 run_target in add_install_script/test.json
index c9ed33e..7753c31 100644
--- a/test cases/failing/113 run_target in add_install_script/test.json
+++ b/test cases/failing/112 run_target in add_install_script/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/113 run_target in add_install_script/meson.build:7: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/112 run_target in add_install_script/meson.build:7: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/113 run_target in add_install_script/trivial.c b/test cases/failing/112 run_target in add_install_script/trivial.c
index 1b14571..1b14571 100644
--- a/test cases/failing/113 run_target in add_install_script/trivial.c
+++ b/test cases/failing/112 run_target in add_install_script/trivial.c
diff --git a/test cases/failing/114 pathsep in install_symlink/meson.build b/test cases/failing/113 pathsep in install_symlink/meson.build
index cce82c2..cce82c2 100644
--- a/test cases/failing/114 pathsep in install_symlink/meson.build
+++ b/test cases/failing/113 pathsep in install_symlink/meson.build
diff --git a/test cases/failing/114 pathsep in install_symlink/test.json b/test cases/failing/113 pathsep in install_symlink/test.json
index bca4e0e..bbf25e2 100644
--- a/test cases/failing/114 pathsep in install_symlink/test.json
+++ b/test cases/failing/113 pathsep in install_symlink/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/114 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/113 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/115 subproject version conflict/meson.build b/test cases/failing/114 subproject version conflict/meson.build
index ffbcc13..ffbcc13 100644
--- a/test cases/failing/115 subproject version conflict/meson.build
+++ b/test cases/failing/114 subproject version conflict/meson.build
diff --git a/test cases/failing/115 subproject version conflict/subprojects/A/meson.build b/test cases/failing/114 subproject version conflict/subprojects/A/meson.build
index 7da4df0..7da4df0 100644
--- a/test cases/failing/115 subproject version conflict/subprojects/A/meson.build
+++ b/test cases/failing/114 subproject version conflict/subprojects/A/meson.build
diff --git a/test cases/failing/115 subproject version conflict/subprojects/B/meson.build b/test cases/failing/114 subproject version conflict/subprojects/B/meson.build
index 0ead934..0ead934 100644
--- a/test cases/failing/115 subproject version conflict/subprojects/B/meson.build
+++ b/test cases/failing/114 subproject version conflict/subprojects/B/meson.build
diff --git a/test cases/failing/115 subproject version conflict/test.json b/test cases/failing/114 subproject version conflict/test.json
index 23c7eb5..6548972 100644
--- a/test cases/failing/115 subproject version conflict/test.json
+++ b/test cases/failing/114 subproject version conflict/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/115 subproject version conflict/meson.build:4:8: ERROR: Subproject B version is 100 but ['1'] required."
+ "line": "test cases/failing/114 subproject version conflict/meson.build:4:8: ERROR: Subproject B version is 100 but ['1'] required."
}
]
}
diff --git a/test cases/failing/116 structured source empty string/main.rs b/test cases/failing/115 structured source empty string/main.rs
index e69de29..e69de29 100644
--- a/test cases/failing/116 structured source empty string/main.rs
+++ b/test cases/failing/115 structured source empty string/main.rs
diff --git a/test cases/failing/116 structured source empty string/meson.build b/test cases/failing/115 structured source empty string/meson.build
index 0715991..0715991 100644
--- a/test cases/failing/116 structured source empty string/meson.build
+++ b/test cases/failing/115 structured source empty string/meson.build
diff --git a/test cases/failing/116 structured source empty string/test.json b/test cases/failing/115 structured source empty string/test.json
index 00efbe3..48c1c83 100644
--- a/test cases/failing/116 structured source empty string/test.json
+++ b/test cases/failing/115 structured source empty string/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/116 structured source empty string/meson.build:9:2: ERROR: structured_sources: keys to dictionary argument may not be an empty string."
+ "line": "test cases/failing/115 structured source empty string/meson.build:9:2: ERROR: structured_sources: keys to dictionary argument may not be an empty string."
}
]
}
diff --git a/test cases/failing/117 structured_sources conflicts/main.rs b/test cases/failing/116 structured_sources conflicts/main.rs
index e69de29..e69de29 100644
--- a/test cases/failing/117 structured_sources conflicts/main.rs
+++ b/test cases/failing/116 structured_sources conflicts/main.rs
diff --git a/test cases/failing/117 structured_sources conflicts/meson.build b/test cases/failing/116 structured_sources conflicts/meson.build
index 561ad86..561ad86 100644
--- a/test cases/failing/117 structured_sources conflicts/meson.build
+++ b/test cases/failing/116 structured_sources conflicts/meson.build
diff --git a/test cases/failing/117 structured_sources conflicts/test.json b/test cases/failing/116 structured_sources conflicts/test.json
index a7201b5..7b53923 100644
--- a/test cases/failing/117 structured_sources conflicts/test.json
+++ b/test cases/failing/116 structured_sources conflicts/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/117 structured_sources conflicts/meson.build:7:0: ERROR: Conflicting sources in structured sources: main.rs"
+ "line": "test cases/failing/116 structured_sources conflicts/meson.build:7:0: ERROR: Conflicting sources in structured sources: main.rs"
}
]
}
diff --git a/test cases/failing/118 missing compiler/meson.build b/test cases/failing/117 missing compiler/meson.build
index 19bdd0c..19bdd0c 100644
--- a/test cases/failing/118 missing compiler/meson.build
+++ b/test cases/failing/117 missing compiler/meson.build
diff --git a/test cases/failing/118 missing compiler/subprojects/sub/main.c b/test cases/failing/117 missing compiler/subprojects/sub/main.c
index 44e82e2..44e82e2 100644
--- a/test cases/failing/118 missing compiler/subprojects/sub/main.c
+++ b/test cases/failing/117 missing compiler/subprojects/sub/main.c
diff --git a/test cases/failing/118 missing compiler/subprojects/sub/meson.build b/test cases/failing/117 missing compiler/subprojects/sub/meson.build
index b60850c..b60850c 100644
--- a/test cases/failing/118 missing compiler/subprojects/sub/meson.build
+++ b/test cases/failing/117 missing compiler/subprojects/sub/meson.build
diff --git a/test cases/failing/118 missing compiler/test.json b/test cases/failing/117 missing compiler/test.json
index a2494e4..525e927 100644
--- a/test cases/failing/118 missing compiler/test.json
+++ b/test cases/failing/117 missing compiler/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/118 missing compiler/subprojects/sub/meson.build:4:0: ERROR: No host machine compiler for 'subprojects/sub/main.c'"
+ "line": "test cases/failing/117 missing compiler/subprojects/sub/meson.build:4:0: ERROR: No host machine compiler for 'subprojects/sub/main.c'"
}
]
}
diff --git a/test cases/failing/119 cmake subproject error/meson.build b/test cases/failing/118 cmake subproject error/meson.build
index 9304af7..9304af7 100644
--- a/test cases/failing/119 cmake subproject error/meson.build
+++ b/test cases/failing/118 cmake subproject error/meson.build
diff --git a/test cases/failing/119 cmake subproject error/subprojects/cmlib/CMakeLists.txt b/test cases/failing/118 cmake subproject error/subprojects/cmlib/CMakeLists.txt
index a845525..a845525 100644
--- a/test cases/failing/119 cmake subproject error/subprojects/cmlib/CMakeLists.txt
+++ b/test cases/failing/118 cmake subproject error/subprojects/cmlib/CMakeLists.txt
diff --git a/test cases/failing/119 cmake subproject error/test.json b/test cases/failing/118 cmake subproject error/test.json
index d8271a2..4e47f89 100644
--- a/test cases/failing/119 cmake subproject error/test.json
+++ b/test cases/failing/118 cmake subproject error/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/119 cmake subproject error/meson.build:4:14: ERROR: Failed to configure the CMake subproject: Fancy error message"
+ "line": "test cases/failing/118 cmake subproject error/meson.build:4:14: ERROR: Failed to configure the CMake subproject: Fancy error message"
}
],
"tools": {
diff --git a/test cases/failing/120 pkgconfig not relocatable outside prefix/meson.build b/test cases/failing/119 pkgconfig not relocatable outside prefix/meson.build
index 7ebdfc3..7ebdfc3 100644
--- a/test cases/failing/120 pkgconfig not relocatable outside prefix/meson.build
+++ b/test cases/failing/119 pkgconfig not relocatable outside prefix/meson.build
diff --git a/test cases/failing/120 pkgconfig not relocatable outside prefix/test.json b/test cases/failing/119 pkgconfig not relocatable outside prefix/test.json
index 32c6e09..1bb2494 100644
--- a/test cases/failing/120 pkgconfig not relocatable outside prefix/test.json
+++ b/test cases/failing/119 pkgconfig not relocatable outside prefix/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/120 pkgconfig not relocatable outside prefix/meson\\.build:17:5: ERROR: Pkgconfig prefix cannot be outside of the prefix when pkgconfig\\.relocatable=true. Pkgconfig prefix is (C:)?/opt/lib/pkgconfig.",
+ "line": "test cases/failing/119 pkgconfig not relocatable outside prefix/meson\\.build:17:5: ERROR: Pkgconfig prefix cannot be outside of the prefix when pkgconfig\\.relocatable=true. Pkgconfig prefix is (C:)?/opt/lib/pkgconfig.",
"match": "re"
}
]
diff --git a/test cases/failing/121 subproject sandbox violation/meson.build b/test cases/failing/120 subproject sandbox violation/meson.build
index d41994c..d41994c 100644
--- a/test cases/failing/121 subproject sandbox violation/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/meson_options.txt b/test cases/failing/120 subproject sandbox violation/meson_options.txt
index e7b782d..e7b782d 100644
--- a/test cases/failing/121 subproject sandbox violation/meson_options.txt
+++ b/test cases/failing/120 subproject sandbox violation/meson_options.txt
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/file.txt b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/file.txt
index e69de29..e69de29 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/file.txt
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/file.txt
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/meson.build b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/meson.build
index bd33bf3..bd33bf3 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/nested/meson.build b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/nested/meson.build
index 038c139..038c139 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj1/nested/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj1/nested/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/file.txt b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/file.txt
index e69de29..e69de29 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/file.txt
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/file.txt
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/meson.build b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/meson.build
index a6032aa..a6032aa 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/nested/meson.build b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/nested/meson.build
index e69de29..e69de29 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj2/nested/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj2/nested/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj3/file.txt b/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/file.txt
index e69de29..e69de29 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj3/file.txt
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/file.txt
diff --git a/test cases/failing/121 subproject sandbox violation/subprojects/subproj3/meson.build b/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/meson.build
index c4fa64c..c4fa64c 100644
--- a/test cases/failing/121 subproject sandbox violation/subprojects/subproj3/meson.build
+++ b/test cases/failing/120 subproject sandbox violation/subprojects/subproj3/meson.build
diff --git a/test cases/failing/121 subproject sandbox violation/test.json b/test cases/failing/120 subproject sandbox violation/test.json
index d8c28d7..d506076 100644
--- a/test cases/failing/121 subproject sandbox violation/test.json
+++ b/test cases/failing/120 subproject sandbox violation/test.json
@@ -10,7 +10,7 @@
},
"stdout": [
{
- "line": "test cases/failing/121 subproject sandbox violation/meson.build:24:0: ERROR: Sandbox violation: Tried to grab file file.txt from a nested subproject."
+ "line": "test cases/failing/120 subproject sandbox violation/meson.build:24:0: ERROR: Sandbox violation: Tried to grab file file.txt from a nested subproject."
}
]
}
diff --git a/test cases/failing/122 override and add_project_dependency/inc/lib.h b/test cases/failing/121 override and add_project_dependency/inc/lib.h
index 4cfc47e..4cfc47e 100644
--- a/test cases/failing/122 override and add_project_dependency/inc/lib.h
+++ b/test cases/failing/121 override and add_project_dependency/inc/lib.h
diff --git a/test cases/failing/122 override and add_project_dependency/lib.c b/test cases/failing/121 override and add_project_dependency/lib.c
index b8b22a3..b8b22a3 100644
--- a/test cases/failing/122 override and add_project_dependency/lib.c
+++ b/test cases/failing/121 override and add_project_dependency/lib.c
diff --git a/test cases/failing/122 override and add_project_dependency/meson.build b/test cases/failing/121 override and add_project_dependency/meson.build
index c878b01..c878b01 100644
--- a/test cases/failing/122 override and add_project_dependency/meson.build
+++ b/test cases/failing/121 override and add_project_dependency/meson.build
diff --git a/test cases/failing/122 override and add_project_dependency/subprojects/a/meson.build b/test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build
index 4f6f070..4f6f070 100644
--- a/test cases/failing/122 override and add_project_dependency/subprojects/a/meson.build
+++ b/test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build
diff --git a/test cases/failing/122 override and add_project_dependency/subprojects/a/prog.c b/test cases/failing/121 override and add_project_dependency/subprojects/a/prog.c
index ce60f81..ce60f81 100644
--- a/test cases/failing/122 override and add_project_dependency/subprojects/a/prog.c
+++ b/test cases/failing/121 override and add_project_dependency/subprojects/a/prog.c
diff --git a/test cases/failing/122 override and add_project_dependency/test.json b/test cases/failing/121 override and add_project_dependency/test.json
index 08eb0c6..e762359 100644
--- a/test cases/failing/122 override and add_project_dependency/test.json
+++ b/test cases/failing/121 override and add_project_dependency/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/122 override and add_project_dependency/subprojects/a/meson.build:6:0: ERROR: Dependencies must be external dependencies"
+ "line": "test cases/failing/121 override and add_project_dependency/subprojects/a/meson.build:6:0: ERROR: Dependencies must be external dependencies"
}
]
}
diff --git a/test cases/failing/123 targets before add_project_dependency/inc/lib.h b/test cases/failing/122 targets before add_project_dependency/inc/lib.h
index 4cfc47e..4cfc47e 100644
--- a/test cases/failing/123 targets before add_project_dependency/inc/lib.h
+++ b/test cases/failing/122 targets before add_project_dependency/inc/lib.h
diff --git a/test cases/failing/123 targets before add_project_dependency/lib.c b/test cases/failing/122 targets before add_project_dependency/lib.c
index b8b22a3..b8b22a3 100644
--- a/test cases/failing/123 targets before add_project_dependency/lib.c
+++ b/test cases/failing/122 targets before add_project_dependency/lib.c
diff --git a/test cases/failing/123 targets before add_project_dependency/meson.build b/test cases/failing/122 targets before add_project_dependency/meson.build
index 38fe102..38fe102 100644
--- a/test cases/failing/123 targets before add_project_dependency/meson.build
+++ b/test cases/failing/122 targets before add_project_dependency/meson.build
diff --git a/test cases/failing/123 targets before add_project_dependency/test.json b/test cases/failing/122 targets before add_project_dependency/test.json
index 4a21eec..674467a 100644
--- a/test cases/failing/123 targets before add_project_dependency/test.json
+++ b/test cases/failing/122 targets before add_project_dependency/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/123 targets before add_project_dependency/meson.build:5:0: ERROR: Tried to use 'add_project_dependencies' after a build target has been declared."
+ "line": "test cases/failing/122 targets before add_project_dependency/meson.build:5:0: ERROR: Tried to use 'add_project_dependencies' after a build target has been declared."
}
]
}
diff --git a/test cases/failing/124 extract from unity/meson.build b/test cases/failing/123 extract from unity/meson.build
index 9e3e65f..9e3e65f 100644
--- a/test cases/failing/124 extract from unity/meson.build
+++ b/test cases/failing/123 extract from unity/meson.build
diff --git a/test cases/failing/124 extract from unity/src1.c b/test cases/failing/123 extract from unity/src1.c
index da971bb..da971bb 100644
--- a/test cases/failing/124 extract from unity/src1.c
+++ b/test cases/failing/123 extract from unity/src1.c
diff --git a/test cases/failing/124 extract from unity/src2.c b/test cases/failing/123 extract from unity/src2.c
index a461669..a461669 100644
--- a/test cases/failing/124 extract from unity/src2.c
+++ b/test cases/failing/123 extract from unity/src2.c
diff --git a/test cases/failing/124 extract from unity/test.json b/test cases/failing/123 extract from unity/test.json
index ef76d2f..ce9f105 100644
--- a/test cases/failing/124 extract from unity/test.json
+++ b/test cases/failing/123 extract from unity/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/124 extract from unity/meson.build:4:37: ERROR: Single object files cannot be extracted in Unity builds. You can only extract all the object files for each compiler at once."
+ "line": "test cases/failing/123 extract from unity/meson.build:4:37: ERROR: Single object files cannot be extracted in Unity builds. You can only extract all the object files for each compiler at once."
}
]
}
diff --git a/test cases/failing/125 subproject object as a dependency/main.c b/test cases/failing/124 subproject object as a dependency/main.c
index 78f2de1..78f2de1 100644
--- a/test cases/failing/125 subproject object as a dependency/main.c
+++ b/test cases/failing/124 subproject object as a dependency/main.c
diff --git a/test cases/failing/125 subproject object as a dependency/meson.build b/test cases/failing/124 subproject object as a dependency/meson.build
index 0114b9a..0114b9a 100644
--- a/test cases/failing/125 subproject object as a dependency/meson.build
+++ b/test cases/failing/124 subproject object as a dependency/meson.build
diff --git a/test cases/failing/125 subproject object as a dependency/subprojects/sub/meson.build b/test cases/failing/124 subproject object as a dependency/subprojects/sub/meson.build
index 0adfd6a..0adfd6a 100644
--- a/test cases/failing/125 subproject object as a dependency/subprojects/sub/meson.build
+++ b/test cases/failing/124 subproject object as a dependency/subprojects/sub/meson.build
diff --git a/test cases/failing/125 subproject object as a dependency/test.json b/test cases/failing/124 subproject object as a dependency/test.json
index 0cb2571..a0eea22 100644
--- a/test cases/failing/125 subproject object as a dependency/test.json
+++ b/test cases/failing/124 subproject object as a dependency/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/125 subproject object as a dependency/meson.build:3:0: ERROR: Tried to use subproject object as a dependency."
+ "line": "test cases/failing/124 subproject object as a dependency/meson.build:3:0: ERROR: Tried to use subproject object as a dependency."
}
]
}
diff --git a/test cases/failing/126 generator host binary/exe.c b/test cases/failing/125 generator host binary/exe.c
index 78f2de1..78f2de1 100644
--- a/test cases/failing/126 generator host binary/exe.c
+++ b/test cases/failing/125 generator host binary/exe.c
diff --git a/test cases/failing/126 generator host binary/lib.in b/test cases/failing/125 generator host binary/lib.in
index d0b6ab7..d0b6ab7 100644
--- a/test cases/failing/126 generator host binary/lib.in
+++ b/test cases/failing/125 generator host binary/lib.in
diff --git a/test cases/failing/126 generator host binary/meson.build b/test cases/failing/125 generator host binary/meson.build
index e769338..e769338 100644
--- a/test cases/failing/126 generator host binary/meson.build
+++ b/test cases/failing/125 generator host binary/meson.build
diff --git a/test cases/failing/126 generator host binary/test.json b/test cases/failing/125 generator host binary/test.json
index c633622..c633622 100644
--- a/test cases/failing/126 generator host binary/test.json
+++ b/test cases/failing/125 generator host binary/test.json
diff --git a/test cases/failing/127 invalid ast/meson.build b/test cases/failing/126 invalid ast/meson.build
index 06011c2..06011c2 100644
--- a/test cases/failing/127 invalid ast/meson.build
+++ b/test cases/failing/126 invalid ast/meson.build
diff --git a/test cases/failing/127 invalid ast/test.json b/test cases/failing/126 invalid ast/test.json
index db7bfe8..7dc0684 100644
--- a/test cases/failing/127 invalid ast/test.json
+++ b/test cases/failing/126 invalid ast/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/127 invalid ast/meson.build:1:44: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0"
+ "line": "test cases/failing/126 invalid ast/meson.build:1:44: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0"
}
]
}
diff --git a/test cases/failing/128 invalid project function/meson.build b/test cases/failing/127 invalid project function/meson.build
index 0032c9c..0032c9c 100644
--- a/test cases/failing/128 invalid project function/meson.build
+++ b/test cases/failing/127 invalid project function/meson.build
diff --git a/test cases/failing/128 invalid project function/test.json b/test cases/failing/127 invalid project function/test.json
index 17c1ac6..bf9cc16 100644
--- a/test cases/failing/128 invalid project function/test.json
+++ b/test cases/failing/127 invalid project function/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/128 invalid project function/meson.build:1:67: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0"
+ "line": "test cases/failing/127 invalid project function/meson.build:1:67: ERROR: Meson version is [0-9.]+(\\.rc[0-9]+)? but project requires 0.1.0"
}
]
}
diff --git a/test cases/failing/129 utf8 with bom/meson.build b/test cases/failing/128 utf8 with bom/meson.build
index 492a0c6..492a0c6 100644
--- a/test cases/failing/129 utf8 with bom/meson.build
+++ b/test cases/failing/128 utf8 with bom/meson.build
diff --git a/test cases/failing/129 utf8 with bom/test.json b/test cases/failing/128 utf8 with bom/test.json
index ec2df13..8b12ba2 100644
--- a/test cases/failing/129 utf8 with bom/test.json
+++ b/test cases/failing/128 utf8 with bom/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/129 utf8 with bom/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
+ "line": "test cases/failing/128 utf8 with bom/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
}
]
}
diff --git a/test cases/failing/130 utf8 with bom subdir/meson.build b/test cases/failing/129 utf8 with bom subdir/meson.build
index 8d3bbd7..8d3bbd7 100644
--- a/test cases/failing/130 utf8 with bom subdir/meson.build
+++ b/test cases/failing/129 utf8 with bom subdir/meson.build
diff --git a/test cases/failing/130 utf8 with bom subdir/subdir/meson.build b/test cases/failing/129 utf8 with bom subdir/subdir/meson.build
index dbf2b9c..dbf2b9c 100644
--- a/test cases/failing/130 utf8 with bom subdir/subdir/meson.build
+++ b/test cases/failing/129 utf8 with bom subdir/subdir/meson.build
diff --git a/test cases/failing/130 utf8 with bom subdir/test.json b/test cases/failing/129 utf8 with bom subdir/test.json
index bbaea1b..66a97d7 100644
--- a/test cases/failing/130 utf8 with bom subdir/test.json
+++ b/test cases/failing/129 utf8 with bom subdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/130 utf8 with bom subdir/subdir/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
+ "line": "test cases/failing/129 utf8 with bom subdir/subdir/meson.build:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
}
]
}
diff --git a/test cases/failing/131 utf8 with bom options/meson.build b/test cases/failing/130 utf8 with bom options/meson.build
index 50413e0..50413e0 100644
--- a/test cases/failing/131 utf8 with bom options/meson.build
+++ b/test cases/failing/130 utf8 with bom options/meson.build
diff --git a/test cases/failing/131 utf8 with bom options/meson.options b/test cases/failing/130 utf8 with bom options/meson.options
index 250c032..250c032 100644
--- a/test cases/failing/131 utf8 with bom options/meson.options
+++ b/test cases/failing/130 utf8 with bom options/meson.options
diff --git a/test cases/failing/131 utf8 with bom options/test.json b/test cases/failing/130 utf8 with bom options/test.json
index e6391c5..f11a78f 100644
--- a/test cases/failing/131 utf8 with bom options/test.json
+++ b/test cases/failing/130 utf8 with bom options/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/131 utf8 with bom options/meson.options:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
+ "line": "test cases/failing/130 utf8 with bom options/meson.options:0:0: ERROR: Builder file must be encoded in UTF-8 (with no BOM)"
}
]
}
diff --git a/test cases/failing/132 module use inside project decl/meson.build b/test cases/failing/131 module use inside project decl/meson.build
index 8f82a5d..8f82a5d 100644
--- a/test cases/failing/132 module use inside project decl/meson.build
+++ b/test cases/failing/131 module use inside project decl/meson.build
diff --git a/test cases/failing/132 module use inside project decl/test.json b/test cases/failing/131 module use inside project decl/test.json
index 33e377b..4fef501 100644
--- a/test cases/failing/132 module use inside project decl/test.json
+++ b/test cases/failing/131 module use inside project decl/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/132 module use inside project decl/meson.build:4:21: ERROR: Module methods (python.find_installation) cannot be invoked during project declaration."
+ "line": "test cases/failing/131 module use inside project decl/meson.build:4:21: ERROR: Module methods (python.find_installation) cannot be invoked during project declaration."
}
]
}
diff --git a/test cases/failing/133 dub missing dependency/dub.json b/test cases/failing/132 dub missing dependency/dub.json
index 1ad9ddc..1ad9ddc 100644
--- a/test cases/failing/133 dub missing dependency/dub.json
+++ b/test cases/failing/132 dub missing dependency/dub.json
diff --git a/test cases/failing/133 dub missing dependency/dub.selections.json b/test cases/failing/132 dub missing dependency/dub.selections.json
index 322586b..322586b 100644
--- a/test cases/failing/133 dub missing dependency/dub.selections.json
+++ b/test cases/failing/132 dub missing dependency/dub.selections.json
diff --git a/test cases/failing/133 dub missing dependency/meson.build b/test cases/failing/132 dub missing dependency/meson.build
index fcccb3b..fcccb3b 100644
--- a/test cases/failing/133 dub missing dependency/meson.build
+++ b/test cases/failing/132 dub missing dependency/meson.build
diff --git a/test cases/failing/133 dub missing dependency/source/app.d b/test cases/failing/132 dub missing dependency/source/app.d
index d66321b..d66321b 100644
--- a/test cases/failing/133 dub missing dependency/source/app.d
+++ b/test cases/failing/132 dub missing dependency/source/app.d
diff --git a/test cases/failing/133 dub missing dependency/test.json b/test cases/failing/132 dub missing dependency/test.json
index e58dbc7..e58dbc7 100644
--- a/test cases/failing/133 dub missing dependency/test.json
+++ b/test cases/failing/132 dub missing dependency/test.json
diff --git a/test cases/failing/134 java sources in non jar target/Test.java b/test cases/failing/133 java sources in non jar target/Test.java
index e69de29..e69de29 100644
--- a/test cases/failing/134 java sources in non jar target/Test.java
+++ b/test cases/failing/133 java sources in non jar target/Test.java
diff --git a/test cases/failing/134 java sources in non jar target/meson.build b/test cases/failing/133 java sources in non jar target/meson.build
index 0aa802a..0aa802a 100644
--- a/test cases/failing/134 java sources in non jar target/meson.build
+++ b/test cases/failing/133 java sources in non jar target/meson.build
diff --git a/test cases/failing/134 java sources in non jar target/test.json b/test cases/failing/133 java sources in non jar target/test.json
index 271ac36..877ea16 100644
--- a/test cases/failing/134 java sources in non jar target/test.json
+++ b/test cases/failing/133 java sources in non jar target/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/134 java sources in non jar target/meson.build:3:0: ERROR: Build target of type \"executable\" cannot build java source: \"Test.java\". Use \"jar\" instead."
+ "line": "test cases/failing/133 java sources in non jar target/meson.build:3:0: ERROR: Build target of type \"executable\" cannot build java source: \"Test.java\". Use \"jar\" instead."
}
]
}
diff --git a/test cases/failing/134 rust link_language/f.rs b/test cases/failing/134 rust link_language/f.rs
new file mode 100644
index 0000000..da0f5d9
--- /dev/null
+++ b/test cases/failing/134 rust link_language/f.rs
@@ -0,0 +1 @@
+pub fn main() {}
diff --git a/test cases/failing/134 rust link_language/meson.build b/test cases/failing/134 rust link_language/meson.build
new file mode 100644
index 0000000..7fd223d
--- /dev/null
+++ b/test cases/failing/134 rust link_language/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2021 Intel Corporation
+
+project('rust wrong link language', 'c')
+
+if not add_languages('rust', required: false)
+ error('MESON_SKIP_TEST test requires rust compiler')
+endif
+
+executable('f', 'f.rs', link_language: 'c')
diff --git a/test cases/failing/134 rust link_language/test.json b/test cases/failing/134 rust link_language/test.json
new file mode 100644
index 0000000..e144149
--- /dev/null
+++ b/test cases/failing/134 rust link_language/test.json
@@ -0,0 +1,8 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/134 rust link_language/meson.build:10:0: ERROR: cannot build Rust sources with a different link_language"
+ }
+ ]
+}
+
diff --git a/test cases/failing/135 invalid build_subdir/existing-dir/config.h.in b/test cases/failing/135 invalid build_subdir/existing-dir/config.h.in
new file mode 100644
index 0000000..14a1558
--- /dev/null
+++ b/test cases/failing/135 invalid build_subdir/existing-dir/config.h.in
@@ -0,0 +1,5 @@
+#define MESSAGE "@var@"
+#define OTHER "@other@" "@second@" "@empty@"
+
+#mesondefine BE_TRUE
+#mesondefine SHOULD_BE_UNDEF
diff --git a/test cases/failing/135 invalid build_subdir/meson.build b/test cases/failing/135 invalid build_subdir/meson.build
new file mode 100644
index 0000000..b54ec9a
--- /dev/null
+++ b/test cases/failing/135 invalid build_subdir/meson.build
@@ -0,0 +1,24 @@
+project('invalid build_subdir test', 'c', meson_version : '>= 1.9.1')
+
+# This setup intentially tries to use a build_subdir
+# with a name matching one in the source directory.
+# produce a Ninja targets with the same name. It only works on
+# unix, because on Windows the target has a '.exe' suffix.
+#
+# This test might fail to work on different backends or when
+# output location is redirected.
+
+conf = configuration_data()
+
+conf.set('var', 'mystring')
+conf.set('other', 'string 2')
+conf.set('second', ' bonus')
+conf.set('BE_TRUE', true)
+
+configure_file(input : files('existing-dir/config.h.in'),
+ output : 'config.h',
+ build_subdir : 'existing-dir',
+ install_dir : 'share/appdir/existing-dir',
+ configuration : conf)
+
+run_target('build_dir', command: ['echo', 'clash 1'])
diff --git a/test cases/failing/135 invalid build_subdir/test.json b/test cases/failing/135 invalid build_subdir/test.json
new file mode 100644
index 0000000..aecc4e9
--- /dev/null
+++ b/test cases/failing/135 invalid build_subdir/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/135 invalid build_subdir/meson.build:18:0: ERROR: Build subdir \"existing-dir\" in \"config.h\" exists in source tree."
+ }
+ ]
+}
diff --git a/test cases/failing/16 extract from subproject/main.c b/test cases/failing/16 extract from subproject/main.c
deleted file mode 100644
index 6c8ecae..0000000
--- a/test cases/failing/16 extract from subproject/main.c
+++ /dev/null
@@ -1,5 +0,0 @@
-int sub_lib_method(void);
-
-int main(void) {
- return 1337 - sub_lib_method();
-}
diff --git a/test cases/failing/16 extract from subproject/meson.build b/test cases/failing/16 extract from subproject/meson.build
deleted file mode 100644
index 286aaa1..0000000
--- a/test cases/failing/16 extract from subproject/meson.build
+++ /dev/null
@@ -1,9 +0,0 @@
-project('extract subproject object', 'c')
-
-sub = subproject('sub_project')
-lib = sub.get_variable('lib')
-
-exe = executable('exe', 'main.c',
- objects : lib.extract_objects('sub_lib.c'))
-
-test('extraction test', exe)
diff --git a/test cases/failing/16 extract from subproject/subprojects/sub_project/meson.build b/test cases/failing/16 extract from subproject/subprojects/sub_project/meson.build
deleted file mode 100644
index 0810df5..0000000
--- a/test cases/failing/16 extract from subproject/subprojects/sub_project/meson.build
+++ /dev/null
@@ -1,3 +0,0 @@
-project('extract subproject object -- subproject', 'c')
-
-lib = library('sub_lib', 'sub_lib.c')
diff --git a/test cases/failing/16 extract from subproject/subprojects/sub_project/sub_lib.c b/test cases/failing/16 extract from subproject/subprojects/sub_project/sub_lib.c
deleted file mode 100644
index be3c9aa..0000000
--- a/test cases/failing/16 extract from subproject/subprojects/sub_project/sub_lib.c
+++ /dev/null
@@ -1,3 +0,0 @@
-int sub_lib_method() {
- return 1337;
-}
diff --git a/test cases/failing/16 extract from subproject/test.json b/test cases/failing/16 extract from subproject/test.json
deleted file mode 100644
index 1616033..0000000
--- a/test cases/failing/16 extract from subproject/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/16 extract from subproject/meson.build:7:32: ERROR: Tried to extract objects from a different subproject."
- }
- ]
-}
diff --git a/test cases/failing/17 same target/file.c b/test cases/failing/16 same target/file.c
index 7412372..7412372 100644
--- a/test cases/failing/17 same target/file.c
+++ b/test cases/failing/16 same target/file.c
diff --git a/test cases/failing/17 same target/meson.build b/test cases/failing/16 same target/meson.build
index ee586d0..ee586d0 100644
--- a/test cases/failing/17 same target/meson.build
+++ b/test cases/failing/16 same target/meson.build
diff --git a/test cases/failing/17 same target/test.json b/test cases/failing/16 same target/test.json
index 0005ba4..ec154b1 100644
--- a/test cases/failing/17 same target/test.json
+++ b/test cases/failing/16 same target/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/17 same target/meson.build:4:0: ERROR: Tried to create target \"foo\", but a target of that name already exists."
+ "line": "test cases/failing/16 same target/meson.build:4:0: ERROR: Tried to create target \"foo\", but a target of that name already exists."
}
]
}
diff --git a/test cases/failing/18 wrong plusassign/meson.build b/test cases/failing/17 wrong plusassign/meson.build
index af7727a..af7727a 100644
--- a/test cases/failing/18 wrong plusassign/meson.build
+++ b/test cases/failing/17 wrong plusassign/meson.build
diff --git a/test cases/failing/18 wrong plusassign/test.json b/test cases/failing/17 wrong plusassign/test.json
index c698f85..a613d7b 100644
--- a/test cases/failing/18 wrong plusassign/test.json
+++ b/test cases/failing/17 wrong plusassign/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/18 wrong plusassign/meson.build:3:0: ERROR: Plusassignment target must be an id."
+ "line": "test cases/failing/17 wrong plusassign/meson.build:3:0: ERROR: Plusassignment target must be an id."
}
]
}
diff --git a/test cases/failing/19 target clash/clash.c b/test cases/failing/18 target clash/clash.c
index 2daa06c..2daa06c 100644
--- a/test cases/failing/19 target clash/clash.c
+++ b/test cases/failing/18 target clash/clash.c
diff --git a/test cases/failing/19 target clash/meson.build b/test cases/failing/18 target clash/meson.build
index 4fd0934..4fd0934 100644
--- a/test cases/failing/19 target clash/meson.build
+++ b/test cases/failing/18 target clash/meson.build
diff --git a/test cases/failing/19 target clash/test.json b/test cases/failing/18 target clash/test.json
index d22b894..d22b894 100644
--- a/test cases/failing/19 target clash/test.json
+++ b/test cases/failing/18 target clash/test.json
diff --git a/test cases/failing/20 version/meson.build b/test cases/failing/19 version/meson.build
index d25a395..d25a395 100644
--- a/test cases/failing/20 version/meson.build
+++ b/test cases/failing/19 version/meson.build
diff --git a/test cases/failing/20 version/test.json b/test cases/failing/19 version/test.json
index 565fbf2..f78e34c 100644
--- a/test cases/failing/20 version/test.json
+++ b/test cases/failing/19 version/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/20 version/meson\\.build:1:44: ERROR: Meson version is .* but project requires >100\\.0\\.0"
+ "line": "test cases/failing/19 version/meson\\.build:1:44: ERROR: Meson version is .* but project requires >100\\.0\\.0"
}
]
}
diff --git a/test cases/failing/21 subver/meson.build b/test cases/failing/20 subver/meson.build
index ea0f914..ea0f914 100644
--- a/test cases/failing/21 subver/meson.build
+++ b/test cases/failing/20 subver/meson.build
diff --git a/test cases/failing/21 subver/subprojects/foo/meson.build b/test cases/failing/20 subver/subprojects/foo/meson.build
index 615ee92..615ee92 100644
--- a/test cases/failing/21 subver/subprojects/foo/meson.build
+++ b/test cases/failing/20 subver/subprojects/foo/meson.build
diff --git a/test cases/failing/21 subver/test.json b/test cases/failing/20 subver/test.json
index 694b777..86f4e5b 100644
--- a/test cases/failing/21 subver/test.json
+++ b/test cases/failing/20 subver/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/21 subver/meson.build:3:4: ERROR: Subproject foo version is 1.0.0 but ['>1.0.0'] required."
+ "line": "test cases/failing/20 subver/meson.build:3:4: ERROR: Subproject foo version is 1.0.0 but ['>1.0.0'] required."
}
]
}
diff --git a/test cases/failing/22 assert/meson.build b/test cases/failing/21 assert/meson.build
index 45cff07..45cff07 100644
--- a/test cases/failing/22 assert/meson.build
+++ b/test cases/failing/21 assert/meson.build
diff --git a/test cases/failing/22 assert/test.json b/test cases/failing/21 assert/test.json
index edae999..8f80c2d 100644
--- a/test cases/failing/22 assert/test.json
+++ b/test cases/failing/21 assert/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/22 assert/meson.build:3:0: ERROR: Assert failed: I am fail."
+ "line": "test cases/failing/21 assert/meson.build:3:0: ERROR: Assert failed: I am fail."
}
]
}
diff --git a/test cases/failing/23 rel testdir/meson.build b/test cases/failing/22 rel testdir/meson.build
index c10558b..c10558b 100644
--- a/test cases/failing/23 rel testdir/meson.build
+++ b/test cases/failing/22 rel testdir/meson.build
diff --git a/test cases/failing/23 rel testdir/simple.c b/test cases/failing/22 rel testdir/simple.c
index 11b7fad..11b7fad 100644
--- a/test cases/failing/23 rel testdir/simple.c
+++ b/test cases/failing/22 rel testdir/simple.c
diff --git a/test cases/failing/23 rel testdir/test.json b/test cases/failing/22 rel testdir/test.json
index bc80bc6..c16acf9 100644
--- a/test cases/failing/23 rel testdir/test.json
+++ b/test cases/failing/22 rel testdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/23 rel testdir/meson.build:4:0: ERROR: test keyword argument \"workdir\" must be an absolute path"
+ "line": "test cases/failing/22 rel testdir/meson.build:4:0: ERROR: test keyword argument \"workdir\" must be an absolute path"
}
]
}
diff --git a/test cases/failing/24 int conversion/meson.build b/test cases/failing/23 int conversion/meson.build
index 8db72f3..8db72f3 100644
--- a/test cases/failing/24 int conversion/meson.build
+++ b/test cases/failing/23 int conversion/meson.build
diff --git a/test cases/failing/24 int conversion/test.json b/test cases/failing/23 int conversion/test.json
index e749928..df3fdd5 100644
--- a/test cases/failing/24 int conversion/test.json
+++ b/test cases/failing/23 int conversion/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/24 int conversion/meson.build:3:13: ERROR: String 'notanumber' cannot be converted to int"
+ "line": "test cases/failing/23 int conversion/meson.build:3:13: ERROR: String 'notanumber' cannot be converted to int"
}
]
}
diff --git a/test cases/failing/25 badlang/meson.build b/test cases/failing/24 badlang/meson.build
index a31059b..a31059b 100644
--- a/test cases/failing/25 badlang/meson.build
+++ b/test cases/failing/24 badlang/meson.build
diff --git a/test cases/failing/25 badlang/test.json b/test cases/failing/24 badlang/test.json
index 0b23fd7..bf387c2 100644
--- a/test cases/failing/25 badlang/test.json
+++ b/test cases/failing/24 badlang/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/25 badlang/meson.build:3:0: ERROR: Tried to use unknown language \"nonexisting\"."
+ "line": "test cases/failing/24 badlang/meson.build:3:0: ERROR: Tried to use unknown language \"nonexisting\"."
}
]
}
diff --git a/test cases/failing/26 output subdir/foo.in b/test cases/failing/25 output subdir/foo.in
index 3d1bf19..3d1bf19 100644
--- a/test cases/failing/26 output subdir/foo.in
+++ b/test cases/failing/25 output subdir/foo.in
diff --git a/test cases/failing/26 output subdir/meson.build b/test cases/failing/25 output subdir/meson.build
index c4bd806..c4bd806 100644
--- a/test cases/failing/26 output subdir/meson.build
+++ b/test cases/failing/25 output subdir/meson.build
diff --git a/test cases/failing/26 output subdir/subdir/dummy.txt b/test cases/failing/25 output subdir/subdir/dummy.txt
index 7e907f1..7e907f1 100644
--- a/test cases/failing/26 output subdir/subdir/dummy.txt
+++ b/test cases/failing/25 output subdir/subdir/dummy.txt
diff --git a/test cases/failing/26 output subdir/test.json b/test cases/failing/25 output subdir/test.json
index df09726..63e0e94 100644
--- a/test cases/failing/26 output subdir/test.json
+++ b/test cases/failing/25 output subdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/26 output subdir/meson.build:3:0: ERROR: configure_file keyword argument \"output\" Output 'subdir/foo' must not contain a path segment."
+ "line": "test cases/failing/25 output subdir/meson.build:3:0: ERROR: configure_file keyword argument \"output\" Output 'subdir/foo' must not contain a path segment."
}
]
}
diff --git a/test cases/failing/27 noprog use/meson.build b/test cases/failing/26 noprog use/meson.build
index af31ece..af31ece 100644
--- a/test cases/failing/27 noprog use/meson.build
+++ b/test cases/failing/26 noprog use/meson.build
diff --git a/test cases/failing/27 noprog use/test.json b/test cases/failing/26 noprog use/test.json
index b84562e..9c6cec4 100644
--- a/test cases/failing/27 noprog use/test.json
+++ b/test cases/failing/26 noprog use/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/27 noprog use/meson.build:5:0: ERROR: Tried to use not-found external program in \"command\""
+ "line": "test cases/failing/26 noprog use/meson.build:5:0: ERROR: Tried to use not-found external program in \"command\""
}
]
}
diff --git a/test cases/failing/28 no crossprop/meson.build b/test cases/failing/27 no crossprop/meson.build
index 9152077..9152077 100644
--- a/test cases/failing/28 no crossprop/meson.build
+++ b/test cases/failing/27 no crossprop/meson.build
diff --git a/test cases/failing/28 no crossprop/test.json b/test cases/failing/27 no crossprop/test.json
index 78be0b7..1182c4f 100644
--- a/test cases/failing/28 no crossprop/test.json
+++ b/test cases/failing/27 no crossprop/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/28 no crossprop/meson.build:3:14: ERROR: Unknown property for host machine: nonexisting"
+ "line": "test cases/failing/27 no crossprop/meson.build:3:14: ERROR: Unknown property for host machine: nonexisting"
}
]
}
diff --git a/test cases/failing/29 nested ternary/meson.build b/test cases/failing/28 nested ternary/meson.build
index 76364ce..76364ce 100644
--- a/test cases/failing/29 nested ternary/meson.build
+++ b/test cases/failing/28 nested ternary/meson.build
diff --git a/test cases/failing/29 nested ternary/test.json b/test cases/failing/28 nested ternary/test.json
index ba05013..5efeca0 100644
--- a/test cases/failing/29 nested ternary/test.json
+++ b/test cases/failing/28 nested ternary/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/29 nested ternary/meson.build:3:12: ERROR: Nested ternary operators are not allowed."
+ "line": "test cases/failing/28 nested ternary/meson.build:3:12: ERROR: Nested ternary operators are not allowed."
}
]
}
diff --git a/test cases/failing/30 invalid man extension/foo.a1 b/test cases/failing/29 invalid man extension/foo.a1
index e69de29..e69de29 100644
--- a/test cases/failing/30 invalid man extension/foo.a1
+++ b/test cases/failing/29 invalid man extension/foo.a1
diff --git a/test cases/failing/30 invalid man extension/meson.build b/test cases/failing/29 invalid man extension/meson.build
index b3694d5..b3694d5 100644
--- a/test cases/failing/30 invalid man extension/meson.build
+++ b/test cases/failing/29 invalid man extension/meson.build
diff --git a/test cases/failing/30 invalid man extension/test.json b/test cases/failing/29 invalid man extension/test.json
index 6ab6843..2ff03f1 100644
--- a/test cases/failing/30 invalid man extension/test.json
+++ b/test cases/failing/29 invalid man extension/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/30 invalid man extension/meson.build:2:5: ERROR: Man file must have a file extension of a number between 1 and 9"
+ "line": "test cases/failing/29 invalid man extension/meson.build:2:5: ERROR: Man file must have a file extension of a number between 1 and 9"
}
]
}
diff --git a/test cases/failing/31 no man extension/foo b/test cases/failing/30 no man extension/foo
index e69de29..e69de29 100644
--- a/test cases/failing/31 no man extension/foo
+++ b/test cases/failing/30 no man extension/foo
diff --git a/test cases/failing/31 no man extension/meson.build b/test cases/failing/30 no man extension/meson.build
index 33fdd3e..33fdd3e 100644
--- a/test cases/failing/31 no man extension/meson.build
+++ b/test cases/failing/30 no man extension/meson.build
diff --git a/test cases/failing/31 no man extension/test.json b/test cases/failing/30 no man extension/test.json
index 3082f48..8239f01 100644
--- a/test cases/failing/31 no man extension/test.json
+++ b/test cases/failing/30 no man extension/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/31 no man extension/meson.build:2:5: ERROR: Man file must have a file extension of a number between 1 and 9"
+ "line": "test cases/failing/30 no man extension/meson.build:2:5: ERROR: Man file must have a file extension of a number between 1 and 9"
}
]
}
diff --git a/test cases/failing/32 exe static shared/meson.build b/test cases/failing/31 exe static shared/meson.build
index 2ae5125..2ae5125 100644
--- a/test cases/failing/32 exe static shared/meson.build
+++ b/test cases/failing/31 exe static shared/meson.build
diff --git a/test cases/failing/32 exe static shared/prog.c b/test cases/failing/31 exe static shared/prog.c
index 26603b6..26603b6 100644
--- a/test cases/failing/32 exe static shared/prog.c
+++ b/test cases/failing/31 exe static shared/prog.c
diff --git a/test cases/failing/32 exe static shared/shlib2.c b/test cases/failing/31 exe static shared/shlib2.c
index 5b68843..5b68843 100644
--- a/test cases/failing/32 exe static shared/shlib2.c
+++ b/test cases/failing/31 exe static shared/shlib2.c
diff --git a/test cases/failing/32 exe static shared/stat.c b/test cases/failing/31 exe static shared/stat.c
index 56ec66c..56ec66c 100644
--- a/test cases/failing/32 exe static shared/stat.c
+++ b/test cases/failing/31 exe static shared/stat.c
diff --git a/test cases/failing/32 exe static shared/test.json b/test cases/failing/31 exe static shared/test.json
index 0b859e3..cb9efa6 100644
--- a/test cases/failing/32 exe static shared/test.json
+++ b/test cases/failing/31 exe static shared/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/32 exe static shared/meson.build:9:9: ERROR: Can't link non-PIC static library 'stat' into shared library 'shr2'. Use the 'pic' option to static_library to build with PIC."
+ "line": "test cases/failing/31 exe static shared/meson.build:9:9: ERROR: Can't link non-PIC static library 'stat' into shared library 'shr2'. Use the 'pic' option to static_library to build with PIC."
}
]
}
diff --git a/test cases/failing/33 non-root subproject/meson.build b/test cases/failing/32 non-root subproject/meson.build
index 65b1d23..65b1d23 100644
--- a/test cases/failing/33 non-root subproject/meson.build
+++ b/test cases/failing/32 non-root subproject/meson.build
diff --git a/test cases/failing/33 non-root subproject/some/meson.build b/test cases/failing/32 non-root subproject/some/meson.build
index d82f451..d82f451 100644
--- a/test cases/failing/33 non-root subproject/some/meson.build
+++ b/test cases/failing/32 non-root subproject/some/meson.build
diff --git a/test cases/failing/32 non-root subproject/test.json b/test cases/failing/32 non-root subproject/test.json
new file mode 100644
index 0000000..6f6d4ee
--- /dev/null
+++ b/test cases/failing/32 non-root subproject/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/32 non-root subproject/some/meson.build:1:0: ERROR: Attempted to resolve subproject without subprojects directory present."
+ }
+ ]
+}
diff --git a/test cases/failing/34 dependency not-required then required/meson.build b/test cases/failing/33 dependency not-required then required/meson.build
index 54f5a58..54f5a58 100644
--- a/test cases/failing/34 dependency not-required then required/meson.build
+++ b/test cases/failing/33 dependency not-required then required/meson.build
diff --git a/test cases/failing/34 dependency not-required then required/test.json b/test cases/failing/33 dependency not-required then required/test.json
index 7dd8519..7dd8519 100644
--- a/test cases/failing/34 dependency not-required then required/test.json
+++ b/test cases/failing/33 dependency not-required then required/test.json
diff --git a/test cases/failing/33 non-root subproject/test.json b/test cases/failing/33 non-root subproject/test.json
deleted file mode 100644
index 52baf6a..0000000
--- a/test cases/failing/33 non-root subproject/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/33 non-root subproject/some/meson.build:1:0: ERROR: Neither a subproject directory nor a someproj.wrap file was found."
- }
- ]
-}
diff --git a/test cases/failing/35 project argument after target/exe.c b/test cases/failing/34 project argument after target/exe.c
index 11b7fad..11b7fad 100644
--- a/test cases/failing/35 project argument after target/exe.c
+++ b/test cases/failing/34 project argument after target/exe.c
diff --git a/test cases/failing/35 project argument after target/meson.build b/test cases/failing/34 project argument after target/meson.build
index 5402c67..5402c67 100644
--- a/test cases/failing/35 project argument after target/meson.build
+++ b/test cases/failing/34 project argument after target/meson.build
diff --git a/test cases/failing/35 project argument after target/test.json b/test cases/failing/34 project argument after target/test.json
index f5efd9b..7a41b7d 100644
--- a/test cases/failing/35 project argument after target/test.json
+++ b/test cases/failing/34 project argument after target/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/35 project argument after target/meson.build:7:0: ERROR: Tried to use 'add_project_arguments' after a build target has been declared."
+ "line": "test cases/failing/34 project argument after target/meson.build:7:0: ERROR: Tried to use 'add_project_arguments' after a build target has been declared."
}
]
}
diff --git a/test cases/failing/36 pkgconfig dependency impossible conditions/meson.build b/test cases/failing/35 pkgconfig dependency impossible conditions/meson.build
index 874b581..874b581 100644
--- a/test cases/failing/36 pkgconfig dependency impossible conditions/meson.build
+++ b/test cases/failing/35 pkgconfig dependency impossible conditions/meson.build
diff --git a/test cases/failing/36 pkgconfig dependency impossible conditions/test.json b/test cases/failing/35 pkgconfig dependency impossible conditions/test.json
index 6deddbe..74b9731 100644
--- a/test cases/failing/36 pkgconfig dependency impossible conditions/test.json
+++ b/test cases/failing/35 pkgconfig dependency impossible conditions/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/36 pkgconfig dependency impossible conditions/meson.build:7:0: ERROR: Dependency 'zlib' is required but not found."
+ "line": "test cases/failing/35 pkgconfig dependency impossible conditions/meson.build:7:0: ERROR: Dependency 'zlib' is required but not found."
}
]
}
diff --git a/test cases/failing/37 has function external dependency/meson.build b/test cases/failing/36 has function external dependency/meson.build
index 45a3bc2..45a3bc2 100644
--- a/test cases/failing/37 has function external dependency/meson.build
+++ b/test cases/failing/36 has function external dependency/meson.build
diff --git a/test cases/failing/37 has function external dependency/mylib.c b/test cases/failing/36 has function external dependency/mylib.c
index d9fbd34..d9fbd34 100644
--- a/test cases/failing/37 has function external dependency/mylib.c
+++ b/test cases/failing/36 has function external dependency/mylib.c
diff --git a/test cases/failing/37 has function external dependency/test.json b/test cases/failing/36 has function external dependency/test.json
index 81d6f91..705cf45 100644
--- a/test cases/failing/37 has function external dependency/test.json
+++ b/test cases/failing/36 has function external dependency/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/37 has function external dependency/meson.build:8:3: ERROR: Dependencies must be external dependencies"
+ "line": "test cases/failing/36 has function external dependency/meson.build:8:3: ERROR: Dependencies must be external dependencies"
}
]
}
diff --git a/test cases/failing/38 prefix absolute/meson.build b/test cases/failing/37 prefix absolute/meson.build
index 6d0114b..6d0114b 100644
--- a/test cases/failing/38 prefix absolute/meson.build
+++ b/test cases/failing/37 prefix absolute/meson.build
diff --git a/test cases/failing/38 prefix absolute/test.json b/test cases/failing/37 prefix absolute/test.json
index 859906c..c1f8271 100644
--- a/test cases/failing/38 prefix absolute/test.json
+++ b/test cases/failing/37 prefix absolute/test.json
@@ -5,7 +5,7 @@
"stdout": [
{
"comment": "literal 'some/path/notabs' appears in output, irrespective of os.path.sep, as that's the prefix",
- "line": "test cases/failing/38 prefix absolute/meson.build:1:0: ERROR: prefix value 'some/path/notabs' must be an absolute path"
+ "line": "test cases/failing/37 prefix absolute/meson.build:1:0: ERROR: prefix value 'some/path/notabs' must be an absolute path"
}
]
}
diff --git a/test cases/failing/39 kwarg assign/dummy.c b/test cases/failing/38 kwarg assign/dummy.c
index 16fcdd9..16fcdd9 100644
--- a/test cases/failing/39 kwarg assign/dummy.c
+++ b/test cases/failing/38 kwarg assign/dummy.c
diff --git a/test cases/failing/39 kwarg assign/meson.build b/test cases/failing/38 kwarg assign/meson.build
index 7d42521..7d42521 100644
--- a/test cases/failing/39 kwarg assign/meson.build
+++ b/test cases/failing/38 kwarg assign/meson.build
diff --git a/test cases/failing/39 kwarg assign/prog.c b/test cases/failing/38 kwarg assign/prog.c
index 11b7fad..11b7fad 100644
--- a/test cases/failing/39 kwarg assign/prog.c
+++ b/test cases/failing/38 kwarg assign/prog.c
diff --git a/test cases/failing/39 kwarg assign/test.json b/test cases/failing/38 kwarg assign/test.json
index e12f2dc..daf2550 100644
--- a/test cases/failing/39 kwarg assign/test.json
+++ b/test cases/failing/38 kwarg assign/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/39 kwarg assign/meson.build:3:30: ERROR: Tried to assign values inside an argument list."
+ "line": "test cases/failing/38 kwarg assign/meson.build:3:30: ERROR: Tried to assign values inside an argument list."
}
]
}
diff --git a/test cases/failing/40 custom target plainname many inputs/1.txt b/test cases/failing/39 custom target plainname many inputs/1.txt
index d00491f..d00491f 100644
--- a/test cases/failing/40 custom target plainname many inputs/1.txt
+++ b/test cases/failing/39 custom target plainname many inputs/1.txt
diff --git a/test cases/failing/40 custom target plainname many inputs/2.txt b/test cases/failing/39 custom target plainname many inputs/2.txt
index 0cfbf08..0cfbf08 100644
--- a/test cases/failing/40 custom target plainname many inputs/2.txt
+++ b/test cases/failing/39 custom target plainname many inputs/2.txt
diff --git a/test cases/failing/40 custom target plainname many inputs/catfiles.py b/test cases/failing/39 custom target plainname many inputs/catfiles.py
index 1c53e24..1c53e24 100644
--- a/test cases/failing/40 custom target plainname many inputs/catfiles.py
+++ b/test cases/failing/39 custom target plainname many inputs/catfiles.py
diff --git a/test cases/failing/40 custom target plainname many inputs/meson.build b/test cases/failing/39 custom target plainname many inputs/meson.build
index 8513bf8..8513bf8 100644
--- a/test cases/failing/40 custom target plainname many inputs/meson.build
+++ b/test cases/failing/39 custom target plainname many inputs/meson.build
diff --git a/test cases/failing/40 custom target plainname many inputs/test.json b/test cases/failing/39 custom target plainname many inputs/test.json
index f5c3abf..e3f4c1b 100644
--- a/test cases/failing/40 custom target plainname many inputs/test.json
+++ b/test cases/failing/39 custom target plainname many inputs/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/40 custom target plainname many inputs/meson.build:5:0: ERROR: custom_target: output cannot contain \"@PLAINNAME@\" or \"@BASENAME@\" when there is more than one input (we can't know which to use)"
+ "line": "test cases/failing/39 custom target plainname many inputs/meson.build:5:0: ERROR: custom_target: output cannot contain \"@PLAINNAME@\" or \"@BASENAME@\" when there is more than one input (we can't know which to use)"
}
]
}
diff --git a/test cases/failing/41 custom target outputs not matching install_dirs/generator.py b/test cases/failing/40 custom target outputs not matching install_dirs/generator.py
index 4ac6179..4ac6179 100755
--- a/test cases/failing/41 custom target outputs not matching install_dirs/generator.py
+++ b/test cases/failing/40 custom target outputs not matching install_dirs/generator.py
diff --git a/test cases/failing/41 custom target outputs not matching install_dirs/meson.build b/test cases/failing/40 custom target outputs not matching install_dirs/meson.build
index ed99dba..ed99dba 100644
--- a/test cases/failing/41 custom target outputs not matching install_dirs/meson.build
+++ b/test cases/failing/40 custom target outputs not matching install_dirs/meson.build
diff --git a/test cases/failing/41 custom target outputs not matching install_dirs/test.json b/test cases/failing/40 custom target outputs not matching install_dirs/test.json
index f9e2ba7..f9e2ba7 100644
--- a/test cases/failing/41 custom target outputs not matching install_dirs/test.json
+++ b/test cases/failing/40 custom target outputs not matching install_dirs/test.json
diff --git a/test cases/failing/42 project name colon/meson.build b/test cases/failing/41 project name colon/meson.build
index 53e947e..53e947e 100644
--- a/test cases/failing/42 project name colon/meson.build
+++ b/test cases/failing/41 project name colon/meson.build
diff --git a/test cases/failing/42 project name colon/test.json b/test cases/failing/41 project name colon/test.json
index c3b880e..8c8e284 100644
--- a/test cases/failing/42 project name colon/test.json
+++ b/test cases/failing/41 project name colon/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/42 project name colon/meson.build:1:0: ERROR: Project name 'name with :' must not contain ':'"
+ "line": "test cases/failing/41 project name colon/meson.build:1:0: ERROR: Project name 'name with :' must not contain ':'"
}
]
}
diff --git a/test cases/failing/43 abs subdir/bob/meson.build b/test cases/failing/42 abs subdir/bob/meson.build
index 7bbf4b2..7bbf4b2 100644
--- a/test cases/failing/43 abs subdir/bob/meson.build
+++ b/test cases/failing/42 abs subdir/bob/meson.build
diff --git a/test cases/failing/43 abs subdir/meson.build b/test cases/failing/42 abs subdir/meson.build
index a8534d0..a8534d0 100644
--- a/test cases/failing/43 abs subdir/meson.build
+++ b/test cases/failing/42 abs subdir/meson.build
diff --git a/test cases/failing/43 abs subdir/test.json b/test cases/failing/42 abs subdir/test.json
index 201ed44..5b99c24 100644
--- a/test cases/failing/43 abs subdir/test.json
+++ b/test cases/failing/42 abs subdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/43 abs subdir/meson.build:5:0: ERROR: Subdir argument must be a relative path."
+ "line": "test cases/failing/42 abs subdir/meson.build:5:0: ERROR: Subdir argument must be a relative path."
}
]
}
diff --git a/test cases/failing/44 abspath to srcdir/meson.build b/test cases/failing/43 abspath to srcdir/meson.build
index 78c6124..78c6124 100644
--- a/test cases/failing/44 abspath to srcdir/meson.build
+++ b/test cases/failing/43 abspath to srcdir/meson.build
diff --git a/test cases/failing/44 abspath to srcdir/test.json b/test cases/failing/43 abspath to srcdir/test.json
index c64ecfb..af9588d 100644
--- a/test cases/failing/44 abspath to srcdir/test.json
+++ b/test cases/failing/43 abspath to srcdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/44 abspath to srcdir/meson.build:3:0: ERROR: Tried to form an absolute path to a dir in the source tree."
+ "line": "test cases/failing/43 abspath to srcdir/meson.build:3:0: ERROR: Tried to form an absolute path to a dir in the source tree."
}
]
}
diff --git a/test cases/failing/45 pkgconfig variables reserved/meson.build b/test cases/failing/44 pkgconfig variables reserved/meson.build
index 82ae995..82ae995 100644
--- a/test cases/failing/45 pkgconfig variables reserved/meson.build
+++ b/test cases/failing/44 pkgconfig variables reserved/meson.build
diff --git a/test cases/failing/45 pkgconfig variables reserved/simple.c b/test cases/failing/44 pkgconfig variables reserved/simple.c
index e8a6d83..e8a6d83 100644
--- a/test cases/failing/45 pkgconfig variables reserved/simple.c
+++ b/test cases/failing/44 pkgconfig variables reserved/simple.c
diff --git a/test cases/failing/45 pkgconfig variables reserved/simple.h b/test cases/failing/44 pkgconfig variables reserved/simple.h
index bb52e6d..bb52e6d 100644
--- a/test cases/failing/45 pkgconfig variables reserved/simple.h
+++ b/test cases/failing/44 pkgconfig variables reserved/simple.h
diff --git a/test cases/failing/45 pkgconfig variables reserved/test.json b/test cases/failing/44 pkgconfig variables reserved/test.json
index 853eb82..66ded59 100644
--- a/test cases/failing/45 pkgconfig variables reserved/test.json
+++ b/test cases/failing/44 pkgconfig variables reserved/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/45 pkgconfig variables reserved/meson.build:8:5: ERROR: Variable \"prefix\" is reserved"
+ "line": "test cases/failing/44 pkgconfig variables reserved/meson.build:8:5: ERROR: Variable \"prefix\" is reserved"
}
]
}
diff --git a/test cases/failing/46 pkgconfig variables zero length/meson.build b/test cases/failing/45 pkgconfig variables zero length/meson.build
index 65d3344..65d3344 100644
--- a/test cases/failing/46 pkgconfig variables zero length/meson.build
+++ b/test cases/failing/45 pkgconfig variables zero length/meson.build
diff --git a/test cases/failing/46 pkgconfig variables zero length/simple.c b/test cases/failing/45 pkgconfig variables zero length/simple.c
index e8a6d83..e8a6d83 100644
--- a/test cases/failing/46 pkgconfig variables zero length/simple.c
+++ b/test cases/failing/45 pkgconfig variables zero length/simple.c
diff --git a/test cases/failing/46 pkgconfig variables zero length/simple.h b/test cases/failing/45 pkgconfig variables zero length/simple.h
index bb52e6d..bb52e6d 100644
--- a/test cases/failing/46 pkgconfig variables zero length/simple.h
+++ b/test cases/failing/45 pkgconfig variables zero length/simple.h
diff --git a/test cases/failing/46 pkgconfig variables zero length/test.json b/test cases/failing/45 pkgconfig variables zero length/test.json
index b174065..899b9ed 100644
--- a/test cases/failing/46 pkgconfig variables zero length/test.json
+++ b/test cases/failing/45 pkgconfig variables zero length/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/46 pkgconfig variables zero length/meson.build:8:5: ERROR: pkgconfig.generate keyword argument \"variables\" empty variable name"
+ "line": "test cases/failing/45 pkgconfig variables zero length/meson.build:8:5: ERROR: pkgconfig.generate keyword argument \"variables\" empty variable name"
}
]
}
diff --git a/test cases/failing/47 pkgconfig variables not key value/meson.build b/test cases/failing/46 pkgconfig variables not key value/meson.build
index 02fa737..02fa737 100644
--- a/test cases/failing/47 pkgconfig variables not key value/meson.build
+++ b/test cases/failing/46 pkgconfig variables not key value/meson.build
diff --git a/test cases/failing/47 pkgconfig variables not key value/simple.c b/test cases/failing/46 pkgconfig variables not key value/simple.c
index e8a6d83..e8a6d83 100644
--- a/test cases/failing/47 pkgconfig variables not key value/simple.c
+++ b/test cases/failing/46 pkgconfig variables not key value/simple.c
diff --git a/test cases/failing/47 pkgconfig variables not key value/simple.h b/test cases/failing/46 pkgconfig variables not key value/simple.h
index bb52e6d..bb52e6d 100644
--- a/test cases/failing/47 pkgconfig variables not key value/simple.h
+++ b/test cases/failing/46 pkgconfig variables not key value/simple.h
diff --git a/test cases/failing/47 pkgconfig variables not key value/test.json b/test cases/failing/46 pkgconfig variables not key value/test.json
index 11cd374..5729d1e 100644
--- a/test cases/failing/47 pkgconfig variables not key value/test.json
+++ b/test cases/failing/46 pkgconfig variables not key value/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/47 pkgconfig variables not key value/meson.build:8:5: ERROR: pkgconfig.generate keyword argument \"variables\" variable 'this_should_be_key_value' must have a value separated by equals sign."
+ "line": "test cases/failing/46 pkgconfig variables not key value/meson.build:8:5: ERROR: pkgconfig.generate keyword argument \"variables\" variable 'this_should_be_key_value' must have a value separated by equals sign."
}
]
}
diff --git a/test cases/failing/48 executable comparison/meson.build b/test cases/failing/47 executable comparison/meson.build
index 041bcf3..041bcf3 100644
--- a/test cases/failing/48 executable comparison/meson.build
+++ b/test cases/failing/47 executable comparison/meson.build
diff --git a/test cases/failing/48 executable comparison/prog.c b/test cases/failing/47 executable comparison/prog.c
index 0314ff1..0314ff1 100644
--- a/test cases/failing/48 executable comparison/prog.c
+++ b/test cases/failing/47 executable comparison/prog.c
diff --git a/test cases/failing/48 executable comparison/test.json b/test cases/failing/47 executable comparison/test.json
index db6dac3..f365477 100644
--- a/test cases/failing/48 executable comparison/test.json
+++ b/test cases/failing/47 executable comparison/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/48 executable comparison/meson.build:6:14: ERROR: Object <ExecutableHolder prog1@exe: prog1(.exe)?> of type Executable does not support the `<` operator."
+ "line": "test cases/failing/47 executable comparison/meson.build:6:14: ERROR: Object <ExecutableHolder prog1@exe: prog1(.exe)?> of type Executable does not support the `<` operator."
}
]
}
diff --git a/test cases/failing/49 inconsistent comparison/meson.build b/test cases/failing/48 inconsistent comparison/meson.build
index 237a157..237a157 100644
--- a/test cases/failing/49 inconsistent comparison/meson.build
+++ b/test cases/failing/48 inconsistent comparison/meson.build
diff --git a/test cases/failing/49 inconsistent comparison/test.json b/test cases/failing/48 inconsistent comparison/test.json
index 4440a86..42157a5 100644
--- a/test cases/failing/49 inconsistent comparison/test.json
+++ b/test cases/failing/48 inconsistent comparison/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/49 inconsistent comparison/meson.build:5:12: ERROR: Object <[ArrayHolder] holds [list]: []> of type array does not support the `<` operator."
+ "line": "test cases/failing/48 inconsistent comparison/meson.build:5:12: ERROR: Object <[ArrayHolder] holds [list]: []> of type array does not support the `<` operator."
}
]
}
diff --git a/test cases/failing/50 slashname/meson.build b/test cases/failing/49 slashname/meson.build
index 29fe1fc..29fe1fc 100644
--- a/test cases/failing/50 slashname/meson.build
+++ b/test cases/failing/49 slashname/meson.build
diff --git a/test cases/failing/50 slashname/sub/meson.build b/test cases/failing/49 slashname/sub/meson.build
index 3baacf6..3baacf6 100644
--- a/test cases/failing/50 slashname/sub/meson.build
+++ b/test cases/failing/49 slashname/sub/meson.build
diff --git a/test cases/failing/50 slashname/sub/prog.c b/test cases/failing/49 slashname/sub/prog.c
index 722de0a..722de0a 100644
--- a/test cases/failing/50 slashname/sub/prog.c
+++ b/test cases/failing/49 slashname/sub/prog.c
diff --git a/test cases/failing/50 slashname/test.json b/test cases/failing/49 slashname/test.json
index 44b566c..e07d163 100644
--- a/test cases/failing/50 slashname/test.json
+++ b/test cases/failing/49 slashname/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/50 slashname/meson.build:9:0: ERROR: Target \"sub/prog\" has a path segment pointing to directory \"sub\". This is an error."
+ "line": "test cases/failing/49 slashname/meson.build:9:0: ERROR: Target \"sub/prog\" has a path segment pointing to directory \"sub\". This is an error."
}
]
}
diff --git a/test cases/failing/51 reserved meson prefix/meson-foo/meson.build b/test cases/failing/50 reserved meson prefix/meson-foo/meson.build
index e69de29..e69de29 100644
--- a/test cases/failing/51 reserved meson prefix/meson-foo/meson.build
+++ b/test cases/failing/50 reserved meson prefix/meson-foo/meson.build
diff --git a/test cases/failing/51 reserved meson prefix/meson.build b/test cases/failing/50 reserved meson prefix/meson.build
index 1339035..1339035 100644
--- a/test cases/failing/51 reserved meson prefix/meson.build
+++ b/test cases/failing/50 reserved meson prefix/meson.build
diff --git a/test cases/failing/51 reserved meson prefix/test.json b/test cases/failing/50 reserved meson prefix/test.json
index 9eb1fa4..512a445 100644
--- a/test cases/failing/51 reserved meson prefix/test.json
+++ b/test cases/failing/50 reserved meson prefix/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/51 reserved meson prefix/meson.build:3:0: ERROR: The \"meson-\" prefix is reserved and cannot be used for top-level subdir()."
+ "line": "test cases/failing/50 reserved meson prefix/meson.build:3:0: ERROR: The \"meson-\" prefix is reserved and cannot be used for top-level subdir()."
}
]
}
diff --git a/test cases/failing/52 or on new line/meson.build b/test cases/failing/51 or on new line/meson.build
index b0bd08e..b0bd08e 100644
--- a/test cases/failing/52 or on new line/meson.build
+++ b/test cases/failing/51 or on new line/meson.build
diff --git a/test cases/failing/52 or on new line/meson_options.txt b/test cases/failing/51 or on new line/meson_options.txt
index 3302cf4..3302cf4 100644
--- a/test cases/failing/52 or on new line/meson_options.txt
+++ b/test cases/failing/51 or on new line/meson_options.txt
diff --git a/test cases/failing/52 or on new line/test.json b/test cases/failing/51 or on new line/test.json
index 49a7255..c05d1a8 100644
--- a/test cases/failing/52 or on new line/test.json
+++ b/test cases/failing/51 or on new line/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/52 or on new line/meson.build:4:8: ERROR: Invalid or clause."
+ "line": "test cases/failing/51 or on new line/meson.build:4:8: ERROR: Invalid or clause."
}
]
}
diff --git a/test cases/failing/53 link with executable/meson.build b/test cases/failing/52 link with executable/meson.build
index 186b3e5..186b3e5 100644
--- a/test cases/failing/53 link with executable/meson.build
+++ b/test cases/failing/52 link with executable/meson.build
diff --git a/test cases/failing/53 link with executable/module.c b/test cases/failing/52 link with executable/module.c
index dc0124a..dc0124a 100644
--- a/test cases/failing/53 link with executable/module.c
+++ b/test cases/failing/52 link with executable/module.c
diff --git a/test cases/failing/53 link with executable/prog.c b/test cases/failing/52 link with executable/prog.c
index f3836d7..f3836d7 100644
--- a/test cases/failing/53 link with executable/prog.c
+++ b/test cases/failing/52 link with executable/prog.c
diff --git a/test cases/failing/53 link with executable/test.json b/test cases/failing/52 link with executable/test.json
index 8c25e07..ba9c345 100644
--- a/test cases/failing/53 link with executable/test.json
+++ b/test cases/failing/52 link with executable/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/53 link with executable/meson.build:4:4: ERROR: Link target 'prog' is not linkable."
+ "line": "test cases/failing/52 link with executable/meson.build:4:4: ERROR: Link target 'prog' is not linkable."
}
]
}
diff --git a/test cases/failing/54 assign custom target index/meson.build b/test cases/failing/53 assign custom target index/meson.build
index 7f2a820..7f2a820 100644
--- a/test cases/failing/54 assign custom target index/meson.build
+++ b/test cases/failing/53 assign custom target index/meson.build
diff --git a/test cases/failing/54 assign custom target index/test.json b/test cases/failing/53 assign custom target index/test.json
index 15287d6..e9990a2 100644
--- a/test cases/failing/54 assign custom target index/test.json
+++ b/test cases/failing/53 assign custom target index/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/54 assign custom target index/meson.build:24:0: ERROR: Assignment target must be an id."
+ "line": "test cases/failing/53 assign custom target index/meson.build:24:0: ERROR: Assignment target must be an id."
}
]
}
diff --git a/test cases/failing/55 getoption prefix/meson.build b/test cases/failing/54 getoption prefix/meson.build
index 8f85cff..8f85cff 100644
--- a/test cases/failing/55 getoption prefix/meson.build
+++ b/test cases/failing/54 getoption prefix/meson.build
diff --git a/test cases/failing/55 getoption prefix/subprojects/abc/meson.build b/test cases/failing/54 getoption prefix/subprojects/abc/meson.build
index aa9c3df..aa9c3df 100644
--- a/test cases/failing/55 getoption prefix/subprojects/abc/meson.build
+++ b/test cases/failing/54 getoption prefix/subprojects/abc/meson.build
diff --git a/test cases/failing/55 getoption prefix/subprojects/abc/meson_options.txt b/test cases/failing/54 getoption prefix/subprojects/abc/meson_options.txt
index 89e624e..89e624e 100644
--- a/test cases/failing/55 getoption prefix/subprojects/abc/meson_options.txt
+++ b/test cases/failing/54 getoption prefix/subprojects/abc/meson_options.txt
diff --git a/test cases/failing/55 getoption prefix/test.json b/test cases/failing/54 getoption prefix/test.json
index 9f3a936..d2ba2f6 100644
--- a/test cases/failing/55 getoption prefix/test.json
+++ b/test cases/failing/54 getoption prefix/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/55 getoption prefix/meson.build:5:0: ERROR: Having a colon in option name is forbidden, projects are not allowed to directly access options of other subprojects."
+ "line": "test cases/failing/54 getoption prefix/meson.build:5:0: ERROR: Having a colon in option name is forbidden, projects are not allowed to directly access options of other subprojects."
}
]
}
diff --git a/test cases/failing/56 bad option argument/meson.build b/test cases/failing/55 bad option argument/meson.build
index 5219cfb..5219cfb 100644
--- a/test cases/failing/56 bad option argument/meson.build
+++ b/test cases/failing/55 bad option argument/meson.build
diff --git a/test cases/failing/56 bad option argument/meson_options.txt b/test cases/failing/55 bad option argument/meson_options.txt
index 0e0372b..0e0372b 100644
--- a/test cases/failing/56 bad option argument/meson_options.txt
+++ b/test cases/failing/55 bad option argument/meson_options.txt
diff --git a/test cases/failing/56 bad option argument/test.json b/test cases/failing/55 bad option argument/test.json
index c7957cd..0bfc4b9 100644
--- a/test cases/failing/56 bad option argument/test.json
+++ b/test cases/failing/55 bad option argument/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/56 bad option argument/meson_options.txt:1:0: ERROR: string option got unknown keyword arguments \"value_\""
+ "line": "test cases/failing/55 bad option argument/meson_options.txt:1:0: ERROR: string option got unknown keyword arguments \"value_\""
}
]
}
diff --git a/test cases/failing/57 subproj filegrab/meson.build b/test cases/failing/56 subproj filegrab/meson.build
index b5c484c..b5c484c 100644
--- a/test cases/failing/57 subproj filegrab/meson.build
+++ b/test cases/failing/56 subproj filegrab/meson.build
diff --git a/test cases/failing/57 subproj filegrab/prog.c b/test cases/failing/56 subproj filegrab/prog.c
index 0314ff1..0314ff1 100644
--- a/test cases/failing/57 subproj filegrab/prog.c
+++ b/test cases/failing/56 subproj filegrab/prog.c
diff --git a/test cases/failing/57 subproj filegrab/subprojects/a/meson.build b/test cases/failing/56 subproj filegrab/subprojects/a/meson.build
index 80b9888..80b9888 100644
--- a/test cases/failing/57 subproj filegrab/subprojects/a/meson.build
+++ b/test cases/failing/56 subproj filegrab/subprojects/a/meson.build
diff --git a/test cases/failing/57 subproj filegrab/test.json b/test cases/failing/56 subproj filegrab/test.json
index 8b0b27f..cdd1029 100644
--- a/test cases/failing/57 subproj filegrab/test.json
+++ b/test cases/failing/56 subproj filegrab/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/57 subproj filegrab/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file prog.c outside current (sub)project."
+ "line": "test cases/failing/56 subproj filegrab/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file prog.c outside current (sub)project."
}
]
}
diff --git a/test cases/failing/58 grab subproj/meson.build b/test cases/failing/57 grab subproj/meson.build
index 30fc690..30fc690 100644
--- a/test cases/failing/58 grab subproj/meson.build
+++ b/test cases/failing/57 grab subproj/meson.build
diff --git a/test cases/failing/58 grab subproj/subprojects/foo/meson.build b/test cases/failing/57 grab subproj/subprojects/foo/meson.build
index b346f6d..b346f6d 100644
--- a/test cases/failing/58 grab subproj/subprojects/foo/meson.build
+++ b/test cases/failing/57 grab subproj/subprojects/foo/meson.build
diff --git a/test cases/failing/58 grab subproj/subprojects/foo/sub.c b/test cases/failing/57 grab subproj/subprojects/foo/sub.c
index a94b1f5..a94b1f5 100644
--- a/test cases/failing/58 grab subproj/subprojects/foo/sub.c
+++ b/test cases/failing/57 grab subproj/subprojects/foo/sub.c
diff --git a/test cases/failing/58 grab subproj/test.json b/test cases/failing/57 grab subproj/test.json
index bdb0fdf..1915efd 100644
--- a/test cases/failing/58 grab subproj/test.json
+++ b/test cases/failing/57 grab subproj/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/58 grab subproj/meson.build:7:0: ERROR: Sandbox violation: Tried to grab file sub.c from a nested subproject."
+ "line": "test cases/failing/57 grab subproj/meson.build:7:0: ERROR: Sandbox violation: Tried to grab file sub.c from a nested subproject."
}
]
}
diff --git a/test cases/failing/59 grab sibling/meson.build b/test cases/failing/58 grab sibling/meson.build
index 5ddc295..5ddc295 100644
--- a/test cases/failing/59 grab sibling/meson.build
+++ b/test cases/failing/58 grab sibling/meson.build
diff --git a/test cases/failing/59 grab sibling/subprojects/a/meson.build b/test cases/failing/58 grab sibling/subprojects/a/meson.build
index 6dd9f61..6dd9f61 100644
--- a/test cases/failing/59 grab sibling/subprojects/a/meson.build
+++ b/test cases/failing/58 grab sibling/subprojects/a/meson.build
diff --git a/test cases/failing/59 grab sibling/subprojects/b/meson.build b/test cases/failing/58 grab sibling/subprojects/b/meson.build
index 57f261a..57f261a 100644
--- a/test cases/failing/59 grab sibling/subprojects/b/meson.build
+++ b/test cases/failing/58 grab sibling/subprojects/b/meson.build
diff --git a/test cases/failing/59 grab sibling/subprojects/b/sneaky.c b/test cases/failing/58 grab sibling/subprojects/b/sneaky.c
index 46718c6..46718c6 100644
--- a/test cases/failing/59 grab sibling/subprojects/b/sneaky.c
+++ b/test cases/failing/58 grab sibling/subprojects/b/sneaky.c
diff --git a/test cases/failing/59 grab sibling/test.json b/test cases/failing/58 grab sibling/test.json
index ec17e7e..cbf8b2d 100644
--- a/test cases/failing/59 grab sibling/test.json
+++ b/test cases/failing/58 grab sibling/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/59 grab sibling/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file sneaky.c outside current (sub)project."
+ "line": "test cases/failing/58 grab sibling/subprojects/a/meson.build:3:0: ERROR: Sandbox violation: Tried to grab file sneaky.c outside current (sub)project."
}
]
}
diff --git a/test cases/failing/60 string as link target/meson.build b/test cases/failing/59 string as link target/meson.build
index cb83fff..cb83fff 100644
--- a/test cases/failing/60 string as link target/meson.build
+++ b/test cases/failing/59 string as link target/meson.build
diff --git a/test cases/failing/60 string as link target/prog.c b/test cases/failing/59 string as link target/prog.c
index 0314ff1..0314ff1 100644
--- a/test cases/failing/60 string as link target/prog.c
+++ b/test cases/failing/59 string as link target/prog.c
diff --git a/test cases/failing/60 string as link target/test.json b/test cases/failing/59 string as link target/test.json
index a97b124..ddc6399 100644
--- a/test cases/failing/60 string as link target/test.json
+++ b/test cases/failing/59 string as link target/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/60 string as link target/meson.build:2:0: ERROR: '' is not a target."
+ "line": "test cases/failing/59 string as link target/meson.build:2:0: ERROR: '' is not a target."
}
]
}
diff --git a/test cases/failing/61 dependency not-found and required/meson.build b/test cases/failing/60 dependency not-found and required/meson.build
index 1ce5747..1ce5747 100644
--- a/test cases/failing/61 dependency not-found and required/meson.build
+++ b/test cases/failing/60 dependency not-found and required/meson.build
diff --git a/test cases/failing/61 dependency not-found and required/test.json b/test cases/failing/60 dependency not-found and required/test.json
index f7a6c9f..c104a6a 100644
--- a/test cases/failing/61 dependency not-found and required/test.json
+++ b/test cases/failing/60 dependency not-found and required/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/61 dependency not-found and required/meson.build:2:6: ERROR: Dependency is required but has no candidates."
+ "line": "test cases/failing/60 dependency not-found and required/meson.build:2:6: ERROR: Dependency is required but has no candidates."
}
]
}
diff --git a/test cases/failing/62 subproj different versions/main.c b/test cases/failing/61 subproj different versions/main.c
index 8793c62..8793c62 100644
--- a/test cases/failing/62 subproj different versions/main.c
+++ b/test cases/failing/61 subproj different versions/main.c
diff --git a/test cases/failing/62 subproj different versions/meson.build b/test cases/failing/61 subproj different versions/meson.build
index e964e42..e964e42 100644
--- a/test cases/failing/62 subproj different versions/meson.build
+++ b/test cases/failing/61 subproj different versions/meson.build
diff --git a/test cases/failing/62 subproj different versions/subprojects/a/a.c b/test cases/failing/61 subproj different versions/subprojects/a/a.c
index cd41a65..cd41a65 100644
--- a/test cases/failing/62 subproj different versions/subprojects/a/a.c
+++ b/test cases/failing/61 subproj different versions/subprojects/a/a.c
diff --git a/test cases/failing/62 subproj different versions/subprojects/a/a.h b/test cases/failing/61 subproj different versions/subprojects/a/a.h
index 8f1d49e..8f1d49e 100644
--- a/test cases/failing/62 subproj different versions/subprojects/a/a.h
+++ b/test cases/failing/61 subproj different versions/subprojects/a/a.h
diff --git a/test cases/failing/62 subproj different versions/subprojects/a/meson.build b/test cases/failing/61 subproj different versions/subprojects/a/meson.build
index e84182a..e84182a 100644
--- a/test cases/failing/62 subproj different versions/subprojects/a/meson.build
+++ b/test cases/failing/61 subproj different versions/subprojects/a/meson.build
diff --git a/test cases/failing/62 subproj different versions/subprojects/b/b.c b/test cases/failing/61 subproj different versions/subprojects/b/b.c
index f85f8c3..f85f8c3 100644
--- a/test cases/failing/62 subproj different versions/subprojects/b/b.c
+++ b/test cases/failing/61 subproj different versions/subprojects/b/b.c
diff --git a/test cases/failing/62 subproj different versions/subprojects/b/b.h b/test cases/failing/61 subproj different versions/subprojects/b/b.h
index eced786..eced786 100644
--- a/test cases/failing/62 subproj different versions/subprojects/b/b.h
+++ b/test cases/failing/61 subproj different versions/subprojects/b/b.h
diff --git a/test cases/failing/62 subproj different versions/subprojects/b/meson.build b/test cases/failing/61 subproj different versions/subprojects/b/meson.build
index 0398340..0398340 100644
--- a/test cases/failing/62 subproj different versions/subprojects/b/meson.build
+++ b/test cases/failing/61 subproj different versions/subprojects/b/meson.build
diff --git a/test cases/failing/62 subproj different versions/subprojects/c/c.h b/test cases/failing/61 subproj different versions/subprojects/c/c.h
index 2b15f60..2b15f60 100644
--- a/test cases/failing/62 subproj different versions/subprojects/c/c.h
+++ b/test cases/failing/61 subproj different versions/subprojects/c/c.h
diff --git a/test cases/failing/62 subproj different versions/subprojects/c/meson.build b/test cases/failing/61 subproj different versions/subprojects/c/meson.build
index 7184933..7184933 100644
--- a/test cases/failing/62 subproj different versions/subprojects/c/meson.build
+++ b/test cases/failing/61 subproj different versions/subprojects/c/meson.build
diff --git a/test cases/failing/62 subproj different versions/test.json b/test cases/failing/61 subproj different versions/test.json
index 705ff51..b395baa 100644
--- a/test cases/failing/62 subproj different versions/test.json
+++ b/test cases/failing/61 subproj different versions/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/62 subproj different versions/subprojects/b/meson.build:3:8: ERROR: Dependency 'c' is required but not found."
+ "line": "test cases/failing/61 subproj different versions/subprojects/b/meson.build:3:8: ERROR: Dependency 'c' is required but not found."
}
]
}
diff --git a/test cases/failing/63 wrong boost module/meson.build b/test cases/failing/62 wrong boost module/meson.build
index 937e587..937e587 100644
--- a/test cases/failing/63 wrong boost module/meson.build
+++ b/test cases/failing/62 wrong boost module/meson.build
diff --git a/test cases/failing/63 wrong boost module/test.json b/test cases/failing/62 wrong boost module/test.json
index ec3c1b0..75ef82b 100644
--- a/test cases/failing/63 wrong boost module/test.json
+++ b/test cases/failing/62 wrong boost module/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/63 wrong boost module/meson.build:9:10: ERROR: Dependency \"boost\" not found, tried system"
+ "line": "test cases/failing/62 wrong boost module/meson.build:9:10: ERROR: Dependency \"boost\" not found, tried system"
}
]
}
diff --git a/test cases/failing/64 install_data rename bad size/file1.txt b/test cases/failing/63 install_data rename bad size/file1.txt
index e69de29..e69de29 100644
--- a/test cases/failing/64 install_data rename bad size/file1.txt
+++ b/test cases/failing/63 install_data rename bad size/file1.txt
diff --git a/test cases/failing/64 install_data rename bad size/file2.txt b/test cases/failing/63 install_data rename bad size/file2.txt
index e69de29..e69de29 100644
--- a/test cases/failing/64 install_data rename bad size/file2.txt
+++ b/test cases/failing/63 install_data rename bad size/file2.txt
diff --git a/test cases/failing/64 install_data rename bad size/meson.build b/test cases/failing/63 install_data rename bad size/meson.build
index 849bb9a..849bb9a 100644
--- a/test cases/failing/64 install_data rename bad size/meson.build
+++ b/test cases/failing/63 install_data rename bad size/meson.build
diff --git a/test cases/failing/64 install_data rename bad size/test.json b/test cases/failing/63 install_data rename bad size/test.json
index 5ed4748..28988af 100644
--- a/test cases/failing/64 install_data rename bad size/test.json
+++ b/test cases/failing/63 install_data rename bad size/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/64 install_data rename bad size/meson.build:3:0: ERROR: \"rename\" and \"sources\" argument lists must be the same length if \"rename\" is given. Rename has 1 elements and sources has 2."
+ "line": "test cases/failing/63 install_data rename bad size/meson.build:3:0: ERROR: \"rename\" and \"sources\" argument lists must be the same length if \"rename\" is given. Rename has 1 elements and sources has 2."
}
]
}
diff --git a/test cases/failing/65 skip only subdir/meson.build b/test cases/failing/64 skip only subdir/meson.build
index 4832bd4..4832bd4 100644
--- a/test cases/failing/65 skip only subdir/meson.build
+++ b/test cases/failing/64 skip only subdir/meson.build
diff --git a/test cases/failing/65 skip only subdir/subdir/meson.build b/test cases/failing/64 skip only subdir/subdir/meson.build
index 1ba447b..1ba447b 100644
--- a/test cases/failing/65 skip only subdir/subdir/meson.build
+++ b/test cases/failing/64 skip only subdir/subdir/meson.build
diff --git a/test cases/failing/65 skip only subdir/test.json b/test cases/failing/64 skip only subdir/test.json
index 2747566..54fa369 100644
--- a/test cases/failing/65 skip only subdir/test.json
+++ b/test cases/failing/64 skip only subdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/65 skip only subdir/meson.build:8:0: ERROR: File main.cpp does not exist."
+ "line": "test cases/failing/64 skip only subdir/meson.build:8:0: ERROR: File main.cpp does not exist."
}
]
}
diff --git a/test cases/failing/66 dual override/meson.build b/test cases/failing/65 dual override/meson.build
index 999b8bc..999b8bc 100644
--- a/test cases/failing/66 dual override/meson.build
+++ b/test cases/failing/65 dual override/meson.build
diff --git a/test cases/failing/66 dual override/overrides.py b/test cases/failing/65 dual override/overrides.py
index 49e9b7a..49e9b7a 100644
--- a/test cases/failing/66 dual override/overrides.py
+++ b/test cases/failing/65 dual override/overrides.py
diff --git a/test cases/failing/66 dual override/test.json b/test cases/failing/65 dual override/test.json
index b8c2651..6daa014 100644
--- a/test cases/failing/66 dual override/test.json
+++ b/test cases/failing/65 dual override/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/66 dual override/meson.build:5:6: ERROR: Tried to override executable \"override\" which has already been overridden."
+ "line": "test cases/failing/65 dual override/meson.build:5:6: ERROR: Tried to override executable \"override\" which has already been overridden."
}
]
}
diff --git a/test cases/failing/67 override used/meson.build b/test cases/failing/66 override used/meson.build
index 582fc1b..582fc1b 100644
--- a/test cases/failing/67 override used/meson.build
+++ b/test cases/failing/66 override used/meson.build
diff --git a/test cases/failing/67 override used/other.py b/test cases/failing/66 override used/other.py
index f62ba96..f62ba96 100755
--- a/test cases/failing/67 override used/other.py
+++ b/test cases/failing/66 override used/other.py
diff --git a/test cases/failing/67 override used/something.py b/test cases/failing/66 override used/something.py
index 64c9454..64c9454 100755
--- a/test cases/failing/67 override used/something.py
+++ b/test cases/failing/66 override used/something.py
diff --git a/test cases/failing/67 override used/test.json b/test cases/failing/66 override used/test.json
index 90d8b4b..c73e4a4 100644
--- a/test cases/failing/67 override used/test.json
+++ b/test cases/failing/66 override used/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/67 override used/meson.build:5:6: ERROR: Tried to override finding of executable \"something.py\" which has already been found."
+ "line": "test cases/failing/66 override used/meson.build:5:6: ERROR: Tried to override finding of executable \"something.py\" which has already been found."
}
]
}
diff --git a/test cases/failing/68 run_command unclean exit/meson.build b/test cases/failing/67 run_command unclean exit/meson.build
index f6bf895..f6bf895 100644
--- a/test cases/failing/68 run_command unclean exit/meson.build
+++ b/test cases/failing/67 run_command unclean exit/meson.build
diff --git a/test cases/failing/68 run_command unclean exit/returncode.py b/test cases/failing/67 run_command unclean exit/returncode.py
index 84dbc5d..84dbc5d 100755
--- a/test cases/failing/68 run_command unclean exit/returncode.py
+++ b/test cases/failing/67 run_command unclean exit/returncode.py
diff --git a/test cases/failing/68 run_command unclean exit/test.json b/test cases/failing/67 run_command unclean exit/test.json
index cef67e5..9578849 100644
--- a/test cases/failing/68 run_command unclean exit/test.json
+++ b/test cases/failing/67 run_command unclean exit/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/68 run_command unclean exit/meson\\.build:4:0: ERROR: Command `.*['\"].*[\\\\/]test cases[\\\\/]failing[\\\\/]68 run_command unclean exit[\\\\/]\\.[\\\\/]returncode\\.py['\"] 1` failed with status 1\\."
+ "line": "test cases/failing/67 run_command unclean exit/meson\\.build:4:0: ERROR: Command `.*['\"].*[\\\\/]test cases[\\\\/]failing[\\\\/]67 run_command unclean exit[\\\\/]\\.[\\\\/]returncode\\.py['\"] 1` failed with status 1\\."
}
]
}
diff --git a/test cases/failing/69 int literal leading zero/meson.build b/test cases/failing/68 int literal leading zero/meson.build
index 87c776f..87c776f 100644
--- a/test cases/failing/69 int literal leading zero/meson.build
+++ b/test cases/failing/68 int literal leading zero/meson.build
diff --git a/test cases/failing/69 int literal leading zero/test.json b/test cases/failing/68 int literal leading zero/test.json
index 200b569..c98217a 100644
--- a/test cases/failing/69 int literal leading zero/test.json
+++ b/test cases/failing/68 int literal leading zero/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"comment": "this error message is not very informative",
- "line": "test cases/failing/69 int literal leading zero/meson.build:5:13: ERROR: Expecting eof got number."
+ "line": "test cases/failing/68 int literal leading zero/meson.build:5:13: ERROR: Expecting eof got number."
}
]
}
diff --git a/test cases/failing/70 configuration immutable/input b/test cases/failing/69 configuration immutable/input
index e69de29..e69de29 100644
--- a/test cases/failing/70 configuration immutable/input
+++ b/test cases/failing/69 configuration immutable/input
diff --git a/test cases/failing/70 configuration immutable/meson.build b/test cases/failing/69 configuration immutable/meson.build
index b6cac41..b6cac41 100644
--- a/test cases/failing/70 configuration immutable/meson.build
+++ b/test cases/failing/69 configuration immutable/meson.build
diff --git a/test cases/failing/70 configuration immutable/test.json b/test cases/failing/69 configuration immutable/test.json
index fc735fa..94f0428 100644
--- a/test cases/failing/70 configuration immutable/test.json
+++ b/test cases/failing/69 configuration immutable/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/70 configuration immutable/meson.build:12:16: ERROR: Can not set values on configuration object that has been used."
+ "line": "test cases/failing/69 configuration immutable/meson.build:12:16: ERROR: Can not set values on configuration object that has been used."
}
]
}
diff --git a/test cases/failing/71 link with shared module on osx/meson.build b/test cases/failing/70 link with shared module on osx/meson.build
index bf18b36..bf18b36 100644
--- a/test cases/failing/71 link with shared module on osx/meson.build
+++ b/test cases/failing/70 link with shared module on osx/meson.build
diff --git a/test cases/failing/71 link with shared module on osx/module.c b/test cases/failing/70 link with shared module on osx/module.c
index 81b0d5a..81b0d5a 100644
--- a/test cases/failing/71 link with shared module on osx/module.c
+++ b/test cases/failing/70 link with shared module on osx/module.c
diff --git a/test cases/failing/71 link with shared module on osx/prog.c b/test cases/failing/70 link with shared module on osx/prog.c
index 8164d8d..8164d8d 100644
--- a/test cases/failing/71 link with shared module on osx/prog.c
+++ b/test cases/failing/70 link with shared module on osx/prog.c
diff --git a/test cases/failing/71 link with shared module on osx/test.json b/test cases/failing/70 link with shared module on osx/test.json
index 206d429..430edba 100644
--- a/test cases/failing/71 link with shared module on osx/test.json
+++ b/test cases/failing/70 link with shared module on osx/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/71 link with shared module on osx/meson.build:8:4: ERROR: target prog links against shared module mymodule. This is not permitted on OSX"
+ "line": "test cases/failing/70 link with shared module on osx/meson.build:8:4: ERROR: target prog links against shared module mymodule. This is not permitted on OSX"
}
]
}
diff --git a/test cases/failing/72 non-ascii in ascii encoded configure file/config9.h.in b/test cases/failing/71 non-ascii in ascii encoded configure file/config9.h.in
index 323bec6..323bec6 100644
--- a/test cases/failing/72 non-ascii in ascii encoded configure file/config9.h.in
+++ b/test cases/failing/71 non-ascii in ascii encoded configure file/config9.h.in
diff --git a/test cases/failing/72 non-ascii in ascii encoded configure file/meson.build b/test cases/failing/71 non-ascii in ascii encoded configure file/meson.build
index eadb627..eadb627 100644
--- a/test cases/failing/72 non-ascii in ascii encoded configure file/meson.build
+++ b/test cases/failing/71 non-ascii in ascii encoded configure file/meson.build
diff --git a/test cases/failing/72 non-ascii in ascii encoded configure file/test.json b/test cases/failing/71 non-ascii in ascii encoded configure file/test.json
index 1dc0b91..8f993c9 100644
--- a/test cases/failing/72 non-ascii in ascii encoded configure file/test.json
+++ b/test cases/failing/71 non-ascii in ascii encoded configure file/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/72 non-ascii in ascii encoded configure file/meson\\.build:5:0: ERROR: Could not write output file .*[\\\\/]config9\\.h: 'ascii' codec can't encode character '\\\\u0434' in position 17: ordinal not in range\\(128\\)"
+ "line": "test cases/failing/71 non-ascii in ascii encoded configure file/meson\\.build:5:0: ERROR: Could not write output file .*[\\\\/]config9\\.h: 'ascii' codec can't encode character '\\\\u0434' in position 17: ordinal not in range\\(128\\)"
}
]
}
diff --git a/test cases/failing/73 subproj dependency not-found and required/meson.build b/test cases/failing/72 subproj dependency not-found and required/meson.build
index c5a2961..c5a2961 100644
--- a/test cases/failing/73 subproj dependency not-found and required/meson.build
+++ b/test cases/failing/72 subproj dependency not-found and required/meson.build
diff --git a/test cases/failing/72 subproj dependency not-found and required/test.json b/test cases/failing/72 subproj dependency not-found and required/test.json
new file mode 100644
index 0000000..015893b
--- /dev/null
+++ b/test cases/failing/72 subproj dependency not-found and required/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/72 subproj dependency not-found and required/meson.build:2:10: ERROR: Attempted to resolve subproject without subprojects directory present."
+ }
+ ]
+}
diff --git a/test cases/failing/73 subproj dependency not-found and required/test.json b/test cases/failing/73 subproj dependency not-found and required/test.json
deleted file mode 100644
index 11ab031..0000000
--- a/test cases/failing/73 subproj dependency not-found and required/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/73 subproj dependency not-found and required/meson.build:2:10: ERROR: Neither a subproject directory nor a missing.wrap file was found."
- }
- ]
-}
diff --git a/test cases/failing/74 unfound run/meson.build b/test cases/failing/73 unfound run/meson.build
index 3f37e9a..3f37e9a 100644
--- a/test cases/failing/74 unfound run/meson.build
+++ b/test cases/failing/73 unfound run/meson.build
diff --git a/test cases/failing/74 unfound run/test.json b/test cases/failing/73 unfound run/test.json
index 8d31785..00e708c 100644
--- a/test cases/failing/74 unfound run/test.json
+++ b/test cases/failing/73 unfound run/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/74 unfound run/meson.build:4:0: ERROR: Tried to use non-existing executable 'nonexisting_prog'"
+ "line": "test cases/failing/73 unfound run/meson.build:4:0: ERROR: Tried to use non-existing executable 'nonexisting_prog'"
}
]
}
diff --git a/test cases/failing/75 framework dependency with version/meson.build b/test cases/failing/74 framework dependency with version/meson.build
index ee315eb..ee315eb 100644
--- a/test cases/failing/75 framework dependency with version/meson.build
+++ b/test cases/failing/74 framework dependency with version/meson.build
diff --git a/test cases/failing/75 framework dependency with version/test.json b/test cases/failing/74 framework dependency with version/test.json
index 8a01356..5673758 100644
--- a/test cases/failing/75 framework dependency with version/test.json
+++ b/test cases/failing/74 framework dependency with version/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/75 framework dependency with version/meson.build:8:6: ERROR: Dependency lookup for appleframeworks with method 'framework' failed: Unknown version, but need ['>0']."
+ "line": "test cases/failing/74 framework dependency with version/meson.build:8:6: ERROR: Dependency lookup for appleframeworks with method 'framework' failed: Unknown version, but need ['>0']."
}
]
}
diff --git a/test cases/failing/75 override exe config/foo.c b/test cases/failing/75 override exe config/foo.c
new file mode 100644
index 0000000..03b2213
--- /dev/null
+++ b/test cases/failing/75 override exe config/foo.c
@@ -0,0 +1,3 @@
+int main(void) {
+ return 0;
+}
diff --git a/test cases/failing/76 override exe config/meson.build b/test cases/failing/75 override exe config/meson.build
index a5d0924..a5d0924 100644
--- a/test cases/failing/76 override exe config/meson.build
+++ b/test cases/failing/75 override exe config/meson.build
diff --git a/test cases/failing/76 override exe config/test.json b/test cases/failing/75 override exe config/test.json
index 23b9055..2260dcc 100644
--- a/test cases/failing/76 override exe config/test.json
+++ b/test cases/failing/75 override exe config/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/76 override exe config/meson.build:6:0: ERROR: Program 'bar' was overridden with the compiled executable 'foo' and therefore cannot be used during configuration"
+ "line": "test cases/failing/75 override exe config/meson.build:6:0: ERROR: Program 'bar' was overridden with the compiled executable 'foo' and therefore cannot be used during configuration"
}
]
}
diff --git a/test cases/failing/77 gl dependency with version/meson.build b/test cases/failing/76 gl dependency with version/meson.build
index 0127093..0127093 100644
--- a/test cases/failing/77 gl dependency with version/meson.build
+++ b/test cases/failing/76 gl dependency with version/meson.build
diff --git a/test cases/failing/77 gl dependency with version/test.json b/test cases/failing/76 gl dependency with version/test.json
index 83665cb..3fd6f45 100644
--- a/test cases/failing/77 gl dependency with version/test.json
+++ b/test cases/failing/76 gl dependency with version/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/77 gl dependency with version/meson.build:9:6: ERROR: Dependency lookup for gl with method 'system' failed: Unknown version, but need ['>0']."
+ "line": "test cases/failing/76 gl dependency with version/meson.build:9:6: ERROR: Dependency lookup for gl with method 'system' failed: Unknown version, but need ['>0']."
}
]
}
diff --git a/test cases/failing/78 threads dependency with version/meson.build b/test cases/failing/77 threads dependency with version/meson.build
index 6023fae..6023fae 100644
--- a/test cases/failing/78 threads dependency with version/meson.build
+++ b/test cases/failing/77 threads dependency with version/meson.build
diff --git a/test cases/failing/78 threads dependency with version/test.json b/test cases/failing/77 threads dependency with version/test.json
index ec02c2b..270f7ad 100644
--- a/test cases/failing/78 threads dependency with version/test.json
+++ b/test cases/failing/77 threads dependency with version/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/78 threads dependency with version/meson.build:3:6: ERROR: Dependency lookup for threads with method 'system' failed: Unknown version, but need ['>0']."
+ "line": "test cases/failing/77 threads dependency with version/meson.build:3:6: ERROR: Dependency lookup for threads with method 'system' failed: Unknown version, but need ['>0']."
}
]
}
diff --git a/test cases/failing/79 gtest dependency with version/meson.build b/test cases/failing/78 gtest dependency with version/meson.build
index efbffe1..efbffe1 100644
--- a/test cases/failing/79 gtest dependency with version/meson.build
+++ b/test cases/failing/78 gtest dependency with version/meson.build
diff --git a/test cases/failing/79 gtest dependency with version/test.json b/test cases/failing/78 gtest dependency with version/test.json
index a32a3c2..48789c6 100644
--- a/test cases/failing/79 gtest dependency with version/test.json
+++ b/test cases/failing/78 gtest dependency with version/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/79 gtest dependency with version/meson.build:8:6: ERROR: Dependency 'gtest' is required but not found."
+ "line": "test cases/failing/78 gtest dependency with version/meson.build:8:6: ERROR: Dependency 'gtest' is required but not found."
}
]
}
diff --git a/test cases/failing/80 dub library/meson.build b/test cases/failing/79 dub library/meson.build
index 306d5b3..306d5b3 100644
--- a/test cases/failing/80 dub library/meson.build
+++ b/test cases/failing/79 dub library/meson.build
diff --git a/test cases/failing/80 dub library/test.json b/test cases/failing/79 dub library/test.json
index d3c7d42..9f59604 100644
--- a/test cases/failing/80 dub library/test.json
+++ b/test cases/failing/79 dub library/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/80 dub library/meson.build:11:0: ERROR: Dependency \"dubtestproject\" not found"
+ "line": "test cases/failing/79 dub library/meson.build:11:0: ERROR: Dependency \"dubtestproject\" not found"
}
]
}
diff --git a/test cases/failing/81 dub executable/meson.build b/test cases/failing/80 dub executable/meson.build
index 9a134ea..9a134ea 100644
--- a/test cases/failing/81 dub executable/meson.build
+++ b/test cases/failing/80 dub executable/meson.build
diff --git a/test cases/failing/81 dub executable/test.json b/test cases/failing/80 dub executable/test.json
index 8aefa17..edb74f6 100644
--- a/test cases/failing/81 dub executable/test.json
+++ b/test cases/failing/80 dub executable/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/81 dub executable/meson.build:11:0: ERROR: Dependency \"dubtestproject:test1\" not found"
+ "line": "test cases/failing/80 dub executable/meson.build:11:0: ERROR: Dependency \"dubtestproject:test1\" not found"
}
]
}
diff --git a/test cases/failing/82 dub compiler/meson.build b/test cases/failing/81 dub compiler/meson.build
index 36f1849..36f1849 100644
--- a/test cases/failing/82 dub compiler/meson.build
+++ b/test cases/failing/81 dub compiler/meson.build
diff --git a/test cases/failing/82 dub compiler/test.json b/test cases/failing/81 dub compiler/test.json
index 89d1882..d82984d 100644
--- a/test cases/failing/82 dub compiler/test.json
+++ b/test cases/failing/81 dub compiler/test.json
@@ -13,7 +13,7 @@
},
"stdout": [
{
- "line": "test cases/failing/82 dub compiler/meson.build:17:0: ERROR: Dependency \"dubtestproject:test2\" not found"
+ "line": "test cases/failing/81 dub compiler/meson.build:17:0: ERROR: Dependency \"dubtestproject:test2\" not found"
}
]
}
diff --git a/test cases/failing/83 subproj not-found dep/meson.build b/test cases/failing/82 subproj not-found dep/meson.build
index 2b17df1..2b17df1 100644
--- a/test cases/failing/83 subproj not-found dep/meson.build
+++ b/test cases/failing/82 subproj not-found dep/meson.build
diff --git a/test cases/failing/83 subproj not-found dep/subprojects/somesubproj/meson.build b/test cases/failing/82 subproj not-found dep/subprojects/somesubproj/meson.build
index 5f451f4..5f451f4 100644
--- a/test cases/failing/83 subproj not-found dep/subprojects/somesubproj/meson.build
+++ b/test cases/failing/82 subproj not-found dep/subprojects/somesubproj/meson.build
diff --git a/test cases/failing/83 subproj not-found dep/test.json b/test cases/failing/82 subproj not-found dep/test.json
index 5da4fb9..e551ac7 100644
--- a/test cases/failing/83 subproj not-found dep/test.json
+++ b/test cases/failing/82 subproj not-found dep/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/83 subproj not-found dep/meson.build:2:10: ERROR: Dependency '(anonymous)' is required but not found."
+ "line": "test cases/failing/82 subproj not-found dep/meson.build:2:10: ERROR: Dependency '(anonymous)' is required but not found."
}
]
}
diff --git a/test cases/failing/84 invalid configure file/input b/test cases/failing/83 invalid configure file/input
index e69de29..e69de29 100644
--- a/test cases/failing/84 invalid configure file/input
+++ b/test cases/failing/83 invalid configure file/input
diff --git a/test cases/failing/84 invalid configure file/meson.build b/test cases/failing/83 invalid configure file/meson.build
index 08eca2b..08eca2b 100644
--- a/test cases/failing/84 invalid configure file/meson.build
+++ b/test cases/failing/83 invalid configure file/meson.build
diff --git a/test cases/failing/84 invalid configure file/test.json b/test cases/failing/83 invalid configure file/test.json
index 2b7a745..7982d17 100644
--- a/test cases/failing/84 invalid configure file/test.json
+++ b/test cases/failing/83 invalid configure file/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/84 invalid configure file/meson.build:3:0: ERROR: \"install_dir\" must be specified when \"install\" in a configure_file is true"
+ "line": "test cases/failing/83 invalid configure file/meson.build:3:0: ERROR: \"install_dir\" must be specified when \"install\" in a configure_file is true"
}
]
}
diff --git a/test cases/failing/85 kwarg dupe/meson.build b/test cases/failing/84 kwarg dupe/meson.build
index 06821a2..06821a2 100644
--- a/test cases/failing/85 kwarg dupe/meson.build
+++ b/test cases/failing/84 kwarg dupe/meson.build
diff --git a/test cases/failing/85 kwarg dupe/prog.c b/test cases/failing/84 kwarg dupe/prog.c
index 5f3fbe6..5f3fbe6 100644
--- a/test cases/failing/85 kwarg dupe/prog.c
+++ b/test cases/failing/84 kwarg dupe/prog.c
diff --git a/test cases/failing/85 kwarg dupe/test.json b/test cases/failing/84 kwarg dupe/test.json
index 99719ad..c568d6b 100644
--- a/test cases/failing/85 kwarg dupe/test.json
+++ b/test cases/failing/84 kwarg dupe/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/85 kwarg dupe/meson.build:6:2: ERROR: Entry \"install\" defined both as a keyword argument and in a \"kwarg\" entry."
+ "line": "test cases/failing/84 kwarg dupe/meson.build:6:2: ERROR: Entry \"install\" defined both as a keyword argument and in a \"kwarg\" entry."
}
]
}
diff --git a/test cases/failing/86 missing pch file/meson.build b/test cases/failing/85 missing pch file/meson.build
index a67b798..a67b798 100644
--- a/test cases/failing/86 missing pch file/meson.build
+++ b/test cases/failing/85 missing pch file/meson.build
diff --git a/test cases/failing/86 missing pch file/prog.c b/test cases/failing/85 missing pch file/prog.c
index 11b7fad..11b7fad 100644
--- a/test cases/failing/86 missing pch file/prog.c
+++ b/test cases/failing/85 missing pch file/prog.c
diff --git a/test cases/failing/85 missing pch file/test.json b/test cases/failing/85 missing pch file/test.json
new file mode 100644
index 0000000..05fb7ac
--- /dev/null
+++ b/test cases/failing/85 missing pch file/test.json
@@ -0,0 +1,8 @@
+{
+ "stdout": [
+ {
+ "comment": "literal 'pch/prog.h' from meson.build appears in output, irrespective of os.path.sep",
+ "line": "test cases/failing/85 missing pch file/meson.build:2:6: ERROR: The following PCH files do not exist: pch/prog.h, pch/prog_pch.c"
+ }
+ ]
+}
diff --git a/test cases/failing/86 missing pch file/test.json b/test cases/failing/86 missing pch file/test.json
deleted file mode 100644
index d6a50c2..0000000
--- a/test cases/failing/86 missing pch file/test.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "stdout": [
- {
- "comment": "literal 'pch/prog.h' from meson.build appears in output, irrespective of os.path.sep",
- "line": "test cases/failing/86 missing pch file/meson.build:2:6: ERROR: File pch/prog.h does not exist."
- }
- ]
-}
diff --git a/test cases/failing/87 pch source different folder/include/pch.h b/test cases/failing/86 pch source different folder/include/pch.h
index e69de29..e69de29 100644
--- a/test cases/failing/87 pch source different folder/include/pch.h
+++ b/test cases/failing/86 pch source different folder/include/pch.h
diff --git a/test cases/failing/87 pch source different folder/meson.build b/test cases/failing/86 pch source different folder/meson.build
index d320717..d320717 100644
--- a/test cases/failing/87 pch source different folder/meson.build
+++ b/test cases/failing/86 pch source different folder/meson.build
diff --git a/test cases/failing/87 pch source different folder/prog.c b/test cases/failing/86 pch source different folder/prog.c
index 3fb1295..3fb1295 100644
--- a/test cases/failing/87 pch source different folder/prog.c
+++ b/test cases/failing/86 pch source different folder/prog.c
diff --git a/test cases/failing/87 pch source different folder/src/pch.c b/test cases/failing/86 pch source different folder/src/pch.c
index e69de29..e69de29 100644
--- a/test cases/failing/87 pch source different folder/src/pch.c
+++ b/test cases/failing/86 pch source different folder/src/pch.c
diff --git a/test cases/failing/86 pch source different folder/test.json b/test cases/failing/86 pch source different folder/test.json
new file mode 100644
index 0000000..05b6b78
--- /dev/null
+++ b/test cases/failing/86 pch source different folder/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/86 pch source different folder/meson.build:4:6: ERROR: executable keyword argument \"c_pch\" PCH files must be stored in the same folder."
+ }
+ ]
+}
diff --git a/test cases/failing/87 pch source different folder/test.json b/test cases/failing/87 pch source different folder/test.json
deleted file mode 100644
index 0a9d39d..0000000
--- a/test cases/failing/87 pch source different folder/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/87 pch source different folder/meson.build:4:6: ERROR: PCH files must be stored in the same folder."
- }
- ]
-}
diff --git a/test cases/failing/88 unknown config tool/meson.build b/test cases/failing/87 unknown config tool/meson.build
index 536976e..536976e 100644
--- a/test cases/failing/88 unknown config tool/meson.build
+++ b/test cases/failing/87 unknown config tool/meson.build
diff --git a/test cases/failing/88 unknown config tool/test.json b/test cases/failing/87 unknown config tool/test.json
index 5a53b26..025a5a4 100644
--- a/test cases/failing/88 unknown config tool/test.json
+++ b/test cases/failing/87 unknown config tool/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/88 unknown config tool/meson.build:2:0: ERROR: Dependency \"no-such-config-tool\" not found"
+ "line": "test cases/failing/87 unknown config tool/meson.build:2:0: ERROR: Dependency \"no-such-config-tool\" not found"
}
]
}
diff --git a/test cases/failing/89 custom target install data/Info.plist.cpp b/test cases/failing/88 custom target install data/Info.plist.cpp
index 9ca2fcb..9ca2fcb 100644
--- a/test cases/failing/89 custom target install data/Info.plist.cpp
+++ b/test cases/failing/88 custom target install data/Info.plist.cpp
diff --git a/test cases/failing/89 custom target install data/meson.build b/test cases/failing/88 custom target install data/meson.build
index 00d348c..00d348c 100644
--- a/test cases/failing/89 custom target install data/meson.build
+++ b/test cases/failing/88 custom target install data/meson.build
diff --git a/test cases/failing/89 custom target install data/preproc.py b/test cases/failing/88 custom target install data/preproc.py
index e6eba4c..e6eba4c 100644
--- a/test cases/failing/89 custom target install data/preproc.py
+++ b/test cases/failing/88 custom target install data/preproc.py
diff --git a/test cases/failing/89 custom target install data/test.json b/test cases/failing/88 custom target install data/test.json
index 3364a6a..fe34eba 100644
--- a/test cases/failing/89 custom target install data/test.json
+++ b/test cases/failing/88 custom target install data/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/89 custom target install data/meson.build:11:0: ERROR: install_data argument 1 was of type \"CustomTarget\" but should have been one of: \"str\", \"File\""
+ "line": "test cases/failing/88 custom target install data/meson.build:11:0: ERROR: install_data argument 1 was of type \"CustomTarget\" but should have been one of: \"str\", \"File\""
}
]
}
diff --git a/test cases/failing/90 add dict non string key/meson.build b/test cases/failing/89 add dict non string key/meson.build
index c81a3f7..c81a3f7 100644
--- a/test cases/failing/90 add dict non string key/meson.build
+++ b/test cases/failing/89 add dict non string key/meson.build
diff --git a/test cases/failing/90 add dict non string key/test.json b/test cases/failing/89 add dict non string key/test.json
index 822e09f..ba841f4 100644
--- a/test cases/failing/90 add dict non string key/test.json
+++ b/test cases/failing/89 add dict non string key/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/90 add dict non string key/meson.build:9:9: ERROR: Key must be a string"
+ "line": "test cases/failing/89 add dict non string key/meson.build:9:9: ERROR: Key must be a string"
}
]
}
diff --git a/test cases/failing/91 add dict duplicate keys/meson.build b/test cases/failing/90 add dict duplicate keys/meson.build
index 7a9b523..7a9b523 100644
--- a/test cases/failing/91 add dict duplicate keys/meson.build
+++ b/test cases/failing/90 add dict duplicate keys/meson.build
diff --git a/test cases/failing/91 add dict duplicate keys/test.json b/test cases/failing/90 add dict duplicate keys/test.json
index b6941e7..a816913 100644
--- a/test cases/failing/91 add dict duplicate keys/test.json
+++ b/test cases/failing/90 add dict duplicate keys/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/91 add dict duplicate keys/meson.build:9:27: ERROR: Duplicate dictionary key: myKey"
+ "line": "test cases/failing/90 add dict duplicate keys/meson.build:9:27: ERROR: Duplicate dictionary key: myKey"
}
]
}
diff --git a/test cases/failing/92 no host get_external_property/meson.build b/test cases/failing/91 no host get_external_property/meson.build
index c956754..c956754 100644
--- a/test cases/failing/92 no host get_external_property/meson.build
+++ b/test cases/failing/91 no host get_external_property/meson.build
diff --git a/test cases/failing/92 no host get_external_property/test.json b/test cases/failing/91 no host get_external_property/test.json
index 0f7a803..64f4681 100644
--- a/test cases/failing/92 no host get_external_property/test.json
+++ b/test cases/failing/91 no host get_external_property/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/92 no host get_external_property/meson.build:3:14: ERROR: Unknown property for host machine: nonexisting"
+ "line": "test cases/failing/91 no host get_external_property/meson.build:3:14: ERROR: Unknown property for host machine: nonexisting"
}
]
}
diff --git a/test cases/failing/93 no native compiler/main.c b/test cases/failing/92 no native compiler/main.c
index 9b6bdc2..9b6bdc2 100644
--- a/test cases/failing/93 no native compiler/main.c
+++ b/test cases/failing/92 no native compiler/main.c
diff --git a/test cases/failing/93 no native compiler/meson.build b/test cases/failing/92 no native compiler/meson.build
index f0126ac..f0126ac 100644
--- a/test cases/failing/93 no native compiler/meson.build
+++ b/test cases/failing/92 no native compiler/meson.build
diff --git a/test cases/failing/93 no native compiler/test.json b/test cases/failing/92 no native compiler/test.json
index adf99f1..38a8359 100644
--- a/test cases/failing/93 no native compiler/test.json
+++ b/test cases/failing/92 no native compiler/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/93 no native compiler/meson.build:12:0: ERROR: No host machine compiler for \"main.c\""
+ "line": "test cases/failing/92 no native compiler/meson.build:12:0: ERROR: No host machine compiler for \"main.c\""
}
]
}
diff --git a/test cases/failing/94 subdir parse error/meson.build b/test cases/failing/93 subdir parse error/meson.build
index a744396..a744396 100644
--- a/test cases/failing/94 subdir parse error/meson.build
+++ b/test cases/failing/93 subdir parse error/meson.build
diff --git a/test cases/failing/94 subdir parse error/subdir/meson.build b/test cases/failing/93 subdir parse error/subdir/meson.build
index 3ac5ef9..3ac5ef9 100644
--- a/test cases/failing/94 subdir parse error/subdir/meson.build
+++ b/test cases/failing/93 subdir parse error/subdir/meson.build
diff --git a/test cases/failing/94 subdir parse error/test.json b/test cases/failing/93 subdir parse error/test.json
index 0bf82c1..fa565da 100644
--- a/test cases/failing/94 subdir parse error/test.json
+++ b/test cases/failing/93 subdir parse error/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/94 subdir parse error/subdir/meson.build:1:0: ERROR: Plusassignment target must be an id."
+ "line": "test cases/failing/93 subdir parse error/subdir/meson.build:1:0: ERROR: Plusassignment target must be an id."
}
]
}
diff --git a/test cases/failing/95 invalid option file/meson.build b/test cases/failing/94 invalid option file/meson.build
index b0347c3..b0347c3 100644
--- a/test cases/failing/95 invalid option file/meson.build
+++ b/test cases/failing/94 invalid option file/meson.build
diff --git a/test cases/failing/95 invalid option file/meson_options.txt b/test cases/failing/94 invalid option file/meson_options.txt
index eef843b..eef843b 100644
--- a/test cases/failing/95 invalid option file/meson_options.txt
+++ b/test cases/failing/94 invalid option file/meson_options.txt
diff --git a/test cases/failing/94 invalid option file/test.json b/test cases/failing/94 invalid option file/test.json
new file mode 100644
index 0000000..6e04708
--- /dev/null
+++ b/test cases/failing/94 invalid option file/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/94 invalid option file/meson_options.txt:1:0: ERROR: lexer: unrecognized token \"'\""
+ }
+ ]
+}
diff --git a/test cases/failing/95 invalid option file/test.json b/test cases/failing/95 invalid option file/test.json
deleted file mode 100644
index 073ac67..0000000
--- a/test cases/failing/95 invalid option file/test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "stdout": [
- {
- "line": "test cases/failing/95 invalid option file/meson_options.txt:1:0: ERROR: lexer"
- }
- ]
-}
diff --git a/test cases/failing/96 no lang/main.c b/test cases/failing/95 no lang/main.c
index 9b6bdc2..9b6bdc2 100644
--- a/test cases/failing/96 no lang/main.c
+++ b/test cases/failing/95 no lang/main.c
diff --git a/test cases/failing/96 no lang/meson.build b/test cases/failing/95 no lang/meson.build
index 85c5db8..85c5db8 100644
--- a/test cases/failing/96 no lang/meson.build
+++ b/test cases/failing/95 no lang/meson.build
diff --git a/test cases/failing/96 no lang/test.json b/test cases/failing/95 no lang/test.json
index ab4ca5c..1e5ea2d 100644
--- a/test cases/failing/96 no lang/test.json
+++ b/test cases/failing/95 no lang/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/96 no lang/meson.build:2:0: ERROR: No host machine compiler for 'main.c'"
+ "line": "test cases/failing/95 no lang/meson.build:2:0: ERROR: No host machine compiler for 'main.c'"
}
]
}
diff --git a/test cases/failing/97 no glib-compile-resources/meson.build b/test cases/failing/96 no glib-compile-resources/meson.build
index aae0569..aae0569 100644
--- a/test cases/failing/97 no glib-compile-resources/meson.build
+++ b/test cases/failing/96 no glib-compile-resources/meson.build
diff --git a/test cases/failing/97 no glib-compile-resources/test.json b/test cases/failing/96 no glib-compile-resources/test.json
index f3d92dd..682b2c2 100644
--- a/test cases/failing/97 no glib-compile-resources/test.json
+++ b/test cases/failing/96 no glib-compile-resources/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/97 no glib-compile-resources/meson.build:8:12: ERROR: Program 'glib-compile-resources' not found or not executable"
+ "line": "test cases/failing/96 no glib-compile-resources/meson.build:8:12: ERROR: Program 'glib-compile-resources' not found or not executable"
}
]
}
diff --git a/test cases/failing/97 no glib-compile-resources/trivial.gresource.xml b/test cases/failing/96 no glib-compile-resources/trivial.gresource.xml
index 1447b98..1447b98 100644
--- a/test cases/failing/97 no glib-compile-resources/trivial.gresource.xml
+++ b/test cases/failing/96 no glib-compile-resources/trivial.gresource.xml
diff --git a/test cases/failing/98 number in combo/meson.build b/test cases/failing/97 number in combo/meson.build
index 1a647df..1a647df 100644
--- a/test cases/failing/98 number in combo/meson.build
+++ b/test cases/failing/97 number in combo/meson.build
diff --git a/test cases/failing/98 number in combo/nativefile.ini b/test cases/failing/97 number in combo/nativefile.ini
index 55f10fc..55f10fc 100644
--- a/test cases/failing/98 number in combo/nativefile.ini
+++ b/test cases/failing/97 number in combo/nativefile.ini
diff --git a/test cases/failing/98 number in combo/test.json b/test cases/failing/97 number in combo/test.json
index c1c9484..195f592 100644
--- a/test cases/failing/98 number in combo/test.json
+++ b/test cases/failing/97 number in combo/test.json
@@ -1,5 +1,5 @@
{
"stdout": [
- { "line": "test cases/failing/98 number in combo/meson.build:1:0: ERROR: Value \"1\" (of type \"number\") for option \"optimization\" is not one of the choices. Possible choices are (as string): \"plain\", \"0\", \"g\", \"1\", \"2\", \"3\", \"s\"." }
+ { "line": "test cases/failing/97 number in combo/meson.build:1:0: ERROR: Value \"1\" (of type \"number\") for option \"optimization\" is not one of the choices. Possible choices are (as string): \"plain\", \"0\", \"g\", \"1\", \"2\", \"3\", \"s\"." }
]
}
diff --git a/test cases/failing/99 bool in combo/meson.build b/test cases/failing/98 bool in combo/meson.build
index c5efd67..c5efd67 100644
--- a/test cases/failing/99 bool in combo/meson.build
+++ b/test cases/failing/98 bool in combo/meson.build
diff --git a/test cases/failing/99 bool in combo/meson_options.txt b/test cases/failing/98 bool in combo/meson_options.txt
index 0c8f5de..0c8f5de 100644
--- a/test cases/failing/99 bool in combo/meson_options.txt
+++ b/test cases/failing/98 bool in combo/meson_options.txt
diff --git a/test cases/failing/99 bool in combo/nativefile.ini b/test cases/failing/98 bool in combo/nativefile.ini
index b423957..b423957 100644
--- a/test cases/failing/99 bool in combo/nativefile.ini
+++ b/test cases/failing/98 bool in combo/nativefile.ini
diff --git a/test cases/failing/99 bool in combo/test.json b/test cases/failing/98 bool in combo/test.json
index b3effc7..f3df2bd 100644
--- a/test cases/failing/99 bool in combo/test.json
+++ b/test cases/failing/98 bool in combo/test.json
@@ -1,5 +1,5 @@
{
"stdout": [
- { "line": "test cases/failing/99 bool in combo/meson.build:1:0: ERROR: Value \"True\" (of type \"boolean\") for option \"opt\" is not one of the choices. Possible choices are (as string): \"true\", \"false\"." }
+ { "line": "test cases/failing/98 bool in combo/meson.build:1:0: ERROR: Value \"True\" (of type \"boolean\") for option \"opt\" is not one of the choices. Possible choices are (as string): \"true\", \"false\"." }
]
}
diff --git a/test cases/failing/100 compiler no lang/meson.build b/test cases/failing/99 compiler no lang/meson.build
index 366bbdd..366bbdd 100644
--- a/test cases/failing/100 compiler no lang/meson.build
+++ b/test cases/failing/99 compiler no lang/meson.build
diff --git a/test cases/failing/99 compiler no lang/test.json b/test cases/failing/99 compiler no lang/test.json
new file mode 100644
index 0000000..9b531dd
--- /dev/null
+++ b/test cases/failing/99 compiler no lang/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/99 compiler no lang/meson.build:2:6: ERROR: Tried to access compiler for language \"c\", not specified for host machine."
+ }
+ ]
+}
diff --git a/test cases/format/1 default/indentation.meson b/test cases/format/1 default/indentation.meson
index fe78847..14e1beb 100644
--- a/test cases/format/1 default/indentation.meson
+++ b/test cases/format/1 default/indentation.meson
@@ -92,3 +92,31 @@ if meson.project_version().version_compare('>1.2')
# comment
endif
endif
+
+# Test for trailing comma:
+m = [1, 2, 3]
+n = [
+ 1,
+ 2,
+ 3,
+]
+
+# Overindent regressions (issue #14935)
+if (host_cpu_family == 'x86' and host_system in [
+ 'cygwin',
+ 'windows',
+ 'os2',
+ 'interix',
+])
+ message('hi')
+endif
+
+# Underindent in files() (issue #14998)
+sources = files(
+ # bar
+ # more bar
+ 'bar.c',
+ # foo
+ # more foo
+ 'foo.c',
+)
diff --git a/test cases/format/5 transform/default.expected.meson b/test cases/format/5 transform/default.expected.meson
index 4a9851a..91b3bc2 100644
--- a/test cases/format/5 transform/default.expected.meson
+++ b/test cases/format/5 transform/default.expected.meson
@@ -8,7 +8,7 @@ f = files(options_ini, 'expected.meson', 'source.meson')
# This array should fit on one line
a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
-# This array is too long and should be splitted
+# This array is too long and should be split
a2 = [
2,
3,
@@ -81,4 +81,30 @@ arguments = [
'--x',
]
+# issue #15032
+if true
+ if true
+ if (
+ true
+ and true
+ and false
+ )
+ message('Hello, world!')
+ endif
+ endif
+endif
+
+# issue #15019
+if get_option('client')
+ sources += files(
+ 'pypaste/client/__init__.py',
+ 'pypaste/client/__main__.py',
+ 'pypaste/client/plugins/__init__.py',
+ 'pypaste/client/plugins/pgz/__init__.py',
+ 'pypaste/client/plugins/zen/__init__.py',
+ )
+
+ dependencies += ['requests']
+endif
+
# no final endline
diff --git a/test cases/format/5 transform/muon.expected.meson b/test cases/format/5 transform/muon.expected.meson
index 3b61270..e7d4610 100644
--- a/test cases/format/5 transform/muon.expected.meson
+++ b/test cases/format/5 transform/muon.expected.meson
@@ -8,7 +8,7 @@ f = files('expected.meson', 'source.meson', options_ini)
# This array should fit on one line
a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
-# This array is too long and should be splitted
+# This array is too long and should be split
a2 = [
2,
3,
@@ -81,4 +81,30 @@ arguments = [
'--x',
]
+# issue #15032
+if true
+ if true
+ if (
+ true
+ and true
+ and false
+ )
+ message('Hello, world!')
+ endif
+ endif
+endif
+
+# issue #15019
+if get_option('client')
+ sources += files(
+ 'pypaste/client/__init__.py',
+ 'pypaste/client/__main__.py',
+ 'pypaste/client/plugins/__init__.py',
+ 'pypaste/client/plugins/zen/__init__.py',
+ 'pypaste/client/plugins/pgz/__init__.py',
+ )
+
+ dependencies += ['requests']
+endif
+
# no final endline
diff --git a/test cases/format/5 transform/options.expected.meson b/test cases/format/5 transform/options.expected.meson
index 84917c1..756c226 100644
--- a/test cases/format/5 transform/options.expected.meson
+++ b/test cases/format/5 transform/options.expected.meson
@@ -8,25 +8,14 @@ f = files(options_ini, 'expected.meson', 'source.meson')
# This array should fit on one line
a1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 ]
-# This array is too long and should be splitted
+# This array is too long and should be split
a2 = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ]
# space array
a3 = [ 1, 2, 3 ]
# multi line expression
-is_foo = (
- true
- and false
- and true
- and false
- and true
- and false
- and true
- and false
- and true
- and false
-)
+is_foo = (true and false and true and false and true and false and true and false and true and false)
# no single comma function
fct = files(
@@ -60,4 +49,30 @@ arguments = [
'--x',
]
+# issue #15032
+if true
+ if true
+ if (
+ true
+ and true
+ and false
+ )
+ message('Hello, world!')
+ endif
+ endif
+endif
+
+# issue #15019
+if get_option('client')
+ sources += files(
+ 'pypaste/client/__init__.py',
+ 'pypaste/client/__main__.py',
+ 'pypaste/client/plugins/__init__.py',
+ 'pypaste/client/plugins/pgz/__init__.py',
+ 'pypaste/client/plugins/zen/__init__.py',
+ )
+
+ dependencies += [ 'requests' ]
+endif
+
# no final endline \ No newline at end of file
diff --git a/test cases/format/5 transform/source.meson b/test cases/format/5 transform/source.meson
index a3b326b..a400412 100644
--- a/test cases/format/5 transform/source.meson
+++ b/test cases/format/5 transform/source.meson
@@ -12,25 +12,14 @@ f = files(
# This array should fit on one line
a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
-# This array is too long and should be splitted
+# This array is too long and should be split
a2 = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
# space array
a3 = [ 1, 2, 3 ]
# multi line expression
-is_foo = (
- true
- and false
- and true
- and false
- and true
- and false
- and true
- and false
- and true
- and false
-)
+is_foo = (true and false and true and false and true and false and true and false and true and false)
# no single comma function
fct = files(
@@ -49,4 +38,29 @@ f'This is not a fstring'
arguments = ['a', '--opt_a', 'opt_a_value', 'b', 'c', '--opt_d', '--opt_e', 'opt_e_value',
'--opt_f', '--opt_g', 'opt_g_value', 'other_value', 'again', '--x']
+# issue #15032
+if true
+ if true
+ if (true and
+ true and
+ false
+ )
+ message('Hello, world!')
+ endif
+ endif
+endif
+
+# issue #15019
+if get_option('client')
+ sources += files(
+ 'pypaste/client/__init__.py',
+ 'pypaste/client/__main__.py',
+ 'pypaste/client/plugins/__init__.py',
+ 'pypaste/client/plugins/zen/__init__.py',
+ 'pypaste/client/plugins/pgz/__init__.py'
+ )
+
+ dependencies += ['requests']
+endif
+
# no final endline \ No newline at end of file
diff --git a/test cases/fortran/23 preprocess/main.f90 b/test cases/fortran/23 preprocess/main.f90
index 7cbc11c..8251741 100644
--- a/test cases/fortran/23 preprocess/main.f90
+++ b/test cases/fortran/23 preprocess/main.f90
@@ -1,4 +1,14 @@
#define MYDEF program
MYDEF foo
- write (*,*) 'Hello, world!'
+ character(20) :: str
+#ifdef CORRECT
+ str = 'Hello, ' // 'world!'
+#else
+ str = 'Preprocessing error!'
+#endif
+ if (str /= 'Hello, world!') then
+ print *, 'Preprocessing failed.'
+ error stop 1
+ end if
+ stop 0
end MYDEF foo
diff --git a/test cases/fortran/23 preprocess/meson.build b/test cases/fortran/23 preprocess/meson.build
index b776940..88077d3 100644
--- a/test cases/fortran/23 preprocess/meson.build
+++ b/test cases/fortran/23 preprocess/meson.build
@@ -1,7 +1,12 @@
-project('preprocess', 'fortran')
+project('preprocess', 'fortran', meson_version: '>1.3.2')
fc = meson.get_compiler('fortran')
-pp_files = fc.preprocess('main.f90', output: '@PLAINNAME@')
+pp_files = fc.preprocess(
+ 'main.f90',
+ compile_args: ['-DCORRECT=true'],
+ output: '@PLAINNAME@')
-library('foo', pp_files)
+t = executable('foo', pp_files)
+
+test('check_result', t)
diff --git a/test cases/frameworks/15 llvm/test.json b/test cases/frameworks/15 llvm/test.json
index fa883b1..b9cdc20 100644
--- a/test cases/frameworks/15 llvm/test.json
+++ b/test cases/frameworks/15 llvm/test.json
@@ -2,9 +2,9 @@
"matrix": {
"options": {
"method": [
- { "val": "config-tool", "expect_skip_on_jobname": ["msys2-gcc"] },
- { "val": "cmake", "expect_skip_on_jobname": ["msys2-gcc"] },
- { "val": "combination", "expect_skip_on_jobname": ["msys2-gcc"] }
+ { "val": "config-tool" },
+ { "val": "cmake" },
+ { "val": "combination" }
],
"link-static": [
{ "val": true, "expect_skip_on_jobname": ["arch", "opensuse", "linux-gentoo-gcc"] },
diff --git a/test cases/frameworks/17 mpi/test.json b/test cases/frameworks/17 mpi/test.json
index 3a46657..cbd1686 100644
--- a/test cases/frameworks/17 mpi/test.json
+++ b/test cases/frameworks/17 mpi/test.json
@@ -2,10 +2,8 @@
"matrix": {
"options": {
"method": [
- { "val": "auto",
- "expect_skip_on_jobname": ["ubuntu"] },
- { "val": "pkg-config",
- "expect_skip_on_jobname": ["ubuntu"] },
+ { "val": "auto" },
+ { "val": "pkg-config" },
{ "val": "config-tool",
"expect_skip_on_jobname": ["fedora"] },
{
diff --git a/test cases/frameworks/18 vulkan/meson.build b/test cases/frameworks/18 vulkan/meson.build
index 5cfe89f..ab9f291 100644
--- a/test cases/frameworks/18 vulkan/meson.build
+++ b/test cases/frameworks/18 vulkan/meson.build
@@ -1,6 +1,8 @@
project('vulkan test', 'c')
-vulkan_dep = dependency('vulkan', required : false)
+method = get_option('method')
+
+vulkan_dep = dependency('vulkan', required : false, method : method)
if not vulkan_dep.found()
error('MESON_SKIP_TEST: vulkan not found.')
endif
diff --git a/test cases/frameworks/18 vulkan/meson.options b/test cases/frameworks/18 vulkan/meson.options
new file mode 100644
index 0000000..962fbe2
--- /dev/null
+++ b/test cases/frameworks/18 vulkan/meson.options
@@ -0,0 +1,6 @@
+option(
+ 'method',
+ type : 'combo',
+ choices : ['auto', 'pkg-config', 'system'],
+ value : 'auto',
+)
diff --git a/test cases/frameworks/18 vulkan/test.json b/test cases/frameworks/18 vulkan/test.json
index 66afb97..820f075 100644
--- a/test cases/frameworks/18 vulkan/test.json
+++ b/test cases/frameworks/18 vulkan/test.json
@@ -1,3 +1,14 @@
{
+ "env": {
+ "VULKAN_SDK": "/usr"
+ },
+ "matrix": {
+ "options": {
+ "method": [
+ { "val": "pkg-config" },
+ { "val": "system" }
+ ]
+ }
+ },
"expect_skip_on_jobname": ["azure", "cygwin", "macos", "msys2"]
}
diff --git a/test cases/frameworks/25 hdf5/meson.build b/test cases/frameworks/25 hdf5/meson.build
index 38e0012..095c63f 100644
--- a/test cases/frameworks/25 hdf5/meson.build
+++ b/test cases/frameworks/25 hdf5/meson.build
@@ -28,6 +28,7 @@ test_fortran = add_languages('fortran', required: false)
if test_fortran
cpp = meson.get_compiler('cpp')
fc = meson.get_compiler('fortran')
+ fs = import('fs')
if host_machine.system() == 'darwin' and cpp.get_id() == 'clang' and fc.get_id() == 'gcc'
# Search paths don't work correctly here and -lgfortran doesn't work
@@ -35,6 +36,10 @@ if test_fortran
elif host_machine.system() == 'windows' and cpp.get_id() != 'gcc' and fc.get_id() == 'gcc'
# mixing gfortran with non-gcc doesn't work on windows
test_fortran = false
+ elif fs.is_dir('/ci') and '-I' not in run_command('h5fc', '-show').stdout()
+ # h5fc does not include needed -I flags when HDF5 is built using CMake
+ # https://github.com/HDFGroup/hdf5/issues/5660
+ test_fortran = false
endif
# --- Fortran tests
diff --git a/test cases/frameworks/25 hdf5/test.json b/test cases/frameworks/25 hdf5/test.json
index 2448f57..5243d54 100644
--- a/test cases/frameworks/25 hdf5/test.json
+++ b/test cases/frameworks/25 hdf5/test.json
@@ -2,7 +2,7 @@
"matrix": {
"options": {
"method": [
- { "val": "pkg-config", "expect_skip_on_jobname": ["linux-gentoo-gcc"] },
+ { "val": "pkg-config" },
{ "val": "config-tool" }
]
}
diff --git a/test cases/frameworks/38 gettext extractor/meson.build b/test cases/frameworks/38 gettext extractor/meson.build
index 9a54df5..a31c87d 100644
--- a/test cases/frameworks/38 gettext extractor/meson.build
+++ b/test cases/frameworks/38 gettext extractor/meson.build
@@ -9,6 +9,10 @@ if not find_program('xgettext', required: false).found()
error('MESON_SKIP_TEST xgettext command not found')
endif
+if host_machine.system() == 'darwin'
+ error('MESON_SKIP_TEST test is unstable on macOS for unknown reasons')
+endif
+
i18n = import('i18n')
xgettext_args = ['-ktr', '--add-comments=TRANSLATOR:', '--from-code=UTF-8']
diff --git a/test cases/unit/126 test slice/test.py b/test cases/frameworks/38 gettext extractor/src/lib3/foo.c
index e69de29..e69de29 100644
--- a/test cases/unit/126 test slice/test.py
+++ b/test cases/frameworks/38 gettext extractor/src/lib3/foo.c
diff --git a/test cases/frameworks/38 gettext extractor/src/lib3/meson.build b/test cases/frameworks/38 gettext extractor/src/lib3/meson.build
new file mode 100644
index 0000000..34afeeb
--- /dev/null
+++ b/test cases/frameworks/38 gettext extractor/src/lib3/meson.build
@@ -0,0 +1,4 @@
+fs = import('fs')
+
+foo = fs.copyfile('foo.c')
+i18n.xgettext('test', foo[0])
diff --git a/test cases/frameworks/38 gettext extractor/src/meson.build b/test cases/frameworks/38 gettext extractor/src/meson.build
index 27fc813..b26646c 100644
--- a/test cases/frameworks/38 gettext extractor/src/meson.build
+++ b/test cases/frameworks/38 gettext extractor/src/meson.build
@@ -1,5 +1,6 @@
subdir('lib1')
subdir('lib2')
+subdir('lib3')
main = executable('say', 'main.c', link_with: [lib2], include_directories: lib2_includes)
diff --git a/test cases/frameworks/38 gettext extractor/test.json b/test cases/frameworks/38 gettext extractor/test.json
index c5952ff..032698e 100644
--- a/test cases/frameworks/38 gettext extractor/test.json
+++ b/test cases/frameworks/38 gettext extractor/test.json
@@ -2,5 +2,5 @@
"installed": [
{ "type": "file", "file": "usr/intl/main.pot" }
],
- "expect_skip_on_jobname": ["azure", "cygwin"]
+ "expect_skip_on_jobname": ["azure", "cygwin", "macos"]
}
diff --git a/test cases/frameworks/7 gnome/gdbus/meson.build b/test cases/frameworks/7 gnome/gdbus/meson.build
index fdb3896..22896e0 100644
--- a/test cases/frameworks/7 gnome/gdbus/meson.build
+++ b/test cases/frameworks/7 gnome/gdbus/meson.build
@@ -52,6 +52,23 @@ assert(gdbus_src.length() == 3, 'expected 3 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')
+if not pretend_glib_old and glib.version().version_compare('>=2.75.2')
+ gdbus_src_docs = gnome.gdbus_codegen('generated-gdbus-docs',
+ sources : files('data/com.example.Sample.xml'),
+ interface_prefix : 'com.example.',
+ namespace : 'Sample',
+ docbook : 'generated-gdbus-docs-doc',
+ rst : 'generated-gdbus-docs-rst',
+ markdown : 'generated-gdbus-docs-md',
+ )
+ assert(gdbus_src_docs.length() == 5, 'expected 5 targets')
+ assert(gdbus_src_docs[0].full_path().endswith('.c'), 'expected 1 c source file')
+ assert(gdbus_src_docs[1].full_path().endswith('.h'), 'expected 1 c header file')
+ assert('generated-gdbus-docs-doc' in gdbus_src_docs[2].full_path(), 'expected 1 docbook file')
+ assert('generated-gdbus-docs-rst' in gdbus_src_docs[3].full_path(), 'expected 1 reStructuredText file')
+ assert('generated-gdbus-docs-md' in gdbus_src_docs[4].full_path(), 'expected 1 markdown file')
+endif
+
if not pretend_glib_old and glib.version().version_compare('>=2.51.3')
includes = []
else
diff --git a/test cases/frameworks/7 gnome/meson.build b/test cases/frameworks/7 gnome/meson.build
index f75ca93..37934b7 100644
--- a/test cases/frameworks/7 gnome/meson.build
+++ b/test cases/frameworks/7 gnome/meson.build
@@ -1,4 +1,4 @@
-project('gobject-introspection', 'c', meson_version: '>= 1.2.0')
+project('gobject-introspection', 'c', meson_version: '>= 1.9.0')
copyfile = find_program('copyfile.py')
copyfile_gen = generator(copyfile,
diff --git a/test cases/frameworks/8 flex/meson.build b/test cases/frameworks/8 flex/meson.build
index 55b96dd..0b11070 100644
--- a/test cases/frameworks/8 flex/meson.build
+++ b/test cases/frameworks/8 flex/meson.build
@@ -1,11 +1,16 @@
-project('flex and bison', 'c')
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2024-2025 Intel Corporation
+
+project('flex and bison', 'c', meson_version : '>= 1.10.0')
# The point of this test is that one generator
# may output headers that are necessary to build
# the sources of a different generator.
-flex = find_program('flex', required: false)
-bison = find_program('bison', required: false)
+# TODO: handle win_flex/win_bison
+
+flex = find_program('reflex', 'flex', 'lex', required: false)
+bison = find_program('bison', 'byacc', 'yacc', required: false)
if not flex.found()
error('MESON_SKIP_TEST flex not found.')
@@ -15,22 +20,20 @@ if not bison.found()
error('MESON_SKIP_TEST bison not found.')
endif
-lgen = generator(flex,
-output : '@PLAINNAME@.yy.c',
-arguments : ['-o', '@OUTPUT@', '@INPUT@'])
-
-lfiles = lgen.process('lexer.l')
-
-pgen = generator(bison,
-output : ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
-arguments : ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'])
+codegen = import('unstable-codegen')
+lex = codegen.lex(implementations : ['flex', 'reflex', 'lex'])
+message('lex implementation:', lex.implementation())
+lfiles = lex.generate('lexer.l')
-pfiles = pgen.process('parser.y')
+yacc = codegen.yacc(implementations : ['byacc', 'bison', 'yacc'])
+message('yacc implementation:', yacc.implementation())
+pfiles = yacc.generate('parser.y', header : '@BASENAME@.tab.h')
-e = executable('pgen', 'prog.c',
- lfiles,
- pfiles,
- override_options: 'unity=off')
+e = executable(
+ 'pgen',
+ 'prog.c', lfiles, pfiles,
+ override_options : ['unity=off'],
+)
test('parsertest', e,
args: [meson.current_source_dir() / 'testfile'])
diff --git a/test cases/native/9 override with exe/subprojects/sub/meson.build b/test cases/native/9 override with exe/subprojects/sub/meson.build
index f0343b2..74deaea 100644
--- a/test cases/native/9 override with exe/subprojects/sub/meson.build
+++ b/test cases/native/9 override with exe/subprojects/sub/meson.build
@@ -1,3 +1,11 @@
-project('sub', 'c', version : '1.0')
+project('sub', 'c', version : '1.0', meson_version: '>= 1.9.0')
foobar = executable('foobar', 'foobar.c', native : true)
meson.override_find_program('foobar', foobar)
+
+found_foobar = find_program('foobar')
+if found_foobar.version() != meson.project_version()
+ error('Overriden Executable had incorrect version: got @0@, expected @1@'.format(found_foobar.version(), meson.project_version()))
+endif
+
+test('foobar executable', foobar, args : [ meson.current_build_dir() / 'test-output.c' ])
+test('overriden foobar executable', found_foobar, args : [ meson.current_build_dir() / 'test-output.c' ])
diff --git a/test cases/python/11 script path/gen b/test cases/python/11 script path/gen
new file mode 100755
index 0000000..3d31694
--- /dev/null
+++ b/test cases/python/11 script path/gen
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+
+if __name__ == '__main__':
+ with open('x.c', 'w', encoding='utf-8') as f:
+ f.write('int main() { return 0; }\n')
+ exit(0)
diff --git a/test cases/python/11 script path/meson.build b/test cases/python/11 script path/meson.build
new file mode 100644
index 0000000..c913ca4
--- /dev/null
+++ b/test cases/python/11 script path/meson.build
@@ -0,0 +1,19 @@
+project('11 script path', 'c')
+
+if meson.backend() != 'ninja'
+ error('MESON_SKIP_TEST: Ninja backend required')
+endif
+
+run = find_program('run.py')
+
+gen = find_program('gen')
+
+src = custom_target(
+ 'src',
+ command: [run, gen],
+ output: 'x.c',
+)
+
+exe = executable('e',
+ src,
+)
diff --git a/test cases/python/11 script path/run.py b/test cases/python/11 script path/run.py
new file mode 100755
index 0000000..a8e6011
--- /dev/null
+++ b/test cases/python/11 script path/run.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+import sys
+import subprocess
+
+if __name__ == '__main__':
+ subprocess.check_call(sys.argv[1:])
diff --git a/test cases/python/9 extmodule limited api/meson.build b/test cases/python/9 extmodule limited api/meson.build
index bdf1b7b..9585124 100644
--- a/test cases/python/9 extmodule limited api/meson.build
+++ b/test cases/python/9 extmodule limited api/meson.build
@@ -4,6 +4,10 @@ project('Python limited api', 'c',
py_mod = import('python')
py = py_mod.find_installation()
+if py.get_variable('Py_GIL_DISABLED', 0) == 1 and py.language_version().version_compare('<3.15')
+ error('MESON_SKIP_TEST: Freethreading Python does not support limited API')
+endif
+
ext_mod_limited = py.extension_module('limited',
'limited.c',
limited_api: '3.7',
diff --git a/test cases/rewrite/1 basic/addSrc.json b/test cases/rewrite/1 basic/addSrc.json
index b8bc439..52603f6 100644
--- a/test cases/rewrite/1 basic/addSrc.json
+++ b/test cases/rewrite/1 basic/addSrc.json
@@ -43,6 +43,24 @@
},
{
"type": "target",
+ "target": "trivialprog10",
+ "operation": "src_add",
+ "sources": ["fileA.cpp", "fileB.cpp", "a1.cpp"]
+ },
+ {
+ "type": "target",
+ "target": "trivialprog11",
+ "operation": "src_add",
+ "sources": ["fileA.cpp", "a1.cpp"]
+ },
+ {
+ "type": "target",
+ "target": "trivialprog12",
+ "operation": "src_add",
+ "sources": ["fileA.cpp", "fileB.cpp", "a1.cpp"]
+ },
+ {
+ "type": "target",
"target": "trivialprog0",
"operation": "info"
},
@@ -90,5 +108,25 @@
"type": "target",
"target": "trivialprog9",
"operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog10",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog11",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog12",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "rightName",
+ "operation": "info"
}
]
diff --git a/test cases/rewrite/1 basic/addTgt.json b/test cases/rewrite/1 basic/addTgt.json
index 2f4e7e2..02d600a 100644
--- a/test cases/rewrite/1 basic/addTgt.json
+++ b/test cases/rewrite/1 basic/addTgt.json
@@ -1,7 +1,7 @@
[
{
"type": "target",
- "target": "trivialprog10",
+ "target": "trivialprog13",
"operation": "target_add",
"sources": ["new1.cpp", "new2.cpp"],
"target_type": "shared_library"
diff --git a/test cases/rewrite/1 basic/expected_dag.txt b/test cases/rewrite/1 basic/expected_dag.txt
new file mode 100644
index 0000000..c5025b4
--- /dev/null
+++ b/test cases/rewrite/1 basic/expected_dag.txt
@@ -0,0 +1,129 @@
+Data flowing to FunctionNode(1:0):
+ StringNode(1:8)
+ StringNode(1:23)
+Data flowing to ArrayNode(3:7):
+ StringNode(3:8)
+ StringNode(3:20)
+Data flowing to FunctionNode(4:7):
+ ArrayNode(4:13)
+Data flowing to ArrayNode(4:13):
+ StringNode(4:14)
+ StringNode(4:27)
+Data flowing to IdNode(5:7):
+ ArrayNode(3:7)
+Data flowing to ArrayNode(6:7):
+ IdNode(6:8)
+Data flowing to IdNode(6:8):
+ IdNode(5:7)
+Data flowing to ArithmeticNode(7:7):
+ ArrayNode(7:7)
+ ArrayNode(8:8)
+Data flowing to ArrayNode(7:7):
+ StringNode(7:8)
+ StringNode(7:20)
+Data flowing to ArrayNode(8:8):
+ StringNode(8:9)
+Data flowing to ArrayNode(9:7):
+ StringNode(9:8)
+ StringNode(9:20)
+Data flowing to FunctionNode(10:7):
+ IdNode(10:13)
+Data flowing to IdNode(10:13):
+ ArrayNode(9:7)
+Data flowing to ArrayNode(11:7):
+ StringNode(11:8)
+ StringNode(11:20)
+Data flowing to IdNode(12:7):
+ ArrayNode(11:7)
+Data flowing to ArrayNode(13:7):
+ StringNode(13:8)
+ StringNode(13:21)
+Data flowing to FunctionNode(15:13):
+ StringNode(14:7)
+ StringNode(15:26)
+Data flowing to FunctionNode(20:7):
+ StringNode(20:18)
+ ArithmeticNode(20:34)
+Data flowing to ArithmeticNode(20:34):
+ IdNode(20:34)
+ IdNode(20:41)
+Data flowing to IdNode(20:34):
+ ArrayNode(3:7)
+Data flowing to IdNode(20:41):
+ FunctionNode(4:7)
+Data flowing to FunctionNode(21:7):
+ StringNode(21:18)
+ IdNode(21:34)
+Data flowing to IdNode(21:34):
+ ArrayNode(3:7)
+Data flowing to FunctionNode(22:7):
+ StringNode(22:18)
+ ArrayNode(22:34)
+Data flowing to ArrayNode(22:34):
+ IdNode(22:35)
+Data flowing to IdNode(22:35):
+ FunctionNode(4:7)
+Data flowing to FunctionNode(23:7):
+ StringNode(23:18)
+ ArrayNode(23:34)
+Data flowing to ArrayNode(23:34):
+ StringNode(23:35)
+ StringNode(23:47)
+Data flowing to FunctionNode(24:7):
+ StringNode(24:18)
+ ArrayNode(24:34)
+Data flowing to ArrayNode(24:34):
+ StringNode(24:35)
+ ArrayNode(24:47)
+Data flowing to ArrayNode(24:47):
+ StringNode(24:48)
+Data flowing to FunctionNode(25:7):
+ StringNode(25:18)
+ ArrayNode(25:34)
+Data flowing to ArrayNode(25:34):
+ IdNode(25:35)
+ StringNode(25:41)
+Data flowing to IdNode(25:35):
+ FunctionNode(4:7)
+Data flowing to FunctionNode(26:7):
+ StringNode(26:18)
+ StringNode(26:34)
+ StringNode(26:46)
+Data flowing to FunctionNode(27:7):
+ StringNode(27:18)
+ StringNode(27:34)
+ FunctionNode(27:47)
+ StringNode(27:69)
+Data flowing to FunctionNode(27:47):
+ ArrayNode(3:7)
+ StringNode(27:60)
+Data flowing to FunctionNode(28:7):
+ StringNode(28:18)
+ IdNode(28:34)
+Data flowing to IdNode(28:34):
+ IdNode(5:7)
+Data flowing to FunctionNode(29:0):
+ StringNode(29:11)
+ IdNode(29:27)
+Data flowing to IdNode(29:27):
+ ArrayNode(6:7)
+Data flowing to FunctionNode(30:0):
+ StringNode(30:11)
+ IdNode(30:28)
+Data flowing to IdNode(30:28):
+ ArithmeticNode(7:7)
+Data flowing to FunctionNode(31:0):
+ StringNode(31:11)
+ IdNode(31:28)
+Data flowing to IdNode(31:28):
+ FunctionNode(10:7)
+Data flowing to FunctionNode(32:0):
+ StringNode(32:11)
+ IdNode(32:28)
+Data flowing to IdNode(32:28):
+ IdNode(12:7)
+Data flowing to FunctionNode(33:0):
+ IdNode(33:11)
+ StringNode(33:23)
+Data flowing to IdNode(33:11):
+ FunctionNode(15:13)
diff --git a/test cases/rewrite/1 basic/info.json b/test cases/rewrite/1 basic/info.json
index 0f1a3bd..9977f5a 100644
--- a/test cases/rewrite/1 basic/info.json
+++ b/test cases/rewrite/1 basic/info.json
@@ -53,5 +53,25 @@
"type": "target",
"target": "trivialprog10",
"operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog11",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog12",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog13",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "rightName",
+ "operation": "info"
}
]
diff --git a/test cases/rewrite/1 basic/meson.build b/test cases/rewrite/1 basic/meson.build
index 0f87c45..5fe9527 100644
--- a/test cases/rewrite/1 basic/meson.build
+++ b/test cases/rewrite/1 basic/meson.build
@@ -4,6 +4,16 @@ src1 = ['main.cpp', 'fileA.cpp']
src2 = files(['fileB.cpp', 'fileC.cpp'])
src3 = src1
src4 = [src3]
+src5 = ['main.cpp', 'fileA.cpp']
+src5 += ['fileB.cpp']
+src6 = ['main.cpp', 'fileA.cpp']
+src6 = files(src6)
+src7 = ['main.cpp', 'fileA.cpp']
+src8 = src7
+src7 = ['fileB.cpp', 'fileC.cpp']
+name = 'rightName'
+trickyName = get_variable('name')
+name = 'wrongName'
# Magic comment
@@ -14,6 +24,10 @@ exe3 = executable('trivialprog3', ['main.cpp', 'fileA.cpp'])
exe4 = executable('trivialprog4', ['main.cpp', ['fileA.cpp']])
exe5 = executable('trivialprog5', [src2, 'main.cpp'])
exe6 = executable('trivialprog6', 'main.cpp', 'fileA.cpp')
-exe7 = executable('trivialprog7', 'fileB.cpp', src1, 'fileC.cpp')
+exe7 = executable('trivialprog7', 'fileB.cpp', get_variable('src1'), 'fileC.cpp')
exe8 = executable('trivialprog8', src3)
executable('trivialprog9', src4)
+executable('trivialprog10', src5)
+executable('trivialprog11', src6)
+executable('trivialprog12', src8)
+executable(trickyName, 'main.cpp')
diff --git a/test cases/rewrite/1 basic/rmSrc.json b/test cases/rewrite/1 basic/rmSrc.json
index 2e7447c..de56bbe 100644
--- a/test cases/rewrite/1 basic/rmSrc.json
+++ b/test cases/rewrite/1 basic/rmSrc.json
@@ -1,12 +1,6 @@
[
{
"type": "target",
- "target": "trivialprog1",
- "operation": "src_rm",
- "sources": ["fileA.cpp"]
- },
- {
- "type": "target",
"target": "trivialprog3",
"operation": "src_rm",
"sources": ["fileA.cpp"]
@@ -21,7 +15,7 @@
"type": "target",
"target": "trivialprog5",
"operation": "src_rm",
- "sources": ["fileB.cpp"]
+ "sources": ["main.cpp"]
},
{
"type": "target",
@@ -37,6 +31,18 @@
},
{
"type": "target",
+ "target": "trivialprog10",
+ "operation": "src_rm",
+ "sources": ["fileA.cpp", "fileB.cpp"]
+ },
+ {
+ "type": "target",
+ "target": "trivialprog11",
+ "operation": "src_rm",
+ "sources": ["fileA.cpp"]
+ },
+ {
+ "type": "target",
"target": "trivialprog0",
"operation": "info"
},
@@ -84,5 +90,25 @@
"type": "target",
"target": "trivialprog9",
"operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog10",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog11",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "trivialprog12",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "rightName",
+ "operation": "info"
}
]
diff --git a/test cases/rewrite/1 basic/rmTgt.json b/test cases/rewrite/1 basic/rmTgt.json
index dbaf025..bc6dc30 100644
--- a/test cases/rewrite/1 basic/rmTgt.json
+++ b/test cases/rewrite/1 basic/rmTgt.json
@@ -13,5 +13,10 @@
"type": "target",
"target": "trivialprog9",
"operation": "target_rm"
+ },
+ {
+ "type": "target",
+ "target": "rightName",
+ "operation": "target_rm"
}
]
diff --git a/test cases/rewrite/10 duplicate globals/info.json b/test cases/rewrite/10 duplicate globals/info.json
new file mode 100644
index 0000000..3d15f04
--- /dev/null
+++ b/test cases/rewrite/10 duplicate globals/info.json
@@ -0,0 +1,8 @@
+[
+ {
+ "type": "kwargs",
+ "function": "project",
+ "id": "/",
+ "operation": "info"
+ }
+]
diff --git a/test cases/rewrite/10 duplicate globals/meson.build b/test cases/rewrite/10 duplicate globals/meson.build
new file mode 100644
index 0000000..a9ebf96
--- /dev/null
+++ b/test cases/rewrite/10 duplicate globals/meson.build
@@ -0,0 +1,5 @@
+project('a', 'c', license: 'MIT')
+set_variable(
+ 'z',
+ '-Wl,--version-script,@0@/src/lib.sym'.format(meson.current_source_dir())
+)
diff --git a/test cases/rewrite/3 kwargs/add.json b/test cases/rewrite/3 kwargs/add.json
index 5b3ce0b..94354d1 100644
--- a/test cases/rewrite/3 kwargs/add.json
+++ b/test cases/rewrite/3 kwargs/add.json
@@ -5,7 +5,8 @@
"id": "/",
"operation": "set",
"kwargs": {
- "license": "GPL"
+ "license": "GPL",
+ "license_files": "GPL.txt"
}
},
{
diff --git a/test cases/rewrite/3 kwargs/set.json b/test cases/rewrite/3 kwargs/set.json
index 6ca2ee4..923ce40 100644
--- a/test cases/rewrite/3 kwargs/set.json
+++ b/test cases/rewrite/3 kwargs/set.json
@@ -7,7 +7,8 @@
"kwargs": {
"version": "0.0.2",
"meson_version": "0.50.0",
- "license": ["GPL", "MIT"]
+ "license": ["GPL", "MIT"],
+ "license_files": ["GPL.txt", "MIT.txt"]
}
},
{
diff --git a/test cases/rewrite/8 kwargs dict/info.json b/test cases/rewrite/8 kwargs dict/info.json
new file mode 100644
index 0000000..11a9e1a
--- /dev/null
+++ b/test cases/rewrite/8 kwargs dict/info.json
@@ -0,0 +1,14 @@
+[
+ {
+ "type": "kwargs",
+ "function": "project",
+ "id": "/",
+ "operation": "info"
+ },
+ {
+ "type": "kwargs",
+ "function": "dependency",
+ "id": "dep1",
+ "operation": "info"
+ }
+]
diff --git a/test cases/rewrite/8 kwargs dict/meson.build b/test cases/rewrite/8 kwargs dict/meson.build
new file mode 100644
index 0000000..1c5f25b
--- /dev/null
+++ b/test cases/rewrite/8 kwargs dict/meson.build
@@ -0,0 +1,10 @@
+project(
+ 'rewritetest', 'cpp',
+ version: '0.0.1',
+ default_options: {
+ 'c_std': 'c11',
+ 'cpp_std': 'c++17',
+ },
+)
+
+dep1 = dependency('zlib', required: false, default_options: {'foo': 'bar'})
diff --git a/test cases/rewrite/9 tricky dataflow/addSrc.json b/test cases/rewrite/9 tricky dataflow/addSrc.json
new file mode 100644
index 0000000..17e4292
--- /dev/null
+++ b/test cases/rewrite/9 tricky dataflow/addSrc.json
@@ -0,0 +1,77 @@
+[
+ {
+ "type": "target",
+ "target": "tgt1",
+ "operation": "src_add",
+ "sources": [
+ "new.c"
+ ]
+ },
+ {
+ "type": "target",
+ "target": "tgt2",
+ "operation": "src_add",
+ "sources": [
+ "new.c"
+ ]
+ },
+ {
+ "type": "target",
+ "target": "tgt3",
+ "operation": "src_add",
+ "sources": [
+ "new.c"
+ ]
+ },
+ {
+ "type": "target",
+ "target": "tgt5",
+ "operation": "src_add",
+ "sources": [
+ "new.c"
+ ]
+ },
+ {
+ "type": "target",
+ "target": "tgt6",
+ "operation": "src_add",
+ "sources": [
+ "new.c"
+ ]
+ },
+ {
+ "type": "target",
+ "target": "tgt1",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt2",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt3",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt4",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt5",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt6",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt7",
+ "operation": "info"
+ }
+]
diff --git a/test cases/rewrite/9 tricky dataflow/info.json b/test cases/rewrite/9 tricky dataflow/info.json
new file mode 100644
index 0000000..8d4ac55
--- /dev/null
+++ b/test cases/rewrite/9 tricky dataflow/info.json
@@ -0,0 +1,37 @@
+[
+ {
+ "type": "target",
+ "target": "tgt1",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt2",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt3",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt4",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt5",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt6",
+ "operation": "info"
+ },
+ {
+ "type": "target",
+ "target": "tgt7",
+ "operation": "info"
+ }
+]
diff --git a/test cases/rewrite/9 tricky dataflow/meson.build b/test cases/rewrite/9 tricky dataflow/meson.build
new file mode 100644
index 0000000..0431739
--- /dev/null
+++ b/test cases/rewrite/9 tricky dataflow/meson.build
@@ -0,0 +1,41 @@
+project('rewrite tricky dataflow', 'c')
+
+# Adding a file to `begin` will add this file to the sources of `tgt1`, but
+# not to any other target. But a buggy rewriter might think that adding a file
+# to `begin` will also add this file to `end` and will refuse to add a file to
+# `begin`.
+begin = ['foo.c']
+tgt1 = library('tgt1', begin)
+distraction = executable('distraction', link_with: tgt1)
+
+
+tgt2_srcs = ['foo.c']
+if host_machine.system() == 'windows' # Some condition that cannot be known statically
+ tgt2_srcs += ['bar.c']
+endif
+executable('tgt2', tgt2_srcs)
+
+
+tgt34_srcs = ['foo.c']
+executable('tgt3', tgt34_srcs)
+if host_machine.system() == 'windows'
+ tgt34_srcs += ['bar.c']
+endif
+executable('tgt4', tgt34_srcs)
+
+
+dont_add_here_5 = ['foo.c']
+ct = custom_target('ct', output: 'out.c', input: dont_add_here_5, command: ['placeholder', '@INPUT@', '@OUTPUT@'])
+executable('tgt5', ct)
+
+
+dont_add_here_6 = ['foo.c']
+gen = generator(find_program('cp'), output: '@BASENAME@_copy.c', arguments: ['@INPUT@', '@OUTPUT@'])
+generated = gen.process(dont_add_here_6)
+executable('tgt6', generated)
+
+if false
+ # Should produce a warning, but should not crash
+ var = not_defined_1
+ executable('tgt7', not_defined_2, var)
+endif
diff --git a/test cases/rust/1 basic/meson.build b/test cases/rust/1 basic/meson.build
index f422beb..c8cef92 100644
--- a/test cases/rust/1 basic/meson.build
+++ b/test cases/rust/1 basic/meson.build
@@ -1,11 +1,16 @@
project('rustprog', 'rust', default_options : ['b_ndebug=true'])
e = executable('rust-program', 'prog.rs',
- rust_args : ['-C', 'lto'], # Just a test
install : true
)
test('rusttest', e)
+e = executable('rust-dynamic', 'prog.rs',
+ override_options: {'rust_dynamic_std': true},
+ install : true
+)
+test('rusttest-dynamic', e)
+
subdir('subdir')
# this should fail due to debug_assert
diff --git a/test cases/rust/1 basic/test.json b/test cases/rust/1 basic/test.json
index 95e6ced..3cbdefa 100644
--- a/test cases/rust/1 basic/test.json
+++ b/test cases/rust/1 basic/test.json
@@ -3,6 +3,8 @@
{"type": "exe", "file": "usr/bin/rust-program"},
{"type": "pdb", "file": "usr/bin/rust-program"},
{"type": "exe", "file": "usr/bin/rust-program2"},
- {"type": "pdb", "file": "usr/bin/rust-program2"}
+ {"type": "pdb", "file": "usr/bin/rust-program2"},
+ {"type": "exe", "file": "usr/bin/rust-dynamic"},
+ {"type": "pdb", "file": "usr/bin/rust-dynamic"}
]
}
diff --git a/test cases/rust/12 bindgen/cpp/meson.build b/test cases/rust/12 bindgen/cpp/meson.build
index 8e7103f..6494ebd 100644
--- a/test cases/rust/12 bindgen/cpp/meson.build
+++ b/test cases/rust/12 bindgen/cpp/meson.build
@@ -1,4 +1,4 @@
-# SPDX-license-identifer: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021-2023 Intel Corporation
fs = import('fs')
diff --git a/test cases/rust/12 bindgen/dependencies/internal_dep.h b/test cases/rust/12 bindgen/dependencies/internal_dep.h
index b0629de..f44e278 100644
--- a/test cases/rust/12 bindgen/dependencies/internal_dep.h
+++ b/test cases/rust/12 bindgen/dependencies/internal_dep.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright © 2022 Intel Corporation
-#include "gen.h"
+#include "gen/gen.h"
int64_t add64(const int64_t, const int64_t);
diff --git a/test cases/rust/12 bindgen/dependencies/internal_main.rs b/test cases/rust/12 bindgen/dependencies/internal_main.rs
index 4890b43..8a04601 100644
--- a/test cases/rust/12 bindgen/dependencies/internal_main.rs
+++ b/test cases/rust/12 bindgen/dependencies/internal_main.rs
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/gen_header.py b/test cases/rust/12 bindgen/gen/gen_header.py
index 07b699b..71bf7fa 100644
--- a/test cases/rust/12 bindgen/src/gen_header.py
+++ b/test cases/rust/12 bindgen/gen/gen_header.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# SPDX-license-Identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021-2023 Intel Corporation
import argparse
diff --git a/test cases/rust/12 bindgen/gen/meson.build b/test cases/rust/12 bindgen/gen/meson.build
new file mode 100644
index 0000000..ff5fd84
--- /dev/null
+++ b/test cases/rust/12 bindgen/gen/meson.build
@@ -0,0 +1,13 @@
+gen_h = custom_target(
+ 'gen.h',
+ command : [find_program('gen_header.py'), '@INPUT@', '@OUTPUT@'],
+ output : 'gen.h',
+ input : meson.project_source_root() / 'src/header.h'
+)
+
+gen2_h = custom_target(
+ 'other.h',
+ command : [find_program('gen_header.py'), '@INPUT@', '@OUTPUT@'],
+ output : 'other.h',
+ input : meson.project_source_root() / 'include/other.h'
+)
diff --git a/test cases/rust/12 bindgen/include/other.h b/test cases/rust/12 bindgen/include/other.h
index 3f715f1..df70e00 100644
--- a/test cases/rust/12 bindgen/include/other.h
+++ b/test cases/rust/12 bindgen/include/other.h
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
# pragma once
diff --git a/test cases/rust/12 bindgen/meson.build b/test cases/rust/12 bindgen/meson.build
index 57e44a0..d3dfba9 100644
--- a/test cases/rust/12 bindgen/meson.build
+++ b/test cases/rust/12 bindgen/meson.build
@@ -1,4 +1,4 @@
-# SPDX-license-identifer: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021-2024 Intel Corporation
project(
@@ -66,20 +66,7 @@ rust_bin = executable(
test('main', rust_bin)
# Test a generated header
-gen_h = custom_target(
- 'gen.h',
- command : [find_program('src/gen_header.py'), '@INPUT@', '@OUTPUT@'],
- output : 'gen.h',
- input : 'src/header.h'
-)
-
-gen2_h = custom_target(
- 'other.h',
- command : [find_program('src/gen_header.py'), '@INPUT@', '@OUTPUT@'],
- output : 'other.h',
- input : 'include/other.h'
-)
-
+subdir('gen')
gen_rs = rust.bindgen(
input : [gen_h, gen2_h],
output : 'gen.rs',
diff --git a/test cases/rust/12 bindgen/src/cpp.rs b/test cases/rust/12 bindgen/src/cpp.rs
index 51a594c..4164b38 100644
--- a/test cases/rust/12 bindgen/src/cpp.rs
+++ b/test cases/rust/12 bindgen/src/cpp.rs
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2023 Intel Corporation
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/global.rs b/test cases/rust/12 bindgen/src/global.rs
index 4b70b1e..b3cc545 100644
--- a/test cases/rust/12 bindgen/src/global.rs
+++ b/test cases/rust/12 bindgen/src/global.rs
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2023 Intel Corporation
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/header.h b/test cases/rust/12 bindgen/src/header.h
index 2c03f33..4fa4e8f 100644
--- a/test cases/rust/12 bindgen/src/header.h
+++ b/test cases/rust/12 bindgen/src/header.h
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#pragma once
diff --git a/test cases/rust/12 bindgen/src/header3.h b/test cases/rust/12 bindgen/src/header3.h
index 958a79f..ec2949b 100644
--- a/test cases/rust/12 bindgen/src/header3.h
+++ b/test cases/rust/12 bindgen/src/header3.h
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2023 Red Hat, Inc
#pragma once
diff --git a/test cases/rust/12 bindgen/src/main.rs b/test cases/rust/12 bindgen/src/main.rs
index 3f85e96..ae3850c 100644
--- a/test cases/rust/12 bindgen/src/main.rs
+++ b/test cases/rust/12 bindgen/src/main.rs
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/main2.rs b/test cases/rust/12 bindgen/src/main2.rs
index a3c28d7..0f81ef0 100644
--- a/test cases/rust/12 bindgen/src/main2.rs
+++ b/test cases/rust/12 bindgen/src/main2.rs
@@ -1,5 +1,5 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/main3.rs b/test cases/rust/12 bindgen/src/main3.rs
index fa9d30b..8bb1ba5 100644
--- a/test cases/rust/12 bindgen/src/main3.rs
+++ b/test cases/rust/12 bindgen/src/main3.rs
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2023 Red Hat, Inc
#![allow(non_upper_case_globals)]
diff --git a/test cases/rust/12 bindgen/src/source.c b/test cases/rust/12 bindgen/src/source.c
index d652d28..d968ba4 100644
--- a/test cases/rust/12 bindgen/src/source.c
+++ b/test cases/rust/12 bindgen/src/source.c
@@ -1,4 +1,4 @@
-// SPDX-license-identifer: Apache-2.0
+// SPDX-License-Identifier: Apache-2.0
// Copyright © 2021 Intel Corporation
#include "header.h"
diff --git a/test cases/rust/13 external c dependencies/foo.h b/test cases/rust/13 external c dependencies/foo.h
new file mode 100644
index 0000000..8912552
--- /dev/null
+++ b/test cases/rust/13 external c dependencies/foo.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int foo;
diff --git a/test cases/rust/13 external c dependencies/meson.build b/test cases/rust/13 external c dependencies/meson.build
index c91674c..ff1136b 100644
--- a/test cases/rust/13 external c dependencies/meson.build
+++ b/test cases/rust/13 external c dependencies/meson.build
@@ -1,9 +1,5 @@
project('rust linking to c using dependency', 'c', 'rust')
-if host_machine.system() == 'darwin'
- error('MESON_SKIP_TEST: does not work right on macos, please fix!')
-endif
-
dep_zlib = dependency('zlib', static : get_option('static'), method : get_option('method'), required : false)
if not dep_zlib.found()
error('MESON_SKIP_TEST: Could not find a @0@ zlib'.format(get_option('static') ? 'static' : 'shared'))
@@ -21,3 +17,10 @@ e = executable(
)
test('cdepstest', e)
+
+e2 = executable(
+ 'prog2', 'prog.rs',
+ dependencies : declare_dependency(link_with: l, sources: files('foo.h')),
+)
+
+test('cdepstest', e2)
diff --git a/test cases/rust/14 external libm/meson.build b/test cases/rust/14 external libm/meson.build
index e42eefe..e8c0167 100644
--- a/test cases/rust/14 external libm/meson.build
+++ b/test cases/rust/14 external libm/meson.build
@@ -1,9 +1,5 @@
project('rust linking to libm', 'c', 'rust')
-if host_machine.system() == 'darwin'
- error('MESON_SKIP_TEST: does not work right on macos, please fix!')
-endif
-
cc = meson.get_compiler('c')
dep_m = cc.find_library('m', required : false, static : get_option('static'))
if not dep_m.found()
diff --git a/test cases/rust/19 structured sources/empty.file b/test cases/rust/19 structured sources/empty.file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/rust/19 structured sources/empty.file
diff --git a/test cases/rust/19 structured sources/meson.build b/test cases/rust/19 structured sources/meson.build
index d5b3909..f1925835 100644
--- a/test cases/rust/19 structured sources/meson.build
+++ b/test cases/rust/19 structured sources/meson.build
@@ -41,6 +41,7 @@ test('no-copy', find_program('no_copy_test.py'), args : meson.current_build_dir(
subdir('src2')
executable('copy-no-gen', srcs2)
+executable('copy-no-gen-with-non-rs', srcs2_empty)
m_src = configure_file(
input : 'main-gen-copy.rs',
@@ -56,3 +57,4 @@ m_src2 = configure_file(
executable('gen-no-copy', structured_sources([m_src, m_src2]))
+executable('gen-no-copy-with-non-rust', structured_sources(['empty.file', m_src, m_src2]))
diff --git a/test cases/rust/19 structured sources/src2/meson.build b/test cases/rust/19 structured sources/src2/meson.build
index b4844d2..16ede0d 100644
--- a/test cases/rust/19 structured sources/src2/meson.build
+++ b/test cases/rust/19 structured sources/src2/meson.build
@@ -2,3 +2,7 @@ srcs2 = structured_sources(
['main-unique.rs'],
{'foo': 'foo/mod.rs'},
)
+srcs2_empty = structured_sources(
+ ['../empty.file', 'main-unique.rs'],
+ {'foo': 'foo/mod.rs'},
+)
diff --git a/test cases/rust/2 sharedlib/meson.build b/test cases/rust/2 sharedlib/meson.build
index 295fa04..2380cce 100644
--- a/test cases/rust/2 sharedlib/meson.build
+++ b/test cases/rust/2 sharedlib/meson.build
@@ -1,9 +1,5 @@
project('rust shared library', 'rust', 'c')
-if host_machine.system() == 'darwin'
- error('MESON_SKIP_TEST: does not work right on macos, please fix!')
-endif
-
s = static_library('static', 'value.c')
l = shared_library('stuff', 'stuff.rs', link_whole : s, install : true)
e = executable('prog', 'prog.rs', link_with : l, install : true)
diff --git a/test cases/rust/2 sharedlib/test.json b/test cases/rust/2 sharedlib/test.json
index 585fdeb..11c7f9d 100644
--- a/test cases/rust/2 sharedlib/test.json
+++ b/test cases/rust/2 sharedlib/test.json
@@ -2,7 +2,7 @@
"installed": [
{"type": "exe", "file": "usr/bin/prog"},
{"type": "pdb", "file": "usr/bin/prog"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff.dll"},
{"type": "pdb", "file": "usr/bin/stuff"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff.dll.lib"}
diff --git a/test cases/rust/20 rust and cpp/meson.build b/test cases/rust/20 rust and cpp/meson.build
index c301012..2a545f9 100644
--- a/test cases/rust/20 rust and cpp/meson.build
+++ b/test cases/rust/20 rust and cpp/meson.build
@@ -8,7 +8,7 @@ project(
meson_version : '>= 1.2.0',
)
-cpplib = static_library('cpp', 'lib.cpp')
+cpplib = static_library('cpp-lib', 'lib.cpp')
exe = executable('main', 'main.rs', link_with : cpplib)
test('main', exe)
diff --git a/test cases/rust/22 cargo subproject/meson.build b/test cases/rust/22 cargo subproject/meson.build
index 1b60014..3a79bc8 100644
--- a/test cases/rust/22 cargo subproject/meson.build
+++ b/test cases/rust/22 cargo subproject/meson.build
@@ -1,6 +1,6 @@
project('cargo subproject', 'c')
-foo_dep = dependency('foo-0-rs')
+foo_dep = dependency('foo-0')
exe = executable('app', 'main.c',
dependencies: foo_dep,
)
diff --git a/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml
deleted file mode 100644
index 4b6fa57..0000000
--- a/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[package]
-name = "extra-deps"
-version = "1.0"
diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap
index 99686e9..c399701 100644
--- a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap
+++ b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap
@@ -1,2 +1,5 @@
[wrap-file]
method = cargo
+
+[provide]
+dependency_names = foo-0
diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml
index 8c5351a..9dc3460 100644
--- a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml
+++ b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.0.1"
edition = "2021"
[lib]
-crate-type = ["cdylib"]
+crate-type = ["lib", "staticlib", "cdylib"]
path = "lib.rs"
# This dependency does not exist, verify optional works.
@@ -12,11 +12,6 @@ path = "lib.rs"
optional = true
version = "1.0"
-# This dependency is optional but required for f3 which is on by default.
-[dependencies.extra-dep]
-optional = true
-version = "1.0"
-
[dependencies]
mybar = { version = "0.1", package = "bar", default-features = false }
@@ -27,9 +22,12 @@ features = ["f1"]
[dependencies.libname]
version = "1"
+[target."cfg(unix)".dependencies.unixdep]
+version = "0.1"
+
[features]
default = ["f1"]
f1 = ["f2", "f3"]
f2 = ["f1"]
-f3 = ["mybar/f1", "dep:extra-dep", "notfound?/invalid"]
+f3 = ["mybar/f1", "notfound?/invalid"]
f4 = ["dep:notfound"]
diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs
index 4497dc4..c579815 100644
--- a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs
+++ b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs
@@ -8,6 +8,11 @@ extern "C" {
#[cfg(feature = "foo")]
#[no_mangle]
pub extern "C" fn rust_func() -> i32 {
+ #[cfg(unix)]
+ {
+ extern crate unixdep;
+ assert!(unixdep::only_on_unix() == 0);
+ }
assert!(common::common_func() == 0);
assert!(libothername::stuff() == 42);
let v: i32;
diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build
index 67c7b82..84bd066 100644
--- a/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build
+++ b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build
@@ -1 +1,4 @@
extra_args += ['--cfg', 'feature="foo"']
+if 'f3' in features
+ extra_deps += dependency('extra-dep-1-rs', fallback: 'extra-dep-1-rs')
+endif
diff --git a/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs.wrap
new file mode 100644
index 0000000..99686e9
--- /dev/null
+++ b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method = cargo
diff --git a/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/Cargo.toml
new file mode 100644
index 0000000..d72fb39
--- /dev/null
+++ b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "unixdep"
+version = "0.1"
+edition = "2021"
+
+[lib]
+path = "lib.rs"
diff --git a/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/lib.rs b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/lib.rs
new file mode 100644
index 0000000..a736e8a
--- /dev/null
+++ b/test cases/rust/22 cargo subproject/subprojects/unixdep-0.1-rs/lib.rs
@@ -0,0 +1,8 @@
+pub fn only_on_unix() -> i32 {
+ 0
+}
+
+#[cfg(not(unix))]
+pub fn broken() -> i32 {
+ plop
+}
diff --git a/test cases/rust/27 objects/lib1-dylib.rs b/test cases/rust/27 objects/lib1-dylib.rs
index 1dbf614..b5080e8 100644
--- a/test cases/rust/27 objects/lib1-dylib.rs
+++ b/test cases/rust/27 objects/lib1-dylib.rs
@@ -13,3 +13,13 @@ pub extern "C" fn c_func()
{
unsafe { from_lib1(); }
}
+
+/// ```
+/// #[cfg(not(nodep))] use lib12::rust_func;
+/// #[cfg(nodep)] use lib12_nodep::rust_func;
+/// rust_func();
+/// ```
+pub fn rust_func()
+{
+ unsafe { from_lib1(); }
+}
diff --git a/test cases/rust/27 objects/meson.build b/test cases/rust/27 objects/meson.build
index 78373e4..ad9578a 100644
--- a/test cases/rust/27 objects/meson.build
+++ b/test cases/rust/27 objects/meson.build
@@ -1,4 +1,5 @@
-project('staticlib group', 'c', 'rust', meson_version: '>=1.8.0')
+project('staticlib group', 'c', 'rust', meson_version: '>=1.8.0',
+ default_options: ['rust_std=2021'])
lib1 = static_library('lib1', 'lib1.c')
dep1 = declare_dependency(objects: lib1.extract_all_objects(recursive: false))
@@ -26,3 +27,14 @@ lib12 = shared_library('dylib2objs_as_dep', 'lib1-dylib.rs',
dependencies: dep2,
rust_abi: 'c')
executable('dylib_as_dep', 'main.rs', link_with: lib12)
+
+lib12_rlib = static_library('lib12', 'lib1-dylib.rs',
+ dependencies: dep2)
+
+rust = import('rust')
+rust.doctest('rlib with dep', lib12_rlib)
+
+lib12_rlib_nodep = static_library('lib12_nodep', 'lib1-dylib.rs')
+rust.doctest('rlib with dep in tests', lib12_rlib_nodep,
+ rust_args: ['--cfg', 'nodep'],
+ dependencies: dep2)
diff --git a/test cases/rust/28 mixed/hello.rs b/test cases/rust/28 mixed/hello.rs
new file mode 100644
index 0000000..549fa94
--- /dev/null
+++ b/test cases/rust/28 mixed/hello.rs
@@ -0,0 +1,6 @@
+#![no_main]
+
+#[no_mangle]
+pub extern "C" fn hello_rust() {
+ println!("hello world");
+}
diff --git a/test cases/rust/28 mixed/main.cc b/test cases/rust/28 mixed/main.cc
new file mode 100644
index 0000000..10daae4
--- /dev/null
+++ b/test cases/rust/28 mixed/main.cc
@@ -0,0 +1,5 @@
+#include <iostream>
+
+extern "C" void hello_rust(void);
+
+int main() { std::cout << "This is C++!\n"; hello_rust(); }
diff --git a/test cases/rust/28 mixed/meson.build b/test cases/rust/28 mixed/meson.build
new file mode 100644
index 0000000..fac3d46
--- /dev/null
+++ b/test cases/rust/28 mixed/meson.build
@@ -0,0 +1,12 @@
+# use C++ to make it harder
+project('mixed', ['cpp', 'rust'])
+
+e1 = executable('mixed', 'hello.rs', 'main.cc')
+e2 = executable('mixed-structured', structured_sources('hello.rs'), 'main.cc')
+
+hello2 = import('fs').copyfile('hello.rs', 'hello2.rs')
+e3 = executable('mixed-structured-gen', structured_sources(hello2), 'main.cc')
+
+test('mixed', e1)
+test('mixed-structured', e2)
+test('mixed-structured-gen', e3)
diff --git a/test cases/rust/29 self-contained/lib.rs b/test cases/rust/29 self-contained/lib.rs
new file mode 100644
index 0000000..f8a8bf2
--- /dev/null
+++ b/test cases/rust/29 self-contained/lib.rs
@@ -0,0 +1,4 @@
+#[unsafe(export_name = "hello")]
+pub extern "C" fn hello() {
+ println!("Hello, world!");
+}
diff --git a/test cases/rust/29 self-contained/main.c b/test cases/rust/29 self-contained/main.c
new file mode 100644
index 0000000..11bedf5
--- /dev/null
+++ b/test cases/rust/29 self-contained/main.c
@@ -0,0 +1,6 @@
+extern void hello(void);
+
+int main(void)
+{
+ hello();
+}
diff --git a/test cases/rust/29 self-contained/meson.build b/test cases/rust/29 self-contained/meson.build
new file mode 100644
index 0000000..25de68f
--- /dev/null
+++ b/test cases/rust/29 self-contained/meson.build
@@ -0,0 +1,17 @@
+project('self-contained', meson_version : '>= 1.3.0')
+
+if not add_languages('c', native : false, required : false)
+ error('MESON_SKIP_TEST clang not installed')
+endif
+
+if not add_languages('rust', native : false, required : false)
+ error('MESON_SKIP_TEST Rust x86_64-unknown-linux-musl target not installed')
+endif
+
+if meson.get_compiler('c').find_library('libunwind', required : false).found()
+ error('MESON_SKIP_TEST libunwind is installed globally')
+endif
+
+lib = static_library('rust', 'lib.rs', rust_abi : 'c')
+
+executable('exe', 'main.c', link_with : lib)
diff --git a/test cases/rust/29 self-contained/test.json b/test cases/rust/29 self-contained/test.json
new file mode 100644
index 0000000..fccefd6
--- /dev/null
+++ b/test cases/rust/29 self-contained/test.json
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "CC": "clang -target x86_64-unknown-linux-musl",
+ "RUSTC": "rustc --target x86_64-unknown-linux-musl"
+ }
+}
diff --git a/test cases/rust/3 staticlib/meson.build b/test cases/rust/3 staticlib/meson.build
index cf8e103..c577fab 100644
--- a/test cases/rust/3 staticlib/meson.build
+++ b/test cases/rust/3 staticlib/meson.build
@@ -5,3 +5,6 @@ v = static_library('value', 'value.c')
l = static_library('stuff', 'stuff.rs', link_whole : [o, v], install : true)
e = executable('prog', 'prog.rs', link_with : l, install : true)
test('linktest', e)
+
+l = static_library('stuff2', 'stuff2.rs', link_with : [o, v])
+e = executable('prog2', 'prog2.rs', link_with : l)
diff --git a/test cases/rust/3 staticlib/prog2.rs b/test cases/rust/3 staticlib/prog2.rs
new file mode 100644
index 0000000..9c25c77
--- /dev/null
+++ b/test cases/rust/3 staticlib/prog2.rs
@@ -0,0 +1,5 @@
+extern crate stuff2;
+
+fn main() {
+ println!("printing: {}", stuff2::explore());
+}
diff --git a/test cases/rust/3 staticlib/stuff2.rs b/test cases/rust/3 staticlib/stuff2.rs
new file mode 100644
index 0000000..5e0167a
--- /dev/null
+++ b/test cases/rust/3 staticlib/stuff2.rs
@@ -0,0 +1,14 @@
+#![crate_name = "stuff2"]
+
+extern crate other;
+
+extern "C" {
+ fn c_explore_value() -> i32;
+}
+
+pub fn explore(
+) -> String {
+ unsafe {
+ other::explore(c_explore_value())
+ }
+}
diff --git a/test cases/rust/30 rlib link subdir/cdep/f.c b/test cases/rust/30 rlib link subdir/cdep/f.c
new file mode 100644
index 0000000..d64ff8c
--- /dev/null
+++ b/test cases/rust/30 rlib link subdir/cdep/f.c
@@ -0,0 +1 @@
+int f() { return 42; }
diff --git a/test cases/rust/30 rlib link subdir/cdep/meson.build b/test cases/rust/30 rlib link subdir/cdep/meson.build
new file mode 100644
index 0000000..b7fd7e2
--- /dev/null
+++ b/test cases/rust/30 rlib link subdir/cdep/meson.build
@@ -0,0 +1 @@
+cdep = static_library('f', 'f.c')
diff --git a/test cases/rust/30 rlib link subdir/f-sys.rs b/test cases/rust/30 rlib link subdir/f-sys.rs
new file mode 100644
index 0000000..8897120
--- /dev/null
+++ b/test cases/rust/30 rlib link subdir/f-sys.rs
@@ -0,0 +1,3 @@
+extern "C" {
+ pub fn f() -> ::std::os::raw::c_int;
+}
diff --git a/test cases/rust/30 rlib link subdir/main.rs b/test cases/rust/30 rlib link subdir/main.rs
new file mode 100644
index 0000000..8ae3c32
--- /dev/null
+++ b/test cases/rust/30 rlib link subdir/main.rs
@@ -0,0 +1 @@
+fn main() { assert_eq!(unsafe { ::f_sys::f() }, 42); }
diff --git a/test cases/rust/30 rlib link subdir/meson.build b/test cases/rust/30 rlib link subdir/meson.build
new file mode 100644
index 0000000..89acff2
--- /dev/null
+++ b/test cases/rust/30 rlib link subdir/meson.build
@@ -0,0 +1,21 @@
+project(
+ 'rlib-link-subdir',
+ ['c', 'rust'],
+ default_options: ['rust_std=2021']
+)
+
+subdir('cdep')
+f_sys = static_library(
+ 'f_sys',
+ 'f-sys.rs',
+ link_with: cdep,
+ rust_abi: 'rust',
+)
+
+exe = executable(
+ 'main',
+ 'main.rs',
+ link_with: f_sys,
+)
+
+test('basic', exe)
diff --git a/test cases/rust/31 both machines/lib.rs b/test cases/rust/31 both machines/lib.rs
new file mode 100644
index 0000000..4325933
--- /dev/null
+++ b/test cases/rust/31 both machines/lib.rs
@@ -0,0 +1,3 @@
+pub fn answer() -> u32 {
+ 42
+}
diff --git a/test cases/rust/31 both machines/meson.build b/test cases/rust/31 both machines/meson.build
new file mode 100644
index 0000000..511518a
--- /dev/null
+++ b/test cases/rust/31 both machines/meson.build
@@ -0,0 +1,36 @@
+project(
+ 'testproj',
+ 'rust',
+ version : '0.1',
+ meson_version : '>= 1.9.0',
+ default_options : ['rust_std=2021'],
+)
+
+lib_host = static_library(
+ 'lib',
+ 'lib.rs',
+ rust_abi: 'rust'
+)
+
+lib_build = static_library(
+ 'lib+build',
+ 'lib.rs',
+ rust_abi: 'rust',
+ native: true,
+)
+
+exe_host = executable(
+ 'test-host',
+ 'test.rs',
+ link_with: lib_host,
+)
+
+exe_build = executable(
+ 'test-build',
+ 'test.rs',
+ link_with: lib_build,
+ native: true,
+)
+
+test('host', exe_host)
+test('build', exe_build)
diff --git a/test cases/rust/31 both machines/test.rs b/test cases/rust/31 both machines/test.rs
new file mode 100644
index 0000000..c9679d4
--- /dev/null
+++ b/test cases/rust/31 both machines/test.rs
@@ -0,0 +1,5 @@
+use lib::answer;
+
+fn main() {
+ println!("The answer is {}.\n", answer());
+}
diff --git a/test cases/rust/32 cargo workspace/meson.build b/test cases/rust/32 cargo workspace/meson.build
new file mode 100644
index 0000000..4d69fdf
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/meson.build
@@ -0,0 +1,13 @@
+project('cargo workspace', 'c', 'rust')
+
+foo_rs = dependency('foo-1-rs')
+e = executable('test-foo-1-rs', 'test_foo_1.rs',
+ dependencies: [foo_rs],
+)
+test('test-foo-1-rs', e)
+
+foo_cdylib = dependency('foo-1')
+e = executable('test-foo-1-cdylib', 'test_foo_1.c',
+ dependencies: [foo_cdylib],
+)
+test('test-foo-1-rs', e)
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo.wrap b/test cases/rust/32 cargo workspace/subprojects/foo.wrap
new file mode 100644
index 0000000..3d21ca7
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo.wrap
@@ -0,0 +1,5 @@
+[wrap-file]
+method=cargo
+
+[provide]
+dependency_names=foo-1-rs
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/Cargo.toml b/test cases/rust/32 cargo workspace/subprojects/foo/Cargo.toml
new file mode 100644
index 0000000..52f28d8
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/Cargo.toml
@@ -0,0 +1,15 @@
+[workspace]
+resolver = "2"
+members = [
+ "src/foo",
+ "src/member1"
+]
+default-members = ["src/foo"]
+
+[workspace.package]
+edition = "2021"
+version = "1.0.0"
+
+[workspace.dependencies]
+member1 = { path="./src/member1" }
+member2 = { path="subprojects/member2-1.0", features = ["f1"] }
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/Cargo.toml b/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/Cargo.toml
new file mode 100644
index 0000000..52b39e9
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "foo"
+edition.workspace = true
+version.workspace = true
+
+[lib]
+crate-type = ["lib", "cdylib"]
+
+[dependencies]
+m1 = { path="../member1", package="member1" }
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/src/lib.rs b/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/src/lib.rs
new file mode 100644
index 0000000..65803da
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/src/foo/src/lib.rs
@@ -0,0 +1,6 @@
+extern crate m1;
+
+#[no_mangle]
+pub extern "C" fn foo() -> i32 {
+ m1::member1() + 1
+}
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/src/lib.rs b/test cases/rust/32 cargo workspace/subprojects/foo/src/lib.rs
new file mode 100644
index 0000000..c377381
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/src/lib.rs
@@ -0,0 +1,3 @@
+pub fn foo() -> i32 {
+ member1::member1() + 1
+}
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/Cargo.toml b/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/Cargo.toml
new file mode 100644
index 0000000..2c52137
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "member1"
+edition.workspace = true
+version.workspace = true
+
+[dependencies]
+member2 = { workspace = true, features=["f2"] }
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/src/lib.rs b/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/src/lib.rs
new file mode 100644
index 0000000..d4778ce
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/src/member1/src/lib.rs
@@ -0,0 +1,5 @@
+extern crate member2;
+
+pub fn member1() -> i32 {
+ member2::member2() + 1
+}
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1-rs.wrap b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1-rs.wrap
new file mode 100644
index 0000000..57ae2fa
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1-rs.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory=member2-1.0
+method=cargo
+
+[provide]
+dependency_names=member2-1-rs
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/Cargo.toml b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/Cargo.toml
new file mode 100644
index 0000000..059120a
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "member2"
+edition.workspace = true
+version.workspace = true
+
+[features]
+default = []
+f1 = []
+f2 = []
diff --git a/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/src/lib.rs b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/src/lib.rs
new file mode 100644
index 0000000..75703a9
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/foo/subprojects/member2-1.0/src/lib.rs
@@ -0,0 +1,5 @@
+#[cfg(feature = "f1")]
+#[cfg(feature = "f2")]
+pub fn member2() -> i32 {
+ 1
+}
diff --git a/test cases/rust/32 cargo workspace/subprojects/member2.wrap b/test cases/rust/32 cargo workspace/subprojects/member2.wrap
new file mode 100644
index 0000000..3d43da6
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/subprojects/member2.wrap
@@ -0,0 +1,2 @@
+[wrap-redirect]
+filename = foo/subprojects/member2-1-rs.wrap
diff --git a/test cases/rust/32 cargo workspace/test_foo_1.c b/test cases/rust/32 cargo workspace/test_foo_1.c
new file mode 100644
index 0000000..d5f12a6
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/test_foo_1.c
@@ -0,0 +1,5 @@
+extern int foo(void);
+
+int main(void) {
+ return foo() == 3 ? 0 : 1;
+}
diff --git a/test cases/rust/32 cargo workspace/test_foo_1.rs b/test cases/rust/32 cargo workspace/test_foo_1.rs
new file mode 100644
index 0000000..7d1ebb7
--- /dev/null
+++ b/test cases/rust/32 cargo workspace/test_foo_1.rs
@@ -0,0 +1,5 @@
+extern crate foo;
+
+pub fn main() {
+ assert!(foo::foo() == 3);
+}
diff --git a/test cases/rust/33 cargo multiple versions/meson.build b/test cases/rust/33 cargo multiple versions/meson.build
new file mode 100644
index 0000000..dd11508
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/meson.build
@@ -0,0 +1,3 @@
+project('cargo multiple crate versions')
+
+subproject('main')
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs.wrap b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs.wrap
new file mode 100644
index 0000000..5133599
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=cargo
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/Cargo.toml b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/Cargo.toml
new file mode 100644
index 0000000..41de669
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "foo"
+version = "1.0"
+
+[lib]
+path = "lib.rs"
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/lib.rs b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/lib.rs
new file mode 100644
index 0000000..f3662d5
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-1-rs/lib.rs
@@ -0,0 +1,3 @@
+pub fn foo1() -> i32 {
+ 1
+}
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs.wrap b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs.wrap
new file mode 100644
index 0000000..5133599
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=cargo
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/Cargo.toml b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/Cargo.toml
new file mode 100644
index 0000000..ce55217
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "foo"
+version = "2.0"
+
+[lib]
+path = "lib.rs"
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/lib.rs b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/lib.rs
new file mode 100644
index 0000000..afbf386
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/foo-2-rs/lib.rs
@@ -0,0 +1,3 @@
+pub fn foo2() -> i32 {
+ 1
+}
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/main.wrap b/test cases/rust/33 cargo multiple versions/subprojects/main.wrap
new file mode 100644
index 0000000..5133599
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/main.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=cargo
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/main/Cargo.toml b/test cases/rust/33 cargo multiple versions/subprojects/main/Cargo.toml
new file mode 100644
index 0000000..0401a59
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/main/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "main"
+
+[dependencies]
+foo1 = { package="foo", version="1" }
+foo2 = { package="foo", version="2" }
+
+[lib]
+path = "lib.rs"
diff --git a/test cases/rust/33 cargo multiple versions/subprojects/main/lib.rs b/test cases/rust/33 cargo multiple versions/subprojects/main/lib.rs
new file mode 100644
index 0000000..5029a27
--- /dev/null
+++ b/test cases/rust/33 cargo multiple versions/subprojects/main/lib.rs
@@ -0,0 +1,6 @@
+extern crate foo1;
+extern crate foo2;
+
+pub fn func() -> i32 {
+ foo1::foo1() + foo2::foo2()
+}
diff --git a/test cases/rust/4 polyglot/meson.build b/test cases/rust/4 polyglot/meson.build
index b2fd8f9..29af73f 100644
--- a/test cases/rust/4 polyglot/meson.build
+++ b/test cases/rust/4 polyglot/meson.build
@@ -1,9 +1,5 @@
project('rust and c polyglot executable', 'c', 'rust')
-if host_machine.system() == 'darwin'
- error('MESON_SKIP_TEST: does not work right on macos, please fix!')
-endif
-
cc = meson.get_compiler('c')
# Test all combinations of crate and target types.
@@ -22,7 +18,7 @@ foreach crate_type : ['lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro
# Note: in the both_libraries() case it is possible that the static part
# is still being built because the shared part raised an error but we
# don't rollback correctly.
- testcase expect_error('(Crate type .* invalid for .*)|(.*must be one of.*not invalid)', how: 're')
+ testcase expect_error('(Crate type .* must be .*)|(.*must be one of.*not invalid)', how: 're')
build_target(name, src,
target_type: target_type,
rust_crate_type: crate_type,
diff --git a/test cases/rust/4 polyglot/stuff.rs b/test cases/rust/4 polyglot/stuff.rs
index 30c3a36..e5d9386 100644
--- a/test cases/rust/4 polyglot/stuff.rs
+++ b/test cases/rust/4 polyglot/stuff.rs
@@ -1,4 +1,4 @@
#[no_mangle]
-pub extern fn f() {
+pub extern "C" fn f() {
println!("Hello from Rust!");
}
diff --git a/test cases/rust/4 polyglot/test.json b/test cases/rust/4 polyglot/test.json
index d963ad8..443bf6c 100644
--- a/test cases/rust/4 polyglot/test.json
+++ b/test cases/rust/4 polyglot/test.json
@@ -3,7 +3,7 @@
{"type": "exe", "file": "usr/bin/prog-stuff_clib_both_libraries"},
{"type": "pdb", "file": "usr/bin/prog-stuff_clib_both_libraries"},
{"type": "pdb", "file": "usr/bin/stuff_clib_both_libraries.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_clib_both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_clib_both_libraries"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_clib_both_libraries.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_clib_both_libraries.dll.lib"},
{"type": "file", "file": "usr/lib/libstuff_clib_both_libraries.a"},
@@ -11,7 +11,7 @@
{"type": "exe", "file": "usr/bin/prog-stuff_clib_shared_library"},
{"type": "pdb", "file": "usr/bin/prog-stuff_clib_shared_library"},
{"type": "pdb", "file": "usr/bin/stuff_clib_shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_clib_shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_clib_shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_clib_shared_library.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_clib_shared_library.dll.lib"},
@@ -22,7 +22,7 @@
{"type": "exe", "file": "usr/bin/prog-stuff_cdylib_shared_library"},
{"type": "pdb", "file": "usr/bin/prog-stuff_cdylib_shared_library"},
{"type": "pdb", "file": "usr/bin/stuff_cdylib_shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_cdylib_shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_cdylib_shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_cdylib_shared_library.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_cdylib_shared_library.dll.lib"},
@@ -31,7 +31,7 @@
{"type": "file", "file": "usr/lib/libstuff_staticlib_static_library.a"},
{"type": "pdb", "file": "usr/bin/stuff__both_libraries.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff__both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff__both_libraries"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libstuff__both_libraries.rlib"},
{"type": "file", "platform": "msvc", "file": "usr/lib/libstuff__both_libraries.rlib"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff__both_libraries.dll"},
@@ -41,13 +41,13 @@
{"type": "file", "platform": "msvc", "file": "usr/lib/libstuff__static_library.rlib"},
{"type": "pdb", "file": "usr/bin/stuff_proc_macro_shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_proc_macro_shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_proc_macro_shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_proc_macro_shared_library.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_proc_macro_shared_library.dll.lib"},
{"type": "pdb", "file": "usr/bin/stuff_lib_both_libraries.pdb"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_lib_both_libraries.rlib"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_lib_both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_lib_both_libraries"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_lib_both_libraries.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/libstuff_lib_both_libraries.rlib"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_lib_both_libraries.dll.lib"},
@@ -59,33 +59,32 @@
{"type": "file", "platform": "msvc", "file": "usr/lib/libstuff_lib_static_library.rlib"},
{"type": "pdb", "file": "usr/bin/stuff__shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff__shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff__shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff__shared_library.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff__shared_library.dll.lib"},
{"type": "pdb", "file": "usr/bin/stuff_lib_shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_lib_shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_lib_shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_lib_shared_library.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_lib_shared_library.dll.lib"},
{"type": "pdb", "file": "usr/bin/stuff_dylib_shared_library.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_dylib_shared_library.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_dylib_shared_library"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_dylib_shared_library.dll.lib"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_dylib_shared_library.dll"},
{"type": "pdb", "file": "usr/bin/stuff_cdylib_both_libraries.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_cdylib_both_libraries.so"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_cdylib_both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_cdylib_both_libraries"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_cdylib_both_libraries.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_cdylib_both_libraries.dll.lib"},
{"type": "pdb", "file": "usr/bin/stuff_proc_macro_both_libraries.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_proc_macro_both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_proc_macro_both_libraries"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_proc_macro_both_libraries.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_proc_macro_both_libraries.dll.lib"},
{"type": "pdb", "file": "usr/bin/stuff_dylib_both_libraries.pdb"},
- {"type": "file", "platform": "gcc", "file": "usr/lib/libstuff_dylib_both_libraries.so"},
+ {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff_dylib_both_libraries"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff_dylib_both_libraries.dll"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff_dylib_both_libraries.dll.lib"}
]
diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build
index 0fa2fa8..81045f2 100644
--- a/test cases/rust/9 unit tests/meson.build
+++ b/test cases/rust/9 unit tests/meson.build
@@ -40,6 +40,12 @@ if rustdoc.found()
protocol : 'rust',
suite : ['doctests'],
)
+
+ doclib = shared_library('rust_doc_lib', ['doctest1.rs'], build_by_default : false)
+ rust.doctest('rust shared doctests', doclib,
+ protocol : 'rust',
+ suite : ['doctests'],
+ )
endif
exe = executable('rust_exe', ['test2.rs', 'test.rs'], build_by_default : false)
diff --git a/test cases/snippets/1 symbol visibility header/main-static-only.c b/test cases/snippets/1 symbol visibility header/main-static-only.c
new file mode 100644
index 0000000..26f5b06
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/main-static-only.c
@@ -0,0 +1,3 @@
+#include <mylib/lib-static-only.h>
+
+int main(void) { return do_stuff(); }
diff --git a/test cases/snippets/1 symbol visibility header/main.c b/test cases/snippets/1 symbol visibility header/main.c
new file mode 100644
index 0000000..1d32f62
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/main.c
@@ -0,0 +1,3 @@
+#include <mylib/lib.h>
+
+int main(void) { return do_stuff(); }
diff --git a/test cases/snippets/1 symbol visibility header/meson.build b/test cases/snippets/1 symbol visibility header/meson.build
new file mode 100644
index 0000000..9a6c27a
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/meson.build
@@ -0,0 +1,13 @@
+project('symbol visibility header', 'c')
+
+sta_dep = dependency('mylib-sta', fallback: 'sub')
+exe = executable('exe-sta', 'main.c', dependencies: sta_dep)
+test('test-sta', exe)
+
+sha_dep = dependency('mylib-sha', fallback: 'sub')
+exe = executable('exe-sha', 'main.c', dependencies: sha_dep)
+test('test-sha', exe)
+
+static_only_dep = dependency('static-only', fallback: 'sub')
+exe = executable('exe-static-only', 'main-static-only.c', dependencies: static_only_dep)
+test('test-static-only', exe)
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/meson.build b/test cases/snippets/1 symbol visibility header/subprojects/sub/meson.build
new file mode 100644
index 0000000..83b7970
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/meson.build
@@ -0,0 +1,5 @@
+project('my lib', 'c')
+
+pkg = import('pkgconfig')
+
+subdir('mylib')
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.c b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.c
new file mode 100644
index 0000000..b7662cd
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.c
@@ -0,0 +1,3 @@
+#include "lib-static-only.h"
+
+int do_stuff(void) { return 0; }
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.h b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.h
new file mode 100644
index 0000000..ebe4524
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib-static-only.h
@@ -0,0 +1,3 @@
+#include <mylib/apiconfig-static-only.h>
+
+MY_LIB_API int do_stuff(void);
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.c b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.c
new file mode 100644
index 0000000..1171720
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.c
@@ -0,0 +1,3 @@
+#include "lib.h"
+
+int do_stuff(void) { return 0; }
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.h b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.h
new file mode 100644
index 0000000..bea53af
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/lib.h
@@ -0,0 +1,3 @@
+#include <mylib/apiconfig.h>
+
+MY_LIB_API int do_stuff(void);
diff --git a/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/meson.build b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/meson.build
new file mode 100644
index 0000000..1e7b45e
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/subprojects/sub/mylib/meson.build
@@ -0,0 +1,39 @@
+snippets = import('snippets')
+
+lib_incdir = include_directories('..')
+lib_args = ['-DMY_LIB_COMPILATION']
+lib_static_args = ['-DMY_LIB_STATIC_COMPILATION']
+
+h = snippets.symbol_visibility_header('apiconfig.h')
+install_headers(h, 'lib.h', subdir: 'mylib')
+mylib = both_libraries('mylib', 'lib.c',
+ include_directories: lib_incdir,
+ gnu_symbol_visibility: 'hidden',
+ c_args: lib_args,
+ c_static_args: lib_static_args,
+ install: true)
+mylib_sta_dep = declare_dependency(link_with: mylib.get_static_lib(),
+ include_directories: lib_incdir,
+ compile_args: lib_static_args)
+mylib_sha_dep = declare_dependency(link_with: mylib.get_shared_lib(),
+ include_directories: lib_incdir)
+meson.override_dependency('mylib-sta', mylib_sta_dep)
+meson.override_dependency('mylib-sha', mylib_sha_dep)
+pkg.generate(mylib,
+ extra_cflags: lib_static_args,
+)
+
+# When using static_only, we don't need lib_static_args because
+# MY_LIB_STATIC_COMPILATION gets defined in the generated header.
+h = snippets.symbol_visibility_header('apiconfig-static-only.h',
+ static_only: true)
+install_headers(h, 'lib-static-only.h', subdir: 'mylib')
+libstaticonly = static_library('static-only', 'lib-static-only.c',
+ include_directories: lib_incdir,
+ gnu_symbol_visibility: 'hidden',
+ c_args: lib_args,
+ install: true)
+static_only_dep = declare_dependency(link_with: libstaticonly,
+ include_directories: lib_incdir)
+meson.override_dependency('static-only', static_only_dep)
+pkg.generate(libstaticonly)
diff --git a/test cases/snippets/1 symbol visibility header/test.json b/test cases/snippets/1 symbol visibility header/test.json
new file mode 100644
index 0000000..0d0be4e
--- /dev/null
+++ b/test cases/snippets/1 symbol visibility header/test.json
@@ -0,0 +1,15 @@
+{
+ "installed": [
+ {"type": "file", "file": "usr/include/mylib/apiconfig-static-only.h"},
+ {"type": "file", "file": "usr/include/mylib/apiconfig.h"},
+ {"type": "file", "file": "usr/include/mylib/lib-static-only.h"},
+ {"type": "file", "file": "usr/include/mylib/lib.h"},
+ {"type": "file", "file": "usr/lib/libmylib.a"},
+ {"type": "expr", "file": "usr/lib/?libmylib?so"},
+ {"type": "implib", "file": "usr/lib/libmylib"},
+ {"type": "pdb", "file": "usr/bin/mylib"},
+ {"type": "file", "file": "usr/lib/libstatic-only.a"},
+ {"type": "file", "file": "usr/lib/pkgconfig/mylib.pc"},
+ {"type": "file", "file": "usr/lib/pkgconfig/static-only.pc"}
+ ]
+}
diff --git a/test cases/swift/11 mixed cpp/main.swift b/test cases/swift/11 mixed cpp/main.swift
new file mode 100644
index 0000000..c055dcd
--- /dev/null
+++ b/test cases/swift/11 mixed cpp/main.swift
@@ -0,0 +1,6 @@
+testCallFromSwift()
+testCallWithParam("Calling C++ function from Swift with param is working")
+
+var test = Test()
+var testtwo = Test(1)
+test.testCallFromClass()
diff --git a/test cases/swift/11 mixed cpp/meson.build b/test cases/swift/11 mixed cpp/meson.build
new file mode 100644
index 0000000..6027341
--- /dev/null
+++ b/test cases/swift/11 mixed cpp/meson.build
@@ -0,0 +1,12 @@
+project('mixed cpp', 'cpp', 'swift')
+
+swiftc = meson.get_compiler('swift')
+
+# Testing C++ and Swift interoperability requires Swift 5.9
+if not swiftc.version().version_compare('>= 5.9')
+ error('MESON_SKIP_TEST Test requires Swift 5.9')
+endif
+
+lib = static_library('mylib', 'mylib.cpp')
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
+test('cpp interface', exe)
diff --git a/test cases/swift/11 mixed cpp/mylib.cpp b/test cases/swift/11 mixed cpp/mylib.cpp
new file mode 100644
index 0000000..0c61681
--- /dev/null
+++ b/test cases/swift/11 mixed cpp/mylib.cpp
@@ -0,0 +1,22 @@
+#include "mylib.h"
+#include <iostream>
+
+Test::Test() {
+ std::cout << "Test initialized" << std::endl;
+}
+
+Test::Test(int param) {
+ std::cout << "Test initialized with param " << param << std::endl;
+}
+
+void Test::testCallFromClass() {
+ std::cout << "Calling C++ class function from Swift is working" << std::endl;
+}
+
+void testCallFromSwift() {
+ std::cout << "Calling this C++ function from Swift is working" << std::endl;
+}
+
+void testCallWithParam(const std::string &param) {
+ std::cout << param << std::endl;
+}
diff --git a/test cases/swift/11 mixed cpp/mylib.h b/test cases/swift/11 mixed cpp/mylib.h
new file mode 100644
index 0000000..c465be4
--- /dev/null
+++ b/test cases/swift/11 mixed cpp/mylib.h
@@ -0,0 +1,13 @@
+#pragma once
+#include <string>
+
+class Test {
+public:
+ Test();
+ Test(int param);
+
+ void testCallFromClass();
+};
+
+void testCallFromSwift();
+void testCallWithParam(const std::string &param);
diff --git a/test cases/swift/12 c std passthrough/header.h b/test cases/swift/12 c std passthrough/header.h
new file mode 100644
index 0000000..287cdf4
--- /dev/null
+++ b/test cases/swift/12 c std passthrough/header.h
@@ -0,0 +1,10 @@
+#pragma once
+
+// let's just assume the default isn't c18.
+#if __STDC_VERSION__ == 201710L
+typedef struct Datatype {
+ int x;
+} Datatype;
+#else
+#error C standard version not set!
+#endif
diff --git a/test cases/swift/12 c std passthrough/main.swift b/test cases/swift/12 c std passthrough/main.swift
new file mode 100644
index 0000000..f6358db
--- /dev/null
+++ b/test cases/swift/12 c std passthrough/main.swift
@@ -0,0 +1,3 @@
+let d = Datatype(x: 1)
+
+precondition(d.x == 1)
diff --git a/test cases/swift/12 c std passthrough/meson.build b/test cases/swift/12 c std passthrough/meson.build
new file mode 100644
index 0000000..202768f
--- /dev/null
+++ b/test cases/swift/12 c std passthrough/meson.build
@@ -0,0 +1,3 @@
+project('c std passthrough', 'swift', 'c', default_options: {'c_std': 'c18'})
+
+executable('program', 'main.swift', 'header.h')
diff --git a/test cases/swift/13 mixed objcpp/main.swift b/test cases/swift/13 mixed objcpp/main.swift
new file mode 100644
index 0000000..cd6dd2b
--- /dev/null
+++ b/test cases/swift/13 mixed objcpp/main.swift
@@ -0,0 +1,2 @@
+var test: ObjCPPTest = ObjCPPTest()
+test.testCallToObjCPP()
diff --git a/test cases/swift/13 mixed objcpp/meson.build b/test cases/swift/13 mixed objcpp/meson.build
new file mode 100644
index 0000000..a76162a
--- /dev/null
+++ b/test cases/swift/13 mixed objcpp/meson.build
@@ -0,0 +1,12 @@
+project('mixed objcpp', 'objcpp', 'swift')
+
+swiftc = meson.get_compiler('swift')
+
+# Testing Objective-C++ and Swift interoperability requires Swift 5.9
+if not swiftc.version().version_compare('>= 5.9')
+ error('MESON_SKIP_TEST Test requires Swift 5.9')
+endif
+
+lib = static_library('mylib', 'mylib.mm')
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
+test('objcpp interface', exe)
diff --git a/test cases/swift/13 mixed objcpp/mylib.h b/test cases/swift/13 mixed objcpp/mylib.h
new file mode 100644
index 0000000..1e7b23d
--- /dev/null
+++ b/test cases/swift/13 mixed objcpp/mylib.h
@@ -0,0 +1,17 @@
+#pragma once
+#import <Foundation/Foundation.h>
+
+class Test {
+public:
+ Test();
+
+ void testCallFromClass();
+};
+
+@interface ObjCPPTest: NSObject {
+ @private Test *test;
+}
+- (id)init;
+- (void)dealloc;
+- (void)testCallToObjCPP;
+@end
diff --git a/test cases/swift/13 mixed objcpp/mylib.mm b/test cases/swift/13 mixed objcpp/mylib.mm
new file mode 100644
index 0000000..f7e9ab3
--- /dev/null
+++ b/test cases/swift/13 mixed objcpp/mylib.mm
@@ -0,0 +1,29 @@
+#include "mylib.h"
+#include <iostream>
+
+Test::Test() {
+ std::cout << "Test initialized" << std::endl;
+}
+
+void Test::testCallFromClass() {
+ std::cout << "Calling Objective-C++ class function from Swift is working" << std::endl;
+}
+
+@implementation ObjCPPTest
+- (id)init {
+ self = [super init];
+ if (self) {
+ test = new Test();
+ }
+ return self;
+}
+
+- (void)dealloc {
+ delete test;
+ [super dealloc];
+}
+
+- (void)testCallToObjCPP {
+ test->testCallFromClass();
+}
+@end
diff --git a/test cases/swift/14 single-file library/main.swift b/test cases/swift/14 single-file library/main.swift
new file mode 100644
index 0000000..ccc8fb9
--- /dev/null
+++ b/test cases/swift/14 single-file library/main.swift
@@ -0,0 +1,3 @@
+import SingleFile
+
+callMe()
diff --git a/test cases/swift/14 single-file library/meson.build b/test cases/swift/14 single-file library/meson.build
new file mode 100644
index 0000000..8eda1d5
--- /dev/null
+++ b/test cases/swift/14 single-file library/meson.build
@@ -0,0 +1,4 @@
+project('single-file library', 'swift')
+
+lib = static_library('SingleFile', 'singlefile.swift')
+executable('program', 'main.swift', link_with: [lib])
diff --git a/test cases/swift/14 single-file library/singlefile.swift b/test cases/swift/14 single-file library/singlefile.swift
new file mode 100644
index 0000000..617952f
--- /dev/null
+++ b/test cases/swift/14 single-file library/singlefile.swift
@@ -0,0 +1 @@
+public func callMe() {}
diff --git a/test cases/swift/15 main in single-file library/main.swift b/test cases/swift/15 main in single-file library/main.swift
new file mode 100644
index 0000000..0d95abb
--- /dev/null
+++ b/test cases/swift/15 main in single-file library/main.swift
@@ -0,0 +1,3 @@
+import CProgram
+
+precondition(callMe() == 4)
diff --git a/test cases/swift/15 main in single-file library/meson.build b/test cases/swift/15 main in single-file library/meson.build
new file mode 100644
index 0000000..2e1202e
--- /dev/null
+++ b/test cases/swift/15 main in single-file library/meson.build
@@ -0,0 +1,4 @@
+project('main in single-file library', 'swift', 'c')
+
+lib = static_library('Library', 'main.swift', include_directories: ['.'])
+executable('program', 'program.c', link_with: [lib])
diff --git a/test cases/swift/15 main in single-file library/module.modulemap b/test cases/swift/15 main in single-file library/module.modulemap
new file mode 100644
index 0000000..3c1817a
--- /dev/null
+++ b/test cases/swift/15 main in single-file library/module.modulemap
@@ -0,0 +1,3 @@
+module CProgram [extern_c] {
+ header "program.h"
+}
diff --git a/test cases/swift/15 main in single-file library/program.c b/test cases/swift/15 main in single-file library/program.c
new file mode 100644
index 0000000..8959dae
--- /dev/null
+++ b/test cases/swift/15 main in single-file library/program.c
@@ -0,0 +1,5 @@
+#include "program.h"
+
+int callMe() {
+ return 4;
+}
diff --git a/test cases/swift/15 main in single-file library/program.h b/test cases/swift/15 main in single-file library/program.h
new file mode 100644
index 0000000..5058be3
--- /dev/null
+++ b/test cases/swift/15 main in single-file library/program.h
@@ -0,0 +1 @@
+int callMe(void);
diff --git a/test cases/swift/16 main in multi-file library/main.swift b/test cases/swift/16 main in multi-file library/main.swift
new file mode 100644
index 0000000..3682e8d
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/main.swift
@@ -0,0 +1,4 @@
+import CProgram
+
+precondition(callMe() == 4)
+precondition(callMe2() == 6)
diff --git a/test cases/swift/16 main in multi-file library/meson.build b/test cases/swift/16 main in multi-file library/meson.build
new file mode 100644
index 0000000..4d287f3
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/meson.build
@@ -0,0 +1,4 @@
+project('main in multi-file library', 'swift', 'c')
+
+lib = static_library('Library', 'main.swift', 'more.swift', include_directories: ['.'])
+executable('program', 'program.c', link_with: [lib])
diff --git a/test cases/swift/16 main in multi-file library/module.modulemap b/test cases/swift/16 main in multi-file library/module.modulemap
new file mode 100644
index 0000000..3c1817a
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/module.modulemap
@@ -0,0 +1,3 @@
+module CProgram [extern_c] {
+ header "program.h"
+}
diff --git a/test cases/swift/16 main in multi-file library/more.swift b/test cases/swift/16 main in multi-file library/more.swift
new file mode 100644
index 0000000..716500f
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/more.swift
@@ -0,0 +1,3 @@
+func callMe2() -> Int {
+ 6
+}
diff --git a/test cases/swift/16 main in multi-file library/program.c b/test cases/swift/16 main in multi-file library/program.c
new file mode 100644
index 0000000..8959dae
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/program.c
@@ -0,0 +1,5 @@
+#include "program.h"
+
+int callMe() {
+ return 4;
+}
diff --git a/test cases/swift/16 main in multi-file library/program.h b/test cases/swift/16 main in multi-file library/program.h
new file mode 100644
index 0000000..5058be3
--- /dev/null
+++ b/test cases/swift/16 main in multi-file library/program.h
@@ -0,0 +1 @@
+int callMe(void);
diff --git a/test cases/swift/8 extra args/lib.swift b/test cases/swift/8 extra args/lib.swift
new file mode 100644
index 0000000..f8167ad
--- /dev/null
+++ b/test cases/swift/8 extra args/lib.swift
@@ -0,0 +1,3 @@
+public func callMe() {
+ print("test")
+}
diff --git a/test cases/swift/8 extra args/main.swift b/test cases/swift/8 extra args/main.swift
deleted file mode 100644
index 1ff8e07..0000000
--- a/test cases/swift/8 extra args/main.swift
+++ /dev/null
@@ -1 +0,0 @@
-print("test")
diff --git a/test cases/swift/8 extra args/meson.build b/test cases/swift/8 extra args/meson.build
index ead2ff5..d243e36 100644
--- a/test cases/swift/8 extra args/meson.build
+++ b/test cases/swift/8 extra args/meson.build
@@ -2,8 +2,8 @@ project('extra args', 'swift')
trace_fname = 'trace.json'
-lib = static_library('main',
- 'main.swift',
+lib = static_library('lib',
+ 'lib.swift',
swift_args: [
'-emit-loaded-module-trace',
'-emit-loaded-module-trace-path', '../' + trace_fname
diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json
index fa5e0ec..e479bcb 100644
--- a/test cases/unit/116 empty project/expected_mods.json
+++ b/test cases/unit/116 empty project/expected_mods.json
@@ -181,6 +181,7 @@
"mesonbuild.backend.backends",
"mesonbuild.backend.ninjabackend",
"mesonbuild.build",
+ "mesonbuild.cmdline",
"mesonbuild.compilers",
"mesonbuild.compilers.compilers",
"mesonbuild.compilers.detect",
@@ -230,15 +231,15 @@
"mesonbuild.programs",
"mesonbuild.scripts",
"mesonbuild.scripts.meson_exe",
+ "mesonbuild.tooldetect",
"mesonbuild.utils",
"mesonbuild.utils.core",
"mesonbuild.utils.platform",
- "mesonbuild.utils.posix",
"mesonbuild.utils.universal",
"mesonbuild.utils.vsenv",
"mesonbuild.wrap",
"mesonbuild.wrap.wrap"
],
- "count": 69
+ "count": 71
}
}
diff --git a/test cases/unit/120 rewrite/meson.build b/test cases/unit/120 rewrite/meson.build
index 7d0330b..654b09d 100644
--- a/test cases/unit/120 rewrite/meson.build
+++ b/test cases/unit/120 rewrite/meson.build
@@ -62,6 +62,7 @@ cppcoro = declare_dependency(
)
+cpp_compiler = meson.get_compiler('cpp')
if get_option('unicode') #if comment
#if comment 2
mfc=cpp_compiler.find_library(get_option('debug')?'mfc140ud':'mfc140u')
@@ -80,6 +81,10 @@ assert(not (3 in [1, 2]), '''3 shouldn't be in [1, 2]''')
assert('b' in ['a', 'b'], ''''b' should be in ['a', 'b']''')
assert('c' not in ['a', 'b'], ''''c' shouldn't be in ['a', 'b']''')
+exe1 = 'exe1'
+exe2 = 'exe2'
+exe3 = 'exe3'
+
assert(exe1 in [exe1, exe2], ''''exe1 should be in [exe1, exe2]''')
assert(exe3 not in [exe1, exe2], ''''exe3 shouldn't be in [exe1, exe2]''')
@@ -185,5 +190,11 @@ if a \
debug('help!')
endif
+if false
+ # Should produce a warning, but should not crash
+ foo = not_defined
+ message(not_defined)
+endif
+
# End of file comment with no linebreak \ No newline at end of file
diff --git a/test cases/unit/126 python extension/foo.c b/test cases/unit/126 python extension/foo.c
new file mode 100644
index 0000000..e1a0dfb
--- /dev/null
+++ b/test cases/unit/126 python extension/foo.c
@@ -0,0 +1,31 @@
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+
+static PyObject *
+bar_impl(PyObject *self, PyObject *args)
+{
+ return Py_None;
+}
+
+
+static PyMethodDef foo_methods[] = {
+ {"bar", bar_impl, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL} /* sentinel */
+};
+
+
+static struct PyModuleDef foo_module = {
+ PyModuleDef_HEAD_INIT,
+ "foo", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ foo_methods, /* m_methods */
+};
+
+
+PyMODINIT_FUNC
+PyInit_foo(void)
+{
+ return PyModule_Create(&foo_module);
+}
diff --git a/test cases/unit/126 python extension/meson.build b/test cases/unit/126 python extension/meson.build
new file mode 100644
index 0000000..8cebcea
--- /dev/null
+++ b/test cases/unit/126 python extension/meson.build
@@ -0,0 +1,20 @@
+project('python extension', 'c', meson_version : '>=1.3.0')
+
+py = import('python').find_installation('')
+
+py.extension_module(
+ 'foo', 'foo.c',
+ install: true,
+)
+
+limited_api_supported = true
+if py.language_version().version_compare('>=3.13') and py.language_version().version_compare('<3.15')
+ limited_api_supported = py.get_variable('Py_GIL_DISABLED') != 1
+endif
+if limited_api_supported
+ py.extension_module(
+ 'foo_stable', 'foo.c',
+ install: true,
+ limited_api: '3.2',
+ )
+endif
diff --git a/test cases/unit/125 pkgsubproj/meson.build b/test cases/unit/127 pkgsubproj/meson.build
index b4cf89f..b4cf89f 100644
--- a/test cases/unit/125 pkgsubproj/meson.build
+++ b/test cases/unit/127 pkgsubproj/meson.build
diff --git a/test cases/unit/125 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/127 pkgsubproj/subprojects/sub/meson.build
index 99622b6..99622b6 100644
--- a/test cases/unit/125 pkgsubproj/subprojects/sub/meson.build
+++ b/test cases/unit/127 pkgsubproj/subprojects/sub/meson.build
diff --git a/test cases/unit/126 test slice/meson.build b/test cases/unit/128 test slice/meson.build
index a41c2f6..a41c2f6 100644
--- a/test cases/unit/126 test slice/meson.build
+++ b/test cases/unit/128 test slice/meson.build
diff --git a/test cases/unit/128 test slice/test.py b/test cases/unit/128 test slice/test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/unit/128 test slice/test.py
diff --git a/test cases/unit/127 sanitizers/meson.build b/test cases/unit/129 sanitizers/meson.build
index b42fb35..b42fb35 100644
--- a/test cases/unit/127 sanitizers/meson.build
+++ b/test cases/unit/129 sanitizers/meson.build
diff --git a/test cases/unit/128 long opt vs D/meson.build b/test cases/unit/130 long opt vs D/meson.build
index e05d88d..e05d88d 100644
--- a/test cases/unit/128 long opt vs D/meson.build
+++ b/test cases/unit/130 long opt vs D/meson.build
diff --git a/test cases/unit/128 long opt vs D/meson_options.txt b/test cases/unit/130 long opt vs D/meson_options.txt
index 255bf15..255bf15 100644
--- a/test cases/unit/128 long opt vs D/meson_options.txt
+++ b/test cases/unit/130 long opt vs D/meson_options.txt
diff --git a/test cases/unit/131 vala internal glib/lib.vala b/test cases/unit/131 vala internal glib/lib.vala
new file mode 100644
index 0000000..e62e632
--- /dev/null
+++ b/test cases/unit/131 vala internal glib/lib.vala
@@ -0,0 +1,3 @@
+public int func() {
+ return 42;
+}
diff --git a/test cases/unit/131 vala internal glib/meson.build b/test cases/unit/131 vala internal glib/meson.build
new file mode 100644
index 0000000..9479082
--- /dev/null
+++ b/test cases/unit/131 vala internal glib/meson.build
@@ -0,0 +1,21 @@
+project('vala internal glib')
+
+if not add_languages('vala', required: false)
+ error('MESON_SKIP_TEST valac not installed')
+endif
+
+glib_ver = get_option('glib-version')
+if glib_ver == 'unset'
+ error('Required to set -Dglib-version')
+endif
+
+glib_dep = declare_dependency(version: glib_ver)
+meson.override_dependency('glib-2.0', glib_dep)
+
+named_glib_dep = dependency('glib-2.0')
+
+assert(named_glib_dep.type_name() == 'internal')
+assert(glib_dep == named_glib_dep)
+
+tgt = static_library('vala-tgt', 'lib.vala',
+ dependencies: named_glib_dep)
diff --git a/test cases/unit/131 vala internal glib/meson.options b/test cases/unit/131 vala internal glib/meson.options
new file mode 100644
index 0000000..f8a1ece
--- /dev/null
+++ b/test cases/unit/131 vala internal glib/meson.options
@@ -0,0 +1 @@
+option('glib-version', type: 'string', value: 'unset')
diff --git a/test cases/unit/132 custom target index test/meson.build b/test cases/unit/132 custom target index test/meson.build
new file mode 100644
index 0000000..adbbb4d
--- /dev/null
+++ b/test cases/unit/132 custom target index test/meson.build
@@ -0,0 +1,16 @@
+# testcase by blue42u
+project('test')
+python3 = find_program('python3')
+
+gen_code = '''
+import sys, pathlib
+pathlib.Path(sys.argv[1]).write_text("foo")
+pathlib.Path(sys.argv[2]).write_text("bar")'''
+foobar_txt = custom_target(command: [python3, '-c', gen_code, '@OUTPUT@'], output: ['foo.txt', 'bar.txt'])
+
+test_code = '''
+import sys, pathlib
+sys.exit(0 if pathlib.Path(sys.argv[1]).read_text() == sys.argv[2] else 4)'''
+
+test('foo.txt', python3, args: ['-c', test_code, foobar_txt[0], 'foo'])
+test('bar.txt', python3, args: ['-c', test_code, foobar_txt[1], 'bar'])
diff --git a/test cases/unit/56 introspection/meson.build b/test cases/unit/56 introspection/meson.build
index 568d5cc..c7856fe 100644
--- a/test cases/unit/56 introspection/meson.build
+++ b/test cases/unit/56 introspection/meson.build
@@ -58,6 +58,21 @@ test('test case 1', t1)
test('test case 2', t2, depends: t3)
benchmark('benchmark 1', t3, args: [cus1, cus2, cus3])
+### BEGIN: Stuff to test the handling of `UnknownValue`
+var_1 = 1
+var_2 = 2
+unknown_var = 'var_1'
+if host_machine.system() == 'windows'
+ unknown_var = 'var_2'
+endif
+# The IntrospectionInterpreter can't know the value of unknown_var and use the `UnknownValue` class.
+
+message(get_variable(unknown_var))
+
+dependency(unknown_var, version: [unknown_var], required: false)
+dependency(unknown_var, version: unknown_var, required: false)
+### END: Stuff to test the handling of `UnknownValue`
+
### Stuff to test the AST JSON printer
foreach x : ['a', 'b', 'c']
if x == 'a'
diff --git a/test cases/unit/58 introspect buildoptions/meson.build b/test cases/unit/58 introspect buildoptions/meson.build
index 36e03e0..a6a8b86 100644
--- a/test cases/unit/58 introspect buildoptions/meson.build
+++ b/test cases/unit/58 introspect buildoptions/meson.build
@@ -14,5 +14,13 @@ if r.returncode() != 0
error('FAILED')
endif
+name_prefix = 'lib'
+if get_option('buildtype') == 'release'
+ # ensure that these variables become an UnkownValue
+ name_prefix = []
+endif
+
+static_library('hello', 'hello.c', name_prefix: name_prefix)
+
add_languages(r.stdout().strip(), required: true)
add_languages('afgggergearvearghergervergreaergaergasv', required: false)
diff --git a/test cases/unit/69 cross/crossfile.in b/test cases/unit/69 cross/crossfile.in
index 678e8d3..beab9bc 100644
--- a/test cases/unit/69 cross/crossfile.in
+++ b/test cases/unit/69 cross/crossfile.in
@@ -3,3 +3,6 @@ system = '@system@'
cpu_family = '@cpu_family@'
cpu = '@cpu@'
endian = '@endian@'
+
+[built-in options]
+c_args = ['-funroll-loops']
diff --git a/test cases/unit/69 cross/meson.build b/test cases/unit/69 cross/meson.build
index acf4f0f..645d453 100644
--- a/test cases/unit/69 cross/meson.build
+++ b/test cases/unit/69 cross/meson.build
@@ -1,16 +1,25 @@
project('crosstest')
+add_languages('c', native: true)
if get_option('generate')
conf_data = configuration_data()
conf_data.set('system', build_machine.system())
conf_data.set('cpu', build_machine.cpu())
conf_data.set('cpu_family', build_machine.cpu_family())
conf_data.set('endian', build_machine.endian())
+ conf_data.set('c_args', '-pedantic')
configure_file(input: 'crossfile.in',
output: 'crossfile',
configuration: conf_data)
- message('Written cross file')
+ configure_file(input: 'nativefile.in',
+ output: 'nativefile',
+ configuration: conf_data)
+ message('Written native and cross file')
+
+ add_languages('c', native: false)
+ assert(get_option('build.c_args') == get_option('c_args'))
else
assert(meson.is_cross_build(), 'not setup as cross build')
+ assert(get_option('build.c_args') == ['-pedantic'])
endif
diff --git a/test cases/unit/69 cross/nativefile.in b/test cases/unit/69 cross/nativefile.in
new file mode 100644
index 0000000..9a63999
--- /dev/null
+++ b/test cases/unit/69 cross/nativefile.in
@@ -0,0 +1,2 @@
+[built-in options]
+build.c_args = ['@c_args@']
diff --git a/test cases/unit/92 new subproject in configured project/meson.build b/test cases/unit/92 new subproject in configured project/meson.build
index b82aa41..c200e57 100644
--- a/test cases/unit/92 new subproject in configured project/meson.build
+++ b/test cases/unit/92 new subproject in configured project/meson.build
@@ -1,4 +1,4 @@
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation
project('existing project with new subproject', 'c')
diff --git a/test cases/unit/92 new subproject in configured project/meson_options.txt b/test cases/unit/92 new subproject in configured project/meson_options.txt
index 12d8395..a928da2 100644
--- a/test cases/unit/92 new subproject in configured project/meson_options.txt
+++ b/test cases/unit/92 new subproject in configured project/meson_options.txt
@@ -1,3 +1,3 @@
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation
option('use-sub', type : 'boolean', value : false)
diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c b/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c
index 9713d9f..1a14f7e 100644
--- a/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c
+++ b/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c
@@ -1,4 +1,4 @@
-/* SPDX-license-identifier: Apache-2.0 */
+/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright © 2021 Intel Corporation */
int func(void) {
diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build b/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build
index a833b0c..5f9ac7e 100644
--- a/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build
+++ b/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build
@@ -1,4 +1,4 @@
-# SPDX-license-identifier: Apache-2.0
+# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation
project('new subproject', 'c')
diff --git a/test cases/vala/31 generated ui file subdirectory/meson.build b/test cases/vala/31 generated ui file subdirectory/meson.build
new file mode 100644
index 0000000..4210581
--- /dev/null
+++ b/test cases/vala/31 generated ui file subdirectory/meson.build
@@ -0,0 +1,22 @@
+project('demo', 'c', 'vala')
+
+gnome = import('gnome', required: false)
+
+if not gnome.found()
+ error('MESON_SKIP_TEST: gnome module not supported')
+endif
+
+deps = [
+ dependency('glib-2.0', version : '>=2.50'),
+ dependency('gobject-2.0'),
+ dependency('gtk+-3.0'),
+]
+
+subdir('subdir')
+
+executable(
+ 'demo',
+ 'test.vala',
+ resources,
+ dependencies: deps,
+)
diff --git a/test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in b/test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in
new file mode 100644
index 0000000..bf5c831
--- /dev/null
+++ b/test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="3.0"/>
+ <template class="TestBox" parent="GtkBox">
+ </template>
+</interface>
diff --git a/test cases/vala/31 generated ui file subdirectory/subdir/meson.build b/test cases/vala/31 generated ui file subdirectory/subdir/meson.build
new file mode 100644
index 0000000..dbe9344
--- /dev/null
+++ b/test cases/vala/31 generated ui file subdirectory/subdir/meson.build
@@ -0,0 +1,13 @@
+ui_tgt = custom_target(
+ input: 'TestBox.ui.in',
+ output: 'TestBox.ui',
+ command: [find_program('cat')],
+ feed: true,
+ capture: true,
+)
+
+resources = gnome.compile_resources('test-resources',
+ 'test.gresource.xml',
+ c_name: 'test_res',
+ dependencies: ui_tgt,
+)
diff --git a/test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml b/test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml
new file mode 100644
index 0000000..382b951
--- /dev/null
+++ b/test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/com/mesonbuild/test">
+ <file>TestBox.ui</file>
+ </gresource>
+</gresources>
diff --git a/test cases/vala/31 generated ui file subdirectory/test.vala b/test cases/vala/31 generated ui file subdirectory/test.vala
new file mode 100644
index 0000000..36f565b
--- /dev/null
+++ b/test cases/vala/31 generated ui file subdirectory/test.vala
@@ -0,0 +1,7 @@
+[GtkTemplate (ui = "/com/mesonbuild/test/TestBox.ui")]
+class TestBox: Gtk.Box {
+}
+
+int main() {
+ return 0;
+}
diff --git a/test cases/vala/32 valaless vapigen/clib.c b/test cases/vala/32 valaless vapigen/clib.c
new file mode 100644
index 0000000..a55ecd4
--- /dev/null
+++ b/test cases/vala/32 valaless vapigen/clib.c
@@ -0,0 +1,5 @@
+#include "clib.h"
+
+int clib_fun(void) {
+ return 42;
+}
diff --git a/test cases/vala/32 valaless vapigen/clib.h b/test cases/vala/32 valaless vapigen/clib.h
new file mode 100644
index 0000000..4d855c9
--- /dev/null
+++ b/test cases/vala/32 valaless vapigen/clib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int clib_fun(void);
diff --git a/test cases/vala/32 valaless vapigen/meson.build b/test cases/vala/32 valaless vapigen/meson.build
new file mode 100644
index 0000000..22a99e5
--- /dev/null
+++ b/test cases/vala/32 valaless vapigen/meson.build
@@ -0,0 +1,34 @@
+project('valaless-vapigen', 'c')
+
+if host_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST Does not work with the Vala currently packaged in cygwin')
+endif
+
+gnome = import('gnome')
+
+clib_src = [
+ 'clib.c',
+ 'clib.h'
+]
+
+clib_lib = shared_library('clib', clib_src)
+
+clib_gir = gnome.generate_gir(clib_lib,
+ sources: clib_src,
+ namespace: 'Clib',
+ nsversion: '0',
+ header: 'clib.h',
+ symbol_prefix: 'clib'
+)
+
+clib_vapi = gnome.generate_vapi('clib', sources: clib_gir[0])
+
+clib_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ link_with: clib_lib,
+ sources: clib_gir,
+ dependencies: clib_vapi
+)
+
+
+test('clib-test', executable('clib-test', 'test_clib.c', dependencies: clib_dep))
diff --git a/test cases/vala/32 valaless vapigen/test_clib.c b/test cases/vala/32 valaless vapigen/test_clib.c
new file mode 100644
index 0000000..6fd426c
--- /dev/null
+++ b/test cases/vala/32 valaless vapigen/test_clib.c
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+#include <clib.h>
+
+int main(void) {
+ if (clib_fun () == 42)
+ return EXIT_SUCCESS;
+ else
+ return EXIT_FAILURE;
+}
diff --git a/test cases/vala/9 gir/meson.build b/test cases/vala/9 gir/meson.build
index 3320527..c70938b 100644
--- a/test cases/vala/9 gir/meson.build
+++ b/test cases/vala/9 gir/meson.build
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2025 Intel Corporation
+
project('foo', 'c', 'vala')
glib = dependency('glib-2.0')
@@ -12,6 +15,6 @@ foo = shared_library('foo', 'foo.vala',
custom_target('foo-typelib',
command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
- input: meson.current_build_dir() + '/Foo-1.0.gir',
+ input: foo.vala_gir(),
output: 'Foo-1.0.typelib',
- depends: foo)
+ build_by_default : true)
diff --git a/test cases/warning/9 meson.options/meson.build b/test cases/warning/8 meson.options/meson.build
index 59c3872..59c3872 100644
--- a/test cases/warning/9 meson.options/meson.build
+++ b/test cases/warning/8 meson.options/meson.build
diff --git a/test cases/warning/9 meson.options/meson.options b/test cases/warning/8 meson.options/meson.options
index b84ee83..b84ee83 100644
--- a/test cases/warning/9 meson.options/meson.options
+++ b/test cases/warning/8 meson.options/meson.options
diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson.build b/test cases/warning/8 meson.options/subprojects/no-warn/meson.build
index f86fbf7..f86fbf7 100644
--- a/test cases/warning/9 meson.options/subprojects/no-warn/meson.build
+++ b/test cases/warning/8 meson.options/subprojects/no-warn/meson.build
diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson.options b/test cases/warning/8 meson.options/subprojects/no-warn/meson.options
index b84ee83..b84ee83 100644
--- a/test cases/warning/9 meson.options/subprojects/no-warn/meson.options
+++ b/test cases/warning/8 meson.options/subprojects/no-warn/meson.options
diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt b/test cases/warning/8 meson.options/subprojects/no-warn/meson_options.txt
index b84ee83..b84ee83 100644
--- a/test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt
+++ b/test cases/warning/8 meson.options/subprojects/no-warn/meson_options.txt
diff --git a/test cases/warning/9 meson.options/test.json b/test cases/warning/8 meson.options/test.json
index f711924..f711924 100644
--- a/test cases/warning/9 meson.options/test.json
+++ b/test cases/warning/8 meson.options/test.json
diff --git a/test cases/warning/8 target with no sources/meson.build b/test cases/warning/9 target with no sources/meson.build
index 2a8ee11..2a8ee11 100644
--- a/test cases/warning/8 target with no sources/meson.build
+++ b/test cases/warning/9 target with no sources/meson.build
diff --git a/test cases/warning/8 target with no sources/test.json b/test cases/warning/9 target with no sources/test.json
index 30e5b05..30e5b05 100644
--- a/test cases/warning/8 target with no sources/test.json
+++ b/test cases/warning/9 target with no sources/test.json
diff --git a/test cases/windows/21 masm/meson.build b/test cases/windows/21 masm/meson.build
index b6b8fbb..5335a0d 100644
--- a/test cases/windows/21 masm/meson.build
+++ b/test cases/windows/21 masm/meson.build
@@ -1,9 +1,5 @@
project('test-masm', 'c')
-if get_option('backend').startswith('vs')
- error('MESON_SKIP_TEST: masm is not supported by vs backend')
-endif
-
cc = meson.get_compiler('c')
# MASM must be found when using MSVC, otherwise it is optional
diff --git a/test cases/windows/25 embed manifest/DPIAware.manifest b/test cases/windows/25 embed manifest/DPIAware.manifest
new file mode 100644
index 0000000..f2708ec
--- /dev/null
+++ b/test cases/windows/25 embed manifest/DPIAware.manifest
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <asmv3:application>
+ <asmv3:windowsSettings>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+</assembly>
diff --git a/test cases/windows/25 embed manifest/meson.build b/test cases/windows/25 embed manifest/meson.build
new file mode 100644
index 0000000..0f4c9b4
--- /dev/null
+++ b/test cases/windows/25 embed manifest/meson.build
@@ -0,0 +1,11 @@
+project('can-manifests-be-embedded', 'c')
+
+cc = meson.get_compiler('c')
+
+if cc.get_linker_id() not in ['link', 'lld-link', 'xilink'] # cc.get_linker_argument_syntax() != 'link'
+ error('MESON_SKIP_TEST: test is only relevant for the Microsoft linker')
+endif
+
+# Ensure that the manifest can be embedded
+executable('prog', 'prog.c',
+ link_args: ['/MANIFEST:EMBED', '/MANIFESTINPUT:' + meson.project_source_root() / 'DPIAware.manifest'])
diff --git a/test cases/windows/25 embed manifest/prog.c b/test cases/windows/25 embed manifest/prog.c
new file mode 100644
index 0000000..b1d9c2c
--- /dev/null
+++ b/test cases/windows/25 embed manifest/prog.c
@@ -0,0 +1,3 @@
+int main(int argc, char *argv[]) {
+ return 0;
+}
diff --git a/tools/dircondenser.py b/tools/dircondenser.py
index b8679a4..a07ede1 100755
--- a/tools/dircondenser.py
+++ b/tools/dircondenser.py
@@ -71,6 +71,7 @@ def condense(dirname: str) -> None:
replace_source('run_unittests.py', replacements)
replace_source('run_project_tests.py', replacements)
replace_source('run_format_tests.py', replacements)
+ replace_source('run_shell_checks.py', replacements)
for f in glob('unittests/*.py'):
replace_source(f, replacements)
diff --git a/tools/run_with_cov.py b/tools/run_with_cov.py
deleted file mode 100755
index 0d3fba6..0000000
--- a/tools/run_with_cov.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: Apache-2.0
-# Copyright 2021 The Meson development team
-
-import subprocess
-import coverage
-import os
-import sys
-from pathlib import Path
-
-root_path = Path(__file__).parent.parent.absolute()
-
-# Python magic so we can import mesonlib
-sys.path.append(root_path.as_posix())
-from mesonbuild import mesonlib
-
-def generate_coveragerc() -> Path:
- i_file = (root_path / 'data' / '.coveragerc.in')
- o_file = (root_path / '.coveragerc')
- raw = i_file.read_text(encoding='utf-8')
- raw = raw.replace('@ROOT@', root_path.as_posix())
- o_file.write_text(raw, encoding='utf-8')
- return o_file
-
-def main() -> int:
- # Remove old run data
- out_dir = root_path / '.coverage'
- mesonlib.windows_proof_rmtree(out_dir.as_posix())
- out_dir.mkdir(parents=True, exist_ok=True)
-
- # Setup coverage
- python_path = (root_path / 'ci').as_posix()
- os.environ['PYTHONPATH'] = os.pathsep.join([python_path, os.environ.get('PYTHONPATH', '')])
- os.environ['COVERAGE_PROCESS_START'] = generate_coveragerc().as_posix()
- coverage.process_startup()
-
- # Run the actual command
- cmd = mesonlib.python_command + sys.argv[1:]
- return subprocess.run(cmd, env=os.environ.copy()).returncode
-
-if __name__ == '__main__':
- raise SystemExit(main())
diff --git a/unittests/__init__.py b/unittests/__init__.py
index e69de29..fb8fb8e 100644
--- a/unittests/__init__.py
+++ b/unittests/__init__.py
@@ -0,0 +1,20 @@
+import os
+
+import mesonbuild.compilers
+from mesonbuild.mesonlib import setup_vsenv
+
+def unset_envs():
+ # For unit tests we must fully control all command lines
+ # so that there are no unexpected changes coming from the
+ # environment, for example when doing a package build.
+ varnames = ['CPPFLAGS', 'LDFLAGS'] + list(mesonbuild.compilers.compilers.CFLAGS_MAPPING.values())
+ for v in varnames:
+ if v in os.environ:
+ del os.environ[v]
+
+def set_envs():
+ os.environ.setdefault('MESON_UNIT_TEST_BACKEND', 'ninja')
+
+setup_vsenv()
+unset_envs()
+set_envs()
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index 0618b20..1304658 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -2,6 +2,7 @@
# Copyright 2016-2021 The Meson development team
# Copyright © 2023-2025 Intel Corporation
+import itertools
import subprocess
import re
import json
@@ -13,6 +14,7 @@ import platform
import pickle
import zipfile, tarfile
import sys
+import sysconfig
from unittest import mock, SkipTest, skipIf, skipUnless, expectedFailure
from contextlib import contextmanager
from glob import glob
@@ -29,7 +31,7 @@ import mesonbuild.coredata
import mesonbuild.machinefile
import mesonbuild.modules.gnome
from mesonbuild.mesonlib import (
- BuildDirLock, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd,
+ DirectoryLock, DirectoryLockAction, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd,
is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, quote_arg,
relpath, is_linux, git, search_version, do_conf_file, do_conf_str, default_prefix,
MesonException, EnvironmentException,
@@ -45,7 +47,7 @@ from mesonbuild.compilers.c import VisualStudioCCompiler, ClangClCCompiler
from mesonbuild.compilers.cpp import VisualStudioCPPCompiler, ClangClCPPCompiler
from mesonbuild.compilers import (
detect_static_linker, detect_c_compiler, compiler_from_language,
- detect_compiler_for
+ detect_compiler_for, lang_suffixes
)
from mesonbuild.linkers import linkers
@@ -222,6 +224,47 @@ class AllPlatformTests(BasePlatformTests):
confdata.values = {'VAR': (['value'], 'description')}
self.assertRaises(MesonException, conf_str, ['#mesondefine VAR'], confdata, 'meson')
+ def test_cmake_configuration(self):
+ if self.backend is not Backend.ninja:
+ raise SkipTest('ninja backend needed to configure with cmake')
+
+ cmake = ExternalProgram('cmake')
+ if not cmake.found():
+ raise SkipTest('cmake not available')
+
+ cmake_version = cmake.get_version()
+ if not version_compare(cmake_version, '>=3.13.5'):
+ raise SkipTest('cmake is too old')
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ srcdir = os.path.join(tmpdir, 'src')
+
+ shutil.copytree(os.path.join(self.src_root, 'test cases', 'common', '14 configure file'), srcdir)
+ self.init(srcdir)
+
+ cmake_builddir = os.path.join(srcdir, "cmake_builddir")
+ self.assertNotEqual(self.builddir, cmake_builddir)
+ self._run([cmake.path, '-G', 'Ninja', '-S', srcdir, '-B', cmake_builddir])
+
+ header_list = [
+ 'config7.h',
+ 'config10.h',
+ ]
+
+ for header in header_list:
+ meson_header = ""
+ cmake_header = ""
+
+ with open(os.path.join(self.builddir, header), encoding='utf-8') as f:
+ meson_header = f.read()
+
+ cmake_header_path = os.path.join(cmake_builddir, header)
+ with open(os.path.join(cmake_builddir, header), encoding='utf-8') as f:
+ cmake_header = f.read()
+
+ self.assertTrue(cmake_header, f'cmake generated header {header} is empty')
+ self.assertEqual(cmake_header, meson_header)
+
def test_absolute_prefix_libdir(self):
'''
Tests that setting absolute paths for --prefix and --libdir work. Can't
@@ -853,7 +896,13 @@ class AllPlatformTests(BasePlatformTests):
self._run(command)
self.assertEqual(0, failure_count, 'Expected %d tests to fail.' % failure_count)
except subprocess.CalledProcessError as e:
- self.assertEqual(e.returncode, failure_count)
+ actual_fails = 0
+ with open(os.path.join(self.logdir, 'testlog.json'), encoding='utf-8') as f:
+ for line in f:
+ res = json.loads(line)
+ if res['is_fail']:
+ actual_fails += 1
+ self.assertEqual(actual_fails, failure_count)
def test_suite_selection(self):
testdir = os.path.join(self.unit_test_dir, '4 suite selection')
@@ -869,11 +918,11 @@ class AllPlatformTests(BasePlatformTests):
self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj'])
self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc'])
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail'])
self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix'])
self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj'])
self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjsucc'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail'])
self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix'])
self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail'])
@@ -896,9 +945,9 @@ class AllPlatformTests(BasePlatformTests):
self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:fail'])
self.assertFailedTestCount(4, self.mtest_command + ['--no-suite', 'subprjmix:success'])
- self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
- self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(4, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test'])
self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail'])
@@ -912,7 +961,7 @@ class AllPlatformTests(BasePlatformTests):
self.utime(os.path.join(testdir, 'meson.build'))
o = self._run(self.mtest_command + ['--list'])
self.assertIn('Regenerating build files', o)
- self.assertIn('test_features / xfail', o)
+ self.assertIn('test_features:xfail', o)
o = self._run(self.mtest_command + ['--list'])
self.assertNotIn('Regenerating build files', o)
# no real targets should have been built
@@ -1012,7 +1061,7 @@ class AllPlatformTests(BasePlatformTests):
def test_internal_include_order(self):
- if mesonbuild.environment.detect_msys2_arch() and ('MESON_RSP_THRESHOLD' in os.environ):
+ if mesonbuild.envconfig.detect_msys2_arch() and ('MESON_RSP_THRESHOLD' in os.environ):
raise SkipTest('Test does not yet support gcc rsp files on msys2')
testdir = os.path.join(self.common_test_dir, '130 include order')
@@ -1099,110 +1148,144 @@ class AllPlatformTests(BasePlatformTests):
for lang, evar in langs:
# Detect with evar and do sanity checks on that
if evar in os.environ:
- ecc = compiler_from_language(env, lang, MachineChoice.HOST)
- self.assertTrue(ecc.version)
- elinker = detect_static_linker(env, ecc)
- # Pop it so we don't use it for the next detection
- evalue = os.environ.pop(evar)
- # Very rough/strict heuristics. Would never work for actual
- # compiler detection, but should be ok for the tests.
- ebase = os.path.basename(evalue)
- if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
- self.assertIsInstance(ecc, gnu)
- self.assertIsInstance(elinker, ar)
- elif 'clang-cl' in ebase:
- self.assertIsInstance(ecc, clangcl)
- self.assertIsInstance(elinker, lib)
- elif 'clang' in ebase:
- self.assertIsInstance(ecc, clang)
- self.assertIsInstance(elinker, ar)
- elif ebase.startswith('ic'):
- self.assertIsInstance(ecc, intel)
- self.assertIsInstance(elinker, ar)
- elif ebase.startswith('cl'):
- self.assertIsInstance(ecc, msvc)
- self.assertIsInstance(elinker, lib)
- else:
- raise AssertionError(f'Unknown compiler {evalue!r}')
- # Check that we actually used the evalue correctly as the compiler
- self.assertEqual(ecc.get_exelist(), split_args(evalue))
+ with self.subTest(lang=lang, evar=evar):
+ try:
+ ecc = compiler_from_language(env, lang, MachineChoice.HOST)
+ except EnvironmentException:
+ # always raise in ci, we expect to have a valid ObjC and ObjC++ compiler of some kind
+ if is_ci():
+ self.fail(f'Could not find a compiler for {lang}')
+ if sys.version_info < (3, 11):
+ continue
+ self.skipTest(f'No valid compiler for {lang}.')
+ finally:
+ # Pop it so we don't use it for the next detection
+ evalue = os.environ.pop(evar)
+ assert ecc is not None, "Something went really wrong"
+ self.assertTrue(ecc.version)
+ elinker = detect_static_linker(env, ecc)
+ # Very rough/strict heuristics. Would never work for actual
+ # compiler detection, but should be ok for the tests.
+ ebase = os.path.basename(evalue)
+ if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
+ self.assertIsInstance(ecc, gnu)
+ self.assertIsInstance(elinker, ar)
+ elif 'clang-cl' in ebase:
+ self.assertIsInstance(ecc, clangcl)
+ self.assertIsInstance(elinker, lib)
+ elif 'clang' in ebase:
+ self.assertIsInstance(ecc, clang)
+ self.assertIsInstance(elinker, ar)
+ elif ebase.startswith('ic'):
+ self.assertIsInstance(ecc, intel)
+ self.assertIsInstance(elinker, ar)
+ elif ebase.startswith('cl'):
+ self.assertIsInstance(ecc, msvc)
+ self.assertIsInstance(elinker, lib)
+ else:
+ self.fail(f'Unknown compiler {evalue!r}')
+ # Check that we actually used the evalue correctly as the compiler
+ self.assertEqual(ecc.get_exelist(), split_args(evalue))
+
# Do auto-detection of compiler based on platform, PATH, etc.
- cc = compiler_from_language(env, lang, MachineChoice.HOST)
- self.assertTrue(cc.version)
- linker = detect_static_linker(env, cc)
- # Check compiler type
- if isinstance(cc, gnu):
- self.assertIsInstance(linker, ar)
- if is_osx():
- self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
- elif is_sunos():
- self.assertIsInstance(cc.linker, (linkers.SolarisDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
- else:
- self.assertIsInstance(cc.linker, linkers.GnuLikeDynamicLinkerMixin)
- if isinstance(cc, clangcl):
- self.assertIsInstance(linker, lib)
- self.assertIsInstance(cc.linker, linkers.ClangClDynamicLinker)
- if isinstance(cc, clang):
- self.assertIsInstance(linker, ar)
- if is_osx():
- self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
- elif is_windows():
- # This is clang, not clang-cl. This can be either an
- # ld-like linker of link.exe-like linker (usually the
- # former for msys2, the latter otherwise)
- self.assertIsInstance(cc.linker, (linkers.MSVCDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
- elif is_sunos():
- self.assertIsInstance(cc.linker, (linkers.SolarisDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
- else:
- self.assertIsInstance(cc.linker, linkers.GnuLikeDynamicLinkerMixin)
- if isinstance(cc, intel):
- self.assertIsInstance(linker, ar)
- if is_osx():
- self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
- elif is_windows():
- self.assertIsInstance(cc.linker, linkers.XilinkDynamicLinker)
- else:
- self.assertIsInstance(cc.linker, linkers.GnuDynamicLinker)
- if isinstance(cc, msvc):
- self.assertTrue(is_windows())
- self.assertIsInstance(linker, lib)
- self.assertEqual(cc.id, 'msvc')
- self.assertTrue(hasattr(cc, 'is_64'))
- self.assertIsInstance(cc.linker, linkers.MSVCDynamicLinker)
- # If we're on Windows CI, we know what the compiler will be
- if 'arch' in os.environ:
- if os.environ['arch'] == 'x64':
- self.assertTrue(cc.is_64)
+ with self.subTest(lang=lang):
+ try:
+ cc = compiler_from_language(env, lang, MachineChoice.HOST)
+ except EnvironmentException:
+ # always raise in ci, we expect to have a valid ObjC and ObjC++ compiler of some kind
+ if is_ci():
+ self.fail(f'Could not find a compiler for {lang}')
+ if sys.version_info < (3, 11):
+ continue
+ self.skipTest(f'No valid compiler for {lang}.')
+ assert cc is not None, "Something went really wrong"
+ self.assertTrue(cc.version)
+ linker = detect_static_linker(env, cc)
+ # Check compiler type
+ if isinstance(cc, gnu):
+ self.assertIsInstance(linker, ar)
+ if is_osx():
+ self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
+ elif is_sunos():
+ self.assertIsInstance(cc.linker, (linkers.SolarisDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
+ else:
+ self.assertIsInstance(cc.linker, linkers.GnuLikeDynamicLinkerMixin)
+ if isinstance(cc, clangcl):
+ self.assertIsInstance(linker, lib)
+ self.assertIsInstance(cc.linker, linkers.ClangClDynamicLinker)
+ if isinstance(cc, clang):
+ self.assertIsInstance(linker, ar)
+ if is_osx():
+ self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
+ elif is_windows():
+ # This is clang, not clang-cl. This can be either an
+ # ld-like linker of link.exe-like linker (usually the
+ # former for msys2, the latter otherwise)
+ self.assertIsInstance(cc.linker, (linkers.MSVCDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
+ elif is_sunos():
+ self.assertIsInstance(cc.linker, (linkers.SolarisDynamicLinker, linkers.GnuLikeDynamicLinkerMixin))
else:
- self.assertFalse(cc.is_64)
+ self.assertIsInstance(cc.linker, linkers.GnuLikeDynamicLinkerMixin)
+ if isinstance(cc, intel):
+ self.assertIsInstance(linker, ar)
+ if is_osx():
+ self.assertIsInstance(cc.linker, linkers.AppleDynamicLinker)
+ elif is_windows():
+ self.assertIsInstance(cc.linker, linkers.XilinkDynamicLinker)
+ else:
+ self.assertIsInstance(cc.linker, linkers.GnuDynamicLinker)
+ if isinstance(cc, msvc):
+ self.assertTrue(is_windows())
+ self.assertIsInstance(linker, lib)
+ self.assertEqual(cc.id, 'msvc')
+ self.assertTrue(hasattr(cc, 'is_64'))
+ self.assertIsInstance(cc.linker, linkers.MSVCDynamicLinker)
+ # If we're on Windows CI, we know what the compiler will be
+ if 'arch' in os.environ:
+ if os.environ['arch'] == 'x64':
+ self.assertTrue(cc.is_64)
+ else:
+ self.assertFalse(cc.is_64)
+
# Set evar ourselves to a wrapper script that just calls the same
# exelist + some argument. This is meant to test that setting
# something like `ccache gcc -pipe` or `distcc ccache gcc` works.
- wrapper = os.path.join(testdir, 'compiler wrapper.py')
- wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
- os.environ[evar] = ' '.join(quote_arg(w) for w in wrappercc)
-
- # Check static linker too
- wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
- os.environ['AR'] = ' '.join(quote_arg(w) for w in wrapperlinker)
-
- # Need a new env to re-run environment loading
- env = get_fake_env(testdir, self.builddir, self.prefix)
-
- wcc = compiler_from_language(env, lang, MachineChoice.HOST)
- wlinker = detect_static_linker(env, wcc)
- # Pop it so we don't use it for the next detection
- os.environ.pop('AR')
- # Must be the same type since it's a wrapper around the same exelist
- self.assertIs(type(cc), type(wcc))
- self.assertIs(type(linker), type(wlinker))
- # Ensure that the exelist is correct
- self.assertEqual(wcc.get_exelist(), wrappercc)
- self.assertEqual(wlinker.get_exelist(), wrapperlinker)
- # Ensure that the version detection worked correctly
- self.assertEqual(cc.version, wcc.version)
- if hasattr(cc, 'is_64'):
- self.assertEqual(cc.is_64, wcc.is_64)
+ with self.subTest('wrapper script', lang=lang):
+ wrapper = os.path.join(testdir, 'compiler wrapper.py')
+ wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
+ os.environ[evar] = ' '.join(quote_arg(w) for w in wrappercc)
+
+ # Check static linker too
+ wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
+ os.environ['AR'] = ' '.join(quote_arg(w) for w in wrapperlinker)
+
+ # Need a new env to re-run environment loading
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+
+ try:
+ wcc = compiler_from_language(env, lang, MachineChoice.HOST)
+ except EnvironmentException:
+ # always raise in ci, we expect to have a valid ObjC and ObjC++ compiler of some kind
+ if is_ci():
+ self.fail(f'Could not find a compiler for {lang}')
+ if sys.version_info < (3, 11):
+ continue
+ self.skipTest(f'No valid compiler for {lang}.')
+ wlinker = detect_static_linker(env, wcc)
+ del os.environ['AR']
+
+ # Must be the same type since it's a wrapper around the same exelist
+ self.assertIs(type(cc), type(wcc))
+ self.assertIs(type(linker), type(wlinker))
+
+ # Ensure that the exelist is correct
+ self.assertEqual(wcc.get_exelist(), wrappercc)
+ self.assertEqual(wlinker.get_exelist(), wrapperlinker)
+
+ # Ensure that the version detection worked correctly
+ self.assertEqual(cc.version, wcc.version)
+ if hasattr(cc, 'is_64'):
+ self.assertEqual(cc.is_64, wcc.is_64)
def test_always_prefer_c_compiler_for_asm(self):
testdir = os.path.join(self.common_test_dir, '133 c cpp and asm')
@@ -1367,7 +1450,7 @@ class AllPlatformTests(BasePlatformTests):
Test that conflicts between -D for builtin options and the corresponding
long option are detected without false positives or negatives.
'''
- testdir = os.path.join(self.unit_test_dir, '128 long opt vs D')
+ testdir = os.path.join(self.unit_test_dir, '130 long opt vs D')
for opt in ['-Dsysconfdir=/etc', '-Dsysconfdir2=/etc']:
exception_raised = False
@@ -2001,8 +2084,8 @@ class AllPlatformTests(BasePlatformTests):
against what was detected in the binary.
'''
env, cc = get_convincing_fake_env_and_cc(self.builddir, self.prefix)
- expected_uscore = cc._symbols_have_underscore_prefix_searchbin(env)
- list_uscore = cc._symbols_have_underscore_prefix_list(env)
+ expected_uscore = cc._symbols_have_underscore_prefix_searchbin()
+ list_uscore = cc._symbols_have_underscore_prefix_list()
if list_uscore is not None:
self.assertEqual(list_uscore, expected_uscore)
else:
@@ -2014,8 +2097,8 @@ class AllPlatformTests(BasePlatformTests):
against what was detected in the binary.
'''
env, cc = get_convincing_fake_env_and_cc(self.builddir, self.prefix)
- expected_uscore = cc._symbols_have_underscore_prefix_searchbin(env)
- define_uscore = cc._symbols_have_underscore_prefix_define(env)
+ expected_uscore = cc._symbols_have_underscore_prefix_searchbin()
+ define_uscore = cc._symbols_have_underscore_prefix_define()
if define_uscore is not None:
self.assertEqual(define_uscore, expected_uscore)
else:
@@ -2390,10 +2473,10 @@ class AllPlatformTests(BasePlatformTests):
# lexer/parser/interpreter we have tests for.
for (t, f) in [
('10 out of bounds', 'meson.build'),
- ('18 wrong plusassign', 'meson.build'),
- ('56 bad option argument', 'meson_options.txt'),
- ('94 subdir parse error', os.path.join('subdir', 'meson.build')),
- ('95 invalid option file', 'meson_options.txt'),
+ ('17 wrong plusassign', 'meson.build'),
+ ('55 bad option argument', 'meson_options.txt'),
+ ('93 subdir parse error', os.path.join('subdir', 'meson.build')),
+ ('94 invalid option file', 'meson_options.txt'),
]:
tdir = os.path.join(self.src_root, 'test cases', 'failing', t)
@@ -2421,17 +2504,17 @@ class AllPlatformTests(BasePlatformTests):
self.assertIn('ERROR: compiler.has_header_symbol got unknown keyword arguments "prefixxx"', cm.exception.output)
def test_templates(self):
- ninja = mesonbuild.environment.detect_ninja()
+ ninja = mesonbuild.tooldetect.detect_ninja()
if ninja is None:
raise SkipTest('This test currently requires ninja. Fix this once "meson build" works.')
langs = ['c']
env = get_fake_env()
- for l in ['cpp', 'cs', 'd', 'java', 'cuda', 'fortran', 'objc', 'objcpp', 'rust', 'vala']:
+ for l in ['cpp', 'cs', 'cuda', 'd', 'fortran', 'java', 'objc', 'objcpp', 'rust', 'vala']:
try:
comp = detect_compiler_for(env, l, MachineChoice.HOST, True, '')
with tempfile.TemporaryDirectory() as d:
- comp.sanity_check(d, env)
+ comp.sanity_check(d)
langs.append(l)
except EnvironmentException:
pass
@@ -2441,34 +2524,81 @@ class AllPlatformTests(BasePlatformTests):
if is_osx():
langs = [l for l in langs if l != 'd']
- for lang in langs:
- for target_type in ('executable', 'library'):
- with self.subTest(f'Language: {lang}; type: {target_type}'):
- if is_windows() and lang == 'fortran' and target_type == 'library':
- # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way
- # see "test cases/fortran/6 dynamic"
- fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '')
- if fc.get_id() in {'intel-cl', 'pgi'}:
- continue
- # test empty directory
- with tempfile.TemporaryDirectory() as tmpdir:
- self._run(self.meson_command + ['init', '--language', lang, '--type', target_type],
- workdir=tmpdir)
- self._run(self.setup_command + ['--backend=ninja', 'builddir'],
- workdir=tmpdir)
- self._run(ninja,
- workdir=os.path.join(tmpdir, 'builddir'))
- # test directory with existing code file
- if lang in {'c', 'cpp', 'd'}:
- with tempfile.TemporaryDirectory() as tmpdir:
- with open(os.path.join(tmpdir, 'foo.' + lang), 'w', encoding='utf-8') as f:
- f.write('int main(void) {}')
- self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
- elif lang in {'java'}:
- with tempfile.TemporaryDirectory() as tmpdir:
- with open(os.path.join(tmpdir, 'Foo.' + lang), 'w', encoding='utf-8') as f:
- f.write('public class Foo { public static void main() {} }')
- self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
+ def _template_test_fresh(lang, target_type):
+ if is_windows() and lang == 'fortran' and target_type == 'library':
+ # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way
+ # see "test cases/fortran/6 dynamic"
+ fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '')
+ if fc.get_id() in {'intel-cl', 'pgi'}:
+ return
+
+ # test empty directory
+ with tempfile.TemporaryDirectory() as tmpdir:
+ self._run(self.meson_command + ['init', '--language', lang, '--type', target_type],
+ workdir=tmpdir)
+ self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir)
+ self._run(ninja, workdir=os.path.join(tmpdir, 'builddir'))
+
+ # custom executable name
+ if target_type == 'executable':
+ with tempfile.TemporaryDirectory() as tmpdir:
+ self._run(self.meson_command + ['init', '--language', lang, '--type', target_type,
+ '--executable', 'foobar'],
+ workdir=tmpdir)
+ self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir)
+ self._run(ninja, workdir=os.path.join(tmpdir, 'builddir'))
+
+ if lang not in {'cs', 'java'}:
+ exe = os.path.join(tmpdir, 'builddir', 'foobar' + exe_suffix)
+ self.assertTrue(os.path.exists(exe))
+
+ def _template_test_dirty(lang, target_type):
+ if is_windows() and lang == 'fortran' and target_type == 'library':
+ # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way
+ # see "test cases/fortran/6 dynamic"
+ fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '')
+ if fc.get_id() in {'intel-cl', 'pgi'}:
+ return
+
+ # test empty directory
+ with tempfile.TemporaryDirectory() as tmpdir:
+ self._run(self.meson_command + ['init', '--language', lang, '--type', target_type],
+ workdir=tmpdir)
+ self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir)
+ self._run(ninja, workdir=os.path.join(tmpdir, 'builddir'))
+
+ # Check for whether we're doing source collection by repeating
+ # with a bogus file we should pick up (and then fail to compile).
+ with tempfile.TemporaryDirectory() as tmpdir:
+ suffix = lang_suffixes[lang][0]
+ # Assume that this is a good enough string to error out
+ # in all languages.
+ with open(os.path.join(tmpdir, 'bar.' + suffix), 'w', encoding='utf-8') as f:
+ f.write('error bar')
+ self._run(self.meson_command + ['init', '--language', lang, '--type', target_type],
+ workdir=tmpdir)
+ self._run(self.setup_command + ['--backend=ninja', 'builddir'],
+ workdir=tmpdir)
+ with self.assertRaises(subprocess.CalledProcessError):
+ self._run(ninja,
+ workdir=os.path.join(tmpdir, 'builddir'))
+
+ # test directory with existing code file
+ if lang in {'c', 'cpp', 'd'}:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ with open(os.path.join(tmpdir, 'foo.' + lang), 'w', encoding='utf-8') as f:
+ f.write('int main(void) {}')
+ self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
+
+ elif lang in {'java'}:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ with open(os.path.join(tmpdir, 'Foo.' + lang), 'w', encoding='utf-8') as f:
+ f.write('public class Foo { public static void main() {} }')
+ self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
+
+ for lang, target_type, fresh in itertools.product(langs, ('executable', 'library'), (True, False)):
+ with self.subTest(f'Language: {lang}; type: {target_type}; fresh: {fresh}'):
+ _template_test_fresh(lang, target_type) if fresh else _template_test_dirty(lang, target_type)
def test_compiler_run_command(self):
'''
@@ -2499,10 +2629,9 @@ class AllPlatformTests(BasePlatformTests):
def test_flock(self):
exception_raised = False
with tempfile.TemporaryDirectory() as tdir:
- os.mkdir(os.path.join(tdir, 'meson-private'))
- with BuildDirLock(tdir):
+ with DirectoryLock(tdir, 'lock', DirectoryLockAction.FAIL, 'failed to lock directory'):
try:
- with BuildDirLock(tdir):
+ with DirectoryLock(tdir, 'lock', DirectoryLockAction.FAIL, 'expected failure'):
pass
except MesonException:
exception_raised = True
@@ -2677,35 +2806,35 @@ class AllPlatformTests(BasePlatformTests):
out = self.init(testdir, extra_args=['--profile-self', '--fatal-meson-warnings'])
self.assertNotIn('[default: true]', out)
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('default_library'), 'static')
- self.assertEqual(obj.optstore.get_value('warning_level'), '1')
- self.assertEqual(obj.optstore.get_value(OptionKey('set_sub_opt', '')), True)
- self.assertEqual(obj.optstore.get_value(OptionKey('subp_opt', 'subp')), 'default3')
+ self.assertEqual(obj.optstore.get_value_for('default_library'), 'static')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '1')
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('set_sub_opt', '')), True)
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('subp_opt', 'subp')), 'default3')
self.wipe()
# warning_level is special, it's --warnlevel instead of --warning-level
# for historical reasons
self.init(testdir, extra_args=['--warnlevel=2', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '2')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '2')
self.setconf('--warnlevel=3')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '3')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '3')
self.setconf('--warnlevel=everything')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), 'everything')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), 'everything')
self.wipe()
# But when using -D syntax, it should be 'warning_level'
self.init(testdir, extra_args=['-Dwarning_level=2', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '2')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '2')
self.setconf('-Dwarning_level=3')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '3')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '3')
self.setconf('-Dwarning_level=everything')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), 'everything')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), 'everything')
self.wipe()
# Mixing --option and -Doption is forbidden
@@ -2729,15 +2858,15 @@ class AllPlatformTests(BasePlatformTests):
# --default-library should override default value from project()
self.init(testdir, extra_args=['--default-library=both', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('default_library'), 'both')
+ self.assertEqual(obj.optstore.get_value_for('default_library'), 'both')
self.setconf('--default-library=shared')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('default_library'), 'shared')
+ self.assertEqual(obj.optstore.get_value_for('default_library'), 'shared')
if self.backend is Backend.ninja:
# reconfigure target works only with ninja backend
self.build('reconfigure')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('default_library'), 'shared')
+ self.assertEqual(obj.optstore.get_value_for('default_library'), 'shared')
self.wipe()
# Should fail on unknown options
@@ -2774,22 +2903,22 @@ class AllPlatformTests(BasePlatformTests):
# Test we can set subproject option
self.init(testdir, extra_args=['-Dsubp:subp_opt=foo', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value(OptionKey('subp_opt', 'subp')), 'foo')
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('subp_opt', 'subp')), 'foo')
self.wipe()
# c_args value should be parsed with split_args
self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dfoo', '-Dbar', '-Dthird=one two'])
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('c_args')), ['-Dfoo', '-Dbar', '-Dthird=one two'])
self.setconf('-Dc_args="foo bar" one two')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['foo bar', 'one', 'two'])
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('c_args')), ['foo bar', 'one', 'two'])
self.wipe()
self.init(testdir, extra_args=['-Dset_percent_opt=myoption%', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value(OptionKey('set_percent_opt', '')), 'myoption%')
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('set_percent_opt', '')), 'myoption%')
self.wipe()
# Setting a 2nd time the same option should override the first value
@@ -2800,19 +2929,19 @@ class AllPlatformTests(BasePlatformTests):
'-Dc_args=-Dfoo', '-Dc_args=-Dbar',
'-Db_lundef=false', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('bindir'), 'bar')
- self.assertEqual(obj.optstore.get_value('buildtype'), 'release')
- self.assertEqual(obj.optstore.get_value('b_sanitize'), ['thread'])
- self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dbar'])
+ self.assertEqual(obj.optstore.get_value_for('bindir'), 'bar')
+ self.assertEqual(obj.optstore.get_value_for('buildtype'), 'release')
+ self.assertEqual(obj.optstore.get_value_for('b_sanitize'), ['thread'])
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('c_args')), ['-Dbar'])
self.setconf(['--bindir=bar', '--bindir=foo',
'-Dbuildtype=release', '-Dbuildtype=plain',
'-Db_sanitize=thread', '-Db_sanitize=address',
'-Dc_args=-Dbar', '-Dc_args=-Dfoo'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('bindir'), 'foo')
- self.assertEqual(obj.optstore.get_value('buildtype'), 'plain')
- self.assertEqual(obj.optstore.get_value('b_sanitize'), ['address'])
- self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dfoo'])
+ self.assertEqual(obj.optstore.get_value_for('bindir'), 'foo')
+ self.assertEqual(obj.optstore.get_value_for('buildtype'), 'plain')
+ self.assertEqual(obj.optstore.get_value_for('b_sanitize'), ['address'])
+ self.assertEqual(obj.optstore.get_value_for(OptionKey('c_args')), ['-Dfoo'])
self.wipe()
except KeyError:
# Ignore KeyError, it happens on CI for compilers that does not
@@ -2826,25 +2955,25 @@ class AllPlatformTests(BasePlatformTests):
# Verify default values when passing no args
self.init(testdir)
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '0')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '0')
self.wipe()
# verify we can override w/ --warnlevel
self.init(testdir, extra_args=['--warnlevel=1'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '1')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '1')
self.setconf('--warnlevel=0')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '0')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '0')
self.wipe()
# verify we can override w/ -Dwarning_level
self.init(testdir, extra_args=['-Dwarning_level=1'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '1')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '1')
self.setconf('-Dwarning_level=0')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.optstore.get_value('warning_level'), '0')
+ self.assertEqual(obj.optstore.get_value_for('warning_level'), '0')
self.wipe()
def test_feature_check_usage_subprojects(self):
@@ -2981,6 +3110,121 @@ class AllPlatformTests(BasePlatformTests):
self.wipe()
self.init(testdir, extra_args=['-Dstart_native=true'], override_envvars=env)
+ @skipIf(is_osx(), 'Not implemented for Darwin yet')
+ @skipIf(is_windows(), 'POSIX only')
+ def test_python_build_config_extensions(self):
+ testdir = os.path.join(self.unit_test_dir,
+ '126 python extension')
+
+ VERSION_INFO_KEYS = ('major', 'minor', 'micro', 'releaselevel', 'serial')
+ EXTENSION_SUFFIX = '.extension-suffix.so'
+ STABLE_ABI_SUFFIX = '.stable-abi-suffix.so'
+ # macOS framework builds put libpython in PYTHONFRAMEWORKPREFIX.
+ LIBDIR = (sysconfig.get_config_var('PYTHONFRAMEWORKPREFIX') or
+ sysconfig.get_config_var('LIBDIR'))
+
+ python_build_config = {
+ 'schema_version': '1.0',
+ 'base_interpreter': sys.executable,
+ 'base_prefix': '/usr',
+ 'platform': sysconfig.get_platform(),
+ 'language': {
+ 'version': sysconfig.get_python_version(),
+ 'version_info': {key: getattr(sys.version_info, key) for key in VERSION_INFO_KEYS}
+ },
+ 'implementation': {
+ attr: (
+ getattr(sys.implementation, attr)
+ if attr != 'version' else
+ {key: getattr(sys.implementation.version, key) for key in VERSION_INFO_KEYS}
+ )
+ for attr in dir(sys.implementation)
+ if not attr.startswith('__')
+ },
+ 'abi': {
+ 'flags': list(sys.abiflags),
+ 'extension_suffix': EXTENSION_SUFFIX,
+ 'stable_abi_suffix': STABLE_ABI_SUFFIX,
+ },
+ 'suffixes': {
+ 'source': ['.py'],
+ 'bytecode': ['.pyc'],
+ 'optimized_bytecode': ['.pyc'],
+ 'debug_bytecode': ['.pyc'],
+ 'extensions': [EXTENSION_SUFFIX, STABLE_ABI_SUFFIX, '.so'],
+ },
+ 'libpython': {
+ 'dynamic': os.path.join(LIBDIR, sysconfig.get_config_var('LDLIBRARY')),
+ 'static': os.path.join(LIBDIR, sysconfig.get_config_var('LIBRARY')),
+ # set it to False on PyPy, since dylib is optional, but also
+ # the value is currently wrong:
+ # https://github.com/pypy/pypy/issues/5249
+ 'link_extensions': '__pypy__' not in sys.builtin_module_names,
+ },
+ 'c_api': {
+ 'headers': sysconfig.get_config_var('INCLUDEPY'),
+ }
+ }
+
+ py3library = sysconfig.get_config_var('PY3LIBRARY')
+ if py3library is not None:
+ python_build_config['libpython']['dynamic_stableabi'] = os.path.join(LIBDIR, py3library)
+
+ build_stable_abi = sysconfig.get_config_var('Py_GIL_DISABLED') != 1 or sys.version_info >= (3, 15)
+ intro_installed_file = os.path.join(self.builddir, 'meson-info', 'intro-installed.json')
+ expected_files = [
+ os.path.join(self.builddir, 'foo' + EXTENSION_SUFFIX),
+ ]
+ if build_stable_abi:
+ expected_files += [
+ os.path.join(self.builddir, 'foo_stable' + STABLE_ABI_SUFFIX),
+ ]
+ if is_cygwin():
+ expected_files += [
+ os.path.join(self.builddir, 'foo' + EXTENSION_SUFFIX.replace('.so', '.dll.a')),
+ ]
+ if build_stable_abi:
+ expected_files += [
+ os.path.join(self.builddir, 'foo_stable' + STABLE_ABI_SUFFIX.replace('.so', '.dll.a')),
+ ]
+
+ for with_pkgconfig in (False, True):
+ with self.subTest(with_pkgconfig=with_pkgconfig):
+ if with_pkgconfig:
+ libpc = sysconfig.get_config_var('LIBPC')
+ if libpc is None:
+ continue
+ python_build_config['c_api']['pkgconfig_path'] = libpc
+ # Old Ubuntu versions have incorrect LIBDIR, skip testing non-pkgconfig variant there.
+ elif not os.path.exists(python_build_config['libpython']['dynamic']):
+ continue
+
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as python_build_config_file:
+ json.dump(python_build_config, fp=python_build_config_file)
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as cross_file:
+ cross_file.write(
+ textwrap.dedent(f'''
+ [binaries]
+ pkg-config = 'pkg-config'
+
+ [built-in options]
+ python.build_config = '{python_build_config_file.name}'
+ '''.strip())
+ )
+ cross_file.flush()
+
+ for extra_args in (
+ ['--python.build-config', python_build_config_file.name],
+ ['--cross-file', cross_file.name],
+ ):
+ with self.subTest(extra_args=extra_args):
+ self.init(testdir, extra_args=extra_args)
+ self.build()
+ with open(intro_installed_file) as f:
+ intro_installed = json.load(f)
+ self.assertEqual(sorted(expected_files), sorted(intro_installed))
+ self.wipe()
+
def __reconfigure(self):
# Set an older version to force a reconfigure from scratch
filename = os.path.join(self.privatedir, 'coredata.dat')
@@ -3242,10 +3486,15 @@ class AllPlatformTests(BasePlatformTests):
def test_identity_cross(self):
testdir = os.path.join(self.unit_test_dir, '69 cross')
# Do a build to generate a cross file where the host is this target
- self.init(testdir, extra_args=['-Dgenerate=true'])
+ # build.c_args is ignored here.
+ self.init(testdir, extra_args=['-Dgenerate=true', '-Dc_args=-funroll-loops',
+ '-Dbuild.c_args=-pedantic'])
+ self.meson_native_files = [os.path.join(self.builddir, "nativefile")]
+ self.assertTrue(os.path.exists(self.meson_native_files[0]))
self.meson_cross_files = [os.path.join(self.builddir, "crossfile")]
self.assertTrue(os.path.exists(self.meson_cross_files[0]))
- # Now verify that this is detected as cross
+ # Now verify that this is detected as cross and build options are
+ # processed correctly
self.new_builddir()
self.init(testdir)
@@ -3264,6 +3513,11 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.unit_test_dir, '58 introspect buildoptions')
self._run(self.mconf_command + [testdir])
+ @skip_if_not_language('rust')
+ def test_meson_configure_srcdir(self):
+ testdir = os.path.join(self.rust_test_dir, '20 rust and cpp')
+ self._run(self.mconf_command + [testdir])
+
def test_introspect_buildoptions_cross_only(self):
testdir = os.path.join(self.unit_test_dir, '82 cross only introspect')
testfile = os.path.join(testdir, 'meson.build')
@@ -3612,6 +3866,8 @@ class AllPlatformTests(BasePlatformTests):
# Account for differences in output
res_wb = [i for i in res_wb if i['type'] != 'custom']
for i in res_wb:
+ if i['id'] == 'test1@exe':
+ i['build_by_default'] = 'unknown'
i['filename'] = [os.path.relpath(x, self.builddir) for x in i['filename']]
for k in ('install_filename', 'dependencies', 'win_subsystem'):
if k in i:
@@ -3730,7 +3986,7 @@ class AllPlatformTests(BasePlatformTests):
},
{
'name': 'bugDep1',
- 'required': True,
+ 'required': 'unknown',
'version': [],
'has_fallback': False,
'conditional': False
@@ -3748,7 +4004,21 @@ class AllPlatformTests(BasePlatformTests):
'version': ['>=1.0.0', '<=99.9.9'],
'has_fallback': True,
'conditional': True
- }
+ },
+ {
+ 'conditional': False,
+ 'has_fallback': False,
+ 'name': 'unknown',
+ 'required': False,
+ 'version': 'unknown'
+ },
+ {
+ 'conditional': False,
+ 'has_fallback': False,
+ 'name': 'unknown',
+ 'required': False,
+ 'version': 'unknown'
+ },
]
self.maxDiff = None
self.assertListEqual(res_nb, expected)
@@ -3859,7 +4129,7 @@ class AllPlatformTests(BasePlatformTests):
return basename
def get_shared_lib_name(basename: str) -> str:
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
return f'lib{basename}.dll'
elif is_windows():
return f'{basename}.dll'
@@ -4072,16 +4342,16 @@ class AllPlatformTests(BasePlatformTests):
self.assertTrue((covdir / f).is_file(), msg=f'{f} is not a file')
def test_coverage(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4092,16 +4362,16 @@ class AllPlatformTests(BasePlatformTests):
self._check_coverage_files()
def test_coverage_complex(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '105 generatorcustom')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4112,16 +4382,16 @@ class AllPlatformTests(BasePlatformTests):
self._check_coverage_files()
def test_coverage_html(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4132,16 +4402,16 @@ class AllPlatformTests(BasePlatformTests):
self._check_coverage_files(['html'])
def test_coverage_text(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4152,16 +4422,16 @@ class AllPlatformTests(BasePlatformTests):
self._check_coverage_files(['text'])
def test_coverage_xml(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4172,16 +4442,16 @@ class AllPlatformTests(BasePlatformTests):
self._check_coverage_files(['xml'])
def test_coverage_escaping(self):
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with coverage on MSYS2')
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.tooldetect.detect_gcovr()
if not gcovr_exe:
raise SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '243 escape++')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
- if not mesonbuild.environment.detect_llvm_cov():
+ if not mesonbuild.tooldetect.detect_llvm_cov():
raise SkipTest('llvm-cov not found')
if cc.get_id() == 'msvc':
raise SkipTest('Test only applies to non-MSVC compilers')
@@ -4255,6 +4525,14 @@ class AllPlatformTests(BasePlatformTests):
self.build()
self.run_tests()
+ def test_custom_target_index_as_test_prereq(self):
+ if self.backend is not Backend.ninja:
+ raise SkipTest('ninja backend needed for "meson test" to build test dependencies')
+
+ testdir = os.path.join(self.unit_test_dir, '132 custom target index test')
+ self.init(testdir)
+ self.run_tests()
+
@skipUnless(is_linux() and (re.search('^i.86$|^x86$|^x64$|^x86_64$|^amd64$', platform.processor()) is not None),
'Requires ASM compiler for x86 or x86_64 platform currently only available on Linux CI runners')
def test_nostdlib(self):
@@ -4453,6 +4731,10 @@ class AllPlatformTests(BasePlatformTests):
self.assertIn(f'TEST_C="{expected}"', o)
self.assertIn('export TEST_C', o)
+ cmd = self.meson_command + ['devenv', '-C', self.builddir] + python_command + ['-c', 'import sys; sys.exit(42)']
+ result = subprocess.run(cmd, encoding='utf-8')
+ self.assertEqual(result.returncode, 42)
+
def test_clang_format_check(self):
if self.backend is not Backend.ninja:
raise SkipTest(f'Skipping clang-format tests with {self.backend.name} backend')
@@ -4726,120 +5008,140 @@ class AllPlatformTests(BasePlatformTests):
expected = {
'targets': {
f'{self.builddir}/out1-notag.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out1-notag.txt',
'install_rpath': None,
'tag': None,
'subproject': None,
},
f'{self.builddir}/out2-notag.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out2-notag.txt',
'install_rpath': None,
'tag': None,
'subproject': None,
},
f'{self.builddir}/libstatic.a': {
+ 'build_rpaths': [],
'destination': '{libdir_static}/libstatic.a',
'install_rpath': None,
'tag': 'devel',
'subproject': None,
},
f'{self.builddir}/' + exe_name('app'): {
+ 'build_rpaths': [],
'destination': '{bindir}/' + exe_name('app'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/' + exe_name('app-otherdir'): {
+ 'build_rpaths': [],
'destination': '{prefix}/otherbin/' + exe_name('app-otherdir'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/subdir/' + exe_name('app2'): {
+ 'build_rpaths': [],
'destination': '{bindir}/' + exe_name('app2'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/' + shared_lib_name('shared'): {
+ 'build_rpaths': [],
'destination': '{libdir_shared}/' + shared_lib_name('shared'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/' + shared_lib_name('both'): {
+ 'build_rpaths': [],
'destination': '{libdir_shared}/' + shared_lib_name('both'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/' + static_lib_name('both'): {
+ 'build_rpaths': [],
'destination': '{libdir_static}/' + static_lib_name('both'),
'install_rpath': None,
'tag': 'devel',
'subproject': None,
},
f'{self.builddir}/' + shared_lib_name('bothcustom'): {
+ 'build_rpaths': [],
'destination': '{libdir_shared}/' + shared_lib_name('bothcustom'),
'install_rpath': None,
'tag': 'custom',
'subproject': None,
},
f'{self.builddir}/' + static_lib_name('bothcustom'): {
+ 'build_rpaths': [],
'destination': '{libdir_static}/' + static_lib_name('bothcustom'),
'install_rpath': None,
'tag': 'custom',
'subproject': None,
},
f'{self.builddir}/subdir/' + shared_lib_name('both2'): {
+ 'build_rpaths': [],
'destination': '{libdir_shared}/' + shared_lib_name('both2'),
'install_rpath': None,
'tag': 'runtime',
'subproject': None,
},
f'{self.builddir}/subdir/' + static_lib_name('both2'): {
+ 'build_rpaths': [],
'destination': '{libdir_static}/' + static_lib_name('both2'),
'install_rpath': None,
'tag': 'devel',
'subproject': None,
},
f'{self.builddir}/out1-custom.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out1-custom.txt',
'install_rpath': None,
'tag': 'custom',
'subproject': None,
},
f'{self.builddir}/out2-custom.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out2-custom.txt',
'install_rpath': None,
'tag': 'custom',
'subproject': None,
},
f'{self.builddir}/out3-custom.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out3-custom.txt',
'install_rpath': None,
'tag': 'custom',
'subproject': None,
},
f'{self.builddir}/subdir/out1.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out1.txt',
'install_rpath': None,
'tag': None,
'subproject': None,
},
f'{self.builddir}/subdir/out2.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out2.txt',
'install_rpath': None,
'tag': None,
'subproject': None,
},
f'{self.builddir}/out-devel.h': {
+ 'build_rpaths': [],
'destination': '{includedir}/out-devel.h',
'install_rpath': None,
'tag': 'devel',
'subproject': None,
},
f'{self.builddir}/out3-notag.txt': {
+ 'build_rpaths': [],
'destination': '{datadir}/out3-notag.txt',
'install_rpath': None,
'tag': None,
@@ -5185,7 +5487,7 @@ class AllPlatformTests(BasePlatformTests):
self.__test_multi_stds(test_objc=True)
def test_slice(self):
- testdir = os.path.join(self.unit_test_dir, '126 test slice')
+ testdir = os.path.join(self.unit_test_dir, '128 test slice')
self.init(testdir)
self.build()
@@ -5197,7 +5499,9 @@ class AllPlatformTests(BasePlatformTests):
'10/10': [10],
}.items():
output = self._run(self.mtest_command + ['--slice=' + arg])
- tests = sorted([ int(x) for x in re.findall(r'\n[ 0-9]+/[0-9]+ test-([0-9]*)', output) ])
+ tests = sorted([
+ int(x) for x in re.findall(r'^[ 0-9]+/[0-9]+ test_slice:test-([0-9]*)', output, flags=re.MULTILINE)
+ ])
self.assertEqual(tests, expectation)
for arg, expectation in {'': 'error: argument --slice: value does not conform to format \'SLICE/NUM_SLICES\'',
@@ -5217,7 +5521,7 @@ class AllPlatformTests(BasePlatformTests):
env = get_fake_env()
cc = detect_c_compiler(env, MachineChoice.HOST)
has_rsp = cc.linker.id in {
- 'ld.bfd', 'ld.gold', 'ld.lld', 'ld.mold', 'ld.qcld', 'ld.wasm',
+ 'ld.bfd', 'ld.eld', 'ld.gold', 'ld.lld', 'ld.mold', 'ld.qcld', 'ld.wasm',
'link', 'lld-link', 'mwldarm', 'mwldeppc', 'optlink', 'xilink',
}
self.assertEqual(cc.linker.get_accepts_rsp(), has_rsp)
diff --git a/unittests/cargotests.py b/unittests/cargotests.py
index d1ac838..643cece 100644
--- a/unittests/cargotests.py
+++ b/unittests/cargotests.py
@@ -8,8 +8,11 @@ import tempfile
import textwrap
import typing as T
-from mesonbuild.cargo import builder, cfg, load_wraps
+from mesonbuild.cargo import cfg
from mesonbuild.cargo.cfg import TokenType
+from mesonbuild.cargo.interpreter import load_cargo_lock
+from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace
+from mesonbuild.cargo.toml import load_toml
from mesonbuild.cargo.version import convert
@@ -101,6 +104,12 @@ class CargoCfgTest(unittest.TestCase):
(TokenType.IDENTIFIER, 'unix'),
(TokenType.RPAREN, None),
]),
+ ('cfg(windows)', [
+ (TokenType.CFG, None),
+ (TokenType.LPAREN, None),
+ (TokenType.IDENTIFIER, 'windows'),
+ (TokenType.RPAREN, None),
+ ]),
]
for data, expected in cases:
with self.subTest():
@@ -130,69 +139,51 @@ class CargoCfgTest(unittest.TestCase):
cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86")),
cfg.Equal(cfg.Identifier("target_os"), cfg.String("linux")),
]))),
+ ('cfg(all(any(target_os = "android", target_os = "linux"), any(custom_cfg)))',
+ cfg.All([
+ cfg.Any([
+ cfg.Equal(cfg.Identifier("target_os"), cfg.String("android")),
+ cfg.Equal(cfg.Identifier("target_os"), cfg.String("linux")),
+ ]),
+ cfg.Any([
+ cfg.Identifier("custom_cfg"),
+ ]),
+ ])),
]
for data, expected in cases:
with self.subTest():
self.assertEqual(cfg.parse(iter(cfg.lexer(data))), expected)
- def test_ir_to_meson(self) -> None:
- build = builder.Builder('')
- HOST_MACHINE = build.identifier('host_machine')
-
+ def test_eval_ir(self) -> None:
+ d = {
+ 'target_os': 'unix',
+ 'unix': '',
+ }
cases = [
- ('target_os = "windows"',
- build.equal(build.method('system', HOST_MACHINE),
- build.string('windows'))),
- ('target_arch = "x86"',
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86'))),
- ('target_family = "unix"',
- build.equal(build.method('system', HOST_MACHINE),
- build.string('unix'))),
- ('not(target_arch = "x86")',
- build.not_(build.equal(
- build.method('cpu_family', HOST_MACHINE),
- build.string('x86')))),
- ('any(target_arch = "x86", target_arch = "x86_64")',
- build.or_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86')),
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86_64')))),
- ('any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")',
- build.or_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86')),
- build.or_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86_64')),
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('aarch64'))))),
- ('all(target_arch = "x86", target_arch = "x86_64")',
- build.and_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86')),
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86_64')))),
- ('all(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")',
- build.and_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86')),
- build.and_(
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('x86_64')),
- build.equal(build.method('cpu_family', HOST_MACHINE),
- build.string('aarch64'))))),
+ ('target_os = "windows"', False),
+ ('target_os = "unix"', True),
+ ('doesnotexist = "unix"', False),
+ ('not(target_os = "windows")', True),
+ ('any(target_os = "windows", target_arch = "x86_64")', False),
+ ('any(target_os = "windows", target_os = "unix")', True),
+ ('all(target_os = "windows", target_os = "unix")', False),
+ ('all(not(target_os = "windows"), target_os = "unix")', True),
+ ('any(unix, windows)', True),
+ ('all()', True),
+ ('any()', False),
+ ('cfg(unix)', True),
+ ('cfg(windows)', False),
]
for data, expected in cases:
with self.subTest():
- value = cfg.ir_to_meson(cfg.parse(iter(cfg.lexer(data))), build)
+ value = cfg.eval_cfg(data, d)
self.assertEqual(value, expected)
class CargoLockTest(unittest.TestCase):
- def test_cargo_lock(self) -> None:
+ def test_wraps(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
- with open(os.path.join(tmpdir, 'Cargo.lock'), 'w', encoding='utf-8') as f:
+ filename = os.path.join(tmpdir, 'Cargo.lock')
+ with open(filename, 'w', encoding='utf-8') as f:
f.write(textwrap.dedent('''\
version = 3
[[package]]
@@ -204,18 +195,379 @@ class CargoLockTest(unittest.TestCase):
name = "bar"
version = "0.1"
source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#23c5599424cc75ec66618891c915d9f490f6e4c2"
+ [[package]]
+ name = "member"
+ version = "0.1"
+ source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#23c5599424cc75ec66618891c915d9f490f6e4c2"
'''))
- wraps = load_wraps(tmpdir, 'subprojects')
+ cargolock = load_cargo_lock(filename, 'subprojects')
+ wraps = cargolock.wraps
self.assertEqual(len(wraps), 2)
- self.assertEqual(wraps[0].name, 'foo-0.1-rs')
- self.assertEqual(wraps[0].directory, 'foo-0.1')
- self.assertEqual(wraps[0].type, 'file')
- self.assertEqual(wraps[0].get('method'), 'cargo')
- self.assertEqual(wraps[0].get('source_url'), 'https://crates.io/api/v1/crates/foo/0.1/download')
- self.assertEqual(wraps[0].get('source_hash'), '8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb')
- self.assertEqual(wraps[1].name, 'bar-0.1-rs')
- self.assertEqual(wraps[1].directory, 'bar')
- self.assertEqual(wraps[1].type, 'git')
- self.assertEqual(wraps[1].get('method'), 'cargo')
- self.assertEqual(wraps[1].get('url'), 'https://github.com/gtk-rs/gtk-rs-core')
- self.assertEqual(wraps[1].get('revision'), '23c5599424cc75ec66618891c915d9f490f6e4c2')
+ self.assertEqual(wraps['foo-0.1-rs'].name, 'foo-0.1-rs')
+ self.assertEqual(wraps['foo-0.1-rs'].directory, 'foo-0.1')
+ self.assertEqual(wraps['foo-0.1-rs'].type, 'file')
+ self.assertEqual(wraps['foo-0.1-rs'].get('method'), 'cargo')
+ self.assertEqual(wraps['foo-0.1-rs'].get('source_url'), 'https://crates.io/api/v1/crates/foo/0.1/download')
+ self.assertEqual(wraps['foo-0.1-rs'].get('source_hash'), '8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].name, 'gtk-rs-core-0.19')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].directory, 'gtk-rs-core-0.19')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].type, 'git')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].get('method'), 'cargo')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].get('url'), 'https://github.com/gtk-rs/gtk-rs-core')
+ self.assertEqual(wraps['gtk-rs-core-0.19'].get('revision'), '23c5599424cc75ec66618891c915d9f490f6e4c2')
+ self.assertEqual(list(wraps['gtk-rs-core-0.19'].provided_deps), ['gtk-rs-core-0.19', 'bar-0.1-rs', 'member-0.1-rs'])
+
+class CargoTomlTest(unittest.TestCase):
+ CARGO_TOML_1 = textwrap.dedent('''\
+ [package]
+ name = "mandelbrot"
+ version = "0.1.0"
+ authors = ["Sebastian Dröge <sebastian@centricular.com>"]
+ edition = "2018"
+ license = "GPL-3.0"
+
+ [package.metadata.docs.rs]
+ all-features = true
+ rustc-args = [
+ "--cfg",
+ "docsrs",
+ ]
+ rustdoc-args = [
+ "--cfg",
+ "docsrs",
+ "--generate-link-to-definition",
+ ]
+
+ [dependencies]
+ gtk = { package = "gtk4", version = "0.9" }
+ num-complex = "0.4"
+ rayon = "1.0"
+ once_cell = "1"
+ async-channel = "2.0"
+ zerocopy = { version = "0.7", features = ["derive"] }
+
+ [lints.rust]
+ unknown_lints = "allow"
+ unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)' ] }
+
+ [lints.clippy]
+ pedantic = {level = "warn", priority = -1}
+
+ [dev-dependencies.gir-format-check]
+ version = "^0.1"
+ ''')
+
+ CARGO_TOML_2 = textwrap.dedent('''\
+ [package]
+ name = "pango"
+ edition = "2021"
+ rust-version = "1.70"
+ version = "0.20.4"
+ authors = ["The gtk-rs Project Developers"]
+
+ [package.metadata.system-deps.pango]
+ name = "pango"
+ version = "1.40"
+
+ [package.metadata.system-deps.pango.v1_42]
+ version = "1.42"
+
+ [lib]
+ name = "pango"
+
+ [[test]]
+ name = "check_gir"
+ path = "tests/check_gir.rs"
+
+ [features]
+ v1_42 = ["pango-sys/v1_42"]
+ v1_44 = [
+ "v1_42",
+ "pango-sys/v1_44",
+ ]
+ ''')
+
+ CARGO_TOML_3 = textwrap.dedent('''\
+ [package]
+ name = "bits"
+ edition = "2021"
+ rust-version = "1.70"
+ version = "0.1.0"
+
+ [lib]
+ proc-macro = true
+ crate-type = ["lib"] # ignored
+ ''')
+
+ CARGO_TOML_WS = textwrap.dedent('''\
+ [workspace]
+ resolver = "2"
+ members = ["tutorial"]
+
+ [workspace.package]
+ version = "0.14.0-alpha.1"
+ repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
+ edition = "2021"
+ rust-version = "1.83"
+
+ [workspace.dependencies]
+ glib = { path = "glib" }
+ gtk = { package = "gtk4", version = "0.9" }
+ once_cell = "1.0"
+ syn = { version = "2", features = ["parse"] }
+
+ [workspace.lints.rust]
+ warnings = "deny"
+ ''')
+
+ def test_cargo_toml_ws_lints(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_WS)
+ workspace_toml = load_toml(fname)
+
+ workspace = Workspace.from_raw(workspace_toml, tmpdir)
+ pkg = Manifest.from_raw({'package': {'name': 'foo'},
+ 'lints': {'workspace': True}}, 'Cargo.toml', workspace)
+ lints = pkg.lints
+ self.assertEqual(lints[0].name, 'warnings')
+ self.assertEqual(lints[0].level, 'deny')
+ self.assertEqual(lints[0].priority, 0)
+
+ pkg = Manifest.from_raw({'package': {'name': 'bar'}}, 'Cargo.toml', workspace)
+ self.assertEqual(pkg.lints, [])
+
+ def test_cargo_toml_ws_package(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_WS)
+ workspace_toml = load_toml(fname)
+
+ workspace = Workspace.from_raw(workspace_toml, tmpdir)
+ pkg = Package.from_raw({'name': 'foo', 'version': {'workspace': True}}, workspace)
+ self.assertEqual(pkg.name, 'foo')
+ self.assertEqual(pkg.version, '0.14.0-alpha.1')
+ self.assertEqual(pkg.edition, '2015')
+ self.assertEqual(pkg.repository, None)
+
+ def test_cargo_toml_ws_dependency(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_WS)
+ workspace_toml = load_toml(fname)
+
+ workspace = Workspace.from_raw(workspace_toml, tmpdir)
+ dep = Dependency.from_raw('glib', {'workspace': True}, 'member', workspace)
+ self.assertEqual(dep.package, 'glib')
+ self.assertEqual(dep.version, '')
+ self.assertEqual(dep.meson_version, [])
+ self.assertEqual(dep.path, os.path.join('..', 'glib'))
+ self.assertEqual(dep.features, [])
+
+ dep = Dependency.from_raw('gtk', {'workspace': True}, 'member', workspace)
+ self.assertEqual(dep.package, 'gtk4')
+ self.assertEqual(dep.version, '0.9')
+ self.assertEqual(dep.meson_version, ['>= 0.9', '< 0.10'])
+ self.assertEqual(dep.api, '0.9')
+ self.assertEqual(dep.features, [])
+
+ dep = Dependency.from_raw('once_cell', {'workspace': True, 'optional': True}, 'member', workspace)
+ self.assertEqual(dep.package, 'once_cell')
+ self.assertEqual(dep.version, '1.0')
+ self.assertEqual(dep.meson_version, ['>= 1.0', '< 2'])
+ self.assertEqual(dep.api, '1')
+ self.assertEqual(dep.features, [])
+ self.assertTrue(dep.optional)
+
+ dep = Dependency.from_raw('syn', {'workspace': True, 'features': ['full']}, 'member', workspace)
+ self.assertEqual(dep.package, 'syn')
+ self.assertEqual(dep.version, '2')
+ self.assertEqual(dep.meson_version, ['>= 2', '< 3'])
+ self.assertEqual(dep.api, '2')
+ self.assertEqual(sorted(set(dep.features)), ['full', 'parse'])
+
+ def test_cargo_toml_package(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_1)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(manifest.package.name, 'mandelbrot')
+ self.assertEqual(manifest.package.version, '0.1.0')
+ self.assertEqual(manifest.package.authors[0], 'Sebastian Dröge <sebastian@centricular.com>')
+ self.assertEqual(manifest.package.edition, '2018')
+ self.assertEqual(manifest.package.license, 'GPL-3.0')
+
+ print(manifest.package.metadata)
+ self.assertEqual(len(manifest.package.metadata), 1)
+
+ def test_cargo_toml_lints(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_1)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(len(manifest.lints), 3)
+ self.assertEqual(manifest.lints[0].name, 'clippy::pedantic')
+ self.assertEqual(manifest.lints[0].level, 'warn')
+ self.assertEqual(manifest.lints[0].priority, -1)
+ self.assertEqual(manifest.lints[0].check_cfg, None)
+
+ self.assertEqual(manifest.lints[1].name, 'unknown_lints')
+ self.assertEqual(manifest.lints[1].level, 'allow')
+ self.assertEqual(manifest.lints[1].priority, 0)
+ self.assertEqual(manifest.lints[1].check_cfg, None)
+
+ self.assertEqual(manifest.lints[2].name, 'unexpected_cfgs')
+ self.assertEqual(manifest.lints[2].level, 'deny')
+ self.assertEqual(manifest.lints[2].priority, 0)
+ self.assertEqual(manifest.lints[2].check_cfg, ['cfg(test)', 'cfg(MESON)'])
+
+ def test_cargo_toml_lints_to_args(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_1)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(manifest.lints[0].to_arguments(False), ['-W', 'clippy::pedantic'])
+ self.assertEqual(manifest.lints[0].to_arguments(True), ['-W', 'clippy::pedantic'])
+ self.assertEqual(manifest.lints[1].to_arguments(False), ['-A', 'unknown_lints'])
+ self.assertEqual(manifest.lints[1].to_arguments(True), ['-A', 'unknown_lints'])
+ self.assertEqual(manifest.lints[2].to_arguments(False), ['-D', 'unexpected_cfgs'])
+ self.assertEqual(manifest.lints[2].to_arguments(True),
+ ['-D', 'unexpected_cfgs', '--check-cfg', 'cfg(test)',
+ '--check-cfg', 'cfg(MESON)'])
+
+ def test_cargo_toml_dependencies(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_1)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(len(manifest.dependencies), 6)
+ self.assertEqual(manifest.dependencies['gtk'].package, 'gtk4')
+ self.assertEqual(manifest.dependencies['gtk'].version, '0.9')
+ self.assertEqual(manifest.dependencies['gtk'].meson_version, ['>= 0.9', '< 0.10'])
+ self.assertEqual(manifest.dependencies['gtk'].api, '0.9')
+ self.assertEqual(manifest.dependencies['num-complex'].package, 'num-complex')
+ self.assertEqual(manifest.dependencies['num-complex'].version, '0.4')
+ self.assertEqual(manifest.dependencies['num-complex'].meson_version, ['>= 0.4', '< 0.5'])
+ self.assertEqual(manifest.dependencies['rayon'].package, 'rayon')
+ self.assertEqual(manifest.dependencies['rayon'].version, '1.0')
+ self.assertEqual(manifest.dependencies['rayon'].meson_version, ['>= 1.0', '< 2'])
+ self.assertEqual(manifest.dependencies['rayon'].api, '1')
+ self.assertEqual(manifest.dependencies['once_cell'].package, 'once_cell')
+ self.assertEqual(manifest.dependencies['once_cell'].version, '1')
+ self.assertEqual(manifest.dependencies['once_cell'].meson_version, ['>= 1', '< 2'])
+ self.assertEqual(manifest.dependencies['once_cell'].api, '1')
+ self.assertEqual(manifest.dependencies['async-channel'].package, 'async-channel')
+ self.assertEqual(manifest.dependencies['async-channel'].version, '2.0')
+ self.assertEqual(manifest.dependencies['async-channel'].meson_version, ['>= 2.0', '< 3'])
+ self.assertEqual(manifest.dependencies['async-channel'].api, '2')
+ self.assertEqual(manifest.dependencies['zerocopy'].package, 'zerocopy')
+ self.assertEqual(manifest.dependencies['zerocopy'].version, '0.7')
+ self.assertEqual(manifest.dependencies['zerocopy'].meson_version, ['>= 0.7', '< 0.8'])
+ self.assertEqual(manifest.dependencies['zerocopy'].features, ['derive'])
+ self.assertEqual(manifest.dependencies['zerocopy'].api, '0.7')
+
+ self.assertEqual(len(manifest.dev_dependencies), 1)
+ self.assertEqual(manifest.dev_dependencies['gir-format-check'].package, 'gir-format-check')
+ self.assertEqual(manifest.dev_dependencies['gir-format-check'].version, '^0.1')
+ self.assertEqual(manifest.dev_dependencies['gir-format-check'].meson_version, ['>= 0.1', '< 0.2'])
+ self.assertEqual(manifest.dev_dependencies['gir-format-check'].api, '0.1')
+
+ def test_cargo_toml_proc_macro(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_3)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(manifest.lib.name, 'bits')
+ self.assertEqual(manifest.lib.crate_type, ['proc-macro'])
+ self.assertEqual(manifest.lib.path, 'src/lib.rs')
+
+ del manifest_toml['lib']['crate-type']
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+ self.assertEqual(manifest.lib.name, 'bits')
+ self.assertEqual(manifest.lib.crate_type, ['proc-macro'])
+ self.assertEqual(manifest.lib.path, 'src/lib.rs')
+
+ def test_cargo_toml_targets(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_2)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(manifest.lib.name, 'pango')
+ self.assertEqual(manifest.lib.crate_type, ['lib'])
+ self.assertEqual(manifest.lib.path, 'src/lib.rs')
+ self.assertEqual(manifest.lib.test, True)
+ self.assertEqual(manifest.lib.doctest, True)
+ self.assertEqual(manifest.lib.bench, True)
+ self.assertEqual(manifest.lib.doc, True)
+ self.assertEqual(manifest.lib.harness, True)
+ self.assertEqual(manifest.lib.edition, '2021')
+ self.assertEqual(manifest.lib.required_features, [])
+ self.assertEqual(manifest.lib.plugin, False)
+
+ self.assertEqual(len(manifest.test), 1)
+ self.assertEqual(manifest.test[0].name, 'check_gir')
+ self.assertEqual(manifest.test[0].crate_type, ['bin'])
+ self.assertEqual(manifest.test[0].path, 'tests/check_gir.rs')
+ self.assertEqual(manifest.lib.path, 'src/lib.rs')
+ self.assertEqual(manifest.test[0].test, True)
+ self.assertEqual(manifest.test[0].doctest, True)
+ self.assertEqual(manifest.test[0].bench, False)
+ self.assertEqual(manifest.test[0].doc, False)
+ self.assertEqual(manifest.test[0].harness, True)
+ self.assertEqual(manifest.test[0].edition, '2021')
+ self.assertEqual(manifest.test[0].required_features, [])
+ self.assertEqual(manifest.test[0].plugin, False)
+
+ def test_cargo_toml_system_deps(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_2)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertIn('system-deps', manifest.package.metadata)
+
+ self.assertEqual(len(manifest.system_dependencies), 1)
+ self.assertEqual(manifest.system_dependencies['pango'].name, 'pango')
+ self.assertEqual(manifest.system_dependencies['pango'].version, '1.40')
+ self.assertEqual(manifest.system_dependencies['pango'].meson_version, ['>=1.40'])
+ self.assertEqual(manifest.system_dependencies['pango'].optional, False)
+ self.assertEqual(manifest.system_dependencies['pango'].feature, None)
+
+ self.assertEqual(len(manifest.system_dependencies['pango'].feature_overrides), 1)
+ self.assertEqual(manifest.system_dependencies['pango'].feature_overrides['v1_42'], {'version': '1.42'})
+
+ def test_cargo_toml_features(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ fname = os.path.join(tmpdir, 'Cargo.toml')
+ with open(fname, 'w', encoding='utf-8') as f:
+ f.write(self.CARGO_TOML_2)
+ manifest_toml = load_toml(fname)
+ manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
+
+ self.assertEqual(len(manifest.features), 3)
+ self.assertEqual(manifest.features['v1_42'], ['pango-sys/v1_42'])
+ self.assertEqual(manifest.features['v1_44'], ['v1_42', 'pango-sys/v1_44'])
+ self.assertEqual(manifest.features['default'], [])
diff --git a/unittests/datatests.py b/unittests/datatests.py
index bd83b81..e529e4e 100644
--- a/unittests/datatests.py
+++ b/unittests/datatests.py
@@ -167,10 +167,10 @@ class DataTests(unittest.TestCase):
debug = False
else:
raise RuntimeError(f'Invalid debug value {debug!r} in row:\n{m.group()}')
- env.coredata.set_option(OptionKey('buildtype'), buildtype)
- self.assertEqual(env.coredata.optstore.get_value('buildtype'), buildtype)
- self.assertEqual(env.coredata.optstore.get_value('optimization'), opt)
- self.assertEqual(env.coredata.optstore.get_value('debug'), debug)
+ env.coredata.optstore.set_option(OptionKey('buildtype'), buildtype)
+ self.assertEqual(env.coredata.optstore.get_value_for('buildtype'), buildtype)
+ self.assertEqual(env.coredata.optstore.get_value_for('optimization'), opt)
+ self.assertEqual(env.coredata.optstore.get_value_for('debug'), debug)
def test_cpu_families_documented(self):
with open("docs/markdown/Reference-tables.md", encoding='utf-8') as f:
@@ -183,7 +183,7 @@ class DataTests(unittest.TestCase):
arches = [m.group(1) for m in re.finditer(r"^\| (\w+) +\|", content, re.MULTILINE)]
# Drop the header
arches = set(arches[1:])
- self.assertEqual(arches, set(mesonbuild.environment.known_cpu_families))
+ self.assertEqual(arches, set(mesonbuild.envconfig.known_cpu_families))
def test_markdown_files_in_sitemap(self):
'''
diff --git a/unittests/failuretests.py b/unittests/failuretests.py
index 18d0c5e..3743f29 100644
--- a/unittests/failuretests.py
+++ b/unittests/failuretests.py
@@ -41,10 +41,10 @@ def no_pkgconfig():
return [None]
return old_search(self, name, search_dirs, exclude_paths)
- def new_which(cmd, *kwargs):
+ def new_which(cmd, **kwargs):
if cmd == 'pkg-config':
return None
- return old_which(cmd, *kwargs)
+ return old_which(cmd, **kwargs)
shutil.which = new_which
ExternalProgram._search = new_search
@@ -147,11 +147,11 @@ class FailureTests(BasePlatformTests):
def test_dependency(self):
if subprocess.call(['pkg-config', '--exists', 'zlib']) != 0:
raise unittest.SkipTest('zlib not found with pkg-config')
- a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
- ("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
- ("dependency('zlib', version : 1)", "Item must be a list or one of <class 'str'>"),
- ("dependency('zlib', required : 1)", "[Rr]equired.*boolean"),
- ("dependency('zlib', method : 1)", "[Mm]ethod.*string"),
+ a = (("dependency('zlib', method : 'fail')", 'dependency keyword argument "method" must be one of auto, builtin, cmake, config-tool, cups-config, dub, extraframework, libwmf-config, pcap-config, pkg-config, qmake, sdlconfig, sysconfig, system, not fail'),
+ ("dependency('zlib', static : '1')", "dependency keyword argument 'static' was of type str but should have been one of: bool, NoneType"),
+ ("dependency('zlib', version : 1)", r"dependency keyword argument 'version' was of type array\[int\] but should have been array\[str\]"),
+ ("dependency('zlib', required : 1)", "dependency keyword argument 'required' was of type int but should have been one of: bool, UserFeatureOption"),
+ ("dependency('zlib', method : 1)", "dependency keyword argument 'method' was of type int but should have been str"),
("dependency('zlibfail')", self.dnf),)
for contents, match in a:
self.assertMesonRaises(contents, match)
@@ -206,7 +206,7 @@ class FailureTests(BasePlatformTests):
if not shutil.which('wx-config-3.0') and not shutil.which('wx-config') and not shutil.which('wx-config-gtk3'):
raise unittest.SkipTest('Neither wx-config, wx-config-3.0 nor wx-config-gtk3 found')
self.assertMesonRaises("dependency('wxwidgets', modules : 1)",
- "module argument is not a string")
+ r"dependency keyword argument 'modules' was of type array\[int\] but should have been array\[str\]")
def test_llvm_dependency(self):
self.assertMesonRaises("dependency('llvm', modules : 'fail')",
@@ -215,7 +215,7 @@ class FailureTests(BasePlatformTests):
def test_boost_notfound_dependency(self):
# Can be run even if Boost is found or not
self.assertMesonRaises("dependency('boost', modules : 1)",
- "module.*not a string")
+ r"dependency keyword argument 'modules' was of type array\[int\] but should have been array\[str\]")
self.assertMesonRaises("dependency('boost', modules : 'fail')",
f"(fail.*not found|{self.dnf})")
diff --git a/unittests/internaltests.py b/unittests/internaltests.py
index d7994ee..74b36a8 100644
--- a/unittests/internaltests.py
+++ b/unittests/internaltests.py
@@ -44,10 +44,7 @@ from mesonbuild.programs import ExternalProgram
import mesonbuild.modules.pkgconfig
from mesonbuild import utils
-
-from run_tests import (
- FakeCompilerOptions, get_fake_env, get_fake_options
-)
+from run_tests import get_fake_env, get_fake_options
from .helpers import *
@@ -112,7 +109,7 @@ class InternalTests(unittest.TestCase):
stat.S_IRGRP | stat.S_IXGRP)
def test_compiler_args_class_none_flush(self):
- cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, False, mock.Mock())
+ cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, get_fake_env())
a = cc.compiler_args(['-I.'])
#first we are checking if the tree construction deduplicates the correct -I argument
a += ['-I..']
@@ -129,14 +126,14 @@ class InternalTests(unittest.TestCase):
self.assertEqual(a, ['-I.', '-I./tests2/', '-I./tests/', '-I..'])
def test_compiler_args_class_d(self):
- d = DmdDCompiler([], 'fake', MachineChoice.HOST, 'info', 'arch')
+ d = DmdDCompiler([], 'fake', MachineChoice.HOST, get_fake_env(), 'arch')
# check include order is kept when deduplicating
a = d.compiler_args(['-Ifirst', '-Isecond', '-Ithird'])
a += ['-Ifirst']
self.assertEqual(a, ['-Ifirst', '-Isecond', '-Ithird'])
def test_compiler_args_class_clike(self):
- cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, False, mock.Mock())
+ cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, get_fake_env())
# Test that empty initialization works
a = cc.compiler_args()
self.assertEqual(a, [])
@@ -217,9 +214,10 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_class_visualstudio(self):
- linker = linkers.MSVCDynamicLinker(MachineChoice.HOST, [])
+ env = get_fake_env()
+ linker = linkers.MSVCDynamicLinker(env, MachineChoice.HOST, [])
# Version just needs to be > 19.0.0
- cc = VisualStudioCPPCompiler([], [], '20.00', MachineChoice.HOST, False, mock.Mock(), 'x64', linker=linker)
+ cc = VisualStudioCPPCompiler([], [], '20.00', MachineChoice.HOST, env, 'x64', linker=linker)
a = cc.compiler_args(cc.get_always_args())
self.assertEqual(a.to_native(copy=True), ['/nologo', '/showIncludes', '/utf-8', '/Zc:__cplusplus'])
@@ -239,8 +237,9 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_class_gnuld(self):
## Test --start/end-group
- linker = linkers.GnuBFDDynamicLinker([], MachineChoice.HOST, '-Wl,', [])
- gcc = GnuCCompiler([], [], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
+ env = get_fake_env()
+ linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, '-Wl,', [])
+ gcc = GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
@@ -267,8 +266,9 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_remove_system(self):
## Test --start/end-group
- linker = linkers.GnuBFDDynamicLinker([], MachineChoice.HOST, '-Wl,', [])
- gcc = GnuCCompiler([], [], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
+ env = get_fake_env()
+ linker = linkers.GnuBFDDynamicLinker([], env, MachineChoice.HOST, '-Wl,', [])
+ gcc = GnuCCompiler([], [], 'fake', MachineChoice.HOST, env, linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
@@ -534,18 +534,18 @@ class InternalTests(unittest.TestCase):
kwargs = {'sources': [1, [2, [3]]]}
self.assertEqual([1, 2, 3], extract(kwargs, 'sources'))
- def _test_all_naming(self, cc, env, patterns, platform):
+ def _test_all_naming(self, cc, patterns, platform):
shr = patterns[platform]['shared']
stc = patterns[platform]['static']
shrstc = shr + tuple(x for x in stc if x not in shr)
stcshr = stc + tuple(x for x in shr if x not in stc)
- p = cc.get_library_naming(env, LibType.SHARED)
+ p = cc.get_library_naming(LibType.SHARED)
self.assertEqual(p, shr)
- p = cc.get_library_naming(env, LibType.STATIC)
+ p = cc.get_library_naming(LibType.STATIC)
self.assertEqual(p, stc)
- p = cc.get_library_naming(env, LibType.PREFER_STATIC)
+ p = cc.get_library_naming(LibType.PREFER_STATIC)
self.assertEqual(p, stcshr)
- p = cc.get_library_naming(env, LibType.PREFER_SHARED)
+ p = cc.get_library_naming(LibType.PREFER_SHARED)
self.assertEqual(p, shrstc)
# Test find library by mocking up openbsd
if platform != 'openbsd':
@@ -554,10 +554,14 @@ class InternalTests(unittest.TestCase):
for i in ['libfoo.so.6.0', 'libfoo.so.5.0', 'libfoo.so.54.0', 'libfoo.so.66a.0b', 'libfoo.so.70.0.so.1',
'libbar.so.7.10', 'libbar.so.7.9', 'libbar.so.7.9.3']:
libpath = Path(tmpdir) / i
- libpath.write_text('', encoding='utf-8')
- found = cc._find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True)
+ src = libpath.with_suffix('.c')
+ with src.open('w', encoding='utf-8') as f:
+ f.write('int meson_foobar (void) { return 0; }')
+ subprocess.check_call(['gcc', str(src), '-o', str(libpath), '-shared'])
+
+ found = cc._find_library_real('foo', [tmpdir], 'int main(void) { return 0; }', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
self.assertEqual(os.path.basename(found[0]), 'libfoo.so.54.0')
- found = cc._find_library_real('bar', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True)
+ found = cc._find_library_real('bar', [tmpdir], 'int main(void) { return 0; }', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
self.assertEqual(os.path.basename(found[0]), 'libbar.so.7.10')
def test_find_library_patterns(self):
@@ -584,26 +588,26 @@ class InternalTests(unittest.TestCase):
env = get_fake_env()
cc = detect_c_compiler(env, MachineChoice.HOST)
if is_osx():
- self._test_all_naming(cc, env, patterns, 'darwin')
+ self._test_all_naming(cc, patterns, 'darwin')
elif is_cygwin():
- self._test_all_naming(cc, env, patterns, 'cygwin')
+ self._test_all_naming(cc, patterns, 'cygwin')
elif is_windows():
if cc.get_argument_syntax() == 'msvc':
- self._test_all_naming(cc, env, patterns, 'windows-msvc')
+ self._test_all_naming(cc, patterns, 'windows-msvc')
else:
- self._test_all_naming(cc, env, patterns, 'windows-mingw')
+ self._test_all_naming(cc, patterns, 'windows-mingw')
elif is_openbsd():
- self._test_all_naming(cc, env, patterns, 'openbsd')
+ self._test_all_naming(cc, patterns, 'openbsd')
else:
- self._test_all_naming(cc, env, patterns, 'linux')
+ self._test_all_naming(cc, patterns, 'linux')
env.machines.host.system = 'openbsd'
- self._test_all_naming(cc, env, patterns, 'openbsd')
+ self._test_all_naming(cc, patterns, 'openbsd')
env.machines.host.system = 'darwin'
- self._test_all_naming(cc, env, patterns, 'darwin')
+ self._test_all_naming(cc, patterns, 'darwin')
env.machines.host.system = 'cygwin'
- self._test_all_naming(cc, env, patterns, 'cygwin')
+ self._test_all_naming(cc, patterns, 'cygwin')
env.machines.host.system = 'windows'
- self._test_all_naming(cc, env, patterns, 'windows-mingw')
+ self._test_all_naming(cc, patterns, 'windows-mingw')
@skipIfNoPkgconfig
def test_pkgconfig_parse_libs(self):
@@ -613,23 +617,27 @@ class InternalTests(unittest.TestCase):
https://github.com/mesonbuild/meson/issues/3951
'''
def create_static_lib(name):
- if not is_osx():
- name.open('w', encoding='utf-8').close()
- return
src = name.with_suffix('.c')
out = name.with_suffix('.o')
with src.open('w', encoding='utf-8') as f:
f.write('int meson_foobar (void) { return 0; }')
# use of x86_64 is hardcoded in run_tests.py:get_fake_env()
- subprocess.check_call(['clang', '-c', str(src), '-o', str(out), '-arch', 'x86_64'])
+ if is_osx():
+ subprocess.check_call(['clang', '-c', str(src), '-o', str(out), '-arch', 'x86_64'])
+ else:
+ subprocess.check_call(['gcc', '-c', str(src), '-o', str(out)])
subprocess.check_call(['ar', 'csr', str(name), str(out)])
+ # The test relies on some open-coded toolchain invocations for
+ # library creation in create_static_lib.
+ if is_windows() or is_cygwin():
+ return
+
with tempfile.TemporaryDirectory() as tmpdir:
pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True)
env = get_fake_env()
compiler = detect_c_compiler(env, MachineChoice.HOST)
env.coredata.compilers.host = {'c': compiler}
- env.coredata.optstore.set_value_object(OptionKey('c_link_args'), FakeCompilerOptions())
p1 = Path(tmpdir) / '1'
p2 = Path(tmpdir) / '2'
p1.mkdir()
@@ -1598,9 +1606,9 @@ class InternalTests(unittest.TestCase):
"""Mock all of the ways we could get the trial at once."""
mocked = mock.Mock(return_value=value)
- with mock.patch('mesonbuild.environment.detect_windows_arch', mocked), \
- mock.patch('mesonbuild.environment.platform.processor', mocked), \
- mock.patch('mesonbuild.environment.platform.machine', mocked):
+ with mock.patch('mesonbuild.envconfig.detect_windows_arch', mocked), \
+ mock.patch('mesonbuild.envconfig.platform.processor', mocked), \
+ mock.patch('mesonbuild.envconfig.platform.machine', mocked):
yield
cases = [
@@ -1631,28 +1639,28 @@ class InternalTests(unittest.TestCase):
('aarch64_be', 'aarch64'),
]
- cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, False, mock.Mock())
+ cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, get_fake_env())
- with mock.patch('mesonbuild.environment.any_compiler_has_define', mock.Mock(return_value=False)):
+ with mock.patch('mesonbuild.envconfig.any_compiler_has_define', mock.Mock(return_value=False)):
for test, expected in cases:
with self.subTest(test, has_define=False), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu_family({'c': cc})
+ actual = mesonbuild.envconfig.detect_cpu_family({'c': cc})
self.assertEqual(actual, expected)
- with mock.patch('mesonbuild.environment.any_compiler_has_define', mock.Mock(return_value=True)):
+ with mock.patch('mesonbuild.envconfig.any_compiler_has_define', mock.Mock(return_value=True)):
for test, expected in [('x86_64', 'x86'), ('aarch64', 'arm'), ('ppc', 'ppc64'), ('mips64', 'mips64')]:
with self.subTest(test, has_define=True), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu_family({'c': cc})
+ actual = mesonbuild.envconfig.detect_cpu_family({'c': cc})
self.assertEqual(actual, expected)
# machine_info_can_run calls detect_cpu_family with no compilers at all
with mock.patch(
- 'mesonbuild.environment.any_compiler_has_define',
+ 'mesonbuild.envconfig.any_compiler_has_define',
mock.Mock(side_effect=AssertionError('Should not be called')),
):
for test, expected in [('mips64', 'mips64')]:
with self.subTest(test, has_compiler=False), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu_family({})
+ actual = mesonbuild.envconfig.detect_cpu_family({})
self.assertEqual(actual, expected)
def test_detect_cpu(self) -> None:
@@ -1662,9 +1670,9 @@ class InternalTests(unittest.TestCase):
"""Mock all of the ways we could get the trial at once."""
mocked = mock.Mock(return_value=value)
- with mock.patch('mesonbuild.environment.detect_windows_arch', mocked), \
- mock.patch('mesonbuild.environment.platform.processor', mocked), \
- mock.patch('mesonbuild.environment.platform.machine', mocked):
+ with mock.patch('mesonbuild.envconfig.detect_windows_arch', mocked), \
+ mock.patch('mesonbuild.envconfig.platform.processor', mocked), \
+ mock.patch('mesonbuild.envconfig.platform.machine', mocked):
yield
cases = [
@@ -1680,27 +1688,27 @@ class InternalTests(unittest.TestCase):
('aarch64_be', 'aarch64'),
]
- cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, False, mock.Mock())
+ cc = ClangCCompiler([], [], 'fake', MachineChoice.HOST, get_fake_env())
- with mock.patch('mesonbuild.environment.any_compiler_has_define', mock.Mock(return_value=False)):
+ with mock.patch('mesonbuild.envconfig.any_compiler_has_define', mock.Mock(return_value=False)):
for test, expected in cases:
with self.subTest(test, has_define=False), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu({'c': cc})
+ actual = mesonbuild.envconfig.detect_cpu({'c': cc})
self.assertEqual(actual, expected)
- with mock.patch('mesonbuild.environment.any_compiler_has_define', mock.Mock(return_value=True)):
+ with mock.patch('mesonbuild.envconfig.any_compiler_has_define', mock.Mock(return_value=True)):
for test, expected in [('x86_64', 'i686'), ('aarch64', 'arm'), ('ppc', 'ppc64'), ('mips64', 'mips64')]:
with self.subTest(test, has_define=True), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu({'c': cc})
+ actual = mesonbuild.envconfig.detect_cpu({'c': cc})
self.assertEqual(actual, expected)
with mock.patch(
- 'mesonbuild.environment.any_compiler_has_define',
+ 'mesonbuild.envconfig.any_compiler_has_define',
mock.Mock(side_effect=AssertionError('Should not be called')),
):
for test, expected in [('mips64', 'mips64')]:
with self.subTest(test, has_compiler=False), mock_trial(test):
- actual = mesonbuild.environment.detect_cpu({})
+ actual = mesonbuild.envconfig.detect_cpu({})
self.assertEqual(actual, expected)
@mock.patch('mesonbuild.interpreter.Interpreter.load_root_meson_file', mock.Mock(return_value=None))
@@ -1786,6 +1794,7 @@ class InternalTests(unittest.TestCase):
'g-ir-generate': [f'/usr/bin/{gnu_tuple}-g-ir-generate'],
'g-ir-inspect': [f'/usr/bin/{gnu_tuple}-g-ir-inspect'],
'g-ir-scanner': [f'/usr/bin/{gnu_tuple}-g-ir-scanner'],
+ 'vapigen': [f'/usr/bin/{gnu_tuple}-vapigen'],
}
for title, dpkg_arch, gccsuffix, env, expected in [
diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py
index 6b896d7..4f775d6 100644
--- a/unittests/linuxliketests.py
+++ b/unittests/linuxliketests.py
@@ -446,6 +446,24 @@ class LinuxlikeTests(BasePlatformTests):
libdir = self.installdir + os.path.join(self.prefix, self.libdir)
self._test_soname_impl(libdir, True)
+ @skip_if_not_base_option('b_sanitize')
+ def test_c_link_args_and_env(self):
+ '''
+ Test that the CFLAGS / CXXFLAGS environment variables are
+ included on the linker command line when c_link_args is
+ set but c_args is not.
+ '''
+ if is_cygwin():
+ raise SkipTest('asan not available on Cygwin')
+ if is_openbsd():
+ raise SkipTest('-fsanitize=address is not supported on OpenBSD')
+
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = {'CFLAGS': '-fsanitize=address'}
+ self.init(testdir, extra_args=['-Dc_link_args="-L/usr/lib"'],
+ override_envvars=env)
+ self.build()
+
def test_compiler_check_flags_order(self):
'''
Test that compiler check flags override all other flags. This can't be
@@ -986,6 +1004,22 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(got_rpath, yonder_libdir, rpath_format)
@skip_if_not_base_option('b_sanitize')
+ def test_env_cflags_ldflags(self):
+ if is_cygwin():
+ raise SkipTest('asan not available on Cygwin')
+ if is_openbsd():
+ raise SkipTest('-fsanitize=address is not supported on OpenBSD')
+
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = {'CFLAGS': '-fsanitize=address', 'LDFLAGS': '-I.'}
+ self.init(testdir, override_envvars=env)
+ self.build()
+ compdb = self.get_compdb()
+ for i in compdb:
+ self.assertIn("-fsanitize=address", i["command"])
+ self.wipe()
+
+ @skip_if_not_base_option('b_sanitize')
def test_pch_with_address_sanitizer(self):
if is_cygwin():
raise SkipTest('asan not available on Cygwin')
@@ -1110,7 +1144,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertPathExists(os.path.join(pkg_dir, 'librelativepath.pc'))
env = get_fake_env(testdir, self.builddir, self.prefix)
- env.coredata.set_options({OptionKey('pkg_config_path'): pkg_dir}, subproject='')
+ env.coredata.optstore.set_option(OptionKey('pkg_config_path'), pkg_dir)
kwargs = {'required': True, 'silent': True}
relative_path_dep = PkgConfigDependency('librelativepath', env, kwargs)
self.assertTrue(relative_path_dep.found())
@@ -1126,13 +1160,13 @@ class LinuxlikeTests(BasePlatformTests):
pkg_dir = os.path.join(testdir, 'pkgconfig')
env = get_fake_env(testdir, self.builddir, self.prefix)
- env.coredata.set_options({OptionKey('pkg_config_path'): pkg_dir}, subproject='')
+ env.coredata.optstore.set_option(OptionKey('pkg_config_path'), pkg_dir)
# Regression test: This used to modify the value of `pkg_config_path`
# option, adding the meson-uninstalled directory to it.
PkgConfigInterface.setup_env({}, env, MachineChoice.HOST, uninstalled=True)
- pkg_config_path = env.coredata.optstore.get_value('pkg_config_path')
+ pkg_config_path = env.coredata.optstore.get_value_for('pkg_config_path')
self.assertEqual(pkg_config_path, [pkg_dir])
def test_pkgconfig_uninstalled_env_added(self):
@@ -1161,8 +1195,7 @@ class LinuxlikeTests(BasePlatformTests):
env = get_fake_env(testdir, self.builddir, self.prefix)
- env.coredata.set_options({OptionKey('pkg_config_path'): external_pkg_config_path_dir},
- subproject='')
+ env.coredata.optstore.set_option(OptionKey('pkg_config_path'), external_pkg_config_path_dir)
newEnv = PkgConfigInterface.setup_env({}, env, MachineChoice.HOST, uninstalled=True)
@@ -1227,8 +1260,9 @@ class LinuxlikeTests(BasePlatformTests):
myenv['PKG_CONFIG_PATH'] = _prepend_pkg_config_path(self.privatedir)
stdo = subprocess.check_output([PKG_CONFIG, '--libs-only-l', 'libsomething'], env=myenv)
deps = [b'-lgobject-2.0', b'-lgio-2.0', b'-lglib-2.0', b'-lsomething']
- if is_windows() or is_cygwin() or is_osx() or is_openbsd():
+ if is_windows() or is_osx() or is_openbsd():
# On Windows, libintl is a separate library
+ # It used to be on Cygwin as well, but no longer is.
deps.append(b'-lintl')
self.assertEqual(set(deps), set(stdo.split()))
@@ -1489,7 +1523,7 @@ class LinuxlikeTests(BasePlatformTests):
env = get_fake_env()
cc = detect_c_compiler(env, MachineChoice.HOST)
linker = cc.linker
- if not linker.export_dynamic_args(env):
+ if not linker.export_dynamic_args():
raise SkipTest('Not applicable for linkers without --export-dynamic')
self.init(testdir)
build_ninja = os.path.join(self.builddir, 'build.ninja')
@@ -1725,16 +1759,13 @@ class LinuxlikeTests(BasePlatformTests):
self.assertNotIn('-lfoo', content)
def test_prelinking(self):
- # Prelinking currently only works on recently new GNU toolchains.
- # Skip everything else. When support for other toolchains is added,
- # remove limitations as necessary.
- if 'clang' in os.environ.get('CC', 'dummy') and not is_osx():
- raise SkipTest('Prelinking not supported with Clang.')
testdir = os.path.join(self.unit_test_dir, '86 prelinking')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.id == "gcc" and not version_compare(cc.version, '>=9'):
raise SkipTest('Prelinking not supported with gcc 8 or older.')
+ if cc.id == 'clang' and not version_compare(cc.version, '>=14'):
+ raise SkipTest('Prelinking not supported with Clang 13 or older.')
self.init(testdir)
self.build()
outlib = os.path.join(self.builddir, 'libprelinked.a')
@@ -1859,7 +1890,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content)
def test_top_options_in_sp(self):
- testdir = os.path.join(self.unit_test_dir, '125 pkgsubproj')
+ testdir = os.path.join(self.unit_test_dir, '127 pkgsubproj')
self.init(testdir)
def test_unreadable_dir_in_declare_dep(self):
@@ -1937,8 +1968,25 @@ class LinuxlikeTests(BasePlatformTests):
self.check_has_flag(compdb, sub1src, '-O2')
self.check_has_flag(compdb, sub2src, '-O2')
+ @skip_if_not_language('rust')
+ @skip_if_not_base_option('b_sanitize')
+ def test_rust_sanitizers(self):
+ args = ['-Drust_nightly=disabled', '-Db_lundef=false']
+ testdir = os.path.join(self.rust_test_dir, '28 mixed')
+ tests = ['address']
+
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cpp = detect_cpp_compiler(env, MachineChoice.HOST)
+ if cpp.find_library('ubsan', []):
+ tests += ['address,undefined']
+
+ for value in tests:
+ self.init(testdir, extra_args=args + ['-Db_sanitize=' + value])
+ self.build()
+ self.wipe()
+
def test_sanitizers(self):
- testdir = os.path.join(self.unit_test_dir, '127 sanitizers')
+ testdir = os.path.join(self.unit_test_dir, '129 sanitizers')
with self.subTest('no b_sanitize value'):
try:
diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py
index b2839e6..7f583de 100644
--- a/unittests/machinefiletests.py
+++ b/unittests/machinefiletests.py
@@ -204,7 +204,7 @@ class NativeFileTests(BasePlatformTests):
def test_config_tool_dep(self):
# Do the skip at this level to avoid screwing up the cache
- if mesonbuild.environment.detect_msys2_arch():
+ if mesonbuild.envconfig.detect_msys2_arch():
raise SkipTest('Skipped due to problems with LLVM on MSYS2')
if not shutil.which('llvm-config'):
raise SkipTest('No llvm-installed, cannot test')
@@ -550,8 +550,8 @@ class NativeFileTests(BasePlatformTests):
# into augments.
self.assertEqual(found, 2, 'Did not find all two sections')
- def test_builtin_options_subprojects_overrides_buildfiles(self):
- # If the buildfile says subproject(... default_library: shared), ensure that's overwritten
+ def test_builtin_options_machinefile_overrides_subproject(self):
+ # The buildfile says subproject(... default_library: static), the machinefile overrides it
testcase = os.path.join(self.common_test_dir, '223 persubproject options')
config = self.helper_create_native_file({'sub2:built-in options': {'default_library': 'shared'}})
@@ -563,8 +563,8 @@ class NativeFileTests(BasePlatformTests):
check = cm.exception.stdout
self.assertIn(check, 'Parent should override default_library')
- def test_builtin_options_subprojects_dont_inherits_parent_override(self):
- # If the buildfile says subproject(... default_library: shared), ensure that's overwritten
+ def test_builtin_options_machinefile_global_loses_over_subproject(self):
+ # The buildfile says subproject(... default_library: static), ensure that it overrides the machinefile
testcase = os.path.join(self.common_test_dir, '223 persubproject options')
config = self.helper_create_native_file({'built-in options': {'default_library': 'both'}})
self.init(testcase, extra_args=['--native-file', config])
diff --git a/unittests/optiontests.py b/unittests/optiontests.py
index 5ed601f..a3a2f54 100644
--- a/unittests/optiontests.py
+++ b/unittests/optiontests.py
@@ -35,13 +35,29 @@ class OptionTests(unittest.TestCase):
optstore.initialize_from_top_level_project_call({OptionKey('someoption'): new_value}, {}, {})
self.assertEqual(optstore.get_value_for(k), new_value)
+ def test_machine_vs_project(self):
+ optstore = OptionStore(False)
+ name = 'backend'
+ default_value = 'ninja'
+ proj_value = 'xcode'
+ mfile_value = 'vs2010'
+ k = OptionKey(name)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ vo = UserStringOption(k.name, 'You know what this is', default_value)
+ optstore.add_system_option(k.name, vo)
+ self.assertEqual(optstore.get_value_for(k), default_value)
+ optstore.initialize_from_top_level_project_call({OptionKey(name): proj_value}, {},
+ {OptionKey(name): mfile_value})
+ self.assertEqual(optstore.get_value_for(k), mfile_value)
+
def test_subproject_system_option(self):
"""Test that subproject system options get their default value from the global
option (e.g. "sub:b_lto" can be initialized from "b_lto")."""
optstore = OptionStore(False)
- name = 'someoption'
- default_value = 'somevalue'
- new_value = 'new_value'
+ name = 'b_lto'
+ default_value = 'false'
+ new_value = 'true'
k = OptionKey(name)
subk = k.evolve(subproject='sub')
optstore.initialize_from_top_level_project_call({}, {}, {OptionKey(name): new_value})
@@ -105,6 +121,14 @@ class OptionTests(unittest.TestCase):
self.assertEqual(optstore.get_value_for(name, 'sub'), sub_value)
self.assertEqual(num_options(optstore), 2)
+ def test_toplevel_project_yielding(self):
+ optstore = OptionStore(False)
+ name = 'someoption'
+ top_value = 'top'
+ vo = UserStringOption(name, 'A top level option', top_value, True)
+ optstore.add_project_option(OptionKey(name, ''), vo)
+ self.assertEqual(optstore.get_value_for(name, ''), top_value)
+
def test_project_yielding(self):
optstore = OptionStore(False)
name = 'someoption'
@@ -136,6 +160,30 @@ class OptionTests(unittest.TestCase):
self.assertEqual(optstore.get_value_for(sub_name, 'sub'), sub_value)
self.assertEqual(num_options(optstore), 2)
+ def test_project_yielding_initialize(self):
+ optstore = OptionStore(False)
+ name = 'someoption'
+ top_value = 'top'
+ sub_value = 'sub'
+ subp = 'subp'
+ cmd_line = { OptionKey(name): top_value, OptionKey(name, subp): sub_value }
+
+ vo = UserStringOption(name, 'A top level option', 'default1')
+ optstore.add_project_option(OptionKey(name, ''), vo)
+ optstore.initialize_from_top_level_project_call({}, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, ''), top_value)
+ self.assertEqual(num_options(optstore), 1)
+
+ vo2 = UserStringOption(name, 'A subproject option', 'default2', True)
+ optstore.add_project_option(OptionKey(name, 'subp'), vo2)
+ self.assertEqual(optstore.get_value_for(name, ''), top_value)
+ self.assertEqual(optstore.get_value_for(name, subp), top_value)
+ self.assertEqual(num_options(optstore), 2)
+
+ optstore.initialize_from_subproject_call(subp, {}, {}, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, ''), top_value)
+ self.assertEqual(optstore.get_value_for(name, subp), sub_value)
+
def test_augments(self):
optstore = OptionStore(False)
name = 'cpp_std'
@@ -155,25 +203,25 @@ class OptionTests(unittest.TestCase):
# First augment a subproject
with self.subTest('set subproject override'):
- optstore.set_from_configure_command([f'{sub_name}:{name}={aug_value}'], [])
+ optstore.set_from_configure_command({OptionKey.from_string(f'{sub_name}:{name}'): aug_value})
self.assertEqual(optstore.get_value_for(name), top_value)
self.assertEqual(optstore.get_value_for(name, sub_name), aug_value)
self.assertEqual(optstore.get_value_for(name, sub2_name), top_value)
with self.subTest('unset subproject override'):
- optstore.set_from_configure_command([], [f'{sub_name}:{name}'])
+ optstore.set_from_configure_command({OptionKey.from_string(f'{sub_name}:{name}'): None})
self.assertEqual(optstore.get_value_for(name), top_value)
self.assertEqual(optstore.get_value_for(name, sub_name), top_value)
self.assertEqual(optstore.get_value_for(name, sub2_name), top_value)
# And now augment the top level option
- optstore.set_from_configure_command([f':{name}={aug_value}'], [])
+ optstore.set_from_configure_command({OptionKey.from_string(f':{name}'): aug_value})
self.assertEqual(optstore.get_value_for(name, None), top_value)
self.assertEqual(optstore.get_value_for(name, ''), aug_value)
self.assertEqual(optstore.get_value_for(name, sub_name), top_value)
self.assertEqual(optstore.get_value_for(name, sub2_name), top_value)
- optstore.set_from_configure_command([], [f':{name}'])
+ optstore.set_from_configure_command({OptionKey.from_string(f':{name}'): None})
self.assertEqual(optstore.get_value_for(name), top_value)
self.assertEqual(optstore.get_value_for(name, sub_name), top_value)
self.assertEqual(optstore.get_value_for(name, sub2_name), top_value)
@@ -193,12 +241,257 @@ class OptionTests(unittest.TestCase):
choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'],
)
optstore.add_system_option(name, co)
- optstore.set_from_configure_command([f'{sub_name}:{name}={aug_value}'], [])
- optstore.set_from_configure_command([f'{sub_name}:{name}={set_value}'], [])
+ optstore.set_from_configure_command({OptionKey.from_string(f'{sub_name}:{name}'): aug_value})
+ optstore.set_from_configure_command({OptionKey.from_string(f'{sub_name}:{name}'): set_value})
self.assertEqual(optstore.get_value_for(name), top_value)
self.assertEqual(optstore.get_value_for(name, sub_name), set_value)
- def test_b_default(self):
+ def test_build_to_host(self):
+ key = OptionKey('cpp_std')
+ def_value = 'c++98'
+ opt_value = 'c++17'
+ optstore = OptionStore(False)
+ co = UserComboOption(key.name,
+ 'C++ language standard to use',
+ def_value,
+ choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'],
+ )
+ optstore.add_compiler_option('cpp', key, co)
+
+ cmd_line = {key: opt_value}
+ optstore.initialize_from_top_level_project_call({}, cmd_line, {})
+ self.assertEqual(optstore.get_option_and_value_for(key.as_build())[1], opt_value)
+ self.assertEqual(optstore.get_value_for(key.as_build()), opt_value)
+
+ def test_build_to_host_subproject(self):
+ key = OptionKey('cpp_std')
+ def_value = 'c++98'
+ opt_value = 'c++17'
+ subp = 'subp'
+ optstore = OptionStore(False)
+ co = UserComboOption(key.name,
+ 'C++ language standard to use',
+ def_value,
+ choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'],
+ )
+ optstore.add_compiler_option('cpp', key, co)
+
+ spcall = {key: opt_value}
+ optstore.initialize_from_top_level_project_call({}, {}, {})
+ optstore.initialize_from_subproject_call(subp, spcall, {}, {}, {})
+ self.assertEqual(optstore.get_option_and_value_for(key.evolve(subproject=subp,
+ machine=MachineChoice.BUILD))[1], opt_value)
+ self.assertEqual(optstore.get_value_for(key.evolve(subproject=subp,
+ machine=MachineChoice.BUILD)), opt_value)
+
+ def test_build_to_host_cross(self):
+ key = OptionKey('cpp_std')
+ def_value = 'c++98'
+ opt_value = 'c++17'
+ optstore = OptionStore(True)
+ for k in [key, key.as_build()]:
+ co = UserComboOption(key.name,
+ 'C++ language standard to use',
+ def_value,
+ choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'],
+ )
+ optstore.add_compiler_option('cpp', k, co)
+
+ cmd_line = {key: opt_value}
+ optstore.initialize_from_top_level_project_call({}, cmd_line, {})
+ print(optstore.options)
+
+ self.assertEqual(optstore.get_option_and_value_for(key)[1], opt_value)
+ self.assertEqual(optstore.get_option_and_value_for(key.as_build())[1], def_value)
+ self.assertEqual(optstore.get_value_for(key), opt_value)
+ self.assertEqual(optstore.get_value_for(key.as_build()), def_value)
+
+ def test_b_nonexistent(self):
+ optstore = OptionStore(False)
+ self.assertTrue(optstore.accept_as_pending_option(OptionKey('b_ndebug')))
+ self.assertFalse(optstore.accept_as_pending_option(OptionKey('b_whatever')))
+
+ def test_backend_option_pending(self):
optstore = OptionStore(False)
- value = optstore.get_default_for_b_option(OptionKey('b_vscrt'))
- self.assertEqual(value, 'from_buildtype')
+ # backend options are known after the first invocation
+ self.assertTrue(optstore.accept_as_pending_option(OptionKey('backend_whatever'), True))
+ self.assertFalse(optstore.accept_as_pending_option(OptionKey('backend_whatever'), False))
+
+ def test_reconfigure_b_nonexistent(self):
+ optstore = OptionStore(False)
+ optstore.set_from_configure_command({OptionKey('b_ndebug'): True})
+
+ def test_unconfigure_nonexistent(self):
+ optstore = OptionStore(False)
+ with self.assertRaises(MesonException):
+ optstore.set_from_configure_command({OptionKey('nonexistent'): None})
+
+ def test_subproject_proj_opt_with_same_name(self):
+ name = 'tests'
+ subp = 'subp'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserBooleanOption(name, 'Tests', False)
+ optstore.add_project_option(OptionKey(name, subproject=''), o)
+ o = UserBooleanOption(name, 'Tests', True)
+ optstore.add_project_option(OptionKey(name, subproject=subp), o)
+
+ cmd_line = {OptionKey(name): True}
+ spcall = {OptionKey(name): False}
+
+ optstore.initialize_from_top_level_project_call({}, cmd_line, {})
+ optstore.initialize_from_subproject_call(subp, spcall, {}, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, ''), True)
+ self.assertEqual(optstore.get_value_for(name, subp), False)
+
+ def test_subproject_cmdline_override_global(self):
+ name = 'optimization'
+ subp = 'subp'
+ new_value = '0'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserComboOption(name, 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])
+ optstore.add_system_option(name, o)
+
+ toplevel_proj_default = {OptionKey(name): 's'}
+ subp_proj_default = {OptionKey(name): '3'}
+ cmd_line = {OptionKey(name): new_value}
+
+ optstore.initialize_from_top_level_project_call(toplevel_proj_default, cmd_line, {})
+ optstore.initialize_from_subproject_call(subp, {}, subp_proj_default, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, subp), new_value)
+ self.assertEqual(optstore.get_value_for(name), new_value)
+
+ def test_subproject_parent_override_subp(self):
+ name = 'optimization'
+ subp = 'subp'
+ default_value = 's'
+ subp_value = '0'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserComboOption(name, 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])
+ optstore.add_system_option(name, o)
+
+ toplevel_proj_default = {OptionKey(name, subproject=subp): subp_value, OptionKey(name): default_value}
+ subp_proj_default = {OptionKey(name): '3'}
+
+ optstore.initialize_from_top_level_project_call(toplevel_proj_default, {}, {})
+ optstore.initialize_from_subproject_call(subp, {}, subp_proj_default, {}, {})
+ self.assertEqual(optstore.get_value_for(name, subp), subp_value)
+ self.assertEqual(optstore.get_value_for(name), default_value)
+
+ def test_subproject_cmdline_override_global_and_augment(self):
+ name = 'optimization'
+ subp = 'subp'
+ global_value = 's'
+ new_value = '0'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserComboOption(name, 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])
+ optstore.add_system_option(name, o)
+
+ toplevel_proj_default = {OptionKey(name): '1'}
+ subp_proj_default = {OptionKey(name): '3'}
+ cmd_line = {OptionKey(name): global_value, OptionKey(name, subproject=subp): new_value}
+
+ optstore.initialize_from_top_level_project_call(toplevel_proj_default, cmd_line, {})
+ optstore.initialize_from_subproject_call(subp, {}, subp_proj_default, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, subp), new_value)
+ self.assertEqual(optstore.get_value_for(name), global_value)
+
+ def test_subproject_cmdline_override_toplevel(self):
+ name = 'default_library'
+ subp = 'subp'
+ toplevel_value = 'both'
+ subp_value = 'static'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserComboOption(name, 'Kind of library', 'both', choices=['shared', 'static', 'both'])
+ optstore.add_system_option(name, o)
+
+ toplevel_proj_default = {OptionKey(name): 'shared'}
+ subp_proj_default = {OptionKey(name): subp_value}
+ cmd_line = {OptionKey(name, subproject=''): toplevel_value}
+
+ optstore.initialize_from_top_level_project_call(toplevel_proj_default, cmd_line, {})
+ optstore.initialize_from_subproject_call(subp, {}, subp_proj_default, cmd_line, {})
+ self.assertEqual(optstore.get_value_for(name, subp), subp_value)
+ self.assertEqual(optstore.get_value_for(name, ''), toplevel_value)
+
+ def test_subproject_buildtype(self):
+ subp = 'subp'
+ main1 = {OptionKey('buildtype'): 'release'}
+ main2 = {OptionKey('optimization'): '3', OptionKey('debug'): 'false'}
+ sub1 = {OptionKey('buildtype'): 'debug'}
+ sub2 = {OptionKey('optimization'): '0', OptionKey('debug'): 'true'}
+
+ for mainopt, subopt in ((main1, sub1),
+ (main2, sub2),
+ ({**main1, **main2}, {**sub1, **sub2})):
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+ o = UserComboOption('buildtype', 'Build type to use', 'debug', choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])
+ optstore.add_system_option(o.name, o)
+ o = UserComboOption('optimization', 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])
+ optstore.add_system_option(o.name, o)
+ o = UserBooleanOption('debug', 'Enable debug symbols and other information', True)
+ optstore.add_system_option(o.name, o)
+
+ optstore.initialize_from_top_level_project_call(mainopt, {}, {})
+ optstore.initialize_from_subproject_call(subp, {}, subopt, {}, {})
+ self.assertEqual(optstore.get_value_for('buildtype', subp), 'debug')
+ self.assertEqual(optstore.get_value_for('optimization', subp), '0')
+ self.assertEqual(optstore.get_value_for('debug', subp), True)
+
+ def test_deprecated_nonstring_value(self):
+ # TODO: add a lot more deprecated option tests
+ optstore = OptionStore(False)
+ name = 'deprecated'
+ do = UserStringOption(name, 'An option with some deprecation', '0',
+ deprecated={'true': '1'})
+ optstore.add_system_option(name, do)
+ optstore.set_option(OptionKey(name), True)
+ value = optstore.get_value_for(name)
+ self.assertEqual(value, '1')
+
+ def test_pending_augment_validation(self):
+ name = 'b_lto'
+ subproject = 'mysubproject'
+
+ optstore = OptionStore(False)
+ prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+ optstore.add_system_option('prefix', prefix)
+
+ optstore.initialize_from_top_level_project_call({}, {}, {})
+ optstore.initialize_from_subproject_call(subproject, {}, {OptionKey(name): 'true'}, {}, {})
+
+ bo = UserBooleanOption(name, 'LTO', False)
+ key = OptionKey(name, subproject=subproject)
+ optstore.add_system_option(key, bo)
+ stored_value = optstore.get_value_for(key)
+ self.assertIsInstance(stored_value, bool)
+ self.assertTrue(stored_value)
+
+ def test_yielding_boolean_option_with_falsy_parent(self):
+ """Test that yielding is correctly initialized when parent option value is False."""
+ optstore = OptionStore(False)
+ name = 'someoption'
+ subproject_name = 'sub'
+ parent_option = UserBooleanOption(name, 'A parent boolean option', False, yielding=True)
+ optstore.add_project_option(OptionKey(name, ''), parent_option)
+
+ child_option = UserBooleanOption(name, 'A child boolean option', True, yielding=True)
+ child_key = OptionKey(name, subproject_name)
+ optstore.add_project_option(child_key, child_option)
+ self.assertTrue(optstore.options[child_key].yielding)
diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py
index d6c0078..7660ee4 100644
--- a/unittests/platformagnostictests.py
+++ b/unittests/platformagnostictests.py
@@ -95,6 +95,23 @@ class PlatformAgnosticTests(BasePlatformTests):
testdir = os.path.join(self.unit_test_dir, '102 python without pkgconfig')
self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'})
+ def test_vala_target_with_internal_glib(self):
+ testdir = os.path.join(self.unit_test_dir, '131 vala internal glib')
+ for run in [{ 'version': '2.84.4', 'expected': '2.84'}, { 'version': '2.85.2', 'expected': '2.84' }]:
+ self.new_builddir()
+ self.init(testdir, extra_args=[f'-Dglib-version={run["version"]}'])
+ try:
+ with open(os.path.join(self.builddir, 'meson-info', 'intro-targets.json'), 'r', encoding='utf-8') as tgt_intro:
+ intro = json.load(tgt_intro)
+ target = list(filter(lambda tgt: tgt['name'] == 'vala-tgt', intro))
+ self.assertLength(target, 1)
+ sources = target[0]['target_sources']
+ vala_sources = filter(lambda src: src.get('language') == 'vala', sources)
+ for src in vala_sources:
+ self.assertIn(('--target-glib', run['expected']), zip(src['parameters'], src['parameters'][1:]))
+ except FileNotFoundError:
+ self.skipTest('Current backend does not produce introspection data')
+
def test_debug_function_outputs_to_meson_log(self):
testdir = os.path.join(self.unit_test_dir, '104 debug function')
log_msg = 'This is an example debug output, should only end up in debug log'
@@ -175,7 +192,7 @@ class PlatformAgnosticTests(BasePlatformTests):
with self.subTest('Changing the backend'):
with self.assertRaises(subprocess.CalledProcessError) as cm:
self.setconf('-Dbackend=none')
- self.assertIn("ERROR: Tried to modify read only option 'backend'", cm.exception.stdout)
+ self.assertIn('ERROR: Tried to modify read only option "backend"', cm.exception.stdout)
# Check that the new value was not written in the store.
with self.subTest('option is stored correctly'):
@@ -203,10 +220,10 @@ class PlatformAgnosticTests(BasePlatformTests):
# Reconfigure of not empty builddir should work
self.new_builddir()
Path(self.builddir, 'dummy').touch()
- self.init(testdir, extra_args=['--reconfigure'])
+ self.init(testdir, extra_args=['--reconfigure', '--buildtype=custom'])
# Setup a valid builddir should update options but not reconfigure
- self.assertEqual(self.getconf('buildtype'), 'debug')
+ self.assertEqual(self.getconf('buildtype'), 'custom')
o = self.init(testdir, extra_args=['-Dbuildtype=release'])
self.assertIn('Directory already configured', o)
self.assertNotIn('The Meson build system', o)
@@ -279,10 +296,10 @@ class PlatformAgnosticTests(BasePlatformTests):
data = json.load(f)['meson']
with open(os.path.join(testdir, 'expected_mods.json'), encoding='utf-8') as f:
- expected = json.load(f)['meson']['modules']
+ expected = json.load(f)['meson']
- self.assertEqual(data['modules'], expected)
- self.assertEqual(data['count'], 70)
+ self.assertEqual(data['modules'], expected['modules'])
+ self.assertEqual(data['count'], expected['count'])
def test_meson_package_cache_dir(self):
# Copy testdir into temporary directory to not pollute meson source tree.
@@ -421,12 +438,12 @@ class PlatformAgnosticTests(BasePlatformTests):
with self.subTest('unknown user option'):
out = self.init(testdir, extra_args=['-Dnot_an_option=1'], allow_fail=True)
- self.assertIn('ERROR: Unknown options: "not_an_option"', out)
+ self.assertIn('ERROR: Unknown option: "not_an_option"', out)
with self.subTest('unknown builtin option'):
self.new_builddir()
out = self.init(testdir, extra_args=['-Db_not_an_option=1'], allow_fail=True)
- self.assertIn('ERROR: Unknown options: "b_not_an_option"', out)
+ self.assertIn('ERROR: Unknown option: "b_not_an_option"', out)
def test_configure_new_option(self) -> None:
@@ -451,7 +468,17 @@ class PlatformAgnosticTests(BasePlatformTests):
f.write(line)
with self.assertRaises(subprocess.CalledProcessError) as e:
self.setconf('-Dneg_int_opt=0')
- self.assertIn('Unknown options: ":neg_int_opt"', e.exception.stdout)
+ self.assertIn('Unknown option: "neg_int_opt"', e.exception.stdout)
+
+ def test_reconfigure_option(self) -> None:
+ testdir = self.copy_srcdir(os.path.join(self.common_test_dir, '40 options'))
+ self.init(testdir)
+ self.assertEqual(self.getconf('neg_int_opt'), -3)
+ with self.assertRaises(subprocess.CalledProcessError) as e:
+ self.init(testdir, extra_args=['--reconfigure', '-Dneg_int_opt=0'])
+ self.assertEqual(self.getconf('neg_int_opt'), -3)
+ self.init(testdir, extra_args=['--reconfigure', '-Dneg_int_opt=-2'])
+ self.assertEqual(self.getconf('neg_int_opt'), -2)
def test_configure_option_changed_constraints(self) -> None:
"""Changing the constraints of an option without reconfiguring should work."""
@@ -491,7 +518,7 @@ class PlatformAgnosticTests(BasePlatformTests):
os.unlink(os.path.join(testdir, 'meson_options.txt'))
with self.assertRaises(subprocess.CalledProcessError) as e:
self.setconf('-Dneg_int_opt=0')
- self.assertIn('Unknown options: ":neg_int_opt"', e.exception.stdout)
+ self.assertIn('Unknown option: "neg_int_opt"', e.exception.stdout)
def test_configure_options_file_added(self) -> None:
"""A new project option file should be detected."""
@@ -533,3 +560,8 @@ class PlatformAgnosticTests(BasePlatformTests):
self.clean()
self._run(self.mtest_command + ['runner-with-exedep'])
+
+ def test_setup_mixed_long_short_options(self) -> None:
+ """Mixing unity and unity_size as long and short options should work."""
+ testdir = self.copy_srcdir(os.path.join(self.common_test_dir, '1 trivial'))
+ self.init(testdir, extra_args=['-Dunity=on', '--unity-size=123'])
diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py
index 57a6782..73e6c7f 100644
--- a/unittests/rewritetests.py
+++ b/unittests/rewritetests.py
@@ -46,6 +46,18 @@ class RewriterTests(BasePlatformTests):
args = [args]
return self.rewrite_raw(directory, ['command'] + args)
+ # The rewriter sorts the sources alphabetically, but this is very unstable
+ # and buggy, so we do not test it.
+ def assertEqualIgnoreOrder(self, a, b):
+ def deepsort(x):
+ if isinstance(x, list):
+ return sorted(deepsort(el) for el in x)
+ elif isinstance(x, dict):
+ return {k: deepsort(v) for k,v in x.items()}
+ else:
+ return x
+ self.assertDictEqual(deepsort(a), deepsort(b))
+
def test_target_source_list(self):
self.prime('1 basic')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
@@ -61,32 +73,40 @@ class RewriterTests(BasePlatformTests):
'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp'], 'extra_files': []},
+ 'trivialprog11@exe': {'name': 'trivialprog11', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog12@exe': {'name': 'trivialprog12', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'rightName@exe': {'name': 'rightName', 'sources': ['main.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_add_sources(self):
self.prime('1 basic')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
expected = {
'target': {
- 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp', 'a7.cpp', 'fileB.cpp', 'fileC.cpp'], 'extra_files': []},
- 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
- 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['a7.cpp', 'fileB.cpp', 'fileC.cpp'], 'extra_files': []},
+ 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp', 'fileC.cpp'], 'extra_files': []},
+ 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp', 'fileA.cpp', 'a1.cpp', 'a2.cpp'], 'extra_files': []},
+ 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['fileB.cpp', 'fileC.cpp', 'a7.cpp'], 'extra_files': []},
'trivialprog3@exe': {'name': 'trivialprog3', 'sources': ['a5.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
- 'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['a5.cpp', 'main.cpp', 'fileA.cpp'], 'extra_files': []},
- 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['a3.cpp', 'main.cpp', 'a7.cpp', 'fileB.cpp', 'fileC.cpp'], 'extra_files': []},
+ 'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['main.cpp', 'a5.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['fileB.cpp', 'fileC.cpp', 'a3.cpp', 'main.cpp'], 'extra_files': []},
'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp', 'fileA.cpp', 'a4.cpp'], 'extra_files': []},
- 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
- 'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
- 'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
+ 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
+ 'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp', 'fileA.cpp', 'a1.cpp', 'a6.cpp' ], 'extra_files': []},
+ 'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp', 'a1.cpp'], 'extra_files': []},
+ 'trivialprog11@exe': {'name': 'trivialprog11', 'sources': ['a1.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []},
+ 'trivialprog12@exe': {'name': 'trivialprog12', 'sources': ['a1.cpp', 'fileA.cpp', 'fileB.cpp', 'main.cpp'], 'extra_files': []},
+ 'rightName@exe': {'name': 'rightName', 'sources': ['main.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_add_sources_abs(self):
self.prime('1 basic')
@@ -95,7 +115,7 @@ class RewriterTests(BasePlatformTests):
inf = json.dumps([{"type": "target", "target": "trivialprog1", "operation": "info"}])
self.rewrite(self.builddir, add)
out = self.rewrite(self.builddir, inf)
- expected = {'target': {'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['a1.cpp', 'a2.cpp', 'a6.cpp', 'fileA.cpp', 'main.cpp'], 'extra_files': []}}}
+ expected = {'target': {'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp', 'fileA.cpp', 'a1.cpp', 'a2.cpp', 'a6.cpp'], 'extra_files': []}}}
self.assertDictEqual(out, expected)
def test_target_remove_sources(self):
@@ -103,28 +123,32 @@ class RewriterTests(BasePlatformTests):
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'rmSrc.json'))
expected = {
'target': {
- 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp', 'fileC.cpp'], 'extra_files': []},
- 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp'], 'extra_files': []},
- 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['fileC.cpp'], 'extra_files': []},
+ 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp', 'fileC.cpp'], 'extra_files': []},
+ 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['fileB.cpp', 'fileC.cpp'], 'extra_files': []},
'trivialprog3@exe': {'name': 'trivialprog3', 'sources': ['main.cpp'], 'extra_files': []},
'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['main.cpp'], 'extra_files': []},
- 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp', 'fileC.cpp'], 'extra_files': []},
+ 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['fileB.cpp', 'fileC.cpp'], 'extra_files': []},
'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp'], 'extra_files': []},
- 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileC.cpp', 'main.cpp'], 'extra_files': []},
- 'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp'], 'extra_files': []},
- 'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp'], 'extra_files': []},
+ 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileC.cpp', 'main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp'], 'extra_files': []},
+ 'trivialprog11@exe': {'name': 'trivialprog11', 'sources': ['main.cpp'], 'extra_files': []},
+ 'trivialprog12@exe': {'name': 'trivialprog12', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'rightName@exe': {'name': 'rightName', 'sources': ['main.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_subdir(self):
self.prime('2 subdirs')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
- expected = {'name': 'something', 'sources': ['first.c', 'second.c', 'third.c'], 'extra_files': []}
+ expected = {'name': 'something', 'sources': ['third.c', f'sub2{os.path.sep}first.c', f'sub2{os.path.sep}second.c'], 'extra_files': []}
self.assertDictEqual(list(out['target'].values())[0], expected)
# Check the written file
@@ -145,9 +169,12 @@ class RewriterTests(BasePlatformTests):
'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp'], 'extra_files': []},
+ 'trivialprog11@exe': {'name': 'trivialprog11', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog12@exe': {'name': 'trivialprog12', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_add(self):
self.prime('1 basic')
@@ -166,10 +193,14 @@ class RewriterTests(BasePlatformTests):
'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
- 'trivialprog10@sha': {'name': 'trivialprog10', 'sources': ['new1.cpp', 'new2.cpp'], 'extra_files': []},
+ 'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp', 'fileA.cpp', 'fileB.cpp'], 'extra_files': []},
+ 'trivialprog11@exe': {'name': 'trivialprog11', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog12@exe': {'name': 'trivialprog12', 'sources': ['main.cpp', 'fileA.cpp'], 'extra_files': []},
+ 'trivialprog13@sha': {'name': 'trivialprog13', 'sources': ['new1.cpp', 'new2.cpp'], 'extra_files': []},
+ 'rightName@exe': {'name': 'rightName', 'sources': ['main.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_remove_subdir(self):
self.prime('2 subdirs')
@@ -181,7 +212,7 @@ class RewriterTests(BasePlatformTests):
self.prime('2 subdirs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'addTgt.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- expected = {'name': 'something', 'sources': ['first.c', 'second.c'], 'extra_files': []}
+ expected = {'name': 'something', 'sources': [f'sub2{os.path.sep}first.c', f'sub2{os.path.sep}second.c'], 'extra_files': []}
self.assertDictEqual(out['target']['94b671c@@something@exe'], expected)
def test_target_source_sorting(self):
@@ -228,16 +259,23 @@ class RewriterTests(BasePlatformTests):
}
}
}
+ for k1, v1 in expected.items():
+ for k2, v2 in v1.items():
+ for k3, v3 in v2.items():
+ if isinstance(v3, list):
+ for i in range(len(v3)):
+ v3[i] = v3[i].replace('/', os.path.sep)
self.assertDictEqual(out, expected)
def test_target_same_name_skip(self):
self.prime('4 same name targets')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- expected = {'name': 'myExe', 'sources': ['main.cpp'], 'extra_files': []}
+ expected1 = {'name': 'myExe', 'sources': ['main.cpp'], 'extra_files': []}
+ expected2 = {'name': 'myExe', 'sources': [f'sub1{os.path.sep}main.cpp'], 'extra_files': []}
self.assertEqual(len(out['target']), 2)
- for val in out['target'].values():
- self.assertDictEqual(expected, val)
+ self.assertDictEqual(expected1, out['target']['myExe@exe'])
+ self.assertDictEqual(expected2, out['target']['9a11041@@myExe@exe'])
def test_kwargs_info(self):
self.prime('3 kwargs')
@@ -251,13 +289,30 @@ class RewriterTests(BasePlatformTests):
}
self.assertDictEqual(out, expected)
+ def test_kwargs_info_dict(self):
+ self.prime('8 kwargs dict')
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
+ expected = {
+ 'kwargs': {
+ 'project#/': {
+ 'default_options': {'c_std': 'c11', 'cpp_std': 'c++17'},
+ 'version': '0.0.1'
+ },
+ 'dependency#dep1': {
+ 'default_options': {'foo': 'bar'},
+ 'required': False
+ }
+ }
+ }
+ self.assertDictEqual(out, expected)
+
def test_kwargs_set(self):
self.prime('3 kwargs')
self.rewrite(self.builddir, os.path.join(self.builddir, 'set.json'))
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
expected = {
'kwargs': {
- 'project#/': {'version': '0.0.2', 'meson_version': '0.50.0', 'license': ['GPL', 'MIT']},
+ 'project#/': {'version': '0.0.2', 'meson_version': '0.50.0', 'license': ['GPL', 'MIT'], 'license_files': ['GPL.txt', 'MIT.txt']},
'target#tgt1': {'build_by_default': False, 'build_rpath': '/usr/local', 'dependencies': 'dep1'},
'dependency#dep1': {'required': True, 'method': 'cmake'}
}
@@ -270,7 +325,7 @@ class RewriterTests(BasePlatformTests):
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
expected = {
'kwargs': {
- 'project#/': {'version': '0.0.1', 'license': ['GPL', 'MIT', 'BSD', 'Boost']},
+ 'project#/': {'version': '0.0.1', 'license': ['GPL', 'MIT', 'BSD', 'Boost'], 'license_files': 'GPL.txt'},
'target#tgt1': {'build_by_default': True},
'dependency#dep1': {'required': False}
}
@@ -347,48 +402,78 @@ class RewriterTests(BasePlatformTests):
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addExtraFiles.json'))
expected = {
'target': {
- 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a2.hpp', 'a6.hpp', 'fileA.hpp', 'main.hpp', 'a7.hpp', 'fileB.hpp', 'fileC.hpp']},
- 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a2.hpp', 'a6.hpp', 'fileA.hpp', 'main.hpp']},
+ 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp'], 'extra_files': ['fileA.hpp', 'main.hpp', 'fileB.hpp', 'fileC.hpp']},
+ 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a2.hpp', 'fileA.hpp', 'main.hpp']},
'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['main.cpp'], 'extra_files': ['a7.hpp', 'fileB.hpp', 'fileC.hpp']},
'trivialprog3@exe': {'name': 'trivialprog3', 'sources': ['main.cpp'], 'extra_files': ['a5.hpp', 'fileA.hpp', 'main.hpp']},
'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['main.cpp'], 'extra_files': ['a5.hpp', 'main.hpp', 'fileA.hpp']},
- 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp'], 'extra_files': ['a3.hpp', 'main.hpp', 'a7.hpp', 'fileB.hpp', 'fileC.hpp']},
- 'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a2.hpp', 'a6.hpp', 'fileA.hpp', 'main.hpp']},
- 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a2.hpp', 'a6.hpp', 'fileA.hpp', 'main.hpp']},
+ 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp'], 'extra_files': ['a3.hpp', 'main.hpp', 'fileB.hpp', 'fileC.hpp']},
+ 'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp'], 'extra_files': ['fileA.hpp', 'main.hpp']},
+ 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a6.hpp', 'fileA.hpp', 'main.hpp']},
'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp'], 'extra_files': ['a2.hpp', 'a7.hpp']},
'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp'], 'extra_files': ['a8.hpp', 'a9.hpp']},
'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp'], 'extra_files': ['a1.hpp', 'a4.hpp']},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
def test_target_remove_extra_files(self):
self.prime('6 extra_files')
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'rmExtraFiles.json'))
expected = {
'target': {
- 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileC.hpp']},
- 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp'], 'extra_files': ['main.hpp']},
- 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['main.cpp'], 'extra_files': ['fileC.hpp']},
+ 'trivialprog0@exe': {'name': 'trivialprog0', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileA.hpp', 'fileB.hpp', 'fileC.hpp']},
+ 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileA.hpp']},
+ 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['main.cpp'], 'extra_files': ['fileB.hpp', 'fileC.hpp']},
'trivialprog3@exe': {'name': 'trivialprog3', 'sources': ['main.cpp'], 'extra_files': ['main.hpp']},
'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['main.cpp'], 'extra_files': ['main.hpp']},
- 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileC.hpp']},
- 'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp'], 'extra_files': ['main.hpp']},
- 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['main.cpp'], 'extra_files': ['main.hpp']},
+ 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp'], 'extra_files': ['fileB.hpp', 'fileC.hpp', 'main.hpp']},
+ 'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileA.hpp']},
+ 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['main.cpp'], 'extra_files': ['main.hpp', 'fileA.hpp']},
'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp'], 'extra_files': []},
'trivialprog9@exe': {'name': 'trivialprog9', 'sources': ['main.cpp'], 'extra_files': []},
'trivialprog10@exe': {'name': 'trivialprog10', 'sources': ['main.cpp'], 'extra_files': []},
}
}
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
# Check the written file
out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
- self.assertDictEqual(out, expected)
+ self.assertEqualIgnoreOrder(out, expected)
+
+ def test_duplicate_globals(self):
+ self.prime('10 duplicate globals')
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
+ expected = {
+ 'kwargs': {
+ 'project#/': {'license': 'MIT'}
+ }
+ }
+ self.assertEqualIgnoreOrder(out, expected)
+
+ def test_tricky_dataflow(self):
+ self.prime('9 tricky dataflow')
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'addSrc.json'))
+ expected = {
+ 'target': {
+ 'tgt1@sha': {'name': 'tgt1', 'sources': ['foo.c', 'new.c'], 'extra_files': []},
+ 'tgt2@exe': {'name': 'tgt2', 'sources': ['new.c', 'unknown'], 'extra_files': []},
+ 'tgt3@exe': {'name': 'tgt3', 'sources': ['foo.c', 'new.c'], 'extra_files': []},
+ 'tgt4@exe': {'name': 'tgt4', 'sources': ['unknown'], 'extra_files': []},
+ 'tgt5@exe': {'name': 'tgt5', 'sources': ['unknown', 'new.c'], 'extra_files': []},
+ 'tgt6@exe': {'name': 'tgt6', 'sources': ['unknown', 'new.c'], 'extra_files': []},
+ 'tgt7@exe': {'name': 'tgt7', 'sources': ['unknown', 'unknown'], 'extra_files': []},
+ }
+ }
+ self.assertEqualIgnoreOrder(out, expected)
+
+ # Check the written file
+ out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json'))
+ self.assertEqualIgnoreOrder(out, expected)
def test_raw_printer_is_idempotent(self):
test_path = Path(self.unit_test_dir, '120 rewrite')
@@ -421,3 +506,24 @@ class RewriterTests(BasePlatformTests):
}
}
self.assertDictEqual(out, expected)
+
+ # Asserts that AstInterpreter.dataflow_dag is what it should be
+ def test_dataflow_dag(self):
+ test_path = Path(self.rewrite_test_dir, '1 basic')
+ interpreter = IntrospectionInterpreter(test_path, '', 'ninja')
+ interpreter.analyze()
+
+ def sortkey(node):
+ return (node.lineno, node.colno, node.end_lineno, node.end_colno)
+
+ def node_to_str(node):
+ return f"{node.__class__.__name__}({node.lineno}:{node.colno})"
+
+ dag_as_str = ""
+ for target in sorted(interpreter.dataflow_dag.tgt_to_srcs.keys(), key=sortkey):
+ dag_as_str += f"Data flowing to {node_to_str(target)}:\n"
+ for source in sorted(interpreter.dataflow_dag.tgt_to_srcs[target], key=sortkey):
+ dag_as_str += f" {node_to_str(source)}\n"
+
+ expected = Path(test_path / "expected_dag.txt").read_text(encoding='utf-8').strip()
+ self.assertEqual(dag_as_str.strip(), expected)
diff --git a/unittests/windowstests.py b/unittests/windowstests.py
index 7fa4ab2..bf1225d 100644
--- a/unittests/windowstests.py
+++ b/unittests/windowstests.py
@@ -398,7 +398,16 @@ class WindowsTests(BasePlatformTests):
if OptionKey('b_vscrt') not in cc.base_options:
raise SkipTest('Compiler does not support setting the VS CRT')
+ MSVCRT_MAP = {
+ '/MD': '-fms-runtime-lib=dll',
+ '/MDd': '-fms-runtime-lib=dll_dbg',
+ '/MT': '-fms-runtime-lib=static',
+ '/MTd': '-fms-runtime-lib=static_dbg',
+ }
+
def sanitycheck_vscrt(vscrt):
+ if cc.get_argument_syntax() != 'msvc':
+ vscrt = MSVCRT_MAP[vscrt]
checks = self.get_meson_log_sanitychecks()
self.assertGreater(len(checks), 0)
for check in checks:
@@ -466,6 +475,12 @@ class WindowsTests(BasePlatformTests):
# Studio is picked, as a regression test for
# https://github.com/mesonbuild/meson/issues/9774
env['PATH'] = get_path_without_cmd('ninja', env['PATH'])
+ # Add a multiline variable to test that it is handled correctly
+ # with a line that contains only '=' and a line that would result
+ # in an invalid variable name.
+ # see: https://github.com/mesonbuild/meson/pull/13682
+ env['MULTILINE_VAR_WITH_EQUALS'] = 'Foo\r\n=====\r\n'
+ env['MULTILINE_VAR_WITH_INVALID_NAME'] = 'Foo\n%=Bar\n'
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)