From 6e9e344bcfcd63dba9c393012240ae7f7d41e5ee Mon Sep 17 00:00:00 2001 From: Jason Scott Date: Wed, 16 Oct 2024 10:51:13 -0400 Subject: [PATCH 001/624] add c2000 to ti library naming conditional so static libs are found --- mesonbuild/compilers/mixins/clike.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index e982ca023e41..d91004dd556f 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1034,8 +1034,8 @@ def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool elif env.machines[self.for_machine].is_cygwin(): shlibext = ['dll', 'dll.a'] prefixes = ['cyg'] + prefixes - elif self.id.lower() == 'c6000' or self.id.lower() == 'ti': - # TI C6000 compiler can use both extensions for static or dynamic libs. + 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'] else: From 143f848a439a35dc2e4512c02aebc67cc134b7fc Mon Sep 17 00:00:00 2001 From: Jason Scott Date: Fri, 18 Oct 2024 12:09:11 -0400 Subject: [PATCH 002/624] populate compiler args for linking command for ti targets --- mesonbuild/backend/ninjabackend.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 91603025ba92..bab842302771 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3554,6 +3554,17 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. for t in target.link_depends]) elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs) elem.add_dep(dep_targets + custom_target_libraries) + + # Compiler args must be included in TI C28x linker commands. + if linker.get_id() in {'c2000', 'c6000', 'ti'}: + compile_args = [] + for for_machine in MachineChoice: + clist = self.environment.coredata.compilers[for_machine] + for langname, compiler in clist.items(): + if langname in {'c', 'cpp'} and compiler.get_id() in {'c2000', 'c6000', 'ti'}: + compile_args += self.generate_basic_compiler_args(target, compiler) + elem.add_item('ARGS', compile_args) + elem.add_item('LINK_ARGS', commands) self.create_target_linker_introspection(target, linker, commands) return elem From 02960bc4493238886fbe0b3d09a2ba405d791246 Mon Sep 17 00:00:00 2001 From: Jason Scott Date: Fri, 18 Oct 2024 12:50:31 -0400 Subject: [PATCH 003/624] add getter for id so linker can be identified --- mesonbuild/linkers/linkers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index d011e67b9c54..264a1bb24020 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -26,6 +26,9 @@ class StaticLinker: def __init__(self, exelist: T.List[str]): self.exelist = exelist + def get_id(self) -> str: + return self.id + def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs: return CompilerArgs(self, args) From 5e920e39078e4b693a456a255de734cfb5c45b2a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 20 Oct 2024 22:01:56 +0300 Subject: [PATCH 004/624] Bump version number for new development. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 963aedb402d4..98656b27d9b0 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -74,7 +74,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.6.0' +version = '1.6.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 From 165c62208475085fdafddca29eee201bc98c3ab8 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 14 Oct 2024 00:58:18 +0200 Subject: [PATCH 005/624] Add GNU/Hurd kernel results uname -s does return gnu there. Resolves: https://github.com/mesonbuild/meson/issues/13740 Signed-off-by: Samuel Thibault --- docs/markdown/Reference-tables.md | 1 + mesonbuild/environment.py | 1 + mesonbuild/scripts/env2mfile.py | 1 + unittests/internaltests.py | 6 +----- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index e4e964c2d3d7..a5d0d5cdcc18 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -182,6 +182,7 @@ Native names as returned by the `.kernel()` method. | freebsd | | | openbsd | | | netbsd | | +| gnu | GNU Hurd | | nt | | | xnu | Kernel of various Apple OSes | | illumos | Kernel derived from OpenSolaris by community efforts | diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 71a2f3afcb6a..2621b9ca2db1 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -476,6 +476,7 @@ def detect_cpu(compilers: CompilersDict) -> str: 'darwin': 'xnu', 'dragonfly': 'dragonfly', 'haiku': 'haiku', + 'gnu': 'gnu', } def detect_kernel(system: str) -> T.Optional[str]: diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 207d75749c95..b2c9a45b2f80 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -156,6 +156,7 @@ def get_args_from_envvars(infos: MachineInfo) -> None: # map from DEB_HOST_ARCH_OS to Meson machine.kernel() deb_kernel_map = { 'kfreebsd': 'freebsd', + 'hurd': 'gnu', } def replace_special_cases(special_cases: T.Mapping[str, str], name: str) -> str: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 23d08e6e2992..3f50ac6e009b 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -1933,11 +1933,7 @@ def expected_binaries(gnu_tuple: str) -> T.Dict[str, T.List[str]]: }, system='gnu', subsystem='gnu', - # TODO: Currently hurd; should match whatever happens - # during native builds, but at the moment native builds - # fail when kernel() is called. - # https://github.com/mesonbuild/meson/issues/13740 - kernel='TODO', + kernel='gnu', cpu='i686', cpu_family='x86', endian='little', From dee6f5795711000d029dc923a0694d97454a7b0b Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sun, 20 Oct 2024 21:46:31 +0200 Subject: [PATCH 006/624] docs: fix typo --- docs/markdown/Release-notes-for-1.6.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Release-notes-for-1.6.0.md b/docs/markdown/Release-notes-for-1.6.0.md index 9e5cea6d17d5..4b2948771cc5 100644 --- a/docs/markdown/Release-notes-for-1.6.0.md +++ b/docs/markdown/Release-notes-for-1.6.0.md @@ -126,7 +126,7 @@ valid tools are `moc`, `uic`, `rcc` and `lrelease`. ## Simple tool to test build reproducibility Meson now ships with a command for testing whether your project can be -[built reprodicibly](https://reproducible-builds.org/). It can be used +[built reproducibly](https://reproducible-builds.org/). It can be used by running a command like the following in the source root of your project: From 8a75fda303471a369d0c5dcfc649e3101b88a9f6 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Sun, 20 Oct 2024 14:41:34 -0500 Subject: [PATCH 007/624] docs: grammar suggestion ("Clarity..") --- docs/markdown/Release-notes-for-1.3.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Release-notes-for-1.3.0.md b/docs/markdown/Release-notes-for-1.3.0.md index 0c7660f230ca..c2687039ab59 100644 --- a/docs/markdown/Release-notes-for-1.3.0.md +++ b/docs/markdown/Release-notes-for-1.3.0.md @@ -6,7 +6,7 @@ short-description: Release notes for 1.3.0 # New features Meson 1.3.0 was released on 19 November 2023 -## Clarify of implicitly-included headers in C-like compiler checks +## Clarify implicitly-included headers in C-like compiler checks Compiler check methods `compiler.compute_int()`, `compiler.alignment()` and `compiler.sizeof()` now have their implicitly-included headers From c4ec412f281b88cd5facb631bd339e01dcec5395 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 17 Oct 2024 18:08:51 -0400 Subject: [PATCH 008/624] compilers: Fix inconsistent option descriptions Remove trailing periods for consistency with other option descriptions, and use a consistent description for `winlibs` options everywhere (the one in the documentation). --- mesonbuild/compilers/c.py | 6 +++--- mesonbuild/compilers/cpp.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index f67281f04a51..5868211a6f15 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -158,7 +158,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserArrayOption, self.form_compileropt_key('winlibs'), - 'Standard Win libraries to link against', + 'Standard Windows libs to link against', gnu_winlibs), ) return opts @@ -311,7 +311,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserArrayOption, key.evolve('c_winlibs'), - 'Standard Win libraries to link against', + 'Standard Windows libs to link against', gnu_winlibs), ) return opts @@ -462,7 +462,7 @@ def get_options(self) -> MutableKeyedOptionDictType: self.create_option( options.UserArrayOption, self.form_compileropt_key('winlibs'), - 'Windows libs to link against.', + 'Standard Windows libs to link against', msvc_winlibs, ), ) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 930e7b7e5bb9..5b654be5d1f7 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -244,7 +244,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserComboOption, self.form_compileropt_key('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), self.create_option(options.UserBooleanOption, @@ -271,7 +271,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserArrayOption, self.form_compileropt_key('winlibs'), - 'Standard Win libraries to link against', + 'Standard Windows libs to link against', gnu_winlibs), ) return opts @@ -407,7 +407,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserComboOption, key.evolve('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), ) @@ -457,7 +457,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserComboOption, self.form_compileropt_key('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), self.create_option(options.UserBooleanOption, @@ -485,7 +485,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserArrayOption, key.evolve('cpp_winlibs'), - 'Standard Win libraries to link against', + 'Standard Windows libs to link against', gnu_winlibs), ) return opts @@ -612,7 +612,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserComboOption, self.form_compileropt_key('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), self.create_option(options.UserBooleanOption, @@ -693,7 +693,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts, self.create_option(options.UserComboOption, self.form_compileropt_key('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), self.create_option(options.UserBooleanOption, @@ -766,7 +766,7 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List opts, self.create_option(options.UserComboOption, self.form_compileropt_key('eh'), - 'C++ exception handling type.', + 'C++ exception handling type', ['none', 'default', 'a', 's', 'sc'], 'default'), self.create_option(options.UserBooleanOption, @@ -775,7 +775,7 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List True), self.create_option(options.UserArrayOption, self.form_compileropt_key('winlibs'), - 'Windows libs to link against.', + 'Standard Windows libs to link against', msvc_winlibs), ) std_opt = opts[key] From 5402d38dbd65c36786b788725120a8e66bc42716 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 17 Oct 2024 18:11:26 -0400 Subject: [PATCH 009/624] backend/ninja: Fix inconsistent rule descriptions Remove some trailing periods and always use present continuous tense (since these appear as status messages). --- mesonbuild/backend/ninjabackend.py | 22 +++++++++++----------- unittests/allplatformstests.py | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ee86c27e0b61..f29af251ee61 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1282,38 +1282,38 @@ def generate_coverage_command(self, elem: NinjaBuildElement, outputs: T.List[str def generate_coverage_rules(self, gcovr_exe: T.Optional[str], gcovr_version: T.Optional[str], llvm_cov_exe: T.Optional[str]) -> None: e = self.create_phony_target('coverage', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, [], gcovr_exe, llvm_cov_exe) - e.add_item('description', 'Generates coverage reports') + e.add_item('description', 'Generating coverage reports') self.add_build(e) self.generate_coverage_legacy_rules(gcovr_exe, gcovr_version, llvm_cov_exe) def generate_coverage_legacy_rules(self, gcovr_exe: T.Optional[str], gcovr_version: T.Optional[str], llvm_cov_exe: T.Optional[str]) -> None: e = self.create_phony_target('coverage-html', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--html'], gcovr_exe, llvm_cov_exe) - e.add_item('description', 'Generates HTML coverage report') + e.add_item('description', 'Generating HTML coverage report') self.add_build(e) if gcovr_exe: e = self.create_phony_target('coverage-xml', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--xml'], gcovr_exe, llvm_cov_exe) - e.add_item('description', 'Generates XML coverage report') + e.add_item('description', 'Generating XML coverage report') self.add_build(e) e = self.create_phony_target('coverage-text', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--text'], gcovr_exe, llvm_cov_exe) - e.add_item('description', 'Generates text coverage report') + e.add_item('description', 'Generating text coverage report') self.add_build(e) if mesonlib.version_compare(gcovr_version, '>=4.2'): e = self.create_phony_target('coverage-sonarqube', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--sonarqube'], gcovr_exe, llvm_cov_exe) - e.add_item('description', 'Generates Sonarqube XML coverage report') + e.add_item('description', 'Generating Sonarqube XML coverage report') self.add_build(e) def generate_install(self) -> None: self.create_install_data_files() elem = self.create_phony_target('install', 'CUSTOM_COMMAND', 'PHONY') elem.add_dep('all') - elem.add_item('DESC', 'Installing files.') + elem.add_item('DESC', 'Installing files') elem.add_item('COMMAND', self.environment.get_build_command() + ['install', '--no-rebuild']) elem.add_item('pool', 'console') self.add_build(elem) @@ -1327,7 +1327,7 @@ def generate_tests(self) -> None: cmd += ['--print-errorlogs'] elem = self.create_phony_target('test', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) - elem.add_item('DESC', 'Running all tests.') + elem.add_item('DESC', 'Running all tests') elem.add_item('pool', 'console') self.add_build(elem) @@ -1337,7 +1337,7 @@ def generate_tests(self) -> None: 'benchmarklog', '--num-processes=1', '--no-rebuild'] elem = self.create_phony_target('benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) - elem.add_item('DESC', 'Running benchmark suite.') + elem.add_item('DESC', 'Running benchmark suite') elem.add_item('pool', 'console') self.add_build(elem) @@ -1372,7 +1372,7 @@ def generate_rules(self) -> None: '.'] self.add_rule(NinjaRule('REGENERATE_BUILD', c, [], - 'Regenerating build files.', + 'Regenerating build files', extra='generator = 1')) def add_rule_comment(self, comment: NinjaComment) -> None: @@ -2559,7 +2559,7 @@ def generate_scanner_rules(self) -> None: command = self.environment.get_build_command() + \ ['--internal', 'depscan'] args = ['$picklefile', '$out', '$in'] - description = 'Module scanner.' + description = 'Scanning modules' rule = NinjaRule(rulename, command, args, description) self.add_rule(rule) @@ -3411,7 +3411,7 @@ def generate_prelink(self, target, obj_list): cmd = self.replace_paths(target, cmd) elem.add_item('COMMAND', cmd) - elem.add_item('description', f'Prelinking {prelink_name}.') + elem.add_item('description', f'Prelinking {prelink_name}') self.add_build(elem) return [prelink_name] diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index cadbc40e9680..f4434bb9f359 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -889,17 +889,17 @@ def test_mtest_reconfigure(self): self.init(testdir) self.utime(os.path.join(testdir, 'meson.build')) o = self._run(self.mtest_command + ['--list']) - self.assertIn('Regenerating build files.', o) + self.assertIn('Regenerating build files', o) self.assertIn('test_features / xfail', o) o = self._run(self.mtest_command + ['--list']) - self.assertNotIn('Regenerating build files.', o) + self.assertNotIn('Regenerating build files', o) # no real targets should have been built tester = os.path.join(self.builddir, 'tester' + exe_suffix) self.assertPathDoesNotExist(tester) # check that we don't reconfigure if --no-rebuild is passed self.utime(os.path.join(testdir, 'meson.build')) o = self._run(self.mtest_command + ['--list', '--no-rebuild']) - self.assertNotIn('Regenerating build files.', o) + self.assertNotIn('Regenerating build files', o) def test_unexisting_test_name(self): testdir = os.path.join(self.unit_test_dir, '4 suite selection') From 7300e4ed88286ed4fd1f3bf9f2b6b29a1f8768c0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 23 Sep 2024 19:33:30 +0100 Subject: [PATCH 010/624] env2mfile: Generalize detection of pkg-config to have a list of tools Cross-tools on Debian generally follow the naming convention set by Autotools AC_CHECK_TOOL, where the name is prefixed with the GNU architecture tuple for the host architecture. env2mfile was already using this for pkg-config, but there are many other tools that can be detected in the same way. Signed-off-by: Simon McVittie --- mesonbuild/scripts/env2mfile.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index b2c9a45b2f80..16ab1b980c4f 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -230,10 +230,13 @@ def dpkg_architecture_to_machine_info(output: str, options: T.Any) -> MachineInf deb_detect_cmake(infos, data) except ValueError: pass - try: - infos.binaries['pkg-config'] = locate_path("%s-pkg-config" % host_arch) - except ValueError: - pass # pkg-config is optional + for tool in [ + 'pkg-config', + ]: + try: + infos.binaries[tool] = locate_path("%s-%s" % (host_arch, tool)) + except ValueError: + pass # optional try: infos.binaries['cups-config'] = locate_path("cups-config") except ValueError: From 851d866349b41533d57ef8a3c3e49f01f95680e3 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 2 Oct 2024 19:23:42 +0100 Subject: [PATCH 011/624] env2mfile: Use Debian cross-prefixed GObject-Introspection tools In Debian testing/unstable, there are wrappers available for various GObject-Introspection tools during cross-builds, using qemu internally. Signed-off-by: Simon McVittie --- mesonbuild/scripts/env2mfile.py | 6 ++++++ unittests/internaltests.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 16ab1b980c4f..2a366fd19950 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -231,6 +231,12 @@ def dpkg_architecture_to_machine_info(output: str, options: T.Any) -> MachineInf except ValueError: pass for tool in [ + 'g-ir-annotation-tool', + 'g-ir-compiler', + 'g-ir-doc-tool', + 'g-ir-generate', + 'g-ir-inspect', + 'g-ir-scanner', 'pkg-config', ]: try: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 3f50ac6e009b..0cf70600189f 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -1750,6 +1750,12 @@ def expected_binaries(gnu_tuple: str) -> T.Dict[str, T.List[str]]: 'cmake': ['/usr/bin/cmake'], 'pkg-config': [f'/usr/bin/{gnu_tuple}-pkg-config'], 'cups-config': ['/usr/bin/cups-config'], + 'g-ir-annotation-tool': [f'/usr/bin/{gnu_tuple}-g-ir-annotation-tool'], + 'g-ir-compiler': [f'/usr/bin/{gnu_tuple}-g-ir-compiler'], + 'g-ir-doc-tool': [f'/usr/bin/{gnu_tuple}-g-ir-doc-tool'], + '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'], } for title, dpkg_arch, gccsuffix, env, expected in [ From 86f9fe4c9a825feb93ca4b2ecf701d0877120d06 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 2 Oct 2024 19:25:12 +0100 Subject: [PATCH 012/624] env2mfile: Automatically set exe_wrapper on Debian if possible Recent versions of the architecture-properties package provide a cross-exe-wrapper package containing ${DEB_HOST_GNU_TYPE}-cross-exe-wrapper, which is currently a wrapper around qemu-user but could use different emulators on each architecture if it becomes necessary in the future. Signed-off-by: Simon McVittie --- mesonbuild/scripts/env2mfile.py | 7 +++++++ unittests/internaltests.py | 1 + 2 files changed, 8 insertions(+) diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 2a366fd19950..7896954fada9 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -243,6 +243,13 @@ def dpkg_architecture_to_machine_info(output: str, options: T.Any) -> MachineInf infos.binaries[tool] = locate_path("%s-%s" % (host_arch, tool)) except ValueError: pass # optional + for tool, exe in [ + ('exe_wrapper', 'cross-exe-wrapper'), + ]: + try: + infos.binaries[tool] = locate_path("%s-%s" % (host_arch, exe)) + except ValueError: + pass try: infos.binaries['cups-config'] = locate_path("cups-config") except ValueError: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 0cf70600189f..0116f7fde1fe 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -1750,6 +1750,7 @@ def expected_binaries(gnu_tuple: str) -> T.Dict[str, T.List[str]]: 'cmake': ['/usr/bin/cmake'], 'pkg-config': [f'/usr/bin/{gnu_tuple}-pkg-config'], 'cups-config': ['/usr/bin/cups-config'], + 'exe_wrapper': [f'/usr/bin/{gnu_tuple}-cross-exe-wrapper'], 'g-ir-annotation-tool': [f'/usr/bin/{gnu_tuple}-g-ir-annotation-tool'], 'g-ir-compiler': [f'/usr/bin/{gnu_tuple}-g-ir-compiler'], 'g-ir-doc-tool': [f'/usr/bin/{gnu_tuple}-g-ir-doc-tool'], From f0a86b2b8c05c091e3fc03c2982affbd3ec7f59e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 2 Oct 2024 19:27:00 +0100 Subject: [PATCH 013/624] env2mfile: Use a cross valac on Debian if possible This is functionally equivalent to the logic used to locate the cross exe_wrapper, but puts it below the "Compilers" heading rather than "Other binaries". Signed-off-by: Simon McVittie --- mesonbuild/scripts/env2mfile.py | 7 +++++++ unittests/internaltests.py | 1 + 2 files changed, 8 insertions(+) diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 7896954fada9..05bd1544e7c0 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -250,6 +250,13 @@ def dpkg_architecture_to_machine_info(output: str, options: T.Any) -> MachineInf infos.binaries[tool] = locate_path("%s-%s" % (host_arch, exe)) except ValueError: pass + for tool, exe in [ + ('vala', 'valac'), + ]: + try: + infos.compilers[tool] = locate_path("%s-%s" % (host_arch, exe)) + except ValueError: + pass try: infos.binaries['cups-config'] = locate_path("cups-config") except ValueError: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 0116f7fde1fe..69f52a413627 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -1739,6 +1739,7 @@ def expected_compilers( 'cpp': [f'/usr/bin/{gnu_tuple}-g++{gcc_suffix}'], 'objc': [f'/usr/bin/{gnu_tuple}-gobjc{gcc_suffix}'], 'objcpp': [f'/usr/bin/{gnu_tuple}-gobjc++{gcc_suffix}'], + 'vala': [f'/usr/bin/{gnu_tuple}-valac'], } def expected_binaries(gnu_tuple: str) -> T.Dict[str, T.List[str]]: From c02e0b7b1e2499f3ae18d26e443e18043fff3046 Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Wed, 24 Jan 2024 17:16:09 +0100 Subject: [PATCH 014/624] pkgconfig: fix use of uninstalled libraries Prepend the path of uninstalled libraries to PKG_CONFIG_PATH so they have preference over other search paths set by the user. see: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3247 --- mesonbuild/dependencies/pkgconfig.py | 2 +- unittests/linuxliketests.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index bc24f760fd0d..c6e6a5e4f2ca 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -260,7 +260,7 @@ def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables: if uninstalled: uninstalled_path = Path(self.env.get_build_dir(), 'meson-uninstalled').as_posix() if uninstalled_path not in extra_paths: - extra_paths.append(uninstalled_path) + extra_paths.insert(0, uninstalled_path) env.set('PKG_CONFIG_PATH', extra_paths) sysroot = self.env.properties[self.for_machine].get_sys_root() if sysroot: diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 3ac98076095c..1e9e38d1b323 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1141,6 +1141,42 @@ def test_pkgconfig_duplicate_path_entries(self): pkg_config_path = env.coredata.optstore.get_value('pkg_config_path') self.assertEqual(pkg_config_path, [pkg_dir]) + def test_pkgconfig_uninstalled_env_added(self): + ''' + Checks that the meson-uninstalled dir is added to PKG_CONFIG_PATH + ''' + testdir = os.path.join(self.unit_test_dir, '111 pkgconfig duplicate path entries') + meson_uninstalled_dir = os.path.join(self.builddir, 'meson-uninstalled') + + env = get_fake_env(testdir, self.builddir, self.prefix) + + newEnv = PkgConfigInterface.setup_env({}, env, MachineChoice.HOST, uninstalled=True) + + pkg_config_path_dirs = newEnv['PKG_CONFIG_PATH'].split(os.pathsep) + + self.assertEqual(len(pkg_config_path_dirs), 1) + self.assertEqual(pkg_config_path_dirs[0], meson_uninstalled_dir) + + def test_pkgconfig_uninstalled_env_prepended(self): + ''' + Checks that the meson-uninstalled dir is prepended to PKG_CONFIG_PATH + ''' + testdir = os.path.join(self.unit_test_dir, '111 pkgconfig duplicate path entries') + meson_uninstalled_dir = os.path.join(self.builddir, 'meson-uninstalled') + external_pkg_config_path_dir = os.path.join('usr', 'local', 'lib', 'pkgconfig') + + env = get_fake_env(testdir, self.builddir, self.prefix) + + env.coredata.set_options({OptionKey('pkg_config_path'): external_pkg_config_path_dir}, + subproject='') + + newEnv = PkgConfigInterface.setup_env({}, env, MachineChoice.HOST, uninstalled=True) + + pkg_config_path_dirs = newEnv['PKG_CONFIG_PATH'].split(os.pathsep) + + self.assertEqual(pkg_config_path_dirs[0], meson_uninstalled_dir) + self.assertEqual(pkg_config_path_dirs[1], external_pkg_config_path_dir) + @skipIfNoPkgconfig def test_pkgconfig_internal_libraries(self): ''' From afd89440aaf114c00652d799b8043d3d43fb807a Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 9 Mar 2024 18:44:40 -0500 Subject: [PATCH 015/624] cargo: Fix feature resolution Introduce a global Cargo interpreter state that keeps track of enabled features on each crate. Before generating AST of a Cargo subproject, it downloads every sub-subproject and resolves the set of features enabled on each of them recursively. When it later generates AST for one its dependencies, its set of features and dependencies is already determined. --- .../markdown/Wrap-dependency-system-manual.md | 21 - docs/markdown/snippets/cargo_features.md | 14 + mesonbuild/cargo/__init__.py | 4 +- mesonbuild/cargo/interpreter.py | 602 +++++++++--------- mesonbuild/environment.py | 3 + mesonbuild/interpreter/interpreter.py | 5 +- .../subprojects/bar-0.1-rs/Cargo.toml | 4 + .../subprojects/common-0-rs.wrap | 2 + .../subprojects/common-0-rs/Cargo.toml | 12 + .../subprojects/common-0-rs/lib.rs | 4 + .../subprojects/extra-dep-1-rs/Cargo.toml | 3 + .../subprojects/extra-dep-1-rs/meson.build | 2 - .../subprojects/foo-0-rs/Cargo.toml | 4 + .../subprojects/foo-0-rs/lib.rs | 3 + 14 files changed, 343 insertions(+), 340 deletions(-) create mode 100644 docs/markdown/snippets/cargo_features.md create mode 100644 test cases/rust/22 cargo subproject/subprojects/common-0-rs.wrap create mode 100644 test cases/rust/22 cargo subproject/subprojects/common-0-rs/Cargo.toml create mode 100644 test cases/rust/22 cargo subproject/subprojects/common-0-rs/lib.rs create mode 100644 test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index d84e4aa186d2..c1652c1c3b74 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -354,27 +354,6 @@ method = cargo dependency_names = foo-bar-0.1-rs ``` -Cargo features are exposed as Meson boolean options, with the `feature-` prefix. -For example the `default` feature is named `feature-default` and can be set from -the command line with `-Dfoo-1-rs:feature-default=false`. When a cargo subproject -depends on another cargo subproject, it will automatically enable features it -needs using the `dependency('foo-1-rs', default_options: ...)` mechanism. However, -unlike Cargo, the set of enabled features is not managed globally. Let's assume -the main project depends on `foo-1-rs` and `bar-1-rs`, and they both depend on -`common-1-rs`. The main project will first look up `foo-1-rs` which itself will -configure `common-rs` with a set of features. Later, when `bar-1-rs` does a lookup -for `common-1-rs` it has already been configured and the set of features cannot be -changed. If `bar-1-rs` wants extra features from `common-1-rs`, Meson will error out. -It is currently the responsibility of the main project to resolve those -issues by enabling extra features on each subproject: -```meson -project(..., - default_options: { - 'common-1-rs:feature-something': true, - }, -) -``` - In addition, if the file `meson/meson.build` exists, Meson will call `subdir('meson')` where the project can add manual logic that would usually be part of `build.rs`. Some naming conventions need to be respected: diff --git a/docs/markdown/snippets/cargo_features.md b/docs/markdown/snippets/cargo_features.md new file mode 100644 index 000000000000..26f1bff95d9d --- /dev/null +++ b/docs/markdown/snippets/cargo_features.md @@ -0,0 +1,14 @@ +## Cargo features are resolved globally + +When configuring a Cargo dependency, Meson will now resolve its complete +dependency tree and feature set before generating the subproject AST. +This solves many cases of Cargo subprojects being configured with missing +features that the main project had to enable by hand using e.g. +`default_options: ['foo-rs:feature-default=true']`. + +Note that there could still be issues in the case there are multiple Cargo +entry points. That happens if the main Meson project makes multiple `dependency()` +calls for different Cargo crates that have common dependencies. + +Breaks: This change removes per feature Meson options that were previously +possible to set as shown above or from command line `-Dfoo-rs:feature-foo=true`. diff --git a/mesonbuild/cargo/__init__.py b/mesonbuild/cargo/__init__.py index 10cb0be103c0..0a4d5f2abdcb 100644 --- a/mesonbuild/cargo/__init__.py +++ b/mesonbuild/cargo/__init__.py @@ -1,6 +1,6 @@ __all__ = [ - 'interpret', + 'Interpreter', 'load_wraps', ] -from .interpreter import interpret, load_wraps +from .interpreter import Interpreter, load_wraps diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 5f89c05ec88b..ac1c7633b1de 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -24,13 +24,11 @@ from . import builder from . import version from ..mesonlib import MesonException, Popen_safe -from ..options import OptionKey -from .. import coredata, options, mlog +from .. import coredata, mlog from ..wrap.wrap import PackageDefinition if T.TYPE_CHECKING: from types import ModuleType - from typing import Any from . import manifest from .. import mparser @@ -151,7 +149,10 @@ class Package: 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) @dataclasses.dataclass class Dependency: @@ -280,7 +281,6 @@ class Manifest: Cargo subprojects can contain what Meson wants to treat as multiple, interdependent, subprojects. - :param subdir: the subdirectory that this cargo project is in :param path: the path within the cargo subproject. """ @@ -295,7 +295,6 @@ class Manifest: example: T.List[Example] features: T.Dict[str, T.List[str]] target: T.Dict[str, T.Dict[str, Dependency]] - subdir: str path: str = '' def __post_init__(self) -> None: @@ -326,7 +325,6 @@ def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = 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()}, - subdir, path, ) @@ -393,18 +391,6 @@ def _dependency_varname(package_name: str) -> str: return f'{fixup_meson_varname(package_name)}_dep' -_OPTION_NAME_PREFIX = 'feature-' - - -def _option_name(feature: str) -> str: - # Add a prefix to avoid collision with Meson reserved options (e.g. "debug") - return _OPTION_NAME_PREFIX + feature - - -def _options_varname(depname: str) -> str: - return f'{fixup_meson_varname(depname)}_options' - - def _extra_args_varname() -> str: return 'extra_args' @@ -413,128 +399,174 @@ def _extra_deps_varname() -> str: return 'extra_deps' -def _create_project(cargo: Manifest, build: builder.Builder) -> T.List[mparser.BaseNode]: - """Create a function call - - :param cargo: The Manifest to generate from - :param build: The AST builder - :return: a list nodes - """ - args: T.List[mparser.BaseNode] = [] - args.extend([ - build.string(cargo.package.name), - build.string('rust'), - ]) - kwargs: T.Dict[str, mparser.BaseNode] = { - 'version': build.string(cargo.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([build.string(f'rust_std={cargo.package.edition}')]), - } - if cargo.package.license: - kwargs['license'] = build.string(cargo.package.license) - elif cargo.package.license_file: - kwargs['license_files'] = build.string(cargo.package.license_file) - - return [build.function('project', args, kwargs)] - - -def _process_feature(cargo: Manifest, feature: str) -> T.Tuple[T.Set[str], T.Dict[str, T.Set[str]], T.Set[str]]: - # Set of features that must also be enabled if this feature is enabled. - features: T.Set[str] = set() - # Map dependency name to a set of features that must also be enabled on that - # dependency if this feature is enabled. - dep_features: T.Dict[str, T.Set[str]] = collections.defaultdict(set) - # Set of dependencies that are required if this feature is enabled. - required_deps: T.Set[str] = set() - # Set of features that must be processed recursively. - to_process: T.Set[str] = {feature} - while to_process: - f = to_process.pop() - if '/' in f: - dep, dep_f = f.split('/', 1) - if dep[-1] == '?': - dep = dep[:-1] - else: - required_deps.add(dep) - dep_features[dep].add(dep_f) - elif f.startswith('dep:'): - required_deps.add(f[4:]) - elif f not in features: - features.add(f) - to_process.update(cargo.features.get(f, [])) - # A feature can also be a dependency - if f in cargo.dependencies: - required_deps.add(f) - return features, dep_features, required_deps - - -def _create_features(cargo: Manifest, build: builder.Builder) -> T.List[mparser.BaseNode]: - # https://doc.rust-lang.org/cargo/reference/features.html#the-features-section - - # Declare a dict that map enabled features to true. One for current project - # and one per dependency. - ast: T.List[mparser.BaseNode] = [] - ast.append(build.assign(build.dict({}), 'features')) - for depname in cargo.dependencies: - ast.append(build.assign(build.dict({}), _options_varname(depname))) - - # Declare a dict that map required dependencies to true - ast.append(build.assign(build.dict({}), 'required_deps')) - - for feature in cargo.features: - # if get_option(feature) - # required_deps += {'dep': true, ...} - # features += {'foo': true, ...} - # xxx_options += {'feature-foo': true, ...} - # ... - # endif - features, dep_features, required_deps = _process_feature(cargo, feature) - lines: T.List[mparser.BaseNode] = [ - build.plusassign( - build.dict({build.string(d): build.bool(True) for d in required_deps}), - 'required_deps'), - build.plusassign( - build.dict({build.string(f): build.bool(True) for f in features}), - 'features'), +class PackageState: + def __init__(self, manifest: Manifest) -> None: + self.manifest = manifest + 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) + + +@dataclasses.dataclass(frozen=True) +class PackageKey: + package_name: str + api: str + + +class Interpreter: + def __init__(self, env: Environment) -> None: + self.environment = env + # Map Cargo.toml's subdir to loaded manifest. + self.manifests: T.Dict[str, Manifest] = {} + # 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` + self._enable_feature(pkg, 'default') + + # Build an AST for this package + 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'), + build.function('message', [ + build.string('Enabled features:'), + build.array([build.string(f) for f in pkg.features]), + ]), ] - for depname, enabled_features in dep_features.items(): - lines.append(build.plusassign( - build.dict({build.string(_option_name(f)): build.bool(True) for f in enabled_features}), - _options_varname(depname))) - - ast.append(build.if_(build.function('get_option', [build.string(_option_name(feature))]), build.block(lines))) - - ast.append(build.function('message', [ - build.string('Enabled features:'), - build.method('keys', build.identifier('features'))], - )) - - return ast - - -def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mparser.BaseNode]: - ast: T.List[mparser.BaseNode] = [] - for name, dep in cargo.dependencies.items(): - # xxx_options += {'feature-default': true, ...} - extra_options: T.Dict[mparser.BaseNode, mparser.BaseNode] = { - build.string(_option_name('default')): build.bool(dep.default_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)) + + return build.block(ast) + + def _fetch_package(self, package_name: str, api: str) -> T.Tuple[PackageState, bool]: + key = PackageKey(package_name, api) + pkg = self.packages.get(key) + if pkg: + return pkg, True + meson_depname = _dependency_name(package_name, api) + subdir, _ = self.environment.wrap_resolver.resolve(meson_depname) + manifest = self._load_manifest(subdir) + pkg = PackageState(manifest) + self.packages[key] = pkg + # Fetch required dependencies recursively. + for depname, dep in 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 _load_manifest(self, subdir: str) -> Manifest: + 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_ + + def _add_dependency(self, pkg: PackageState, depname: str) -> None: + if depname in pkg.required_deps: + return + pkg.required_deps.add(depname) + dep = pkg.manifest.dependencies[depname] + dep_pkg, _ = self._fetch_package(dep.package, dep.api) + if dep.default_features: + self._enable_feature(dep_pkg, 'default') for f in dep.features: - extra_options[build.string(_option_name(f))] = build.bool(True) - ast.append(build.plusassign(build.dict(extra_options), _options_varname(name))) - + self._enable_feature(dep_pkg, f) + for f in pkg.optional_deps_features[depname]: + self._enable_feature(dep_pkg, f) + + def _enable_feature(self, pkg: PackageState, feature: str) -> None: + if feature in pkg.features: + return + pkg.features.add(feature) + # A feature can also be a dependency. + if feature in pkg.manifest.dependencies: + self._add_dependency(pkg, feature) + # Recurse on extra features and dependencies this feature pulls. + # https://doc.rust-lang.org/cargo/reference/features.html#the-features-section + for f in pkg.manifest.features.get(feature, []): + if '/' in f: + 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[depname] + dep_pkg = self._dep_package(dep) + self._enable_feature(dep_pkg, 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]: + """Create the project() function call + + :param pkg: The package to generate from + :param build: The AST builder + :return: a list nodes + """ + args: T.List[mparser.BaseNode] = [] + args.extend([ + build.string(pkg.manifest.package.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([build.string(f'rust_std={pkg.manifest.package.edition}')]), + } + if pkg.manifest.package.license: + kwargs['license'] = build.string(pkg.manifest.package.license) + elif pkg.manifest.package.license_file: + kwargs['license_files'] = build.string(pkg.manifest.package.license_file) + + return [build.function('project', args, kwargs)] + + def _create_dependencies(self, pkg: PackageState, build: builder.Builder) -> T.List[mparser.BaseNode]: + ast: T.List[mparser.BaseNode] = [] + for depname in pkg.required_deps: + dep = pkg.manifest.dependencies[depname] + ast += self._create_dependency(dep, build) + return ast + + def _create_dependency(self, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]: + pkg = self._dep_package(dep) kw = { 'version': build.array([build.string(s) for s in dep.version]), - 'default_options': build.identifier(_options_varname(name)), } - if dep.optional: - kw['required'] = build.method('get', build.identifier('required_deps'), [ - build.string(name), build.bool(False) - ]) - # Lookup for this dependency with the features we want in default_options kwarg. # # However, this subproject could have been previously configured with a @@ -546,8 +578,8 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar # otherwise abort with an error message. The user has to set the corresponding # option manually with -Dxxx-rs:feature-yyy=true, or the main project can do # that in its project(..., default_options: ['xxx-rs:feature-yyy=true']). - ast.extend([ - # xxx_dep = dependency('xxx', version : ..., default_options : xxx_options) + return [ + # xxx_dep = dependency('xxx', version : ...) build.assign( build.function( 'dependency', @@ -556,188 +588,132 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar ), _dependency_varname(dep.package), ), - # if xxx_dep.found() - build.if_(build.method('found', build.identifier(_dependency_varname(dep.package))), build.block([ - # actual_features = xxx_dep.get_variable('features', default_value : '').split(',') - build.assign( + # actual_features = xxx_dep.get_variable('features', default_value : '').split(',') + build.assign( + build.method( + 'split', build.method( - 'split', - build.method( - 'get_variable', - build.identifier(_dependency_varname(dep.package)), - [build.string('features')], - {'default_value': build.string('')} - ), - [build.string(',')], + 'get_variable', + build.identifier(_dependency_varname(dep.package)), + [build.string('features')], + {'default_value': build.string('')} ), - 'actual_features' + [build.string(',')], ), - # needed_features = [] - # foreach f, _ : xxx_options - # needed_features += f.substring(8) - # endforeach - build.assign(build.array([]), 'needed_features'), - build.foreach(['f', 'enabled'], build.identifier(_options_varname(name)), build.block([ - build.if_(build.identifier('enabled'), build.block([ - build.plusassign( - build.method('substring', build.identifier('f'), [build.number(len(_OPTION_NAME_PREFIX))]), - 'needed_features'), - ])), - ])), - # foreach f : needed_features - # if f not in actual_features - # error() - # endif - # endforeach - 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', [ - build.string('Dependency'), - build.string(_dependency_name(dep.package, dep.api)), - build.string('previously configured with features'), - build.identifier('actual_features'), - build.string('but need'), - build.identifier('needed_features'), - ]) - ])) - ])), + 'actual_features' + ), + # needed_features = [f1, f2, ...] + # foreach f : needed_features + # if f not in actual_features + # error() + # endif + # endforeach + build.assign(build.array([build.string(f) for f in pkg.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', [ + build.string('Dependency'), + build.string(_dependency_name(dep.package, dep.api)), + build.string('previously configured with features'), + build.identifier('actual_features'), + build.string('but need'), + build.identifier('needed_features'), + ]) + ])) ])), - ]) - return ast - - -def _create_meson_subdir(cargo: Manifest, build: builder.Builder) -> T.List[mparser.BaseNode]: - # Allow Cargo subprojects to add extra Rust args in meson/meson.build file. - # This is used to replace build.rs logic. - - # extra_args = [] - # extra_deps = [] - # fs = import('fs') - # if fs.is_dir('meson') - # subdir('meson') - # endif - return [ - build.assign(build.array([]), _extra_args_varname()), - build.assign(build.array([]), _extra_deps_varname()), - build.assign(build.function('import', [build.string('fs')]), 'fs'), - build.if_(build.method('is_dir', build.identifier('fs'), [build.string('meson')]), - build.block([build.function('subdir', [build.string('meson')])])) - ] - - -def _create_lib(cargo: Manifest, build: builder.Builder, crate_type: manifest.CRATE_TYPE) -> T.List[mparser.BaseNode]: - dependencies: T.List[mparser.BaseNode] = [] - dependency_map: T.Dict[mparser.BaseNode, mparser.BaseNode] = {} - for name, dep in cargo.dependencies.items(): - dependencies.append(build.identifier(_dependency_varname(dep.package))) - if name != dep.package: - dependency_map[build.string(fixup_meson_varname(dep.package))] = build.string(name) - - rust_args: T.List[mparser.BaseNode] = [ - build.identifier('features_args'), - build.identifier(_extra_args_varname()) - ] - - dependencies.append(build.identifier(_extra_deps_varname())) - - posargs: T.List[mparser.BaseNode] = [ - build.string(fixup_meson_varname(cargo.package.name)), - build.string(cargo.lib.path), - ] - - kwargs: T.Dict[str, mparser.BaseNode] = { - 'dependencies': build.array(dependencies), - 'rust_dependency_map': build.dict(dependency_map), - 'rust_args': build.array(rust_args), - } - - lib: mparser.BaseNode - if cargo.lib.proc_macro or crate_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' + ] + + def _create_meson_subdir(self, build: builder.Builder) -> T.List[mparser.BaseNode]: + # Allow Cargo subprojects to add extra Rust args in meson/meson.build file. + # This is used to replace build.rs logic. + + # extra_args = [] + # extra_deps = [] + # fs = import('fs') + # if fs.is_dir('meson') + # subdir('meson') + # endif + return [ + build.assign(build.array([]), _extra_args_varname()), + build.assign(build.array([]), _extra_deps_varname()), + build.assign(build.function('import', [build.string('fs')]), 'fs'), + build.if_(build.method('is_dir', build.identifier('fs'), [build.string('meson')]), + 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]: + dependencies: T.List[mparser.BaseNode] = [] + dependency_map: T.Dict[mparser.BaseNode, mparser.BaseNode] = {} + for name in pkg.required_deps: + dep = pkg.manifest.dependencies[name] + dependencies.append(build.identifier(_dependency_varname(dep.package))) + if name != dep.package: + dependency_map[build.string(fixup_meson_varname(dep.package))] = build.string(name) + + rust_args: T.List[mparser.BaseNode] = [ + build.identifier('features_args'), + build.identifier(_extra_args_varname()) + ] + + dependencies.append(build.identifier(_extra_deps_varname())) + + posargs: T.List[mparser.BaseNode] = [ + build.string(fixup_meson_varname(pkg.manifest.package.name)), + build.string(pkg.manifest.lib.path), + ] + + kwargs: T.Dict[str, mparser.BaseNode] = { + 'dependencies': build.array(dependencies), + 'rust_dependency_map': build.dict(dependency_map), + 'rust_args': build.array(rust_args), + } + + lib: mparser.BaseNode + if pkg.manifest.lib.proc_macro or crate_type == 'proc-macro': + lib = build.method('proc_macro', build.identifier('rust'), posargs, kwargs) 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) - - # features_args = [] - # foreach f, _ : features - # features_args += ['--cfg', 'feature="' + f + '"'] - # endforeach - # lib = xxx_library() - # dep = declare_dependency() - # meson.override_dependency() - return [ - build.assign(build.array([]), 'features_args'), - build.foreach(['f', '_'], build.identifier('features'), build.block([ - build.plusassign( - build.array([ - build.string('--cfg'), - build.plus(build.string('feature="'), build.plus(build.identifier('f'), build.string('"'))), - ]), - 'features_args') - ]) - ), - build.assign(lib, 'lib'), - build.assign( - build.function( - 'declare_dependency', - kw={ - 'link_with': build.identifier('lib'), - 'variables': build.dict({ - build.string('features'): build.method('join', build.string(','), [build.method('keys', build.identifier('features'))]), - }) - }, + if crate_type in {'lib', 'rlib', 'staticlib'}: + target_type = 'static_library' + elif crate_type in {'dylib', 'cdylib'}: + target_type = 'shared_library' + 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) + + features_args: T.List[mparser.BaseNode] = [] + for f in pkg.features: + features_args += [build.string('--cfg'), build.string(f'feature="{f}"')] + + # 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( + 'declare_dependency', + kw={ + 'link_with': build.identifier('lib'), + 'variables': build.dict({ + build.string('features'): build.string(','.join(pkg.features)), + }) + }, + ), + 'dep' + ), + build.method( + 'override_dependency', + build.identifier('meson'), + [ + build.string(_dependency_name(pkg.manifest.package.name, pkg.manifest.package.api)), + build.identifier('dep'), + ], ), - 'dep' - ), - build.method( - 'override_dependency', - build.identifier('meson'), - [ - build.string(_dependency_name(cargo.package.name, _version_to_api(cargo.package.version))), - build.identifier('dep'), - ], - ), - ] - - -def interpret(subp_name: str, subdir: str, env: Environment) -> T.Tuple[mparser.CodeBlockNode, dict[OptionKey, options.UserOption[Any]]]: - # subp_name should be in the form "foo-0.1-rs" - package_name = subp_name.rsplit('-', 2)[0] - manifests = _load_manifests(os.path.join(env.source_dir, subdir)) - cargo = manifests.get(package_name) - if not cargo: - raise MesonException(f'Cargo package {package_name!r} not found in {subdir}') - - filename = os.path.join(cargo.subdir, cargo.path, 'Cargo.toml') - build = builder.Builder(filename) - - # Generate project options - project_options: T.Dict[OptionKey, options.UserOption] = {} - for feature in cargo.features: - key = OptionKey(_option_name(feature), subproject=subp_name) - enabled = feature == 'default' - project_options[key] = options.UserBooleanOption(key.name, f'Cargo {feature} feature', enabled) - - ast = _create_project(cargo, build) - ast += [build.assign(build.function('import', [build.string('rust')]), 'rust')] - ast += _create_features(cargo, build) - ast += _create_dependencies(cargo, build) - ast += _create_meson_subdir(cargo, build) - - # Libs are always auto-discovered and there's no other way to handle them, - # which is unfortunate for reproducibility - if os.path.exists(os.path.join(env.source_dir, cargo.subdir, cargo.path, cargo.lib.path)): - for crate_type in cargo.lib.crate_type: - ast.extend(_create_lib(cargo, build, crate_type)) - - return build.block(ast), project_options + ] def load_wraps(source_dir: str, subproject_dir: str) -> T.List[PackageDefinition]: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 2621b9ca2db1..81a23404ab79 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -45,6 +45,7 @@ from .compilers import Compiler from .wrap.wrap import Resolver + from . import cargo CompilersDict = T.Dict[str, Compiler] @@ -687,6 +688,8 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared 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 _load_machine_file_options(self, config: 'ConfigParser', properties: Properties, machine: MachineChoice) -> None: """Read the contents of a Machine file and put it in the options store.""" diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 8a2e0e954af3..27177f30d40a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1045,9 +1045,10 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, 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) with mlog.nested(subp_name): - ast, options = cargo.interpret(subp_name, subdir, self.environment) - self.coredata.update_project_options(options, subp_name) + ast = self.environment.cargo.interpret(subdir) return self._do_subproject_meson( subp_name, subdir, default_options, kwargs, ast, # FIXME: Are there other files used by cargo interpreter? diff --git a/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml index d60a5d8f1c76..c0f7ffd8dd1e 100644 --- a/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml +++ b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml @@ -8,6 +8,10 @@ version = "0.1" optional = true version = "1.0" +[dependencies.common] +version = "0.0.1" +features = ["f2"] + [features] default = ["f2"] f1 = [] diff --git a/test cases/rust/22 cargo subproject/subprojects/common-0-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/common-0-rs.wrap new file mode 100644 index 000000000000..99686e90e78e --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/common-0-rs.wrap @@ -0,0 +1,2 @@ +[wrap-file] +method = cargo diff --git a/test cases/rust/22 cargo subproject/subprojects/common-0-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/common-0-rs/Cargo.toml new file mode 100644 index 000000000000..b22e1accf288 --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/common-0-rs/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "common" +version = "0.0.1" +edition = "2021" + +[lib] +crate-type = ["rlib"] +path = "lib.rs" + +[features] +f1 = [] +f2 = [] diff --git a/test cases/rust/22 cargo subproject/subprojects/common-0-rs/lib.rs b/test cases/rust/22 cargo subproject/subprojects/common-0-rs/lib.rs new file mode 100644 index 000000000000..a7adf8f62953 --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/common-0-rs/lib.rs @@ -0,0 +1,4 @@ +#[cfg(all(feature = "f1", feature = "f2"))] +pub fn common_func() -> i32 { + 0 +} 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 new file mode 100644 index 000000000000..4b6fa5777f45 --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "extra-deps" +version = "1.0" diff --git a/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build index 40d109b2d0f8..b5ca439513dc 100644 --- a/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build +++ b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build @@ -1,7 +1,5 @@ project('extra dep', 'c', version: '1.0') -assert(get_option('feature-default') == true) - l = static_library('extra-dep', 'lib.c') d = declare_dependency(link_with: l, variables: { 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 0f0225d06cc9..858efa4dd9cc 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 @@ -20,6 +20,10 @@ version = "1.0" [dependencies] mybar = { version = "0.1", package = "bar", default-features = false } +[dependencies.common] +version = "0.0.1" +features = ["f1"] + [features] default = ["f1"] f1 = ["f2", "f3"] 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 1c8cbc9d3fb5..a1a976a80339 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 @@ -1,3 +1,5 @@ +extern crate common; + extern "C" { fn extra_func() -> i32; } @@ -5,6 +7,7 @@ extern "C" { #[cfg(feature = "foo")] #[no_mangle] pub extern "C" fn rust_func() -> i32 { + assert!(common::common_func() == 0); let v: i32; unsafe { v = extra_func(); From ed62a975b23072dfedd770a398576603b0e64977 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sun, 10 Mar 2024 09:40:10 -0400 Subject: [PATCH 016/624] cargo: Use the library name instead of package name The library name defaults to its package name, but it can be different. For example: - package name: cairo-sys-rs - library name: cairo-sys - dependency name: ffi --- mesonbuild/cargo/interpreter.py | 6 ++++-- .../22 cargo subproject/subprojects/foo-0-rs/Cargo.toml | 3 +++ .../rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs | 2 ++ .../rust/22 cargo subproject/subprojects/libname-1-rs.wrap | 2 ++ .../subprojects/libname-1-rs/Cargo.toml | 7 +++++++ .../22 cargo subproject/subprojects/libname-1-rs/lib.rs | 3 +++ 6 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test cases/rust/22 cargo subproject/subprojects/libname-1-rs.wrap create mode 100644 test cases/rust/22 cargo subproject/subprojects/libname-1-rs/Cargo.toml create mode 100644 test cases/rust/22 cargo subproject/subprojects/libname-1-rs/lib.rs diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index ac1c7633b1de..b7b5b4a98784 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -648,7 +648,9 @@ def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: man dep = pkg.manifest.dependencies[name] dependencies.append(build.identifier(_dependency_varname(dep.package))) if name != dep.package: - dependency_map[build.string(fixup_meson_varname(dep.package))] = build.string(name) + 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) rust_args: T.List[mparser.BaseNode] = [ build.identifier('features_args'), @@ -658,7 +660,7 @@ def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: man dependencies.append(build.identifier(_extra_deps_varname())) posargs: T.List[mparser.BaseNode] = [ - build.string(fixup_meson_varname(pkg.manifest.package.name)), + build.string(fixup_meson_varname(pkg.manifest.lib.name)), build.string(pkg.manifest.lib.path), ] 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 858efa4dd9cc..8c5351a77b2e 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 @@ -24,6 +24,9 @@ mybar = { version = "0.1", package = "bar", default-features = false } version = "0.0.1" features = ["f1"] +[dependencies.libname] +version = "1" + [features] default = ["f1"] f1 = ["f2", "f3"] 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 a1a976a80339..4497dc4a4f3e 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 @@ -1,4 +1,5 @@ extern crate common; +extern crate libothername; extern "C" { fn extra_func() -> i32; @@ -8,6 +9,7 @@ extern "C" { #[no_mangle] pub extern "C" fn rust_func() -> i32 { assert!(common::common_func() == 0); + assert!(libothername::stuff() == 42); let v: i32; unsafe { v = extra_func(); diff --git a/test cases/rust/22 cargo subproject/subprojects/libname-1-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs.wrap new file mode 100644 index 000000000000..99686e90e78e --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs.wrap @@ -0,0 +1,2 @@ +[wrap-file] +method = cargo diff --git a/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/Cargo.toml new file mode 100644 index 000000000000..1fbc87c166ff --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "libname" +version = "1" + +[lib] +name="libothername" +path = "lib.rs" diff --git a/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/lib.rs b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/lib.rs new file mode 100644 index 000000000000..ff82dd1004be --- /dev/null +++ b/test cases/rust/22 cargo subproject/subprojects/libname-1-rs/lib.rs @@ -0,0 +1,3 @@ +pub fn stuff() -> i32 { + 42 +} From 6736a540c77c3c60a6526b6f39415ad1090d0fe2 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 17 Jun 2024 13:33:38 -0400 Subject: [PATCH 017/624] cargo: Load Cargo.lock and subprojects/*.wrap while recursing In the case the main project has a .wrap file for a cargo subproject, that subproject's Cargo.lock must be loaded before we can recursively fetch all its dependencies. --- mesonbuild/cargo/interpreter.py | 3 +++ mesonbuild/interpreter/interpreter.py | 16 +++++++--------- mesonbuild/wrap/wrap.py | 7 ++++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index b7b5b4a98784..f8f73a3b2ef3 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -33,6 +33,7 @@ from . import manifest from .. import mparser from ..environment import Environment + from ..interpreterbase import SubProject # tomllib is present in python 3.11, before that it is a pypi module called tomli, # we try to import tomllib, then tomli, @@ -458,6 +459,8 @@ def _fetch_package(self, package_name: str, api: str) -> T.Tuple[PackageState, b return pkg, True meson_depname = _dependency_name(package_name, api) subdir, _ = self.environment.wrap_resolver.resolve(meson_depname) + 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) pkg = PackageState(manifest) self.packages[key] = pkg diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 27177f30d40a..7bb2337425c5 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1294,15 +1294,13 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str self.build.subproject_dir = self.subproject_dir # Load wrap files from this (sub)project. - wrap_mode = WrapMode.from_string(self.coredata.get_option(OptionKey('wrap_mode'))) - if not self.is_subproject() or wrap_mode != WrapMode.nopromote: - subdir = os.path.join(self.subdir, spdirname) - r = wrap.Resolver(self.environment.get_source_dir(), subdir, self.subproject, wrap_mode) - if self.is_subproject(): - assert self.environment.wrap_resolver is not None, 'for mypy' - self.environment.wrap_resolver.merge_wraps(r) - else: - self.environment.wrap_resolver = r + subprojects_dir = os.path.join(self.subdir, spdirname) + if not self.is_subproject(): + wrap_mode = WrapMode.from_string(self.coredata.get_option(OptionKey('wrap_mode'))) + self.environment.wrap_resolver = wrap.Resolver(self.environment.get_source_dir(), subprojects_dir, self.subproject, wrap_mode) + else: + assert self.environment.wrap_resolver is not None, 'for mypy' + self.environment.wrap_resolver.load_and_merge(subprojects_dir, self.subproject) self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 197a4478a40e..7aae1663fd1f 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -394,7 +394,7 @@ def get_from_wrapdb(self, subp_name: str) -> T.Optional[PackageDefinition]: self.add_wrap(wrap) return wrap - def merge_wraps(self, other_resolver: 'Resolver') -> None: + 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(): @@ -402,6 +402,11 @@ def merge_wraps(self, other_resolver: 'Resolver') -> None: for k, v in other_resolver.provided_programs.items(): self.provided_programs.setdefault(k, v) + def load_and_merge(self, subdir: str, subproject: SubProject) -> None: + if self.wrap_mode != WrapMode.nopromote: + other_resolver = Resolver(self.source_dir, subdir, subproject, self.wrap_mode, self.wrap_frontend, self.allow_insecure, self.silent) + self._merge_wraps(other_resolver) + 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. # Thus the query name must also be in lower case. From db82c2d777b05a1df076e39a470ab0cbce31a184 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Sat, 22 Apr 2023 13:51:47 -0400 Subject: [PATCH 018/624] cargo: Add support for `system-deps` dependencies --- mesonbuild/cargo/interpreter.py | 79 ++++++++++++++++++- test cases/rust/26 cargo system deps/main.rs | 5 ++ .../rust/26 cargo system deps/meson.build | 11 +++ .../subprojects/sub-1-rs.wrap | 2 + .../subprojects/sub-1-rs/Cargo.toml | 16 ++++ .../subprojects/sub-1-rs/lib.rs | 15 ++++ 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 test cases/rust/26 cargo system deps/main.rs create mode 100644 test cases/rust/26 cargo system deps/meson.build create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml create mode 100644 test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index f8f73a3b2ef3..00e7136223f6 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -144,7 +144,7 @@ class Package: 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.Dict[str, str]] = dataclasses.field(default_factory=dict) + metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict) default_run: T.Optional[str] = None autobins: bool = True autoexamples: bool = True @@ -155,6 +155,45 @@ class Package: def __post_init__(self) -> None: self.api = _version_to_api(self.version) +@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: @@ -289,6 +328,7 @@ class Manifest: 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] @@ -300,6 +340,7 @@ class Manifest: 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: @@ -563,8 +604,38 @@ def _create_dependencies(self, pkg: PackageState, build: builder.Builder) -> T.L for depname in pkg.required_deps: dep = pkg.manifest.dependencies[depname] ast += self._create_dependency(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): + 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]: + kw = { + 'version': build.array([build.string(s) for s in dep.version]), + 'required': build.bool(not dep.optional), + } + varname = f'{fixup_meson_varname(name)}_system_dep' + cfg = f'system_deps_have_{fixup_meson_varname(name)}' + return [ + build.assign( + build.function( + 'dependency', + [build.string(dep.name)], + kw, + ), + varname, + ), + build.if_( + build.method('found', build.identifier(varname)), build.block([ + build.plusassign( + build.array([build.string('--cfg'), build.string(cfg)]), + 'system_deps_args' + ), + ]) + ), + ] + def _create_dependency(self, dep: Dependency, build: builder.Builder) -> T.List[mparser.BaseNode]: pkg = self._dep_package(dep) kw = { @@ -654,10 +725,14 @@ def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: man 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) + for name, sys_dep in pkg.manifest.system_dependencies.items(): + if sys_dep.enabled(pkg.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(_extra_args_varname()), + build.identifier('system_deps_args'), ] dependencies.append(build.identifier(_extra_deps_varname())) diff --git a/test cases/rust/26 cargo system deps/main.rs b/test cases/rust/26 cargo system deps/main.rs new file mode 100644 index 000000000000..25e3b2fb896f --- /dev/null +++ b/test cases/rust/26 cargo system deps/main.rs @@ -0,0 +1,5 @@ +extern crate sub; + +pub fn main() { + sub::func(); +} diff --git a/test cases/rust/26 cargo system deps/meson.build b/test cases/rust/26 cargo system deps/meson.build new file mode 100644 index 000000000000..9545bb452c16 --- /dev/null +++ b/test cases/rust/26 cargo system deps/meson.build @@ -0,0 +1,11 @@ +project('cargo system-deps', 'rust') + +glib = dependency('glib-2.0', required: false) +if not glib.found() + error('MESON_SKIP_TEST: Need glib system dependency') +endif + +sub_dep = dependency('sub-1-rs') +exe = executable('main', 'main.rs', dependencies : sub_dep) +test('main', exe) + diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap new file mode 100644 index 000000000000..99686e90e78e --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs.wrap @@ -0,0 +1,2 @@ +[wrap-file] +method = cargo diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml new file mode 100644 index 000000000000..88d5445f6696 --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = 'sub' +version = '1' + +[build-dependencies] +system-deps = "6" + +[lib] +name = "sub" +path = "lib.rs" + +[package.metadata.system-deps] +glib = { name = "glib-2.0", version=" 2.0 , 2.1 , <3 ", feature="default" } +gobject = { name = "gobject-2.0", version=">=99", optional=true } +notfound = { feature="notfound" } +libffi = "1.0" diff --git a/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs new file mode 100644 index 000000000000..6e39d236532c --- /dev/null +++ b/test cases/rust/26 cargo system deps/subprojects/sub-1-rs/lib.rs @@ -0,0 +1,15 @@ +extern "C" { + fn g_get_tmp_dir() -> *mut std::ffi::c_void; +} + +#[cfg(system_deps_have_glib)] +#[cfg(not(system_deps_have_gobject))] +pub fn func() { + unsafe { + g_get_tmp_dir(); + } +} + +pub fn func1() { + func() +} From 8c5505c28ad801c90af17d639e8bf3de8ca3143f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= Date: Wed, 23 Oct 2024 12:53:44 +0200 Subject: [PATCH 019/624] Reimplement several methods for Fortran compilers The output_is_64bit, sizeof, cross_sizeof, compute_int and cross_compute_int methods are reimplemented for Fortran compilers. Those inherited from CLikeCompiler do not work since they assume C or C++. Note that those tests rely on Fortran 2008 features (notably the c_sizeof operator). Closes #12757 --- ...xed_sizeof_and_find_library_for_fortran.md | 8 ++ mesonbuild/compilers/fortran.py | 132 ++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md diff --git a/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md b/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md new file mode 100644 index 000000000000..6893429e8640 --- /dev/null +++ b/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md @@ -0,0 +1,8 @@ +## Fixed `sizeof` and `find_library` methods for Fortran compilers + +The implementation of the `.sizeof()` method has been fixed for Fortran +compilers (it was previously broken since it would try to compile a C code +snippet). Note that this functionality requires Fortran 2008 support. + +Incidentally this also fixes the `.find_library()` method for Fortran compilers +when the `prefer_static` built-in option is set to true. diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 5012fba074a0..e54864dff696 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -4,9 +4,11 @@ from __future__ import annotations import typing as T +import functools import os from .. import options +from .. import mesonlib from .compilers import ( clike_debug_args, Compiler, @@ -121,6 +123,136 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 'none'), ) + def _compile_int(self, expression: str, prefix: str, env: 'Environment', + 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 + # Taken from https://github.com/j3-fortran/fortran_proposals/issues/70 + t = f'''program test + {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] + + 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: + # 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): + 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): + low = cur = 0 + while self._compile_int(f'{expression} > {cur}', prefix, env, extra_args, dependencies): + low = cur + 1 + if low > maxint: + raise mesonlib.EnvironmentException('Cross-compile check overflowed') + cur = min(cur * 2 + 1, maxint) + high = cur + else: + high = cur = -1 + while self._compile_int(f'{expression} < {cur}', prefix, env, extra_args, dependencies): + high = cur - 1 + if high < minint: + raise mesonlib.EnvironmentException('Cross-compile check overflowed') + cur = max(cur * 2, minint) + low = cur + else: + # Sanity check limits given by user + 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): + 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): + high = cur + else: + low = cur + 1 + + return low + + def 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]]], + 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) + t = f'''program test + {prefix} + print '(i0)', {expression} + end program test + ''' + res = self.run(t, env, 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: + if extra_args is None: + extra_args = [] + t = f'''program test + use iso_c_binding + {prefix} + {typename} :: something + end program test + ''' + if not self.compiles(t, env, 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) + + def 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) -> 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) + return r, False + t = f'''program test + use iso_c_binding + {prefix} + {typename} :: x + print '(i0)', c_sizeof(x) + end program test + ''' + res = self.cached_run(t, env, extra_args=extra_args, + dependencies=dependencies) + if not res.compiled: + return -1, False + if res.returncode != 0: + raise mesonlib.EnvironmentException('Could not run sizeof test binary.') + return int(res.stdout), res.cached + + @functools.lru_cache() + def output_is_64bit(self, env: 'Environment') -> bool: + ''' + returns true if the output produced is 64-bit, false if 32-bit + ''' + return self.sizeof('type(c_ptr)', '', env)[0] == 8 + class GnuFortranCompiler(GnuCompiler, FortranCompiler): From b131b2dc76ff0b14d755b1a3bbf7ce9565f49b0d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Sat, 26 Oct 2024 20:05:35 -0700 Subject: [PATCH 020/624] modules/rust: Add support for autolib field in the Cargo.toml This adds support for parsing the new `autolib` member (https://github.com/rust-lang/cargo/pull/14591). This is a quick and easy fix, suitable for backport. A larger more involved fix will be to stop splatting out the cargo dictionaries, and instead pass the fields we know about 1 by 1, and warning the user if we come across unknown fields. Fixes: #13826 --- mesonbuild/cargo/interpreter.py | 1 + mesonbuild/cargo/manifest.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 00e7136223f6..2f67a781ddb7 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -146,6 +146,7 @@ class Package: 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 diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 50c048991333..95b0d4bb8cb2 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2022-2023 Intel Corporation +# Copyright © 2022-2024 Intel Corporation """Type definitions for cargo manifest files.""" @@ -33,6 +33,7 @@ 'publish': bool, 'metadata': T.Dict[str, T.Dict[str, str]], 'default-run': str, + 'autolib': bool, 'autobins': bool, 'autoexamples': bool, 'autotests': bool, @@ -65,6 +66,7 @@ class FixedPackage(TypedDict, total=False): publish: bool metadata: T.Dict[str, T.Dict[str, str]] default_run: str + autolib: bool autobins: bool autoexamples: bool autotests: bool From 38dc9894add23691a948a8a733d3175e0b94b183 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 26 Oct 2024 17:48:19 -0400 Subject: [PATCH 021/624] Move LD_LIBRARY_PATH logic to environment object --- mesonbuild/backend/backends.py | 22 +--------------------- mesonbuild/environment.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 079b62dbdeb9..a4be50f664b1 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1987,12 +1987,9 @@ def get_introspection_data(self, target_id: str, target: build.Target) -> T.List return [] def get_devenv(self) -> mesonlib.EnvironmentVariables: - env = mesonlib.EnvironmentVariables() extra_paths = set() library_paths = set() - build_machine = self.environment.machines[MachineChoice.BUILD] host_machine = self.environment.machines[MachineChoice.HOST] - need_wine = not build_machine.is_windows() and host_machine.is_windows() for t in self.build.get_targets().values(): in_default_dir = t.should_install() and not t.get_install_dir()[2] if t.for_machine != MachineChoice.HOST or not in_default_dir: @@ -2012,24 +2009,7 @@ def get_devenv(self) -> mesonlib.EnvironmentVariables: # LD_LIBRARY_PATH. This allows running system applications using # that library. library_paths.add(tdir) - if need_wine: - # Executable paths should be in both PATH and WINEPATH. - # - Having them in PATH makes bash completion find it, - # and make running "foo.exe" find it when wine-binfmt is installed. - # - Having them in WINEPATH makes "wine foo.exe" find it. - library_paths.update(extra_paths) - if library_paths: - if need_wine: - env.prepend('WINEPATH', list(library_paths), separator=';') - elif host_machine.is_windows() or host_machine.is_cygwin(): - extra_paths.update(library_paths) - elif host_machine.is_darwin(): - env.prepend('DYLD_LIBRARY_PATH', list(library_paths)) - else: - env.prepend('LD_LIBRARY_PATH', list(library_paths)) - if extra_paths: - env.prepend('PATH', list(extra_paths)) - return env + return self.environment.get_env_for_paths(library_paths, extra_paths) def compiler_to_generator_args(self, target: build.BuildTarget, compiler: 'Compiler', output: str = '@OUTPUT@', diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 81a23404ab79..584d7366d448 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -1000,3 +1000,25 @@ def get_exe_wrapper(self) -> T.Optional[ExternalProgram]: def has_exe_wrapper(self) -> bool: return self.exe_wrapper and self.exe_wrapper.found() + + def get_env_for_paths(self, library_paths: T.Set[str], extra_paths: T.Set[str]) -> mesonlib.EnvironmentVariables: + env = mesonlib.EnvironmentVariables() + need_wine = not self.machines.build.is_windows() and self.machines.host.is_windows() + if need_wine: + # Executable paths should be in both PATH and WINEPATH. + # - Having them in PATH makes bash completion find it, + # and make running "foo.exe" find it when wine-binfmt is installed. + # - Having them in WINEPATH makes "wine foo.exe" find it. + library_paths.update(extra_paths) + if library_paths: + if need_wine: + env.prepend('WINEPATH', list(library_paths), separator=';') + elif self.machines.host.is_windows() or self.machines.host.is_cygwin(): + extra_paths.update(library_paths) + elif self.machines.host.is_darwin(): + env.prepend('DYLD_LIBRARY_PATH', list(library_paths)) + else: + env.prepend('LD_LIBRARY_PATH', list(library_paths)) + if extra_paths: + env.prepend('PATH', list(extra_paths)) + return env From 1840bb02ba741fb62a8d613a71431a8d7fa86a00 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 26 Oct 2024 17:49:10 -0400 Subject: [PATCH 022/624] external-project: Setup devenv to run programs --- docs/markdown/External-Project-module.md | 8 ++++++-- .../markdown/snippets/external_project_devenv.md | 7 +++++++ mesonbuild/modules/external_project.py | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/external_project_devenv.md diff --git a/docs/markdown/External-Project-module.md b/docs/markdown/External-Project-module.md index 615c6c117d0c..39449fe21102 100644 --- a/docs/markdown/External-Project-module.md +++ b/docs/markdown/External-Project-module.md @@ -5,6 +5,8 @@ *This is an experimental module, API could change.* +*Added 0.56.0* + This module allows building code that uses build systems other than Meson. This module is intended to be used to build Autotools subprojects as fallback if the dependency couldn't be found on the @@ -47,7 +49,8 @@ Known limitations: from `-uninstalled.pc` files. This is arguably a bug that could be fixed in future version of pkg-config/pkgconf. -*Added 0.56.0* +*Since 1.7.0* [Meson devenv][Commands.md#devenv] setup `PATH` and +`LD_LIBRARY_PATH` to be able to run programs. ## Functions @@ -78,7 +81,8 @@ Keyword arguments: added in case some tags are not found in `configure_options`: `'--prefix=@PREFIX@'`, `'--libdir=@PREFIX@/@LIBDIR@'`, and `'--includedir=@PREFIX@/@INCLUDEDIR@'`. It was previously considered a fatal - error to not specify them. + error to not specify them. *Since 1.7.0* `@BINDIR@` and `'--bindir=@PREFIX@/@BINDIR@'` + default argument have been added. - `cross_configure_options`: Extra options appended to `configure_options` only when cross compiling. special tag `@HOST@` will be replaced by `'{}-{}-{}'.format(host_machine.cpu_family(), build_machine.system(), host_machine.system()`. diff --git a/docs/markdown/snippets/external_project_devenv.md b/docs/markdown/snippets/external_project_devenv.md new file mode 100644 index 000000000000..1927bd32f4e9 --- /dev/null +++ b/docs/markdown/snippets/external_project_devenv.md @@ -0,0 +1,7 @@ +## Devenv support in external project module + +The [external project module](External-Project-module.md) now setups `PATH` and +`LD_LIBRARY_PATH` to be able to run programs. + +`@BINDIR@` is now substitued in arguments and `'--bindir=@PREFIX@/@BINDIR@'` +default argument have been added. diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py index fb82a384d919..9e283e026b36 100644 --- a/mesonbuild/modules/external_project.py +++ b/mesonbuild/modules/external_project.py @@ -81,6 +81,9 @@ def __init__(self, _l = self.env.coredata.get_option(OptionKey('libdir')) assert isinstance(_l, str), 'for mypy' self.libdir = Path(_l) + _l = self.env.coredata.get_option(OptionKey('bindir')) + assert isinstance(_l, str), 'for mypy' + self.bindir = Path(_l) _i = self.env.coredata.get_option(OptionKey('includedir')) assert isinstance(_i, str), 'for mypy' self.includedir = Path(_i) @@ -118,6 +121,7 @@ def _configure(self, state: 'ModuleState') -> None: d = [('PREFIX', '--prefix=@PREFIX@', self.prefix.as_posix()), ('LIBDIR', '--libdir=@PREFIX@/@LIBDIR@', self.libdir.as_posix()), + ('BINDIR', '--bindir=@PREFIX@/@BINDIR@', self.bindir.as_posix()), ('INCLUDEDIR', None, self.includedir.as_posix()), ] self._validate_configure_options(d, state) @@ -278,6 +282,7 @@ class ExternalProjectModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter'): super().__init__(interpreter) + self.devenv: T.Optional[EnvironmentVariables] = None self.methods.update({'add_project': self.add_project, }) @@ -299,8 +304,19 @@ def add_project(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'AddProj kwargs['env'], kwargs['verbose'], kwargs['depends']) + abs_libdir = Path(project.install_dir, project.rel_prefix, project.libdir).as_posix() + abs_bindir = Path(project.install_dir, project.rel_prefix, project.bindir).as_posix() + env = state.environment.get_env_for_paths({abs_libdir}, {abs_bindir}) + if self.devenv is None: + self.devenv = env + else: + self.devenv.merge(env) return ModuleReturnValue(project, project.targets) + def postconf_hook(self, b: build.Build) -> None: + if self.devenv is not None: + b.devenv.append(self.devenv) + def initialize(interp: 'Interpreter') -> ExternalProjectModule: return ExternalProjectModule(interp) From 8242187eb06adaabbd9c9dbb6e5d9a8c102ee6a2 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 23 Oct 2024 22:47:48 +0530 Subject: [PATCH 023/624] meson: De-dup rpath arguments This fixes spammy warnings on apple clang: ld: warning: duplicate -rpath 'build/dist/darwin_universal/arm64/lib/pkgconfig/../../lib' ignored --- mesonbuild/compilers/mixins/clike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index d56547b475ed..408e815fef97 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -53,7 +53,7 @@ class CLikeCompilerArgs(arglist.CompilerArgs): # NOTE: not thorough. A list of potential corner cases can be found in # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 - dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic') + dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic', '-Wl,-rpath') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread') From a337dfe2650824c98683d71a91a577706c3e0e62 Mon Sep 17 00:00:00 2001 From: Jean-Bernard Berteaux Date: Wed, 16 Oct 2024 09:27:38 +0200 Subject: [PATCH 024/624] test: report timeout as failure --- mesonbuild/mtest.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index c417bc0b38b7..503cb14325dd 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -856,7 +856,7 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: et.SubElement(testcase, 'failure') elif subtest.result is TestResult.UNEXPECTEDPASS: fail = et.SubElement(testcase, 'failure') - fail.text = 'Test unexpected passed.' + fail.text = 'Test unexpectedly passed.' elif subtest.result is TestResult.INTERRUPT: fail = et.SubElement(testcase, 'error') fail.text = 'Test was interrupted by user.' @@ -891,6 +891,18 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: elif test.res is TestResult.FAIL: et.SubElement(testcase, 'failure') suite.attrib['failures'] = str(int(suite.attrib['failures']) + 1) + elif test.res is TestResult.UNEXPECTEDPASS: + fail = et.SubElement(testcase, 'failure') + fail.text = 'Test unexpectedly passed.' + suite.attrib['failures'] = str(int(suite.attrib['failures']) + 1) + elif test.res is TestResult.INTERRUPT: + fail = et.SubElement(testcase, 'error') + fail.text = 'Test was interrupted by user.' + suite.attrib['errors'] = str(int(suite.attrib['errors']) + 1) + elif test.res is TestResult.TIMEOUT: + fail = et.SubElement(testcase, 'error') + fail.text = 'Test did not finish before configured timeout.' + suite.attrib['errors'] = str(int(suite.attrib['errors']) + 1) if test.stdo: out = et.SubElement(testcase, 'system-out') out.text = replace_unencodable_xml_chars(test.stdo.rstrip()) From 59a608878c83d82f6943def8d9be15fa49d128a7 Mon Sep 17 00:00:00 2001 From: x1z53 Date: Wed, 23 Oct 2024 00:26:49 +0300 Subject: [PATCH 025/624] msetup: Correction of the message text --- mesonbuild/msetup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 81f4af1c444f..e634c05ab5aa 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -165,7 +165,7 @@ def validate_dirs(self) -> T.Tuple[str, str]: if not self.options.reconfigure and not self.options.wipe: print('Directory already configured.\n\n' 'Just run your build command (e.g. ninja) and Meson will regenerate as necessary.\n' - 'Run "meson setup --reconfigure to force Meson to regenerate.\n\n' + 'Run "meson setup --reconfigure" to force Meson to regenerate.\n\n' 'If build failures persist, run "meson setup --wipe" to rebuild from scratch\n' 'using the same options as passed when configuring the build.') if self.options.cmd_line_options: From 6091ef0f5aa556c9967990cedfb64868028c64ed Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 31 Oct 2024 17:26:49 -0400 Subject: [PATCH 026/624] Revert "meson: De-dup rpath arguments" This reverts commit 8242187eb06adaabbd9c9dbb6e5d9a8c102ee6a2. This same change was previously introduced in #12495 / commit 30ab9747ae03364ac2ccd4a8f508286c57ed54a5 and subsequently reverted in #12672 / commit 2fbc7b5ce3aced483b196dd10ca9eee1713b7494 Reintroduced in #13819 but it's still the same old problem. To repeat the problem: This breaks at least: - frameworks/17 mpi - frameworks/30 scalapack The problem is that openmpi's pkg-config emitted link arguments includes: ``` -Wl,-rpath -Wl,/path/to/libdir ``` The deduplication logic in meson doesn't contain sufficient information to tell when the compiler is passing an argument that requires values, and definitely cannot tell when that argument is split across argv. But for arguments that *can* do this, it is not possible to deduplicate a single argument as standalone, because it is not standalone. The argument for deduplicating rpath here was that if you have multiple dependencies that all add the same rpath, the Apple ld64 emits a non-fatal warning "duplicate -rpath ignored". Since this is non-fatal, it's not a major issue. A major issue is when builds fatally error out with: ``` FAILED: scalapack_c cc -o scalapack_c scalapack_c.p/main.c.o -Wl,--as-needed -Wl,--no-undefined -Wl,--start-group /usr/lib64/libscalapack.so /usr/lib64/liblapack.so /usr/lib64/libblas.so -Wl,-rpath -Wl,/usr/lib64 -Wl,/usr/lib64 -Wl,--enable-new-dtags /usr/lib64/libmpi.so -Wl,--end-group /usr/libexec/gcc/x86_64-pc-linux-gnu/ld: error: /usr/lib64: read: Is a directory ``` Today we have CI for this so the change actually caused our own unittest CI to go red. --- mesonbuild/compilers/mixins/clike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 408e815fef97..d56547b475ed 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -53,7 +53,7 @@ class CLikeCompilerArgs(arglist.CompilerArgs): # NOTE: not thorough. A list of potential corner cases can be found in # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 - dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic', '-Wl,-rpath') + dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread') From a608e56beba7eb1b3335e824ba2f1fe8453f0979 Mon Sep 17 00:00:00 2001 From: Luke Smith Date: Wed, 30 Oct 2024 12:57:41 +0800 Subject: [PATCH 027/624] Correct typo in example Python module option python.install_env is the correct command, as documented in https://mesonbuild.com/Builtin-options.html#python-module. python.install_venv causes an error, it is not immediately obvious that the slight typo is the cause. --- docs/markdown/Python-module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md index f3ee9ffc5a98..cd2a641d9250 100644 --- a/docs/markdown/Python-module.md +++ b/docs/markdown/Python-module.md @@ -18,7 +18,7 @@ compatible with [PEP-517](https://peps.python.org/pep-0517/), check out If you are building Python extension modules against a Python interpreter located in a venv or Conda environment, you probably want to set -`python.install_venv=auto`; +`python.install_env=auto`; see [Python module options](Builtin-options.md#python-module) for details. *Added 0.46.0* From d7e1d19285e99a602d6ded7d9d0b06bbb3ae6fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Nogueira?= Date: Fri, 4 Oct 2024 22:11:43 -0300 Subject: [PATCH 028/624] modules/wayland: document that scan_xml can take any protocol. The quick example can be slightly misleading in implying that scan_xml must use find_protocol, which is not the case. So explicitly mention it in the scan_xml docs. --- docs/markdown/Wayland-module.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Wayland-module.md b/docs/markdown/Wayland-module.md index 679329373713..3ffa58739415 100644 --- a/docs/markdown/Wayland-module.md +++ b/docs/markdown/Wayland-module.md @@ -53,7 +53,8 @@ generated = wl_mod.scan_xml( include_core_only : true, ) ``` -This function accepts one or more arguments of either string or file type. +This function accepts one or more arguments of either string or file type, so +it can be used in conjunction with `find_protocol` or not. It takes the following keyword arguments: - `public` Optional arg that specifies the scope of the generated code. @@ -63,7 +64,7 @@ It takes the following keyword arguments: - `server` Optional arg that specifies if server side header file is generated. The default is false. - `include_core_only` Optional arg that specifies that generated headers only include - `wayland--core.h` instead of `wayland-.h`. + `wayland--core.h` instead of `wayland-.h`. The default is true. Since *0.64.0* **Returns**: a list of [[@custom_tgt]] in the order source, client side header, From 4ac3e7d356cfc1a6a290cd6961f01c11510c0618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 31 Oct 2024 17:37:52 -0500 Subject: [PATCH 029/624] Enable GCC to find free-threaded python DLL library --- mesonbuild/dependencies/python.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index fff4aaa9e849..326e605d8543 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -229,7 +229,10 @@ def get_windows_link_args(self, limited_api: bool) -> T.Optional[T.List[str]]: elif imp_lower == 'pypy': libpath = Path(f'libpypy{verdot}-c.dll') else: - libpath = Path(f'python{vernum}.dll') + if self.is_freethreaded: + libpath = Path(f'python{vernum}t.dll') + else: + libpath = Path(f'python{vernum}.dll') else: if self.is_freethreaded: libpath = Path('libs') / f'python{vernum}t.lib' From 2d40813d4264b312acc51b78250513b3536c682b Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Wed, 30 Oct 2024 15:05:16 +0100 Subject: [PATCH 030/624] Add -q as alternative to --quiet for meson install Since "meson test" already has both -q and --quiet it makes a lot of sense to add the short option to "meson install" too for reasons of symmetry. --- data/shell-completions/bash/meson | 1 + data/shell-completions/zsh/_meson | 2 +- mesonbuild/minstall.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/shell-completions/bash/meson b/data/shell-completions/bash/meson index 0814342dbe2a..404369818dad 100644 --- a/data/shell-completions/bash/meson +++ b/data/shell-completions/bash/meson @@ -408,6 +408,7 @@ _meson-install() { shortopts=( h n + q C ) diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson index 8178060b4eda..f64dfd4f3d44 100644 --- a/data/shell-completions/zsh/_meson +++ b/data/shell-completions/zsh/_meson @@ -217,7 +217,7 @@ local -a meson_commands=( "$__meson_cd" '--no-rebuild[Do not rebuild before installing]' '--only-changed[Do not overwrite files that are older than the copied file]' - '--quiet[Do not print every file that was installed]' + '(--quiet -q)'{'--quiet','-q'}'[Do not print every file that was installed]' ) _arguments \ '(: -)'{'--help','-h'}'[show a help message and quit]' \ diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 9921295fda8e..93324d94f4f6 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -74,7 +74,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Do not rebuild before installing.') parser.add_argument('--only-changed', default=False, action='store_true', help='Only overwrite files that are older than the copied file.') - parser.add_argument('--quiet', default=False, action='store_true', + parser.add_argument('-q', '--quiet', default=False, action='store_true', help='Do not print every file that was installed.') parser.add_argument('--destdir', default=None, help='Sets or overrides DESTDIR environment. (Since 0.57.0)') From b8cdd06af5ecf8ddc9c788c95cf05cb5b8c5cda7 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Wed, 30 Oct 2024 15:11:37 +0100 Subject: [PATCH 031/624] Consistently list short options first for install Other commands already list the short options first plus -h is always listed before --help. --- mesonbuild/minstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 93324d94f4f6..860826bf1b84 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -78,7 +78,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Do not print every file that was installed.') parser.add_argument('--destdir', default=None, help='Sets or overrides DESTDIR environment. (Since 0.57.0)') - parser.add_argument('--dry-run', '-n', action='store_true', + parser.add_argument('-n', '--dry-run', action='store_true', help='Doesn\'t actually install, but print logs. (Since 0.57.0)') parser.add_argument('--skip-subprojects', nargs='?', const='*', default='', help='Do not install files from given subprojects. (Since 0.58.0)') From 040c8d04cc66af891e251939bd44bcc0da11a543 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Mon, 4 Nov 2024 12:23:05 +0100 Subject: [PATCH 032/624] CMakeToolchain: Log output on compiler state failure --- mesonbuild/cmake/toolchain.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 43f262605922..9eb961c52bc7 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -232,10 +232,15 @@ def update_cmake_compiler_state(self) -> None: cmake_args += trace.trace_args() cmake_args += cmake_get_generator_args(self.env) cmake_args += [f'-DCMAKE_TOOLCHAIN_FILE={temp_toolchain_file.as_posix()}', '.'] - rc, _, raw_trace = self.cmakebin.call(cmake_args, build_dir=build_dir, disable_cache=True) + rc, raw_stdout, raw_trace = self.cmakebin.call(cmake_args, build_dir=build_dir, disable_cache=True) if rc != 0: mlog.warning('CMake Toolchain: Failed to determine CMake compilers state') + mlog.debug(f' -- return code: {rc}') + for line in raw_stdout.split('\n'): + mlog.debug(f' -- stdout: {line.rstrip()}') + for line in raw_trace.split('\n'): + mlog.debug(f' -- stderr: {line.rstrip()}') return # Parse output From 273894d9897dc60e524506e5dbefc550184aa2cf Mon Sep 17 00:00:00 2001 From: Nick <0xb000@gmail.com> Date: Mon, 4 Nov 2024 17:58:54 +0200 Subject: [PATCH 033/624] Xcode backend: only use found appleframeworksn Add a check before using an `appleframeworks` to make sure it was found. This fixes an exception in meson while encountering an optional dep for the target. --- mesonbuild/backend/xcodebackend.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index fc490c1b5d35..31fd272b3f0b 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -526,7 +526,7 @@ def generate_native_frameworks_map(self) -> None: self.native_frameworks_fileref = {} for t in self.build_targets.values(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks': + if dep.name == 'appleframeworks' and dep.found(): for f in dep.frameworks: self.native_frameworks[f] = self.gen_id() self.native_frameworks_fileref[f] = self.gen_id() @@ -691,7 +691,7 @@ def generate_pbx_aggregate_target(self, objects_dict: PbxDict) -> None: def generate_pbx_build_file(self, objects_dict: PbxDict) -> None: for tname, t in self.build_targets.items(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks': + if dep.name == 'appleframeworks' and dep.found(): for f in dep.frameworks: fw_dict = PbxDict() fwkey = self.native_frameworks[f] @@ -848,7 +848,7 @@ def generate_pbx_container_item_proxy(self, objects_dict: PbxDict) -> None: def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: for tname, t in self.build_targets.items(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks': + if dep.name == 'appleframeworks' and dep.found(): for f in dep.frameworks: fw_dict = PbxDict() framework_fileref = self.native_frameworks_fileref[f] @@ -1023,7 +1023,7 @@ def generate_pbx_frameworks_buildphase(self, objects_dict: PbxDict) -> None: file_list = PbxArray() bt_dict.add_item('files', file_list) for dep in t.get_external_deps(): - if dep.name == 'appleframeworks': + if dep.name == 'appleframeworks' and dep.found(): for f in dep.frameworks: file_list.add_item(self.native_frameworks[f], f'{f}.framework in Frameworks') bt_dict.add_item('runOnlyForDeploymentPostprocessing', 0) @@ -1071,7 +1071,7 @@ def generate_pbx_group(self, objects_dict: PbxDict) -> None: for t in self.build_targets.values(): for dep in t.get_external_deps(): - if dep.name == 'appleframeworks': + if dep.name == 'appleframeworks' and dep.found(): for f in dep.frameworks: frameworks_children.add_item(self.native_frameworks_fileref[f], f) From 4c7226e3632704e38606839cc962b27d44cd32e3 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Sat, 12 Oct 2024 11:15:45 +0100 Subject: [PATCH 034/624] coverage.py: Guard use of `--html-nested` behind version check. `--html-nested` was added to gcovr in version 6.0 so passing it to versions before this will result in gcovr complaining that it doesn't recognise the argument. Added a version check to ensure that we pass a recognised argument for versions before 6.0 Fixes #13781 --- mesonbuild/scripts/coverage.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index f01946944afe..a4dfebfb9d9b 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -159,9 +159,14 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build htmloutdir = os.path.join(log_dir, 'coveragereport') if not os.path.isdir(htmloutdir): os.mkdir(htmloutdir) + # Use `--html-details` if gcovr version < 6.0, otherwise + # use `--html-nested`. + html_arg = '--html-details' + if mesonlib.version_compare(gcovr_version, '>=6.0'): + html_arg = '--html-nested' subprocess.check_call(gcovr_base_cmd + gcovr_config + ['--html', - '--html-nested', + html_arg, '--print-summary', '-o', os.path.join(htmloutdir, 'index.html'), ] + gcov_exe_args) From a6318fb861d315ccbf46570d839101f3474e0d5f Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Mon, 4 Nov 2024 08:43:04 +0100 Subject: [PATCH 035/624] Add archiver to reference tables Meson allows to set the archiver via the `AR` environment variable and a native / cross file. Only the latter approach seems to be documented. This patch adds the `AR` environment variable to the reference tables next to the compilers. --- docs/markdown/Reference-tables.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index a5d0d5cdcc18..0cf8bcfd73a2 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -386,6 +386,7 @@ machine](#Environment-variables-per-machine) section for details. | C# | CSC | CSC | The linker is the compiler | | Cython | CYTHON | | | | nasm | NASM | | Uses the C linker | +| archiver | | AR | | *The old environment variables are still supported, but are deprecated and will be removed in a future version of Meson. From a2b0e665bb8dbd1353311faf8af5b55113d21d96 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 28 Oct 2024 14:23:24 -0700 Subject: [PATCH 036/624] run_single_test.py: Fix for symlink changes --- run_single_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/run_single_test.py b/run_single_test.py index 6cc75ac17b4e..b7c294269013 100755 --- a/run_single_test.py +++ b/run_single_test.py @@ -13,10 +13,11 @@ import typing as T from mesonbuild import mlog +from mesonbuild.mesonlib import is_windows from run_tests import handle_meson_skip_test from run_project_tests import TestDef, load_test_json, run_test, BuildStep from run_project_tests import setup_commands, detect_system_compiler, detect_tools -from run_project_tests import setup_symlinks, clear_transitive_files +from run_project_tests import scan_test_data_symlinks, setup_symlinks, clear_transitive_files if T.TYPE_CHECKING: from run_project_tests import CompilerArgumentType @@ -45,6 +46,8 @@ def main() -> None: parser.add_argument('--quick', action='store_true', help='Skip some compiler and tool checking') args = T.cast('ArgumentType', parser.parse_args()) + if not is_windows(): + scan_test_data_symlinks() setup_symlinks() setup_commands(args.backend) if not args.quick: From 5d95342c214ecb3341ad1a4e0d135076b11bbd08 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 28 Oct 2024 13:56:30 -0700 Subject: [PATCH 037/624] cargo/interpreter: delete dead code --- mesonbuild/cargo/interpreter.py | 43 --------------------------------- 1 file changed, 43 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 2f67a781ddb7..99dd3f0666e4 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -11,9 +11,7 @@ from __future__ import annotations import dataclasses -import glob import importlib -import itertools import json import os import shutil @@ -372,47 +370,6 @@ def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = ) -def _load_manifests(subdir: str) -> T.Dict[str, Manifest]: - filename = os.path.join(subdir, 'Cargo.toml') - raw = load_toml(filename) - - manifests: T.Dict[str, Manifest] = {} - - raw_manifest: T.Union[manifest.Manifest, manifest.VirtualManifest] - if 'package' in raw: - raw_manifest = T.cast('manifest.Manifest', raw) - manifest_ = _convert_manifest(raw_manifest, subdir) - manifests[manifest_.package.name] = manifest_ - else: - raw_manifest = T.cast('manifest.VirtualManifest', raw) - - if 'workspace' in raw_manifest: - # XXX: need to verify that python glob and cargo globbing are the - # same and probably write a glob implementation. Blarg - - # We need to chdir here to make the glob work correctly - pwd = os.getcwd() - os.chdir(subdir) - members: T.Iterable[str] - try: - members = itertools.chain.from_iterable( - glob.glob(m) for m in raw_manifest['workspace']['members']) - finally: - os.chdir(pwd) - if 'exclude' in raw_manifest['workspace']: - members = (x for x in members if x not in raw_manifest['workspace']['exclude']) - - for m in members: - filename = os.path.join(subdir, m, 'Cargo.toml') - raw = load_toml(filename) - - raw_manifest = T.cast('manifest.Manifest', raw) - man = _convert_manifest(raw_manifest, subdir, m) - manifests[man.package.name] = man - - return manifests - - def _version_to_api(version: str) -> str: # x.y.z -> x # 0.x.y -> 0.x From 1751c300b8c4b26c1b60b03787ee33e14eb04d45 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 28 Oct 2024 13:19:08 -0700 Subject: [PATCH 038/624] cargo/manifest: package section is required --- mesonbuild/cargo/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 95b0d4bb8cb2..d95df7f4fe3c 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -195,7 +195,7 @@ class Workspace(TypedDict): Manifest = TypedDict( 'Manifest', { - 'package': Package, + 'package': Required[Package], 'badges': T.Dict[str, Badge], 'dependencies': T.Dict[str, DependencyV], 'dev-dependencies': T.Dict[str, DependencyV], From 909b04d5b1072f0d6421dbd2d420fa879ab80b36 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 28 Oct 2024 13:40:02 -0700 Subject: [PATCH 039/624] cargo/interpreter: use alternate initializers for all manifest types This allows us to move a bit of the validation and fixing into these classes --- mesonbuild/cargo/interpreter.py | 45 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 99dd3f0666e4..6c09212247a3 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -28,6 +28,8 @@ if T.TYPE_CHECKING: from types import ModuleType + from typing_extensions import Self + from . import manifest from .. import mparser from ..environment import Environment @@ -154,6 +156,12 @@ class Package: 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()}) + return cls(**pkg) + @dataclasses.dataclass class SystemDependency: @@ -263,6 +271,10 @@ class BuildTarget: required_features: T.List[str] = dataclasses.field(default_factory=list) plugin: bool = False + @classmethod + def from_raw(cls, raw: manifest.BuildTarget) -> Self: + build = _fixup_raw_mappings(raw) + return cls(**build) @dataclasses.dataclass class Library(BuildTarget): @@ -276,6 +288,17 @@ class Library(BuildTarget): 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: + fixed = _fixup_raw_mappings(raw) + + # 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 + + return cls(**fixed) + @dataclasses.dataclass class Binary(BuildTarget): @@ -343,26 +366,16 @@ def __post_init__(self) -> None: def _convert_manifest(raw_manifest: manifest.Manifest, subdir: str, path: str = '') -> Manifest: - # This cast is a bit of a hack to deal with proc-macro - lib = _fixup_raw_mappings(raw_manifest.get('lib', {})) - - # We need to set the name field if it's not set manually, - # including if other fields are set in the lib section - lib.setdefault('name', raw_manifest['package']['name']) - - pkg = T.cast('manifest.FixedPackage', - {fixup_meson_varname(k): v for k, v in raw_manifest['package'].items()}) - return Manifest( - Package(**pkg), + 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(**lib), - [Binary(**_fixup_raw_mappings(b)) for b in raw_manifest.get('bin', {})], - [Test(**_fixup_raw_mappings(b)) for b in raw_manifest.get('test', {})], - [Benchmark(**_fixup_raw_mappings(b)) for b in raw_manifest.get('bench', {})], - [Example(**_fixup_raw_mappings(b)) for b in raw_manifest.get('example', {})], + 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()}, From 6eb1a2726e15d4301b000b177e72e75263bdd499 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Sun, 27 Oct 2024 10:17:54 -0700 Subject: [PATCH 040/624] cargo: Warn when encountering unknown keys Cargo sometimes adds new keys and Meson needs to gracefully handle those. Currently, an unknown key will trigger an uncaught Python exception, which is pretty much the worse case. With this change Meson will instead issue a warning much like the one for unknown cpu architectures. See #13826 for the motivation for this change --- mesonbuild/cargo/interpreter.py | 53 +++++++++++++++++-- .../subprojects/bar-0.1-rs/Cargo.toml | 1 + test cases/rust/22 cargo subproject/test.json | 8 +++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 test cases/rust/22 cargo subproject/test.json diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 6c09212247a3..0528c4c9008b 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -28,13 +28,22 @@ if T.TYPE_CHECKING: from types import ModuleType - from typing_extensions import Self + from typing_extensions import Protocol, Self from . import manifest from .. import mparser from ..environment import Environment from ..interpreterbase import SubProject + # 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? @@ -54,6 +63,14 @@ 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 @@ -118,6 +135,30 @@ def _fixup_raw_mappings(d: T.Union[manifest.BuildTarget, manifest.LibTarget, man 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: @@ -160,6 +201,7 @@ def __post_init__(self) -> None: 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 @@ -240,7 +282,8 @@ 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)) - return cls(name, **_fixup_raw_mappings(raw)) + fixed = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Dependency entry {name}') + return cls(name, **fixed) @dataclasses.dataclass @@ -273,7 +316,8 @@ class BuildTarget: @classmethod def from_raw(cls, raw: manifest.BuildTarget) -> Self: - build = _fixup_raw_mappings(raw) + name = raw.get('name', '') + build = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Binary entry {name}') return cls(**build) @dataclasses.dataclass @@ -289,13 +333,14 @@ class Library(BuildTarget): doc_scrape_examples: bool = True @classmethod - def from_raw(cls, raw: manifest.LibTarget, fallback_name: str) -> Self: + def from_raw(cls, raw: manifest.LibTarget, fallback_name: str) -> Self: # type: ignore[override] fixed = _fixup_raw_mappings(raw) # 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) diff --git a/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml index c0f7ffd8dd1e..d8b4eb26c172 100644 --- a/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml +++ b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "bar" version = "0.1" +flooob = "lolz" # This dependency does not exist, it is required by default but this subproject # is called with default-features=false. diff --git a/test cases/rust/22 cargo subproject/test.json b/test cases/rust/22 cargo subproject/test.json new file mode 100644 index 000000000000..c973b1c91f5c --- /dev/null +++ b/test cases/rust/22 cargo subproject/test.json @@ -0,0 +1,8 @@ +{ + "stdout": [ + { + "line": "foo-0-rs| WARNING: Package entry bar has unexpected keys \"flooob\". 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." + } + ] +} + From 41dbfd93e455367993808a5975bc101231ccaa52 Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Tue, 5 Nov 2024 16:26:51 -0500 Subject: [PATCH 041/624] Document hotdoc.generate_doc(depends:) kwarg Document the results of commit bcc127b3f, which added 'depends' and deprecated using 'dependencies' for custom targets. Fixes: #11479 --- docs/markdown/Hotdoc-module.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Hotdoc-module.md b/docs/markdown/Hotdoc-module.md index d33dd3e7c304..cf972ca35186 100644 --- a/docs/markdown/Hotdoc-module.md +++ b/docs/markdown/Hotdoc-module.md @@ -33,12 +33,19 @@ Generates documentation using [hotdoc] and installs it into `$prefix/share/doc/h * `sitemap` ([[@str]] or [[@file]]) (**required**): The hotdoc sitemap file * `index` ([[@str]] or [[@file]]) (**required**): Location of the index file -* `dependencies`([[@build_tgt]]): Targets on which the documentation generation depends on. +* `dependencies`([[@build_tgt]]): Build targets to use when generating documentation. +* `depends`([[@custom_tgt]]): Custom targets on which this documentation target depends. * `subprojects`: A list of `HotdocTarget` that are used as subprojects for hotdoc to generate the documentation. * ... Any argument of `hotdoc` can be used replacing dashes (`-`) with underscores (`_`). For a full list of available parameters, just have a look at `hotdoc help`. +*Changed in 0.64.1:* `depends:` added. +Previously, all types of targets were accepted by `dependencies:`. +This is now deprecated. +Use `dependencies:` only with build targets, to pass their configuration to hotdoc. +Use `depends:` to set up dependency relationships on custom targets. + **Returns:** `HotdocTarget`: A [[custom_target]] with the @@ -72,4 +79,4 @@ hotdoc.generate_doc('foobar', ) ``` -[hotdoc]: https://hotdoc.github.io/ \ No newline at end of file +[hotdoc]: https://hotdoc.github.io/ From bfce1450ac68c1da7151b7ad1943f47bdd31f192 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 5 Nov 2024 15:03:11 -0500 Subject: [PATCH 042/624] vcs_tag: Add install kwargs Fixes: #4893 --- docs/markdown/snippets/vcs_tag.md | 4 ++++ docs/yaml/functions/vcs_tag.yaml | 31 +++++++++++++++++++++++++ mesonbuild/interpreter/interpreter.py | 15 ++++++++++++ test cases/common/66 vcstag/meson.build | 4 +++- test cases/common/66 vcstag/test.json | 8 +++++++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/vcs_tag.md create mode 100644 test cases/common/66 vcstag/test.json diff --git a/docs/markdown/snippets/vcs_tag.md b/docs/markdown/snippets/vcs_tag.md new file mode 100644 index 000000000000..c60996a2ea31 --- /dev/null +++ b/docs/markdown/snippets/vcs_tag.md @@ -0,0 +1,4 @@ +## Install vcs_tag() output + +[[vcs_tag]] now has `install`, `install_dir`, `install_tag` and `install_mode` +keyword arguments to install the generated file. diff --git a/docs/yaml/functions/vcs_tag.yaml b/docs/yaml/functions/vcs_tag.yaml index b4aad12c60de..3a3568429edc 100644 --- a/docs/yaml/functions/vcs_tag.yaml +++ b/docs/yaml/functions/vcs_tag.yaml @@ -55,3 +55,34 @@ kwargs: type: str default: "'@VCS_TAG@'" description: String in the input file to substitute with the commit information. + + install: + type: bool + default: false + since: 1.7.0 + description: | + When true, this generated file is installed during + the install step, and `install_dir` must be set and not empty. + + install_dir: + type: str + since: 1.7.0 + description: | + The subdirectory to install the generated file to (e.g. `share/myproject`). + + install_mode: + type: list[str | int] + since: 1.7.0 + description: | + Specify the file mode in symbolic format + and optionally the owner/uid and group/gid for the installed files. + + See the `install_mode` kwarg of [[install_data]] for more information. + + install_tag: + type: str + since: 1.7.0 + description: | + A string used by the `meson install --tags` command + to install only a subset of the files. By default the file has no install + tag which means it is not being installed when `--tags` argument is specified. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 7bb2337425c5..99530a8c89c5 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1954,6 +1954,10 @@ def func_build_target(self, node: mparser.BaseNode, ), KwargInfo('fallback', (str, NoneType)), KwargInfo('replace_string', str, default='@VCS_TAG@'), + INSTALL_KW.evolve(since='1.7.0'), + INSTALL_DIR_KW.evolve(since='1.7.0'), + INSTALL_TAG_KW.evolve(since='1.7.0'), + INSTALL_MODE_KW.evolve(since='1.7.0'), ) def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: 'kwtypes.VcsTag') -> build.CustomTarget: if kwargs['fallback'] is None: @@ -1994,6 +1998,13 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: replace_string, regex_selector] + vcs_cmd + install = kwargs['install'] + install_mode = self._warn_kwarg_install_mode_sticky(kwargs['install_mode']) + install_dir = [] if kwargs['install_dir'] is None else [kwargs['install_dir']] + install_tag = [] if kwargs['install_tag'] is None else [kwargs['install_tag']] + if install and not install_dir: + raise InvalidArguments('vcs_tag: "install_dir" keyword argument must be set when "install" is true.') + tg = build.CustomTarget( kwargs['output'][0], self.subdir, @@ -2004,6 +2015,10 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: kwargs['output'], build_by_default=True, build_always_stale=True, + install=install, + install_dir=install_dir, + install_mode=install_mode, + install_tag=install_tag, ) self.add_target(tg.name, tg) return tg diff --git a/test cases/common/66 vcstag/meson.build b/test cases/common/66 vcstag/meson.build index 38fa590385cf..53f90d8571f9 100644 --- a/test cases/common/66 vcstag/meson.build +++ b/test cases/common/66 vcstag/meson.build @@ -32,7 +32,9 @@ tagprog = executable('tagprog', 'tagprog.c', version_src) version_src_executable = vcs_tag(input : 'vcstag.c.in', output : 'vcstag-executable.c', -command : [tagprog]) +command : [tagprog], +install: true, +install_dir: get_option('includedir')) executable('tagprog-custom', 'tagprog.c', version_src_custom) executable('tagprog-fallback', 'tagprog.c', version_src_fallback) diff --git a/test cases/common/66 vcstag/test.json b/test cases/common/66 vcstag/test.json new file mode 100644 index 000000000000..4c9e3a197215 --- /dev/null +++ b/test cases/common/66 vcstag/test.json @@ -0,0 +1,8 @@ +{ + "installed": [ + { + "type": "file", + "file": "usr/include/vcstag-executable.c" + } + ] +} From f0851c9e4b1760c552f7921e6b6a379b006ba014 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 30 Oct 2024 00:44:02 -0500 Subject: [PATCH 043/624] Check for uppercase 'head' when updating subprojects --- docs/markdown/Wrap-dependency-system-manual.md | 4 ++-- mesonbuild/msubprojects.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index c1652c1c3b74..73358e7cfc2f 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -66,7 +66,7 @@ An example wrap-git will look like this: ```ini [wrap-git] url = https://github.com/libfoobar/libfoobar.git -revision = head +revision = HEAD depth = 1 ``` @@ -124,7 +124,7 @@ case, the directory will be copied into `subprojects/` before applying patches. - `url` - name of the wrap-git repository to clone. Required. - `revision` - name of the revision to checkout. Must be either: a valid value (such as a git tag) for the VCS's `checkout` command, or - (for git) `head` to track upstream's default branch. Required. + (for git) `HEAD` to track upstream's default branch. Required. ### Specific to wrap-git - `depth` - shallowly clone the repository to X number of commits. This saves bandwidth and disk diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index c15415485217..c74283c29c0f 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -324,7 +324,8 @@ def update_git(self) -> bool: self.log(' -> Not a git repository.') self.log('Pass --reset option to delete directory and redownload.') return False - revision = self.wrap.values.get('revision') + revision_val = self.wrap.values.get('revision') + revision = revision_val if revision_val.upper() != 'HEAD' else 'HEAD' url = self.wrap.values.get('url') push_url = self.wrap.values.get('push-url') if not revision or not url: From b3f9b9dc06fcd6c0a3070380b42e0ab5fd6c1226 Mon Sep 17 00:00:00 2001 From: Noah Gitsham Date: Mon, 11 Nov 2024 16:55:46 +0000 Subject: [PATCH 044/624] Add Linux PAM to list of users They recently made the switch from Autotools to Meson --- docs/markdown/Users.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 0b1de42f4daa..71a095604302 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -109,6 +109,7 @@ format files - [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 From 929df93ba80be347bea5df850ebce280aed64be7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Nov 2024 10:45:12 -0700 Subject: [PATCH 045/624] interpreter: remove current_lineno This was only used in a couple of places, and requires extra tracking to ensure it is correct, while we already have `current_node.lineno`, which is always accurate and up to date. I have also fixed a potential strict-null issue by using a sentinel node for `current_node` --- mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/interpreterbase/interpreterbase.py | 3 +-- mesonbuild/modules/__init__.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 99530a8c89c5..2c277ea13de6 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2701,11 +2701,11 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], ofile_rpath = os.path.join(self.subdir, output) if ofile_rpath in self.configure_file_outputs: mesonbuildfile = os.path.join(self.subdir, 'meson.build') - current_call = f"{mesonbuildfile}:{self.current_lineno}" + current_call = f"{mesonbuildfile}:{self.current_node.lineno}" first_call = "{}:{}".format(mesonbuildfile, self.configure_file_outputs[ofile_rpath]) 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_lineno + self.configure_file_outputs[ofile_rpath] = self.current_node.lineno (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output)) ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 525d5d6c54a0..5f9df4cb5bcc 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -82,7 +82,7 @@ def __init__(self, source_root: str, subdir: str, subproject: 'SubProject'): self.current_lineno = -1 # Current node set during a function call. This can be used as location # when printing a warning message during a method call. - self.current_node: mparser.BaseNode = None + self.current_node = mparser.BaseNode(-1, -1, 'sentinel') # This is set to `version_string` when this statement is evaluated: # meson.version().compare_version(version_string) # If it was part of a if-clause, it is used to temporally override the @@ -183,7 +183,6 @@ def evaluate_codeblock(self, node: mparser.CodeBlockNode, start: int = 0, end: T while i < len(statements): cur = statements[i] try: - self.current_lineno = cur.lineno self.evaluate_statement(cur) except Exception as e: if getattr(e, 'lineno', None) is None: diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 86dc8762e65a..f9374cc1d48c 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -38,7 +38,7 @@ def __init__(self, interpreter: 'Interpreter') -> None: self.subproject = interpreter.subproject self.subdir = interpreter.subdir self.root_subdir = interpreter.root_subdir - self.current_lineno = interpreter.current_lineno + 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 From e9fe3cc9110667741aef582f5fc38931ccb2359a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 26 Jan 2024 13:39:17 -0800 Subject: [PATCH 046/624] interperter: Remove unused Interpreter.subproject_directory_name --- mesonbuild/interpreter/interpreter.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2c277ea13de6..f7336e360c92 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -281,9 +281,6 @@ def __init__( self.backend = backend self.summary: T.Dict[str, 'Summary'] = {} self.modules: T.Dict[str, NewExtensionModule] = {} - # Subproject directory is usually the name of the subproject, but can - # be different for dependencies provided by wrap files. - self.subproject_directory_name = subdir.split(os.path.sep)[-1] self.subproject_dir = subproject_dir self.relaxations = relaxations or set() if ast is None: From 396f20fd913ba30523ee833abcf0cdfae2dfd30d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 26 Jan 2024 13:59:30 -0800 Subject: [PATCH 047/624] interpreter: only calculate build_filename if we're actually going to use it --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f7336e360c92..418042782103 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -313,8 +313,8 @@ def __init__( # exist we don't want to add a dependency on it, it's autogenerated # from the actual build files, and is just for reference. self.build_def_files: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() - build_filename = os.path.join(self.subdir, environment.build_filename) if not is_translated: + build_filename = os.path.join(self.subdir, environment.build_filename) self.build_def_files.add(build_filename) self.parse_project() self._redetect_machines() From 0095908a9d4e6852998682ffa991092c06b867b4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 29 Jan 2024 10:30:18 -0800 Subject: [PATCH 048/624] interpreter: remove Interpreter.generators Which is built, but never used --- mesonbuild/interpreter/interpreter.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 418042782103..d7523e7ea36f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -289,7 +289,6 @@ def __init__( self.ast = ast self.sanity_check_ast() self.builtin.update({'meson': MesonMain(self.build, self)}) - self.generators: T.List[build.Generator] = [] self.processed_buildfiles: T.Set[str] = set() self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen @@ -2215,9 +2214,7 @@ def func_generator(self, node: mparser.FunctionNode, if '@OUTPUT@' in o: raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.') - gen = build.Generator(args[0], **kwargs) - self.generators.append(gen) - return gen + return build.Generator(args[0], **kwargs) @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) @typed_kwargs('benchmark', *TEST_KWS) From 67ea6d4e7915d4189460cc00c4eef43aca4adade Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 29 Jan 2024 11:39:47 -0800 Subject: [PATCH 049/624] interpreter/dependencyfallbacks: Add copyright header --- mesonbuild/interpreter/dependencyfallbacks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index fd8a025ea220..f7a1b99fe783 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -1,3 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2021-2024 The Meson Developers +# Copyright © 2021-2024 Intel Corporation + from __future__ import annotations from .interpreterobjects import extract_required_kwarg From f1a09aabf129d95bca6f2493e6a6899e17fa4d33 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 29 Jan 2024 13:13:56 -0800 Subject: [PATCH 050/624] build: store Build.modules as a set Since it's only used to check membership, a set makes more sense. Also add some documentation to the attribute --- mesonbuild/build.py | 6 +++++- mesonbuild/interpreter/interpreter.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 460ed549be92..0d9374d40170 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -275,7 +275,11 @@ def __init__(self, environment: environment.Environment): self.dependency_overrides: PerMachine[T.Dict[T.Tuple, DependencyOverride]] = PerMachineDefaultable.default( environment.is_cross_build(), {}, {}) self.devenv: T.List[EnvironmentVariables] = [] - self.modules: T.List[str] = [] + self.modules: T.Set[str] = set() + """Used to track which modules are enabled in all subprojects. + + Needed for tracking whether a modules options needs to be exposed to the user. + """ def get_build_targets(self): build_targets = OrderedDict() diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d7523e7ea36f..dd73522a0acc 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -632,8 +632,8 @@ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str], ext_module = NotFoundExtensionModule(real_modname) else: ext_module = module.initialize(self) - assert isinstance(ext_module, (ExtensionModule, NewExtensionModule)), 'for mypy' - self.build.modules.append(real_modname) + assert isinstance(ext_module, (ExtensionModule, NewExtensionModule)) + self.build.modules.add(real_modname) if ext_module.INFO.added: FeatureNew.single_use(f'module {ext_module.INFO.name}', ext_module.INFO.added, self.subproject, location=node) if ext_module.INFO.deprecated: From cad608108b54c8996b005a39ab9c380cc19c458d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 29 Jan 2024 14:36:48 -0800 Subject: [PATCH 051/624] interpreter: don't use `in dict.keys()` use `in dict` The former performs a linear search, which is slower than the set based search. --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index dd73522a0acc..04fa67a9536f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1127,7 +1127,7 @@ def set_backend(self) -> None: return from ..backend import backends - if OptionKey('genvslite') in self.user_defined_options.cmd_line_options.keys(): + if OptionKey('genvslite') in self.user_defined_options.cmd_line_options: # Use of the '--genvslite vsxxxx' option ultimately overrides any '--backend xxx' # option the user may specify. backend_name = self.coredata.get_option(OptionKey('genvslite')) From 5d0e0737218d3a551f32cfb629993ccb8bb7d855 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 2 Feb 2024 09:54:52 -0800 Subject: [PATCH 052/624] interpreter: remove is_translated It's not actually useful, we can just add the root meson.build file to the depfiles when we translate the meson.build into ast. If there's a provided ast then we don't go down that path anyway. --- mesonbuild/interpreter/interpreter.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 04fa67a9536f..07e28c295351 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -269,7 +269,6 @@ def __init__( subproject_dir: str = 'subprojects', default_project_options: T.Optional[T.Dict[OptionKey, str]] = None, ast: T.Optional[mparser.CodeBlockNode] = None, - is_translated: bool = False, relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, user_defined_options: T.Optional[coredata.SharedCMDOptions] = None, ) -> None: @@ -283,6 +282,7 @@ def __init__( self.modules: T.Dict[str, NewExtensionModule] = {} self.subproject_dir = subproject_dir self.relaxations = relaxations or set() + self.build_def_files: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() if ast is None: self.load_root_meson_file() else: @@ -305,16 +305,6 @@ def __init__( self.build_holder_map() self.user_defined_options = user_defined_options self.compilers: PerMachine[T.Dict[str, 'compilers.Compiler']] = PerMachine({}, {}) - - # build_def_files needs to be defined before parse_project is called - # - # For non-meson subprojects, we'll be using the ast. Even if it does - # exist we don't want to add a dependency on it, it's autogenerated - # from the actual build files, and is just for reference. - self.build_def_files: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() - if not is_translated: - build_filename = os.path.join(self.subdir, environment.build_filename) - self.build_def_files.add(build_filename) self.parse_project() self._redetect_machines() @@ -339,6 +329,11 @@ def _redetect_machines(self) -> None: self.builtin['target_machine'] = \ OBJ.MachineHolder(self.build.environment.machines.target, self) + def load_root_meson_file(self) -> None: + build_filename = os.path.join(self.subdir, environment.build_filename) + self.build_def_files.add(build_filename) + super().load_root_meson_file() + def build_func_dict(self) -> None: self.funcs.update({'add_global_arguments': self.func_add_global_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments, @@ -968,8 +963,7 @@ def _do_subproject_meson(self, subp_name: str, subdir: str, new_build = self.build.copy() subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, - default_options, ast=ast, is_translated=(ast is not None), - relaxations=relaxations, + default_options, ast=ast, relaxations=relaxations, user_defined_options=self.user_defined_options) # Those lists are shared by all interpreters. That means that # even if the subproject fails, any modification that the subproject From 6f67b10e0823e864610e3adae028a56400c07043 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 May 2024 15:50:32 -0700 Subject: [PATCH 053/624] ast/introspection: Drop duplicate None check The AstInterpreter does this check as well, so don't do it twice. --- mesonbuild/ast/introspection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index b2eb1f8cd633..3e8d564e2fbf 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -55,7 +55,6 @@ def __init__(self, subproject: SubProject = SubProject(''), subproject_dir: str = 'subprojects', env: T.Optional[environment.Environment] = None): - visitors = visitors if visitors is not None else [] super().__init__(source_root, subdir, subproject, visitors=visitors) options = IntrospectionHelper(cross_file) From 1985ad19cb69f6db1d7e2e0c89fa6796782bfd43 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Fri, 8 Nov 2024 16:18:57 -0500 Subject: [PATCH 054/624] mdevenv: exec directly into the program to run We don't need an extra process in the process tree (specifically the `meson devenv` process itself). Aside for the redundancy, it's actively problematic if you abort a program in the devenv by using CTRL+C, as meson itself will then emit a traceback (KeyboardInterrupt) which is quite ugly. --- mesonbuild/mdevenv.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 064cf5ed6f89..8c6ce2031d45 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -9,7 +9,7 @@ from pathlib import Path from . import build, minstall -from .mesonlib import (EnvironmentVariables, MesonException, is_windows, setup_vsenv, +from .mesonlib import (EnvironmentVariables, MesonException, join_args, is_windows, setup_vsenv, get_wine_shortpath, MachineChoice, relpath) from .options import OptionKey from . import mlog @@ -226,10 +226,9 @@ def run(options: argparse.Namespace) -> int: args[0] = abs_path or args[0] try: - return subprocess.call(args, close_fds=False, - env=devenv, - cwd=workdir) - except subprocess.CalledProcessError as e: - return e.returncode + os.chdir(workdir) + os.execvpe(args[0], args, env=devenv) except FileNotFoundError: raise MesonException(f'Command not found: {args[0]}') + except OSError as e: + raise MesonException(f'Command `{join_args(args)}` failed to execute: {e}') From d76f739bffdfcf298ed83bd829045b9a12f43f8e Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sun, 17 Nov 2024 10:59:27 -0800 Subject: [PATCH 055/624] Update Users.md with netatalk --- docs/markdown/Users.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 71a095604302..918d1465c0b3 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -120,6 +120,7 @@ format files - [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 From 731dcdf92fdb0e4675f61e028eb83ff31e8893f5 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Sat, 16 Nov 2024 19:58:07 +0100 Subject: [PATCH 056/624] docs: fix description of `py.install_sources` arguments There is actually very little overlap between `install_sources` and `install_data` in arguments they accept: only 2/7 keywords for `install_data` apply to `install_sources`. Closes gh-12601 --- docs/markdown/Python-module.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md index cd2a641d9250..1ea5e64853b3 100644 --- a/docs/markdown/Python-module.md +++ b/docs/markdown/Python-module.md @@ -121,7 +121,7 @@ Additionally, the following diverge from [[shared_module]]'s default behavior: - `gnu_symbol_visibility`: if unset, it will default to `'hidden'` on versions of Python that support this (the python headers define `PyMODINIT_FUNC` has default visibility). - + Note that Cython support uses `extension_module`, see [the reference for Cython](Cython.md). *since 0.63.0* `extension_module` automatically adds a dependency to the library @@ -158,11 +158,8 @@ void py_installation.install_sources(list_of_files, ...) Install actual python sources (`.py`). -All positional and keyword arguments are the same as for -[[install_data]], with the addition of the following: - -*Since 0.60.0* `python.platlibdir` and `python.purelibdir` options can be used -to control the default installation path. See [Python module options](Builtin-options.md#python-module). +Source files to install are given as positional argument, in the same way as for +[[install_data]]. Supported keyword arguments are: - `pure`: On some platforms, architecture independent files are expected to be placed in a separate directory. However, if the @@ -177,6 +174,12 @@ to control the default installation path. See [Python module options](Builtin-op - `install_tag` *(since 0.60.0)*: A string used by `meson install --tags` command to install only a subset of the files. By default it has the tag `python-runtime`. +- `preserve_path`: if `true`, disable stripping child-directories from data + files when installing. Default is `false`. *(since 0.64.0)* + +*Since 0.60.0* `python.platlibdir` and `python.purelibdir` options can be used +to control the default installation path. See [Python module options](Builtin-options.md#python-module). + #### `get_install_dir()` ``` meson From 33376efc4c1daf53c5509ea5fa7611b18d10416a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 18 Nov 2024 09:18:40 -0800 Subject: [PATCH 057/624] tests: HDF5 no longer skips on MacOS The `-show` switch was added into the cmake-generated scripts in HDFGroup/hdf5@6fa09bd and backported to 1.14.5. --- test cases/frameworks/25 hdf5/test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/frameworks/25 hdf5/test.json b/test cases/frameworks/25 hdf5/test.json index 590d4bc20283..2448f57425d7 100644 --- a/test cases/frameworks/25 hdf5/test.json +++ b/test cases/frameworks/25 hdf5/test.json @@ -3,7 +3,7 @@ "options": { "method": [ { "val": "pkg-config", "expect_skip_on_jobname": ["linux-gentoo-gcc"] }, - { "val": "config-tool", "expect_skip_on_jobname": ["macos"] } + { "val": "config-tool" } ] } }, From fb2c3b348cd468db2a520c33d3ac4c6d07ea0407 Mon Sep 17 00:00:00 2001 From: MihailJP Date: Sat, 9 Nov 2024 22:12:38 +0900 Subject: [PATCH 058/624] make external_project work again on MinGW On Python 3.12, self.prefix.relative_to(self.prefix.drive) no longer works and raises error like: ``` ValueError: 'C:/msys64/mingw64' is not in the subpath of 'C:' ``` --- mesonbuild/modules/external_project.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py index 9e283e026b36..57a1819a3e9c 100644 --- a/mesonbuild/modules/external_project.py +++ b/mesonbuild/modules/external_project.py @@ -19,7 +19,7 @@ from ..interpreter.type_checking import ENV_KW, DEPENDS_KW from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args from ..mesonlib import (EnvironmentException, MesonException, Popen_safe, MachineChoice, - get_variable_regex, do_replacement, join_args) + get_variable_regex, do_replacement, join_args, relpath) from ..options import OptionKey if T.TYPE_CHECKING: @@ -93,10 +93,10 @@ def __init__(self, # will install files into "c:/bar/c:/foo" which is an invalid path. # Work around that issue by removing the drive from prefix. if self.prefix.drive: - self.prefix = self.prefix.relative_to(self.prefix.drive) + self.prefix = Path(relpath(self.prefix, self.prefix.drive)) # self.prefix is an absolute path, so we cannot append it to another path. - self.rel_prefix = self.prefix.relative_to(self.prefix.root) + self.rel_prefix = Path(relpath(self.prefix, self.prefix.root)) self._configure(state) From 3c38e4720f7df0a9d0138363a9a995b31e848401 Mon Sep 17 00:00:00 2001 From: MihailJP Date: Sun, 10 Nov 2024 01:31:06 +0900 Subject: [PATCH 059/624] make the testsuite handle MSYS2's addition of cmd In some reason, recent version of MSYS2 is shipped with /usr/bin/cmd which makes the test fail --- unittests/windowstests.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/unittests/windowstests.py b/unittests/windowstests.py index 8448ab1649cc..c5b0ba08d306 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -54,20 +54,20 @@ def test_find_program(self): PATH to point to a directory with Python scripts. ''' testdir = os.path.join(self.platform_test_dir, '8 find program') - # Find `cmd` and `cmd.exe` - prog1 = ExternalProgram('cmd') - self.assertTrue(prog1.found(), msg='cmd not found') - prog2 = ExternalProgram('cmd.exe') - self.assertTrue(prog2.found(), msg='cmd.exe not found') + # Find `xcopy` and `xcopy.exe` + prog1 = ExternalProgram('xcopy') + self.assertTrue(prog1.found(), msg='xcopy not found') + prog2 = ExternalProgram('xcopy.exe') + self.assertTrue(prog2.found(), msg='xcopy.exe not found') self.assertPathEqual(prog1.get_path(), prog2.get_path()) - # Find cmd.exe with args without searching - prog = ExternalProgram('cmd', command=['cmd', '/C']) - self.assertTrue(prog.found(), msg='cmd not found with args') - self.assertPathEqual(prog.get_command()[0], 'cmd') - # Find cmd with an absolute path that's missing the extension - cmd_path = prog2.get_path()[:-4] - prog = ExternalProgram(cmd_path) - self.assertTrue(prog.found(), msg=f'{cmd_path!r} not found') + # Find xcopy.exe with args without searching + prog = ExternalProgram('xcopy', command=['xcopy', '/?']) + self.assertTrue(prog.found(), msg='xcopy not found with args') + self.assertPathEqual(prog.get_command()[0], 'xcopy') + # Find xcopy with an absolute path that's missing the extension + xcopy_path = prog2.get_path()[:-4] + prog = ExternalProgram(xcopy_path) + self.assertTrue(prog.found(), msg=f'{xcopy_path!r} not found') # Finding a script with no extension inside a directory works prog = ExternalProgram(os.path.join(testdir, 'test-script')) self.assertTrue(prog.found(), msg='test-script not found') From 93f59313e10090bfe6058fabbb235aa549f933c0 Mon Sep 17 00:00:00 2001 From: MihailJP Date: Sun, 10 Nov 2024 19:21:36 +0900 Subject: [PATCH 060/624] check if test 33 running under MinGW --- test cases/common/33 run program/check-mingw.py | 10 ++++++++++ test cases/common/33 run program/meson.build | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100755 test cases/common/33 run program/check-mingw.py diff --git a/test cases/common/33 run program/check-mingw.py b/test cases/common/33 run program/check-mingw.py new file mode 100755 index 000000000000..f10c28ba1a5c --- /dev/null +++ b/test cases/common/33 run program/check-mingw.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import os, sys, re + +if 'MSYSTEM' in os.environ and os.environ['MSYSTEM'] != '': + print(os.environ['MSYSTEM']) +else: + match = re.search(r'[\\/](mingw32|mingw64|clang32|clang64|clangarm64|ucrt64)[\\/]', sys.executable, flags=re.IGNORECASE) + if match: + print(match.group(1).upper()) diff --git a/test cases/common/33 run program/meson.build b/test cases/common/33 run program/meson.build index 2257d93c7fc1..1cd530523357 100644 --- a/test cases/common/33 run program/meson.build +++ b/test cases/common/33 run program/meson.build @@ -1,6 +1,9 @@ project('run command', version : run_command('get-version.py', check : true).stdout().strip(), meson_version: '>=0.1.0') -if build_machine.system() == 'windows' +check_mingw = run_command('check-mingw.py', check : true).stdout().strip() +is_mingw = not (check_mingw == '' or check_mingw == 'MSYS') + +if build_machine.system() == 'windows' and not is_mingw c = run_command('cmd', '/c', 'echo', 'hello', check: false) else c = run_command('echo', 'hello', check: false) @@ -45,7 +48,7 @@ endif # We should be able to have files() in argument f = files('meson.build') -if build_machine.system() == 'windows' +if build_machine.system() == 'windows' and not is_mingw c = run_command('cmd', '/c', 'echo', f, check: false) else c = run_command('echo', f, check: false) From 110e2de4fa76da9c2bb7fe7b2008ac859564dbd3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Nov 2024 12:16:19 +0100 Subject: [PATCH 061/624] rust: avoid warnings from rust.test Any argument from the base target is copied to the test target, but some keyword arguments for libraries are not available in executable. Remove them. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/rust.py | 3 +++ test cases/rust/9 unit tests/meson.build | 4 ++-- unittests/allplatformstests.py | 7 +++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 1368c4c1970f..60a58698c858 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -162,6 +162,9 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func 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'] 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'] diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build index b444271ae18c..e6274578146e 100644 --- a/test cases/rust/9 unit tests/meson.build +++ b/test cases/rust/9 unit tests/meson.build @@ -1,4 +1,4 @@ -project('rust unit tests', 'rust') +project('rust unit tests', 'rust', meson_version: '>=1.2.0') t = executable( 'rust_test', @@ -33,7 +33,7 @@ test( exe = executable('rust_exe', ['test2.rs', 'test.rs'], build_by_default : false) -rust = import('unstable-rust') +rust = import('rust') rust.test('rust_test_from_exe', exe, should_fail : true) lib = static_library('rust_static', ['test.rs'], build_by_default : false, rust_crate_type : 'lib') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index f4434bb9f359..b6a87af1fc7f 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4890,6 +4890,13 @@ def test_rust_clippy(self) -> None: self.assertTrue('error: use of a blacklisted/placeholder name `foo`' in cm.exception.stdout or 'error: use of a disallowed/placeholder name `foo`' in cm.exception.stdout) + @skip_if_not_language('rust') + def test_rust_test_warnings(self) -> None: + if self.backend is not Backend.ninja: + raise unittest.SkipTest('Rust is only supported with ninja currently') + testdir = os.path.join(self.rust_test_dir, '9 unit tests') + self.init(testdir, extra_args=['--fatal-meson-warnings']) + @skip_if_not_language('rust') def test_rust_rlib_linkage(self) -> None: if self.backend is not Backend.ninja: From 8400083418445bfe2d36e57e0988d3f2d208cad4 Mon Sep 17 00:00:00 2001 From: prajwal-ibm Date: Thu, 7 Nov 2024 04:06:40 -0600 Subject: [PATCH 062/624] Fix building on AIX when no project languages are used Closes #13878 --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cb3552d7f0c1..7b573e4e4d8a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2369,7 +2369,7 @@ def generate_dynamic_link_rules(self) -> None: options = self._rsp_options(compiler) self.add_rule(NinjaRule(rule, command, args, description, **options, extra=pool)) - if self.environment.machines[for_machine].is_aix(): + if self.environment.machines[for_machine].is_aix() and complist: rule = 'AIX_LINKER{}'.format(self.get_rule_suffix(for_machine)) description = 'Archiving AIX shared library' cmdlist = compiler.get_command_to_archive_shlib() From 1b15bd034324a0ba173d4f8109471e2a7e338a66 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 18:25:03 +0100 Subject: [PATCH 063/624] compilers: avoid one or more version_compare per target version_compare can take a few milliseconds. If you have a thousand object files or a multiple thereof, it adds up. Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/backend/ninjabackend.py | 5 +++-- mesonbuild/compilers/mixins/gnu.py | 9 ++++++--- mesonbuild/compilers/vala.py | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7b573e4e4d8a..e0721145bf32 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -623,6 +623,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) if ninja is None: raise MesonException('Could not detect Ninja v1.8.2 or newer') (self.ninja_command, self.ninja_version) = ninja + self.ninja_has_dyndeps = mesonlib.version_compare(self.ninja_version, '>=1.10.0') outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename) tempfilename = outfilename + '~' with open(tempfilename, 'w', encoding='utf-8') as outfile: @@ -1089,7 +1090,7 @@ def generate_target(self, target) -> None: self.add_build(elem) def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool: - if mesonlib.version_compare(self.ninja_version, '<1.10.0'): + if not self.ninja_has_dyndeps: return False if 'fortran' in target.compilers: return True @@ -2451,7 +2452,7 @@ def use_dyndeps_for_fortran(self) -> bool: '''Use the new Ninja feature for scanning dependencies during build, rather than up front. Remove this and all old scanning code once Ninja minimum version is bumped to 1.10.''' - return mesonlib.version_compare(self.ninja_version, '>=1.10.0') + return self.ninja_has_dyndeps def generate_fortran_dep_hack(self, crstr: str) -> None: if self.use_dyndeps_for_fortran(): diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 62f55543a0a7..21a57b44fef1 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -550,16 +550,19 @@ 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') def get_colorout_args(self, colortype: str) -> T.List[str]: - if mesonlib.version_compare(self.version, '>=4.9.0'): + if self._has_color_support: return gnu_color_args[colortype][:] return [] def get_warn_args(self, level: str) -> T.List[str]: # Mypy doesn't understand cooperative inheritance args = super().get_warn_args(level) - if mesonlib.version_compare(self.version, '<4.8.0') and '-Wpedantic' in args: + if not self._has_wpedantic_support and '-Wpedantic' in args: # -Wpedantic was added in 4.8.0 # https://gcc.gnu.org/gcc-4.8/changes.html args[args.index('-Wpedantic')] = '-pedantic' @@ -612,7 +615,7 @@ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[s def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]: if threads == 0: - if mesonlib.version_compare(self.version, '>= 10.0'): + if self._has_lto_auto_support: return ['-flto=auto'] # This matches clang's behavior of using the number of cpus return [f'-flto={multiprocessing.cpu_count()}'] diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index a1d57b38cb8e..35c7a682e013 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -31,6 +31,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic self.version = version self.base_options = {OptionKey('b_colorout')} self.force_link = False + self._has_color_support = version_compare(self.version, '>=0.37.1') def needs_static_linker(self) -> bool: return False # Because compiles into C. @@ -80,7 +81,7 @@ def get_werror_args(self) -> T.List[str]: return ['--fatal-warnings'] def get_colorout_args(self, colortype: str) -> T.List[str]: - if version_compare(self.version, '>=0.37.1'): + if self._has_color_support: return ['--color=' + colortype] return [] From 58d1efb5b25c0cbb79e00fa480494bf29296d40e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 14:22:20 +0100 Subject: [PATCH 064/624] ninjabackend: use an unordered set for NinjaBuildElement The deps and orderdeps are sorted on output, so there is no need to preserve their order. Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e0721145bf32..05d5320798fd 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -323,8 +323,8 @@ def __init__(self, all_outputs: T.Set[str], outfilenames, rulename, infilenames, self.infilenames = [infilenames] else: self.infilenames = infilenames - self.deps = OrderedSet() - self.orderdeps = OrderedSet() + self.deps = set() + self.orderdeps = set() self.elems = [] self.all_outputs = all_outputs self.output_errors = '' From 3ba0976394553e9ef9039bb1fd74aa2fe212facf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 15 Nov 2024 11:42:45 -0800 Subject: [PATCH 065/624] interpreter: put back assertion message that was dropped in error --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 07e28c295351..58385c58c5f7 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -627,7 +627,7 @@ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str], ext_module = NotFoundExtensionModule(real_modname) else: ext_module = module.initialize(self) - assert isinstance(ext_module, (ExtensionModule, NewExtensionModule)) + assert isinstance(ext_module, (ExtensionModule, NewExtensionModule)), 'for mypy' self.build.modules.add(real_modname) if ext_module.INFO.added: FeatureNew.single_use(f'module {ext_module.INFO.name}', ext_module.INFO.added, self.subproject, location=node) From 7b10f48d1cfbdf4c5d0f061d9da32e057ffdc460 Mon Sep 17 00:00:00 2001 From: Nick <0xb000@gmail.com> Date: Mon, 4 Nov 2024 20:43:33 +0200 Subject: [PATCH 066/624] De-duplicate BuildTarget.sources If the same source is provided by multiple dependencies it was added multiple times, as `added_sources` was only guarding against duplicates within the same source list. This was not a problem with ninja, but it triggers multiple sanity checks within xcode backend while attempting to create multiple ids for the same file. Rename `added_sources` to `seen_sources` as per reviewers request --- mesonbuild/build.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0d9374d40170..a00209ad45a8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -762,6 +762,8 @@ def __init__( self.pch: T.Dict[str, T.List[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. + self.seen_sources: T.Set[File] = set() self.generated: T.List['GeneratedTypes'] = [] self.extra_files: T.List[File] = [] self.d_features: DFeatures = { @@ -884,12 +886,11 @@ def process_sourcelist(self, sources: T.List['SourceOutputs']) -> None: (static as they are only regenerated if meson itself is regenerated) 3. Sources files generated by another target or a Generator (generated) """ - added_sources: T.Set[File] = set() # If the same source is defined multiple times, use it only once. for s in sources: if isinstance(s, File): - if s not in added_sources: + if s not in self.seen_sources: self.sources.append(s) - added_sources.add(s) + self.seen_sources.add(s) elif isinstance(s, (CustomTarget, CustomTargetIndex, GeneratedList)): self.generated.append(s) From 8a9c7b6330d07a4491d63f486b234d65767bb37c Mon Sep 17 00:00:00 2001 From: Noah Gitsham Date: Mon, 11 Nov 2024 21:55:11 +0000 Subject: [PATCH 067/624] Remove trailing full stops from entries in list of users Makes all entries consistent --- docs/markdown/Users.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 918d1465c0b3..6e113f1b59c8 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -51,7 +51,7 @@ topic](https://github.com/topics/meson). - [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. + - [Geary](https://wiki.gnome.org/Apps/Geary), an email application built around conversations, for the GNOME 3 desktop - [GIMP](https://gitlab.gnome.org/GNOME/gimp), an image manipulation program (master branch) - [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 @@ -97,10 +97,10 @@ topic](https://github.com/topics/meson). - [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. + - [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. + - [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) @@ -129,7 +129,7 @@ format files - [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. + - [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 @@ -140,7 +140,7 @@ format files - [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 - - [Paper Rock Scissors](https://github.com/michaelbrockus/paper_rock_scissors), a game with weapons themed at home paper rock scissors style. + - [Paper Rock Scissors](https://github.com/michaelbrockus/paper_rock_scissors), a game with weapons themed at home paper rock scissors style - [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 @@ -151,7 +151,7 @@ format files - [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. + - [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://remove-to-waste.info), 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) @@ -171,7 +171,7 @@ format files - [ThorVG](https://www.thorvg.org/), vector-based scenes and animations library - [Tilix](https://github.com/gnunn1/tilix), a tiling terminal emulator for Linux using GTK+ 3 - [Tizonia](https://github.com/tizonia/tizonia-openmax-il), a command-line cloud music player for Linux with support for Spotify, Google Play Music, YouTube, SoundCloud, TuneIn, Plex servers and Chromecast devices - - [Fossil Logic](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. + - [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 From 69af44d50ef6fdceefff08446d915cc6b1fe2d50 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Wed, 20 Nov 2024 10:00:51 +0100 Subject: [PATCH 068/624] run_single_test.py: skip setup_symlinks() call on Windows --- run_single_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_single_test.py b/run_single_test.py index b7c294269013..23b175641b50 100755 --- a/run_single_test.py +++ b/run_single_test.py @@ -48,7 +48,7 @@ def main() -> None: if not is_windows(): scan_test_data_symlinks() - setup_symlinks() + setup_symlinks() setup_commands(args.backend) if not args.quick: detect_system_compiler(args) From 9f3f88feed74518f406839f4935b79a45d364540 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 16:19:49 +0100 Subject: [PATCH 069/624] rust: fix computation of library directory Using a rustup-based toolchain fails the "rust/2 sharedlib" test for me: ./prog: error while loading shared libraries: libstd-211931512faabf29.so: cannot open shared object file: No such file or directory This happens because recent rustup places the standard library under SYSROOT/lib/rustlib/TARGET/lib. Retrieve the right directory using "--print target-libdir". This also provides a more accurate version for rustc installed in /usr. Before: $ echo $(/usr/bin/rustc --print sysroot)/lib /usr/lib After: $ /usr/bin/rustc --print target-libdir /usr/lib/rustlib/x86_64-unknown-linux-gnu/lib While at it, cache the value to avoid repeated process invocation. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 2 +- mesonbuild/compilers/rust.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 05d5320798fd..5716ea29e351 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2125,7 +2125,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): # ... but then add rustc's sysroot to account for rustup # installations for rpath_arg in rpath_args: - args += ['-C', 'link-arg=' + rpath_arg + ':' + os.path.join(rustc.get_sysroot(), 'lib')] + args += ['-C', 'link-arg=' + rpath_arg + ':' + rustc.get_target_libdir()] proc_macro_dylib_path = None if getattr(target, 'rust_crate_type', '') == 'proc-macro': diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index f09911db642c..02ac593842ad 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -142,11 +142,18 @@ def _native_static_libs(self, work_dir: str, source_name: str) -> None: def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: return ['--dep-info', outfile] + @functools.lru_cache(maxsize=None) def get_sysroot(self) -> str: cmd = self.get_exelist(ccache=False) + ['--print', 'sysroot'] p, stdo, stde = Popen_safe_logged(cmd) return stdo.split('\n', maxsplit=1)[0] + @functools.lru_cache(maxsize=None) + def get_target_libdir(self) -> str: + cmd = self.get_exelist(ccache=False) + ['--print', 'target-libdir'] + p, stdo, stde = Popen_safe_logged(cmd) + return stdo.split('\n', maxsplit=1)[0] + @functools.lru_cache(maxsize=None) def get_crt_static(self) -> bool: cmd = self.get_exelist(ccache=False) + ['--print', 'cfg'] From 467da051c859ba3112803b035e317bddadd756ef Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 25 Nov 2024 15:08:28 +0200 Subject: [PATCH 070/624] Add VS preview version. --- mesonbuild/compilers/mixins/visualstudio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index b4677f4172ba..30127eca479c 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -338,6 +338,8 @@ def _calculate_toolset_version(self, version: int) -> T.Optional[str]: return '14.2' # (Visual Studio 2019) elif version < 1940: return '14.3' # (Visual Studio 2022) + elif version < 1950: + return '14.4' # (Visual Studio current preview version, might not be final) mlog.warning(f'Could not find toolset for version {self.version!r}') return None From fd309fff248bb697498befa89c3b81049b8b2c0c Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 20 Nov 2024 08:00:20 +0000 Subject: [PATCH 071/624] environment: Never require an exe_wrapper for native builds It is possible to run a container or chroot with one ABI on a CPU and kernel that would normally have a different ABI, most commonly by running a 32-bit container on a 64-bit CPU and kernel. When we do a native build in such an environment, the build and host architectures are both equal to the architecture of the container, and it is safe to assume that we can run executables from that architecture, because if we could not, we wouldn't be running Python successfully. Until now, we have been handling this by adding explicit special cases in `machine_info_can_run()` for each known-good combination of the detected CPU and the host architecture: every x86_64 can run x86 binaries, and every mips64 is assumed to be able to run 32-bit mips binaries. However, the equivalent would not be true on ARM systems: *most* aarch64 CPUs can run arm binaries, but not all (according to Wikipedia, ARM Cortex-A34 is an example of a purely 64-bit CPU that cannot execute 32-bit instructions). Instead, assume that if we are doing a native build (not a cross build), by definition we can run build-architecture executables, and since the host architecture is equal to the build architecture during a native build, this implies that we can run host-architecture executables too. This makes the behaviour of `need_exe_wrapper()` consistent with `meson.can_run_host_binaries()`, which in turn avoids `Compiler.run()` failing with error message "Can not run test applications in this cross environment" during native builds even though `meson.can_run_host_binaries()` has previously succeeded. Resolves: https://github.com/mesonbuild/meson/issues/13841 Signed-off-by: Simon McVittie --- mesonbuild/environment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 584d7366d448..c09d7e312ab7 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -991,6 +991,8 @@ def need_exe_wrapper(self, for_machine: MachineChoice = MachineChoice.HOST): value = self.properties[for_machine].get('needs_exe_wrapper', None) if value is not None: return value + if not self.is_cross_build(): + return False return not machine_info_can_run(self.machines[for_machine]) def get_exe_wrapper(self) -> T.Optional[ExternalProgram]: From 9e4feed91ac0c43bf60103c26c1ceb004c7bac0a Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sun, 1 Dec 2024 10:53:16 -0500 Subject: [PATCH 072/624] cargo: Fix crash when enabling feature on dev/build dependencies --- mesonbuild/cargo/interpreter.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 0528c4c9008b..53c0a8095e0a 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -17,6 +17,7 @@ import shutil import collections import urllib.parse +import itertools import typing as T from . import builder @@ -546,8 +547,13 @@ def _load_manifest(self, subdir: str) -> Manifest: def _add_dependency(self, pkg: PackageState, depname: str) -> None: if depname in pkg.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.manifest.dependencies[depname] dep_pkg, _ = self._fetch_package(dep.package, dep.api) if dep.default_features: self._enable_feature(dep_pkg, 'default') @@ -580,9 +586,10 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None: pkg.optional_deps_features[depname].add(dep_f) else: self._add_dependency(pkg, depname) - dep = pkg.manifest.dependencies[depname] - dep_pkg = self._dep_package(dep) - self._enable_feature(dep_pkg, dep_f) + dep = pkg.manifest.dependencies.get(depname) + if dep: + dep_pkg = self._dep_package(dep) + self._enable_feature(dep_pkg, dep_f) elif f.startswith('dep:'): self._add_dependency(pkg, f[4:]) else: From 0efc33940417ed3ca80addf702fb7549eef99b69 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 29 Nov 2024 22:51:43 +0200 Subject: [PATCH 073/624] env2mfile: add flag to use _FOR_BUILD envvars. --- mesonbuild/scripts/env2mfile.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 05bd1544e7c0..16051a871941 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -16,12 +16,6 @@ if T.TYPE_CHECKING: import argparse -def has_for_build() -> bool: - for cenv in envconfig.ENV_VAR_COMPILER_MAP.values(): - if os.environ.get(cenv + '_FOR_BUILD'): - return True - return False - # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ def add_arguments(parser: 'argparse.ArgumentParser') -> None: @@ -35,6 +29,8 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: help='Generate a cross compilation file.') parser.add_argument('--native', default=False, action='store_true', help='Generate a native compilation file.') + parser.add_argument('--use-for-build', default=False, action='store_true', + help='Use _FOR_BUILD envvars.') parser.add_argument('--system', default=None, help='Define system for cross compilation.') parser.add_argument('--subsystem', default=None, @@ -421,12 +417,10 @@ def detect_missing_native_binaries(infos: MachineInfo) -> None: infos.binaries[toolname] = [exe] def detect_native_env(options: T.Any) -> MachineInfo: - use_for_build = has_for_build() - if use_for_build: - mlog.log('Using FOR_BUILD envvars for detection') + if options.use_for_build: + mlog.log('Using _FOR_BUILD envvars for detection (native file for use during cross compilation)') esuffix = '_FOR_BUILD' else: - mlog.log('Using regular envvars for detection.') esuffix = '' infos = detect_compilers_from_envvars(esuffix) detect_missing_native_compilers(infos) @@ -443,6 +437,8 @@ def run(options: T.Any) -> None: mlog.notice('This functionality is experimental and subject to change.') detect_cross = options.cross if detect_cross: + if options.use_for_build: + sys.exit('--use-for-build only makes sense for --native, not --cross') infos = detect_cross_env(options) write_system_info = True else: From f8ade622c635bc2516bf45cb8028404e1d0b83d8 Mon Sep 17 00:00:00 2001 From: Nick <0xb000@gmail.com> Date: Tue, 26 Nov 2024 12:16:59 +0200 Subject: [PATCH 074/624] Slightly fix compiler args order for the Xcode backend Reorder compile args so that target has a chance to override options set by its dependencies. Fix piling up of custom target include paths. --- mesonbuild/backend/xcodebackend.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 31fd272b3f0b..55a8b9968b61 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1703,12 +1703,12 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non for d in swift_dep_dirs: args += compiler.get_include_args(d, False) if args: - lang_cargs = cargs + cti_args = [] if compiler and target.implicit_include_directories: # It is unclear what is the cwd when xcode runs. -I. does not seem to # add the root build dir to the search path. So add an absolute path instead. # This may break reproducible builds, in which case patches are welcome. - lang_cargs += self.get_custom_target_dir_include_args(target, compiler, absolute_path=True) + cti_args = self.get_custom_target_dir_include_args(target, compiler, absolute_path=True) # Xcode cannot handle separate compilation flags for C and ObjectiveC. They are both # put in OTHER_CFLAGS. Same with C++ and ObjectiveC++. if lang == 'objc': @@ -1716,11 +1716,8 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non elif lang == 'objcpp': lang = 'cpp' langname = LANGNAMEMAP[lang] - if langname in langargs: - langargs[langname] += args - else: - langargs[langname] = args - langargs[langname] += lang_cargs + langargs.setdefault(langname, []) + langargs[langname] = cargs + cti_args + args symroot = os.path.join(self.environment.get_build_dir(), target.subdir) bt_dict = PbxDict() objects_dict.add_item(valid, bt_dict, buildtype) From 0dc8f6751143f2066b0bb284ba9b4dc637e6b874 Mon Sep 17 00:00:00 2001 From: Nick <0xb000@gmail.com> Date: Tue, 26 Nov 2024 12:21:39 +0200 Subject: [PATCH 075/624] Fix ugly path after path join with the empty dirname --- mesonbuild/backend/xcodebackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 55a8b9968b61..5c03c715720e 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1718,7 +1718,7 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non langname = LANGNAMEMAP[lang] langargs.setdefault(langname, []) langargs[langname] = cargs + cti_args + args - symroot = os.path.join(self.environment.get_build_dir(), target.subdir) + symroot = os.path.join(self.environment.get_build_dir(), target.subdir).rstrip('/') bt_dict = PbxDict() objects_dict.add_item(valid, bt_dict, buildtype) bt_dict.add_item('isa', 'XCBuildConfiguration') From 57aea2c244966a2206ffea0a906ebbce5509deac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 19:00:35 +0100 Subject: [PATCH 076/624] add netinet/sctp.h to Fedora It is needed by objfw. Signed-off-by: Paolo Bonzini --- ci/ciimage/fedora/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/fedora/install.sh b/ci/ciimage/fedora/install.sh index 65b2012d5000..8d818bf12c9d 100755 --- a/ci/ciimage/fedora/install.sh +++ b/ci/ciimage/fedora/install.sh @@ -12,7 +12,7 @@ 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 objfw mercurial gtk-sharp2-devel libpcap-devel gpgme-devel + doxygen vulkan-devel vulkan-validation-layers-devel openssh lksctp-tools-devel objfw mercurial gtk-sharp2-devel libpcap-devel gpgme-devel qt5-qtbase-devel qt5-qttools-devel qt5-linguist qt5-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 From f9f69d835e01cc6467455b3ad166a7a367920304 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 19:02:06 +0100 Subject: [PATCH 077/624] libgcrypt-config is no more on ubuntu-rolling --- test cases/frameworks/24 libgcrypt/test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/frameworks/24 libgcrypt/test.json b/test cases/frameworks/24 libgcrypt/test.json index 9c282daa86e7..4860d9e00e65 100644 --- a/test cases/frameworks/24 libgcrypt/test.json +++ b/test cases/frameworks/24 libgcrypt/test.json @@ -1,3 +1,3 @@ { - "expect_skip_on_jobname": ["arch", "azure", "cygwin", "msys2"] + "expect_skip_on_jobname": ["arch", "azure", "cygwin", "msys2", "ubuntu-rolling"] } From 2fd0dacf06712857993496b7da1dc16dce71f523 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 11:11:41 +0100 Subject: [PATCH 078/624] mtest: rust: allow parsing doctest output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doctests have a slightly different output compared to what "protocol: rust" supports: running 2 tests test ../doctest1.rs - my_func (line 7) ... ignored test ../doctest1.rs - (line 3) ... ok test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.12s Add a little more parsing in order to accept this; a simple minded split() fails to unpack the tuple. I plan to contribute an extension of the rust module to invoke doctests, for now this allows running rustdoc --test with "protocol: 'rust'" and get information about the subtests: ▶ 4/8 ../doctest1.rs:my_func:7 SKIP ▶ 4/8 ../doctest1.rs:3 OK 4/8 rust_unit_tests:doctests / rust doctest OK 0.28s 1 subtests passed Signed-off-by: Paolo Bonzini --- mesonbuild/mtest.py | 13 +++++++++++-- test cases/rust/9 unit tests/doctest1.rs | 12 ++++++++++++ test cases/rust/9 unit tests/meson.build | 13 +++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 test cases/rust/9 unit tests/doctest1.rs diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 503cb14325dd..27e0f796a3f1 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -81,6 +81,9 @@ UNENCODABLE_XML_CHR_RANGES = [fr'{chr(low)}-{chr(high)}' for (low, high) in UNENCODABLE_XML_UNICHRS] UNENCODABLE_XML_CHRS_RE = re.compile('([' + ''.join(UNENCODABLE_XML_CHR_RANGES) + '])') +RUST_TEST_RE = re.compile(r'^test (?!result)(.*) \.\.\. (.*)$') +RUST_DOCTEST_RE = re.compile(r'^(.*?) - (.*? |)\(line (\d+)\)') + def is_windows() -> bool: platname = platform.system().lower() @@ -1157,8 +1160,14 @@ def parse_res(n: int, name: str, result: str) -> TAPParser.Test: n = 1 async for line in lines: - if line.startswith('test ') and not line.startswith('test result'): - _, name, _, result = line.rstrip().split(' ') + match = RUST_TEST_RE.match(line) + if match: + name, result = match.groups() + doctest = RUST_DOCTEST_RE.match(name) + if doctest: + name = ':'.join((x.rstrip() for x in doctest.groups() if x)) + else: + name = name.rstrip() name = name.replace('::', '.') t = parse_res(n, name, result) self.results.append(t) diff --git a/test cases/rust/9 unit tests/doctest1.rs b/test cases/rust/9 unit tests/doctest1.rs new file mode 100644 index 000000000000..d270f7d67047 --- /dev/null +++ b/test cases/rust/9 unit tests/doctest1.rs @@ -0,0 +1,12 @@ +//! This is a doctest +//! +//! ``` +//! assert_eq!(2+2, 4) +//! ``` + +/// ```ignore +/// this one will be skipped +/// ``` +fn my_func() +{ +} diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build index e6274578146e..aa9da679693e 100644 --- a/test cases/rust/9 unit tests/meson.build +++ b/test cases/rust/9 unit tests/meson.build @@ -31,6 +31,19 @@ test( suite : ['foo'], ) +rustdoc = find_program('rustdoc', required: false) +if rustdoc.found() + # rustdoc is invoked mostly like rustc. This is a simple example + # where it is easy enough to invoke it by hand. + test( + 'rust doctest', + rustdoc, + args : ['--test', '--crate-name', 'doctest1', '--crate-type', 'lib', files('doctest1.rs')], + protocol : 'rust', + suite : ['doctests'], + ) +endif + exe = executable('rust_exe', ['test2.rs', 'test.rs'], build_by_default : false) rust = import('rust') From d8ea5c4d8875bf198f088c603868edf66a3c7c65 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 29 Oct 2024 16:51:36 -0400 Subject: [PATCH 079/624] fix generate_gir with BothLibraries dependency Co-authored-by: Xavier Claessens --- mesonbuild/build.py | 52 ++++++++++++------- mesonbuild/interpreter/interpreter.py | 22 ++++---- mesonbuild/interpreter/interpreterobjects.py | 10 ++-- .../frameworks/38 gir both_libraries/bar.c | 7 +++ .../frameworks/38 gir both_libraries/bar.h | 1 + .../frameworks/38 gir both_libraries/foo.c | 6 +++ .../frameworks/38 gir both_libraries/foo.h | 1 + .../38 gir both_libraries/meson.build | 42 +++++++++++++++ .../38 gir both_libraries/test.json | 3 ++ 9 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 test cases/frameworks/38 gir both_libraries/bar.c create mode 100644 test cases/frameworks/38 gir both_libraries/bar.h create mode 100644 test cases/frameworks/38 gir both_libraries/foo.c create mode 100644 test cases/frameworks/38 gir both_libraries/foo.h create mode 100644 test cases/frameworks/38 gir both_libraries/meson.build create mode 100644 test cases/frameworks/38 gir both_libraries/test.json diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a00209ad45a8..35f1f24a42f8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -774,6 +774,7 @@ def __init__( } self.pic = False self.pie = False + 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.process_sourcelist(sources) @@ -1740,16 +1741,20 @@ def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None: 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]: bl_type = self.environment.coredata.get_option(OptionKey('default_both_libraries')) if bl_type == 'auto': - bl_type = 'static' if isinstance(self, StaticLibrary) else 'shared' - - def _resolve_both_libs(lib: LibTypes) -> LibTypes: - if isinstance(lib, BothLibraries): - return lib.get(bl_type) - return lib + if isinstance(self, StaticLibrary): + bl_type = 'static' + elif isinstance(self, SharedLibrary): + bl_type = 'shared' self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets - lib_list = listify(kwargs.get(key, [])) + self_libs - return [_resolve_both_libs(t) for t in lib_list] + + lib_list = [] + for lib in listify(kwargs.get(key, [])) + self_libs: + if isinstance(lib, (Target, BothLibraries)): + lib_list.append(lib.get(bl_type)) + else: + lib_list.append(lib) + return lib_list def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: """Base case used by BothLibraries""" @@ -2204,6 +2209,14 @@ def is_linkable_target(self): def is_internal(self) -> bool: return not self.install + def set_shared(self, shared_library: SharedLibrary) -> None: + self.both_lib = shared_library + + def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + if lib_type == 'shared': + return self.both_lib or self + return self + class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs @@ -2470,6 +2483,14 @@ def type_suffix(self): def is_linkable_target(self): return True + def set_static(self, static_library: StaticLibrary) -> None: + self.both_lib = static_library + + def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: + if lib_type == 'static': + return self.both_lib or self + return self + # A shared library that is meant to be used with dlopen rather than linking # into something else. class SharedModule(SharedLibrary): @@ -2506,7 +2527,7 @@ def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, No return self.environment.get_shared_module_dir(), '{moduledir_shared}' class BothLibraries(SecondLevelHolder): - def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static', 'auto']) -> None: + def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static']) -> None: self._preferred_library = preferred_library self.shared = shared self.static = static @@ -2914,23 +2935,14 @@ class AliasTarget(RunTarget): typename = 'alias' - def __init__(self, name: str, dependencies: T.Sequence[T.Union[Target, BothLibraries]], + def __init__(self, name: str, dependencies: T.Sequence[Target], subdir: str, subproject: str, environment: environment.Environment): - super().__init__(name, [], list(self._deps_generator(dependencies)), subdir, subproject, environment) + super().__init__(name, [], dependencies, subdir, subproject, environment) def __repr__(self): repr_str = "<{0} {1}>" return repr_str.format(self.__class__.__name__, self.get_id()) - @staticmethod - def _deps_generator(dependencies: T.Sequence[T.Union[Target, BothLibraries]]) -> T.Iterator[Target]: - for dep in dependencies: - if isinstance(dep, BothLibraries): - yield dep.shared - yield dep.static - else: - yield dep - class Jar(BuildTarget): known_kwargs = known_jar_kwargs diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 58385c58c5f7..10ca3e6bb2e8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -31,7 +31,7 @@ from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import ObjectHolder, ContextManagerObject -from ..interpreterbase import stringifyUserArguments, resolve_second_level_holders +from ..interpreterbase import stringifyUserArguments from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..optinterpreter import optname_regex @@ -681,7 +681,6 @@ def func_files(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwa KwargInfo('version', (str, NoneType)), KwargInfo('objects', ContainerTypeInfo(list, build.ExtractedObjects), listify=True, default=[], since='1.1.0'), ) - @noSecondLevelHolderResolving def func_declare_dependency(self, node: mparser.BaseNode, args: T.List[TYPE_var], kwargs: kwtypes.FuncDeclareDependency) -> dependencies.Dependency: deps = kwargs['dependencies'] @@ -1906,15 +1905,12 @@ def func_jar(self, node: mparser.BaseNode, @permittedKwargs(known_build_target_kwargs) @typed_pos_args('build_target', str, varargs=SOURCES_VARARGS) @typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True) - @noSecondLevelHolderResolving def func_build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.BuildTarget ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary, build.SharedModule, build.BothLibraries, build.Jar]: target_type = kwargs['target_type'] - if target_type not in {'both_libraries', 'library'}: - args, kwargs = resolve_second_level_holders(args, kwargs) if target_type == 'executable': return self.build_target(node, args, kwargs, build.Executable) @@ -2176,13 +2172,19 @@ def func_run_target(self, node: mparser.FunctionNode, args: T.Tuple[str], @FeatureNew('alias_target', '0.52.0') @typed_pos_args('alias_target', str, varargs=(build.Target, build.BothLibraries), min_varargs=1) @noKwargs - @noSecondLevelHolderResolving def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T.Union[build.Target, build.BothLibraries]]], kwargs: TYPE_kwargs) -> build.AliasTarget: name, deps = args if any(isinstance(d, build.RunTarget) for d in deps): FeatureNew.single_use('alias_target that depends on run_targets', '0.60.0', self.subproject) - tg = build.AliasTarget(name, deps, self.subdir, self.subproject, self.environment) + real_deps: T.List[build.Target] = [] + for d in deps: + if isinstance(d, build.BothLibraries): + real_deps.append(d.shared) + real_deps.append(d.static) + else: + real_deps.append(d) + tg = build.AliasTarget(name, real_deps, self.subdir, self.subproject, self.environment) self.add_target(name, tg) return tg @@ -3286,16 +3288,18 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source # Keep only compilers used for linking static_lib.compilers = {k: v for k, v in static_lib.compilers.items() if k in compilers.clink_langs} + # Cross reference them to implement as_shared() and as_static() methods. + shared_lib.set_static(static_lib) + static_lib.set_shared(shared_lib) + return build.BothLibraries(shared_lib, static_lib, preferred_library) def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library): default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject)) assert isinstance(default_library, str), 'for mypy' if default_library == 'shared': - args, kwargs = resolve_second_level_holders(args, kwargs) return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary) elif default_library == 'static': - args, kwargs = resolve_second_level_holders(args, kwargs) return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary) elif default_library == 'both': return self.build_both_libraries(node, args, kwargs) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index a919102607be..f4a2b4107ed3 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1001,8 +1001,6 @@ class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]): class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]): def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'): - # FIXME: This build target always represents the shared library, but - # that should be configurable. super().__init__(libs, interp) self.methods.update({'get_shared_lib': self.get_shared_lib_method, 'get_static_lib': self.get_static_lib_method, @@ -1017,12 +1015,16 @@ def __repr__(self) -> str: @noPosargs @noKwargs def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary: - return self.held_object.shared + lib = copy.copy(self.held_object.shared) + lib.both_lib = None + return lib @noPosargs @noKwargs def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary: - return self.held_object.static + lib = copy.copy(self.held_object.static) + lib.both_lib = None + return lib class SharedModuleHolder(BuildTargetHolder[build.SharedModule]): pass diff --git a/test cases/frameworks/38 gir both_libraries/bar.c b/test cases/frameworks/38 gir both_libraries/bar.c new file mode 100644 index 000000000000..4cb41f798294 --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/bar.c @@ -0,0 +1,7 @@ +#include "bar.h" +#include "foo.h" + +int bar_func(void) +{ + return foo_func() + 42; +} diff --git a/test cases/frameworks/38 gir both_libraries/bar.h b/test cases/frameworks/38 gir both_libraries/bar.h new file mode 100644 index 000000000000..d22827b837f7 --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/bar.h @@ -0,0 +1 @@ +int bar_func(void); diff --git a/test cases/frameworks/38 gir both_libraries/foo.c b/test cases/frameworks/38 gir both_libraries/foo.c new file mode 100644 index 000000000000..b88aa91dabb4 --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/foo.c @@ -0,0 +1,6 @@ +#include "foo.h" + +int foo_func(void) +{ + return 42; +} diff --git a/test cases/frameworks/38 gir both_libraries/foo.h b/test cases/frameworks/38 gir both_libraries/foo.h new file mode 100644 index 000000000000..2a0867249307 --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/foo.h @@ -0,0 +1 @@ +int foo_func(void); diff --git a/test cases/frameworks/38 gir both_libraries/meson.build b/test cases/frameworks/38 gir both_libraries/meson.build new file mode 100644 index 000000000000..cb9cdd31f3ed --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/meson.build @@ -0,0 +1,42 @@ +project('gir both libraries', 'c') + +gir = dependency('gobject-introspection-1.0', required: false) +if not gir.found() + error('MESON_SKIP_TEST gobject-introspection not found.') +endif + +if host_machine.system() == 'cygwin' + # FIXME: g-ir-scanner seems broken on cygwin: + # ERROR: can't resolve libraries to shared libraries: foo++ + error('MESON_SKIP_TEST g-ir-scanner is broken on cygwin.') +endif + +gnome = import('gnome') + +# Regression test simulating how GStreamer generate its GIRs. +# Generated gobject-introspection binaries for every GStreamer libraries must +# first call gst_init() defined in the main libgstreamer, which means they need +# to link on that lib. +# A regression caused by https://github.com/mesonbuild/meson/pull/12632 made +# Meson not link the binary generated for bar with libfoo in the case it uses +# both_libraries(). + +libfoo = both_libraries('foo', 'foo.c') +foo_gir = gnome.generate_gir(libfoo, + namespace: 'foo', + nsversion: '1.0', + sources: ['foo.c', 'foo.h'], +) +foo_dep = declare_dependency( + link_with: libfoo, + sources: foo_gir, +) + +libbar = both_libraries('bar', 'bar.c', dependencies: foo_dep) +gnome.generate_gir(libbar, + namespace: 'bar', + nsversion: '1.0', + sources: ['bar.c', 'bar.h'], + extra_args: '--add-init-section=extern void foo_func(void);foo_func();', + dependencies: foo_dep, +) diff --git a/test cases/frameworks/38 gir both_libraries/test.json b/test cases/frameworks/38 gir both_libraries/test.json new file mode 100644 index 000000000000..82ac42a293b3 --- /dev/null +++ b/test cases/frameworks/38 gir both_libraries/test.json @@ -0,0 +1,3 @@ +{ + "expect_skip_on_jobname": ["azure", "macos", "msys2", "cygwin"] +} \ No newline at end of file From 38051a5a4bc558905d89146c9a27a215fc0b4ab4 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 15 Oct 2024 15:46:07 -0400 Subject: [PATCH 080/624] format: allow input from stdin Fixes #13791 --- docs/markdown/Commands.md | 4 ++++ docs/markdown/snippets/format_from_stdin.md | 4 ++++ mesonbuild/mformat.py | 15 +++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/format_from_stdin.md diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index 8e34800a44b3..a99deb4941a9 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -489,6 +489,10 @@ When `--recursive` option is specified, `meson.build` files from `subdir` are also analyzed (must be used in conjunction with `--inplace` or `--check-only` option). +*Since 1.7.0* You can use `-` as source file name to read source from standard +input instead of reading it from a file. This cannot be used with `--recursive` +or `--inline` arguments. + #### Differences with `muon fmt` diff --git a/docs/markdown/snippets/format_from_stdin.md b/docs/markdown/snippets/format_from_stdin.md new file mode 100644 index 000000000000..cebe976c41b0 --- /dev/null +++ b/docs/markdown/snippets/format_from_stdin.md @@ -0,0 +1,4 @@ +## format command now accept stdin argument + +You can now use `-` argument for `meson format` to read input from stdin +instead of reading it from a file. diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index 119c89351ec4..bda89bf3fa10 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -9,6 +9,7 @@ from copy import deepcopy from dataclasses import dataclass, field, fields, asdict from pathlib import Path +import sys from . import mparser from .mesonlib import MesonException @@ -966,6 +967,12 @@ def run(options: argparse.Namespace) -> int: if options.recursive and not (options.inplace or options.check_only): raise MesonException('--recursive argument requires either --inplace or --check-only option') + from_stdin = len(options.sources) == 1 and options.sources[0].name == '-' and options.sources[0].parent == Path() + if options.recursive and from_stdin: + 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') + sources: T.List[Path] = options.sources.copy() or [Path(build_filename)] if not options.configuration: default_config_path = sources[0].parent / 'meson.format' @@ -979,7 +986,11 @@ def run(options: argparse.Namespace) -> int: src_file = src_file / build_filename try: - code = src_file.read_text(encoding='utf-8') + if from_stdin: + src_file = Path('STDIN') # used for error messages and introspection + code = sys.stdin.read() + else: + code = src_file.read_text(encoding='utf-8') except IOError as e: raise MesonException(f'Unable to read from {src_file}') from e @@ -1002,7 +1013,7 @@ def run(options: argparse.Namespace) -> int: with options.output.open('w', encoding='utf-8', newline=formatter.current_config.newline) as of: of.write(formatted) except IOError as e: - raise MesonException(f'Unable to write to {src_file}') from e + raise MesonException(f'Unable to write to {options.output}') from e else: print(formatted, end='') From 83253cdbaa8afd268286ca06520ca1cf2095dd28 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Dec 2024 12:01:59 -0800 Subject: [PATCH 081/624] interpreter: fix type issues with vcs_tag updates The annotations weren't updated to reflect the changes. --- mesonbuild/interpreter/interpreter.py | 6 ++++-- mesonbuild/interpreter/kwargs.py | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 10ca3e6bb2e8..d717485e8a1e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -110,6 +110,8 @@ import copy if T.TYPE_CHECKING: + from typing_extensions import Literal + from . import kwargs as kwtypes from ..backend.backends import Backend from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs @@ -1986,8 +1988,8 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: install = kwargs['install'] install_mode = self._warn_kwarg_install_mode_sticky(kwargs['install_mode']) - install_dir = [] if kwargs['install_dir'] is None else [kwargs['install_dir']] - install_tag = [] if kwargs['install_tag'] is None else [kwargs['install_tag']] + install_dir: T.List[T.Union[str, Literal[False]]] = [] if kwargs['install_dir'] is None else [kwargs['install_dir']] + install_tag: T.List[T.Optional[str]] = [] if kwargs['install_tag'] is None else [kwargs['install_tag']] if install and not install_dir: raise InvalidArguments('vcs_tag: "install_dir" keyword argument must be set when "install" is true.') diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index ae4866a88ad8..87f121e90b0f 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright © 2021 The Meson Developers -# Copyright © 2021 Intel Corporation +# Copyright © 2021-2024 Intel Corporation from __future__ import annotations """Keyword Argument type annotations.""" @@ -286,6 +286,10 @@ class VcsTag(TypedDict): build.ExtractedObjects, build.GeneratedList, ExternalProgram, File]] output: T.List[str] replace_string: str + install: bool + install_tag: T.Optional[str] + install_dir: T.Optional[str] + install_mode: FileMode class ConfigureFile(TypedDict): From 8578a995ae45ff737e25b8ff9d8ab0abe8d3b64c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 9 Dec 2024 15:28:45 +0100 Subject: [PATCH 082/624] modernize Rust template Signed-off-by: Paolo Bonzini --- mesonbuild/templates/rusttemplates.py | 27 +++++++++++++++------------ mesonbuild/templates/sampleimpl.py | 12 +++++++----- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 5bb7e4c19006..1dbf5b614115 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -20,25 +20,28 @@ pub fn {function_name}() -> i32 {{ return internal_function(); }} -''' -lib_rust_test_template = '''extern crate {crate_file}; +#[cfg(test)] +mod tests {{ + use super::*; -fn main() {{ - println!("printing: {{}}", {crate_file}::{function_name}()); + #[test] + fn test_function() {{ + assert_eq!({function_name}(), 0); + }} }} ''' lib_rust_meson_template = '''project('{project_name}', 'rust', - version : '{version}', - default_options : ['warning_level=3']) + version : '{version}', meson_version: '>=1.3.0', + default_options : ['rust_std=2021', 'warning_level=3']) + +rust = import('rust') shlib = static_library('{lib_name}', '{source_file}', install : true) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) -test('{test_name}', test_exe) +rust.test('{test_name}', shlib) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( @@ -54,8 +57,8 @@ ''' hello_rust_meson_template = '''project('{project_name}', 'rust', - version : '{version}', - default_options : ['warning_level=3']) + version : '{version}', meson_version: '>=1.3.0', + default_options : ['rust_std=2021', 'warning_level=3']) exe = executable('{exe_name}', '{source_name}', install : true) @@ -70,7 +73,7 @@ class RustProject(FileImpl): exe_template = hello_rust_template exe_meson_template = hello_rust_meson_template lib_template = lib_rust_template - lib_test_template = lib_rust_test_template + lib_test_template = None lib_meson_template = lib_rust_meson_template def lib_kwargs(self) -> T.Dict[str, str]: diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index 570a370b8e3f..c222a1bf9aa7 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -41,7 +41,7 @@ def lib_template(self) -> str: pass @abc.abstractproperty - def lib_test_template(self) -> str: + def lib_test_template(self) -> T.Optional[str]: pass @abc.abstractproperty @@ -85,8 +85,9 @@ def create_library(self) -> None: } with open(lib_name, 'w', encoding='utf-8') as f: f.write(self.lib_template.format(**kwargs)) - with open(test_name, 'w', encoding='utf-8') as f: - f.write(self.lib_test_template.format(**kwargs)) + if self.lib_test_template: + with open(test_name, 'w', encoding='utf-8') as f: + f.write(self.lib_test_template.format(**kwargs)) with open('meson.build', 'w', encoding='utf-8') as f: f.write(self.lib_meson_template.format(**kwargs)) @@ -132,8 +133,9 @@ def create_library(self) -> None: kwargs = self.lib_kwargs() with open(lib_name, 'w', encoding='utf-8') as f: f.write(self.lib_template.format(**kwargs)) - with open(test_name, 'w', encoding='utf-8') as f: - f.write(self.lib_test_template.format(**kwargs)) + if self.lib_test_template: + with open(test_name, 'w', encoding='utf-8') as f: + f.write(self.lib_test_template.format(**kwargs)) with open('meson.build', 'w', encoding='utf-8') as f: f.write(self.lib_meson_template.format(**kwargs)) From 54cab0910e26e82f5d4cd7c087781ed7826d649a Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 10 Dec 2024 03:57:40 +0000 Subject: [PATCH 083/624] ci: fix Ubuntu Bionic job Do the same as https://github.com/pytorch/test-infra/pull/5959 and download an old nodejs to keep Ubuntu Bionic working. Bug: https://github.com/actions/checkout/issues/1809 Bug: https://github.com/actions/runner/issues/3373 --- .github/workflows/os_comp.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 0830a05e5836..0912a750a76b 100644 --- a/.github/workflows/os_comp.yml +++ b/.github/workflows/os_comp.yml @@ -26,12 +26,6 @@ on: - ".github/workflows/os_comp.yml" - "run*tests.py" -# make GHA actions use node16 which still works with bionic -# See https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/ -# Unclear how long this will work though -env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - permissions: contents: read @@ -49,13 +43,24 @@ jobs: - { name: Gentoo, id: gentoo } - { name: OpenSUSE, id: opensuse } - { name: Ubuntu Bionic, id: bionic } - container: mesonbuild/${{ matrix.cfg.id }}:latest + container: + image: mesonbuild/${{ matrix.cfg.id }}:latest + volumes: + - ${{ matrix.cfg.id == 'bionic' && '/node20217:/node20217:rw,rshared' || ' ' }} + - ${{ matrix.cfg.id == 'bionic' && '/node20217:/__e/node20:ro,rshared' || ' ' }} env: MESON_CI_JOBNAME: linux-${{ matrix.cfg.id }}-gcc steps: - # Need v3 because of bionic - - uses: actions/checkout@v3 + - name: install nodejs20glibc2.17 + if: ${{ matrix.cfg.id == 'bionic' }} + run: | + apt install curl -y + curl -LO https://unofficial-builds.nodejs.org/download/release/v20.9.0/node-v20.9.0-linux-x64-glibc-217.tar.xz + tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217 + ldd /__e/node20/bin/node + - uses: actions/checkout@v4 + - name: Run tests # All environment variables are stored inside the docker image in /ci/env_vars.sh # They are defined in the `env` section in each image.json. CI_ARGS should be set From 8156e12ce0de62aa47472e529489f7412628e6e5 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Mon, 9 Dec 2024 12:06:47 +0100 Subject: [PATCH 084/624] separate mesondefine and cmakedefine logic --- mesonbuild/utils/universal.py | 170 +++++++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 45 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 88d8e1f891c7..76a6df8da642 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1173,6 +1173,46 @@ def join_args(args: T.Iterable[str]) -> str: def do_replacement(regex: T.Pattern[str], line: str, variable_format: Literal['meson', 'cmake', 'cmake@'], confdata: T.Union[T.Dict[str, T.Tuple[str, T.Optional[str]]], 'ConfigurationData']) -> T.Tuple[str, T.Set[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) + else: + raise MesonException('Invalid variable format') + +def do_replacement_meson(regex: T.Pattern[str], line: str, + 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) + # \@escaped\@ variables + elif match.groupdict().get('escaped') is not None: + return match.group('escaped')[1:-2]+'@' + else: + # Template variable to be replaced + varname = match.group('variable') + var_str = '' + if varname in confdata: + var, _ = confdata.get(varname) + if isinstance(var, str): + var_str = 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: + missing_variables.add(varname) + 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, + 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: @@ -1181,7 +1221,7 @@ def variable_replace(match: T.Match[str]) -> str: num_escapes = match.end(0) - match.start(0) return '\\' * (num_escapes // 2) # Handle cmake escaped \${} tags - elif variable_format == 'cmake' and match.group(0) == '\\${': + elif not at_only and match.group(0) == '\\${': return '${' # \@escaped\@ variables elif match.groupdict().get('escaped') is not None: @@ -1194,7 +1234,7 @@ def variable_replace(match: T.Match[str]) -> str: var, _ = confdata.get(varname) if isinstance(var, str): var_str = var - elif variable_format.startswith("cmake") and isinstance(var, bool): + elif isinstance(var, bool): var_str = str(int(var)) elif isinstance(var, int): var_str = str(var) @@ -1207,11 +1247,36 @@ def variable_replace(match: T.Match[str]) -> str: return var_str return re.sub(regex, variable_replace, line), missing_variables -def do_define(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData', - variable_format: Literal['meson', 'cmake', 'cmake@'], subproject: T.Optional[SubProject] = None) -> str: - cmake_bool_define = False - if variable_format != "meson": - cmake_bool_define = "cmakedefine01" in line +def do_define_meson(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData', + subproject: T.Optional[SubProject] = None) -> str: + + arr = line.split() + if len(arr) != 2: + raise MesonException('#mesondefine does not contain exactly two tokens: %s' % line.strip()) + + varname = arr[1] + try: + v, _ = confdata.get(varname) + except KeyError: + return '/* #undef %s */\n' % varname + + if isinstance(v, str): + result = f'#define {varname} {v}'.strip() + '\n' + result, _ = do_replacement_meson(regex, result, confdata) + return result + elif isinstance(v, bool): + if v: + return '#define %s\n' % varname + else: + return '#undef %s\n' % varname + elif isinstance(v, int): + return '#define %s %d\n' % (varname, v) + 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, + subproject: T.Optional[SubProject] = None) -> str: + cmake_bool_define = 'cmakedefine01' in line def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str: arr = line.split() @@ -1230,12 +1295,10 @@ def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str: return ' '.join(define_value) arr = line.split() - if len(arr) != 2: - if variable_format == 'meson': - raise MesonException('#mesondefine does not contain exactly two tokens: %s' % line.strip()) - elif subproject is not None: - from ..interpreterbase.decorators import FeatureNew - FeatureNew.single_use('cmakedefine without exactly two tokens', '0.54.1', subproject) + + if len(arr) != 2 and subproject is not None: + from ..interpreterbase.decorators import FeatureNew + FeatureNew.single_use('cmakedefine without exactly two tokens', '0.54.1', subproject) varname = arr[1] try: @@ -1246,26 +1309,13 @@ def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str: else: return '/* #undef %s */\n' % varname - if isinstance(v, str) or variable_format != "meson": - if variable_format == 'meson': - result = v - else: - if not cmake_bool_define and not v: - return '/* #undef %s */\n' % varname + if not cmake_bool_define and not v: + return '/* #undef %s */\n' % varname - result = get_cmake_define(line, confdata) - result = f'#define {varname} {result}'.strip() + '\n' - result, _ = do_replacement(regex, result, variable_format, confdata) - return result - elif isinstance(v, bool): - if v: - return '#define %s\n' % varname - else: - return '#undef %s\n' % varname - elif isinstance(v, int): - return '#define %s %d\n' % (varname, v) - else: - raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname) + result = get_cmake_define(line, confdata) + result = f'#define {varname} {result}'.strip() + '\n' + result, _ = do_replacement_cmake(regex, 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 @@ -1291,20 +1341,50 @@ def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'm def do_conf_str(src: str, data: T.List[str], confdata: 'ConfigurationData', variable_format: Literal['meson', 'cmake', 'cmake@'], subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]: - def line_is_valid(line: str, variable_format: str) -> bool: - if variable_format == 'meson': + if variable_format == 'meson': + return do_conf_str_meson(src, data, confdata, subproject) + elif variable_format in {'cmake', 'cmake@'}: + return do_conf_str_cmake(src, data, confdata, variable_format == 'cmake@', subproject) + else: + raise MesonException('Invalid variable format') + +def do_conf_str_meson(src: str, data: T.List[str], confdata: 'ConfigurationData', + subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]: + + regex = get_variable_regex('meson') + + search_token = '#mesondefine' + + result: T.List[str] = [] + missing_variables: T.Set[str] = set() + # Detect when the configuration data is empty and no tokens were found + # 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): + confdata_useless = False + line = do_define_meson(regex, line, confdata, subproject) + else: if '#cmakedefine' in line: - return False - else: # cmake format - if '#mesondefine' in line: - return False - return True + raise MesonException(f'Format error in {src}: saw "{line.strip()}" when format set to "meson"') + line, missing = do_replacement_meson(regex, line, confdata) + missing_variables.update(missing) + if missing: + confdata_useless = False + result.append(line) + + return result, missing_variables, confdata_useless + +def do_conf_str_cmake(src: str, data: T.List[str], confdata: 'ConfigurationData', at_only: bool, + subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]: + + variable_format: Literal['cmake', 'cmake@'] = 'cmake' + if at_only: + variable_format = 'cmake@' regex = get_variable_regex(variable_format) - search_token = '#mesondefine' - if variable_format != 'meson': - search_token = '#cmakedefine' + search_token = '#cmakedefine' result: T.List[str] = [] missing_variables: T.Set[str] = set() @@ -1314,11 +1394,11 @@ def line_is_valid(line: str, variable_format: str) -> bool: for line in data: if line.lstrip().startswith(search_token): confdata_useless = False - line = do_define(regex, line, confdata, variable_format, subproject) + line = do_define_cmake(regex, line, confdata, at_only, subproject) else: - if not line_is_valid(line, variable_format): + if '#mesondefine' in line: raise MesonException(f'Format error in {src}: saw "{line.strip()}" when format set to "{variable_format}"') - line, missing = do_replacement(regex, line, variable_format, confdata) + line, missing = do_replacement_cmake(regex, line, at_only, confdata) missing_variables.update(missing) if missing: confdata_useless = False From e542901af6e30865715d3c3c18f703910a096ec0 Mon Sep 17 00:00:00 2001 From: LIU Hao Date: Wed, 11 Dec 2024 09:27:53 +0800 Subject: [PATCH 085/624] compilers: Pass `vs_module_defs` with `/DEF:` to LLD-LINK Recently, it is possible to install Clang with Visual Studio Installer. By default this Clang has a MSVC target, and invokes the Microsoft Linker; if `-fuse-ld=lld` is specified, it will invoke LLD-LINK. Both linkers take MSVC-style arguments, and take DEF files with `/DEF:`. Previously DEF files were passed in the GNU way, directly on the linker command line like an object file, which caused errors like lld-link: error: ..\my.def: unknown file type While Clang-CL takes Unix-style options, it actually passes MSVC-style options to LINK or LLD-LINK with `-Wl,`. There is already a check for both linkers in `linker_to_compiler_args()`, so it's necessary to do the same in `gen_vs_module_defs_args()`. This commit closes https://github.com/mesonbuild/meson/issues/13988. Signed-off-by: LIU Hao --- mesonbuild/compilers/mixins/clang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index a0d3d5ffb069..41b35c041336 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -135,7 +135,7 @@ def openmp_flags(self, env: Environment) -> T.List[str]: return [] def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]: - if isinstance(self.linker, (MSVCDynamicLinker)): + 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] From 0b41b364be716064285928c00330d1fc6b07cd88 Mon Sep 17 00:00:00 2001 From: Nick <0xb000@gmail.com> Date: Mon, 4 Nov 2024 18:33:41 +0200 Subject: [PATCH 086/624] Xcode backend: better quoting for spaces in HEADER_SEARCH_PATHS Xcode treats this dict value as a space-separated string, any spaces in the path will make the path invalid by splitting it into pieces. Split out header path processing into a helper function. --- mesonbuild/backend/xcodebackend.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 5c03c715720e..0e40d0239e5a 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1756,19 +1756,12 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non settings_dict.add_item('GCC_PREFIX_HEADER', f'$(PROJECT_DIR)/{relative_pch_path}') settings_dict.add_item('GCC_PREPROCESSOR_DEFINITIONS', '') settings_dict.add_item('GCC_SYMBOLS_PRIVATE_EXTERN', 'NO') - header_arr = PbxArray() - unquoted_headers = [] - unquoted_headers.append(self.get_target_private_dir_abs(target)) + unquoted_headers = [self.get_target_private_dir_abs(target)] if target.implicit_include_directories: unquoted_headers.append(os.path.join(self.environment.get_build_dir(), target.get_subdir())) unquoted_headers.append(os.path.join(self.environment.get_source_dir(), target.get_subdir())) - if headerdirs: - for i in headerdirs: - i = os.path.normpath(i) - unquoted_headers.append(i) - for i in unquoted_headers: - header_arr.add_item(f'"{i}"') - settings_dict.add_item('HEADER_SEARCH_PATHS', header_arr) + unquoted_headers += headerdirs + settings_dict.add_item('HEADER_SEARCH_PATHS', self.normalize_header_search_paths(unquoted_headers)) settings_dict.add_item('INSTALL_PATH', install_path) settings_dict.add_item('LIBRARY_SEARCH_PATHS', '') if isinstance(target, build.SharedModule): @@ -1796,6 +1789,15 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non warn_array.add_item('"$(inherited)"') bt_dict.add_item('name', buildtype) + def normalize_header_search_paths(self, header_dirs) -> PbxArray: + header_arr = PbxArray() + for i in header_dirs: + np = os.path.normpath(i) + # Make sure Xcode will not split single path into separate entries, escaping space with a slash is not enought + item = f'"\\\"{np}\\\""' if ' ' in np else f'"{np}"' + header_arr.add_item(item) + return header_arr + def add_otherargs(self, settings_dict, langargs): for langname, args in langargs.items(): if args: From 16cf71f0512ec26633b6d21e9c5b28613c36596b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 16 Dec 2024 13:29:15 +0200 Subject: [PATCH 087/624] Handle top level options set in subprojects. Closes #13847. --- mesonbuild/coredata.py | 17 ++++++++++++++--- test cases/unit/123 pkgsubproj/meson.build | 3 +++ .../123 pkgsubproj/subprojects/sub/meson.build | 1 + unittests/linuxliketests.py | 4 ++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test cases/unit/123 pkgsubproj/meson.build create mode 100644 test cases/unit/123 pkgsubproj/subprojects/sub/meson.build diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 98656b27d9b0..c3e435271952 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -658,9 +658,20 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' elif k.machine != MachineChoice.BUILD and not self.optstore.is_compiler_option(k): unknown_options.append(k) 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 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.evolve(subproject='') + 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() diff --git a/test cases/unit/123 pkgsubproj/meson.build b/test cases/unit/123 pkgsubproj/meson.build new file mode 100644 index 000000000000..b4cf89fa0b6d --- /dev/null +++ b/test cases/unit/123 pkgsubproj/meson.build @@ -0,0 +1,3 @@ +project('pkg_opt_test') + +subproject('sub') diff --git a/test cases/unit/123 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/123 pkgsubproj/subprojects/sub/meson.build new file mode 100644 index 000000000000..99622b681cdd --- /dev/null +++ b/test cases/unit/123 pkgsubproj/subprojects/sub/meson.build @@ -0,0 +1 @@ +project('subproject', default_options: 'pkgconfig.relocatable=true') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 1e9e38d1b323..c8d5a538df17 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1863,3 +1863,7 @@ def test_complex_link_cases(self): self.assertIn('build t9-e1: c_LINKER t9-e1.p/main.c.o | libt9-s1.a libt9-s2.a libt9-s3.a\n', content) self.assertIn('build t12-e1: c_LINKER t12-e1.p/main.c.o | libt12-s1.a libt12-s2.a libt12-s3.a\n', content) 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, '123 pkgsubproj') + self.init(testdir) From bcb605201aba66343a9ff16d716454295a7a41c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?= Date: Mon, 16 Dec 2024 17:58:20 +0100 Subject: [PATCH 088/624] dependencies: support old vulkan SDK version In old versions of Vulkan SDK, VK_SDK_PATH environment variable was used instead of VULKAN_SDK. This patch check both environment variables is fallback mode. --- mesonbuild/dependencies/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index d88af7945c7c..7adac5e75723 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -189,7 +189,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. super().__init__(name, environment, kwargs, language=language) try: - self.vulkan_sdk = os.environ['VULKAN_SDK'] + 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: From 193715b920750a6bab41b691cb7de25bf39dce53 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 16 Nov 2024 15:24:34 +0100 Subject: [PATCH 089/624] utils: optimize PerMachine There is no need to create and look up a dictionary when MachineChoice is an enum, and there is no need to create a PerMachine object on every __setitem__ of another PerMachine object. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 76a6df8da642..8012dcee3d12 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -473,6 +473,10 @@ def classify_unity_sources(compilers: T.Iterable['Compiler'], sources: T.Sequenc return compsrclist +MACHINE_NAMES = ['build', 'host'] +MACHINE_PREFIXES = ['build.', ''] + + class MachineChoice(enum.IntEnum): """Enum class representing one of the two abstract machine names used in @@ -486,10 +490,10 @@ def __str__(self) -> str: return f'{self.get_lower_case_name()} machine' def get_lower_case_name(self) -> str: - return PerMachine('build', 'host')[self] + return MACHINE_NAMES[self.value] def get_prefix(self) -> str: - return PerMachine('build.', '')[self] + return MACHINE_PREFIXES[self.value] class PerMachine(T.Generic[_T]): @@ -498,10 +502,7 @@ def __init__(self, build: _T, host: _T) -> None: self.host = host def __getitem__(self, machine: MachineChoice) -> _T: - return { - MachineChoice.BUILD: self.build, - MachineChoice.HOST: self.host, - }[machine] + return [self.build, self.host][machine.value] def __setitem__(self, machine: MachineChoice, val: _T) -> None: setattr(self, machine.get_lower_case_name(), val) From eb35d1a05f08f6969851cea81889e3beffdb9ec2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 09:11:49 +0100 Subject: [PATCH 090/624] mtest: do not import from mintro import mintro and its attendant module dependency tree just so we can programmatically get filenames which are documented as a stable API in https://mesonbuild.com/IDE-integration.html. Suggested-by: Eli Schwartz Signed-off-by: Paolo Bonzini --- mesonbuild/mtest.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 27e0f796a3f1..d41c676ef110 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -38,7 +38,6 @@ from .mesonlib import (MesonException, OrderedSet, RealPathAction, get_wine_shortpath, join_args, split_args, setup_vsenv) from .options import OptionKey -from .mintro import get_infodir, load_info_file from .programs import ExternalProgram from .backend.backends import TestProtocol, TestSerialisation @@ -2158,10 +2157,14 @@ def convert_path_to_target(path: str) -> str: assert len(ninja) > 0 + targets_file = os.path.join(wd, 'meson-info/intro-targets.json') + with open(targets_file, encoding='utf-8') as fp: + targets_info = json.load(fp) + depends: T.Set[str] = set() targets: T.Set[str] = set() intro_targets: T.Dict[str, T.List[str]] = {} - for target in load_info_file(get_infodir(wd), kind='targets'): + for target in targets_info: intro_targets[target['id']] = [ convert_path_to_target(f) for f in target['filename']] From 8b9846d9a9d29b427860891c9b699eb4305e348b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 08:58:10 +0100 Subject: [PATCH 091/624] mtest: move determine_worker_count to utils, generalize It is useful to apply a limit to the number of processes even outside "meson test", and specifically for clang tools. In preparation for this, generalize determine_worker_count() to accept a variable MESON_NUM_PROCESSES instead of MESON_TESTTHREADS, and use it throughout instead of multiprocessing.cpu_count(). Signed-off-by: Paolo Bonzini --- docs/markdown/Unit-tests.md | 8 +++++--- docs/markdown/snippets/num-processes.md | 7 +++++++ mesonbuild/compilers/mixins/gnu.py | 6 +++--- mesonbuild/mtest.py | 27 +++---------------------- mesonbuild/scripts/externalproject.py | 5 ++--- mesonbuild/utils/universal.py | 27 +++++++++++++++++++++++++ 6 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 docs/markdown/snippets/num-processes.md diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 898366095b05..13f6093f2e7b 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -83,16 +83,18 @@ possible. By default Meson uses as many concurrent processes as there are cores on the test machine. You can override this with the environment -variable `MESON_TESTTHREADS` like this. +variable `MESON_TESTTHREADS` or, *since 1.7.0*, `MESON_NUM_PROCESSES`: ```console -$ MESON_TESTTHREADS=5 meson test +$ MESON_NUM_PROCESSES=5 meson test ``` -Setting `MESON_TESTTHREADS` to 0 enables the default behavior (core +Setting `MESON_NUM_PROCESSES` to 0 enables the default behavior (core count), whereas setting an invalid value results in setting the job count to 1. +If both environment variables are present, `MESON_NUM_PROCESSES` prevails. + ## Priorities *(added in version 0.52.0)* diff --git a/docs/markdown/snippets/num-processes.md b/docs/markdown/snippets/num-processes.md new file mode 100644 index 000000000000..e4ffdd1dc5c1 --- /dev/null +++ b/docs/markdown/snippets/num-processes.md @@ -0,0 +1,7 @@ +## Control the number of child processes with an environment variable + +Previously, `meson test` checked the `MESON_TESTTHREADS` variable to control +the amount of parallel jobs to run; this was useful when `meson test` is +invoked through `ninja test` for example. With this version, a new variable +`MESON_NUM_PROCESSES` is supported with a broader scope: in addition to +`meson test`, it is also used by the `external_project` module. diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 21a57b44fef1..976fa78714c6 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -8,7 +8,6 @@ import abc import functools import os -import multiprocessing import pathlib import re import subprocess @@ -617,8 +616,9 @@ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T. if threads == 0: if self._has_lto_auto_support: return ['-flto=auto'] - # This matches clang's behavior of using the number of cpus - return [f'-flto={multiprocessing.cpu_count()}'] + # 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()}'] elif threads > 0: return [f'-flto={threads}'] return super().get_lto_compile_args(threads=threads) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index d41c676ef110..556451c5e6ab 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -14,7 +14,6 @@ import datetime import enum import json -import multiprocessing import os import pickle import platform @@ -36,7 +35,8 @@ from .coredata import MesonVersionMismatchException, major_versions_differ from .coredata import version as coredata_version from .mesonlib import (MesonException, OrderedSet, RealPathAction, - get_wine_shortpath, join_args, split_args, setup_vsenv) + get_wine_shortpath, join_args, split_args, setup_vsenv, + determine_worker_count) from .options import OptionKey from .programs import ExternalProgram from .backend.backends import TestProtocol, TestSerialisation @@ -99,27 +99,6 @@ def uniwidth(s: str) -> int: result += UNIWIDTH_MAPPING[w] return result -def determine_worker_count() -> int: - varname = 'MESON_TESTTHREADS' - num_workers = 0 - if varname in os.environ: - try: - num_workers = int(os.environ[varname]) - if num_workers < 0: - raise ValueError - except ValueError: - print(f'Invalid value in {varname}, using 1 thread.') - num_workers = 1 - - if num_workers == 0: - try: - # Fails in some weird environments such as Debian - # reproducible build. - num_workers = multiprocessing.cpu_count() - except Exception: - num_workers = 1 - return num_workers - # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ def add_arguments(parser: argparse.ArgumentParser) -> None: @@ -154,7 +133,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help="Run benchmarks instead of tests.") parser.add_argument('--logbase', default='testlog', help="Base name for log file.") - parser.add_argument('-j', '--num-processes', default=determine_worker_count(), type=int, + parser.add_argument('-j', '--num-processes', default=determine_worker_count(['MESON_TESTTHREADS']), type=int, help='How many parallel processes to use.') parser.add_argument('-v', '--verbose', default=False, action='store_true', help='Do not redirect stdout and stderr') diff --git a/mesonbuild/scripts/externalproject.py b/mesonbuild/scripts/externalproject.py index ce49fbcbf26e..4013b0acf233 100644 --- a/mesonbuild/scripts/externalproject.py +++ b/mesonbuild/scripts/externalproject.py @@ -5,12 +5,11 @@ import os import argparse -import multiprocessing import subprocess from pathlib import Path import typing as T -from ..mesonlib import Popen_safe, split_args +from ..mesonlib import Popen_safe, split_args, determine_worker_count class ExternalProject: def __init__(self, options: argparse.Namespace): @@ -48,7 +47,7 @@ def supports_jobs_flag(self) -> bool: def build(self) -> int: make_cmd = self.make.copy() if self.supports_jobs_flag(): - make_cmd.append(f'-j{multiprocessing.cpu_count()}') + make_cmd.append(f'-j{determine_worker_count()}') rc = self._run('build', make_cmd) if rc != 0: return rc diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 8012dcee3d12..ea49a065a446 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -13,6 +13,7 @@ import stat import time import abc +import multiprocessing import platform, subprocess, operator, os, shlex, shutil, re import collections from functools import lru_cache, wraps @@ -94,6 +95,7 @@ class _VerPickleLoadable(Protocol): 'default_sysconfdir', 'detect_subprojects', 'detect_vcs', + 'determine_worker_count', 'do_conf_file', 'do_conf_str', 'do_replacement', @@ -1086,6 +1088,31 @@ def default_sysconfdir() -> str: return 'etc' +def determine_worker_count(varnames: T.Optional[T.List[str]] = None) -> int: + num_workers = 0 + varnames = varnames or [] + # Add MESON_NUM_PROCESSES last, so it will prevail if more than one + # variable is present. + varnames.append('MESON_NUM_PROCESSES') + for varname in varnames: + if varname in os.environ: + try: + num_workers = int(os.environ[varname]) + if num_workers < 0: + raise ValueError + except ValueError: + print(f'Invalid value in {varname}, using 1 thread.') + num_workers = 1 + + if num_workers == 0: + try: + # Fails in some weird environments such as Debian + # reproducible build. + num_workers = multiprocessing.cpu_count() + except Exception: + num_workers = 1 + return num_workers + def has_path_sep(name: str, sep: str = '/\\') -> bool: 'Checks if any of the specified @sep path separators are in @name' for each in sep: From 15c2c9811411e5e84419bcee487fb3a84ac2756c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 16 Nov 2024 15:27:44 +0100 Subject: [PATCH 092/624] introspect: add machine to target_sources Even though the "targets" introspection info already includes the command line arguments used to invoke the compiler, this is not enough to correlated with the "compilers" introspection info and get extra information from there. Together with the existing "language" key, adding a "machine" key is enough to identify completely an entry in the compilers info. Signed-off-by: Paolo Bonzini --- docs/markdown/IDE-integration.md | 8 ++++++++ docs/markdown/snippets/introspect_machine.md | 5 +++++ mesonbuild/ast/introspection.py | 3 ++- mesonbuild/backend/backends.py | 3 ++- mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/mintro.py | 1 + unittests/allplatformstests.py | 12 ++++++++++-- 7 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 docs/markdown/snippets/introspect_machine.md diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 77edb755d388..ce8d8b42e548 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -93,6 +93,7 @@ can provide code completion for all source files. ```json { "language": "language ID", + "machine": "build" / "host", "compiler": ["The", "compiler", "command"], "parameters": ["list", "of", "compiler", "parameters"], "sources": ["list", "of", "all", "source", "files", "for", "this", "language"], @@ -100,6 +101,13 @@ can provide code completion for all source files. } ``` +*(New in 1.7.0)* The `machine` and `language` keys make it possible to +to access further information about the compiler in the `compilers` +introspection information. `machine` can be absent if `language` is +`unknown`. In this case, information about the compiler is not +available; Meson is therefore unable to know if the output relates +to either the build of the host machine. + It should be noted that the compiler parameters stored in the `parameters` differ from the actual parameters used to compile the file. This is because the parameters are optimized for the usage in an diff --git a/docs/markdown/snippets/introspect_machine.md b/docs/markdown/snippets/introspect_machine.md new file mode 100644 index 000000000000..9b19bd6a20ca --- /dev/null +++ b/docs/markdown/snippets/introspect_machine.md @@ -0,0 +1,5 @@ +## "machine" entry in target introspection data + +The JSON data returned by `meson introspect --targets` now has a `machine` +entry in each `target_sources` block. The new entry can be one of `build` +or `host` for compiler-built targets, or absent for `custom_target` targets. diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 3e8d564e2fbf..6bc6286f24f3 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -283,7 +283,7 @@ def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[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'}} 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)} - for_machine = MachineChoice.HOST + for_machine = MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST objects: T.List[T.Any] = [] empty_sources: T.List[T.Any] = [] # Passing the unresolved sources list causes errors @@ -294,6 +294,7 @@ def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[BaseNode]: 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)), diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a4be50f664b1..d2c6a46965f6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -41,13 +41,14 @@ from ..linkers.linkers import StaticLinker from ..mesonlib import FileMode, FileOrString - from typing_extensions import TypedDict + from typing_extensions import TypedDict, NotRequired _ALL_SOURCES_TYPE = T.List[T.Union[File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]] class TargetIntrospectionData(TypedDict): language: str + machine: NotRequired[str] compiler: T.List[str] parameters: T.List[str] sources: T.List[str] diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 5716ea29e351..64921ffa65a8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -837,6 +837,7 @@ def create_target_source_introspection(self, target: build.Target, comp: compile # The new entry src_block = { 'language': lang, + 'machine': comp.for_machine.get_lower_case_name(), 'compiler': comp.get_exelist(), 'parameters': parameters, 'sources': [], diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 810a2b674b40..b9b09c557427 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -211,6 +211,7 @@ def nodes_to_paths(node_list: T.List[BaseNode]) -> T.List[Path]: 'build_by_default': i['build_by_default'], 'target_sources': [{ 'language': 'unknown', + 'machine': i['machine'], 'compiler': [], 'parameters': [], 'sources': [str(x) for x in sources], diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index b6a87af1fc7f..1b1e16928276 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3333,7 +3333,7 @@ def assertKeyTypes(key_type_list, obj, strict: bool = True): ('win_subsystem', (str, None)), ] - targets_sources_typelist = [ + targets_sources_unknown_lang_typelist = [ ('language', str), ('compiler', list), ('parameters', list), @@ -3342,6 +3342,10 @@ def assertKeyTypes(key_type_list, obj, strict: bool = True): ('unity_sources', (list, None)), ] + targets_sources_typelist = targets_sources_unknown_lang_typelist + [ + ('machine', str), + ] + target_sources_linker_typelist = [ ('linker', list), ('parameters', list), @@ -3456,7 +3460,10 @@ def assertKeyTypes(key_type_list, obj, strict: bool = True): targets_to_find.pop(i['name'], None) for j in i['target_sources']: if 'compiler' in j: - assertKeyTypes(targets_sources_typelist, j) + if j['language'] == 'unknown': + assertKeyTypes(targets_sources_unknown_lang_typelist, j) + else: + assertKeyTypes(targets_sources_typelist, j) self.assertEqual(j['sources'], [os.path.normpath(f) for f in tgt[4]]) else: assertKeyTypes(target_sources_linker_typelist, j) @@ -3558,6 +3565,7 @@ def test_introspect_targets_from_source(self): sources += j.get('sources', []) i['target_sources'] = [{ 'language': 'unknown', + 'machine': 'host', 'compiler': [], 'parameters': [], 'sources': sources, From ef612343d90a79a49b9116e4cf7afb07eaa08ab4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 08:50:55 +0100 Subject: [PATCH 093/624] scripts: make clang tools obey b_colorout Right now, the clang-tidy and clang-format targets use the program default and do not let b_colorout decide whether to colorize output. However, the wrappers that run the tool are going to be changed to buffer output, and that would disable colorization unconditionally. So pass a --color option to the tools and use it when building the command line. clang-format's -fcolor-diagnostics option simply does not work, and the "right" (or at least working) option is --color which is undocumented. --color is present all the way back to clang 10, but I digged into clang-format's source code to figure out what's happening. The problem is that -fcolor-diagnostics is a complete no-operation; in fact it is a bool that is initialized to true. gdb shows: (gdb) p ShowColors $2 = { = { ... > = {Value = true, ... }, ...} on entry to clang-format's main, meaning that specifying the option on the command line does nothing at all. To see how clang-format determines whether to use colors you need to look at enters SMDiagnostic::print, which simply does ColorMode Mode = ShowColors ? ColorMode::Auto : ColorMode::Disable; showing once more that in fact the option cannot force-on the colors ( -fno-color-diagnostics instead works). Continuing in SMDiagnostic::print, this RAII constructor would write the escape sequence to the terminal: WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, Mode); It ends up in WithColor::changeColor, which does if (colorsEnabled()) OS.changeColor(Color, Bold, BG); Digging further down, colorsEnabled() is where the Mode member is consulted: bool WithColor::colorsEnabled() { switch (Mode) { case ColorMode::Enable: return true; case ColorMode::Disable: return false; case ColorMode::Auto: return AutoDetectFunction(OS); } llvm_unreachable("All cases handled above."); } and the "AutoDetectFunction" is static bool DefaultAutoDetectFunction(const raw_ostream &OS) { return *UseColor == cl::BOU_UNSET ? OS.has_colors() : *UseColor == cl::BOU_TRUE; } UseColor is controlled by the "--color" option, so if that option was unset you go to OS.has_colors() even in the presence of -fcolor-diagnostics. This has been around for over 5 years in clang-format, and it was present even earlier, so use it in meson as well. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 3 +++ mesonbuild/scripts/clangformat.py | 13 +++++++++---- mesonbuild/scripts/clangtidy.py | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 64921ffa65a8..81b8bb51ab81 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3659,6 +3659,9 @@ def generate_clangtool(self, name: str, extra_arg: T.Optional[str] = None) -> No if extra_arg: target_name += f'-{extra_arg}' extra_args.append(f'--{extra_arg}') + colorout = self.environment.coredata.optstore.get_value('b_colorout') \ + if OptionKey('b_colorout') in self.environment.coredata.optstore else 'always' + 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)): return diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index 9ce050458986..88cc89071d7c 100644 --- a/mesonbuild/scripts/clangformat.py +++ b/mesonbuild/scripts/clangformat.py @@ -6,6 +6,7 @@ import argparse import subprocess from pathlib import Path +import sys from .run_tool import run_tool from ..environment import detect_clangformat @@ -13,12 +14,15 @@ from ..programs import ExternalProgram import typing as T -def run_clang_format(fname: Path, exelist: T.List[str], check: bool, cformat_ver: T.Optional[str]) -> subprocess.CompletedProcess: +def run_clang_format(fname: Path, exelist: T.List[str], options: argparse.Namespace, cformat_ver: T.Optional[str]) -> subprocess.CompletedProcess: clangformat_10 = False - if check and cformat_ver: + if options.check and cformat_ver: if version_compare(cformat_ver, '>=10'): clangformat_10 = True exelist = exelist + ['--dry-run', '--Werror'] + # The option is not documented but it exists in version 10 + if options.color == 'always' or options.color == 'auto' and sys.stdout.isatty(): + exelist += ['--color=1'] else: original = fname.read_bytes() before = fname.stat().st_mtime @@ -26,7 +30,7 @@ def run_clang_format(fname: Path, exelist: T.List[str], check: bool, cformat_ver after = fname.stat().st_mtime if before != after: print('File reformatted: ', fname) - if check and not clangformat_10: + if options.check and not clangformat_10: # Restore the original if only checking. fname.write_bytes(original) ret.returncode = 1 @@ -35,6 +39,7 @@ def run_clang_format(fname: Path, exelist: T.List[str], check: bool, cformat_ver def run(args: T.List[str]) -> int: parser = argparse.ArgumentParser() parser.add_argument('--check', action='store_true') + parser.add_argument('--color', default='always') parser.add_argument('sourcedir') parser.add_argument('builddir') options = parser.parse_args(args) @@ -52,4 +57,4 @@ def run(args: T.List[str]) -> int: else: cformat_ver = None - return run_tool('clang-format', srcdir, builddir, run_clang_format, exelist, options.check, cformat_ver) + return run_tool('clang-format', srcdir, builddir, run_clang_format, exelist, options, cformat_ver) diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index a922f8514062..fe34801e0e74 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -26,6 +26,7 @@ def run_clang_tidy(fname: Path, tidyexe: list, builddir: Path, fixesdir: T.Optio def run(args: T.List[str]) -> int: parser = argparse.ArgumentParser() parser.add_argument('--fix', action='store_true') + parser.add_argument('--color', default='always') parser.add_argument('sourcedir') parser.add_argument('builddir') options = parser.parse_args(args) @@ -38,6 +39,9 @@ def run(args: T.List[str]) -> int: print(f'Could not execute clang-tidy "{" ".join(tidyexe)}"') return 1 + if options.color == 'always' or options.color == 'auto' and sys.stdout.isatty(): + tidyexe += ['--use-color'] + fixesdir: T.Optional[Path] = None if options.fix: applyexe = detect_clangapply() From dafa6a7ac14f18e5a2529a2251fc6d552ea37547 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 Nov 2024 09:34:11 +0100 Subject: [PATCH 094/624] scripts: rename run_tool to run_clang_tool Differentiate from the "run_tool_on_targets" function that will be introduced in the next commit. Signed-off-by: Paolo Bonzini --- mesonbuild/scripts/clangformat.py | 4 ++-- mesonbuild/scripts/clangtidy.py | 4 ++-- mesonbuild/scripts/run_tool.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index 88cc89071d7c..f0d084f2b57f 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_tool +from .run_tool import run_clang_tool from ..environment import detect_clangformat from ..mesonlib import version_compare from ..programs import ExternalProgram @@ -57,4 +57,4 @@ def run(args: T.List[str]) -> int: else: cformat_ver = None - return run_tool('clang-format', srcdir, builddir, run_clang_format, exelist, options, cformat_ver) + return run_clang_tool('clang-format', srcdir, builddir, run_clang_format, exelist, options, cformat_ver) diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index fe34801e0e74..ab53bbd39bfb 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -11,7 +11,7 @@ import shutil import sys -from .run_tool import run_tool +from .run_tool import run_clang_tool from ..environment import detect_clangtidy, detect_clangapply import typing as T @@ -56,7 +56,7 @@ def run(args: T.List[str]) -> int: fixesdir.unlink() fixesdir.mkdir(parents=True) - tidyret = run_tool('clang-tidy', srcdir, builddir, run_clang_tidy, tidyexe, builddir, fixesdir) + tidyret = run_clang_tool('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/run_tool.py b/mesonbuild/scripts/run_tool.py index a84de15b12df..2cccb1b33120 100644 --- a/mesonbuild/scripts/run_tool.py +++ b/mesonbuild/scripts/run_tool.py @@ -27,7 +27,7 @@ def parse_pattern_file(fname: Path) -> T.List[str]: pass return patterns -def run_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., subprocess.CompletedProcess], *args: T.Any) -> int: +def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., subprocess.CompletedProcess], *args: T.Any) -> int: patterns = parse_pattern_file(srcdir / f'.{name}-include') globs: T.Union[T.List[T.List[Path]], T.List[T.Generator[Path, None, None]]] if patterns: From 5dc537afd051e60ff00731f73e02d98138d9198b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Nov 2024 11:13:04 +0100 Subject: [PATCH 095/624] scripts: convert run_tool to asyncio This improves the handling of keyboard interrupt, and also makes it easy to buffer the output and not mix errors from different subprocesses. This is useful for clang-tidy and will be used by clippy as well. In addition, the new code supports MESON_NUM_PROCESSES. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/num-processes.md | 3 +- mesonbuild/scripts/clangformat.py | 9 +- mesonbuild/scripts/clangtidy.py | 6 +- mesonbuild/scripts/run_tool.py | 120 +++++++++++++++++------- 4 files changed, 97 insertions(+), 41 deletions(-) diff --git a/docs/markdown/snippets/num-processes.md b/docs/markdown/snippets/num-processes.md index e4ffdd1dc5c1..c484d930f287 100644 --- a/docs/markdown/snippets/num-processes.md +++ b/docs/markdown/snippets/num-processes.md @@ -4,4 +4,5 @@ Previously, `meson test` checked the `MESON_TESTTHREADS` variable to control the amount of parallel jobs to run; this was useful when `meson test` is invoked through `ninja test` for example. With this version, a new variable `MESON_NUM_PROCESSES` is supported with a broader scope: in addition to -`meson test`, it is also used by the `external_project` module. +`meson test`, it is also used by the `external_project` module and by +Ninja targets that invoke `clang-tidy` and `clang-format`. diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index f0d084f2b57f..a3c19e9adbec 100644 --- a/mesonbuild/scripts/clangformat.py +++ b/mesonbuild/scripts/clangformat.py @@ -4,17 +4,16 @@ from __future__ import annotations import argparse -import subprocess from pathlib import Path import sys -from .run_tool import run_clang_tool +from .run_tool import run_clang_tool, run_with_buffered_output from ..environment import detect_clangformat from ..mesonlib import version_compare from ..programs import ExternalProgram import typing as T -def run_clang_format(fname: Path, exelist: T.List[str], options: argparse.Namespace, cformat_ver: T.Optional[str]) -> subprocess.CompletedProcess: +async def run_clang_format(fname: Path, exelist: T.List[str], options: argparse.Namespace, cformat_ver: T.Optional[str]) -> int: clangformat_10 = False if options.check and cformat_ver: if version_compare(cformat_ver, '>=10'): @@ -26,14 +25,14 @@ def run_clang_format(fname: Path, exelist: T.List[str], options: argparse.Namesp else: original = fname.read_bytes() before = fname.stat().st_mtime - ret = subprocess.run(exelist + ['-style=file', '-i', str(fname)]) + ret = await run_with_buffered_output(exelist + ['-style=file', '-i', str(fname)]) after = fname.stat().st_mtime if before != after: print('File reformatted: ', fname) if options.check and not clangformat_10: # Restore the original if only checking. fname.write_bytes(original) - ret.returncode = 1 + return 1 return ret def run(args: T.List[str]) -> int: diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index ab53bbd39bfb..550faeef354e 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -11,17 +11,17 @@ import shutil import sys -from .run_tool import run_clang_tool +from .run_tool import run_clang_tool, run_with_buffered_output from ..environment import detect_clangtidy, detect_clangapply import typing as T -def run_clang_tidy(fname: Path, tidyexe: list, builddir: Path, fixesdir: T.Optional[Path]) -> subprocess.CompletedProcess: +async def run_clang_tidy(fname: Path, tidyexe: list, builddir: Path, fixesdir: T.Optional[Path]) -> int: args = [] if fixesdir is not None: handle, name = tempfile.mkstemp(prefix=fname.name + '.', suffix='.yaml', dir=fixesdir) os.close(handle) args.extend(['-export-fixes', name]) - return subprocess.run(tidyexe + args + ['-quiet', '-p', str(builddir), str(fname)]) + return await run_with_buffered_output(tidyexe + args + ['-quiet', '-p', str(builddir), str(fname)]) def run(args: T.List[str]) -> int: parser = argparse.ArgumentParser() diff --git a/mesonbuild/scripts/run_tool.py b/mesonbuild/scripts/run_tool.py index 2cccb1b33120..bccc4cb833af 100644 --- a/mesonbuild/scripts/run_tool.py +++ b/mesonbuild/scripts/run_tool.py @@ -3,17 +3,85 @@ from __future__ import annotations -import itertools +import asyncio.subprocess import fnmatch -import concurrent.futures +import itertools +import signal +import sys from pathlib import Path +from .. import mlog from ..compilers import lang_suffixes -from ..mesonlib import quiet_git +from ..mesonlib import quiet_git, join_args, determine_worker_count +from ..mtest import complete_all import typing as T -if T.TYPE_CHECKING: - import subprocess +Info = T.TypeVar("Info") + +async def run_with_buffered_output(cmdlist: T.List[str]) -> 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, + stdin=asyncio.subprocess.DEVNULL, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT) + stdo, _ = await p.communicate() + except FileNotFoundError as e: + print(mlog.blue('>>>'), quoted_cmdline, file=sys.stderr) + print(mlog.red('not found:'), e.filename, file=sys.stderr) + return 1 + except asyncio.CancelledError: + if p: + p.kill() + await p.wait() + return p.returncode or 1 + else: + return 0 + + if stdo: + print(mlog.blue('>>>'), quoted_cmdline, flush=True) + sys.stdout.buffer.write(stdo) + return p.returncode + +async def _run_workers(infos: T.Iterable[Info], + fn: T.Callable[[Info], T.Iterable[T.Coroutine[None, None, int]]]) -> int: + futures: T.List[asyncio.Future[int]] = [] + semaphore = asyncio.Semaphore(determine_worker_count()) + + async def run_one(worker_coro: T.Coroutine[None, None, int]) -> int: + try: + async with semaphore: + return await worker_coro + except asyncio.CancelledError as e: + worker_coro.throw(e) + return await worker_coro + + def sigterm_handler() -> None: + for f in futures: + f.cancel() + + if sys.platform != 'win32': + loop = asyncio.get_running_loop() + loop.add_signal_handler(signal.SIGINT, sigterm_handler) + loop.add_signal_handler(signal.SIGTERM, sigterm_handler) + + for i in infos: + futures.extend((asyncio.ensure_future(run_one(x)) for x in fn(i))) + if not futures: + return 0 + + try: + await complete_all(futures) + except BaseException: + for f in futures: + f.cancel() + raise + + return max(f.result() for f in futures if f.done() and not f.cancelled()) def parse_pattern_file(fname: Path) -> T.List[str]: patterns = [] @@ -27,7 +95,7 @@ def parse_pattern_file(fname: Path) -> T.List[str]: pass return patterns -def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., subprocess.CompletedProcess], *args: T.Any) -> int: +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]]] if patterns: @@ -44,29 +112,17 @@ def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp'])) suffixes.add('h') suffixes = {f'.{s}' for s in suffixes} - futures = [] - returncode = 0 - e = concurrent.futures.ThreadPoolExecutor() - try: - for f in itertools.chain(*globs): - strf = str(f) - if f.is_dir() or f.suffix not in suffixes or \ - any(fnmatch.fnmatch(strf, i) for i in ignore): - continue - futures.append(e.submit(fn, f, *args)) - concurrent.futures.wait( - futures, - return_when=concurrent.futures.FIRST_EXCEPTION - ) - finally: - # We try to prevent new subprocesses from being started by canceling - # the futures, but this is not water-tight: some may have started - # between the wait being interrupted or exited and the futures being - # canceled. (A fundamental fix would probably require the ability to - # terminate such subprocesses upon cancellation of the future.) - for x in futures: # Python >=3.9: e.shutdown(cancel_futures=True) - x.cancel() - e.shutdown() - if futures: - returncode = max(x.result().returncode for x in futures) - return returncode + for f in itertools.chain.from_iterable(globs): + strf = str(f) + if f.is_dir() or f.suffix not in suffixes or \ + any(fnmatch.fnmatch(strf, i) for i in ignore): + continue + 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': + 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)) From 27c567de5d1807ac72708ea48018a21f0c6b8dd2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Nov 2024 11:20:56 +0100 Subject: [PATCH 096/624] scripts: add "clippy" internal tool Similar to the "ninja scan-build" target for C, add a clippy internal tool that runs clippy-driver on all crates in the project. The approach used is more efficient than with "ninja scan-build", and does not require rerunning Meson in a separate build directory; it uses the introspection data to find the compiler arguments for the target and invokes clippy-driver with a slightly modified command line. This could actually be applied to scan-build as well, reusing the run_tool_on_targets() function. Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/rust.py | 49 +++++++++++++++++++++++++ mesonbuild/scripts/clippy.py | 67 ++++++++++++++++++++++++++++++++++ mesonbuild/scripts/run_tool.py | 10 +++++ 3 files changed, 126 insertions(+) create mode 100644 mesonbuild/scripts/clippy.py diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 02ac593842ad..717d5635f842 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -33,6 +33,35 @@ 's': ['-C', 'opt-level=s'], } +def get_rustup_run_and_args(exelist: T.List[str]) -> T.Optional[T.Tuple[T.List[str], T.List[str]]]: + """Given the command for a rustc executable, check if it is invoked via + "rustup run" and if so separate the "rustup [OPTIONS] run TOOLCHAIN" + part from the arguments to rustc. If the returned value is not None, + other tools (for example clippy-driver or rustdoc) can be run by placing + the name of the tool between the two elements of the tuple.""" + e = iter(exelist) + try: + if os.path.basename(next(e)) != 'rustup': + return None + # minimum three strings: "rustup run TOOLCHAIN" + n = 3 + opt = next(e) + + # options come first + while opt.startswith('-'): + n += 1 + opt = next(e) + + # then "run TOOLCHAIN" + if opt != 'run': + return None + + next(e) + next(e) + return exelist[:n], list(e) + except StopIteration: + return None + class RustCompiler(Compiler): # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX @@ -65,6 +94,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic super().__init__([], exelist, version, for_machine, info, is_cross=is_cross, 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.add(OptionKey('b_vscrt')) @@ -252,6 +282,25 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> 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]: + if self.rustup_run_and_args: + rustup_exelist, args = self.rustup_run_and_args + # do not use extend so that exelist is copied + exelist = rustup_exelist + [name] + else: + exelist = [name] + args = self.exelist[1:] + + from ..programs import find_external_program + for prog in find_external_program(env, self.for_machine, exelist[0], exelist[0], + [exelist[0]], allow_default_for_cross=False): + exelist[0] = prog.path + break + else: + return [] + + return exelist + args + class ClippyRustCompiler(RustCompiler): diff --git a/mesonbuild/scripts/clippy.py b/mesonbuild/scripts/clippy.py new file mode 100644 index 000000000000..a5161462cb35 --- /dev/null +++ b/mesonbuild/scripts/clippy.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 The Meson development team + +from __future__ import annotations +from collections import defaultdict +import os +import tempfile +import typing as T + +from .run_tool import run_tool_on_targets, run_with_buffered_output +from .. import build, mlog +from ..mesonlib import MachineChoice, PerMachine + +if T.TYPE_CHECKING: + from ..compilers.rust import RustCompiler + +class ClippyDriver: + def __init__(self, build: build.Build, tempdir: str): + self.tools: PerMachine[T.List[str]] = PerMachine([], []) + self.warned: T.DefaultDict[str, bool] = defaultdict(lambda: False) + self.tempdir = tempdir + for machine in MachineChoice: + 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) + + def warn_missing_clippy(self, machine: str) -> None: + if self.warned[machine]: + return + mlog.warning(f'clippy-driver not found for {machine} machine') + self.warned[machine] = True + + def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, None, int]]: + for src_block in target['target_sources']: + if src_block['language'] == 'rust': + clippy = getattr(self.tools, src_block['machine']) + if not clippy: + self.warn_missing_clippy(src_block['machine']) + continue + + cmdlist = list(clippy) + prev = None + for arg in src_block['parameters']: + if prev: + prev = None + continue + elif arg in {'--emit', '--out-dir'}: + prev = arg + else: + cmdlist.append(arg) + + cmdlist.extend(src_block['sources']) + # the default for --emit is to go all the way to linking, + # and --emit dep-info= is not enough for clippy to do + # enough analysis, so use --emit metadata. + cmdlist.append('--emit') + cmdlist.append('metadata') + cmdlist.append('--out-dir') + cmdlist.append(self.tempdir) + yield run_with_buffered_output(cmdlist) + +def run(args: T.List[str]) -> int: + os.chdir(args[0]) + build_data = build.load(os.getcwd()) + with tempfile.TemporaryDirectory() as d: + return run_tool_on_targets(ClippyDriver(build_data, d)) diff --git a/mesonbuild/scripts/run_tool.py b/mesonbuild/scripts/run_tool.py index bccc4cb833af..e206ff7fe8d7 100644 --- a/mesonbuild/scripts/run_tool.py +++ b/mesonbuild/scripts/run_tool.py @@ -6,6 +6,7 @@ import asyncio.subprocess import fnmatch import itertools +import json import signal import sys from pathlib import Path @@ -126,3 +127,12 @@ def run_clang_tool(name: str, srcdir: Path, builddir: Path, fn: T.Callable[..., 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_tool_on_targets(fn: T.Callable[[T.Dict[str, T.Any]], + T.Iterable[T.Coroutine[None, None, int]]]) -> int: + if sys.platform == 'win32': + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + + with open('meson-info/intro-targets.json', encoding='utf-8') as fp: + targets = json.load(fp) + return asyncio.run(_run_workers(targets, fn)) From 5768ccba6eb77167da96712c20a4e042efb31d03 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 16 Nov 2024 16:05:05 +0100 Subject: [PATCH 097/624] ninjabackend: add support for "ninja clippy" Add a target that builds all crates that could be extern to others, and then reruns clippy. Signed-off-by: Paolo Bonzini --- docs/markdown/howtox.md | 20 ++++++++++++++++++++ docs/markdown/snippets/clippy.md | 5 +++++ docs/markdown/snippets/num-processes.md | 2 +- mesonbuild/backend/backends.py | 6 ++++++ mesonbuild/backend/ninjabackend.py | 22 +++++++++++++++++++++- unittests/allplatformstests.py | 18 ++++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/clippy.md diff --git a/docs/markdown/howtox.md b/docs/markdown/howtox.md index 4a57e8569137..ba6a3b8f8d63 100644 --- a/docs/markdown/howtox.md +++ b/docs/markdown/howtox.md @@ -239,6 +239,26 @@ And then pass it through the variable (remember to use absolute path): $ SCANBUILD=$(pwd)/my-scan-build.sh ninja -C builddir scan-build ``` +## Use clippy + +If your project includes Rust targets, you can invoke clippy like this: + +```console +$ meson setup builddir +$ ninja -C builddir clippy +``` + +Clippy will also obey the `werror` [builtin option](Builtin-options.md#core-options). + +By default Meson uses as many concurrent processes as there are cores +on the test machine. You can override this with the environment +variable `MESON_NUM_PROCESSES`. + +Meson will look for `clippy-driver` in the same directory as `rustc`, +or try to invoke it using `rustup` if `rustc` points to a `rustup` +binary. If `clippy-driver` is not detected properly, you can add it to +a [machine file](Machine-files.md). + ## Use profile guided optimization Using profile guided optimization with GCC is a two phase diff --git a/docs/markdown/snippets/clippy.md b/docs/markdown/snippets/clippy.md new file mode 100644 index 000000000000..14bcc77428a1 --- /dev/null +++ b/docs/markdown/snippets/clippy.md @@ -0,0 +1,5 @@ +## Meson can run "clippy" on Rust projects + +Meson now defines a `clippy` target if the project uses the Rust programming +language. The target runs clippy on all Rust sources, using the `clippy-driver` +program from the same Rust toolchain as the `rustc` compiler. diff --git a/docs/markdown/snippets/num-processes.md b/docs/markdown/snippets/num-processes.md index c484d930f287..5336377900ce 100644 --- a/docs/markdown/snippets/num-processes.md +++ b/docs/markdown/snippets/num-processes.md @@ -5,4 +5,4 @@ the amount of parallel jobs to run; this was useful when `meson test` is invoked through `ninja test` for example. With this version, a new variable `MESON_NUM_PROCESSES` is supported with a broader scope: in addition to `meson test`, it is also used by the `external_project` module and by -Ninja targets that invoke `clang-tidy` and `clang-format`. +Ninja targets that invoke `clang-tidy`, `clang-format` and `clippy`. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d2c6a46965f6..970fb82f2b4e 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2040,6 +2040,12 @@ def compiler_to_generator_args(self, target: build.BuildTarget, commands += [input] return commands + def have_language(self, langname: str) -> bool: + for for_machine in MachineChoice: + if langname in self.environment.coredata.compilers[for_machine]: + return True + return False + def compiler_to_generator(self, target: build.BuildTarget, compiler: 'Compiler', sources: _ALL_SOURCES_TYPE, diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 81b8bb51ab81..0c34e08deb39 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -463,6 +463,8 @@ class RustCrate: display_name: str root_module: str + crate_type: str + target_name: str edition: RUST_EDITIONS deps: T.List[RustDep] cfg: T.List[str] @@ -1878,6 +1880,7 @@ def __generate_sources_structure(self, root: Path, structured_sources: build.Str return orderdeps, first_file def _add_rust_project_entry(self, name: str, main_rust_file: str, args: CompilerArgs, + crate_type: str, target_name: str, from_subproject: bool, proc_macro_dylib_path: T.Optional[str], deps: T.List[RustDep]) -> None: raw_edition: T.Optional[str] = mesonlib.first(reversed(args), lambda x: x.startswith('--edition')) @@ -1895,6 +1898,8 @@ def _add_rust_project_entry(self, name: str, main_rust_file: str, args: Compiler len(self.rust_crates), name, main_rust_file, + crate_type, + target_name, edition, deps, cfg, @@ -2134,7 +2139,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): self._add_rust_project_entry(target.name, os.path.abspath(os.path.join(self.environment.build_dir, main_rust_file)), - args, + args, cratetype, target_name, bool(target.subproject), proc_macro_dylib_path, project_deps) @@ -3640,6 +3645,20 @@ def generate_dist(self) -> None: elem.add_item('pool', 'console') self.add_build(elem) + def generate_clippy(self) -> None: + if 'clippy' in self.all_outputs or not self.have_language('rust'): + return + + cmd = self.environment.get_build_command() + \ + ['--internal', 'clippy', self.environment.build_dir] + elem = self.create_phony_target('clippy', 'CUSTOM_COMMAND', 'PHONY') + elem.add_item('COMMAND', cmd) + elem.add_item('pool', 'console') + for crate in self.rust_crates.values(): + if crate.crate_type in {'rlib', 'dylib', 'proc-macro'}: + elem.add_dep(crate.target_name) + self.add_build(elem) + def generate_scanbuild(self) -> None: if not environment.detect_scanbuild(): return @@ -3707,6 +3726,7 @@ def generate_utils(self) -> None: self.generate_scanbuild() self.generate_clangformat() self.generate_clangtidy() + self.generate_clippy() self.generate_tags('etags', 'TAGS') self.generate_tags('ctags', 'ctags') self.generate_tags('cscope', 'cscope') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1b1e16928276..6544bcce19ff 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4887,6 +4887,24 @@ def output_name(name, type_): @skip_if_not_language('rust') @unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver') def test_rust_clippy(self) -> None: + if self.backend is not Backend.ninja: + raise unittest.SkipTest('Rust is only supported with ninja currently') + # When clippy is used, we should get an exception since a variable named + # "foo" is used, but is on our denylist + testdir = os.path.join(self.rust_test_dir, '1 basic') + self.init(testdir) + self.build('clippy') + + self.wipe() + self.init(testdir, extra_args=['--werror', '-Db_colorout=never']) + with self.assertRaises(subprocess.CalledProcessError) as cm: + self.build('clippy') + self.assertTrue('error: use of a blacklisted/placeholder name `foo`' in cm.exception.stdout or + 'error: use of a disallowed/placeholder name `foo`' in cm.exception.stdout) + + @skip_if_not_language('rust') + @unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver') + def test_rust_clippy_as_rustc(self) -> None: if self.backend is not Backend.ninja: raise unittest.SkipTest('Rust is only supported with ninja currently') # When clippy is used, we should get an exception since a variable named From 1f1a6d3a45ab968bbc28854c4733dc1303abb176 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 17 Nov 2024 08:40:22 +0100 Subject: [PATCH 098/624] rust: raise a warning if clippy is used instead of rustc clippy-driver is not meant to be a general-purpose compiler front-end. Since Meson can now provide natively the ability to invoke clippy, raise a warning if someone uses it that way. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/clippy.md | 3 +++ mesonbuild/compilers/detect.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/markdown/snippets/clippy.md b/docs/markdown/snippets/clippy.md index 14bcc77428a1..47d02083a0ab 100644 --- a/docs/markdown/snippets/clippy.md +++ b/docs/markdown/snippets/clippy.md @@ -3,3 +3,6 @@ Meson now defines a `clippy` target if the project uses the Rust programming language. The target runs clippy on all Rust sources, using the `clippy-driver` program from the same Rust toolchain as the `rustc` compiler. + +Using `clippy-driver` as the Rust compiler will now emit a warning, as it +is not meant to be a general-purpose compiler front-end. diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 7542fb6283a2..d88441dffcd7 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1042,6 +1042,10 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust version = search_version(out) cls = rust.ClippyRustCompiler + mlog.deprecation( + 'clippy-driver is not intended as a general purpose compiler. ' + 'You can use "ninja clippy" in order to run clippy on a ' + 'meson project.') if 'rustc' in out: # On Linux and mac rustc will invoke gcc (clang for mac From 6eac0158cd4e62ec835a5fa38fd619d6f3958353 Mon Sep 17 00:00:00 2001 From: LIU Hao Date: Mon, 16 Dec 2024 12:04:37 +0800 Subject: [PATCH 099/624] compilers: Do not pass `-fuse-ld=lld` via `-Wl,` `-fuse-ld=` is a driver option for selection of a linker; it shall not be passed to a linker with `-Wl,`. For the Microsoft compiler and linker, the options for the compiler and those for the linker are separated by `/LINK`, which looks like `cl /cl-options ... /link /link-options ...`. Formally, they are passed in the same command line. When Clang is invoking the Microsoft linker or a Microsoft-style linker (that is, LLD-LINK), every linker option has to prefixed by `-Wl,` or `-Xlink`. Previously, using Clang-CL and LLD-LINK, given: cc = meson.get_compiler('c') assert(cc.has_link_argument('/LTCG')) This code failed to detect the `/LTCG` option, because `-fuse-ld=lld` was passed to the linker, as an invalid option: Command line: `clang E:\lh_mouse\Desktop\t\build\meson-private\tmpg0221fee\testfile.c -o E:\lh_mouse\Desktop\t\build\meson-private\tmpg0221fee\output.exe -D_FILE_OFFSET_BITS=64 -O0 -Werror=implicit-function-declaration -Wl,-WX -Wl,/LTCG -Wl,-fuse-ld=lld` -> 4044 stdout: LINK : warning LNK4044: unrecognized option '/fuse-ld=lld'; ignored LINK : error LNK1218: warning treated as error; no output file generated However, it should be noted that not all LINK options can be passed with `-Wl,`. The `/subsystem:windows,6.1` option has a comma, which would be converted to a space. Therefore, this option must be passed as `-Xlinker -subsystem:windows,6.1`. This issue is not addressed in this commit. Signed-off-by: LIU Hao --- mesonbuild/compilers/mixins/clang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 41b35c041336..82df325ec01e 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -187,7 +187,7 @@ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T. def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]: if isinstance(self.linker, (ClangClDynamicLinker, MSVCDynamicLinker)): - return [flag if flag.startswith('-Wl,') else f'-Wl,{flag}' for flag in args] + return [flag if flag.startswith('-Wl,') or flag.startswith('-fuse-ld=') else f'-Wl,{flag}' for flag in args] else: return args From 303916dd5c8a5019c8f982c08d7d77d733a29f1b Mon Sep 17 00:00:00 2001 From: meator Date: Mon, 9 Dec 2024 20:04:36 +0100 Subject: [PATCH 100/624] compiler: fix leftover chars in compiler.links() The following log output: Checking if "strnstr() available" : links: NO now becomes: Checking if "strnstr() available" links: NO This is more consistent with the compiles() method. --- mesonbuild/interpreter/compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 90514446bb12..6f52c0e07ecc 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -587,7 +587,7 @@ def links_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileK compiler = clist[SUFFIX_TO_LANG[suffix]] extra_args = functools.partial(self._determine_args, kwargs) - deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False) + deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False, endl=None) result, cached = self.compiler.links(code, self.environment, compiler=compiler, extra_args=extra_args, From 1c8b523c86f9c2b9ed71104334a1bad8c41079cf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 9 Dec 2024 17:39:12 +0100 Subject: [PATCH 101/624] mtest: tap: accept out-of-order or partly-numbered tests Resolves: #13802 Signed-off-by: Paolo Bonzini --- mesonbuild/mtest.py | 23 ++++++++++++++---- unittests/taptests.py | 56 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 556451c5e6ab..d0added78f8c 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -326,6 +326,8 @@ class Version(T.NamedTuple): plan: T.Optional[Plan] = None lineno = 0 num_tests = 0 + last_test = 0 + highest_test = 0 yaml_lineno: T.Optional[int] = None yaml_indent = '' state = _MAIN @@ -396,10 +398,11 @@ def parse_line(self, line: T.Optional[str]) -> T.Iterator[TYPE_TAPResult]: yield self.Error('unexpected test after late plan') self.found_late_test = True self.num_tests += 1 - num = self.num_tests if m.group(2) is None else int(m.group(2)) - if num != self.num_tests: - yield self.Error('out of order test numbers') - yield from self.parse_test(m.group(1) == 'ok', num, + self.last_test = self.last_test + 1 if m.group(2) is None else int(m.group(2)) + self.highest_test = max(self.highest_test, self.last_test) + if self.plan and self.last_test > self.plan.num_tests: + yield self.Error('test number exceeds maximum specified in test plan') + yield from self.parse_test(m.group(1) == 'ok', self.last_test, m.group(3), m.group(4), m.group(5)) self.state = self._AFTER_TEST return @@ -449,11 +452,21 @@ def parse_line(self, line: T.Optional[str]) -> T.Iterator[TYPE_TAPResult]: if self.state == self._YAML: yield self.Error(f'YAML block not terminated (started on line {self.yaml_lineno})') - if not self.bailed_out and self.plan and self.num_tests != self.plan.num_tests: + if self.bailed_out: + return + + if self.plan and self.num_tests != self.plan.num_tests: if self.num_tests < self.plan.num_tests: yield self.Error(f'Too few tests run (expected {self.plan.num_tests}, got {self.num_tests})') else: yield self.Error(f'Too many tests run (expected {self.plan.num_tests}, got {self.num_tests})') + return + + if self.highest_test != self.num_tests: + if self.highest_test < self.num_tests: + yield self.Error(f'Duplicate test numbers (expected {self.num_tests}, got test numbered {self.highest_test}') + else: + yield self.Error(f'Missing test numbers (expected {self.num_tests}, got test numbered {self.highest_test}') class TestLogger: def flush(self) -> None: diff --git a/unittests/taptests.py b/unittests/taptests.py index 26d96eafdec4..e91194cb564c 100644 --- a/unittests/taptests.py +++ b/unittests/taptests.py @@ -163,10 +163,57 @@ def test_one_test_late_plan(self): self.assert_plan(events, num_tests=1, late=True) self.assert_last(events) + def test_low_max_early_plan(self): + events = self.parse_tap('1..2\nok 1\nok 1') + self.assert_plan(events, num_tests=2, late=False) + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_error(events) # incorrect high test number + self.assert_last(events) + + def test_low_max_late_plan(self): + events = self.parse_tap('ok 1\nok 1\n1..2') + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_plan(events, num_tests=2, late=True) + self.assert_error(events) # incorrect high test number + self.assert_last(events) + + def test_high_max_early_plan(self): + events = self.parse_tap('1..2\nok 2\nok 3') + self.assert_plan(events, num_tests=2, late=False) + self.assert_test(events, number=2, name='', result=TestResult.OK) + self.assert_error(events) # high id + self.assert_test(events, number=3, name='', result=TestResult.OK) + self.assert_error(events) # incorrect high test number + self.assert_last(events) + + def test_high_max_late_plan(self): + events = self.parse_tap('ok 2\nok 3\n1..2') + self.assert_test(events, number=2, name='', result=TestResult.OK) + self.assert_test(events, number=3, name='', result=TestResult.OK) + self.assert_plan(events, num_tests=2, late=True) + self.assert_error(events) + self.assert_last(events) + def test_out_of_order(self): + events = self.parse_tap('1..2\nok 2\nok 1') + self.assert_plan(events, num_tests=2, late=False) + self.assert_test(events, number=2, name='', result=TestResult.OK) + self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_last(events) + + def test_out_of_order_no_plan(self): events = self.parse_tap('ok 2') + self.assert_test(events, number=2, name='', result=TestResult.OK) self.assert_error(events) + + def test_out_of_order_missing_numbers(self): + events = self.parse_tap('1..3\nok 2\nok\nok 1') + self.assert_plan(events, num_tests=3, late=False) self.assert_test(events, number=2, name='', result=TestResult.OK) + self.assert_test(events, number=3, name='', result=TestResult.OK) + self.assert_test(events, number=1, name='', result=TestResult.OK) self.assert_last(events) def test_middle_plan(self): @@ -184,7 +231,7 @@ def test_too_many_plans(self): self.assert_test(events, number=1, name='', result=TestResult.OK) self.assert_last(events) - def test_too_many(self): + def test_too_many_late_plan(self): events = self.parse_tap('ok 1\nnot ok 2\n1..1') self.assert_test(events, number=1, name='', result=TestResult.OK) self.assert_test(events, number=2, name='', result=TestResult.FAIL) @@ -192,14 +239,16 @@ def test_too_many(self): self.assert_error(events) self.assert_last(events) + def test_too_many_early_plan(self): events = self.parse_tap('1..1\nok 1\nnot ok 2') self.assert_plan(events, num_tests=1, late=False) self.assert_test(events, number=1, name='', result=TestResult.OK) + self.assert_error(events) # test number too high self.assert_test(events, number=2, name='', result=TestResult.FAIL) - self.assert_error(events) + self.assert_error(events) # too many tests run self.assert_last(events) - def test_too_few(self): + def test_too_few_late_plan(self): events = self.parse_tap('ok 1\nnot ok 2\n1..3') self.assert_test(events, number=1, name='', result=TestResult.OK) self.assert_test(events, number=2, name='', result=TestResult.FAIL) @@ -207,6 +256,7 @@ def test_too_few(self): self.assert_error(events) self.assert_last(events) + def test_too_few_early_plan(self): events = self.parse_tap('1..3\nok 1\nnot ok 2') self.assert_plan(events, num_tests=3, late=False) self.assert_test(events, number=1, name='', result=TestResult.OK) From bb1745c614def21f4e44bf0be90a9520e8181794 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Tue, 10 Dec 2024 18:41:50 +0100 Subject: [PATCH 102/624] do @ variable substitution when parsing cmake format the upstream behavior of configure_file in cmake parses both @VAR@ and ${VAR} and only supports disabling the bracket variables through the `@ONLY` argument, meson needs to follow this behavior for compatibility --- mesonbuild/utils/universal.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index ea49a065a446..f26a9a3de8c6 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1257,6 +1257,9 @@ def variable_replace(match: T.Match[str]) -> str: 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) @@ -1358,11 +1361,15 @@ def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'm ''', re.VERBOSE) else: regex = re.compile(r''' - (?:\\\\)+(?=\\?\$) # Match multiple backslashes followed by a dollar sign + (?:\\\\)+(?=\\?(\$|@)) # 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[-a-zA-Z0-9_]+)} # Match a variable enclosed in curly braces and capture the variable name + \${(?P[-a-zA-Z0-9_]+)} # Match a variable enclosed in curly braces and capture the variable name + | # OR + (?[-a-zA-Z0-9_]+)@ # Match a variable enclosed in @ symbols and capture the variable name; no matches beginning with '\@' + | # OR + (?P\\@[-a-zA-Z0-9_]+\\@) # Match an escaped variable enclosed in @ symbols ''', re.VERBOSE) return regex From 38c8568ec34e90a9744ca88c1450d3365039b634 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Tue, 10 Dec 2024 18:50:25 +0100 Subject: [PATCH 103/624] add test case for @ and curly brace substitution with cmake format --- test cases/common/14 configure file/config10.h.in | 4 ++++ test cases/common/14 configure file/meson.build | 10 ++++++++++ test cases/common/14 configure file/prog10.c | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 test cases/common/14 configure file/config10.h.in create mode 100644 test cases/common/14 configure file/prog10.c diff --git a/test cases/common/14 configure file/config10.h.in b/test cases/common/14 configure file/config10.h.in new file mode 100644 index 000000000000..6c0661a6ae9e --- /dev/null +++ b/test cases/common/14 configure file/config10.h.in @@ -0,0 +1,4 @@ +/* Should both be the same */ +#define MESSAGE1 "@var@" +#define MESSAGE2 "${var}" + diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build index 036a562b796c..3a4ff4dc9f40 100644 --- a/test cases/common/14 configure file/meson.build +++ b/test cases/common/14 configure file/meson.build @@ -331,6 +331,16 @@ configure_file(output : 'config9b.h', test('test9', executable('prog9', 'prog9.c')) +# Test @ and curly braces at the same time with cmake format +conf10 = configuration_data() +conf10.set('var', 'foo') +configure_file( + input : 'config10.h.in', + output : '@BASENAME@', + format : 'cmake', + configuration : conf10) +test('test10', executable('prog10', 'prog10.c')) + check_inputs = find_program('check_inputs.py') configure_file(output : 'check_inputs.txt', input : ['prog.c', files('prog2.c', 'prog4.c')], diff --git a/test cases/common/14 configure file/prog10.c b/test cases/common/14 configure file/prog10.c new file mode 100644 index 000000000000..cdca6dae43b2 --- /dev/null +++ b/test cases/common/14 configure file/prog10.c @@ -0,0 +1,7 @@ +#include +#include + +int main(void) { + return strcmp(MESSAGE1, "foo") + || strcmp(MESSAGE2, "foo"); +} From 7a4647b194f925f386aad0c8fe5f95ff20edb0c7 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Wed, 11 Dec 2024 19:20:37 +0100 Subject: [PATCH 104/624] replace cmake configuration workaround with a straight copy --- test cases/frameworks/7 gnome/mkenums/meson.build | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test cases/frameworks/7 gnome/mkenums/meson.build b/test cases/frameworks/7 gnome/mkenums/meson.build index 284231f2f83c..36b73d24561f 100644 --- a/test cases/frameworks/7 gnome/mkenums/meson.build +++ b/test cases/frameworks/7 gnome/mkenums/meson.build @@ -140,12 +140,9 @@ test('enum test 5', enumexe5) # Generate template then use as input to mkenums -# Simple trick to copy the file without substitutions, can be -# removed when https://github.com/mesonbuild/meson/pull/3383 is fixed gen_h_template = configure_file(input: 'enums.h.in', output: 'enums6.h.in', - configuration: configuration_data(), - format: 'cmake') + copy: true) enums_h6 = gnome.mkenums('enums6', sources : 'meson-sample.h', From e56eea7476c60aff1f10a87fdad3e86cd810d92d Mon Sep 17 00:00:00 2001 From: Sam James Date: Thu, 19 Dec 2024 19:19:38 +0000 Subject: [PATCH 105/624] ci: gentoo: include sys-devel/gcc[d], bindgen, rust[clippy,rustfmt] Signed-off-by: Sam James --- ci/ciimage/gentoo/install.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ci/ciimage/gentoo/install.sh b/ci/ciimage/gentoo/install.sh index caf21a1fad69..7849ae3c257b 100755 --- a/ci/ciimage/gentoo/install.sh +++ b/ci/ciimage/gentoo/install.sh @@ -20,8 +20,7 @@ pkgs_stable=( dev-lang/vala dev-lang/python:2.7 dev-java/openjdk-bin - # requires rustfmt, bin rebuild (TODO: file bug) - #dev-util/bindgen + dev-util/bindgen dev-libs/elfutils dev-util/gdbus-codegen @@ -104,8 +103,16 @@ mkdir /etc/portage/binrepos.conf || true mkdir /etc/portage/profile || true cat <<-EOF > /etc/portage/package.use/ci dev-cpp/gtkmm X - + dev-lang/rust clippy rustfmt + dev-lang/rust-bin clippy rustfmt dev-libs/boost python + + # 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-13 ada objc objc++ + sys-devel/gcc pgo lto + sys-libs/zlib static-libs EOF From 21614ac00bbec87eefaddb76cd7a25413d3f9752 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 19 Dec 2024 18:00:44 -0500 Subject: [PATCH 106/624] Revert "CI: Windows: downgrade rust to 1.77" This reverts commit 1da230c9e0f66cf7ecf5a9f5fed70e74e15418ac. Should be fixed now in upstream rustc. --- ci/run.ps1 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ci/run.ps1 b/ci/run.ps1 index d3fda2d8b70e..d781cf725fce 100644 --- a/ci/run.ps1 +++ b/ci/run.ps1 @@ -8,22 +8,21 @@ if ($LastExitCode -ne 0) { $env:Path = ($env:Path.Split(';') | Where-Object { $_ -notmatch 'mingw|Strawberry|Chocolatey|PostgreSQL' }) -join ';' if ($env:arch -eq 'x64') { - rustup default 1.77 # Rust puts its shared stdlib in a secret place, but it is needed to run tests. - $env:Path += ";$HOME/.rustup/toolchains/1.77-x86_64-pc-windows-msvc/bin" + $env:Path += ";$HOME/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin" } elseif ($env:arch -eq 'x86') { # Switch to the x86 Rust toolchain - rustup default 1.77-i686-pc-windows-msvc + rustup default stable-i686-pc-windows-msvc + + # Also install clippy + rustup component add clippy # Rust puts its shared stdlib in a secret place, but it is needed to run tests. - $env:Path += ";$HOME/.rustup/toolchains/1.77-i686-pc-windows-msvc/bin" + $env:Path += ";$HOME/.rustup/toolchains/stable-i686-pc-windows-msvc/bin" # Need 32-bit Python for tests that need the Python dependency $env:Path = "C:\hostedtoolcache\windows\Python\3.7.9\x86;C:\hostedtoolcache\windows\Python\3.7.9\x86\Scripts;$env:Path" } -# Also install clippy -rustup component add clippy - # Set the CI env var for the meson test framework $env:CI = '1' From 79fc8941c5d3855ebd76aad90550081e455cc50f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 19 Dec 2024 16:28:44 -0800 Subject: [PATCH 107/624] modules/rust: Specify the compiler version to bindgen when possible bindgen by default may output code that an older rustc cannot successfully consume. To avoid this, we check if bindgen supports the rustc version we're using, if so, and if the user didn't set the `--rust-target` option, we will supply it to ensure that bindgen writes out code our rustc can use. If it does not support that version explicitly, we leave it at the default, assuming that our compiler version is newer than bindgen. --- mesonbuild/modules/rust.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 60a58698c858..5072e503ec9e 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -62,6 +62,10 @@ class RustModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None + if 'rust' in interpreter.compilers.host: + self._bindgen_rust_target: T.Optional[str] = interpreter.compilers.host['rust'].version + else: + self._bindgen_rust_target = None self.methods.update({ 'test': self.test, 'bindgen': self.bindgen, @@ -250,6 +254,15 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu if self._bindgen_bin is None: self._bindgen_bin = state.find_program('bindgen', wanted=kwargs['bindgen_version']) + if self._bindgen_rust_target is not None: + # ExternalCommand.command's type is bonkers + _, _, err = mesonlib.Popen_safe( + T.cast('T.List[str]', self._bindgen_bin.get_command()) + + ['--rust-target', self._bindgen_rust_target]) + # Sometimes this is "invalid Rust target" and sometimes "invalid + # rust target" + if 'Got an invalid' in err: + self._bindgen_rust_target = None name: str if isinstance(header, File): @@ -317,9 +330,13 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu '@INPUT@', '--output', os.path.join(state.environment.build_dir, '@OUTPUT0@') ] + \ - kwargs['args'] + inline_wrapper_args + ['--'] + \ - kwargs['c_args'] + clang_args + \ - ['-MD', '-MQ', '@INPUT@', '-MF', '@DEPFILE@'] + kwargs['args'] + inline_wrapper_args + if self._bindgen_rust_target and '--rust-target' not in cmd: + cmd.extend(['--rust-target', self._bindgen_rust_target]) + cmd.append('--') + cmd.extend(kwargs['c_args']) + cmd.extend(clang_args) + cmd.extend(['-MD', '-MQ', '@INPUT@', '-MF', '@DEPFILE@']) target = CustomTarget( f'rustmod-bindgen-{name}'.replace('/', '_'), From e9e582020898e52d17b4c48afe65589bc1dee065 Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Tue, 17 Dec 2024 14:41:17 +0100 Subject: [PATCH 108/624] print out which command needs an exe_wrapper It's not the first time I run into an issue with an intentionally missing exe_wrapper during cross compilation. In pretty much all the cases the project I tried to build already had code available to not need one in the first place. Print out what command was actually the culprit to make debugging this easier. --- mesonbuild/backend/backends.py | 2 +- test cases/failing/126 generator host binary/test.json | 5 ++++- unittests/linuxcrosstests.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 970fb82f2b4e..d5bda3b421f5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -574,7 +574,7 @@ def get_executable_serialisation( is_cross_built = not self.environment.machines.matches_build_machine(exe_for_machine) if is_cross_built and self.environment.need_exe_wrapper(): if not self.environment.has_exe_wrapper(): - msg = 'An exe_wrapper is needed but was not found. Please define one ' \ + msg = 'An exe_wrapper is needed for ' + exe_cmd[0] + ' but was not found. Please define one ' \ 'in cross file and check the command and/or add it to PATH.' raise MesonException(msg) exe_wrapper = self.environment.get_exe_wrapper() diff --git a/test cases/failing/126 generator host binary/test.json b/test cases/failing/126 generator host binary/test.json index 7e354d60dcad..c633622cf2f8 100644 --- a/test cases/failing/126 generator host binary/test.json +++ b/test cases/failing/126 generator host binary/test.json @@ -1,5 +1,8 @@ { "stdout": [ - { "line": "ERROR: An exe_wrapper is needed but was not found. Please define one in cross file and check the command and/or add it to PATH." } + { + "match": "re", + "line": "ERROR: An exe_wrapper is needed for .* but was not found. Please define one in cross file and check the command and/or add it to PATH." + } ] } diff --git a/unittests/linuxcrosstests.py b/unittests/linuxcrosstests.py index a35633cdd79b..910429b0e9c7 100644 --- a/unittests/linuxcrosstests.py +++ b/unittests/linuxcrosstests.py @@ -146,7 +146,7 @@ def test_exe_wrapper_behaviour(self): self.meson_cross_files = [os.path.join(testdir, 'broken-cross.txt')] # Force tracebacks so we can detect them properly env = {'MESON_FORCE_BACKTRACE': '1'} - error_message = "An exe_wrapper is needed but was not found. Please define one in cross file and check the command and/or add it to PATH." + error_message = "An exe_wrapper is needed for " + self.builddir + "/prog.exe but was not found. Please define one in cross file and check the command and/or add it to PATH." with self.assertRaises(MesonException) as cm: # Must run in-process or we'll get a generic CalledProcessError From 0025805e303623386f1134b5f5f646bb40b00186 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera Date: Sat, 21 Dec 2024 01:28:15 +0800 Subject: [PATCH 109/624] Prioritise Apple's toolchain in project-tests-appleclang Using Homebrew Clang leads to failures when testing objfw. We probably want to ensure we use Apple Clang here given the workflow job name, in any case. --- .github/workflows/macos.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 88acbef90206..f5a87c957160 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -110,7 +110,9 @@ jobs: # These cannot evaluate anything, so we cannot set PATH or SDKROOT here run: | export SDKROOT="$(xcodebuild -version -sdk macosx Path)" - export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:/opt/homebrew/opt/ncurses/bin:$PATH" + # Append LLVM's bin directory to PATH to prioritise Apple Clang over Homebrew Clang. + # 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 From 61eac6a05a0fe99965cdb88163672aa9ab7f77e7 Mon Sep 17 00:00:00 2001 From: Sam James Date: Wed, 25 Dec 2024 01:51:48 +0000 Subject: [PATCH 110/624] test cases: fix '8 flex' with C23 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With C23 (as upcoming GCC 15 will default to), `void yyerror()` is the same as `void yyerror(void)`, i.e. `yyerror` takes no arguments. Fix the prototype given we *do* call it with an error string: ``` pgen.p/parser.tab.c: In function ‘yyparse’: pgen.p/parser.tab.c:1104:7: error: too many arguments to function ‘yyerror’ 1104 | yyerror (YY_("syntax error")); | ^~~~~~~ ../test cases/frameworks/8 flex/parser.y:3:12: note: declared here 3 | extern int yyerror(); | ^~~~~~~ pgen.p/parser.tab.c:1215:3: error: too many arguments to function ‘yyerror’ 1215 | yyerror (YY_("memory exhausted")); | ^~~~~~~ ../test cases/frameworks/8 flex/parser.y:3:12: note: declared here 3 | extern int yyerror(); | ^~~~~~~ ``` Bug: https://bugs.gentoo.org/946625 --- test cases/frameworks/8 flex/lexer.l | 4 ++-- test cases/frameworks/8 flex/parser.y | 2 +- test cases/frameworks/8 flex/prog.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test cases/frameworks/8 flex/lexer.l b/test cases/frameworks/8 flex/lexer.l index ca6513cb81c1..23a5f4869c6e 100644 --- a/test cases/frameworks/8 flex/lexer.l +++ b/test cases/frameworks/8 flex/lexer.l @@ -3,11 +3,11 @@ #include "parser.tab.h" extern int yylex(void); -extern int yyerror(); +extern int yyerror(char *s); %} %option noyywrap nounput noinput %% ("true"|"false") {return BOOLEAN;} -. { yyerror(); } +. { yyerror("Invalid value"); } diff --git a/test cases/frameworks/8 flex/parser.y b/test cases/frameworks/8 flex/parser.y index 663f2f3cf186..ba8004efdf79 100644 --- a/test cases/frameworks/8 flex/parser.y +++ b/test cases/frameworks/8 flex/parser.y @@ -1,6 +1,6 @@ %{ extern int yylex(void); -extern int yyerror(); +extern int yyerror(char *s); %} %token BOOLEAN diff --git a/test cases/frameworks/8 flex/prog.c b/test cases/frameworks/8 flex/prog.c index ae481d098327..840a0644ae81 100644 --- a/test cases/frameworks/8 flex/prog.c +++ b/test cases/frameworks/8 flex/prog.c @@ -24,7 +24,7 @@ int yywrap(void) { return 0; } -int yyerror(void) { - printf("Parse error\n"); +int yyerror(char* s) { + printf("Parse error: %s\n", s); exit(1); } From 1f6ce53f773d00ab728bda6ae0634076430ce5de Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 26 Nov 2024 20:31:19 +0530 Subject: [PATCH 111/624] arglist: De-dup arg prefixes only when they are used as a prefix This was already done for dedup2_prefixes, also do it for dedup1_prefixes, and move export-dynamic to dedup1_args, where it belongs. Also modify some comments around this to clearly distinguish standalone argument matching and argument-prefix matching. --- mesonbuild/arglist.py | 13 +++++++------ mesonbuild/compilers/mixins/clike.py | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/mesonbuild/arglist.py b/mesonbuild/arglist.py index 54d7157e2ccf..34456581f780 100644 --- a/mesonbuild/arglist.py +++ b/mesonbuild/arglist.py @@ -29,7 +29,7 @@ class Dedup(enum.Enum): same is true for include paths and library paths with -I and -L. UNIQUE - Arguments that once specified cannot be undone, such as `-c` or `-pipe`. New instances of these can be completely skipped. - NO_DEDUP - Whether it matters where or how many times on the command-line + NO_DEDUP - When it matters where or how many times on the command-line a particular argument is present. This can matter for symbol resolution in static or shared libraries, so we cannot de-dup or reorder them. @@ -74,12 +74,12 @@ class CompilerArgs(T.MutableSequence[str]): # Arg prefixes that override by prepending instead of appending prepend_prefixes: T.Tuple[str, ...] = () - # Arg prefixes and args that must be de-duped by returning 2 + # Arg prefixes and standalone args that must be de-duped by returning 2 dedup2_prefixes: T.Tuple[str, ...] = () dedup2_suffixes: T.Tuple[str, ...] = () dedup2_args: T.Tuple[str, ...] = () - # Arg prefixes and args that must be de-duped by returning 1 + # Arg prefixes and standalone args that must be de-duped by returning 1 # # NOTE: not thorough. A list of potential corner cases can be found in # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 @@ -193,15 +193,16 @@ def _can_dedup(cls, arg: str) -> Dedup: with other linkers. """ - # A standalone argument must never be deduplicated because it is - # defined by what comes _after_ it. Thus deduping this: + # Argument prefixes that are actually not used as a prefix must never + # be deduplicated because they are defined by what comes _after_ them. + # Thus deduping this: # -D FOO -D BAR # would yield either # -D FOO BAR # or # FOO -D BAR # both of which are invalid. - if arg in cls.dedup2_prefixes: + if arg in cls.dedup1_prefixes or arg in cls.dedup2_prefixes: return Dedup.NO_DEDUP if arg in cls.dedup2_args or \ arg.startswith(cls.dedup2_prefixes) or \ diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index d56547b475ed..0324885333f8 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -53,9 +53,9 @@ class CLikeCompilerArgs(arglist.CompilerArgs): # NOTE: not thorough. A list of potential corner cases can be found in # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 - dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic') + dedup1_prefixes = ('-l', '-Wl,-l') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') - dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread') + dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread', '-Wl,--export-dynamic') def to_native(self, copy: bool = False) -> T.List[str]: # This seems to be allowed, but could never work? From a00467268549bce29e07cb435033b879ece939c3 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 26 Nov 2024 20:36:14 +0530 Subject: [PATCH 112/624] compilers: De-dup -Wl,-rpath, and -Wl,-rpath-link, When followed by a comma, we can be absolutely sure that these are argument prefixes, and will not consume the next argument to form a single argument. Fixes spammy warnings on apple clang: `ld: warning: duplicate -rpath 'build/dist/darwin_universal/arm64/lib/pkgconfig/../../lib' ignored` Continuation from https://github.com/mesonbuild/meson/pull/13819 --- mesonbuild/compilers/mixins/clike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 0324885333f8..a75cd2a81bf0 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -53,7 +53,7 @@ class CLikeCompilerArgs(arglist.CompilerArgs): # NOTE: not thorough. A list of potential corner cases can be found in # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038 - dedup1_prefixes = ('-l', '-Wl,-l') + dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,-rpath,', '-Wl,-rpath-link,') dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread', '-Wl,--export-dynamic') From f070670e77fe36ece1a89ce00c13419de354b9ce Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Thu, 23 Feb 2023 10:00:21 -0600 Subject: [PATCH 113/624] dependencies: add custom atomic dependency Almost exactly the same as how the dl dependency works. On certain systems (like BSDs that use clang), stdatomic is provided by compiler-rt and doesn't need a separate library explictly linked. On a typical GNU/LINUX system, atomic is a separate library that must be explictly found and linked against. So just add a builtin and system method for these two use cases. --- docs/markdown/Dependencies.md | 10 ++++++++ docs/markdown/snippets/atomic-dependency.md | 9 +++++++ mesonbuild/dependencies/__init__.py | 1 + mesonbuild/dependencies/misc.py | 28 +++++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 docs/markdown/snippets/atomic-dependency.md diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 93f75d5a916b..2e255b209822 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -317,6 +317,16 @@ dep = dependency('appleframeworks', modules : 'foundation') These dependencies can never be found for non-OSX hosts. +## atomic (stdatomic) + +*(added 1.7.0)* + +Provides access to the atomic operations library. This first attempts +to look for a valid atomic external library before trying to fallback +to what is provided by the C runtime libraries. + +`method` may be `auto`, `builtin` or `system`. + ## Blocks Enable support for Clang's blocks extension. diff --git a/docs/markdown/snippets/atomic-dependency.md b/docs/markdown/snippets/atomic-dependency.md new file mode 100644 index 000000000000..2eef5e0b1cb0 --- /dev/null +++ b/docs/markdown/snippets/atomic-dependency.md @@ -0,0 +1,9 @@ +## New custom dependency for atomic + +``` +dependency('atomic') +``` + +checks for the availability of the atomic operation library. First, it looks +for the atomic library. If that is not found, then it will try to use what is +provided by the libc. diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index a3eb6623f02c..95e606975a5e 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -220,6 +220,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. 'shaderc': 'misc', 'iconv': 'misc', 'intl': 'misc', + 'atomic': 'misc', 'dl': 'misc', 'openssl': 'misc', 'libcrypto': 'misc', diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 4815e1c18cd9..7bfe198e0179 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -51,6 +51,27 @@ def netcdf_factory(env: 'Environment', packages['netcdf'] = netcdf_factory +class AtomicBuiltinDependency(BuiltinDependency): + def __init__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any]): + 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 ', env)[0]: + self.is_found = True + + +class AtomicSystemDependency(SystemDependency): + def __init__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any]): + 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) + + 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]): super().__init__(name, env, kwargs) @@ -564,6 +585,13 @@ def shaderc_factory(env: 'Environment', packages['shaderc'] = shaderc_factory +packages['atomic'] = atomic_factory = DependencyFactory( + 'atomic', + [DependencyMethods.SYSTEM, DependencyMethods.BUILTIN], + system_class=AtomicSystemDependency, + builtin_class=AtomicBuiltinDependency, +) + packages['cups'] = cups_factory = DependencyFactory( 'cups', [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK, DependencyMethods.CMAKE], From 08a345758a96892f879b487ee12e3b4e66c33439 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 15:41:40 +0100 Subject: [PATCH 114/624] compilers: cache the results of is_source() is_source() is called almost 900000 times in a QEMU setup. Together with the previously added caching, this basically removes _determine_ext_objs() from the profile when building QEMU. Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/compilers/compilers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 603a3eb484de..8788df0be05d 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -133,11 +133,15 @@ def is_header(fname: 'mesonlib.FileOrString') -> bool: def is_source_suffix(suffix: str) -> bool: return suffix in source_suffixes +@lru_cache(maxsize=None) +def cached_is_source_by_name(fname: str) -> bool: + suffix = fname.split('.')[-1].lower() + return is_source_suffix(suffix) + def is_source(fname: 'mesonlib.FileOrString') -> bool: if isinstance(fname, mesonlib.File): fname = fname.fname - suffix = fname.split('.')[-1].lower() - return is_source_suffix(suffix) + return cached_is_source_by_name(fname) def is_assembly(fname: 'mesonlib.FileOrString') -> bool: if isinstance(fname, mesonlib.File): @@ -152,14 +156,14 @@ def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool: return suffix in llvm_ir_suffixes @lru_cache(maxsize=None) -def cached_by_name(fname: 'mesonlib.FileOrString') -> bool: +def cached_is_object_by_name(fname: str) -> bool: suffix = fname.split('.')[-1] return suffix in obj_suffixes def is_object(fname: 'mesonlib.FileOrString') -> bool: if isinstance(fname, mesonlib.File): fname = fname.fname - return cached_by_name(fname) + return cached_is_object_by_name(fname) def is_library(fname: 'mesonlib.FileOrString') -> bool: if isinstance(fname, mesonlib.File): From a9d7e676e463a30741e9d0df1cfd837e6191df0d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 16:47:23 +0200 Subject: [PATCH 115/624] utils: cache build directory files get_target_generated_sources often calls File.from_built_relative on the same file, if it is used by many sources. This is a somewhat expensive call both CPU- and memory-wise, so cache the creation of build-directory files as well. Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/utils/universal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index f26a9a3de8c6..dc327373a759 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -402,13 +402,15 @@ def from_source_file(source_root: str, subdir: str, fname: str) -> 'File': return File(False, subdir, fname) @staticmethod + @lru_cache(maxsize=None) def from_built_file(subdir: str, fname: str) -> 'File': return File(True, subdir, fname) @staticmethod + @lru_cache(maxsize=None) def from_built_relative(relative: str) -> 'File': dirpart, fnamepart = os.path.split(relative) - return File(True, dirpart, fnamepart) + return File.from_built_file(dirpart, fnamepart) @staticmethod def from_absolute_file(fname: str) -> 'File': From c9635daeba4c7de7c4a230d6c9088f94f06ee8dc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 16:47:23 +0200 Subject: [PATCH 116/624] ninjabackend: use File.from_built_relative() Do not reinvent it in NinjaBackend.determine_ext_objs(), so as to use the recently added caching of the results of File.from_built_relative(). Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/backend/backends.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d5bda3b421f5..fb912b1d83d0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -883,8 +883,7 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects', proj_dir_to_buil for gensrc in extobj.genlist: for r in gensrc.get_outputs(): path = self.get_target_generated_dir(extobj.target, gensrc, r) - dirpart, fnamepart = os.path.split(path) - raw_sources.append(File(True, dirpart, fnamepart)) + raw_sources.append(File.from_built_relative(path)) # Filter out headers and all non-source files sources: T.List['FileOrString'] = [] From 3b36cb2c2c5e2978cc38283e4aa5d71ab5da4335 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Nov 2024 12:15:09 +0100 Subject: [PATCH 117/624] ninjabackend: prefer "in" to regex search Regexes can be surprisingly slow. This small change brings ninja_quote() from 12 to 3 seconds when building QEMU. Before: ncalls tottime percall cumtime percall 3734443 4.872 0.000 11.944 0.000 After: ncalls tottime percall cumtime percall 3595590 3.193 0.000 3.196 0.000 Signed-off-by: Paolo Bonzini Signed-off-by: Eli Schwartz --- mesonbuild/backend/ninjabackend.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0c34e08deb39..7244ea90b84f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -123,13 +123,6 @@ def get_rsp_threshold() -> int: NINJA_QUOTE_VAR_PAT = re.compile(r"[$ \n]") def ninja_quote(text: str, is_build_line: bool = False) -> str: - if is_build_line: - quote_re = NINJA_QUOTE_BUILD_PAT - else: - quote_re = NINJA_QUOTE_VAR_PAT - # Fast path for when no quoting is necessary - if not quote_re.search(text): - return text if '\n' in text: errmsg = f'''Ninja does not support newlines in rules. The content was: @@ -137,7 +130,12 @@ def ninja_quote(text: str, is_build_line: bool = False) -> str: Please report this error with a test case to the Meson bug tracker.''' raise MesonException(errmsg) - return quote_re.sub(r'$\g<0>', text) + + quote_re = NINJA_QUOTE_BUILD_PAT if is_build_line else NINJA_QUOTE_VAR_PAT + if ' ' in text or '$' in text or (is_build_line and ':' in text): + return quote_re.sub(r'$\g<0>', text) + + return text @dataclass From 631cce71db8dbae9f2330d48cb429a7ccec77135 Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Sun, 29 Dec 2024 11:08:37 +0100 Subject: [PATCH 118/624] Fail gracefully when compiler cache is specified without compiler With CC=ccache meson ... meson crashes with [...] File "/usr/lib/python3.10/site-packages/mesonbuild/compilers/detect.py", line 364, in _detect_c_or_cpp_compiler compiler_name = os.path.basename(compiler[0]) IndexError: list index out of range Improve this by throwing an EnvironmentException to fail gracefully when no compiler is specified. Fixes #9933 Fixes #13589 --- mesonbuild/envconfig.py | 13 ++++++++----- unittests/failuretests.py | 5 +++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 86bad9be23ee..c99ae4b016b0 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -438,16 +438,19 @@ def detect_compiler_cache() -> T.List[str]: @classmethod def parse_entry(cls, entry: T.Union[str, T.List[str]]) -> T.Tuple[T.List[str], T.List[str]]: - compiler = mesonlib.stringlistify(entry) + parts = mesonlib.stringlistify(entry) # Ensure ccache exists and remove it if it doesn't - if compiler[0] == 'ccache': - compiler = compiler[1:] + if parts[0] == 'ccache': + compiler = parts[1:] ccache = cls.detect_ccache() - elif compiler[0] == 'sccache': - compiler = compiler[1:] + elif parts[0] == 'sccache': + compiler = parts[1:] ccache = cls.detect_sccache() else: + compiler = parts ccache = [] + if not compiler: + raise EnvironmentException(f'Compiler cache specified without compiler: {parts[0]}') # Return value has to be a list of compiler 'choices' return compiler, ccache diff --git a/unittests/failuretests.py b/unittests/failuretests.py index 8a802120b6f3..e5a3b35ea05e 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -381,3 +381,8 @@ def test_override_resolved_dependency(self): def test_error_func(self): self.assertMesonRaises("error('a', 'b', ['c', ['d', {'e': 'f'}]], 'g')", r"Problem encountered: a b \['c', \['d', {'e' : 'f'}\]\] g") + + def test_compiler_cache_without_compiler(self): + self.assertMesonRaises('', + 'Compiler cache specified without compiler: ccache', + override_envvars={'CC': 'ccache'}) From 1cda655c552cd717bef23f901e372eebd5285542 Mon Sep 17 00:00:00 2001 From: KaruroChori Date: Sun, 5 Jan 2025 08:30:00 +0000 Subject: [PATCH 119/624] Fix wrap.py to avoid following symlinks --- mesonbuild/wrap/wrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 7aae1663fd1f..9af1f39efaae 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -870,4 +870,4 @@ def copy_tree(self, root_src_dir: str, root_dst_dir: str) -> None: except PermissionError: os.chmod(dst_file, stat.S_IWUSR) os.remove(dst_file) - shutil.copy2(src_file, dst_dir) + shutil.copy2(src_file, dst_dir, follow_symlinks=False) From 3741a1d173bd189bdf1b29f46e684bb668e71d5c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 11:37:12 -0800 Subject: [PATCH 120/624] docs: fix cxx -> cpp --- docs/markdown/Design-rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Design-rationale.md b/docs/markdown/Design-rationale.md index 67fec0ab36d2..41339794d542 100644 --- a/docs/markdown/Design-rationale.md +++ b/docs/markdown/Design-rationale.md @@ -236,7 +236,7 @@ Above we mentioned precompiled headers as a feature not supported by other build systems. Here's how you would use them. ```meson -project('pch demo', 'cxx') +project('pch demo', 'cpp') executable('myapp', 'myapp.cpp', pch : 'pch/myapp.hh') ``` From 4379ca1ca729f2ccc7793d4e234bf8e078caa68d Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Mon, 12 Aug 2024 13:13:52 +0200 Subject: [PATCH 121/624] ci: add qt6 QML libraries in Linux images These packages are required to test `qml_module` from qt --- ci/ciimage/arch/install.sh | 2 +- ci/ciimage/fedora/install.sh | 1 + ci/ciimage/opensuse/install.sh | 1 + ci/ciimage/ubuntu-rolling/install.sh | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh index 8f5245149688..a6ef5e0a918e 100755 --- a/ci/ciimage/arch/install.sh +++ b/ci/ciimage/arch/install.sh @@ -14,7 +14,7 @@ pkgs=( itstool glib2-devel gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz doxygen vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools libwmf cmake netcdf-fortran openmpi nasm gnustep-base gettext - python-lxml hotdoc rust-bindgen qt6-base qt6-tools wayland wayland-protocols + python-lxml hotdoc rust-bindgen qt6-base qt6-tools qt6-declarative wayland wayland-protocols # cuda ) diff --git a/ci/ciimage/fedora/install.sh b/ci/ciimage/fedora/install.sh index 8d818bf12c9d..aa8765561d1e 100755 --- a/ci/ciimage/fedora/install.sh +++ b/ci/ciimage/fedora/install.sh @@ -14,6 +14,7 @@ pkgs=( #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 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 ) diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index fdfedcb1bf2e..102bc121f83a 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -13,6 +13,7 @@ pkgs=( #hdf5-devel netcdf-devel libscalapack2-openmpi3-devel libscalapack2-gnu-openmpi3-hpc-devel openmpi3-devel doxygen vulkan-devel vulkan-validationlayers openssh mercurial gtk-sharp3-complete gtk-sharp2-complete libpcap-devel libgpgme-devel libqt5-qtbase-devel libqt5-qttools-devel libqt5-linguist libqt5-qtbase-private-headers-devel + 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 diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index 2066944e4cf5..212ed020ace5 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -12,6 +12,7 @@ pkgs=( python3-pip libxml2-dev libxslt1-dev libyaml-dev libjson-glib-dev wget unzip qt5-qmake qtbase5-dev qtchooser qtbase5-dev-tools clang + qmake6 qt6-base-dev qt6-base-private-dev qt6-declarative-dev qt6-declarative-dev-tools qt6-l10n-tools qt6-base-dev-tools libomp-dev llvm lcov dub ldc From a4cb40bde488cc548e39fbd2e536e25a833f2484 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 7 Jan 2025 18:40:30 +0200 Subject: [PATCH 122/624] Only use uninstalled deps if a build dir exists. --- mesonbuild/dependencies/pkgconfig.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index c6e6a5e4f2ca..447b69ea070f 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -258,9 +258,12 @@ def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables: key = OptionKey('pkg_config_path', machine=self.for_machine) extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:] if uninstalled: - uninstalled_path = Path(self.env.get_build_dir(), 'meson-uninstalled').as_posix() - if uninstalled_path not in extra_paths: - extra_paths.insert(0, uninstalled_path) + bpath = self.env.get_build_dir() + if bpath is not None: + # uninstalled can only be used if a build dir exists. + uninstalled_path = Path(bpath, 'meson-uninstalled').as_posix() + if uninstalled_path not in extra_paths: + extra_paths.insert(0, uninstalled_path) env.set('PKG_CONFIG_PATH', extra_paths) sysroot = self.env.properties[self.for_machine].get_sys_root() if sysroot: From b7260e803151654d0ca83f033defeaa20e07bb68 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 7 Jan 2025 10:12:01 -0500 Subject: [PATCH 123/624] Fix internal dependency names Internal dependency names were generated from object id. This cause problem when objects are copied, especially when generating partial dependency, or when extracting shared or static dependencies from both_library, because dependency names in target and dependencies introspection files become unrelated. This fixes that by generating the dependency name from the internal id, and by using that base name when generating partial dependencies. --- mesonbuild/build.py | 3 ++- mesonbuild/dependencies/base.py | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 35f1f24a42f8..ca4e5d5a7fa0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1364,7 +1364,8 @@ def add_deps(self, deps): [], dep.get_compile_args(), dep.get_link_args(), - [], [], [], [], [], {}, [], [], []) + [], [], [], [], [], {}, [], [], [], + dep.name) self.external_deps.append(extpart) # Deps of deps. self.add_deps(dep.ext_deps) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index ed6138a7ee0b..9c05419b9e09 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -111,7 +111,7 @@ def __init__(self, type_name: DependencyTypeName, kwargs: T.Dict[str, T.Any]) -> # 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 - self.name = f'dep{id(self)}' + self.name = f'dep{self._id}' self.version: T.Optional[str] = None self.language: T.Optional[str] = None # None means C-like self.is_found = False @@ -278,7 +278,8 @@ def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T extra_files: T.Sequence[mesonlib.File], ext_deps: T.List[Dependency], variables: T.Dict[str, str], d_module_versions: T.List[T.Union[str, int]], d_import_dirs: T.List['IncludeDirs'], - objects: T.List['ExtractedObjects']): + objects: T.List['ExtractedObjects'], + name: T.Optional[str] = None): super().__init__(DependencyTypeName('internal'), {}) self.version = version self.is_found = True @@ -296,6 +297,8 @@ def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T self.d_features['versions'] = d_module_versions if d_import_dirs: self.d_features['import_dirs'] = d_import_dirs + if name: + self.name = name def __deepcopy__(self, memo: T.Dict[int, 'InternalDependency']) -> 'InternalDependency': result = self.__class__.__new__(self.__class__) @@ -335,7 +338,7 @@ def get_partial_dependency(self, *, compile_args: bool = False, return InternalDependency( self.version, final_includes, final_compile_args, final_link_args, final_libraries, final_whole_libraries, - final_sources, final_extra_files, final_deps, self.variables, [], [], []) + final_sources, final_extra_files, final_deps, self.variables, [], [], [], self.name) def get_include_dirs(self) -> T.List['IncludeDirs']: return self.include_directories From 836bfb93c4fca7132885b727849315ec19cedfc8 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Mon, 6 Jan 2025 11:09:58 +0000 Subject: [PATCH 124/624] CI: Use a tagged version of cygwin-install-action Use a tagged version of cygwin-install-action, rather than whatever happens to be master at the moment. This should help isolate meson CI from random breakage there. --- .github/workflows/cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index d819f802f1fe..441637c154f7 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v4 - - uses: cygwin/cygwin-install-action@master + - uses: cygwin/cygwin-install-action@v5 with: platform: ${{ matrix.ARCH }} packages: | From 0310ab6c6db1938fd94345bbe8c54bf6c33b90fd Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 12 Nov 2024 15:41:52 -0500 Subject: [PATCH 125/624] fix missing extension in command path On Windows, if the native file contains a path without the extension, the executable may be found, but custom target would fail because that path does not exist. --- mesonbuild/programs.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 9ad38e126b60..46d00ceca2dd 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -55,6 +55,15 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None, if ret: self.command = ret + args else: + if os.path.isabs(cmd) and not os.path.exists(cmd): + # Maybe the name is an absolute path to a native Windows + # executable, but without the extension. This is technically wrong, + # but many people do it because it works in the MinGW shell. + for ext in self.windows_exts: + trial_ext = f'{cmd}.{ext}' + if os.path.exists(trial_ext): + cmd = trial_ext + break self.command = [cmd] + args else: if search_dirs is None: From c66a89fdca79d29d8799b470bed392b0b80f4d00 Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:50:40 +0100 Subject: [PATCH 126/624] Add run0 as a way to raise privileges --- docs/markdown/Installing.md | 2 +- mesonbuild/minstall.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Installing.md b/docs/markdown/Installing.md index 2d18c178fccd..0a7ca9f16a49 100644 --- a/docs/markdown/Installing.md +++ b/docs/markdown/Installing.md @@ -115,7 +115,7 @@ running `sudo meson install` will drop permissions and rebuild out of date targets as the original user, not as root. *(since 1.1.0)* Re-invoking as root will try to guess the user's preferred method for -re-running commands as root. The order of precedence is: sudo, doas, pkexec +re-running commands as root. The order of precedence is: sudo, doas, run0, pkexec (polkit). An elevation tool can be forced by setting `$MESON_ROOT_CMD`. ## DESTDIR support diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 860826bf1b84..0bb1691ebc61 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -564,7 +564,12 @@ def do_install(self, datafilename: str) -> None: if is_windows() or destdir != '' or not os.isatty(sys.stdout.fileno()) or not os.isatty(sys.stderr.fileno()): # can't elevate to root except in an interactive unix environment *and* when not doing a destdir install raise - rootcmd = os.environ.get('MESON_ROOT_CMD') or shutil.which('sudo') or shutil.which('doas') + rootcmd = ( + os.environ.get('MESON_ROOT_CMD') + or shutil.which('sudo') + or shutil.which('doas') + or shutil.which('run0') + ) pkexec = shutil.which('pkexec') if rootcmd is None and pkexec is not None and 'PKEXEC_UID' not in os.environ: rootcmd = pkexec From 29a26ea8175ebc9af66efa40191d9efe906e089b Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Mon, 9 Dec 2024 16:18:50 +0100 Subject: [PATCH 127/624] tests: Avoid modifying '17 prebuild shared' test dir Tests can tread on each other's toes when parallelism is high enough. In this case, `test_prebuilt_shared_lib` creates an object file in the `17 prebuilt shared` test dir. `test_prebuilt_shared_lib_rpath_same_prefix` uses `shutil.copytree` to copy that same test dir to a temporary location. If the former test cleans up its object file while `copytree` is running, the copy can fail with a fatal ENOENT `shutil.Error`. Use `copy_srcdir` to prevent this from happening. --- unittests/allplatformstests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 6544bcce19ff..b5338b834a75 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1766,7 +1766,7 @@ def build_shared_lib(self, compiler, source, objectfile, outfile, impfile, extra def test_prebuilt_shared_lib(self): (cc, _, object_suffix, shared_suffix) = self.detect_prebuild_env() - tdir = os.path.join(self.unit_test_dir, '17 prebuilt shared') + tdir = self.copy_srcdir(os.path.join(self.unit_test_dir, '17 prebuilt shared')) source = os.path.join(tdir, 'alexandria.c') objectfile = os.path.join(tdir, 'alexandria.' + object_suffix) impfile = os.path.join(tdir, 'alexandria.lib') From f9d70016fc134d03bdfe541455809c71cc867e79 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 23 Nov 2024 19:19:12 +0100 Subject: [PATCH 128/624] Revert "check if test 33 running under MinGW" This reverts commit 93f59313e10090bfe6058fabbb235aa549f933c0. --- test cases/common/33 run program/check-mingw.py | 10 ---------- test cases/common/33 run program/meson.build | 7 ++----- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100755 test cases/common/33 run program/check-mingw.py diff --git a/test cases/common/33 run program/check-mingw.py b/test cases/common/33 run program/check-mingw.py deleted file mode 100755 index f10c28ba1a5c..000000000000 --- a/test cases/common/33 run program/check-mingw.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -import os, sys, re - -if 'MSYSTEM' in os.environ and os.environ['MSYSTEM'] != '': - print(os.environ['MSYSTEM']) -else: - match = re.search(r'[\\/](mingw32|mingw64|clang32|clang64|clangarm64|ucrt64)[\\/]', sys.executable, flags=re.IGNORECASE) - if match: - print(match.group(1).upper()) diff --git a/test cases/common/33 run program/meson.build b/test cases/common/33 run program/meson.build index 1cd530523357..2257d93c7fc1 100644 --- a/test cases/common/33 run program/meson.build +++ b/test cases/common/33 run program/meson.build @@ -1,9 +1,6 @@ project('run command', version : run_command('get-version.py', check : true).stdout().strip(), meson_version: '>=0.1.0') -check_mingw = run_command('check-mingw.py', check : true).stdout().strip() -is_mingw = not (check_mingw == '' or check_mingw == 'MSYS') - -if build_machine.system() == 'windows' and not is_mingw +if build_machine.system() == 'windows' c = run_command('cmd', '/c', 'echo', 'hello', check: false) else c = run_command('echo', 'hello', check: false) @@ -48,7 +45,7 @@ endif # We should be able to have files() in argument f = files('meson.build') -if build_machine.system() == 'windows' and not is_mingw +if build_machine.system() == 'windows' c = run_command('cmd', '/c', 'echo', f, check: false) else c = run_command('echo', f, check: false) From f782a1f118b19a90d100d25fa10391930cf39b3f Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 23 Nov 2024 19:19:27 +0100 Subject: [PATCH 129/624] Revert "make the testsuite handle MSYS2's addition of cmd" This reverts commit 3c38e4720f7df0a9d0138363a9a995b31e848401. --- unittests/windowstests.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/unittests/windowstests.py b/unittests/windowstests.py index c5b0ba08d306..8448ab1649cc 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -54,20 +54,20 @@ def test_find_program(self): PATH to point to a directory with Python scripts. ''' testdir = os.path.join(self.platform_test_dir, '8 find program') - # Find `xcopy` and `xcopy.exe` - prog1 = ExternalProgram('xcopy') - self.assertTrue(prog1.found(), msg='xcopy not found') - prog2 = ExternalProgram('xcopy.exe') - self.assertTrue(prog2.found(), msg='xcopy.exe not found') + # Find `cmd` and `cmd.exe` + prog1 = ExternalProgram('cmd') + self.assertTrue(prog1.found(), msg='cmd not found') + prog2 = ExternalProgram('cmd.exe') + self.assertTrue(prog2.found(), msg='cmd.exe not found') self.assertPathEqual(prog1.get_path(), prog2.get_path()) - # Find xcopy.exe with args without searching - prog = ExternalProgram('xcopy', command=['xcopy', '/?']) - self.assertTrue(prog.found(), msg='xcopy not found with args') - self.assertPathEqual(prog.get_command()[0], 'xcopy') - # Find xcopy with an absolute path that's missing the extension - xcopy_path = prog2.get_path()[:-4] - prog = ExternalProgram(xcopy_path) - self.assertTrue(prog.found(), msg=f'{xcopy_path!r} not found') + # Find cmd.exe with args without searching + prog = ExternalProgram('cmd', command=['cmd', '/C']) + self.assertTrue(prog.found(), msg='cmd not found with args') + self.assertPathEqual(prog.get_command()[0], 'cmd') + # Find cmd with an absolute path that's missing the extension + cmd_path = prog2.get_path()[:-4] + prog = ExternalProgram(cmd_path) + self.assertTrue(prog.found(), msg=f'{cmd_path!r} not found') # Finding a script with no extension inside a directory works prog = ExternalProgram(os.path.join(testdir, 'test-script')) self.assertTrue(prog.found(), msg='test-script not found') From 5b3914629884c4e3977ed4c97323b5c5b4ce9563 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 7 Jan 2025 13:23:01 -0800 Subject: [PATCH 130/624] CI: remove codeql It rarely finds real issues that other scanners (pylint, flake8, mypy) don't find, and gives lots of useless and annoying errors that clutter discussions --- .github/codeql/codeql-config.yml | 5 ----- .github/codeql/lgtm.qls | 4 ---- .github/workflows/codeql-analysis.yml | 32 --------------------------- 3 files changed, 41 deletions(-) delete mode 100644 .github/codeql/codeql-config.yml delete mode 100644 .github/codeql/lgtm.qls delete mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml deleted file mode 100644 index 9b144885f41f..000000000000 --- a/.github/codeql/codeql-config.yml +++ /dev/null @@ -1,5 +0,0 @@ -queries: - - uses: ./.github/codeql/lgtm.qls - -paths-ignore: - - 'test cases' diff --git a/.github/codeql/lgtm.qls b/.github/codeql/lgtm.qls deleted file mode 100644 index 9befc76ead95..000000000000 --- a/.github/codeql/lgtm.qls +++ /dev/null @@ -1,4 +0,0 @@ -# for some reason this doesn't work by default any way I can see - -- import: codeql-suites/python-lgtm.qls - from: codeql/python-queries diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 6a78a36c62a2..000000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - analyze: - # lgtm.com does not run in forks, for good reason - if: github.repository == 'mesonbuild/meson' - name: Analyze - runs-on: ubuntu-latest - permissions: - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - # bypass cache: https://github.com/github/codeql-action/issues/1445 - tools: linked - config-file: .github/codeql/codeql-config.yml - languages: python - # we have none - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 From dfe5cbb3e432bb632731f853df05e2023ab233d6 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 6 Jan 2025 22:22:01 +0200 Subject: [PATCH 131/624] Store commands as arrays. --- mesonbuild/interpreter/interpreter.py | 6 +- mesonbuild/utils/universal.py | 83 ++++++++++++++++----------- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d717485e8a1e..4933ba65a1c2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1967,9 +1967,9 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs: else: vcs = mesonlib.detect_vcs(source_dir) if vcs: - mlog.log('Found {} repository at {}'.format(vcs['name'], vcs['wc_dir'])) - vcs_cmd = vcs['get_rev'].split() - regex_selector = vcs['rev_regex'] + mlog.log('Found {} repository at {}'.format(vcs.name, vcs.wc_dir)) + vcs_cmd = vcs.get_rev + regex_selector = vcs.rev_regex else: vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command... diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index dc327373a759..eede9fd2ddab 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -24,6 +24,7 @@ import pickle import errno import json +import dataclasses from mesonbuild import mlog from .core import MesonException, HoldableObject @@ -756,40 +757,50 @@ def windows_detect_native_arch() -> str: raise EnvironmentException('Unable to detect native OS architecture') return arch -def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[T.Dict[str, str]]: +@dataclasses.dataclass +class VcsData: + name: str + cmd: str + repo_dir: str + get_rev: T.List[str] + rev_regex: str + dep: str + wc_dir: T.Optional[str] = None + +def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[VcsData]: vcs_systems = [ - { - 'name': 'git', - 'cmd': 'git', - 'repo_dir': '.git', - 'get_rev': 'git describe --dirty=+ --always', - 'rev_regex': '(.*)', - 'dep': '.git/logs/HEAD' - }, - { - 'name': 'mercurial', - 'cmd': 'hg', - 'repo_dir': '.hg', - 'get_rev': 'hg id -i', - 'rev_regex': '(.*)', - 'dep': '.hg/dirstate' - }, - { - 'name': 'subversion', - 'cmd': 'svn', - 'repo_dir': '.svn', - 'get_rev': 'svn info', - 'rev_regex': 'Revision: (.*)', - 'dep': '.svn/wc.db' - }, - { - 'name': 'bazaar', - 'cmd': 'bzr', - 'repo_dir': '.bzr', - 'get_rev': 'bzr revno', - 'rev_regex': '(.*)', - 'dep': '.bzr' - }, + VcsData( + name = 'git', + cmd = 'git', + repo_dir = '.git', + get_rev = ['git', 'describe', '--dirty=+', '--always'], + rev_regex = '(.*)', + dep = '.git/logs/HEAD', + ), + VcsData( + name = 'mercurial', + cmd = 'hg', + repo_dir = '.hg', + get_rev = ['hg', 'id', '-i'], + rev_regex = '(.*)', + dep= '.hg/dirstate', + ), + VcsData( + name = 'subversion', + cmd = 'svn', + repo_dir = '.svn', + get_rev = ['svn', 'info'], + rev_regex = 'Revision: (.*)', + dep = '.svn/wc.db', + ), + VcsData( + name = 'bazaar', + cmd = 'bzr', + repo_dir = '.bzr', + get_rev = ['bzr', 'revno'], + rev_regex = '(.*)', + dep = '.bzr', + ), ] if isinstance(source_dir, str): source_dir = Path(source_dir) @@ -800,8 +811,10 @@ def detect_vcs(source_dir: T.Union[str, Path]) -> T.Optional[T.Dict[str, str]]: parent_paths_and_self.appendleft(source_dir) for curdir in parent_paths_and_self: for vcs in vcs_systems: - if Path.is_dir(curdir.joinpath(vcs['repo_dir'])) and shutil.which(vcs['cmd']): - vcs['wc_dir'] = str(curdir) + repodir = vcs.repo_dir + cmd = vcs.cmd + if curdir.joinpath(repodir).is_dir() and shutil.which(cmd): + vcs.wc_dir = str(curdir) return vcs return None From a05135fe94383e82147442d701f18c49dfc4ef00 Mon Sep 17 00:00:00 2001 From: gerioldman Date: Sun, 8 Oct 2023 18:30:53 +0200 Subject: [PATCH 132/624] Add TASKING to docs --- docs/markdown/Builtin-options.md | 1 + docs/markdown/Reference-tables.md | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index f16a46ffebea..a14f99f29a62 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -223,6 +223,7 @@ available on all platforms or with all compilers: | b_staticpic | true | true, false | Build static libraries as position independent | | b_pie | false | true, false | Build position-independent executables (since 0.49.0) | | b_vscrt | from_buildtype | none, md, mdd, mt, mtd, from_buildtype, static_from_buildtype | VS runtime library to use (since 0.48.0) (static_from_buildtype since 0.56.0) | +| b_tasking_mil_link | false | true, false | Use MIL linking for the TASKING VX-tools compiler family (since 1.?.?) | The value of `b_sanitize` can be one of: `none`, `address`, `thread`, `undefined`, `memory`, `leak`, `address,undefined`, but note that some diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 0cf8bcfd73a2..7f8e5af2cf5b 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -49,6 +49,11 @@ These are return values of the `get_id` (Compiler family) and | armasm | Microsoft Macro Assembler for ARM and AARCH64 (Since 0.64.0) | | | mwasmarm | Metrowerks Assembler for Embedded ARM | | | mwasmeppc | Metrowerks Assembler for Embedded PowerPC | | +| cctc | TASKING VX-toolset for TriCore compiler | | +| ccarm | TASKING VX-toolset for ARM compiler | | +| cc51 | TASKING VX-toolset for 8051 compiler | | +| ccmsc | TASKING VX-toolset for MCS compiler | | +| ccpcp | TASKING VX-toolset for PCP compiler | | ## Linker ids @@ -80,6 +85,11 @@ These are return values of the `get_linker_id` method in a compiler object. | ccomp | CompCert used as the linker driver | | mwldarm | The Metrowerks Linker with the ARM interface, used with mwccarm only | | mwldeppc | The Metrowerks Linker with the PowerPC interface, used with mwcceppc only | +| ltc | TASKING VX-toolset for TriCore linker | +| lkarm | TASKING VX-toolset for ARM linker | +| lk51 | TASKING VX-toolset for 8051 linker | +| lmsc | TASKING VX-toolset for MCS linker | +| lpcp | TASKING VX-toolset for PCP linker | For languages that don't have separate dynamic linkers such as C# and Java, the `get_linker_id` will return the compiler name. @@ -139,6 +149,7 @@ set in the cross file. | wasm64 | 64 bit Webassembly | | x86 | 32 bit x86 processor | | x86_64 | 64 bit x86 processor | +| tricore | Tricore 32 bit processor | Any cpu family not listed in the above list is not guaranteed to From 62c5db2cb3809e584bd4faef5c0a9d112e36a29c Mon Sep 17 00:00:00 2001 From: gerioldman Date: Sat, 23 Sep 2023 00:33:39 +0200 Subject: [PATCH 133/624] Add TASKING compiler support --- docs/markdown/Builtin-options.md | 2 +- docs/markdown/Reference-tables.md | 2 +- mesonbuild/backend/ninjabackend.py | 99 +++++++++++++++++-- mesonbuild/build.py | 2 + mesonbuild/compilers/c.py | 25 +++++ mesonbuild/compilers/compilers.py | 8 ++ mesonbuild/compilers/detect.py | 43 +++++++++ mesonbuild/compilers/mixins/tasking.py | 126 +++++++++++++++++++++++++ mesonbuild/envconfig.py | 1 + mesonbuild/linkers/base.py | 1 + mesonbuild/linkers/linkers.py | 96 +++++++++++++++++++ 11 files changed, 397 insertions(+), 8 deletions(-) create mode 100644 mesonbuild/compilers/mixins/tasking.py diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index a14f99f29a62..8d1772a43d20 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -223,7 +223,7 @@ available on all platforms or with all compilers: | b_staticpic | true | true, false | Build static libraries as position independent | | b_pie | false | true, false | Build position-independent executables (since 0.49.0) | | b_vscrt | from_buildtype | none, md, mdd, mt, mtd, from_buildtype, static_from_buildtype | VS runtime library to use (since 0.48.0) (static_from_buildtype since 0.56.0) | -| b_tasking_mil_link | false | true, false | Use MIL linking for the TASKING VX-tools compiler family (since 1.?.?) | +| b_tasking_mil_link | false | true, false | Use MIL linking for the TASKING VX-tools compiler family (since 1.4.0) | The value of `b_sanitize` can be one of: `none`, `address`, `thread`, `undefined`, `memory`, `leak`, `address,undefined`, but note that some diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 7f8e5af2cf5b..9db23a0c0311 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -52,7 +52,7 @@ These are return values of the `get_id` (Compiler family) and | cctc | TASKING VX-toolset for TriCore compiler | | | ccarm | TASKING VX-toolset for ARM compiler | | | cc51 | TASKING VX-toolset for 8051 compiler | | -| ccmsc | TASKING VX-toolset for MCS compiler | | +| ccmcs | TASKING VX-toolset for MCS compiler | | | ccpcp | TASKING VX-toolset for PCP compiler | | ## Linker ids diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7244ea90b84f..991f96edc81f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -243,7 +243,7 @@ def _quoter(x: NinjaCommandArg, qf: T.Callable[[str], str] = quote_func) -> str: def write(self, outfile: T.TextIO) -> None: rspfile_args = self.args rspfile_quote_func: T.Callable[[str], str] - if self.rspfile_quote_style is RSPFileSyntax.MSVC: + if self.rspfile_quote_style in {RSPFileSyntax.MSVC, RSPFileSyntax.TASKING}: rspfile_quote_func = cmd_quote rspfile_args = [NinjaCommandArg('$in_newline', arg.quoting) if arg.s == '$in' else arg for arg in rspfile_args] else: @@ -258,7 +258,10 @@ def rule_iter() -> T.Iterable[str]: for rsp in rule_iter(): outfile.write(f'rule {self.name}{rsp}\n') if rsp == '_RSP': - outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) + if self.rspfile_quote_style is RSPFileSyntax.TASKING: + outfile.write(' command = {} --option-file=$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) + else: + outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) outfile.write(' rspfile = $out.rsp\n') outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in rspfile_args]))) else: @@ -410,7 +413,7 @@ def write(self, outfile: T.TextIO) -> None: outfile.write(line) if use_rspfile: - if self.rule.rspfile_quote_style is RSPFileSyntax.MSVC: + if self.rule.rspfile_quote_style in {RSPFileSyntax.MSVC, RSPFileSyntax.TASKING}: qf = cmd_quote else: qf = gcc_rsp_quote @@ -730,6 +733,12 @@ def generate_compdb(self) -> None: for ext in ['', '_RSP']] rules += [f"{rule}{ext}" for rule in [self.compiler_to_pch_rule_name(compiler)] for ext in ['', '_RSP']] + # Add custom MIL link rules to get the files compiled by the TASKING compiler family to MIL files included in the database + key = OptionKey('b_tasking_mil_link') + if key in compiler.base_options: + rule = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine) + rules.append(rule) + rules.append(f'{rule}_RSP') compdb_options = ['-x'] if mesonlib.version_compare(self.ninja_version, '>=1.9') else [] ninja_compdb = self.ninja_command + ['-t', 'compdb'] + compdb_options + rules builddir = self.environment.get_build_dir() @@ -1076,8 +1085,19 @@ def generate_target(self, target) -> None: # Skip the link stage for this special type of target return linker, stdlib_args = self.determine_linker_and_stdlib_args(target) - if isinstance(target, build.StaticLibrary) and target.prelink: + # For prelinking and TASKING mil linking there needs to be an additional link target and the object list is modified + if not isinstance(target, build.StaticLibrary): + final_obj_list = obj_list + elif target.prelink: final_obj_list = self.generate_prelink(target, obj_list) + elif 'c' in target.compilers: + key = OptionKey('b_tasking_mil_link') + if key not in target.get_options() or key not in target.compilers['c'].base_options: + final_obj_list = obj_list + elif target.get_option(key): + final_obj_list = self.generate_mil_link(target, obj_list) + else: + final_obj_list = obj_list else: final_obj_list = obj_list elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args) @@ -2484,6 +2504,33 @@ def generate_llvm_ir_compile_rule(self, compiler) -> None: self.add_rule(NinjaRule(rule, command, args, description, **options)) self.created_llvm_ir_rule[compiler.for_machine] = True + def generate_tasking_mil_compile_rules(self, compiler: Compiler) -> None: + rule = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine) + depargs = NinjaCommandArg.list(compiler.get_dependency_gen_args('$out', '$DEPFILE'), Quoting.none) + 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': + deps = 'msvc' + depfile = None + else: + deps = 'gcc' + depfile = '$DEPFILE' + + options = self._rsp_options(compiler) + + self.add_rule(NinjaRule(rule, command, args, description, **options, deps=deps, depfile=depfile)) + + def generate_tasking_mil_link_rules(self, compiler: Compiler) -> None: + rule = self.get_compiler_rule_name('tasking_mil_link', compiler.for_machine) + command = compiler.get_exelist() + args = ['$ARGS', '--mil-link'] + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + ['-c', '$in'] + description = 'MIL linking object $out' + + options = self._rsp_options(compiler) + + self.add_rule(NinjaRule(rule, command, args, description, **options)) + def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None: if langname == 'java': self.generate_java_compile_rule(compiler) @@ -2574,6 +2621,9 @@ def generate_compile_rules(self) -> None: for langname, compiler in clist.items(): if compiler.get_id() == 'clang': self.generate_llvm_ir_compile_rule(compiler) + if OptionKey('b_tasking_mil_link') in compiler.base_options: + self.generate_tasking_mil_compile_rules(compiler) + self.generate_tasking_mil_link_rules(compiler) self.generate_compile_rule_for(langname, compiler) self.generate_pch_rule_for(langname, compiler) for mode in compiler.get_modes(): @@ -3014,6 +3064,11 @@ def generate_single_compile(self, target: build.BuildTarget, src, else: raise InvalidArguments(f'Invalid source type: {src!r}') obj_basename = self.object_filename_from_source(target, src) + # If mil linking is enabled for the target, then compilation output has to be MIL files instead of object files + if compiler.get_language() == 'c': + key = OptionKey('b_tasking_mil_link') + if key in compiler.base_options and target.get_option(key) and src.rsplit('.', 1)[1] in compilers.lang_suffixes['c']: + obj_basename = f'{os.path.splitext(obj_basename)[0]}.mil' rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) dep_file = compiler.depfile_for_object(rel_obj) @@ -3034,8 +3089,14 @@ def generate_single_compile(self, target: build.BuildTarget, src, i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0])) arr.append(i) pch_dep = arr - - compiler_name = self.compiler_to_rule_name(compiler) + # 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_tasking_mil_link') + if key in compiler.base_options and target.get_option(key) 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) extra_deps = [] if compiler.get_language() == 'fortran': # Can't read source file to scan for deps if it's generated later @@ -3420,6 +3481,29 @@ def generate_prelink(self, target, obj_list): self.add_build(elem) return [prelink_name] + def generate_mil_link(self, target: build.StaticLibrary, obj_list: T.List[str]) -> T.List[str]: + assert isinstance(target, build.StaticLibrary) + + mil_linked_name = os.path.join(self.get_target_private_dir(target), target.name + '-mil_link.o') + mil_link_list = [] + obj_file_list = [] + for obj in obj_list: + if obj.endswith('.mil'): + mil_link_list.append(obj) + else: + obj_file_list.append(obj) + obj_file_list.append(mil_linked_name) + compiler = get_compiler_for_source(target.compilers.values(), mil_link_list[0][:-3] + '.c') + commands = self._generate_single_compile_base_args(target, compiler) + commands += self._generate_single_compile_target_args(target, compiler) + commands = commands.compiler.compiler_args(commands) + + elem = NinjaBuildElement(self.all_outputs, [mil_linked_name], self.get_compiler_rule_name('tasking_mil_link', compiler.for_machine), mil_link_list) + elem.add_item('ARGS', commands) + self.add_build(elem) + + return obj_file_list + def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None): extra_args = extra_args if extra_args is not None else [] stdlib_args = stdlib_args if stdlib_args is not None else [] @@ -3451,6 +3535,9 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. linker, isinstance(target, build.SharedModule), self.environment.get_build_dir()) + # Add --mil-link if the option is enabled + if isinstance(target, build.Executable) and 'c' in target.compilers and OptionKey('b_tasking_mil_link') in target.get_options(): + commands += target.compilers['c'].get_tasking_mil_link_args(target.get_option(OptionKey('b_tasking_mil_link'))) # 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 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ca4e5d5a7fa0..f8ad3da66102 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2019,6 +2019,8 @@ def post_init(self) -> None: elif ('c' in self.compilers and self.compilers['c'].get_id() in {'mwccarm', 'mwcceppc'} or 'cpp' in self.compilers and self.compilers['cpp'].get_id() in {'mwccarm', 'mwcceppc'}): self.suffix = 'nef' + elif ('c' in self.compilers and self.compilers['c'].get_id() in {'cctc', 'ccarm', 'cc51', 'ccmcs', 'ccpcp'}): + self.suffix = 'elf' else: self.suffix = machine.get_exe_suffix() self.filename = self.name diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 5868211a6f15..b72c5b5d2eb5 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -27,6 +27,7 @@ from .mixins.emscripten import EmscriptenMixin from .mixins.metrowerks import MetrowerksCompiler from .mixins.metrowerks import mwccarm_instruction_set_args, mwcceppc_instruction_set_args +from .mixins.tasking import TaskingCompiler from .compilers import ( gnu_winlibs, msvc_winlibs, @@ -830,3 +831,27 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] if std != 'none': args.append('-lang ' + std) return args + +class _TaskingCCompiler(TaskingCompiler, 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, + full_version: T.Optional[str] = None): + CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, + info, linker=linker, full_version=full_version) + TaskingCompiler.__init__(self) + +class TaskingTricoreCCompiler(_TaskingCCompiler): + id = 'cctc' + +class TaskingArmCCompiler(_TaskingCCompiler): + id = 'ccarm' + +class Tasking8051CCompiler(_TaskingCCompiler): + id = 'cc51' + +class TaskingMCSCCompiler(_TaskingCCompiler): + id = 'ccmcs' + +class TaskingPCPCCompiler(_TaskingCCompiler): + id = 'ccpcp' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 8788df0be05d..f8d4ac439b55 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -251,6 +251,7 @@ def init_option(self, name: OptionKey) -> options._U: OptionKey('b_bitcode'): BaseOption(options.UserBooleanOption, 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), OptionKey('b_vscrt'): BaseOption(options.UserComboOption, 'VS run-time library type to use.', 'from_buildtype', choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), + OptionKey('b_tasking_mil_link'): BaseOption(options.UserBooleanOption, 'Use TASKING compiler families MIL linking feature', False), } base_options = {key: base_opt.init_option(key) for key, base_opt in BASE_OPTIONS.items()} @@ -1353,6 +1354,13 @@ def get_preprocessor(self) -> Compiler: def form_compileropt_key(self, basename: str) -> OptionKey: return OptionKey(f'{self.language}_{basename}', machine=self.for_machine) + def get_tasking_mil_link_args(self, option_enabled: bool) -> T.List[str]: + """ + Argument for enabling TASKING's MIL link feature, + for most compilers, this will return nothing. + """ + return [] + def get_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index d88441dffcd7..a528acbf3fbb 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -240,6 +240,17 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker return linkers.MetrowerksStaticLinkerARM(linker) else: return linkers.MetrowerksStaticLinkerEmbeddedPowerPC(linker) + if 'TASKING VX-toolset' in err: + if 'TriCore' in err: + return linkers.TaskingTricoreStaticLinker(linker) + if 'ARM' in err: + return linkers.TaskingARMStaticLinker(linker) + if '8051' in err: + return linkers.Tasking8051StaticLinker(linker) + if 'PCP' in err: + return linkers.TaskingPCPStaticLinker(linker) + else: + return linkers.TaskingMCSStaticLinker(linker) if p.returncode == 0: return linkers.ArLinker(compiler.for_machine, linker) if p.returncode == 1 and err.startswith('usage'): # OSX @@ -605,6 +616,38 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: return cls( ccache, compiler, compiler_version, for_machine, is_cross, info, full_version=full_version, linker=linker) + if 'TASKING VX-toolset' in err: + if 'TriCore' in err or 'AURIX Development Studio' in err: + cls = c.TaskingTricoreCCompiler + lnk = linkers.TaskingTricoreLinker + elif 'ARM' in err: + cls = c.TaskingArmCCompiler + lnk = linkers.TaskingARMLinker + elif '8051' in err: + cls = c.Tasking8051CCompiler + lnk = linkers.Tasking8051Linker + elif 'PCP' in err: + cls = c.TaskingPCPCCompiler + lnk = linkers.TaskingPCPLinker + elif 'MCS' in err: + cls = c.TaskingMCSCCompiler + lnk = linkers.TaskingMCSLinker + else: + raise EnvironmentException('Failed to detect linker for TASKING VX-toolset compiler. Please update your cross file(s).') + + tasking_ver_match = re.search(r'v([0-9]+)\.([0-9]+)r([0-9]+) Build ([0-9]+)', err) + 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) + 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) + return cls( + ccache, compiler, tasking_version, for_machine, is_cross, info, + full_version=full_version, linker=linker) _handle_exceptions(popen_exceptions, compilers) raise EnvironmentException(f'Unknown compiler {compilers}') diff --git a/mesonbuild/compilers/mixins/tasking.py b/mesonbuild/compilers/mixins/tasking.py new file mode 100644 index 000000000000..7675dbc75329 --- /dev/null +++ b/mesonbuild/compilers/mixins/tasking.py @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2012-2023 The Meson development team +from __future__ import annotations + +"""Representations specific to the TASKING embedded C/C++ compiler family.""" + +import os +import typing as T + +from ...mesonlib import EnvironmentException +from ...options import OptionKey + +if T.TYPE_CHECKING: + from ...compilers.compilers import Compiler +else: + # This is a bit clever, for mypy we pretend that these mixins descend from + # Compiler, so we get all of the methods and attributes defined for us, but + # for runtime we make them descend from object (which all classes normally + # do). This gives us DRYer type checking, with no runtime impact + Compiler = object + +tasking_buildtype_args: T.Mapping[str, T.List[str]] = { + 'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': [], + 'custom': [] +} + +tasking_optimization_args: T.Mapping[str, T.List[str]] = { + 'plain': [], + '0': ['-O0'], + 'g': ['-O1'], # There is no debug specific level, O1 is recommended by the compiler + '1': ['-O1'], + '2': ['-O2'], + '3': ['-O3'], + 's': ['-Os'] +} + +tasking_debug_args: T.Mapping[bool, T.List[str]] = { + False: [], + True: ['-g3'] +} + +class TaskingCompiler(Compiler): + ''' + Functionality that is common to all TASKING family compilers. + ''' + + LINKER_PREFIX = '-Wl' + + def __init__(self) -> None: + if not self.is_cross: + raise EnvironmentException(f'{id} supports only cross-compilation.') + + self.base_options = { + OptionKey(o) for o in [ + 'b_tasking_mil_link', + 'b_staticpic', + 'b_ndebug' + ] + } + + default_warn_args = [] # type: T.List[str] + self.warn_args = {'0': [], + '1': default_warn_args, + '2': default_warn_args + [], + '3': default_warn_args + [], + 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]] + # TODO: add additional compilable files so that meson can detect it + self.can_compile_suffixes.add('asm') + + def get_pic_args(self) -> T.List[str]: + return ['--pic'] + + def get_buildtype_args(self, buildtype: str) -> T.List[str]: + return tasking_buildtype_args[buildtype] + + def get_debug_args(self, is_debug: bool) -> T.List[str]: + return tasking_debug_args[is_debug] + + def get_compile_only_args(self) -> T.List[str]: + return ['-c'] + + def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: + return [f'--dep-file={outfile}'] + + def get_depfile_suffix(self) -> str: + return 'dep' + + def get_no_stdinc_args(self) -> T.List[str]: + return ['--no-stdinc'] + + def get_werror_args(self) -> T.List[str]: + return ['--warnings-as-errors'] + + def get_no_stdlib_link_args(self) -> T.List[str]: + return ['--no-default-libraries'] + + def get_output_args(self, outputname: str) -> T.List[str]: + return ['-o', outputname] + + def get_include_args(self, path: str, is_system: bool) -> T.List[str]: + if path == '': + path = '.' + return ['-I' + path] + + def get_optimization_args(self, optimization_level: str) -> T.List[str]: + return tasking_optimization_args[optimization_level] + + def get_no_optimization_args(self) -> T.List[str]: + return ['-O0'] + + 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): + if i[:2] == '-I' or i[:2] == '-L': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + + return parameter_list + + def get_tasking_mil_link_args(self, option_enabled: bool) -> T.List[str]: + return ['--mil-link'] if option_enabled else [] + + def get_preprocess_only_args(self) -> T.List[str]: + return ['-E'] diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index c99ae4b016b0..4055b21761c5 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -64,6 +64,7 @@ 'wasm64', 'x86', 'x86_64', + 'tricore' ) # It would feel more natural to call this "64_BIT_CPU_FAMILIES", but diff --git a/mesonbuild/linkers/base.py b/mesonbuild/linkers/base.py index c8efc9d6d82c..68fdb2ea3a70 100644 --- a/mesonbuild/linkers/base.py +++ b/mesonbuild/linkers/base.py @@ -18,6 +18,7 @@ class RSPFileSyntax(enum.Enum): MSVC = enum.auto() GCC = enum.auto() + TASKING = enum.auto() class ArLikeLinker: diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index d0ffc56182ee..ba7b9228d718 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -526,6 +526,38 @@ class MetrowerksStaticLinkerARM(MetrowerksStaticLinker): class MetrowerksStaticLinkerEmbeddedPowerPC(MetrowerksStaticLinker): id = 'mwldeppc' +class TaskingStaticLinker(StaticLinker): + + def __init__(self, exelist: T.List[str]): + super().__init__(exelist) + + def can_linker_accept_rsp(self) -> bool: + return True + + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.TASKING + + def get_output_args(self, target: str) -> T.List[str]: + return ['-n', target] + + def get_linker_always_args(self) -> T.List[str]: + return ['-r'] + +class TaskingTricoreStaticLinker(TaskingStaticLinker): + id = 'artc' + +class TaskingARMStaticLinker(TaskingStaticLinker): + id = 'ararm' + +class Tasking8051StaticLinker(TaskingStaticLinker): + id = 'ar51' + +class TaskingMCSStaticLinker(TaskingStaticLinker): + id = 'armcs' + +class TaskingPCPStaticLinker(TaskingStaticLinker): + id = 'arpcp' + 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 @@ -1663,3 +1695,67 @@ class MetrowerksLinkerARM(MetrowerksLinker): class MetrowerksLinkerEmbeddedPowerPC(MetrowerksLinker): id = 'mwldeppc' + +class TaskingLinker(DynamicLinker): + + _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { + 'plain': [], + '0': ['-O0'], + 'g': ['-O1'], # There is no debug specific level, O1 is recommended by the compiler + '1': ['-O1'], + '2': ['-O2'], + '3': ['-O2'], # There is no 3rd level optimization for the linker + 's': ['-Os'], + } + + def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(exelist, for_machine, '', [], + version=version) + + def get_accepts_rsp(self) -> bool: + return True + + def get_lib_prefix(self) -> str: + return "" + + def get_allow_undefined_args(self) -> T.List[str]: + return [] + + def invoked_by_compiler(self) -> bool: + return True + + def get_search_args(self, dirname: str) -> T.List[str]: + return self._apply_prefix('-L' + dirname) + + def get_output_args(self, outputname: str) -> T.List[str]: + return ['-o', outputname] + + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.TASKING + + def fatal_warnings(self) -> T.List[str]: + """Arguments to make all warnings errors.""" + return self._apply_prefix('--warnings-as-errors') + + def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: + args = mesonlib.listify(args) + l: T.List[str] = [] + for a in args: + l.extend(self._apply_prefix('-Wl--whole-archive=' + a)) + return l + +class TaskingTricoreLinker(TaskingLinker): + id = 'ltc' + +class TaskingARMLinker(TaskingLinker): + id = 'lkarm' + +class Tasking8051Linker(TaskingLinker): + id = 'lk51' + +class TaskingMCSLinker(TaskingLinker): + id = 'lmsc' + +class TaskingPCPLinker(TaskingLinker): + id = 'lpcp' From b95e1777ddbf0f8aebb56b84a6011468088c06ec Mon Sep 17 00:00:00 2001 From: gerioldman Date: Wed, 28 Aug 2024 02:57:56 +0200 Subject: [PATCH 134/624] First draft version of Tasking MIL linking with b_lto and prelinking --- docs/markdown/Builtin-options.md | 1 - docs/markdown/Reference-tables.md | 12 +---- mesonbuild/backend/backends.py | 21 ++++++-- mesonbuild/backend/ninjabackend.py | 71 ++++++++------------------ mesonbuild/backend/vs2010backend.py | 4 +- mesonbuild/build.py | 7 ++- mesonbuild/compilers/c.py | 19 ++----- mesonbuild/compilers/compilers.py | 16 +++--- mesonbuild/compilers/detect.py | 30 ++--------- mesonbuild/compilers/mixins/apple.py | 4 +- mesonbuild/compilers/mixins/elbrus.py | 4 +- mesonbuild/compilers/mixins/gnu.py | 4 +- mesonbuild/compilers/mixins/tasking.py | 20 ++++++-- mesonbuild/linkers/linkers.py | 35 ++----------- 14 files changed, 85 insertions(+), 163 deletions(-) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 8d1772a43d20..f16a46ffebea 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -223,7 +223,6 @@ available on all platforms or with all compilers: | b_staticpic | true | true, false | Build static libraries as position independent | | b_pie | false | true, false | Build position-independent executables (since 0.49.0) | | b_vscrt | from_buildtype | none, md, mdd, mt, mtd, from_buildtype, static_from_buildtype | VS runtime library to use (since 0.48.0) (static_from_buildtype since 0.56.0) | -| b_tasking_mil_link | false | true, false | Use MIL linking for the TASKING VX-tools compiler family (since 1.4.0) | The value of `b_sanitize` can be one of: `none`, `address`, `thread`, `undefined`, `memory`, `leak`, `address,undefined`, but note that some diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 9db23a0c0311..5b27e3de52ac 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -49,11 +49,7 @@ These are return values of the `get_id` (Compiler family) and | armasm | Microsoft Macro Assembler for ARM and AARCH64 (Since 0.64.0) | | | mwasmarm | Metrowerks Assembler for Embedded ARM | | | mwasmeppc | Metrowerks Assembler for Embedded PowerPC | | -| cctc | TASKING VX-toolset for TriCore compiler | | -| ccarm | TASKING VX-toolset for ARM compiler | | -| cc51 | TASKING VX-toolset for 8051 compiler | | -| ccmcs | TASKING VX-toolset for MCS compiler | | -| ccpcp | TASKING VX-toolset for PCP compiler | | +| tasking | TASKING VX-toolset | | ## Linker ids @@ -85,11 +81,7 @@ These are return values of the `get_linker_id` method in a compiler object. | ccomp | CompCert used as the linker driver | | mwldarm | The Metrowerks Linker with the ARM interface, used with mwccarm only | | mwldeppc | The Metrowerks Linker with the PowerPC interface, used with mwcceppc only | -| ltc | TASKING VX-toolset for TriCore linker | -| lkarm | TASKING VX-toolset for ARM linker | -| lk51 | TASKING VX-toolset for 8051 linker | -| lmsc | TASKING VX-toolset for MCS linker | -| lpcp | TASKING VX-toolset for PCP linker | +| tasking | TASKING VX-toolset | For languages that don't have separate dynamic linkers such as C# and Java, the `get_linker_id` will return the compiler name. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index fb912b1d83d0..18caf7bbe8a7 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -24,10 +24,11 @@ from .. import programs from .. import mesonlib from .. import mlog -from ..compilers import LANGUAGES_USING_LDFLAGS, detect +from ..compilers import LANGUAGES_USING_LDFLAGS, detect, lang_suffixes from ..mesonlib import ( File, MachineChoice, MesonException, OrderedSet, - ExecutableSerialisation, classify_unity_sources, + ExecutableSerialisation, EnvironmentException, + classify_unity_sources, get_compiler_for_source ) from ..options import OptionKey @@ -837,7 +838,7 @@ def canonicalize_filename(fname: str) -> str: fname = fname.replace(ch, '_') return hashed + fname - def object_filename_from_source(self, target: build.BuildTarget, source: 'FileOrString', targetdir: T.Optional[str] = None) -> str: + def object_filename_from_source(self, target: build.BuildTarget, compiler: Compiler, source: 'FileOrString', targetdir: T.Optional[str] = None) -> str: assert isinstance(source, mesonlib.File) if isinstance(target, build.CompileTarget): return target.sources_map[source] @@ -868,7 +869,16 @@ def object_filename_from_source(self, target: build.BuildTarget, source: 'FileOr gen_source = os.path.relpath(os.path.join(build_dir, rel_src), os.path.join(self.environment.get_source_dir(), target.get_subdir())) machine = self.environment.machines[target.for_machine] - ret = self.canonicalize_filename(gen_source) + '.' + machine.get_object_suffix() + object_suffix = machine.get_object_suffix() + # For the TASKING compiler, in case of LTO or prelinking the object suffix has to be .mil + if compiler.get_id() == 'tasking': + if target.get_option(OptionKey('b_lto')) or (isinstance(target, build.StaticLibrary) and target.prelink): + if not source.rsplit('.', 1)[1] in lang_suffixes['c']: + if isinstance(target, build.StaticLibrary) and not target.prelink: + raise EnvironmentException('Tried using MIL linking for a static library with a assembly file. This can only be done if the static library is prelinked or disable \'b_lto\'.') + else: + object_suffix = 'mil' + ret = self.canonicalize_filename(gen_source) + '.' + object_suffix if targetdir is not None: return os.path.join(targetdir, ret) return ret @@ -924,7 +934,8 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects', proj_dir_to_buil sources.append(_src) for osrc in sources: - objname = self.object_filename_from_source(extobj.target, osrc, targetdir) + compiler = get_compiler_for_source(extobj.target.compilers.values(), osrc) + objname = self.object_filename_from_source(extobj.target, compiler, osrc, targetdir) objpath = os.path.join(proj_dir_to_build_root, objname) result.append(objpath) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 991f96edc81f..7125bbdf4e74 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -734,8 +734,7 @@ def generate_compdb(self) -> None: rules += [f"{rule}{ext}" for rule in [self.compiler_to_pch_rule_name(compiler)] for ext in ['', '_RSP']] # Add custom MIL link rules to get the files compiled by the TASKING compiler family to MIL files included in the database - key = OptionKey('b_tasking_mil_link') - if key in compiler.base_options: + if compiler.get_id() == 'tasking': rule = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine) rules.append(rule) rules.append(f'{rule}_RSP') @@ -1085,19 +1084,11 @@ def generate_target(self, target) -> None: # Skip the link stage for this special type of target return linker, stdlib_args = self.determine_linker_and_stdlib_args(target) - # For prelinking and TASKING mil linking there needs to be an additional link target and the object list is modified + if not isinstance(target, build.StaticLibrary): final_obj_list = obj_list elif target.prelink: final_obj_list = self.generate_prelink(target, obj_list) - elif 'c' in target.compilers: - key = OptionKey('b_tasking_mil_link') - if key not in target.get_options() or key not in target.compilers['c'].base_options: - final_obj_list = obj_list - elif target.get_option(key): - final_obj_list = self.generate_mil_link(target, obj_list) - else: - final_obj_list = obj_list else: final_obj_list = obj_list elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args) @@ -2621,9 +2612,8 @@ def generate_compile_rules(self) -> None: for langname, compiler in clist.items(): if compiler.get_id() == 'clang': self.generate_llvm_ir_compile_rule(compiler) - if OptionKey('b_tasking_mil_link') in compiler.base_options: + if compiler.get_id() == 'tasking': self.generate_tasking_mil_compile_rules(compiler) - self.generate_tasking_mil_link_rules(compiler) self.generate_compile_rule_for(langname, compiler) self.generate_pch_rule_for(langname, compiler) for mode in compiler.get_modes(): @@ -3063,12 +3053,7 @@ def generate_single_compile(self, target: build.BuildTarget, src, raise AssertionError(f'BUG: broken generated source file handling for {src!r}') else: raise InvalidArguments(f'Invalid source type: {src!r}') - obj_basename = self.object_filename_from_source(target, src) - # If mil linking is enabled for the target, then compilation output has to be MIL files instead of object files - if compiler.get_language() == 'c': - key = OptionKey('b_tasking_mil_link') - if key in compiler.base_options and target.get_option(key) and src.rsplit('.', 1)[1] in compilers.lang_suffixes['c']: - obj_basename = f'{os.path.splitext(obj_basename)[0]}.mil' + obj_basename = self.object_filename_from_source(target, compiler, src) rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) dep_file = compiler.depfile_for_object(rel_obj) @@ -3092,9 +3077,12 @@ def generate_single_compile(self, target: build.BuildTarget, src, # 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_tasking_mil_link') - if key in compiler.base_options and target.get_option(key) and src.rsplit('.', 1)[1] in compilers.lang_suffixes['c']: - compiler_name = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine) + 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']: + compiler_name = self.get_compiler_rule_name('tasking_mil_compile', compiler.for_machine) + else: + compiler_name = self.compiler_to_rule_name(compiler) else: compiler_name = self.compiler_to_rule_name(compiler) extra_deps = [] @@ -3473,36 +3461,19 @@ def generate_prelink(self, target, obj_list): prelinker = target.get_prelinker() cmd = prelinker.exelist[:] - cmd += prelinker.get_prelink_args(prelink_name, obj_list) + obj_list, args = prelinker.get_prelink_args(prelink_name, obj_list) + cmd += args + if prelinker.get_prelink_append_compile_args(): + compile_args = self._generate_single_compile_base_args(target, prelinker) + compile_args += self._generate_single_compile_target_args(target, prelinker) + compile_args = compile_args.compiler.compiler_args(compile_args) + cmd += compile_args.to_native() cmd = self.replace_paths(target, cmd) elem.add_item('COMMAND', cmd) elem.add_item('description', f'Prelinking {prelink_name}') self.add_build(elem) - return [prelink_name] - - def generate_mil_link(self, target: build.StaticLibrary, obj_list: T.List[str]) -> T.List[str]: - assert isinstance(target, build.StaticLibrary) - - mil_linked_name = os.path.join(self.get_target_private_dir(target), target.name + '-mil_link.o') - mil_link_list = [] - obj_file_list = [] - for obj in obj_list: - if obj.endswith('.mil'): - mil_link_list.append(obj) - else: - obj_file_list.append(obj) - obj_file_list.append(mil_linked_name) - compiler = get_compiler_for_source(target.compilers.values(), mil_link_list[0][:-3] + '.c') - commands = self._generate_single_compile_base_args(target, compiler) - commands += self._generate_single_compile_target_args(target, compiler) - commands = commands.compiler.compiler_args(commands) - - elem = NinjaBuildElement(self.all_outputs, [mil_linked_name], self.get_compiler_rule_name('tasking_mil_link', compiler.for_machine), mil_link_list) - elem.add_item('ARGS', commands) - self.add_build(elem) - - return obj_file_list + return obj_list def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None): extra_args = extra_args if extra_args is not None else [] @@ -3535,9 +3506,6 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. linker, isinstance(target, build.SharedModule), self.environment.get_build_dir()) - # Add --mil-link if the option is enabled - if isinstance(target, build.Executable) and 'c' in target.compilers and OptionKey('b_tasking_mil_link') in target.get_options(): - commands += target.compilers['c'].get_tasking_mil_link_args(target.get_option(OptionKey('b_tasking_mil_link'))) # 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 @@ -3647,6 +3615,9 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. for t in target.link_depends]) 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')): + 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. if linker.get_id() in {'c2000', 'c6000', 'ti'}: diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 08a19c659e44..0fb30a7b91b9 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1725,7 +1725,7 @@ def path_normalize_add(path, lis): self.add_preprocessor_defines(lang, inc_cl, file_defines) self.add_include_dirs(lang, inc_cl, file_inc_dirs) ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + \ - self.object_filename_from_source(target, s) + 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) @@ -1739,7 +1739,7 @@ def path_normalize_add(path, lis): self.add_include_dirs(lang, inc_cl, file_inc_dirs) s = File.from_built_file(target.get_subdir(), s) ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + \ - self.object_filename_from_source(target, s) + self.object_filename_from_source(target, compiler, s) for lang, headers in pch_sources.items(): impl = headers[1] if impl and path_normalize_add(impl, previous_sources): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f8ad3da66102..8c7ee6ec4061 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2019,7 +2019,7 @@ def post_init(self) -> None: elif ('c' in self.compilers and self.compilers['c'].get_id() in {'mwccarm', 'mwcceppc'} or 'cpp' in self.compilers and self.compilers['cpp'].get_id() in {'mwccarm', 'mwcceppc'}): self.suffix = 'nef' - elif ('c' in self.compilers and self.compilers['c'].get_id() in {'cctc', 'ccarm', 'cc51', 'ccmcs', 'ccpcp'}): + elif ('c' in self.compilers and self.compilers['c'].get_id() == 'tasking'): self.suffix = 'elf' else: self.suffix = machine.get_exe_suffix() @@ -2176,7 +2176,10 @@ def post_init(self) -> None: elif self.rust_crate_type == 'staticlib': self.suffix = 'a' else: - self.suffix = 'a' + 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 diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index b72c5b5d2eb5..c75120fee4d0 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -832,7 +832,9 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] args.append('-lang ' + std) return args -class _TaskingCCompiler(TaskingCompiler, CCompiler): +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', linker: T.Optional['DynamicLinker'] = None, @@ -840,18 +842,3 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, linker=linker, full_version=full_version) TaskingCompiler.__init__(self) - -class TaskingTricoreCCompiler(_TaskingCCompiler): - id = 'cctc' - -class TaskingArmCCompiler(_TaskingCCompiler): - id = 'ccarm' - -class Tasking8051CCompiler(_TaskingCCompiler): - id = 'cc51' - -class TaskingMCSCCompiler(_TaskingCCompiler): - id = 'ccmcs' - -class TaskingPCPCCompiler(_TaskingCCompiler): - id = 'ccpcp' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index f8d4ac439b55..3dfa0ff2770f 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -251,7 +251,6 @@ def init_option(self, name: OptionKey) -> options._U: OptionKey('b_bitcode'): BaseOption(options.UserBooleanOption, 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), OptionKey('b_vscrt'): BaseOption(options.UserComboOption, 'VS run-time library type to use.', 'from_buildtype', choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), - OptionKey('b_tasking_mil_link'): BaseOption(options.UserBooleanOption, 'Use TASKING compiler families MIL linking feature', False), } base_options = {key: base_opt.init_option(key) for key, base_opt in BASE_OPTIONS.items()} @@ -433,8 +432,8 @@ class CompileResult(HoldableObject): output_name: T.Optional[str] = field(default=None, init=False) cached: bool = field(default=False, init=False) - class Compiler(HoldableObject, metaclass=abc.ABCMeta): + # Libraries to ignore in find_library() since they are provided by the # compiler or the C library. Currently only used for MSVC. ignore_libs: T.List[str] = [] @@ -1328,9 +1327,13 @@ def get_feature_args(self, kwargs: DFeatures, build_to_src: str) -> T.List[str]: # TODO: using a TypeDict here would improve this raise EnvironmentException(f'{self.id} does not implement get_feature_args') - def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]: + def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]: raise EnvironmentException(f'{self.id} does not know how to do prelinking.') + def get_prelink_append_compile_args(self) -> bool: + """Controls whether compile args have to be used for prelinking or not""" + return False + def rsp_file_syntax(self) -> 'RSPFileSyntax': """The format of the RSP file that this compiler supports. @@ -1354,13 +1357,6 @@ def get_preprocessor(self) -> Compiler: def form_compileropt_key(self, basename: str) -> OptionKey: return OptionKey(f'{self.language}_{basename}', machine=self.for_machine) - def get_tasking_mil_link_args(self, option_enabled: bool) -> T.List[str]: - """ - Argument for enabling TASKING's MIL link feature, - for most compilers, this will return nothing. - """ - return [] - def get_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index a528acbf3fbb..5bc14350e4ee 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -241,16 +241,7 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker else: return linkers.MetrowerksStaticLinkerEmbeddedPowerPC(linker) if 'TASKING VX-toolset' in err: - if 'TriCore' in err: - return linkers.TaskingTricoreStaticLinker(linker) - if 'ARM' in err: - return linkers.TaskingARMStaticLinker(linker) - if '8051' in err: - return linkers.Tasking8051StaticLinker(linker) - if 'PCP' in err: - return linkers.TaskingPCPStaticLinker(linker) - else: - return linkers.TaskingMCSStaticLinker(linker) + return linkers.TaskingStaticLinker(linker) if p.returncode == 0: return linkers.ArLinker(compiler.for_machine, linker) if p.returncode == 1 and err.startswith('usage'): # OSX @@ -617,23 +608,8 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]: ccache, compiler, compiler_version, for_machine, is_cross, info, full_version=full_version, linker=linker) if 'TASKING VX-toolset' in err: - if 'TriCore' in err or 'AURIX Development Studio' in err: - cls = c.TaskingTricoreCCompiler - lnk = linkers.TaskingTricoreLinker - elif 'ARM' in err: - cls = c.TaskingArmCCompiler - lnk = linkers.TaskingARMLinker - elif '8051' in err: - cls = c.Tasking8051CCompiler - lnk = linkers.Tasking8051Linker - elif 'PCP' in err: - cls = c.TaskingPCPCCompiler - lnk = linkers.TaskingPCPLinker - elif 'MCS' in err: - cls = c.TaskingMCSCCompiler - lnk = linkers.TaskingMCSLinker - else: - raise EnvironmentException('Failed to detect linker for TASKING VX-toolset compiler. Please update your cross file(s).') + cls = c.TaskingCCompiler + lnk = linkers.TaskingLinker tasking_ver_match = re.search(r'v([0-9]+)\.([0-9]+)r([0-9]+) Build ([0-9]+)', err) assert tasking_ver_match is not None, 'for mypy' diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py index fc93d38a5673..6ec9239522fe 100644 --- a/mesonbuild/compilers/mixins/apple.py +++ b/mesonbuild/compilers/mixins/apple.py @@ -56,6 +56,6 @@ def openmp_link_flags(self, env: Environment) -> T.List[str]: raise MesonException("Couldn't find libomp") return self.__BASE_OMP_FLAGS + link - def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]: + def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]: # The objects are prelinked through the compiler, which injects -lSystem - return ['-nostdlib', '-r', '-o', prelink_name] + obj_list + return [prelink_name], ['-nostdlib', '-r', '-o', prelink_name] + obj_list diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 66f419cf02d8..5818d8dee0b5 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -76,8 +76,8 @@ def get_default_include_dirs(self) -> T.List[str]: def get_optimization_args(self, optimization_level: str) -> T.List[str]: return gnu_optimization_args[optimization_level] - def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]: - return ['-r', '-nodefaultlibs', '-nostartfiles', '-o', prelink_name] + obj_list + 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', '-nodefaultlibs', '-nostartfiles', '-o', prelink_name] + obj_list def get_pch_suffix(self) -> str: # Actually it's not supported for now, but probably will be supported in future diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 976fa78714c6..66b01ef512e1 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -609,8 +609,8 @@ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]: # error. return ['-Werror=attributes'] - def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]: - return ['-r', '-o', prelink_name] + obj_list + 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]: if threads == 0: diff --git a/mesonbuild/compilers/mixins/tasking.py b/mesonbuild/compilers/mixins/tasking.py index 7675dbc75329..082cff073908 100644 --- a/mesonbuild/compilers/mixins/tasking.py +++ b/mesonbuild/compilers/mixins/tasking.py @@ -56,7 +56,7 @@ def __init__(self) -> None: self.base_options = { OptionKey(o) for o in [ - 'b_tasking_mil_link', + 'b_lto', 'b_staticpic', 'b_ndebug' ] @@ -112,6 +112,21 @@ def get_optimization_args(self, optimization_level: str) -> T.List[str]: def get_no_optimization_args(self) -> T.List[str]: return ['-O0'] + def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]: + mil_link_list = [] + obj_file_list = [] + for obj in obj_list: + if obj.endswith('.mil'): + mil_link_list.append(obj) + else: + obj_file_list.append(obj) + obj_file_list.append(prelink_name) + + return obj_file_list, ['--mil-link', '-o', prelink_name, '-c'] + mil_link_list + + def get_prelink_append_compile_args(self) -> bool: + return True + 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): if i[:2] == '-I' or i[:2] == '-L': @@ -119,8 +134,5 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], bu return parameter_list - def get_tasking_mil_link_args(self, option_enabled: bool) -> T.List[str]: - return ['--mil-link'] if option_enabled else [] - def get_preprocess_only_args(self) -> T.List[str]: return ['-E'] diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index ba7b9228d718..705e4282138b 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -527,6 +527,7 @@ class MetrowerksStaticLinkerEmbeddedPowerPC(MetrowerksStaticLinker): id = 'mwldeppc' class TaskingStaticLinker(StaticLinker): + id = 'tasking' def __init__(self, exelist: T.List[str]): super().__init__(exelist) @@ -543,21 +544,6 @@ def get_output_args(self, target: str) -> T.List[str]: def get_linker_always_args(self) -> T.List[str]: return ['-r'] -class TaskingTricoreStaticLinker(TaskingStaticLinker): - id = 'artc' - -class TaskingARMStaticLinker(TaskingStaticLinker): - id = 'ararm' - -class Tasking8051StaticLinker(TaskingStaticLinker): - id = 'ar51' - -class TaskingMCSStaticLinker(TaskingStaticLinker): - id = 'armcs' - -class TaskingPCPStaticLinker(TaskingStaticLinker): - id = 'arpcp' - 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 @@ -1697,6 +1683,7 @@ class MetrowerksLinkerEmbeddedPowerPC(MetrowerksLinker): id = 'mwldeppc' class TaskingLinker(DynamicLinker): + id = 'tasking' _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { 'plain': [], @@ -1731,6 +1718,9 @@ def get_search_args(self, dirname: str) -> T.List[str]: def get_output_args(self, outputname: str) -> T.List[str]: return ['-o', outputname] + def get_lto_args(self) -> T.List[str]: + return ['--mil-link'] + def rsp_file_syntax(self) -> RSPFileSyntax: return RSPFileSyntax.TASKING @@ -1744,18 +1734,3 @@ def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: for a in args: l.extend(self._apply_prefix('-Wl--whole-archive=' + a)) return l - -class TaskingTricoreLinker(TaskingLinker): - id = 'ltc' - -class TaskingARMLinker(TaskingLinker): - id = 'lkarm' - -class Tasking8051Linker(TaskingLinker): - id = 'lk51' - -class TaskingMCSLinker(TaskingLinker): - id = 'lmsc' - -class TaskingPCPLinker(TaskingLinker): - id = 'lpcp' From eb1e52afa142fc0f38260a9cb3413f2bd63b1675 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 19 May 2022 19:17:01 -0400 Subject: [PATCH 135/624] mtest: fix rebuilding all before running tests Inconsistency in the original implementation of commit 79e2c52a15e896e46ff3cfa3ec16fbf3f132ee01. If an explicit list of targets is passed on the CLI, then that is passed to rebuild_deps. If not, we pass every loaded test to rebuild_deps instead. This means we cannot distinguish between "trying to run all tests" and "trying to run specific tests". We then load all the deps for all tests, and try to build them all as explicit arguments to the underlying ninja. There are two situations where this falls flat: - given underspecified deps - given all (selected?) tests legitimately happen to have no dependencies In both cases, we calculate that there are no deps to rebuild, we run ninja without any targets, and this invokes the default "all" rule and maybe builds a few thousand targets that this specific test run does not need. Additionally, in large projects which define many tests with many dependencies, we could end up overflowing ARG_MAX when processing *all* tests. Instead, pass no tests to rebuild_deps. We then specially handle this by directly running the relevant ninja target for "all test deps", which is overall more elegant than specifying many many dependencies by name. Given a subset of tests to guarantee the freshness of, we instead skip running ninja at all if there are indeed no test dependencies. --- mesonbuild/mtest.py | 48 ++++++++++++------- .../unit/106 underspecified mtest/main.c | 1 + .../unit/106 underspecified mtest/meson.build | 8 ++++ .../unit/106 underspecified mtest/runner.py | 5 ++ unittests/platformagnostictests.py | 15 ++++++ 5 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 test cases/unit/106 underspecified mtest/main.c create mode 100644 test cases/unit/106 underspecified mtest/meson.build create mode 100755 test cases/unit/106 underspecified mtest/runner.py diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index d0added78f8c..39970e530872 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1825,9 +1825,10 @@ def doit(self) -> int: 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 [] if not tests: return 0 - if not self.options.no_rebuild and not rebuild_deps(self.ninja, self.options.wd, tests): + if not self.options.no_rebuild and not rebuild_deps(self.ninja, self.options.wd, rebuild_only_tests, self.options.benchmark): # We return 125 here in case the build failed. # The reason is that exit code 125 tells `git bisect run` that the current # commit should be skipped. Thus users can directly use `meson test` to @@ -2140,7 +2141,7 @@ def list_tests(th: TestHarness) -> bool: print(th.get_pretty_suite(t)) return not tests -def rebuild_deps(ninja: T.List[str], wd: str, tests: T.List[TestSerialisation]) -> bool: +def rebuild_deps(ninja: T.List[str], wd: str, tests: T.List[TestSerialisation], benchmark: bool) -> bool: def convert_path_to_target(path: str) -> str: path = os.path.relpath(path, wd) if os.sep != '/': @@ -2149,23 +2150,34 @@ def convert_path_to_target(path: str) -> str: assert len(ninja) > 0 - targets_file = os.path.join(wd, 'meson-info/intro-targets.json') - with open(targets_file, encoding='utf-8') as fp: - targets_info = json.load(fp) - - depends: T.Set[str] = set() targets: T.Set[str] = set() - intro_targets: T.Dict[str, T.List[str]] = {} - for target in targets_info: - intro_targets[target['id']] = [ - convert_path_to_target(f) - for f in target['filename']] - for t in tests: - for d in t.depends: - if d in depends: - continue - depends.update(d) - targets.update(intro_targets[d]) + if tests: + targets_file = os.path.join(wd, 'meson-info/intro-targets.json') + with open(targets_file, encoding='utf-8') as fp: + targets_info = json.load(fp) + + depends: T.Set[str] = set() + intro_targets: T.Dict[str, T.List[str]] = {} + for target in targets_info: + intro_targets[target['id']] = [ + convert_path_to_target(f) + for f in target['filename']] + for t in tests: + for d in t.depends: + if d in depends: + continue + depends.update(d) + targets.update(intro_targets[d]) + else: + if benchmark: + targets.add('meson-benchmark-prereq') + else: + targets.add('meson-test-prereq') + + if not targets: + # We want to build minimal deps, but if the subset of targets have no + # deps then ninja falls back to 'all'. + return True ret = subprocess.run(ninja + ['-C', wd] + sorted(targets)).returncode if ret != 0: diff --git a/test cases/unit/106 underspecified mtest/main.c b/test cases/unit/106 underspecified mtest/main.c new file mode 100644 index 000000000000..8842fc1226ef --- /dev/null +++ b/test cases/unit/106 underspecified mtest/main.c @@ -0,0 +1 @@ +int main(void) { return 0 ; } diff --git a/test cases/unit/106 underspecified mtest/meson.build b/test cases/unit/106 underspecified mtest/meson.build new file mode 100644 index 000000000000..c0a88d6770c8 --- /dev/null +++ b/test cases/unit/106 underspecified mtest/meson.build @@ -0,0 +1,8 @@ +project('underspecified deps', 'c') + +runner = find_program('runner.py') +exe1 = executable('main1', 'main.c') +exe2 = executable('main2', 'main.c') + +test('runner-with-exedep', runner, args: exe1) +test('runner-without-dep', runner, args: exe2.full_path()) diff --git a/test cases/unit/106 underspecified mtest/runner.py b/test cases/unit/106 underspecified mtest/runner.py new file mode 100755 index 000000000000..9fb9ac40b94e --- /dev/null +++ b/test cases/unit/106 underspecified mtest/runner.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +import sys, subprocess + +subprocess.run(sys.argv[1:], check=True) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 2fb75f284993..ef277dee27bb 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -6,6 +6,7 @@ import json import os import pickle +import subprocess import tempfile import subprocess import textwrap @@ -508,3 +509,17 @@ def test_configure_new_option_subproject(self) -> None: f.write("option('new_option', type : 'boolean', value : false)") self.setconf('-Dsubproject:new_option=true') self.assertEqual(self.getconf('subproject:new_option'), True) + + def test_mtest_rebuild_deps(self): + testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') + self.init(testdir) + + with self.assertRaises(subprocess.CalledProcessError): + self._run(self.mtest_command) + self.clean() + + with self.assertRaises(subprocess.CalledProcessError): + self._run(self.mtest_command + ['runner-without-dep']) + self.clean() + + self._run(self.mtest_command + ['runner-with-exedep']) From 547dfac5fcb29c1f9076cbec34dbda5472ef572e Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 5 Nov 2024 03:24:33 -0500 Subject: [PATCH 136/624] fix trailing whitespace issues --- unittests/platformagnostictests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index ef277dee27bb..aa38b07e0a2e 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -78,7 +78,7 @@ def write_file(code: str): with tempfile.NamedTemporaryFile('w', dir=self.builddir, encoding='utf-8', delete=False) as f: f.write(code) return f.name - + fname = write_file("option('intminmax', type: 'integer', value: 10, min: 0, max: 5)") self.assertRaisesRegex(MesonException, 'Value 10 for option "intminmax" is more than maximum value 5.', interp.process, fname) @@ -86,7 +86,7 @@ def write_file(code: str): fname = write_file("option('array', type: 'array', choices : ['one', 'two', 'three'], value : ['one', 'four'])") self.assertRaisesRegex(MesonException, 'Value "four" for option "array" is not in allowed choices: "one, two, three"', interp.process, fname) - + fname = write_file("option('array', type: 'array', choices : ['one', 'two', 'three'], value : ['four', 'five', 'six'])") self.assertRaisesRegex(MesonException, 'Values "four, five, six" for option "array" are not in allowed choices: "one, two, three"', interp.process, fname) @@ -326,7 +326,7 @@ def test_editorconfig_match_path(self): ('a.txt', '{a,b,c}.txt', True), ('a.txt', '*.{txt,tex,cpp}', True), ('a.hpp', '*.{txt,tex,cpp}', False), - + ('a1.txt', 'a{0..9}.txt', True), ('a001.txt', 'a{0..9}.txt', True), ('a-1.txt', 'a{-10..10}.txt', True), @@ -376,14 +376,14 @@ def test_format_empty_file(self) -> None: for code in ('', '\n'): formatted = formatter.format(code, Path()) self.assertEqual('\n', formatted) - + def test_format_indent_comment_in_brackets(self) -> None: """Ensure comments in arrays and dicts are correctly indented""" formatter = Formatter(None, use_editor_config=False, fetch_subdirs=False) code = 'a = [\n # comment\n]\n' formatted = formatter.format(code, Path()) self.assertEqual(code, formatted) - + code = 'a = [\n # comment\n 1,\n]\n' formatted = formatter.format(code, Path()) self.assertEqual(code, formatted) @@ -391,7 +391,7 @@ def test_format_indent_comment_in_brackets(self) -> None: code = 'a = {\n # comment\n}\n' formatted = formatter.format(code, Path()) self.assertEqual(code, formatted) - + def test_error_configuring_subdir(self): testdir = os.path.join(self.common_test_dir, '152 index customtarget') out = self.init(os.path.join(testdir, 'subdir'), allow_fail=True) From 6299f181295b720391750d493456cd8d7bc3a0fd Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sun, 17 Nov 2024 00:57:19 -0500 Subject: [PATCH 137/624] ninja backend: don't rebuild all tests by default When running `ninja all` we shouldn't build testsuite programs as these might not be wanted e.g. in order to just install the project. We do want them to be built when running `ninja test`. Since meson 0.63 we actually have a dedicated ninja alias for test dependencies -- move these from the "all" rule to the dedicated test/benchmark rules. Closes: https://github.com/mesonbuild/meson/issues/1704 Closes: https://github.com/mesonbuild/meson/issues/1949 Closes: https://github.com/mesonbuild/meson/issues/2518 Closes: https://github.com/mesonbuild/meson/issues/3662 Closes: https://github.com/mesonbuild/meson/pull/5728 Closes: https://github.com/mesonbuild/meson/pull/5867 Closes: https://github.com/mesonbuild/meson/pull/6511 Closes: https://github.com/mesonbuild/meson/pull/11317 Closes: https://github.com/mesonbuild/meson/issues/13378 --- docs/markdown/snippets/test_dependencies.md | 29 +++++++++++++++++++++ mesonbuild/backend/ninjabackend.py | 8 ++---- run_project_tests.py | 9 ++++++- 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/test_dependencies.md diff --git a/docs/markdown/snippets/test_dependencies.md b/docs/markdown/snippets/test_dependencies.md new file mode 100644 index 000000000000..f69efa5cea9e --- /dev/null +++ b/docs/markdown/snippets/test_dependencies.md @@ -0,0 +1,29 @@ +## Test targets no longer built by default + +`meson test` and the `ninja all` rule have been reworked to no longer force +unnecessary rebuilds. + +`meson test` was invoking `ninja all` due to a bug if the chosen set of tests +had no build dependencies. The behavior is now the same as when tests do have +build dependencies, i.e. to only build the actual set of targets that are used +by the test. This change could cause failures when upgrading to Meson 1.7.0, if +the dependencies are not specified correctly in meson.build. Using `ninja test` +has always been guaranteed to "do the right thing" and rebuild `all` as well; +this continues to work. + +`ninja all` does not rebuild all tests anymore; it should be noted that this +change means test programs are no longer guaranteed to have been built, +depending on whether those test programs were *also* defined to build by +default / marked as installable. This avoids building test-only binaries as +part of installing the project (`ninja && ninja install`), which is unnecessary +and has no use case. + +Some users might have been relying on the "all" target building test +dependencies in combination with `meson test --no-rebuild` in order to skip +calling out to ninja when running tests. This might break with this change +because, when given `--no-rebuild`, Meson provides no guarantee that test +dependencies are present and up to date. The recommended workflow is to use +either `ninja test` or `ninja && meson test` but, if you wish to build test +programs and dependencies in a separate stage, you can use for example `ninja +all meson-test-prereq meson-benchmark-prereq` before `meson test --no-rebuild`. +These prereq targets have been available since meson 0.63.0. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7244ea90b84f..7e353c0afd46 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1327,7 +1327,7 @@ def generate_tests(self) -> None: cmd += ['--no-stdsplit'] if self.environment.coredata.get_option(OptionKey('errorlogs')): cmd += ['--print-errorlogs'] - elem = self.create_phony_target('test', 'CUSTOM_COMMAND', ['all', 'PHONY']) + elem = self.create_phony_target('test', 'CUSTOM_COMMAND', ['all', 'meson-test-prereq', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running all tests') elem.add_item('pool', 'console') @@ -1337,7 +1337,7 @@ def generate_tests(self) -> None: cmd = self.environment.get_build_command(True) + [ 'test', '--benchmark', '--logbase', 'benchmarklog', '--num-processes=1', '--no-rebuild'] - elem = self.create_phony_target('benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) + elem = self.create_phony_target('benchmark', 'CUSTOM_COMMAND', ['all', 'meson-benchmark-prereq', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running benchmark suite') elem.add_item('pool', 'console') @@ -3740,10 +3740,6 @@ def generate_ending(self) -> None: ('meson-test-prereq', self.get_testlike_targets()), ('meson-benchmark-prereq', self.get_testlike_targets(True))]: targetlist = [] - # These must also be built by default. - # XXX: Sometime in the future these should be built only before running tests. - if targ == 'all': - targetlist.extend(['meson-test-prereq', 'meson-benchmark-prereq']) for t in deps.values(): # Add the first output of each target to the 'all' target so that # they are all built diff --git a/run_project_tests.py b/run_project_tests.py index ab34c27f21d2..0dc287191f21 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -712,7 +712,14 @@ def _run_test(test: TestDef, # Build with subprocess def build_step() -> None: build_start = time.time() - pc, o, _ = Popen_safe(compile_commands + dir_args, cwd=test_build_dir, stderr=subprocess.STDOUT) + + if backend is Backend.ninja: + # FIXME: meson test inprocess does not handle running ninja via StringIO + targets = ['all', 'meson-test-prereq', 'meson-benchmark-prereq'] + else: + targets = [] + + pc, o, _ = Popen_safe(compile_commands + dir_args + targets, cwd=test_build_dir, stderr=subprocess.STDOUT) testresult.add_step(BuildStep.build, o, '', '', time.time() - build_start) if should_fail == 'build': if pc.returncode != 0: From 7492672af5c94ae80e021a4bb45c308c43f6071f Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Fri, 9 Aug 2024 10:18:02 +0200 Subject: [PATCH 138/624] interpreter: fix signature type of install_data_impl in `func_install_data`, `rename` parameter is `ContainerTypeInfo(list, str)` in `module/python.py`, rename is `None` `rename` is stored in `build.Data` object under the type `T.List[str]` --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4933ba65a1c2..c4d9a7d73483 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2539,7 +2539,7 @@ def func_install_data(self, node: mparser.BaseNode, follow_symlinks=kwargs['follow_symlinks']) def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str, - install_mode: FileMode, rename: T.Optional[str], + install_mode: FileMode, rename: T.Optional[T.List[str]], tag: T.Optional[str], install_data_type: T.Optional[str] = None, preserve_path: bool = False, From 223388a91e62b9a916900ebf15a96cecfb7e2eb0 Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Thu, 12 Dec 2024 17:17:40 +0100 Subject: [PATCH 139/624] qt module: remove quoted annotations --- mesonbuild/modules/_qt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 9f10c58266a5..04929a1b0497 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -109,7 +109,7 @@ class QtBaseModule(ExtensionModule): _moc_supports_depfiles = False _set_of_qt_tools = {'moc', 'uic', 'rcc', 'lrelease'} - def __init__(self, interpreter: 'Interpreter', qt_version: int = 5): + def __init__(self, interpreter: Interpreter, qt_version: int = 5): ExtensionModule.__init__(self, interpreter) self.qt_version = qt_version # It is important that this list does not change order as the order of @@ -126,7 +126,7 @@ def __init__(self, interpreter: 'Interpreter', qt_version: int = 5): 'compile_moc': self.compile_moc, }) - def compilers_detect(self, state: 'ModuleState', qt_dep: 'QtDependencyType') -> None: + def compilers_detect(self, state: ModuleState, qt_dep: QtDependencyType) -> None: """Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH""" wanted = f'== {qt_dep.version}' @@ -169,7 +169,7 @@ def get_version(p: T.Union[ExternalProgram, build.Executable]) -> str: 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: str, required: bool = True) -> None: if self._tools_detected: return self._tools_detected = True @@ -197,7 +197,7 @@ def _detect_tools(self, state: 'ModuleState', method: str, required: bool = True self.tools['lrelease'] = NonExistingExternalProgram(name='lrelease' + suffix) @staticmethod - def _qrc_nodes(state: 'ModuleState', rcc_file: 'FileOrString') -> T.Tuple[str, T.List[str]]: + def _qrc_nodes(state: ModuleState, rcc_file: FileOrString) -> T.Tuple[str, T.List[str]]: abspath: str if isinstance(rcc_file, str): abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file) @@ -225,8 +225,8 @@ def _qrc_nodes(state: 'ModuleState', rcc_file: 'FileOrString') -> T.Tuple[str, T except Exception: 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]: + def _parse_qrc_deps(self, state: ModuleState, + rcc_file_: T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]) -> T.List[File]: result: T.List[File] = [] inputs: T.Sequence['FileOrString'] = [] if isinstance(rcc_file_, (str, File)): @@ -273,7 +273,7 @@ def _parse_qrc_deps(self, state: 'ModuleState', 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: + def has_tools(self, state: ModuleState, args: T.Tuple, kwargs: HasToolKwArgs) -> bool: method = kwargs.get('method', 'auto') # We have to cast here because TypedDicts are invariant, even though # ExtractRequiredKwArgs is a subset of HasToolKwArgs, type checkers @@ -568,7 +568,7 @@ def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwarg 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=[]), ) - def compile_translations(self, state: 'ModuleState', args: T.Tuple, kwargs: 'CompileTranslationsKwArgs') -> ModuleReturnValue: + def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: CompileTranslationsKwArgs) -> ModuleReturnValue: ts_files = kwargs['ts_files'] if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in ts_files): FeatureNew.single_use('qt.compile_translations: custom_target or generator for "ts_files" keyword argument', From 7c68090a33cdf3e8cc96ee98529da89756c014e4 Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Wed, 5 Jun 2024 10:11:59 +0200 Subject: [PATCH 140/624] qt module: allow moc to generate json introspection file --- docs/markdown/Qt6-module.md | 4 ++++ mesonbuild/modules/_qt.py | 24 +++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index 7b6f94715882..9e4e0163707d 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -69,6 +69,8 @@ It takes no positional arguments, and the following keyword arguments: directory. For instance, when a file called `subdir/one.input` is processed it generates a file `{target private directory}/subdir/one.out` when `true`, and `{target private directory}/one.out` when `false` (default). + - `output_json` bool: *New in 1.7.0*. If `true`, generates additionnaly a + JSON representation which may be used by external tools such as qmltyperegistrar ## preprocess @@ -111,6 +113,8 @@ This method takes the following keyword arguments: directory. For instance, when a file called `subdir/one.input` is processed it generates a file `{target private directory}/subdir/one.out` when `true`, and `{target private directory}/one.out` when `false` (default). + - `moc_output_json` bool: *New in 1.7.0*. If `true`, generates additionnaly a + JSON representation which may be used by external tools such as qmltyperegistrar It returns an array of targets and sources to pass to a compilation target. diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 04929a1b0497..c37b1034dc58 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -62,6 +62,7 @@ class MocCompilerKwArgs(TypedDict): include_directories: T.List[T.Union[str, build.IncludeDirs]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]] preserve_paths: bool + output_json: bool class PreprocessKwArgs(TypedDict): @@ -73,6 +74,7 @@ class PreprocessKwArgs(TypedDict): moc_extra_arguments: T.List[str] rcc_extra_arguments: T.List[str] uic_extra_arguments: T.List[str] + moc_output_json: bool include_directories: T.List[T.Union[str, build.IncludeDirs]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]] method: str @@ -108,6 +110,7 @@ class QtBaseModule(ExtensionModule): _rcc_supports_depfiles = False _moc_supports_depfiles = False _set_of_qt_tools = {'moc', 'uic', 'rcc', 'lrelease'} + _moc_supports_json = False def __init__(self, interpreter: Interpreter, qt_version: int = 5): ExtensionModule.__init__(self, interpreter) @@ -182,6 +185,7 @@ def _detect_tools(self, state: ModuleState, method: str, required: bool = True) self.compilers_detect(state, qt) if version_compare(qt.version, '>=5.15.0'): self._moc_supports_depfiles = True + self._moc_supports_json = True else: mlog.warning('moc dependencies will not work properly until you move to Qt >= 5.15', fatal=False) if version_compare(qt.version, '>=5.14.0'): @@ -443,6 +447,7 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil 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'), + KwargInfo('output_json', bool, default=False, since='1.7.0'), ) def compile_moc(self, state: ModuleState, args: T.Tuple, kwargs: MocCompilerKwArgs) -> ModuleReturnValue: if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['headers']): @@ -475,20 +480,31 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. output: T.List[build.GeneratedList] = [] + do_output_json: bool = kwargs['output_json'] + if do_output_json and not self._moc_supports_json: + raise MesonException(f'moc-qt{self.qt_version} doesn\'t support "output_json" option') + # depfile arguments (defaults to .d) DEPFILE_ARGS: T.List[str] = ['--output-dep-file'] if self._moc_supports_depfiles else [] + JSON_ARGS: T.List[str] = ['--output-json'] if do_output_json else [] - arguments = kwargs['extra_args'] + DEPFILE_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] + arguments = kwargs['extra_args'] + DEPFILE_ARGS + JSON_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT0@'] preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None if kwargs['headers']: + header_gen_output: T.List[str] = ['moc_@BASENAME@.cpp'] + if do_output_json: + header_gen_output.append('moc_@BASENAME@.cpp.json') moc_gen = build.Generator( - self.tools['moc'], arguments, ['moc_@BASENAME@.cpp'], + self.tools['moc'], arguments, header_gen_output, 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)) 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( - self.tools['moc'], arguments, ['@BASENAME@.moc'], + 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)) @@ -510,6 +526,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. 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'), + KwargInfo('moc_output_json', bool, default=False, since='1.7.0'), ) def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwargs: PreprocessKwArgs) -> ModuleReturnValue: _sources = args[1:] @@ -551,6 +568,7 @@ def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwarg 'dependencies': kwargs['dependencies'], 'method': method, 'preserve_paths': kwargs['preserve_paths'], + 'output_json': kwargs['moc_output_json'] } sources.extend(self._compile_moc_impl(state, moc_kwargs)) From 4508622a34932d23e336392a8a3c71ed79af4e3f Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Thu, 30 May 2024 18:28:43 +0200 Subject: [PATCH 141/624] qt module: provide qml_module This aims to bring the support of QML modules to meson, the goal is to provide something similar to CMake `qt_add_qml_module` function provided by Qt (see https://doc.qt.io/qt-6/qt-add-qml-module.html ) Fixes: #6988, #9683 --- mesonbuild/modules/_qt.py | 531 +++++++++++++++++++++++++++++++++++++- 1 file changed, 526 insertions(+), 5 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index c37b1034dc58..7d52842f9dd4 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -8,16 +8,17 @@ import shutil import typing as T import xml.etree.ElementTree as ET +import re from . import ModuleReturnValue, ExtensionModule from .. import build from .. import options from .. import mlog from ..dependencies import find_external_dependency, Dependency, ExternalLibrary, InternalDependency -from ..mesonlib import MesonException, File, version_compare, Popen_safe +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 ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs +from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs, typed_pos_args from ..programs import NonExistingExternalProgram if T.TYPE_CHECKING: @@ -83,7 +84,7 @@ class PreprocessKwArgs(TypedDict): class HasToolKwArgs(kwargs.ExtractRequired): method: str - tools: T.List[Literal['moc', 'uic', 'rcc', 'lrelease']] + tools: T.List[Literal['moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar']] class CompileTranslationsKwArgs(TypedDict): @@ -95,6 +96,88 @@ class CompileTranslationsKwArgs(TypedDict): rcc_extra_arguments: T.List[str] ts_files: T.List[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]] + class GenQrcKwArgs(TypedDict): + + sources: T.Sequence[File] + aliases: T.Sequence[str] + prefix: str + output: str + + class GenQmldirKwArgs(TypedDict): + + module_name: str + module_version: str + module_prefix: str + qml_sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + qml_singletons: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + qml_internals: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + designer_supported: bool + imports: T.List[str] + optional_imports: T.List[str] + default_imports: T.List[str] + depends_imports: T.List[str] + typeinfo: str + output: str + + class GenQmlCachegenKwArgs(TypedDict): + + target_name: str + qml_sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] + qml_qrc: T.Union[FileOrString, build.GeneratedTypes] + extra_args: T.List[str] + module_prefix: str + method: str + + class GenQmlTypeRegistrarKwArgs(TypedDict): + + target_name: str + import_name: str + major_version: str + minor_version: str + namespace: str + typeinfo: str + generate_qmltype: bool + collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] + extra_args: T.List[str] + method: str + install: bool + install_dir: T.Optional[str] + + class MocJsonCollectKwArgs(TypedDict): + + target_name: str + moc_json: T.Sequence[build.GeneratedList] + method: str + + class QmlModuleKwArgs(TypedDict): + + version: str + qml_sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_singletons: T.List[T.Union[FileOrString, build.GeneratedTypes]] + qml_internals: T.List[T.Union[FileOrString, build.GeneratedTypes]] + resources_prefix: str + moc_headers: T.List[T.Union[FileOrString, build.GeneratedTypes]] + include_directories: T.List[T.Union[str, build.IncludeDirs]] + imports: T.List[str] + optional_imports: T.List[str] + default_imports: T.List[str] + depends_imports: T.List[str] + designer_supported: bool + namespace: str + typeinfo: str + moc_extra_arguments: T.List[str] + rcc_extra_arguments: T.List[str] + qmlcachegen_extra_arguments: T.List[str] + qmltyperegistrar_extra_arguments: T.List[str] + generate_qmldir: bool + generate_qmltype: bool + cachegen: bool + dependencies: T.List[T.Union[Dependency, ExternalLibrary]] + method: str + preserve_paths: bool + install_dir: str + install: bool + def _list_in_set_validator(choices: T.Set[str]) -> T.Callable[[T.List[str]], T.Optional[str]]: """Check that the choice given was one of the given set.""" def inner(checklist: T.List[str]) -> T.Optional[str]: @@ -105,12 +188,20 @@ def inner(checklist: T.List[str]) -> T.Optional[str]: return inner +#While Qt recomment module name to a be dot separated alphanum, it can technically be +#any well-formed ECMAScript Identifier Name. +#As best effort here we just check for illegal characters +#see https://doc.qt.io/qt-6/qtqml-modules-identifiedmodules.html +_MODULE_NAME_PUNCT = r'- {}<>()[\].:;~%?&,+^=|!\/*"\'' +_MODULE_NAME_RE = f'[^{_MODULE_NAME_PUNCT}0-9][^{_MODULE_NAME_PUNCT}]*(\\.[^{_MODULE_NAME_PUNCT}0-9][^{_MODULE_NAME_PUNCT}]*)*' + class QtBaseModule(ExtensionModule): _tools_detected = False _rcc_supports_depfiles = False _moc_supports_depfiles = False - _set_of_qt_tools = {'moc', 'uic', 'rcc', 'lrelease'} + _set_of_qt_tools = {'moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar'} _moc_supports_json = False + _support_qml_module = False def __init__(self, interpreter: Interpreter, qt_version: int = 5): ExtensionModule.__init__(self, interpreter) @@ -127,6 +218,7 @@ def __init__(self, interpreter: Interpreter, qt_version: int = 5): 'compile_resources': self.compile_resources, 'compile_ui': self.compile_ui, 'compile_moc': self.compile_moc, + 'qml_module': self.qml_module, }) def compilers_detect(self, state: ModuleState, qt_dep: QtDependencyType) -> None: @@ -183,6 +275,9 @@ def _detect_tools(self, state: ModuleState, method: str, required: bool = True) if qt.found(): # Get all tools and then make sure that they are the right version self.compilers_detect(state, qt) + if version_compare(qt.version, '>=6.2.0'): + #5.1x supports qmlcachegen and other tools to some extend, but arguments/build process marginally differs + self._support_qml_module = True if version_compare(qt.version, '>=5.15.0'): self._moc_supports_depfiles = True self._moc_supports_json = True @@ -486,7 +581,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. # depfile arguments (defaults to .d) DEPFILE_ARGS: T.List[str] = ['--output-dep-file'] if self._moc_supports_depfiles else [] - JSON_ARGS: T.List[str] = ['--output-json'] if do_output_json else [] + JSON_ARGS: T.List[str] = ['--output-json'] if do_output_json else [] arguments = kwargs['extra_args'] + DEPFILE_ARGS + JSON_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT0@'] preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None @@ -649,3 +744,429 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil return ModuleReturnValue(results.return_value[0], [results.new_objects, translations]) else: return ModuleReturnValue(translations, [translations]) + + def _source_to_files(self, state: ModuleState, sources: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]) -> T.List[File]: + + content_files = [] + for s in sources: + if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)): + for o in s.get_outputs(): + content_files.append(File.from_built_file(state.backend.get_target_dir(s), o)) + elif isinstance(s, File): + content_files.append(s) + elif isinstance(s, build.GeneratedList): + for gen_src in s.get_outputs(): + content_files.append(File.from_built_file(state.subdir, gen_src)) + else: + content_files.append(File.from_source_file( + state.environment.get_source_dir(), + state.subdir, + s + )) + return content_files + + def _gen_qrc(self, state: ModuleState, kwargs: GenQrcKwArgs) -> File: + + fileout = File.from_built_file(state.subdir, kwargs['output']) + fileout_abs = os.path.join(state.environment.build_dir, fileout.relative_name()) + if not os.path.isdir(state.environment.build_dir): + os.mkdir(state.environment.build_dir) + + rcc = ET.Element('RCC') + qresource = ET.SubElement(rcc, 'qresource', prefix='/' + kwargs['prefix']) + assert (len(kwargs['sources']) == len(kwargs['aliases'])) + for source, alias in zip(kwargs['sources'], kwargs['aliases']): + filenode = ET.SubElement(qresource, 'file', alias=alias) + filenode.text = source.absolute_path( + state.environment.get_source_dir(), + state.environment.get_build_dir() + ) + + tree = ET.ElementTree(rcc) + tree.write(fileout_abs) + return fileout + + def _gen_qmldir(self, state: ModuleState, kwargs: GenQmldirKwArgs) -> File: + module_name: str = kwargs['module_name'] + module_version: str = kwargs['module_version'] + module_prefix: str = kwargs['module_prefix'] + designer_supported: bool = kwargs['designer_supported'] + typeinfo_file: str = kwargs['typeinfo'] + + #Foo.Bar/1.0 foo.bar/auto foo.bar + import_re = re.compile(r'^(' + _MODULE_NAME_RE + r')(/((\d+(\.\d+)?)|auto))?$') + + fileout = File.from_built_file(state.subdir, kwargs['output']) + fileout_abs = os.path.join(state.environment.build_dir, fileout.relative_name()) + if not os.path.isdir(state.environment.build_dir): + os.mkdir(state.environment.build_dir) + + with open(fileout_abs, 'w', encoding='utf-8') as fd: + + def __gen_import(import_type: str, importlist: T.Sequence[str]) -> None: + for import_string in importlist: + match = import_re.match(import_string) + if not match: + raise MesonException(f'invalid syntax for qml import {import_string}') + module: str = match.group(1) + version: str = match.group(4) or '' + fd.write(f'{import_type} {module} {version}\n') + + def __gen_declaration(qualifier: str, version: str, importlist: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]]) -> None: + importpathlist = self._source_to_files(state, importlist) + for s in importpathlist: + basename: str = os.path.basename(s.fname) + classname: str = basename.rsplit('.', maxsplit=1)[0] + + if not basename.endswith(('.qml', '.js', '.mjs')): + raise MesonException(f'unexpected file type declared in qml sources {s}') + + if not classname or '.' in classname or classname[0].islower(): + raise MesonException(f'{basename} is not a valid QML file name') + if version: + fd.write(f'{qualifier}{classname} {version} {basename}\n') + else: + fd.write(f'{qualifier}{classname} {basename}\n') + + fd.write(f'module {module_name}\n') + fd.write(f'prefer :/{module_prefix}/\n') + + __gen_import('import', kwargs['imports']) + __gen_import('optional import', kwargs['optional_imports']) + __gen_import('default import', kwargs['default_imports']) + __gen_import('depends', kwargs['depends_imports']) + __gen_declaration('', module_version, kwargs['qml_sources']) + __gen_declaration('singleton ', module_version, kwargs['qml_singletons']) + __gen_declaration('internal ', '', kwargs['qml_internals']) + + if typeinfo_file: + fd.write(f'typeinfo {typeinfo_file}\n') + + if designer_supported: + fd.write('designersupported\n') + return fileout + + def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> build.CustomTarget: + self._detect_tools(state, kwargs['method']) + if not self.tools['moc'].found(): + raise MesonException('qt.qml_module: ' + + self.tools['moc'].name + ' not found') + + target_name: str = kwargs['target_name'] + moc_json: T.Sequence[build.GeneratedList] = kwargs['moc_json'] + + #there may be a better way :-/ + input_args: T.List[str] = [] + input_counter = 0 + for g in moc_json: + for fname in g.get_outputs(): + if fname.endswith('.json'): + input_args.append(f'@INPUT{input_counter}@') + input_counter += 1 + + return build.CustomTarget( + f'moc_collect_json_{target_name}', + state.subdir, + state.subproject, + state.environment, + self.tools['moc'].get_command() + ['--collect-json', '-o', '@OUTPUT@'] + input_args, + moc_json, + [f'{target_name}_json_collect.json'], + description=f'Collecting json type information for {target_name}', + ) + + def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> T.List[T.Union[build.CustomTarget, build.GeneratedList]]: + self._detect_tools(state, kwargs['method']) + if not self.tools['qmlcachegen'].found(): + raise MesonException('qt.qml_module: ' + + self.tools['qmlcachegen'].name + ' not found') + + target_name: str = kwargs['target_name'] + + command_args = ['-o', '@OUTPUT@'] + kwargs['extra_args'] + for qrc in self._source_to_files(state, [kwargs['qml_qrc']]): + command_args.extend(['--resource', qrc.absolute_path( + state.environment.get_source_dir(), + state.environment.get_build_dir() + )]) + + command_args.append('@INPUT@') + + cache_gen = build.Generator( + 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)) + + cachegen_inputs: T.List[str] = [] + qml_sources_paths = self._source_to_files(state, kwargs['qml_sources']) + for s in qml_sources_paths: + source_basename = os.path.basename(s.fname) + ressource_path = os.path.join('/', kwargs['module_prefix'], source_basename) + cachegen_inputs.append(ressource_path) + + cacheloader_target = build.CustomTarget( + f'cacheloader_{target_name}', + state.subdir, + state.subproject, + state.environment, + self.tools['qmlcachegen'].get_command() + ['-o', '@OUTPUT@'] + ['--resource-name', f'qmlcache_{target_name}'] + kwargs['extra_args'] + ['--resource=@INPUT@'] + cachegen_inputs, + [kwargs['qml_qrc']], + #output name format matters here + [f'{target_name}_qmlcache_loader.cpp'], + description=f'Qml cache loader for {target_name}', + ) + output.append(cacheloader_target) + return output + + def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwArgs) -> build.CustomTarget: + self._detect_tools(state, kwargs['method']) + if not self.tools['qmltyperegistrar'].found(): + raise MesonException('qt.qml_module: ' + + self.tools['qmltyperegistrar'].name + ' not found') + + import_name: str = kwargs['import_name'] + major_version: str = kwargs['major_version'] + minor_version: str = kwargs['minor_version'] + namespace: str = kwargs['namespace'] + typeinfo: str = kwargs['typeinfo'] + target_name: str = kwargs['target_name'] + collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] = kwargs['collected_json'] + + inputs: T.Sequence[T.Union[FileOrString, build.CustomTarget]] = [collected_json] if collected_json else [] + outputs: T.List[str] = [f'{target_name}_qmltyperegistrations.cpp'] + install_dir: T.List[T.Union[str, Literal[False]]] = [False] + install_tag: T.List[T.Union[str, None]] = [None] + + cmd = self.tools['qmltyperegistrar'].get_command() + [ + '--import-name', import_name, + '--major-version', major_version, + '--minor-version', minor_version, + '-o', '@OUTPUT0@', + ] + + cmd.extend(kwargs['extra_args']) + + if namespace: + cmd.extend(['--namespace', namespace]) + + if kwargs['generate_qmltype']: + cmd.extend(['--generate-qmltypes', '@OUTPUT1@']) + if typeinfo == '': + outputs.append(f'{target_name}.qmltypes') + else: + outputs.append(f'{typeinfo}') + install_dir.append(kwargs['install_dir']) + install_tag.append('devel') + + if collected_json: + cmd.append('@INPUT@') + + return build.CustomTarget( + f'typeregistrar_{target_name}', + state.subdir, + state.subproject, + state.environment, + cmd, + inputs, + outputs, + install=kwargs['install'], + install_dir=install_dir, + install_tag=install_tag, + description=f'Qml type registration for {target_name}', + ) + + @FeatureNew('qt.qml_module', '1.7') + @typed_pos_args('qt.qml_module', str) + @typed_kwargs( + 'qt.qml_module', + KwargInfo('version', str, default='254.254'), + #qml sources + KwargInfo('qml_sources', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), + KwargInfo('qml_singletons', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), + KwargInfo('qml_internals', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), + KwargInfo('resources_prefix', str, default='qt/qml'), + #qmldir generation + KwargInfo('imports', ContainerTypeInfo(list, (str)), default=[]), + KwargInfo('optional_imports', ContainerTypeInfo(list, (str)), default=[]), + KwargInfo('default_imports', ContainerTypeInfo(list, (str)), default=[]), + #match DEPENDENCIES argument from CMake, but dependencies keyword is already taken + KwargInfo('depends_imports', ContainerTypeInfo(list, (str)), default=[]), + KwargInfo('designer_supported', bool, default=False), + #for type registration, same arguments as moc + #moc_sources is voluntary ommited as typeregistrar needs to import a header + KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]), + KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]), + KwargInfo('namespace', str, default=''), + KwargInfo('typeinfo', str, default=''), + + KwargInfo('moc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('qmlcachegen_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('qmltyperegistrar_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]), + + KwargInfo('generate_qmldir', bool, default=True), + KwargInfo('generate_qmltype', bool, default=True), + KwargInfo('cachegen', bool, default=True), + + KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), + INSTALL_DIR_KW, + INSTALL_KW, + KwargInfo('method', str, default='auto'), + KwargInfo('preserve_paths', bool, default=False), + ) + def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKwArgs) -> ModuleReturnValue: + + self._detect_tools(state, kwargs['method']) + if not self._support_qml_module: + raise MesonException('qt.qml_module is not suppported for this version of Qt') + + #Major.Minor(.Patch) + version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?$') + module_name_re = re.compile(_MODULE_NAME_RE) + + output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = [] + + module_name: str = args[0] + if not module_name_re.fullmatch(module_name): + raise MesonException(f'qml module URI should be in the form Foo.Bar.xxx, got {module_name}') + + module_version: str = kwargs['version'] + module_version_match = version_re.match(module_version) + if not module_version_match: + raise MesonException(f'qml module version should be in the form Major.Minor, got {module_version}') + module_version_major: str = module_version_match.group(1) + module_version_minor: str = module_version_match.group(2) + #qt ignores .patch version + module_version_short = f'{module_version_major}.{module_version_minor}' + + module_prefix_list: T.List[str] = module_name.split('.') + module_prefix: str = os.path.join(*module_prefix_list) + module_prefix_full: str = os.path.join(*(kwargs['resources_prefix'].split('/') + module_prefix_list)) + + #same format as the one derived from qmltyperegistrar + target_name = re.sub(r'[^A-Za-z0-9]', '_', module_name) + + qrc_resouces: T.List[T.Union[FileOrString, build.GeneratedTypes]] = [] + all_qml: T.Sequence[T.Union[FileOrString, build.GeneratedTypes]] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals'] + all_qml_files: T.List[File] = self._source_to_files(state, all_qml) + all_qml_basename: T.List[str] = [os.path.basename(p.fname) for p in all_qml_files] + + install_dir: str = kwargs['install_dir'] or 'qml' + module_install_dir: str = os.path.join(install_dir, module_prefix) + + if len(all_qml) != 0: + qml_qrc_kwargs: GenQrcKwArgs = { + 'output': f'{target_name}_qml.qrc', + 'sources': all_qml_files, + 'aliases': all_qml_basename, + 'prefix': module_prefix_full, + } + qml_qrc = self._gen_qrc(state, qml_qrc_kwargs) + + if not kwargs['cachegen']: + qrc_resouces.append(qml_qrc) + else: + cachegen_kwargs: GenQmlCachegenKwArgs = { + 'target_name': target_name, + 'qml_qrc': qml_qrc, + 'qml_sources': all_qml, + 'module_prefix': module_prefix_full, + 'extra_args': kwargs['qmlcachegen_extra_arguments'], + 'method': kwargs['method'], + } + output.extend(self._gen_qml_cachegen(state, cachegen_kwargs)) + + #copy QML files for Qt tools + if kwargs['install']: + self.interpreter.install_data_impl(all_qml_files, module_install_dir, + FileMode(), all_qml_basename, 'devel') + + collected_json: T.Optional[T.Union[FileOrString, build.CustomTarget]] = None + if kwargs['moc_headers']: + compile_moc_kwargs: MocCompilerKwArgs = { + 'sources': [], + 'headers': kwargs['moc_headers'], + 'extra_args': kwargs['moc_extra_arguments'], + 'method': kwargs['method'], + 'include_directories': kwargs['include_directories'], + 'dependencies': kwargs['dependencies'], + 'preserve_paths': kwargs['preserve_paths'], + 'output_json': True, + } + moc_output = self._compile_moc_impl(state, compile_moc_kwargs) + output.extend(moc_output) + + moc_collect_json_kwargs: MocJsonCollectKwArgs = { + 'target_name': target_name, + 'moc_json': moc_output, + 'method': kwargs['method'], + } + collected_json = self._moc_json_collect(state, moc_collect_json_kwargs) + output.append(collected_json) + + typeinfo_file: str = '' + #cmake NO_GENERATE_QMLTYPE disable the whole type registration, not just the .qmltype generation + if kwargs['generate_qmltype']: + qmltyperegistrar_kwargs: GenQmlTypeRegistrarKwArgs = { + 'target_name': target_name, + 'import_name': module_name, + 'major_version': module_version_major, + 'minor_version': module_version_minor, + 'collected_json': collected_json, + 'namespace': kwargs['namespace'], + 'generate_qmltype': True, + 'extra_args': kwargs['qmltyperegistrar_extra_arguments'], + 'typeinfo': kwargs['typeinfo'], + 'method': kwargs['method'], + 'install': kwargs['install'], + 'install_dir': module_install_dir, + } + type_registrar_output = self._qml_type_registrar(state, qmltyperegistrar_kwargs) + output.append(type_registrar_output) + if len(type_registrar_output.get_outputs()) == 2: + typeinfo_file = type_registrar_output.get_outputs()[1] + + if kwargs['generate_qmldir']: + qmldir_kwargs: GenQmldirKwArgs = { + 'output': f'{target_name}_qmldir', + 'module_name': module_name, + 'module_version': module_version_short, + 'qml_sources': kwargs['qml_sources'], + 'qml_singletons': kwargs['qml_singletons'], + 'qml_internals': kwargs['qml_internals'], + 'imports': kwargs['imports'], + 'optional_imports': kwargs['optional_imports'], + 'default_imports': kwargs['default_imports'], + 'depends_imports': kwargs['depends_imports'], + 'designer_supported': kwargs['designer_supported'], + 'typeinfo': typeinfo_file, + 'module_prefix': module_prefix_full, + } + qmldir_file: File = self._gen_qmldir(state, qmldir_kwargs) + + qmldir_qrc_kwargs: GenQrcKwArgs = { + 'output': f'{target_name}_qmldir.qrc', + 'sources': self._source_to_files(state, [qmldir_file]), + 'aliases': ['qmldir'], + 'prefix': module_prefix_full, + } + qrc_resouces.append(self._gen_qrc(state, qmldir_qrc_kwargs)) + + if kwargs['install']: + self.interpreter.install_data_impl([qmldir_file], module_install_dir, + FileMode(), ['qmldir'], 'devel') + + if qrc_resouces: + compile_resource_kwargs: ResourceCompilerKwArgs = { + 'name': target_name, + 'sources': qrc_resouces, + 'extra_args': kwargs['rcc_extra_arguments'], + 'method': kwargs['method'], + } + output.extend(self._compile_resources_impl(state, compile_resource_kwargs)) + + return ModuleReturnValue(output, [output]) From 2450342535274d13adbad6c0ac93aca82649c5c6 Mon Sep 17 00:00:00 2001 From: gerioldman Date: Mon, 12 Aug 2024 01:21:08 +0200 Subject: [PATCH 142/624] Interpret TAP bailout output without test plan or test line as error --- mesonbuild/mtest.py | 3 ++- test cases/failing test/5 tap tests/meson.build | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 39970e530872..bb58f617cca5 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1125,7 +1125,8 @@ async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> No 'This is probably a bug in the test; if they are not TAP syntax, prefix them with a #') if all(t.result is TestResult.SKIP for t in self.results): # This includes the case where self.results is empty - res = TestResult.SKIP + if res != TestResult.ERROR: + res = TestResult.SKIP if res and self.res == TestResult.RUNNING: self.res = res diff --git a/test cases/failing test/5 tap tests/meson.build b/test cases/failing test/5 tap tests/meson.build index 664ac34d2f8b..27b9fe7ac82e 100644 --- a/test cases/failing test/5 tap tests/meson.build +++ b/test cases/failing test/5 tap tests/meson.build @@ -7,3 +7,4 @@ test('nonzero return code with tests', test_with_status, protocol: 'tap') test('missing test', tester, args : ['1..1'], protocol: 'tap') test('incorrect skip', tester, args : ['1..1 # skip\nok 1'], protocol: 'tap') test('partially skipped', tester, args : ['not ok 1\nok 2 # skip'], protocol: 'tap') +test('premature bailout', tester, args : ['Bail out!'], protocol: 'tap') From f2b22f87a1cc18693e043c7cd1021af613d9dfef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 18:46:33 +0100 Subject: [PATCH 143/624] rust: change warning_level=0 to "--cap-lints allow" This is a better and more backwards-compatible way to disable all warnings, compared to "-A warnings". The Rust RFC (https://rust-lang.github.io/rfcs/1193-cap-lints.html) explains the rationale: > We would very much like to be able to modify lints, however. For example > rust-lang/rust#26473 updated the missing_docs lint to also look for missing > documentation on const items. This ended up breaking some crates in the > ecosystem due to their usage of #![deny(missing_docs)]. While at it, document that Rust deviates from the other languages in its interpretation of warning_level=0. Signed-off-by: Paolo Bonzini --- docs/markdown/Builtin-options.md | 2 ++ mesonbuild/compilers/rust.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index f16a46ffebea..3a912805a057 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -98,6 +98,8 @@ machine](#specifying-options-per-machine) section for details. | force_fallback_for | [] | Force fallback for those dependencies | no | no | | vsenv | false | Activate Visual Studio environment | no | no | +(For the Rust language only, `warning_level=0` disables all warnings). + #### Details for `backend` Several build file formats are supported as command runners to build the diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 717d5635f842..bc3f4cb6c999 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -69,7 +69,7 @@ class RustCompiler(Compiler): id = 'rustc' _WARNING_LEVELS: T.Dict[str, T.List[str]] = { - '0': ['-A', 'warnings'], + '0': ['--cap-lints', 'allow'], '1': [], '2': [], '3': ['-W', 'warnings'], From 211f1cae3a9b398073695fd8da6be9dae9566579 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 13:40:23 +0100 Subject: [PATCH 144/624] rust: add warning_level=0 to downloaded Cargo subprojects This adds --cap-lints allow, matching how Cargo builds them. In the case of Cargo, this is only applied to non-path dependencies. Without this change, clippy will complain about dependencies as well. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/cargo_cap_lints.md | 8 ++++++++ mesonbuild/cargo/interpreter.py | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/cargo_cap_lints.md diff --git a/docs/markdown/snippets/cargo_cap_lints.md b/docs/markdown/snippets/cargo_cap_lints.md new file mode 100644 index 000000000000..9623ae157209 --- /dev/null +++ b/docs/markdown/snippets/cargo_cap_lints.md @@ -0,0 +1,8 @@ +## `--cap-lints allow` used for Cargo subprojects + +Similar to Cargo itself, all downloaded Cargo subprojects automatically +add the `--cap-lints allow` compiler argument, thus hiding any warnings +from the compiler. + +Related to this, `warning_level=0` now translates into `--cap-lints allow` +for Rust targets instead of `-A warnings`. diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 53c0a8095e0a..af272a86fb04 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -459,8 +459,9 @@ def _extra_deps_varname() -> str: class PackageState: - def __init__(self, manifest: Manifest) -> None: + 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) @@ -520,7 +521,10 @@ def _fetch_package(self, package_name: str, api: str) -> T.Tuple[PackageState, b 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) - pkg = PackageState(manifest) + 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 # Fetch required dependencies recursively. for depname, dep in manifest.dependencies.items(): @@ -602,6 +606,11 @@ def _create_project(self, pkg: PackageState, build: builder.Builder) -> T.List[m :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), @@ -613,7 +622,7 @@ def _create_project(self, pkg: PackageState, build: builder.Builder) -> T.List[m # 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([build.string(f'rust_std={pkg.manifest.package.edition}')]), + 'default_options': build.array(default_options), } if pkg.manifest.package.license: kwargs['license'] = build.string(pkg.manifest.package.license) From 04067fcad7fbf6cfbf1b8656caa8ec51e217e321 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 30 Dec 2024 09:54:16 +0100 Subject: [PATCH 145/624] clippy: skip running it if --cap-lints allow is included in the command line Meson builds libraries before running clippy, thus all dependencies must be present and clippy is run only for the warnings (instead, Cargo treats clippy as a separate toolchain and builds everything). In Meson's case thus it is pointless to run clippy whenever --cap-lints allow is included in the command line. Signed-off-by: Paolo Bonzini --- mesonbuild/scripts/clippy.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonbuild/scripts/clippy.py b/mesonbuild/scripts/clippy.py index a5161462cb35..c4746e3fdb28 100644 --- a/mesonbuild/scripts/clippy.py +++ b/mesonbuild/scripts/clippy.py @@ -41,15 +41,24 @@ def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, N cmdlist = list(clippy) prev = None + lints_cap = None for arg in src_block['parameters']: - if prev: + if prev == '--cap-lints': + cmdlist.append(prev) + lints_cap = arg + prev = None + elif prev: prev = None continue - elif arg in {'--emit', '--out-dir'}: + if arg in {'--emit', '--out-dir', '--cap-lints'}: prev = arg else: cmdlist.append(arg) + # no use in running clippy if it wouldn't print anything anyway + if lints_cap == 'allow': + break + cmdlist.extend(src_block['sources']) # the default for --emit is to go all the way to linking, # and --emit dep-info= is not enough for clippy to do From 7a8a89aab24d7942569b47585ee8e2fddc5692a5 Mon Sep 17 00:00:00 2001 From: fundawang Date: Thu, 18 Jul 2024 23:41:42 +0800 Subject: [PATCH 146/624] Add macros for declarative buildsystems of rpm >= 4.20 --- data/macros.meson | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/macros.meson b/data/macros.meson index dcac9d98553c..597741092734 100644 --- a/data/macros.meson +++ b/data/macros.meson @@ -45,3 +45,11 @@ --num-processes %{_smp_build_ncpus} \ --print-errorlogs \ %{nil}} + +# Declarative buildsystem, requires RPM 4.20+ to work +# https://rpm-software-management.github.io/rpm/manual/buildsystem.html +%buildsystem_meson_conf() %meson %* +%buildsystem_meson_generate_buildrequires() %{nil} +%buildsystem_meson_build() %meson_build %* +%buildsystem_meson_install() %meson_install %* +%buildsystem_meson_check() %meson_test %* From b0827fc1e2c5d989fa57dc7d91ba330dffaaf0aa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Jan 2025 08:15:55 +0100 Subject: [PATCH 147/624] rust: add 2024 edition Rust edition 2024 is being released in February, add support for rust_std=2024. Resolves: #14074 Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/rust-2024.md | 5 +++++ mesonbuild/compilers/rust.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/rust-2024.md diff --git a/docs/markdown/snippets/rust-2024.md b/docs/markdown/snippets/rust-2024.md new file mode 100644 index 000000000000..b1334d3261ad --- /dev/null +++ b/docs/markdown/snippets/rust-2024.md @@ -0,0 +1,5 @@ +## Support for Rust 2024 + +Meson can now request the compiler to use the 2024 edition of Rust. Use +`rust_std=2024` to activate it. Rust 2024 requires the 1.85.0 version +(or newer) of the compiler. diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index bc3f4cb6c999..6b9edb05dcdd 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -223,7 +223,7 @@ def get_options(self) -> MutableKeyedOptionDictType: return dict((self.create_option(options.UserComboOption, self.form_compileropt_key('std'), 'Rust edition to use', - ['none', '2015', '2018', '2021'], + ['none', '2015', '2018', '2021', '2024'], 'none'),)) def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: From 3b28fbf0d97b53d831c01dfe98634cff3dc8a9ec Mon Sep 17 00:00:00 2001 From: borg323 <39573933+borg323@users.noreply.github.com> Date: Tue, 24 Dec 2024 19:49:14 +0200 Subject: [PATCH 148/624] Update cpp language list for intel compiler on windows --- mesonbuild/compilers/cpp.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 5b654be5d1f7..2acc02ffe788 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -916,8 +916,13 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic IntelVisualStudioLikeCompiler.__init__(self, target) def get_options(self) -> 'MutableKeyedOptionDictType': - # This has only been tested with version 19.0, - cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] + # This has only been tested with version 19.0, 2021.2.1, 2024.4.2 and 2025.0.1 + if version_compare(self.version, '<2021.1.0'): + cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] + else: + cpp_stds = ['none', 'c++14', 'c++17', 'c++latest'] + if version_compare(self.version, '>=2024.1.0'): + cpp_stds += ['c++20'] return self._get_options_impl(super().get_options(), cpp_stds) def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: From a3679a64eec7c312c81d657880f34f015426c7db Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 3 Jan 2025 15:33:57 +0100 Subject: [PATCH 149/624] programs: favor version numbers with dots When using `find_program('perl')` we misdetect its version number: Program perl found: YES 40 40 (/usr/bin/perl) This is caused by Perl outputting the version information in a somewhat weird format: $ perl --version This is perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-thread-multi ... The problem here is that our version number detection picks the first match of at one digit followed by at least one more digit and/or dot. Consequently, as "40" matches that regular expression, we'd return its value as the version number. Naturally, the version number detection we perform is best-effort, only, as there is no standardized format used by all programs. That being said, we can do better here by first trying to match a dotted version number so that we'd match the "5.40.0" string, not the "40". And given that most projects use dotted version numbers this should be a strict improvement in cases where we have multiple digits in-text. The old behaviour continues to be used as a fallback though in case we weren't able to match anything to not regress functionality. The new regex also fixes another case: when the version information ends with a trailing dot, like "foo version 1.2.3.", then we'd also have matched that trailing dot. This can be for example the case when version numbers end with ".rc1" or something similar. While we'd ideally include such suffixes into the detected version, that issue is left for another day. Add a couple of tests. Signed-off-by: Patrick Steinhardt --- mesonbuild/programs.py | 5 ++++- unittests/internaltests.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 46d00ceca2dd..7f4cec52d8f0 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -122,7 +122,10 @@ def get_version(self, interpreter: T.Optional['Interpreter'] = None) -> str: output = o.strip() if not output: output = e.strip() - match = re.search(r'([0-9][0-9\.]+)', output) + + match = re.search(r'([0-9](\.[0-9]+)+)', output) + if not match: + match = re.search(r'([0-9][0-9\.]+)', output) if not match: raise mesonlib.MesonException(f'Could not find a version number in output of {raw_cmd!r}') self.cached_version = match.group(1) diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 69f52a413627..36354104907b 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -35,7 +35,7 @@ from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, typed_kwargs, ContainerTypeInfo, KwargInfo from mesonbuild.mesonlib import ( LibType, MachineChoice, PerMachine, Version, is_windows, is_osx, - is_cygwin, is_openbsd, search_version, MesonException, + is_cygwin, is_openbsd, search_version, MesonException, python_command, ) from mesonbuild.options import OptionKey from mesonbuild.interpreter.type_checking import in_set_validator, NoneType @@ -674,6 +674,33 @@ def _call_pkgbin(self, args, env=None): for lib in ('pthread', 'm', 'c', 'dl', 'rt'): self.assertNotIn(f'lib{lib}.a', link_arg, msg=link_args) + def test_program_version(self): + with tempfile.TemporaryDirectory() as tmpdir: + script_path = Path(tmpdir) / 'script.py' + script_path.write_text('import sys\nprint(sys.argv[1])\n', encoding='utf-8') + script_path.chmod(0o755) + + for output, expected in { + '': None, + '1': None, + '1.2.4': '1.2.4', + '1 1.2.4': '1.2.4', + 'foo version 1.2.4': '1.2.4', + 'foo 1.2.4.': '1.2.4', + 'foo 1.2.4': '1.2.4', + 'foo 1.2.4 bar': '1.2.4', + '50 5.4.0': '5.4.0', + 'This is perl 5, version 40, subversion 0 (v5.40.0)': '5.40.0', + 'git version 2.48.0.rc1': '2.48.0', + }.items(): + prog = ExternalProgram('script', command=[python_command, str(script_path), output], silent=True) + + if expected is None: + with self.assertRaisesRegex(MesonException, 'Could not find a version number'): + prog.get_version() + else: + self.assertEqual(prog.get_version(), expected) + def test_version_compare(self): comparefunc = mesonbuild.mesonlib.version_compare_many for (a, b, result) in [ From fbe5655707a945d5b0ebf3c5d29d91fffd91a7d6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 7 Jan 2025 08:55:40 -0800 Subject: [PATCH 150/624] templates: Update copyright information Intel's copyright was missing on these. --- mesonbuild/templates/cpptemplates.py | 1 + mesonbuild/templates/cstemplates.py | 1 + mesonbuild/templates/ctemplates.py | 1 + mesonbuild/templates/cudatemplates.py | 1 + mesonbuild/templates/dlangtemplates.py | 1 + mesonbuild/templates/fortrantemplates.py | 1 + mesonbuild/templates/javatemplates.py | 1 + mesonbuild/templates/mesontemplates.py | 1 + mesonbuild/templates/objcpptemplates.py | 1 + mesonbuild/templates/objctemplates.py | 1 + mesonbuild/templates/rusttemplates.py | 1 + mesonbuild/templates/samplefactory.py | 1 + mesonbuild/templates/sampleimpl.py | 1 + mesonbuild/templates/valatemplates.py | 1 + 14 files changed, 14 insertions(+) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index 70e4dd42b2a7..1975a76f0bd4 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py index 4b16b7265ec2..8522d54ee0d4 100644 --- a/mesonbuild/templates/cstemplates.py +++ b/mesonbuild/templates/cstemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index d7616054a814..d150fe52de12 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 12eefa5a86ef..7c42d342f7dd 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 2e9a32915e59..fdae841b2a46 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index 9ac001564c70..2ea54628f31d 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index e229d7add2fe..e55c5fa3ad21 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index db553c09dd75..caea28bd6e5c 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 33bff2d79fcf..85794a5c4d07 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index 8f46d91fd9b2..b96949968573 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 1dbf5b614115..8e9d5b88d121 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/samplefactory.py b/mesonbuild/templates/samplefactory.py index 0083c614a36d..438f90c9ac32 100644 --- a/mesonbuild/templates/samplefactory.py +++ b/mesonbuild/templates/samplefactory.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index c222a1bf9aa7..14b0460d79c9 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index 1520de0a708f..f577880d4d29 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations From 5c209e196683289319535ada713759b6f4244ef6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 11:58:50 -0800 Subject: [PATCH 151/624] templates: Add a Meson version Without a version certain kinds of warnings will be suppressed, which is bad. I've picked 1.0 because it's pretty old, except for Rust where I've maintained the 1.3.0 requirement --- mesonbuild/templates/cpptemplates.py | 2 ++ mesonbuild/templates/cstemplates.py | 2 ++ mesonbuild/templates/ctemplates.py | 2 ++ mesonbuild/templates/cudatemplates.py | 2 ++ mesonbuild/templates/dlangtemplates.py | 2 ++ mesonbuild/templates/fortrantemplates.py | 2 ++ mesonbuild/templates/javatemplates.py | 2 ++ mesonbuild/templates/mesontemplates.py | 4 ++++ mesonbuild/templates/objcpptemplates.py | 2 ++ mesonbuild/templates/objctemplates.py | 2 ++ mesonbuild/templates/rusttemplates.py | 13 +++++++++++-- mesonbuild/templates/sampleimpl.py | 9 +++++++-- mesonbuild/templates/valatemplates.py | 2 ++ 13 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index 1975a76f0bd4..f21729982d10 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -23,6 +23,7 @@ hello_cpp_meson_template = '''project('{project_name}', 'cpp', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3', 'cpp_std=c++14']) @@ -95,6 +96,7 @@ class {utoken}_PUBLIC {class_name} {{ lib_cpp_meson_template = '''project('{project_name}', 'cpp', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3', 'cpp_std=c++14']) # These arguments are only used to build the shared library diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py index 8522d54ee0d4..4dbb3986351c 100644 --- a/mesonbuild/templates/cstemplates.py +++ b/mesonbuild/templates/cstemplates.py @@ -26,6 +26,7 @@ hello_cs_meson_template = '''project('{project_name}', 'cs', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', @@ -63,6 +64,7 @@ lib_cs_meson_template = '''project('{project_name}', 'cs', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) stlib = shared_library('{lib_name}', '{source_file}', diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index d150fe52de12..a879b9ac5e6f 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -54,6 +54,7 @@ lib_c_meson_template = '''project('{project_name}', 'c', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) # These arguments are only used to build the shared library @@ -105,6 +106,7 @@ ''' hello_c_meson_template = '''project('{project_name}', 'c', + meson_version : '>= {meson_version}', version : '{version}', default_options : ['warning_level=3']) diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 7c42d342f7dd..c79150e8108a 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -23,6 +23,7 @@ hello_cuda_meson_template = '''project('{project_name}', ['cuda', 'cpp'], version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3', 'cpp_std=c++14']) @@ -95,6 +96,7 @@ class {utoken}_PUBLIC {class_name} {{ lib_cuda_meson_template = '''project('{project_name}', ['cuda', 'cpp'], version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) # These arguments are only used to build the shared library diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index fdae841b2a46..eb102aedeea1 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -26,6 +26,7 @@ hello_d_meson_template = '''project('{project_name}', 'd', version : '{version}', + meson_version : '>= {meson_version}', default_options: ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', @@ -64,6 +65,7 @@ lib_d_meson_template = '''project('{project_name}', 'd', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) stlib = static_library('{lib_name}', '{source_file}', diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index 2ea54628f31d..6e33836537b8 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -39,6 +39,7 @@ lib_fortran_meson_template = '''project('{project_name}', 'fortran', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) # These arguments are only used to build the shared library @@ -83,6 +84,7 @@ hello_fortran_meson_template = '''project('{project_name}', 'fortran', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index e55c5fa3ad21..caaa989200bc 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -26,6 +26,7 @@ hello_java_meson_template = '''project('{project_name}', 'java', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) exe = jar('{exe_name}', '{source_name}', @@ -66,6 +67,7 @@ lib_java_meson_template = '''project('{project_name}', 'java', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) jarlib = jar('{class_name}', '{source_file}', diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index caea28bd6e5c..e0f6ca3fbcab 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -11,6 +11,7 @@ meson_executable_template = '''project('{project_name}', {language}, version : '{version}', + meson_version : '>= {meson_version}', default_options : [{default_options}]) executable('{executable}', @@ -21,6 +22,7 @@ meson_jar_template = '''project('{project_name}', '{language}', version : '{version}', + meson_version : '>= {meson_version}', default_options : [{default_options}]) jar('{executable}', @@ -54,6 +56,7 @@ def create_meson_build(options: Arguments) -> None: content = meson_executable_template.format(project_name=options.name, language=language, version=options.version, + meson_version='1.0.0', executable=options.executable, sourcespec=sourcespec, depspec=depspec, @@ -62,6 +65,7 @@ def create_meson_build(options: Arguments) -> None: content = meson_jar_template.format(project_name=options.name, language=options.language, version=options.version, + meson_version='1.0.0' if options.language != 'rust' else '1.3.0', executable=options.executable, main_class=options.name, sourcespec=sourcespec, diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 85794a5c4d07..91ce177c3dd9 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -54,6 +54,7 @@ lib_objcpp_meson_template = '''project('{project_name}', 'objcpp', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) # These arguments are only used to build the shared library @@ -106,6 +107,7 @@ hello_objcpp_meson_template = '''project('{project_name}', 'objcpp', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index b96949968573..59a6cd88bf9f 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -54,6 +54,7 @@ lib_objc_meson_template = '''project('{project_name}', 'objc', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) # These arguments are only used to build the shared library @@ -106,6 +107,7 @@ hello_objc_meson_template = '''project('{project_name}', 'objc', version : '{version}', + meson_version : '>= {meson_version}', default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 8e9d5b88d121..83d8d4f954b3 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -8,6 +8,9 @@ from mesonbuild.templates.sampleimpl import FileImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + lib_rust_template = '''#![crate_name = "{crate_file}"] @@ -35,7 +38,8 @@ lib_rust_meson_template = '''project('{project_name}', 'rust', - version : '{version}', meson_version: '>=1.3.0', + version : '{version}', + meson_version : '>= {meson_version}', default_options : ['rust_std=2021', 'warning_level=3']) rust = import('rust') @@ -58,7 +62,8 @@ ''' hello_rust_meson_template = '''project('{project_name}', 'rust', - version : '{version}', meson_version: '>=1.3.0', + version : '{version}', + meson_version : '>= {meson_version}', default_options : ['rust_std=2021', 'warning_level=3']) exe = executable('{exe_name}', '{source_name}', @@ -77,6 +82,10 @@ class RustProject(FileImpl): lib_test_template = None lib_meson_template = lib_rust_meson_template + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' + def lib_kwargs(self) -> T.Dict[str, str]: kwargs = super().lib_kwargs() kwargs['crate_file'] = self.lowercase_token diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index 14b0460d79c9..b6c3895656c0 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -20,6 +20,7 @@ def __init__(self, args: Arguments): self.lowercase_token = re.sub(r'[^a-z0-9]', '_', self.name.lower()) self.uppercase_token = self.lowercase_token.upper() self.capitalized_token = self.lowercase_token.capitalize() + self.meson_version = '1.0.0' @abc.abstractmethod def create_executable(self) -> None: @@ -67,7 +68,8 @@ def create_executable(self) -> None: f.write(self.exe_meson_template.format(project_name=self.name, exe_name=self.name, source_name=source_name, - version=self.version)) + version=self.version, + meson_version=self.meson_version)) def create_library(self) -> None: lib_name = f'{self.capitalized_token}.{self.source_ext}' @@ -83,6 +85,7 @@ def create_library(self) -> None: 'lib_name': self.lowercase_token, 'test_name': self.lowercase_token, 'version': self.version, + 'meson_version': self.meson_version, } with open(lib_name, 'w', encoding='utf-8') as f: f.write(self.lib_template.format(**kwargs)) @@ -105,7 +108,8 @@ def create_executable(self) -> None: f.write(self.exe_meson_template.format(project_name=self.name, exe_name=self.name, source_name=source_name, - version=self.version)) + version=self.version, + meson_version=self.meson_version)) def lib_kwargs(self) -> T.Dict[str, str]: """Get Language specific keyword arguments @@ -126,6 +130,7 @@ def lib_kwargs(self) -> T.Dict[str, str]: 'lib_name': self.lowercase_token, 'test_name': self.lowercase_token, 'version': self.version, + 'meson_version': self.meson_version, } def create_library(self) -> None: diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index f577880d4d29..fd93ffaf40cb 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -13,6 +13,7 @@ ''' hello_vala_meson_template = '''project('{project_name}', ['c', 'vala'], + meson_version : '>= {meson_version}', version : '{version}') dependencies = [ @@ -48,6 +49,7 @@ ''' lib_vala_meson_template = '''project('{project_name}', ['c', 'vala'], + meson_version : '>= {meson_version}', version : '{version}') dependencies = [ From 68939da3bd33096f1f01e67ecd2f8ff1ee84a4d9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 12:18:38 -0800 Subject: [PATCH 152/624] templates: remove 'c' from vala projects Meson has added 'c' automatically to vala projects since 0.59, since the minimum version is now 1.0 this isn't needed --- mesonbuild/templates/mesontemplates.py | 3 +-- mesonbuild/templates/valatemplates.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index e0f6ca3fbcab..5c6a116fe4a2 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -52,9 +52,8 @@ def create_meson_build(options: Arguments) -> None: for x in options.deps.split(',')) depspec += '],' if options.language != 'java': - language = f"'{options.language}'" if options.language != 'vala' else ['c', 'vala'] content = meson_executable_template.format(project_name=options.name, - language=language, + language=options.language, version=options.version, meson_version='1.0.0', executable=options.executable, diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index fd93ffaf40cb..438434032b95 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -12,7 +12,7 @@ }} ''' -hello_vala_meson_template = '''project('{project_name}', ['c', 'vala'], +hello_vala_meson_template = '''project('{project_name}', 'vala', meson_version : '>= {meson_version}', version : '{version}') @@ -48,7 +48,7 @@ }} ''' -lib_vala_meson_template = '''project('{project_name}', ['c', 'vala'], +lib_vala_meson_template = '''project('{project_name}', 'vala', meson_version : '>= {meson_version}', version : '{version}') From b3d1b7af0707da946701f986e86c6e5421ae84e8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 12:22:58 -0800 Subject: [PATCH 153/624] templates: avoid repeating ourselves --- mesonbuild/templates/mesontemplates.py | 29 +++++++++----------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index 5c6a116fe4a2..83239648c544 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -51,24 +51,15 @@ def create_meson_build(options: Arguments) -> None: depspec += ',\n '.join(f"dependency('{x}')" for x in options.deps.split(',')) depspec += '],' - if options.language != 'java': - content = meson_executable_template.format(project_name=options.name, - language=options.language, - version=options.version, - meson_version='1.0.0', - executable=options.executable, - sourcespec=sourcespec, - depspec=depspec, - default_options=formatted_default_options) - else: - content = meson_jar_template.format(project_name=options.name, - language=options.language, - version=options.version, - meson_version='1.0.0' if options.language != 'rust' else '1.3.0', - executable=options.executable, - main_class=options.name, - sourcespec=sourcespec, - depspec=depspec, - default_options=formatted_default_options) + tmpl = meson_executable_template if options.language != 'java' else meson_jar_template + content = tmpl.format(project_name=options.name, + language=options.language, + version=options.version, + meson_version='1.0.0' if options.language != 'rust' else '1.3.0', + main_class=options.name, + executable=options.executable, + sourcespec=sourcespec, + depspec=depspec, + default_options=formatted_default_options) open('meson.build', 'w', encoding='utf-8').write(content) print('Generated meson.build file:\n\n' + content) From b94cbbfb79eaeaf863d77fca1dd051b102471ad3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 12:23:02 -0800 Subject: [PATCH 154/624] templates: use a proper context manager with open --- mesonbuild/templates/mesontemplates.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index 83239648c544..76f56a1a0201 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -61,5 +61,6 @@ def create_meson_build(options: Arguments) -> None: sourcespec=sourcespec, depspec=depspec, default_options=formatted_default_options) - open('meson.build', 'w', encoding='utf-8').write(content) + with open('meson.build', 'w', encoding='utf-8') as f: + f.write(content) print('Generated meson.build file:\n\n' + content) From 1b14526243c3e12c132ee8f81089fea0425bab7d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 14:28:14 -0800 Subject: [PATCH 155/624] templates: standardize Meson formatting We have all kinds of formatting, different indents, different line break standards. --- mesonbuild/templates/cpptemplates.py | 38 +++++++++++----- mesonbuild/templates/cstemplates.py | 37 ++++++++++----- mesonbuild/templates/ctemplates.py | 37 ++++++++++----- mesonbuild/templates/cudatemplates.py | 38 +++++++++++----- mesonbuild/templates/dlangtemplates.py | 49 +++++++++++++------- mesonbuild/templates/fortrantemplates.py | 37 ++++++++++----- mesonbuild/templates/javatemplates.py | 37 ++++++++++----- mesonbuild/templates/mesontemplates.py | 34 +++++++++----- mesonbuild/templates/objcpptemplates.py | 37 ++++++++++----- mesonbuild/templates/objctemplates.py | 34 +++++++++----- mesonbuild/templates/rusttemplates.py | 34 +++++++++----- mesonbuild/templates/valatemplates.py | 57 ++++++++++++++++-------- 12 files changed, 324 insertions(+), 145 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index f21729982d10..2d7955ed7af1 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -21,14 +21,19 @@ }} ''' -hello_cpp_meson_template = '''project('{project_name}', 'cpp', +hello_cpp_meson_template = '''project( + '{project_name}', + 'cpp', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3', - 'cpp_std=c++14']) + default_options : ['warning_level=3', 'cpp_std=c++14'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' @@ -94,29 +99,38 @@ class {utoken}_PUBLIC {class_name} {{ }} ''' -lib_cpp_meson_template = '''project('{project_name}', 'cpp', +lib_cpp_meson_template = '''project( + '{project_name}', + 'cpp', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3', 'cpp_std=c++14']) + default_options : ['warning_level=3', 'cpp_std=c++14'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, cpp_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py index 4dbb3986351c..7ab1548d2036 100644 --- a/mesonbuild/templates/cstemplates.py +++ b/mesonbuild/templates/cstemplates.py @@ -24,13 +24,19 @@ ''' -hello_cs_meson_template = '''project('{project_name}', 'cs', +hello_cs_meson_template = '''project( + '{project_name}', + 'cs', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' @@ -62,23 +68,32 @@ ''' -lib_cs_meson_template = '''project('{project_name}', 'cs', +lib_cs_meson_template = '''project( + '{project_name}', + 'cs', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -stlib = shared_library('{lib_name}', '{source_file}', +stlib = shared_library( + '{lib_name}', + '{source_file}', install : true, ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : stlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : stlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : stlib) + include_directories : include_directories('.'), + link_with : stlib, +) ''' diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index a879b9ac5e6f..cfb5a400312a 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -52,29 +52,38 @@ }} ''' -lib_c_meson_template = '''project('{project_name}', 'c', +lib_c_meson_template = '''project( + '{project_name}', + 'c', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, c_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) # Make this library usable from the system's # package manager. @@ -105,13 +114,19 @@ }} ''' -hello_c_meson_template = '''project('{project_name}', 'c', +hello_c_meson_template = '''project( + '{project_name}', + 'c', meson_version : '>= {meson_version}', version : '{version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index c79150e8108a..189fe98476ab 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -21,14 +21,19 @@ }} ''' -hello_cuda_meson_template = '''project('{project_name}', ['cuda', 'cpp'], +hello_cuda_meson_template = '''project( + '{project_name}', + ['cuda', 'cpp'], version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3', - 'cpp_std=c++14']) + default_options : ['warning_level=3', 'cpp_std=c++14'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' @@ -94,29 +99,38 @@ class {utoken}_PUBLIC {class_name} {{ }} ''' -lib_cuda_meson_template = '''project('{project_name}', ['cuda', 'cpp'], +lib_cuda_meson_template = '''project( + '{project_name}', + ['cuda', 'cpp'], version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, cpp_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index eb102aedeea1..85968e1f5824 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -24,13 +24,19 @@ }} ''' -hello_d_meson_template = '''project('{project_name}', 'd', - version : '{version}', - meson_version : '>= {meson_version}', - default_options: ['warning_level=3']) +hello_d_meson_template = '''project( + '{project_name}', + 'd', + version : '{version}', + meson_version : '>= {meson_version}', + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' @@ -63,32 +69,43 @@ }} ''' -lib_d_meson_template = '''project('{project_name}', 'd', +lib_d_meson_template = '''project( + '{project_name}', + 'd', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -stlib = static_library('{lib_name}', '{source_file}', +stlib = static_library( + '{lib_name}', + '{source_file}', install : true, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : stlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : stlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : stlib) + include_directories : include_directories('.'), + link_with : stlib, +) # Make this library usable from the Dlang # build system. dlang_mod = import('dlang') -if find_program('dub', required: false).found() - dlang_mod.generate_dub_file(meson.project_name().to_lower(), meson.source_root(), +if find_program('dub', required : false).found() + dlang_mod.generate_dub_file( + meson.project_name().to_lower(), + meson.source_root(), name : meson.project_name(), - license: meson.project_license(), + license : meson.project_license(), sourceFiles : '{source_file}', description : 'Meson sample project.', version : '{version}', diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index 6e33836537b8..c1a6e0148eda 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -37,29 +37,38 @@ end program ''' -lib_fortran_meson_template = '''project('{project_name}', 'fortran', +lib_fortran_meson_template = '''project( + '{project_name}', + 'fortran', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, fortran_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) pkg_mod = import('pkgconfig') pkg_mod.generate( @@ -82,13 +91,19 @@ end program ''' -hello_fortran_meson_template = '''project('{project_name}', 'fortran', +hello_fortran_meson_template = '''project( + '{project_name}', + 'fortran', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index caaa989200bc..8596ad946fe8 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -24,14 +24,20 @@ ''' -hello_java_meson_template = '''project('{project_name}', 'java', +hello_java_meson_template = '''project( + '{project_name}', + 'java', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = jar('{exe_name}', '{source_name}', +exe = jar( + '{exe_name}', + '{source_name}', main_class : '{exe_name}', - install : true) + install : true, +) test('basic', exe) ''' @@ -65,25 +71,34 @@ ''' -lib_java_meson_template = '''project('{project_name}', 'java', +lib_java_meson_template = '''project( + '{project_name}', + 'java', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -jarlib = jar('{class_name}', '{source_file}', +jarlib = jar( + '{class_name}', + '{source_file}', main_class : '{class_name}', install : true, ) -test_jar = jar('{class_test}', '{test_source_file}', +test_jar = jar( + '{class_test}', + '{test_source_file}', main_class : '{class_test}', - link_with : jarlib) + link_with : jarlib, +) test('{test_name}', test_jar) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : jarlib) + include_directories : include_directories('.'), + link_with : jarlib, +) ''' diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index 76f56a1a0201..5fe73930fbb3 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -9,26 +9,38 @@ if T.TYPE_CHECKING: from ..minit import Arguments -meson_executable_template = '''project('{project_name}', {language}, +meson_executable_template = '''project( + '{project_name}', + {language}, version : '{version}', meson_version : '>= {meson_version}', - default_options : [{default_options}]) + default_options : [{default_options}], +) + +executable( + '{executable}', + {sourcespec},{depspec} + install : true, +) -executable('{executable}', - {sourcespec},{depspec} - install : true) ''' -meson_jar_template = '''project('{project_name}', '{language}', +meson_jar_template = '''project( + '{project_name}', + '{language}', version : '{version}', meson_version : '>= {meson_version}', - default_options : [{default_options}]) + default_options : [{default_options}], +) + +jar( + '{executable}', + {sourcespec},{depspec} + main_class : '{main_class}', + install : true, +) -jar('{executable}', - {sourcespec},{depspec} - main_class: '{main_class}', - install : true) ''' diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 91ce177c3dd9..a9e1099a29d8 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -52,29 +52,38 @@ }} ''' -lib_objcpp_meson_template = '''project('{project_name}', 'objcpp', +lib_objcpp_meson_template = '''project( + '{project_name}', + 'objcpp', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, objcpp_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', - link_with : shlib) +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) # Make this library usable from the system's # package manager. @@ -105,13 +114,19 @@ }} ''' -hello_objcpp_meson_template = '''project('{project_name}', 'objcpp', +hello_objcpp_meson_template = '''project( + '{project_name}', + 'objcpp', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index 59a6cd88bf9f..64141e456863 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -52,29 +52,37 @@ }} ''' -lib_objc_meson_template = '''project('{project_name}', 'objc', +lib_objc_meson_template = '''project( + '{project_name}', + 'objc', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library('{lib_name}', '{source_file}', +shlib = shared_library( + '{lib_name}', + '{source_file}', install : true, objc_args : lib_args, gnu_symbol_visibility : 'hidden', ) -test_exe = executable('{test_exe_name}', '{test_source_file}', +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', link_with : shlib) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) # Make this library usable from the system's # package manager. @@ -105,13 +113,19 @@ }} ''' -hello_objc_meson_template = '''project('{project_name}', 'objc', +hello_objc_meson_template = '''project( + '{project_name}', + 'objc', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3'], +) -exe = executable('{exe_name}', '{source_name}', - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 83d8d4f954b3..a012afb85e11 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -37,21 +37,29 @@ ''' -lib_rust_meson_template = '''project('{project_name}', 'rust', +lib_rust_meson_template = '''project( + '{project_name}', + 'rust', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['rust_std=2021', 'warning_level=3']) + default_options : ['rust_std=2021', 'warning_level=3'], +) rust = import('rust') -shlib = static_library('{lib_name}', '{source_file}', install : true) +shlib = static_library( + '{lib_name}', + '{source_file}', + install : true, +) rust.test('{test_name}', shlib) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) ''' hello_rust_template = ''' @@ -61,13 +69,19 @@ }} ''' -hello_rust_meson_template = '''project('{project_name}', 'rust', +hello_rust_meson_template = '''project( + '{project_name}', + 'rust', version : '{version}', meson_version : '>= {meson_version}', - default_options : ['rust_std=2021', 'warning_level=3']) - -exe = executable('{exe_name}', '{source_name}', - install : true) + default_options : ['rust_std=2021', 'warning_level=3'], +) + +exe = executable( + '{exe_name}', + '{source_name}', + install : true, +) test('basic', exe) ''' diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index 438434032b95..b8ae6e0678bd 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -12,17 +12,24 @@ }} ''' -hello_vala_meson_template = '''project('{project_name}', 'vala', +hello_vala_meson_template = '''project( + '{project_name}', + 'vala', meson_version : '>= {meson_version}', - version : '{version}') + version : '{version}', +) dependencies = [ - dependency('glib-2.0'), - dependency('gobject-2.0'), + dependency('glib-2.0'), + dependency('gobject-2.0'), ] -exe = executable('{exe_name}', '{source_name}', dependencies : dependencies, - install : true) +exe = executable( + '{exe_name}', + '{source_name}', + dependencies : dependencies, + install : true, +) test('basic', exe) ''' @@ -48,30 +55,42 @@ }} ''' -lib_vala_meson_template = '''project('{project_name}', 'vala', +lib_vala_meson_template = '''project( + '{project_name}', + 'vala', meson_version : '>= {meson_version}', - version : '{version}') + version : '{version}', +) dependencies = [ - dependency('glib-2.0'), - dependency('gobject-2.0'), + dependency('glib-2.0'), + dependency('gobject-2.0'), ] # These arguments are only used to build the shared library # not the executables that use the library. -shlib = shared_library('foo', '{source_file}', - dependencies: dependencies, - install: true, - install_dir: [true, true, true]) - -test_exe = executable('{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib) +shlib = shared_library( + 'foo', + '{source_file}', + dependencies : dependencies, + install : true, + install_dir : [true, true, true], +) + +test_exe = executable( + '{test_exe_name}', + '{test_source_file}', + dependencies : dependencies, + link_with : shlib, +) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( - include_directories: include_directories('.'), - link_with : shlib) + include_directories : include_directories('.'), + link_with : shlib, +) + ''' From 19cf54418553d835ec1de4d8a919a442545c0272 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 14:45:08 -0800 Subject: [PATCH 156/624] templates: replace abc.abstractproperty with abc.abstractmethod and property because abstractproperty is deprecated --- mesonbuild/templates/sampleimpl.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index b6c3895656c0..f648b8463090 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -30,27 +30,33 @@ def create_executable(self) -> None: def create_library(self) -> None: pass - @abc.abstractproperty + @property + @abc.abstractmethod def exe_template(self) -> str: pass - @abc.abstractproperty + @property + @abc.abstractmethod def exe_meson_template(self) -> str: pass - @abc.abstractproperty + @property + @abc.abstractmethod def lib_template(self) -> str: pass - @abc.abstractproperty + @property + @abc.abstractmethod def lib_test_template(self) -> T.Optional[str]: pass - @abc.abstractproperty + @property + @abc.abstractmethod def lib_meson_template(self) -> str: pass - @abc.abstractproperty + @property + @abc.abstractmethod def source_ext(self) -> str: pass @@ -148,11 +154,13 @@ def create_library(self) -> None: class FileHeaderImpl(FileImpl): - @abc.abstractproperty + @property + @abc.abstractmethod def header_ext(self) -> str: pass - @abc.abstractproperty + @property + @abc.abstractmethod def lib_header_template(self) -> str: pass From 484ac23b512f2ccc008ad9b9520016fbaa3645f0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 15:11:16 -0800 Subject: [PATCH 157/624] templates: Don't overwrite files that exist This is going to allow us to re-use the implementation for the "create a new project" in the "add meson for an existing project" path. --- mesonbuild/templates/sampleimpl.py | 69 +++++++++++++++++------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index f648b8463090..7d2b4dd7cbf3 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -5,6 +5,7 @@ from __future__ import annotations import abc +import os import re import typing as T @@ -21,6 +22,7 @@ def __init__(self, args: Arguments): self.uppercase_token = self.lowercase_token.upper() self.capitalized_token = self.lowercase_token.capitalize() self.meson_version = '1.0.0' + self.force = args.force @abc.abstractmethod def create_executable(self) -> None: @@ -67,15 +69,17 @@ class ClassImpl(SampleImpl): def create_executable(self) -> None: source_name = f'{self.capitalized_token}.{self.source_ext}' - with open(source_name, 'w', encoding='utf-8') as f: - f.write(self.exe_template.format(project_name=self.name, - class_name=self.capitalized_token)) - 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, - source_name=source_name, - version=self.version, - meson_version=self.meson_version)) + if not os.path.exists(source_name): + with open(source_name, 'w', encoding='utf-8') as f: + f.write(self.exe_template.format(project_name=self.name, + class_name=self.capitalized_token)) + 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, + source_name=source_name, + version=self.version, + meson_version=self.meson_version)) def create_library(self) -> None: lib_name = f'{self.capitalized_token}.{self.source_ext}' @@ -93,13 +97,15 @@ def create_library(self) -> None: 'version': self.version, 'meson_version': self.meson_version, } - with open(lib_name, 'w', encoding='utf-8') as f: - f.write(self.lib_template.format(**kwargs)) - if self.lib_test_template: + if not os.path.exists(lib_name): + with open(lib_name, 'w', encoding='utf-8') as f: + f.write(self.lib_template.format(**kwargs)) + if self.lib_test_template and not os.path.exists(test_name): with open(test_name, 'w', encoding='utf-8') as f: f.write(self.lib_test_template.format(**kwargs)) - with open('meson.build', 'w', encoding='utf-8') as f: - f.write(self.lib_meson_template.format(**kwargs)) + if self.force or not os.path.exists('meson.build'): + with open('meson.build', 'w', encoding='utf-8') as f: + f.write(self.lib_meson_template.format(**kwargs)) class FileImpl(SampleImpl): @@ -108,14 +114,16 @@ class FileImpl(SampleImpl): def create_executable(self) -> None: source_name = f'{self.lowercase_token}.{self.source_ext}' - with open(source_name, 'w', encoding='utf-8') as f: - f.write(self.exe_template.format(project_name=self.name)) - 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, - source_name=source_name, - version=self.version, - meson_version=self.meson_version)) + if not os.path.exists(source_name): + with open(source_name, 'w', encoding='utf-8') as f: + f.write(self.exe_template.format(project_name=self.name)) + 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, + source_name=source_name, + version=self.version, + meson_version=self.meson_version)) def lib_kwargs(self) -> T.Dict[str, str]: """Get Language specific keyword arguments @@ -143,13 +151,15 @@ def create_library(self) -> None: lib_name = f'{self.lowercase_token}.{self.source_ext}' test_name = f'{self.lowercase_token}_test.{self.source_ext}' kwargs = self.lib_kwargs() - with open(lib_name, 'w', encoding='utf-8') as f: - f.write(self.lib_template.format(**kwargs)) - if self.lib_test_template: + if not os.path.exists(lib_name): + with open(lib_name, 'w', encoding='utf-8') as f: + f.write(self.lib_template.format(**kwargs)) + if self.lib_test_template and not os.path.exists(test_name): with open(test_name, 'w', encoding='utf-8') as f: f.write(self.lib_test_template.format(**kwargs)) - with open('meson.build', 'w', encoding='utf-8') as f: - f.write(self.lib_meson_template.format(**kwargs)) + if self.force or not os.path.exists('meson.build'): + with open('meson.build', 'w', encoding='utf-8') as f: + f.write(self.lib_meson_template.format(**kwargs)) class FileHeaderImpl(FileImpl): @@ -172,5 +182,6 @@ def lib_kwargs(self) -> T.Dict[str, str]: def create_library(self) -> None: super().create_library() kwargs = self.lib_kwargs() - with open(kwargs['header_file'], 'w', encoding='utf-8') as f: - f.write(self.lib_header_template.format_map(kwargs)) + if not os.path.exists(kwargs['header_file']): + with open(kwargs['header_file'], 'w', encoding='utf-8') as f: + f.write(self.lib_header_template.format_map(kwargs)) From 55e3a5ece5d90ea29dcb8ab2d0bb6898a71e8971 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 15:36:45 -0800 Subject: [PATCH 158/624] templates: plumb dependencies into the new project template The command line value exists, and we'll want it soon. --- mesonbuild/templates/cpptemplates.py | 10 ++++++++++ mesonbuild/templates/cstemplates.py | 10 ++++++++++ mesonbuild/templates/ctemplates.py | 10 ++++++++++ mesonbuild/templates/cudatemplates.py | 10 ++++++++++ mesonbuild/templates/dlangtemplates.py | 11 +++++++++++ mesonbuild/templates/fortrantemplates.py | 10 ++++++++++ mesonbuild/templates/javatemplates.py | 10 ++++++++++ mesonbuild/templates/objcpptemplates.py | 10 ++++++++++ mesonbuild/templates/objctemplates.py | 10 ++++++++++ mesonbuild/templates/rusttemplates.py | 9 +++++++++ mesonbuild/templates/sampleimpl.py | 12 ++++++++++-- mesonbuild/templates/valatemplates.py | 5 +++-- 12 files changed, 113 insertions(+), 4 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index 2d7955ed7af1..2e16f8d0da7b 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -29,10 +29,14 @@ default_options : ['warning_level=3', 'cpp_std=c++14'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', install : true, + dependencies : dependencies, ) test('basic', exe) @@ -107,6 +111,9 @@ class {utoken}_PUBLIC {class_name} {{ default_options : ['warning_level=3', 'cpp_std=c++14'], ) +dependencies = [{dependencies} +] + # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] @@ -117,11 +124,13 @@ class {utoken}_PUBLIC {class_name} {{ install : true, cpp_args : lib_args, gnu_symbol_visibility : 'hidden', + dependencies : dependencies, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', + dependencies : dependencies, link_with : shlib, ) test('{test_name}', test_exe) @@ -129,6 +138,7 @@ class {utoken}_PUBLIC {class_name} {{ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py index 7ab1548d2036..040debc152ed 100644 --- a/mesonbuild/templates/cstemplates.py +++ b/mesonbuild/templates/cstemplates.py @@ -32,10 +32,14 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', install : true, + dependencies : dependencies, ) test('basic', exe) @@ -76,15 +80,20 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + stlib = shared_library( '{lib_name}', '{source_file}', + dependencies : dependencies, install : true, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', + dependencies : dependencies, link_with : stlib, ) test('{test_name}', test_exe) @@ -92,6 +101,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : stlib, ) diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index cfb5a400312a..5772dc2eb339 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -64,17 +64,22 @@ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] +dependencies = [{dependencies} +] + shlib = shared_library( '{lib_name}', '{source_file}', install : true, c_args : lib_args, gnu_symbol_visibility : 'hidden', + dependencies : dependencies, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', + dependencies : dependencies, link_with : shlib, ) test('{test_name}', test_exe) @@ -82,6 +87,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) @@ -122,9 +128,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 189fe98476ab..80b72c7506f0 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -29,9 +29,13 @@ default_options : ['warning_level=3', 'cpp_std=c++14'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) @@ -111,24 +115,30 @@ class {utoken}_PUBLIC {class_name} {{ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] +dependencies = [{dependencies} +] + shlib = shared_library( '{lib_name}', '{source_file}', install : true, cpp_args : lib_args, gnu_symbol_visibility : 'hidden', + dependencies : dependencies, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', link_with : shlib, + dependencies : dependencies, ) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 85968e1f5824..00ac3bcc3ba8 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -32,9 +32,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) @@ -77,23 +81,30 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + + stlib = static_library( '{lib_name}', '{source_file}', install : true, gnu_symbol_visibility : 'hidden', + dependencies : dependencies, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', link_with : stlib, + dependencies : dependencies, ) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : stlib, ) diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index c1a6e0148eda..3d139612a6ef 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -49,24 +49,30 @@ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] +dependencies = [{dependencies} +] + shlib = shared_library( '{lib_name}', '{source_file}', install : true, fortran_args : lib_args, gnu_symbol_visibility : 'hidden', + dependencies : dependencies, ) test_exe = executable( '{test_exe_name}', '{test_source_file}', link_with : shlib, + dependencies : dependencies, ) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) @@ -99,9 +105,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index 8596ad946fe8..c79a8e751af0 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -32,10 +32,14 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = jar( '{exe_name}', '{source_name}', main_class : '{exe_name}', + dependencies : dependencies, install : true, ) @@ -79,9 +83,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + jarlib = jar( '{class_name}', '{source_file}', + dependencies : dependencies, main_class : '{class_name}', install : true, ) @@ -90,6 +98,7 @@ '{class_test}', '{test_source_file}', main_class : '{class_test}', + dependencies : dependencies, link_with : jarlib, ) test('{test_name}', test_jar) @@ -97,6 +106,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : jarlib, ) ''' diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index a9e1099a29d8..dd67c3748aba 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -60,6 +60,9 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] @@ -69,12 +72,14 @@ '{source_file}', install : true, objcpp_args : lib_args, + dependencies : dependencies, gnu_symbol_visibility : 'hidden', ) test_exe = executable( '{test_exe_name}', '{test_source_file}', + dependencies : dependencies, link_with : shlib, ) test('{test_name}', test_exe) @@ -82,6 +87,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) @@ -122,9 +128,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index 64141e456863..30f4804242ce 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -60,6 +60,9 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + # These arguments are only used to build the shared library # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] @@ -69,18 +72,21 @@ '{source_file}', install : true, objc_args : lib_args, + dependencies : dependencies, gnu_symbol_visibility : 'hidden', ) test_exe = executable( '{test_exe_name}', '{test_source_file}', + dependencies : dependencies, link_with : shlib) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) @@ -121,9 +127,13 @@ default_options : ['warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index a012afb85e11..7eab663bb57a 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -47,9 +47,13 @@ rust = import('rust') +dependencies = [{dependencies} +] + shlib = static_library( '{lib_name}', '{source_file}', + dependencies : dependencies, install : true, ) @@ -58,6 +62,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) ''' @@ -77,9 +82,13 @@ default_options : ['rust_std=2021', 'warning_level=3'], ) +dependencies = [{dependencies} +] + exe = executable( '{exe_name}', '{source_name}', + dependencies : dependencies, install : true, ) diff --git a/mesonbuild/templates/sampleimpl.py b/mesonbuild/templates/sampleimpl.py index 7d2b4dd7cbf3..d033f3c143d5 100644 --- a/mesonbuild/templates/sampleimpl.py +++ b/mesonbuild/templates/sampleimpl.py @@ -23,6 +23,7 @@ def __init__(self, args: Arguments): self.capitalized_token = self.lowercase_token.capitalize() self.meson_version = '1.0.0' self.force = args.force + self.dependencies = args.deps.split(',') if args.deps else [] @abc.abstractmethod def create_executable(self) -> None: @@ -62,6 +63,9 @@ def lib_meson_template(self) -> str: def source_ext(self) -> str: pass + def _format_dependencies(self) -> str: + return ''.join(f"\n dependency('{d}')," for d in self.dependencies) + class ClassImpl(SampleImpl): @@ -79,7 +83,8 @@ def create_executable(self) -> None: exe_name=self.name, source_name=source_name, version=self.version, - meson_version=self.meson_version)) + meson_version=self.meson_version, + dependencies=self._format_dependencies())) def create_library(self) -> None: lib_name = f'{self.capitalized_token}.{self.source_ext}' @@ -96,6 +101,7 @@ def create_library(self) -> None: 'test_name': self.lowercase_token, 'version': self.version, 'meson_version': self.meson_version, + 'dependencies': self._format_dependencies(), } if not os.path.exists(lib_name): with open(lib_name, 'w', encoding='utf-8') as f: @@ -123,7 +129,8 @@ def create_executable(self) -> None: exe_name=self.name, source_name=source_name, version=self.version, - meson_version=self.meson_version)) + meson_version=self.meson_version, + dependencies=self._format_dependencies())) def lib_kwargs(self) -> T.Dict[str, str]: """Get Language specific keyword arguments @@ -145,6 +152,7 @@ def lib_kwargs(self) -> T.Dict[str, str]: 'test_name': self.lowercase_token, 'version': self.version, 'meson_version': self.meson_version, + 'dependencies': self._format_dependencies(), } def create_library(self) -> None: diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index b8ae6e0678bd..41cee292555a 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -21,7 +21,7 @@ dependencies = [ dependency('glib-2.0'), - dependency('gobject-2.0'), + dependency('gobject-2.0'),{dependencies} ] exe = executable( @@ -64,7 +64,7 @@ dependencies = [ dependency('glib-2.0'), - dependency('gobject-2.0'), + dependency('gobject-2.0'),{dependencies} ] # These arguments are only used to build the shared library @@ -88,6 +88,7 @@ # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), + dependencies : dependencies, link_with : shlib, ) From 9f0ac314ba0c54cc18c2499845324efc14c1849e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 15:15:44 -0800 Subject: [PATCH 159/624] templates: re-use existing template code for existing libraries This reduces duplication around the meson version, default options, etc. It also allows us to use the default initialization for libraries --- mesonbuild/templates/mesontemplates.py | 72 +++----------------------- 1 file changed, 7 insertions(+), 65 deletions(-) diff --git a/mesonbuild/templates/mesontemplates.py b/mesonbuild/templates/mesontemplates.py index 5fe73930fbb3..23269392fdbe 100644 --- a/mesonbuild/templates/mesontemplates.py +++ b/mesonbuild/templates/mesontemplates.py @@ -6,73 +6,15 @@ import typing as T +from .samplefactory import sample_generator + if T.TYPE_CHECKING: from ..minit import Arguments -meson_executable_template = '''project( - '{project_name}', - {language}, - version : '{version}', - meson_version : '>= {meson_version}', - default_options : [{default_options}], -) - -executable( - '{executable}', - {sourcespec},{depspec} - install : true, -) - -''' - - -meson_jar_template = '''project( - '{project_name}', - '{language}', - version : '{version}', - meson_version : '>= {meson_version}', - default_options : [{default_options}], -) - -jar( - '{executable}', - {sourcespec},{depspec} - main_class : '{main_class}', - install : true, -) - -''' - def create_meson_build(options: Arguments) -> None: - if options.type != 'executable': - raise SystemExit('\nGenerating a meson.build file from existing sources is\n' - 'supported only for project type "executable".\n' - 'Run meson init in an empty directory to create a sample project.') - default_options = ['warning_level=3'] - if options.language == 'cpp': - # This shows how to set this very common option. - default_options += ['cpp_std=c++14'] - # If we get a meson.build autoformatter one day, this code could - # be simplified quite a bit. - formatted_default_options = ', '.join(f"'{x}'" for x in default_options) - sourcespec = ',\n '.join(f"'{x}'" for x in options.srcfiles) - depspec = '' - if options.deps: - depspec = '\n dependencies : [\n ' - depspec += ',\n '.join(f"dependency('{x}')" - for x in options.deps.split(',')) - depspec += '],' - tmpl = meson_executable_template if options.language != 'java' else meson_jar_template - content = tmpl.format(project_name=options.name, - language=options.language, - version=options.version, - meson_version='1.0.0' if options.language != 'rust' else '1.3.0', - main_class=options.name, - executable=options.executable, - sourcespec=sourcespec, - depspec=depspec, - default_options=formatted_default_options) - with open('meson.build', 'w', encoding='utf-8') as f: - f.write(content) - print('Generated meson.build file:\n\n' + content) + proj = sample_generator(options) + if options.type == 'executable': + proj.create_executable() + else: + proj.create_library() From d1422509fc00450928e7a8c40b4fbacc05464f68 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 15:48:33 -0800 Subject: [PATCH 160/624] templates: add meson.override_dependency() --- mesonbuild/templates/cpptemplates.py | 1 + mesonbuild/templates/cstemplates.py | 1 + mesonbuild/templates/ctemplates.py | 1 + mesonbuild/templates/cudatemplates.py | 1 + mesonbuild/templates/dlangtemplates.py | 1 + mesonbuild/templates/fortrantemplates.py | 1 + mesonbuild/templates/javatemplates.py | 1 + mesonbuild/templates/objcpptemplates.py | 1 + mesonbuild/templates/objctemplates.py | 1 + mesonbuild/templates/rusttemplates.py | 1 + mesonbuild/templates/valatemplates.py | 1 + 11 files changed, 11 insertions(+) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index 2e16f8d0da7b..bb09bcb09a87 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -141,6 +141,7 @@ class {utoken}_PUBLIC {class_name} {{ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/cstemplates.py b/mesonbuild/templates/cstemplates.py index 040debc152ed..59c718953271 100644 --- a/mesonbuild/templates/cstemplates.py +++ b/mesonbuild/templates/cstemplates.py @@ -104,6 +104,7 @@ dependencies : dependencies, link_with : stlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) ''' diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index 5772dc2eb339..7cb2de551e6b 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -90,6 +90,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 80b72c7506f0..7db1e0bc6a8e 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -141,6 +141,7 @@ class {utoken}_PUBLIC {class_name} {{ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 00ac3bcc3ba8..db3bdbf16bff 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -107,6 +107,7 @@ dependencies : dependencies, link_with : stlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the Dlang # build system. diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index 3d139612a6ef..a6fd2ef31490 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -75,6 +75,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) pkg_mod = import('pkgconfig') pkg_mod.generate( diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index c79a8e751af0..a2b3ec4dcf0f 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -109,6 +109,7 @@ dependencies : dependencies, link_with : jarlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) ''' diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index dd67c3748aba..be6c1fde08cc 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -90,6 +90,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index 30f4804242ce..ff3aba10c881 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -89,6 +89,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) # Make this library usable from the system's # package manager. diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 7eab663bb57a..2f2d940128c3 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -65,6 +65,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) ''' hello_rust_template = ''' diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index 41cee292555a..3720c3cb3419 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -91,6 +91,7 @@ dependencies : dependencies, link_with : shlib, ) +meson.override_dependency('{project_name}', {ltoken}_dep) ''' From e4ea17bf1d1443269b1fc37f87afd54b30abb8e3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 15:54:19 -0800 Subject: [PATCH 161/624] templates: Use modern pkgconfig generator API --- mesonbuild/templates/cpptemplates.py | 5 +---- mesonbuild/templates/ctemplates.py | 5 +---- mesonbuild/templates/cudatemplates.py | 5 +---- mesonbuild/templates/fortrantemplates.py | 5 +---- mesonbuild/templates/objcpptemplates.py | 5 +---- mesonbuild/templates/objctemplates.py | 5 +---- 6 files changed, 6 insertions(+), 24 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index bb09bcb09a87..bf95f9a5805d 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -149,12 +149,9 @@ class {utoken}_PUBLIC {class_name} {{ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index 7cb2de551e6b..b60e916b8469 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -98,12 +98,9 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 7db1e0bc6a8e..4f0c606a41e6 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -149,12 +149,9 @@ class {utoken}_PUBLIC {class_name} {{ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index a6fd2ef31490..f9aec7ef2b7e 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -79,12 +79,9 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index be6c1fde08cc..7ea4c3f8ee23 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -98,12 +98,9 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index ff3aba10c881..de0217f96b93 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -97,12 +97,9 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - name : '{project_name}', - filebase : '{ltoken}', + shlib, description : 'Meson sample project.', subdirs : '{header_dir}', - libraries : shlib, - version : '{version}', ) ''' From 2180af4ac1537cf42d47891cf19e53de02087d2a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 6 Jan 2025 16:12:46 -0800 Subject: [PATCH 162/624] templates: use library() instead of shared_library() --- mesonbuild/templates/cpptemplates.py | 17 ++++++++++++----- mesonbuild/templates/ctemplates.py | 18 +++++++++++++----- mesonbuild/templates/cudatemplates.py | 18 +++++++++++++----- mesonbuild/templates/fortrantemplates.py | 18 +++++++++++++----- mesonbuild/templates/objcpptemplates.py | 18 +++++++++++++----- mesonbuild/templates/objctemplates.py | 18 +++++++++++++----- mesonbuild/templates/rusttemplates.py | 6 +++--- mesonbuild/templates/valatemplates.py | 6 +++--- 8 files changed, 83 insertions(+), 36 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index bf95f9a5805d..eead3cf05f0b 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -3,9 +3,12 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileHeaderImpl +if T.TYPE_CHECKING: + from ..minit import Arguments hello_cpp_template = '''#include @@ -118,11 +121,11 @@ class {utoken}_PUBLIC {class_name} {{ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - cpp_args : lib_args, + cpp_shared_args : lib_args, gnu_symbol_visibility : 'hidden', dependencies : dependencies, ) @@ -131,7 +134,7 @@ class {utoken}_PUBLIC {class_name} {{ '{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib, + link_with : lib, ) test('{test_name}', test_exe) @@ -139,7 +142,7 @@ class {utoken}_PUBLIC {class_name} {{ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) @@ -149,7 +152,7 @@ class {utoken}_PUBLIC {class_name} {{ pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -166,3 +169,7 @@ class CppProject(FileHeaderImpl): lib_header_template = lib_hpp_template lib_test_template = lib_cpp_test_template lib_meson_template = lib_cpp_meson_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index b60e916b8469..7cf04f6aa9c8 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -3,9 +3,13 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileHeaderImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + lib_h_template = '''#pragma once #if defined _WIN32 || defined __CYGWIN__ @@ -67,11 +71,11 @@ dependencies = [{dependencies} ] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - c_args : lib_args, + c_shared_args : lib_args, gnu_symbol_visibility : 'hidden', dependencies : dependencies, ) @@ -80,7 +84,7 @@ '{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib, + link_with : lib, ) test('{test_name}', test_exe) @@ -88,7 +92,7 @@ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) @@ -98,7 +102,7 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -150,3 +154,7 @@ class CProject(FileHeaderImpl): lib_header_template = lib_h_template lib_test_template = lib_c_test_template lib_meson_template = lib_c_meson_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 4f0c606a41e6..416ba376a05f 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -3,9 +3,13 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileHeaderImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + hello_cuda_template = '''#include @@ -118,11 +122,11 @@ class {utoken}_PUBLIC {class_name} {{ dependencies = [{dependencies} ] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - cpp_args : lib_args, + cpp_shared_args : lib_args, gnu_symbol_visibility : 'hidden', dependencies : dependencies, ) @@ -130,7 +134,7 @@ class {utoken}_PUBLIC {class_name} {{ test_exe = executable( '{test_exe_name}', '{test_source_file}', - link_with : shlib, + link_with : lib, dependencies : dependencies, ) test('{test_name}', test_exe) @@ -139,7 +143,7 @@ class {utoken}_PUBLIC {class_name} {{ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) @@ -149,7 +153,7 @@ class {utoken}_PUBLIC {class_name} {{ pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -166,3 +170,7 @@ class CudaProject(FileHeaderImpl): lib_header_template = lib_h_template lib_test_template = lib_cuda_test_template lib_meson_template = lib_cuda_meson_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index f9aec7ef2b7e..7aaa9d39cf40 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -3,9 +3,13 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + lib_fortran_template = ''' ! This procedure will not be exported and is not ! directly callable by users of this library. @@ -52,11 +56,11 @@ dependencies = [{dependencies} ] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - fortran_args : lib_args, + fortran_shared_args : lib_args, gnu_symbol_visibility : 'hidden', dependencies : dependencies, ) @@ -64,7 +68,7 @@ test_exe = executable( '{test_exe_name}', '{test_source_file}', - link_with : shlib, + link_with : lib, dependencies : dependencies, ) test('{test_name}', test_exe) @@ -73,13 +77,13 @@ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -125,3 +129,7 @@ class FortranProject(FileImpl): lib_template = lib_fortran_template lib_meson_template = lib_fortran_meson_template lib_test_template = lib_fortran_test_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 7ea4c3f8ee23..45e70c6c93cb 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -3,9 +3,13 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileHeaderImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + lib_h_template = '''#pragma once #if defined _WIN32 || defined __CYGWIN__ @@ -67,11 +71,11 @@ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - objcpp_args : lib_args, + objcpp_shared_args : lib_args, dependencies : dependencies, gnu_symbol_visibility : 'hidden', ) @@ -80,7 +84,7 @@ '{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib, + link_with : lib, ) test('{test_name}', test_exe) @@ -88,7 +92,7 @@ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) @@ -98,7 +102,7 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -150,3 +154,7 @@ class ObjCppProject(FileHeaderImpl): lib_header_template = lib_h_template lib_test_template = lib_objcpp_test_template lib_meson_template = lib_objcpp_meson_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index de0217f96b93..0c7891fdb25e 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -3,9 +3,13 @@ # Copyright © 2023-2025 Intel Corporation from __future__ import annotations +import typing as T from mesonbuild.templates.sampleimpl import FileHeaderImpl +if T.TYPE_CHECKING: + from ..minit import Arguments + lib_h_template = '''#pragma once #if defined _WIN32 || defined __CYGWIN__ @@ -67,11 +71,11 @@ # not the executables that use the library. lib_args = ['-DBUILDING_{utoken}'] -shlib = shared_library( +lib = library( '{lib_name}', '{source_file}', install : true, - objc_args : lib_args, + objc_shared_args : lib_args, dependencies : dependencies, gnu_symbol_visibility : 'hidden', ) @@ -80,14 +84,14 @@ '{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib) + link_with : lib) test('{test_name}', test_exe) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) @@ -97,7 +101,7 @@ pkg_mod = import('pkgconfig') pkg_mod.generate( - shlib, + lib, description : 'Meson sample project.', subdirs : '{header_dir}', ) @@ -149,3 +153,7 @@ class ObjCProject(FileHeaderImpl): lib_header_template = lib_h_template lib_test_template = lib_objc_test_template lib_meson_template = lib_objc_meson_template + + def __init__(self, args: Arguments): + super().__init__(args) + self.meson_version = '1.3.0' diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 2f2d940128c3..ee1f0081dcd6 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -50,20 +50,20 @@ dependencies = [{dependencies} ] -shlib = static_library( +lib = static_library( '{lib_name}', '{source_file}', dependencies : dependencies, install : true, ) -rust.test('{test_name}', shlib) +rust.test('{test_name}', lib) # Make this library usable as a Meson subproject. {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) ''' diff --git a/mesonbuild/templates/valatemplates.py b/mesonbuild/templates/valatemplates.py index 3720c3cb3419..b2aab3f31be0 100644 --- a/mesonbuild/templates/valatemplates.py +++ b/mesonbuild/templates/valatemplates.py @@ -69,7 +69,7 @@ # These arguments are only used to build the shared library # not the executables that use the library. -shlib = shared_library( +lib = shared_library( 'foo', '{source_file}', dependencies : dependencies, @@ -81,7 +81,7 @@ '{test_exe_name}', '{test_source_file}', dependencies : dependencies, - link_with : shlib, + link_with : lib, ) test('{test_name}', test_exe) @@ -89,7 +89,7 @@ {ltoken}_dep = declare_dependency( include_directories : include_directories('.'), dependencies : dependencies, - link_with : shlib, + link_with : lib, ) meson.override_dependency('{project_name}', {ltoken}_dep) From ff5b5e66c9a759573af5f265a3404c3a144489de Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Jan 2025 13:09:25 +0100 Subject: [PATCH 163/624] optimize variable assignments Except for set_variable(), the variable name is certainly an identifier because it comes from the parser; thus, the check is unnecessary. Move the regular expression match to func_set_variable(). Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 2 ++ mesonbuild/interpreterbase/interpreterbase.py | 2 -- mesonbuild/mparser.py | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4933ba65a1c2..4a878e54dce8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3520,6 +3520,8 @@ def is_subproject(self) -> bool: @noSecondLevelHolderResolving def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, object], kwargs: 'TYPE_kwargs') -> None: varname, value = args + if mparser.IDENT_RE.fullmatch(varname) is None: + raise InvalidCode('Invalid variable name: ' + varname) self.set_variable(varname, value, holderify=True) @typed_pos_args('get_variable', (str, Disabler), optargs=[object]) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 5f9df4cb5bcc..5849e9ca9c1d 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -655,8 +655,6 @@ def set_variable(self, varname: str, variable: T.Union[TYPE_var, InterpreterObje raise mesonlib.MesonBugException(f'set_variable in InterpreterBase called with a non InterpreterObject {variable} of type {type(variable).__name__}') if not isinstance(varname, str): raise InvalidCode('First argument to set_variable must be a string.') - if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None: - raise InvalidCode('Invalid variable name: ' + varname) if varname in self.builtin: raise InvalidCode(f'Tried to overwrite internal variable "{varname}"') self.variables[varname] = variable diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 4f43455468c6..0ffaceb7c7ad 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -94,6 +94,9 @@ def __eq__(self, other: object) -> bool: return self.tid == other.tid return NotImplemented + +IDENT_RE = re.compile('[_a-zA-Z][_0-9a-zA-Z]*') + class Lexer: def __init__(self, code: str): if code.startswith(codecs.BOM_UTF8.decode('utf-8')): @@ -113,7 +116,7 @@ def __init__(self, code: str): ('whitespace', re.compile(r'[ \t]+')), ('multiline_fstring', re.compile(r"f'''(.|\n)*?'''", re.M)), ('fstring', re.compile(r"f'([^'\\]|(\\.))*'")), - ('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')), + ('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')), From 95d0c40da5de2b85f11b9ccda93ed21969327464 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Jan 2025 12:51:46 +0100 Subject: [PATCH 164/624] optimize Version.__init__ Version objects are created thousands of times by the check_version method of decorators. Creation is inefficient, resulting in five calls to re.match() that do not even use precompiled regex. The use of re.match() however is fully redundant, as finditer() can provide the same information with a better use of groupings. Do that and precompile the tokenization regex. This saves about 3% in the "meson setup" run of QEMU. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index eede9fd2ddab..e2634b925e50 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -828,21 +828,18 @@ def current_vs_supports_modules() -> bool: return True return vsver.startswith('16.9.0') and '-pre.' in vsver +_VERSION_TOK_RE = re.compile(r'(\d+)|([a-zA-Z]+)') + # a helper class which implements the same version ordering as RPM class Version: def __init__(self, s: str) -> None: self._s = s - # split into numeric, alphabetic and non-alphanumeric sequences - sequences1 = re.finditer(r'(\d+|[a-zA-Z]+|[^a-zA-Z\d]+)', s) - - # non-alphanumeric separators are discarded - sequences2 = [m for m in sequences1 if not re.match(r'[^a-zA-Z\d]+', m.group(1))] - + # extract numeric and alphabetic sequences # numeric sequences are converted from strings to ints - sequences3 = [int(m.group(1)) if m.group(1).isdigit() else m.group(1) for m in sequences2] - - self._v = sequences3 + self._v = [ + int(m.group(1)) if m.group(1) else m.group(2) + for m in _VERSION_TOK_RE.finditer(s)] def __str__(self) -> str: return '{} (V={})'.format(self._s, str(self._v)) From 7fd138dad98d698c9305c466f500a1df4fb0d74d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 8 Jan 2025 08:55:19 -0800 Subject: [PATCH 165/624] utils: Add a lazy property decorator --- mesonbuild/utils/universal.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index e2634b925e50..3ec23e1056d0 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -130,6 +130,7 @@ class _VerPickleLoadable(Protocol): 'is_wsl', 'iter_regexin_iter', 'join_args', + 'lazy_property', 'listify', 'listify_array_value', 'partition', @@ -2379,3 +2380,22 @@ def first(iter: T.Iterable[_T], predicate: T.Callable[[_T], bool]) -> T.Optional if predicate(i): return i return None + + +class lazy_property(T.Generic[_T]): + """Descriptor that replaces the function it wraps with the value generated. + + This property will only be calculated the first time it's queried, and will + be cached and the cached value used for subsequent calls. + + This works by shadowing itself with the calculated value, in the instance. + Due to Python's MRO that means that the calculated value will be found + before this property, speeding up subsequent lookups. + """ + def __init__(self, func: T.Callable[[T.Any], _T]): + self.__func = func + + def __get__(self, instance: object, cls: T.Type) -> _T: + value = self.__func(instance) + setattr(instance, self.__func.__name__, value) + return value From 4151d09e262f48b3e06e6677581465758e08b79d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Jan 2025 14:13:04 +0100 Subject: [PATCH 166/624] ninjabackend: convert _should_use_rspfile to a lazy_property _should_use_rspfile() is expensive due to the call to length_estimate(). Make it a lazy property so that it is only called once. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7e353c0afd46..040f3add627b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2017 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -351,6 +351,7 @@ def add_item(self, name: str, elems: T.Union[str, T.List[str], CompilerArgs]) -> if name == 'DEPFILE': self.elems.append((name + '_UNQUOTED', elems)) + @mesonlib.lazy_property def _should_use_rspfile(self) -> bool: # 'phony' is a rule built-in to ninja if self.rulename == 'phony': @@ -368,7 +369,7 @@ def _should_use_rspfile(self) -> bool: def count_rule_references(self) -> None: if self.rulename != 'phony': - if self._should_use_rspfile(): + if self._should_use_rspfile: self.rule.rsprefcount += 1 else: self.rule.refcount += 1 @@ -381,7 +382,7 @@ def write(self, outfile: T.TextIO) -> None: implicit_outs = ' '.join([ninja_quote(i, True) for i in self.implicit_outfilenames]) if implicit_outs: implicit_outs = ' | ' + implicit_outs - use_rspfile = self._should_use_rspfile() + use_rspfile = self._should_use_rspfile if use_rspfile: rulename = self.rulename + '_RSP' mlog.debug(f'Command line for building {self.outfilenames} is long, using a response file') From 5af3d3df0034d7ec3c159cc31f063558d5369df3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Jan 2025 14:33:51 +0100 Subject: [PATCH 167/624] ninjabackend: avoid repeatedly building and analyzing rule commands Two expensive parts of length_estimate() are executed for each target, but they are really always the same. Cache them in __init__, there will always be more targets than rules in cases where speed counts. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 040f3add627b..b4ec9ef0071b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -226,6 +226,9 @@ def strToCommandArg(c: T.Union[NinjaCommandArg, str]) -> NinjaCommandArg: self.refcount = 0 self.rsprefcount = 0 self.rspfile_quote_style = rspfile_quote_style + self.command_str = ' '.join([self._quoter(x) for x in self.command + self.args]) + self.var_refs = [m for m in re.finditer(r'(\${\w+}|\$\w+)?[^$]*', self.command_str) + if m.start(1) != -1] if self.depfile == '$DEPFILE': self.depfile += '_UNQUOTED' @@ -262,7 +265,7 @@ def rule_iter() -> T.Iterable[str]: outfile.write(' rspfile = $out.rsp\n') outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in rspfile_args]))) else: - outfile.write(' command = {}\n'.format(' '.join([self._quoter(x) for x in self.command + self.args]))) + outfile.write(' command = {}\n'.format(self.command_str)) if self.deps: outfile.write(f' deps = {self.deps}\n') if self.depfile: @@ -289,18 +292,16 @@ def length_estimate(self, infiles: str, outfiles: str, ninja_vars['out'] = [outfiles] # expand variables in command - command = ' '.join([self._quoter(x) for x in self.command + self.args]) - estimate = len(command) - for m in re.finditer(r'(\${\w+}|\$\w+)?[^$]*', command): - if m.start(1) != -1: - estimate -= m.end(1) - m.start(1) - chunk = m.group(1) - if chunk[1] == '{': - chunk = chunk[2:-1] - else: - chunk = chunk[1:] - chunk = ninja_vars.get(chunk, []) # undefined ninja variables are empty - estimate += len(' '.join(chunk)) + estimate = len(self.command_str) + for m in self.var_refs: + estimate -= m.end(1) - m.start(1) + chunk = m.group(1) + if chunk[1] == '{': + chunk = chunk[2:-1] + else: + chunk = chunk[1:] + chunk = ninja_vars.get(chunk, []) # undefined ninja variables are empty + estimate += len(' '.join(chunk)) # determine command length return estimate From 27028bd3b173539fd93eed880ad31a73fc7d1719 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 8 Jan 2025 17:24:07 +0100 Subject: [PATCH 168/624] programs: fix regex to match multi-digit major version In a3679a64e (programs: favor version numbers with dots, 2025-01-03) we have changed the regex used to extract version numbers to favor dotted versions. It was reported though that the regex doesn't match major version numbers that start with multiple digits correctly. Fix this. Signed-off-by: Patrick Steinhardt --- mesonbuild/programs.py | 2 +- unittests/internaltests.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 7f4cec52d8f0..d01440cce193 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -123,7 +123,7 @@ def get_version(self, interpreter: T.Optional['Interpreter'] = None) -> str: if not output: output = e.strip() - match = re.search(r'([0-9](\.[0-9]+)+)', output) + match = re.search(r'([0-9]+(\.[0-9]+)+)', output) if not match: match = re.search(r'([0-9][0-9\.]+)', output) if not match: diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 36354104907b..d7994ee085f9 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -689,6 +689,7 @@ def test_program_version(self): 'foo 1.2.4.': '1.2.4', 'foo 1.2.4': '1.2.4', 'foo 1.2.4 bar': '1.2.4', + 'foo 10.0.0': '10.0.0', '50 5.4.0': '5.4.0', 'This is perl 5, version 40, subversion 0 (v5.40.0)': '5.40.0', 'git version 2.48.0.rc1': '2.48.0', From c508b26867741b83b3b73fc88de1ad97acc39c18 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Wed, 8 Jan 2025 14:04:58 -0800 Subject: [PATCH 169/624] Copyedit an error message Fixes: 47acce8ec9 ("Provide a better error message when mixing host and build machines") --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ca4e5d5a7fa0..ed777ff25432 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1500,7 +1500,7 @@ def check_can_link_together(self, t: BuildTargetTypes) -> None: if not self.uses_rust() and links_with_rust_abi: raise InvalidArguments(f'Try to link Rust ABI library {t.name!r} with a non-Rust target {self.name!r}') if self.for_machine is not t.for_machine and (not links_with_rust_abi or t.rust_crate_type != 'proc-macro'): - msg = f'Tried to tied to mix a {t.for_machine} library ("{t.name}") with a {self.for_machine} target "{self.name}"' + msg = f'Tried to mix a {t.for_machine} library ("{t.name}") with a {self.for_machine} target "{self.name}"' if self.environment.is_cross_build(): raise InvalidArguments(msg + ' This is not possible in a cross build.') else: From a8bb13c2d228c74dd234fc07c79abdc4e7e05c55 Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Thu, 6 Jun 2024 18:05:45 +0200 Subject: [PATCH 170/624] qt module: add qml module test --- test cases/frameworks/39 qt qml/Basic.qml | 5 + test cases/frameworks/39 qt qml/Internal.qml | 5 + test cases/frameworks/39 qt qml/Main.qml | 53 ++++++++ .../frameworks/39 qt qml/QmlCppExposed.hpp | 25 ++++ .../39 qt qml/QmlCppOtherExposed.hpp | 25 ++++ test cases/frameworks/39 qt qml/QmlMain.cpp | 31 +++++ .../frameworks/39 qt qml/QmlSingleton.qml | 10 ++ test cases/frameworks/39 qt qml/custom_qmldir | 4 + .../frameworks/39 qt qml/custom_qmldir.qrc | 5 + test cases/frameworks/39 qt qml/meson.build | 115 ++++++++++++++++++ .../frameworks/39 qt qml/meson_options.txt | 1 + .../39 qt qml/subdir/SubdirHeader.hpp | 27 ++++ .../frameworks/39 qt qml/subdir/Thing.qml | 5 + test cases/frameworks/39 qt qml/test.json | 22 ++++ 14 files changed, 333 insertions(+) create mode 100644 test cases/frameworks/39 qt qml/Basic.qml create mode 100644 test cases/frameworks/39 qt qml/Internal.qml create mode 100644 test cases/frameworks/39 qt qml/Main.qml create mode 100644 test cases/frameworks/39 qt qml/QmlCppExposed.hpp create mode 100644 test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp create mode 100644 test cases/frameworks/39 qt qml/QmlMain.cpp create mode 100644 test cases/frameworks/39 qt qml/QmlSingleton.qml create mode 100644 test cases/frameworks/39 qt qml/custom_qmldir create mode 100644 test cases/frameworks/39 qt qml/custom_qmldir.qrc create mode 100644 test cases/frameworks/39 qt qml/meson.build create mode 100644 test cases/frameworks/39 qt qml/meson_options.txt create mode 100644 test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp create mode 100644 test cases/frameworks/39 qt qml/subdir/Thing.qml create mode 100644 test cases/frameworks/39 qt qml/test.json diff --git a/test cases/frameworks/39 qt qml/Basic.qml b/test cases/frameworks/39 qt qml/Basic.qml new file mode 100644 index 000000000000..33c0a28c70b0 --- /dev/null +++ b/test cases/frameworks/39 qt qml/Basic.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property int ok: 1 +} diff --git a/test cases/frameworks/39 qt qml/Internal.qml b/test cases/frameworks/39 qt qml/Internal.qml new file mode 100644 index 000000000000..e8eee472379b --- /dev/null +++ b/test cases/frameworks/39 qt qml/Internal.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property int ok: 5 +} diff --git a/test cases/frameworks/39 qt qml/Main.qml b/test cases/frameworks/39 qt qml/Main.qml new file mode 100644 index 000000000000..94b67186dd35 --- /dev/null +++ b/test cases/frameworks/39 qt qml/Main.qml @@ -0,0 +1,53 @@ +import QtQuick +import My.Module1 as M1 + +Item { + id: root + + Component.onCompleted: { + function checkInstance(label, instance, value) { + if (!instance) { + console.log(label, "KO instance is null") + return false + } if (instance.ok !== value) { + console.log(label, "KO got", instance.ok, "expected", value) + return false + } else { + console.log(label, "OK") + return true + } + } + + function checkClass(namespace, classname, value) { + let newObject = null; + try { + newObject = Qt.createQmlObject( + "import %1; %2 {}".arg(namespace).arg(classname), + root, + "some path" + ) + } catch (e) { + console.log(namespace, classname, "KO failed to instanciate object") + return false + } + return checkInstance("%1 %2".arg(namespace).arg(classname), newObject, value) + } + + let ret = true + ret &= checkClass("My.Module1", "Basic", 1); + ret &= checkClass("My.Module1", "Thing", 2); + ret &= checkClass("My.Module1", "QmlCppExposed", 3); + ret &= checkInstance("My.Module1 QmlSingleton", M1.QmlSingleton, 5) + + ret &= checkClass("My.Module2", "Thing", 2); + ret &= checkClass("My.Module3", "Basic", 1); + ret &= checkClass("My.Module4", "BasicAliased", 1); + ret &= checkClass("My.Module5", "SubdirHeader", 6); + ret &= checkClass("My.Module6", "Basic", 1); + + if (!ret) + Qt.exit(1) + else + Qt.quit() + } +} diff --git a/test cases/frameworks/39 qt qml/QmlCppExposed.hpp b/test cases/frameworks/39 qt qml/QmlCppExposed.hpp new file mode 100644 index 000000000000..10568c8b717e --- /dev/null +++ b/test cases/frameworks/39 qt qml/QmlCppExposed.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class QmlCppExposed : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int ok READ getOk WRITE setOk NOTIFY okChanged) + +public: + inline int getOk() const { return m_ok; } + inline void setOk(int value) { + if (value == m_ok) + return; + m_ok = value; + emit okChanged(); + } + +signals: + void okChanged(); + +private: + int m_ok = 3; +}; diff --git a/test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp b/test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp new file mode 100644 index 000000000000..78426163566f --- /dev/null +++ b/test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class QmlCppOtherExposed : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int ok READ getOk WRITE setOk NOTIFY okChanged) + +public: + inline int getOk() const { return m_ok; } + inline void setOk(int value) { + if (value == m_ok) + return; + m_ok = value; + emit okChanged(); + } + +signals: + void okChanged(); + +private: + int m_ok = 42; +}; diff --git a/test cases/frameworks/39 qt qml/QmlMain.cpp b/test cases/frameworks/39 qt qml/QmlMain.cpp new file mode 100644 index 000000000000..0cec6f3a3f1c --- /dev/null +++ b/test cases/frameworks/39 qt qml/QmlMain.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +//extern type registration +extern void qml_register_types_My_Module6(); + +int main(int argCount, char* argVector[]) +{ + //register resources from static libraries + Q_INIT_RESOURCE(My_Module6); + Q_INIT_RESOURCE(qmlcache_My_Module6); + qml_register_types_My_Module6(); + + //don't require a grapical environment to run the test + qputenv("QT_QPA_PLATFORM", "offscreen"); + + QGuiApplication app(argCount, argVector); + QQmlApplicationEngine engine; + + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, [](QObject *object, const QUrl &url){ + if (object == nullptr) { + qFatal("unable to load scene"); + } + }); + + engine.addImportPath("qrc:///qt/qml"); + engine.addImportPath("qrc:///test"); + engine.load("qrc:///qt/qml/My/Module0/Main.qml"); + return app.exec(); +} diff --git a/test cases/frameworks/39 qt qml/QmlSingleton.qml b/test cases/frameworks/39 qt qml/QmlSingleton.qml new file mode 100644 index 000000000000..73ea95d11bf6 --- /dev/null +++ b/test cases/frameworks/39 qt qml/QmlSingleton.qml @@ -0,0 +1,10 @@ +pragma Singleton +import QtQuick + +Item { + property alias ok: sub.ok + + Internal { + id: sub + } +} diff --git a/test cases/frameworks/39 qt qml/custom_qmldir b/test cases/frameworks/39 qt qml/custom_qmldir new file mode 100644 index 000000000000..9d84db651401 --- /dev/null +++ b/test cases/frameworks/39 qt qml/custom_qmldir @@ -0,0 +1,4 @@ +module My.Module4 +prefer :/qt/qml/My/Module4/ +BasicAliased 1.0 Basic.qml +Thing 1.0 Thing.qml diff --git a/test cases/frameworks/39 qt qml/custom_qmldir.qrc b/test cases/frameworks/39 qt qml/custom_qmldir.qrc new file mode 100644 index 000000000000..bee52092c587 --- /dev/null +++ b/test cases/frameworks/39 qt qml/custom_qmldir.qrc @@ -0,0 +1,5 @@ + + + custom_qmldir + + diff --git a/test cases/frameworks/39 qt qml/meson.build b/test cases/frameworks/39 qt qml/meson.build new file mode 100644 index 000000000000..060e044a5ec8 --- /dev/null +++ b/test cases/frameworks/39 qt qml/meson.build @@ -0,0 +1,115 @@ +project('qt6 qml build test', 'cpp', + meson_version: '>= 1.7.0', + # Qt6 requires C++ 17 support + default_options : ['cpp_std=c++17'] +) + +qt_modules = ['Core', 'Gui', 'Qml'] + +qtdep = dependency('qt6', modules : qt_modules, main : true, private_headers: true, required : false, method : get_option('method')) +if not qtdep.found() + error('MESON_SKIP_TEST qt6 not found.') +endif + +qtmodule = import('qt6') +fs = import('fs') + +qmlmodule1 = qtmodule.qml_module( + 'My.Module1', + version: '1.0', + qml_sources: files('Basic.qml', 'subdir/Thing.qml'), + qml_singletons: files('QmlSingleton.qml'), + qml_internals: files('Internal.qml'), + moc_headers: files('QmlCppExposed.hpp', 'QmlCppOtherExposed.hpp'), + designer_supported: true, + dependencies: [qtdep], + install: true +) + +#with a different resource prefix +qmlmodule2 = qtmodule.qml_module( + 'My.Module2', + version: '1.0', + qml_sources: ['Basic.qml', 'subdir/Thing.qml'], + resources_prefix: '/test', + dependencies: [qtdep], +) + +#test with generated targets +basic_copy = fs.copyfile('Basic.qml') +thing_copy = fs.copyfile('subdir/Thing.qml') + +#build without cachegen +qmlmodule3 = qtmodule.qml_module( + 'My.Module3', + version: '1.10.42', + qml_sources: [basic_copy, thing_copy], + cachegen: false, + dependencies: [qtdep], +) + +#build without cachegen +qmlmodule4 = qtmodule.qml_module( + 'My.Module4', + qml_sources: files('Basic.qml', 'subdir/Thing.qml'), + generate_qmldir: false, + dependencies: [qtdep], +) + +qmlmodule4_res = qtmodule.compile_resources( + name : 'qmlmodule4_resource', + sources : files(['custom_qmldir.qrc']), + method : get_option('method') +) + +#a module with only C++ classes +cpponly_module = qtmodule.qml_module( + 'My.Module5', + version: '1.0', + moc_headers: files('subdir/SubdirHeader.hpp'), + dependencies: [qtdep], + install: true +) + +#module as static library +qmlmodule6 = qtmodule.qml_module( + 'My.Module6', + version: '1.0', + qml_sources: files('Basic.qml'), + moc_headers: files('subdir/SubdirHeader.hpp'), + cachegen: true, + dependencies: [qtdep], +) + +qmlmodule6_static = static_library( + 'Qmlmodule6Lib', + sources: qmlmodule6, + include_directories: include_directories('subdir'), + dependencies: [qtdep], + override_options: 'unity=off', +) + +#qml entry point and qmldir dependecies +qmlmodule0 = qtmodule.qml_module( + 'My.Module0', + version: '1.0', + qml_sources: files('Main.qml'), + imports: ['QtQuick/2.0', 'My.Module1'], + optional_imports: ['My.Module2/auto'], + dependencies: [qtdep], +) + +qmltest = executable( + 'qmlmodule', + sources : [ + 'QmlMain.cpp', qmlmodule0, qmlmodule1, qmlmodule2, + qmlmodule3, qmlmodule4, qmlmodule4_res, cpponly_module + ], + link_with : qmlmodule6_static, + dependencies : qtdep, + # headers in subdirectory needs to have their include path explicitly + # added for the code generated by by qmltyperegistrar. see QTBUG-87221 + include_directories: include_directories('subdir'), + #generated code doesn't support unity build + override_options: 'unity=off', +) diff --git a/test cases/frameworks/39 qt qml/meson_options.txt b/test cases/frameworks/39 qt qml/meson_options.txt new file mode 100644 index 000000000000..bc1069ebc881 --- /dev/null +++ b/test cases/frameworks/39 qt qml/meson_options.txt @@ -0,0 +1 @@ +option('method', type : 'string', value : 'auto', description : 'The method to use to find Qt') diff --git a/test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp b/test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp new file mode 100644 index 000000000000..019a1692335d --- /dev/null +++ b/test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +#include "QmlCppExposed.hpp" + +class SubdirHeader : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int ok READ getOk WRITE setOk NOTIFY okChanged) + +public: + inline int getOk() const { return m_ok; } + inline void setOk(int value) { + if (value == m_ok) + return; + m_ok = value; + emit okChanged(); + } + +signals: + void okChanged(); + +private: + int m_ok = 6; +}; diff --git a/test cases/frameworks/39 qt qml/subdir/Thing.qml b/test cases/frameworks/39 qt qml/subdir/Thing.qml new file mode 100644 index 000000000000..5b015c35ca01 --- /dev/null +++ b/test cases/frameworks/39 qt qml/subdir/Thing.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property int ok: 2 +} diff --git a/test cases/frameworks/39 qt qml/test.json b/test cases/frameworks/39 qt qml/test.json new file mode 100644 index 000000000000..d1f868400658 --- /dev/null +++ b/test cases/frameworks/39 qt qml/test.json @@ -0,0 +1,22 @@ +{ + "matrix": { + "options": { + "method": [ + { "val": "config-tool" }, + { "val": "qmake" }, + { "val": "pkg-config" } + ] + } + }, + "installed": [ + {"type": "file", "file": "usr/qml/My/Module1/QmlSingleton.qml"}, + {"type": "file", "file": "usr/qml/My/Module1/qmldir"}, + {"type": "file", "file": "usr/qml/My/Module1/Basic.qml"}, + {"type": "file", "file": "usr/qml/My/Module1/Internal.qml"}, + {"type": "file", "file": "usr/qml/My/Module1/Thing.qml"}, + {"type": "file", "file": "usr/qml/My/Module1/My_Module1.qmltypes"}, + {"type": "file", "file": "usr/qml/My/Module5/qmldir"}, + {"type": "file", "file": "usr/qml/My/Module5/My_Module5.qmltypes"} + ], + "expect_skip_on_jobname": ["cygwin", "msys2", "azure", "bionic", "macos"] +} From a960e833576ab8a832162a28808b4d56a17b21de Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Fri, 29 Nov 2024 14:39:48 +0100 Subject: [PATCH 171/624] qt module: update add qml_module documentation --- docs/markdown/Qt6-module.md | 78 ++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index 9e4e0163707d..f3b3a32a01f9 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -113,7 +113,7 @@ This method takes the following keyword arguments: directory. For instance, when a file called `subdir/one.input` is processed it generates a file `{target private directory}/subdir/one.out` when `true`, and `{target private directory}/one.out` when `false` (default). - - `moc_output_json` bool: *New in 1.7.0*. If `true`, generates additionnaly a + - `moc_output_json` bool: *New in 1.7.0*. If `true`, generates additionally a JSON representation which may be used by external tools such as qmltyperegistrar It returns an array of targets and sources to pass to a compilation target. @@ -164,6 +164,81 @@ This method takes the following keyword arguments: are `moc`, `uic`, `rcc` and `lrelease`. By default `tools` is set to `['moc', 'uic', 'rcc', 'lrelease']` +## qml_module + +*New in 1.7.0* + +This function requires one positional argument: the URI of the module as dotted +identifier string. For instance `Foo.Bar` + +This method takes the following keyword arguments: + + - `version`: string: the module version in the form `Major.Minor` with an + optional `.Patch`. For instance `1.0` + - `qml_sources` (File | string | custom_target | custom_target index | generator_output)[]: + A list of qml to be embedded in the module + - `qml_singletons` (File | string | custom_target | custom_target index | generator_output)[]: + A list of qml to be embedded in the module and marked as singletons + - `qml_internals` (File | string | custom_target | custom_target index | generator_output)[]: + A list of qml to be embedded in the module and marked as internal files + - `resources_prefix` string: By default `resources_prefix` is set to + `qt/qml`. Prefix resources in the generated QRC with the given prefix + - `imports`: string[]: List of other QML modules imported by this module. Version + can be specified as `Module/1.0` or `Module/auto`. See qmldir documentation + - `optional_imports`: string[]: List of other QML modules that may be imported by this + module. See `imports` for expected format and qmldir documentation + - `default_imports`: string[]: List QML modules that may be loaded by + tooling. See `imports` for expected format and qmldir documentation + - `depends_imports`: string[]: List of QML extra dependencies that may not be + imported by QML, such as dependencies existing in C++ code. See `imports` for + expected format and qmldir documentation + - `designer_supported` bool: If `true` specifies that the module supports Qt + Quick Designer + - `moc_headers` (File | string | custom_target | custom_target index | generator_output)[]: + A list of headers to be transpiled into .cpp files. See [Qt + documentation](https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html) + regarding how to register C++ class as Qml elements. Note: due to some + limitations of qmltyperegistrar, all headers that declare QML types need to + be accessible in the project's include path. + - `namespace`: str: optional C++ namespace for plugin and generation code + - `typeinfo`: str: optional name for the generated qmltype file, by default it + will be generated as `{target_name}.qmltype` + - `rcc_extra_arguments`: string[]: Extra arguments to pass directly to `qt-rcc` + - `moc_extra_arguments`: string[]: Extra arguments to pass directly to `qt-moc` + - `qmlcachegen_extra_arguments`: string[]: Extra arguments to pass directly to + `qmlcachegen` + - `qmltyperegistrar_extra_arguments`: string[]: Extra arguments to pass directly to + `qmltyperegistrar` + - `generate_qmldir`: bool: If `true` (default) auto generate the `qmldir` file + - `generate_qmltype`: bool: If `true` (default) auto generate `qmltype` file + - `cachegen`: bool: If `true` (default) preprocess QML and JS files with + qmlcachegen + - `method` string: The method to use to detect Qt, see [[dependency]] + - `preserve_paths` bool: If `true`, specifies that the output + files need to maintain their directory structure inside the target temporary + directory. For instance, when a file called `subdir/one.input` is processed + it generates a file `{target private directory}/subdir/one.out` when `true`, + and `{target private directory}/one.out` when `false` (default). + - `dependencies`: dependency objects whose include directories are used by + moc. + - `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()` + objects used when transpiling the .moc files + - `install` bool: when true, this target is installed during the install step (optional). + - `install_dir` string: directory to install to (optional). + + +Note: Qt uses static initialization to register its resources, if you're +building a static library you may need to call these entry points +explicitly. For a module `Foo.Bar42` the generated resources are `Foo_Bar42` +and `qmlcache_Foo_Bar42` when qmlcache is used, they can be imported using +`Q_INIT_RESOURCE`. All non-alphanumeric characters from the module name are +replaced with `_`. Type registration may be invoked explicitly using +`extern void qml_register_types_Foo_Bar42()`. + +See [Qt documentation](https://doc.qt.io/qt-6/resources.html#explicit-loading-and-unloading-of-embedded-resources) +for more information + + ## Dependencies See [Qt dependencies](Dependencies.md#qt) @@ -204,4 +279,3 @@ lang_cpp = qt6.compile_translations(qresource: 'lang.qrc') executable('myprog', 'main.cpp', lang_cpp, dependencies: qt6_dep) ``` - From 910db36e3851f384b4aa2bfb834af92f88b61d77 Mon Sep 17 00:00:00 2001 From: "Wu, Zhenyu" Date: Mon, 9 Dec 2024 21:41:29 +0800 Subject: [PATCH 172/624] Add Linear ASM compiler Fix #13670 --- docs/markdown/Reference-tables.md | 33 ++++++++++---------- docs/markdown/snippets/linearasm_features.md | 3 ++ mesonbuild/compilers/asm.py | 29 +++++++++++++++++ mesonbuild/compilers/compilers.py | 1 + mesonbuild/compilers/detect.py | 21 +++++++++++++ 5 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 docs/markdown/snippets/linearasm_features.md diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 5b27e3de52ac..1c5f9a33a4f3 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -216,22 +216,23 @@ Meson natively. These are the parameter names for passing language specific arguments to your build target. -| Language | compiler name | linker name | -| ------------- | ------------- | ----------------- | -| C | c_args | c_link_args | -| C++ | cpp_args | cpp_link_args | -| C# | cs_args | cs_link_args | -| CUDA | cuda_args | cuda_link_args | -| D | d_args | d_link_args | -| Fortran | fortran_args | fortran_link_args | -| Java | java_args | java_link_args | -| Objective C | objc_args | objc_link_args | -| Objective C++ | objcpp_args | objcpp_link_args | -| Rust | rust_args | rust_link_args | -| Vala | vala_args | vala_link_args | -| Cython | cython_args | cython_link_args | -| NASM | nasm_args | N/A | -| MASM | masm_args | N/A | +| Language | compiler name | linker name | +| ------------- | -------------- | ----------------- | +| C | c_args | c_link_args | +| C++ | cpp_args | cpp_link_args | +| C# | cs_args | cs_link_args | +| CUDA | cuda_args | cuda_link_args | +| D | d_args | d_link_args | +| Fortran | fortran_args | fortran_link_args | +| Java | java_args | java_link_args | +| Objective C | objc_args | objc_link_args | +| Objective C++ | objcpp_args | objcpp_link_args | +| Rust | rust_args | rust_link_args | +| Vala | vala_args | vala_link_args | +| Cython | cython_args | cython_link_args | +| NASM | nasm_args | N/A | +| MASM | masm_args | N/A | +| Linear ASM | linearasm_args | N/A | All these `_*` options are specified per machine. See in [specifying options per diff --git a/docs/markdown/snippets/linearasm_features.md b/docs/markdown/snippets/linearasm_features.md new file mode 100644 index 000000000000..571408685ae2 --- /dev/null +++ b/docs/markdown/snippets/linearasm_features.md @@ -0,0 +1,3 @@ +## Add new language Linear Asm + +TI C6000 compiler supports a dialect of TI asm, so we add a new language for it. diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index 8cd5e28dc47f..d358ca992a17 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -7,6 +7,7 @@ from ..options import OptionKey from .compilers import Compiler 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 @@ -259,6 +260,34 @@ def depfile_for_object(self, objfile: str) -> T.Optional[str]: return None +# https://downloads.ti.com/docs/esd/SPRUI04/ +class TILinearAsmCompiler(TICompiler, Compiler): + language = 'linearasm' + + def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, + for_machine: MachineChoice, info: MachineInfo, + 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) + TICompiler.__init__(self) + + def needs_static_linker(self) -> bool: + return True + + def get_always_args(self) -> T.List[str]: + return [] + + 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): language = 'nasm' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3dfa0ff2770f..424bcc19bf6a 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -69,6 +69,7 @@ 'cython': ('pyx', ), 'nasm': ('asm', 'nasm',), 'masm': ('masm',), + 'linearasm': ('sa',), } all_languages = lang_suffixes.keys() c_cpp_suffixes = {'h'} diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 5bc14350e4ee..7bd48d10c320 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -98,6 +98,7 @@ def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineCh 'cython': detect_cython_compiler, 'nasm': detect_nasm_compiler, 'masm': detect_masm_compiler, + 'linearasm': detect_linearasm_compiler, } return lang_map[lang](env, for_machine) if lang in lang_map else None @@ -1376,6 +1377,26 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp _handle_exceptions(popen_exceptions, [comp]) raise EnvironmentException('Unreachable code (exception to make mypy happy)') +def detect_linearasm_compiler(env: Environment, for_machine: MachineChoice) -> Compiler: + from .asm import TILinearAsmCompiler + comp = ['cl6x'] + comp_class: T.Type[Compiler] = 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) + except OSError as e: + popen_exceptions[' '.join(comp + [arg])] = e + _handle_exceptions(popen_exceptions, [comp]) + raise EnvironmentException('Unreachable code (exception to make mypy happy)') + # GNU/Clang defines and version # ============================= From 0279acbfe54a83fa110cf7587b480e8f38adc634 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 9 Jan 2025 15:42:09 +0200 Subject: [PATCH 173/624] Condensed directory names for release. --- run_format_tests.py | 2 +- .../file.txt.in | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../foo.py | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../lib2.rs | 0 .../main.rs | 0 .../meson.build | 0 .../meson.build | 0 .../unit/{103 strip => 104 strip}/lib.c | 0 .../unit/{103 strip => 104 strip}/meson.build | 0 .../meson.build | 0 .../meson.build | 0 .../main.c | 0 .../meson.build | 0 .../runner.py | 0 .../cp.py | 0 .../main.c | 0 .../meson.build | 0 .../symlinked_subproject/datadir/datafile | 0 .../symlinked_subproject/datadir/meson.build | 0 .../symlinked_subproject/meson.build | 0 .../symlinked_subproject/src.c | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/foo/foo.c | 0 .../subprojects/foo/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../unit/{110 freeze => 111 freeze}/freeze.c | 0 .../{110 freeze => 111 freeze}/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../com/mesonbuild/Simple.java | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../main.c | 0 .../meson.build | 0 .../s1.c | 0 .../s2.c | 0 .../s3.c | 0 .../meson.build | 0 .../expected_mods.json | 0 .../meson.build | 0 .../{117 genvslite => 118 genvslite}/main.cpp | 0 .../meson.build | 0 .../cache_dir/bar/meson.build | 0 .../cache_dir/foo.zip | Bin .../meson.build | 0 .../subprojects/bar.wrap | 0 .../subprojects/foo.wrap | 0 .../meson.build | 0 .../nativefile.ini | 0 .../{120 rewrite => 121 rewrite}/meson.build | 0 .../main.c | 0 .../meson.build | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../foo.c | 0 .../foo.dat | 0 .../foo.h | 0 .../foo/foofile | 0 .../meson.build | 0 .../subprojects/bar/bar.c | 0 .../subprojects/bar/bar.dat | 0 .../subprojects/bar/bar.h | 0 .../subprojects/bar/bar/barfile | 0 .../subprojects/bar/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/sub/foo.c | 0 .../subprojects/sub/meson.build | 0 .../.clang-format | 0 .../.clang-format-ignore | 0 .../.clang-format-include | 0 .../meson.build | 0 .../not-included/badformat.cpp | 0 .../src/badformat.c | 0 .../src/badformat.cpp | 0 .../easytogrepfor/genh.py | 0 .../easytogrepfor/meson.build | 0 .../{94 custominc => 95 custominc}/helper.c | 0 .../meson.build | 0 .../{94 custominc => 95 custominc}/prog.c | 0 .../{94 custominc => 95 custominc}/prog2.c | 0 .../meson.build | 0 .../subprojects/something/meson.build | 0 .../meson.build | 0 .../test.c | 0 .../.gitignore | 0 .../libtestprovider/meson.build | 0 .../libtestprovider/provider.c | 0 .../proguser/meson.build | 0 .../proguser/receiver.c | 0 .../bar-custom.txt | 0 .../bar-devel.h | 0 .../bar-notag.txt | 0 .../custom_files/data.txt | 0 .../excludes/excluded.txt | 0 .../excludes/excluded/placeholder.txt | 0 .../excludes/installed.txt | 0 .../foo.in | 0 .../foo1-devel.h | 0 .../lib.c | 0 .../main.c | 0 .../meson.build | 0 .../script.py | 0 .../subdir/bar2-devel.h | 0 .../subdir/foo2.in | 0 .../subdir/foo3-devel.h | 0 .../subdir/lib.c | 0 .../subdir/main.c | 0 .../subdir/meson.build | 0 .../subdir/script.py | 0 .../subprojects/subproject/aaa.txt | 0 .../subprojects/subproject/bbb.txt | 0 .../subprojects/subproject/meson.build | 0 tools/dircondenser.py | 1 + unittests/allplatformstests.py | 34 +++++++++--------- unittests/linuxliketests.py | 12 +++---- unittests/machinefiletests.py | 2 +- unittests/platformagnostictests.py | 18 +++++----- unittests/rewritetests.py | 2 +- unittests/windowstests.py | 2 +- 129 files changed, 37 insertions(+), 36 deletions(-) rename test cases/unit/{99 custom target name => 100 custom target name}/file.txt.in (100%) rename test cases/unit/{99 custom target name => 100 custom target name}/meson.build (100%) rename test cases/unit/{99 custom target name => 100 custom target name}/subdir/meson.build (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/foo.py (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/meson.build (100%) rename test cases/unit/{100 relative find program => 101 relative find program}/subdir/meson.build (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/lib2.rs (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/main.rs (100%) rename test cases/unit/{101 rlib linkage => 102 rlib linkage}/meson.build (100%) rename test cases/unit/{102 python without pkgconfig => 103 python without pkgconfig}/meson.build (100%) rename test cases/unit/{103 strip => 104 strip}/lib.c (100%) rename test cases/unit/{103 strip => 104 strip}/meson.build (100%) rename test cases/unit/{104 debug function => 105 debug function}/meson.build (100%) rename test cases/unit/{105 pkgconfig relocatable with absolute path => 106 pkgconfig relocatable with absolute path}/meson.build (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/main.c (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/meson.build (100%) rename test cases/unit/{106 underspecified mtest => 107 underspecified mtest}/runner.py (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/cp.py (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/main.c (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/datadir/datafile (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/datadir/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/meson.build (100%) rename test cases/unit/{107 subproject symlink => 108 subproject symlink}/symlinked_subproject/src.c (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/meson.build (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/meson_options.txt (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/subprojects/foo/foo.c (100%) rename test cases/unit/{108 new subproject on reconfigure => 109 new subproject on reconfigure}/subprojects/foo/meson.build (100%) rename test cases/unit/{109 configure same noop => 110 configure same noop}/meson.build (100%) rename test cases/unit/{109 configure same noop => 110 configure same noop}/meson_options.txt (100%) rename test cases/unit/{110 freeze => 111 freeze}/freeze.c (100%) rename test cases/unit/{110 freeze => 111 freeze}/meson.build (100%) rename test cases/unit/{111 replace unencodable xml chars => 112 replace unencodable xml chars}/meson.build (100%) rename test cases/unit/{111 replace unencodable xml chars => 112 replace unencodable xml chars}/script.py (100%) rename test cases/unit/{112 classpath => 113 classpath}/com/mesonbuild/Simple.java (100%) rename test cases/unit/{112 classpath => 113 classpath}/meson.build (100%) rename test cases/unit/{113 list build options => 114 list build options}/meson.build (100%) rename test cases/unit/{113 list build options => 114 list build options}/meson_options.txt (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/main.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/meson.build (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s1.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s2.c (100%) rename test cases/unit/{114 complex link cases => 115 complex link cases}/s3.c (100%) rename test cases/unit/{115 c cpp stds => 116 c cpp stds}/meson.build (100%) rename test cases/unit/{116 empty project => 117 empty project}/expected_mods.json (100%) rename test cases/unit/{116 empty project => 117 empty project}/meson.build (100%) rename test cases/unit/{117 genvslite => 118 genvslite}/main.cpp (100%) rename test cases/unit/{117 genvslite => 118 genvslite}/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/cache_dir/bar/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/cache_dir/foo.zip (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/meson.build (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/subprojects/bar.wrap (100%) rename test cases/unit/{118 meson package cache dir => 119 meson package cache dir}/subprojects/foo.wrap (100%) rename test cases/unit/{119 openssl cmake bug => 120 openssl cmake bug}/meson.build (100%) rename test cases/unit/{119 openssl cmake bug => 120 openssl cmake bug}/nativefile.ini (100%) rename test cases/unit/{120 rewrite => 121 rewrite}/meson.build (100%) rename test cases/unit/{121 executable suffix => 122 executable suffix}/main.c (100%) rename test cases/unit/{121 executable suffix => 122 executable suffix}/meson.build (100%) rename test cases/unit/{122 reconfigure base options => 123 reconfigure base options}/meson.build (100%) rename test cases/unit/{122 reconfigure base options => 123 reconfigure base options}/subprojects/sub/meson.build (100%) rename test cases/unit/{123 pkgsubproj => 124 pkgsubproj}/meson.build (100%) rename test cases/unit/{123 pkgsubproj => 124 pkgsubproj}/subprojects/sub/meson.build (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.c (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.dat (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo.h (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/foo/foofile (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/meson.build (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.c (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.dat (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar.h (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/bar/barfile (100%) rename test cases/unit/{91 install skip subprojects => 92 install skip subprojects}/subprojects/bar/meson.build (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/meson.build (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/meson_options.txt (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/subprojects/sub/foo.c (100%) rename test cases/unit/{92 new subproject in configured project => 93 new subproject in configured project}/subprojects/sub/meson.build (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format-ignore (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/.clang-format-include (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/meson.build (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/not-included/badformat.cpp (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/src/badformat.c (100%) rename test cases/unit/{93 clangformat => 94 clangformat}/src/badformat.cpp (100%) rename test cases/unit/{94 custominc => 95 custominc}/easytogrepfor/genh.py (100%) rename test cases/unit/{94 custominc => 95 custominc}/easytogrepfor/meson.build (100%) rename test cases/unit/{94 custominc => 95 custominc}/helper.c (100%) rename test cases/unit/{94 custominc => 95 custominc}/meson.build (100%) rename test cases/unit/{94 custominc => 95 custominc}/prog.c (100%) rename test cases/unit/{94 custominc => 95 custominc}/prog2.c (100%) rename test cases/unit/{95 implicit force fallback => 96 implicit force fallback}/meson.build (100%) rename test cases/unit/{95 implicit force fallback => 96 implicit force fallback}/subprojects/something/meson.build (100%) rename test cases/unit/{96 compiler.links file arg => 97 compiler.links file arg}/meson.build (100%) rename test cases/unit/{96 compiler.links file arg => 97 compiler.links file arg}/test.c (100%) rename test cases/unit/{97 link full name => 98 link full name}/.gitignore (100%) rename test cases/unit/{97 link full name => 98 link full name}/libtestprovider/meson.build (100%) rename test cases/unit/{97 link full name => 98 link full name}/libtestprovider/provider.c (100%) rename test cases/unit/{97 link full name => 98 link full name}/proguser/meson.build (100%) rename test cases/unit/{97 link full name => 98 link full name}/proguser/receiver.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-custom.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/bar-notag.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/custom_files/data.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/excluded.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/excluded/placeholder.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/excludes/installed.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/foo.in (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/foo1-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/lib.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/main.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/meson.build (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/script.py (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/bar2-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/foo2.in (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/foo3-devel.h (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/lib.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/main.c (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/meson.build (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subdir/script.py (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/aaa.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/bbb.txt (100%) rename test cases/unit/{98 install all targets => 99 install all targets}/subprojects/subproject/meson.build (100%) diff --git a/run_format_tests.py b/run_format_tests.py index 30c975882d9b..dee787b332ae 100755 --- a/run_format_tests.py +++ b/run_format_tests.py @@ -51,7 +51,7 @@ def check_format() -> None: 'work area', '.eggs', '_cache', # e.g. .mypy_cache 'venv', # virtualenvs have DOS line endings - '120 rewrite', # we explicitly test for tab in meson.build file + '121 rewrite', # we explicitly test for tab in meson.build file '3 editorconfig', } for (root, _, filenames) in os.walk('.'): diff --git a/test cases/unit/99 custom target name/file.txt.in b/test cases/unit/100 custom target name/file.txt.in similarity index 100% rename from test cases/unit/99 custom target name/file.txt.in rename to test cases/unit/100 custom target name/file.txt.in diff --git a/test cases/unit/99 custom target name/meson.build b/test cases/unit/100 custom target name/meson.build similarity index 100% rename from test cases/unit/99 custom target name/meson.build rename to test cases/unit/100 custom target name/meson.build diff --git a/test cases/unit/99 custom target name/subdir/meson.build b/test cases/unit/100 custom target name/subdir/meson.build similarity index 100% rename from test cases/unit/99 custom target name/subdir/meson.build rename to test cases/unit/100 custom target name/subdir/meson.build diff --git a/test cases/unit/100 relative find program/foo.py b/test cases/unit/101 relative find program/foo.py similarity index 100% rename from test cases/unit/100 relative find program/foo.py rename to test cases/unit/101 relative find program/foo.py diff --git a/test cases/unit/100 relative find program/meson.build b/test cases/unit/101 relative find program/meson.build similarity index 100% rename from test cases/unit/100 relative find program/meson.build rename to test cases/unit/101 relative find program/meson.build diff --git a/test cases/unit/100 relative find program/subdir/meson.build b/test cases/unit/101 relative find program/subdir/meson.build similarity index 100% rename from test cases/unit/100 relative find program/subdir/meson.build rename to test cases/unit/101 relative find program/subdir/meson.build diff --git a/test cases/unit/101 rlib linkage/lib2.rs b/test cases/unit/102 rlib linkage/lib2.rs similarity index 100% rename from test cases/unit/101 rlib linkage/lib2.rs rename to test cases/unit/102 rlib linkage/lib2.rs diff --git a/test cases/unit/101 rlib linkage/main.rs b/test cases/unit/102 rlib linkage/main.rs similarity index 100% rename from test cases/unit/101 rlib linkage/main.rs rename to test cases/unit/102 rlib linkage/main.rs diff --git a/test cases/unit/101 rlib linkage/meson.build b/test cases/unit/102 rlib linkage/meson.build similarity index 100% rename from test cases/unit/101 rlib linkage/meson.build rename to test cases/unit/102 rlib linkage/meson.build diff --git a/test cases/unit/102 python without pkgconfig/meson.build b/test cases/unit/103 python without pkgconfig/meson.build similarity index 100% rename from test cases/unit/102 python without pkgconfig/meson.build rename to test cases/unit/103 python without pkgconfig/meson.build diff --git a/test cases/unit/103 strip/lib.c b/test cases/unit/104 strip/lib.c similarity index 100% rename from test cases/unit/103 strip/lib.c rename to test cases/unit/104 strip/lib.c diff --git a/test cases/unit/103 strip/meson.build b/test cases/unit/104 strip/meson.build similarity index 100% rename from test cases/unit/103 strip/meson.build rename to test cases/unit/104 strip/meson.build diff --git a/test cases/unit/104 debug function/meson.build b/test cases/unit/105 debug function/meson.build similarity index 100% rename from test cases/unit/104 debug function/meson.build rename to test cases/unit/105 debug function/meson.build diff --git a/test cases/unit/105 pkgconfig relocatable with absolute path/meson.build b/test cases/unit/106 pkgconfig relocatable with absolute path/meson.build similarity index 100% rename from test cases/unit/105 pkgconfig relocatable with absolute path/meson.build rename to test cases/unit/106 pkgconfig relocatable with absolute path/meson.build diff --git a/test cases/unit/106 underspecified mtest/main.c b/test cases/unit/107 underspecified mtest/main.c similarity index 100% rename from test cases/unit/106 underspecified mtest/main.c rename to test cases/unit/107 underspecified mtest/main.c diff --git a/test cases/unit/106 underspecified mtest/meson.build b/test cases/unit/107 underspecified mtest/meson.build similarity index 100% rename from test cases/unit/106 underspecified mtest/meson.build rename to test cases/unit/107 underspecified mtest/meson.build diff --git a/test cases/unit/106 underspecified mtest/runner.py b/test cases/unit/107 underspecified mtest/runner.py similarity index 100% rename from test cases/unit/106 underspecified mtest/runner.py rename to test cases/unit/107 underspecified mtest/runner.py diff --git a/test cases/unit/107 subproject symlink/cp.py b/test cases/unit/108 subproject symlink/cp.py similarity index 100% rename from test cases/unit/107 subproject symlink/cp.py rename to test cases/unit/108 subproject symlink/cp.py diff --git a/test cases/unit/107 subproject symlink/main.c b/test cases/unit/108 subproject symlink/main.c similarity index 100% rename from test cases/unit/107 subproject symlink/main.c rename to test cases/unit/108 subproject symlink/main.c diff --git a/test cases/unit/107 subproject symlink/meson.build b/test cases/unit/108 subproject symlink/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/meson.build rename to test cases/unit/108 subproject symlink/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile b/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile rename to test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build b/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build rename to test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/meson.build b/test cases/unit/108 subproject symlink/symlinked_subproject/meson.build similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/meson.build rename to test cases/unit/108 subproject symlink/symlinked_subproject/meson.build diff --git a/test cases/unit/107 subproject symlink/symlinked_subproject/src.c b/test cases/unit/108 subproject symlink/symlinked_subproject/src.c similarity index 100% rename from test cases/unit/107 subproject symlink/symlinked_subproject/src.c rename to test cases/unit/108 subproject symlink/symlinked_subproject/src.c diff --git a/test cases/unit/108 new subproject on reconfigure/meson.build b/test cases/unit/109 new subproject on reconfigure/meson.build similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/meson.build rename to test cases/unit/109 new subproject on reconfigure/meson.build diff --git a/test cases/unit/108 new subproject on reconfigure/meson_options.txt b/test cases/unit/109 new subproject on reconfigure/meson_options.txt similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/meson_options.txt rename to test cases/unit/109 new subproject on reconfigure/meson_options.txt diff --git a/test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c b/test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c rename to test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c diff --git a/test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build b/test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build similarity index 100% rename from test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build rename to test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build diff --git a/test cases/unit/109 configure same noop/meson.build b/test cases/unit/110 configure same noop/meson.build similarity index 100% rename from test cases/unit/109 configure same noop/meson.build rename to test cases/unit/110 configure same noop/meson.build diff --git a/test cases/unit/109 configure same noop/meson_options.txt b/test cases/unit/110 configure same noop/meson_options.txt similarity index 100% rename from test cases/unit/109 configure same noop/meson_options.txt rename to test cases/unit/110 configure same noop/meson_options.txt diff --git a/test cases/unit/110 freeze/freeze.c b/test cases/unit/111 freeze/freeze.c similarity index 100% rename from test cases/unit/110 freeze/freeze.c rename to test cases/unit/111 freeze/freeze.c diff --git a/test cases/unit/110 freeze/meson.build b/test cases/unit/111 freeze/meson.build similarity index 100% rename from test cases/unit/110 freeze/meson.build rename to test cases/unit/111 freeze/meson.build diff --git a/test cases/unit/111 replace unencodable xml chars/meson.build b/test cases/unit/112 replace unencodable xml chars/meson.build similarity index 100% rename from test cases/unit/111 replace unencodable xml chars/meson.build rename to test cases/unit/112 replace unencodable xml chars/meson.build diff --git a/test cases/unit/111 replace unencodable xml chars/script.py b/test cases/unit/112 replace unencodable xml chars/script.py similarity index 100% rename from test cases/unit/111 replace unencodable xml chars/script.py rename to test cases/unit/112 replace unencodable xml chars/script.py diff --git a/test cases/unit/112 classpath/com/mesonbuild/Simple.java b/test cases/unit/113 classpath/com/mesonbuild/Simple.java similarity index 100% rename from test cases/unit/112 classpath/com/mesonbuild/Simple.java rename to test cases/unit/113 classpath/com/mesonbuild/Simple.java diff --git a/test cases/unit/112 classpath/meson.build b/test cases/unit/113 classpath/meson.build similarity index 100% rename from test cases/unit/112 classpath/meson.build rename to test cases/unit/113 classpath/meson.build diff --git a/test cases/unit/113 list build options/meson.build b/test cases/unit/114 list build options/meson.build similarity index 100% rename from test cases/unit/113 list build options/meson.build rename to test cases/unit/114 list build options/meson.build diff --git a/test cases/unit/113 list build options/meson_options.txt b/test cases/unit/114 list build options/meson_options.txt similarity index 100% rename from test cases/unit/113 list build options/meson_options.txt rename to test cases/unit/114 list build options/meson_options.txt diff --git a/test cases/unit/114 complex link cases/main.c b/test cases/unit/115 complex link cases/main.c similarity index 100% rename from test cases/unit/114 complex link cases/main.c rename to test cases/unit/115 complex link cases/main.c diff --git a/test cases/unit/114 complex link cases/meson.build b/test cases/unit/115 complex link cases/meson.build similarity index 100% rename from test cases/unit/114 complex link cases/meson.build rename to test cases/unit/115 complex link cases/meson.build diff --git a/test cases/unit/114 complex link cases/s1.c b/test cases/unit/115 complex link cases/s1.c similarity index 100% rename from test cases/unit/114 complex link cases/s1.c rename to test cases/unit/115 complex link cases/s1.c diff --git a/test cases/unit/114 complex link cases/s2.c b/test cases/unit/115 complex link cases/s2.c similarity index 100% rename from test cases/unit/114 complex link cases/s2.c rename to test cases/unit/115 complex link cases/s2.c diff --git a/test cases/unit/114 complex link cases/s3.c b/test cases/unit/115 complex link cases/s3.c similarity index 100% rename from test cases/unit/114 complex link cases/s3.c rename to test cases/unit/115 complex link cases/s3.c diff --git a/test cases/unit/115 c cpp stds/meson.build b/test cases/unit/116 c cpp stds/meson.build similarity index 100% rename from test cases/unit/115 c cpp stds/meson.build rename to test cases/unit/116 c cpp stds/meson.build diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/117 empty project/expected_mods.json similarity index 100% rename from test cases/unit/116 empty project/expected_mods.json rename to test cases/unit/117 empty project/expected_mods.json diff --git a/test cases/unit/116 empty project/meson.build b/test cases/unit/117 empty project/meson.build similarity index 100% rename from test cases/unit/116 empty project/meson.build rename to test cases/unit/117 empty project/meson.build diff --git a/test cases/unit/117 genvslite/main.cpp b/test cases/unit/118 genvslite/main.cpp similarity index 100% rename from test cases/unit/117 genvslite/main.cpp rename to test cases/unit/118 genvslite/main.cpp diff --git a/test cases/unit/117 genvslite/meson.build b/test cases/unit/118 genvslite/meson.build similarity index 100% rename from test cases/unit/117 genvslite/meson.build rename to test cases/unit/118 genvslite/meson.build diff --git a/test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build b/test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build similarity index 100% rename from test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build rename to test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build diff --git a/test cases/unit/118 meson package cache dir/cache_dir/foo.zip b/test cases/unit/119 meson package cache dir/cache_dir/foo.zip similarity index 100% rename from test cases/unit/118 meson package cache dir/cache_dir/foo.zip rename to test cases/unit/119 meson package cache dir/cache_dir/foo.zip diff --git a/test cases/unit/118 meson package cache dir/meson.build b/test cases/unit/119 meson package cache dir/meson.build similarity index 100% rename from test cases/unit/118 meson package cache dir/meson.build rename to test cases/unit/119 meson package cache dir/meson.build diff --git a/test cases/unit/118 meson package cache dir/subprojects/bar.wrap b/test cases/unit/119 meson package cache dir/subprojects/bar.wrap similarity index 100% rename from test cases/unit/118 meson package cache dir/subprojects/bar.wrap rename to test cases/unit/119 meson package cache dir/subprojects/bar.wrap diff --git a/test cases/unit/118 meson package cache dir/subprojects/foo.wrap b/test cases/unit/119 meson package cache dir/subprojects/foo.wrap similarity index 100% rename from test cases/unit/118 meson package cache dir/subprojects/foo.wrap rename to test cases/unit/119 meson package cache dir/subprojects/foo.wrap diff --git a/test cases/unit/119 openssl cmake bug/meson.build b/test cases/unit/120 openssl cmake bug/meson.build similarity index 100% rename from test cases/unit/119 openssl cmake bug/meson.build rename to test cases/unit/120 openssl cmake bug/meson.build diff --git a/test cases/unit/119 openssl cmake bug/nativefile.ini b/test cases/unit/120 openssl cmake bug/nativefile.ini similarity index 100% rename from test cases/unit/119 openssl cmake bug/nativefile.ini rename to test cases/unit/120 openssl cmake bug/nativefile.ini diff --git a/test cases/unit/120 rewrite/meson.build b/test cases/unit/121 rewrite/meson.build similarity index 100% rename from test cases/unit/120 rewrite/meson.build rename to test cases/unit/121 rewrite/meson.build diff --git a/test cases/unit/121 executable suffix/main.c b/test cases/unit/122 executable suffix/main.c similarity index 100% rename from test cases/unit/121 executable suffix/main.c rename to test cases/unit/122 executable suffix/main.c diff --git a/test cases/unit/121 executable suffix/meson.build b/test cases/unit/122 executable suffix/meson.build similarity index 100% rename from test cases/unit/121 executable suffix/meson.build rename to test cases/unit/122 executable suffix/meson.build diff --git a/test cases/unit/122 reconfigure base options/meson.build b/test cases/unit/123 reconfigure base options/meson.build similarity index 100% rename from test cases/unit/122 reconfigure base options/meson.build rename to test cases/unit/123 reconfigure base options/meson.build diff --git a/test cases/unit/122 reconfigure base options/subprojects/sub/meson.build b/test cases/unit/123 reconfigure base options/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/122 reconfigure base options/subprojects/sub/meson.build rename to test cases/unit/123 reconfigure base options/subprojects/sub/meson.build diff --git a/test cases/unit/123 pkgsubproj/meson.build b/test cases/unit/124 pkgsubproj/meson.build similarity index 100% rename from test cases/unit/123 pkgsubproj/meson.build rename to test cases/unit/124 pkgsubproj/meson.build diff --git a/test cases/unit/123 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/124 pkgsubproj/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/123 pkgsubproj/subprojects/sub/meson.build rename to test cases/unit/124 pkgsubproj/subprojects/sub/meson.build diff --git a/test cases/unit/91 install skip subprojects/foo.c b/test cases/unit/92 install skip subprojects/foo.c similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.c rename to test cases/unit/92 install skip subprojects/foo.c diff --git a/test cases/unit/91 install skip subprojects/foo.dat b/test cases/unit/92 install skip subprojects/foo.dat similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.dat rename to test cases/unit/92 install skip subprojects/foo.dat diff --git a/test cases/unit/91 install skip subprojects/foo.h b/test cases/unit/92 install skip subprojects/foo.h similarity index 100% rename from test cases/unit/91 install skip subprojects/foo.h rename to test cases/unit/92 install skip subprojects/foo.h diff --git a/test cases/unit/91 install skip subprojects/foo/foofile b/test cases/unit/92 install skip subprojects/foo/foofile similarity index 100% rename from test cases/unit/91 install skip subprojects/foo/foofile rename to test cases/unit/92 install skip subprojects/foo/foofile diff --git a/test cases/unit/91 install skip subprojects/meson.build b/test cases/unit/92 install skip subprojects/meson.build similarity index 100% rename from test cases/unit/91 install skip subprojects/meson.build rename to test cases/unit/92 install skip subprojects/meson.build diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.c b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.c similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.c rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.c diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar.h b/test cases/unit/92 install skip subprojects/subprojects/bar/bar.h similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar.h rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar.h diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile b/test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile rename to test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile diff --git a/test cases/unit/91 install skip subprojects/subprojects/bar/meson.build b/test cases/unit/92 install skip subprojects/subprojects/bar/meson.build similarity index 100% rename from test cases/unit/91 install skip subprojects/subprojects/bar/meson.build rename to test cases/unit/92 install skip subprojects/subprojects/bar/meson.build diff --git a/test cases/unit/92 new subproject in configured project/meson.build b/test cases/unit/93 new subproject in configured project/meson.build similarity index 100% rename from test cases/unit/92 new subproject in configured project/meson.build rename to test cases/unit/93 new subproject in configured project/meson.build diff --git a/test cases/unit/92 new subproject in configured project/meson_options.txt b/test cases/unit/93 new subproject in configured project/meson_options.txt similarity index 100% rename from test cases/unit/92 new subproject in configured project/meson_options.txt rename to test cases/unit/93 new subproject in configured project/meson_options.txt diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c b/test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c similarity index 100% rename from test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c rename to test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c diff --git a/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build b/test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build rename to test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build diff --git a/test cases/unit/93 clangformat/.clang-format b/test cases/unit/94 clangformat/.clang-format similarity index 100% rename from test cases/unit/93 clangformat/.clang-format rename to test cases/unit/94 clangformat/.clang-format diff --git a/test cases/unit/93 clangformat/.clang-format-ignore b/test cases/unit/94 clangformat/.clang-format-ignore similarity index 100% rename from test cases/unit/93 clangformat/.clang-format-ignore rename to test cases/unit/94 clangformat/.clang-format-ignore diff --git a/test cases/unit/93 clangformat/.clang-format-include b/test cases/unit/94 clangformat/.clang-format-include similarity index 100% rename from test cases/unit/93 clangformat/.clang-format-include rename to test cases/unit/94 clangformat/.clang-format-include diff --git a/test cases/unit/93 clangformat/meson.build b/test cases/unit/94 clangformat/meson.build similarity index 100% rename from test cases/unit/93 clangformat/meson.build rename to test cases/unit/94 clangformat/meson.build diff --git a/test cases/unit/93 clangformat/not-included/badformat.cpp b/test cases/unit/94 clangformat/not-included/badformat.cpp similarity index 100% rename from test cases/unit/93 clangformat/not-included/badformat.cpp rename to test cases/unit/94 clangformat/not-included/badformat.cpp diff --git a/test cases/unit/93 clangformat/src/badformat.c b/test cases/unit/94 clangformat/src/badformat.c similarity index 100% rename from test cases/unit/93 clangformat/src/badformat.c rename to test cases/unit/94 clangformat/src/badformat.c diff --git a/test cases/unit/93 clangformat/src/badformat.cpp b/test cases/unit/94 clangformat/src/badformat.cpp similarity index 100% rename from test cases/unit/93 clangformat/src/badformat.cpp rename to test cases/unit/94 clangformat/src/badformat.cpp diff --git a/test cases/unit/94 custominc/easytogrepfor/genh.py b/test cases/unit/95 custominc/easytogrepfor/genh.py similarity index 100% rename from test cases/unit/94 custominc/easytogrepfor/genh.py rename to test cases/unit/95 custominc/easytogrepfor/genh.py diff --git a/test cases/unit/94 custominc/easytogrepfor/meson.build b/test cases/unit/95 custominc/easytogrepfor/meson.build similarity index 100% rename from test cases/unit/94 custominc/easytogrepfor/meson.build rename to test cases/unit/95 custominc/easytogrepfor/meson.build diff --git a/test cases/unit/94 custominc/helper.c b/test cases/unit/95 custominc/helper.c similarity index 100% rename from test cases/unit/94 custominc/helper.c rename to test cases/unit/95 custominc/helper.c diff --git a/test cases/unit/94 custominc/meson.build b/test cases/unit/95 custominc/meson.build similarity index 100% rename from test cases/unit/94 custominc/meson.build rename to test cases/unit/95 custominc/meson.build diff --git a/test cases/unit/94 custominc/prog.c b/test cases/unit/95 custominc/prog.c similarity index 100% rename from test cases/unit/94 custominc/prog.c rename to test cases/unit/95 custominc/prog.c diff --git a/test cases/unit/94 custominc/prog2.c b/test cases/unit/95 custominc/prog2.c similarity index 100% rename from test cases/unit/94 custominc/prog2.c rename to test cases/unit/95 custominc/prog2.c diff --git a/test cases/unit/95 implicit force fallback/meson.build b/test cases/unit/96 implicit force fallback/meson.build similarity index 100% rename from test cases/unit/95 implicit force fallback/meson.build rename to test cases/unit/96 implicit force fallback/meson.build diff --git a/test cases/unit/95 implicit force fallback/subprojects/something/meson.build b/test cases/unit/96 implicit force fallback/subprojects/something/meson.build similarity index 100% rename from test cases/unit/95 implicit force fallback/subprojects/something/meson.build rename to test cases/unit/96 implicit force fallback/subprojects/something/meson.build diff --git a/test cases/unit/96 compiler.links file arg/meson.build b/test cases/unit/97 compiler.links file arg/meson.build similarity index 100% rename from test cases/unit/96 compiler.links file arg/meson.build rename to test cases/unit/97 compiler.links file arg/meson.build diff --git a/test cases/unit/96 compiler.links file arg/test.c b/test cases/unit/97 compiler.links file arg/test.c similarity index 100% rename from test cases/unit/96 compiler.links file arg/test.c rename to test cases/unit/97 compiler.links file arg/test.c diff --git a/test cases/unit/97 link full name/.gitignore b/test cases/unit/98 link full name/.gitignore similarity index 100% rename from test cases/unit/97 link full name/.gitignore rename to test cases/unit/98 link full name/.gitignore diff --git a/test cases/unit/97 link full name/libtestprovider/meson.build b/test cases/unit/98 link full name/libtestprovider/meson.build similarity index 100% rename from test cases/unit/97 link full name/libtestprovider/meson.build rename to test cases/unit/98 link full name/libtestprovider/meson.build diff --git a/test cases/unit/97 link full name/libtestprovider/provider.c b/test cases/unit/98 link full name/libtestprovider/provider.c similarity index 100% rename from test cases/unit/97 link full name/libtestprovider/provider.c rename to test cases/unit/98 link full name/libtestprovider/provider.c diff --git a/test cases/unit/97 link full name/proguser/meson.build b/test cases/unit/98 link full name/proguser/meson.build similarity index 100% rename from test cases/unit/97 link full name/proguser/meson.build rename to test cases/unit/98 link full name/proguser/meson.build diff --git a/test cases/unit/97 link full name/proguser/receiver.c b/test cases/unit/98 link full name/proguser/receiver.c similarity index 100% rename from test cases/unit/97 link full name/proguser/receiver.c rename to test cases/unit/98 link full name/proguser/receiver.c diff --git a/test cases/unit/98 install all targets/bar-custom.txt b/test cases/unit/99 install all targets/bar-custom.txt similarity index 100% rename from test cases/unit/98 install all targets/bar-custom.txt rename to test cases/unit/99 install all targets/bar-custom.txt diff --git a/test cases/unit/98 install all targets/bar-devel.h b/test cases/unit/99 install all targets/bar-devel.h similarity index 100% rename from test cases/unit/98 install all targets/bar-devel.h rename to test cases/unit/99 install all targets/bar-devel.h diff --git a/test cases/unit/98 install all targets/bar-notag.txt b/test cases/unit/99 install all targets/bar-notag.txt similarity index 100% rename from test cases/unit/98 install all targets/bar-notag.txt rename to test cases/unit/99 install all targets/bar-notag.txt diff --git a/test cases/unit/98 install all targets/custom_files/data.txt b/test cases/unit/99 install all targets/custom_files/data.txt similarity index 100% rename from test cases/unit/98 install all targets/custom_files/data.txt rename to test cases/unit/99 install all targets/custom_files/data.txt diff --git a/test cases/unit/98 install all targets/excludes/excluded.txt b/test cases/unit/99 install all targets/excludes/excluded.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/excluded.txt rename to test cases/unit/99 install all targets/excludes/excluded.txt diff --git a/test cases/unit/98 install all targets/excludes/excluded/placeholder.txt b/test cases/unit/99 install all targets/excludes/excluded/placeholder.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/excluded/placeholder.txt rename to test cases/unit/99 install all targets/excludes/excluded/placeholder.txt diff --git a/test cases/unit/98 install all targets/excludes/installed.txt b/test cases/unit/99 install all targets/excludes/installed.txt similarity index 100% rename from test cases/unit/98 install all targets/excludes/installed.txt rename to test cases/unit/99 install all targets/excludes/installed.txt diff --git a/test cases/unit/98 install all targets/foo.in b/test cases/unit/99 install all targets/foo.in similarity index 100% rename from test cases/unit/98 install all targets/foo.in rename to test cases/unit/99 install all targets/foo.in diff --git a/test cases/unit/98 install all targets/foo1-devel.h b/test cases/unit/99 install all targets/foo1-devel.h similarity index 100% rename from test cases/unit/98 install all targets/foo1-devel.h rename to test cases/unit/99 install all targets/foo1-devel.h diff --git a/test cases/unit/98 install all targets/lib.c b/test cases/unit/99 install all targets/lib.c similarity index 100% rename from test cases/unit/98 install all targets/lib.c rename to test cases/unit/99 install all targets/lib.c diff --git a/test cases/unit/98 install all targets/main.c b/test cases/unit/99 install all targets/main.c similarity index 100% rename from test cases/unit/98 install all targets/main.c rename to test cases/unit/99 install all targets/main.c diff --git a/test cases/unit/98 install all targets/meson.build b/test cases/unit/99 install all targets/meson.build similarity index 100% rename from test cases/unit/98 install all targets/meson.build rename to test cases/unit/99 install all targets/meson.build diff --git a/test cases/unit/98 install all targets/script.py b/test cases/unit/99 install all targets/script.py similarity index 100% rename from test cases/unit/98 install all targets/script.py rename to test cases/unit/99 install all targets/script.py diff --git a/test cases/unit/98 install all targets/subdir/bar2-devel.h b/test cases/unit/99 install all targets/subdir/bar2-devel.h similarity index 100% rename from test cases/unit/98 install all targets/subdir/bar2-devel.h rename to test cases/unit/99 install all targets/subdir/bar2-devel.h diff --git a/test cases/unit/98 install all targets/subdir/foo2.in b/test cases/unit/99 install all targets/subdir/foo2.in similarity index 100% rename from test cases/unit/98 install all targets/subdir/foo2.in rename to test cases/unit/99 install all targets/subdir/foo2.in diff --git a/test cases/unit/98 install all targets/subdir/foo3-devel.h b/test cases/unit/99 install all targets/subdir/foo3-devel.h similarity index 100% rename from test cases/unit/98 install all targets/subdir/foo3-devel.h rename to test cases/unit/99 install all targets/subdir/foo3-devel.h diff --git a/test cases/unit/98 install all targets/subdir/lib.c b/test cases/unit/99 install all targets/subdir/lib.c similarity index 100% rename from test cases/unit/98 install all targets/subdir/lib.c rename to test cases/unit/99 install all targets/subdir/lib.c diff --git a/test cases/unit/98 install all targets/subdir/main.c b/test cases/unit/99 install all targets/subdir/main.c similarity index 100% rename from test cases/unit/98 install all targets/subdir/main.c rename to test cases/unit/99 install all targets/subdir/main.c diff --git a/test cases/unit/98 install all targets/subdir/meson.build b/test cases/unit/99 install all targets/subdir/meson.build similarity index 100% rename from test cases/unit/98 install all targets/subdir/meson.build rename to test cases/unit/99 install all targets/subdir/meson.build diff --git a/test cases/unit/98 install all targets/subdir/script.py b/test cases/unit/99 install all targets/subdir/script.py similarity index 100% rename from test cases/unit/98 install all targets/subdir/script.py rename to test cases/unit/99 install all targets/subdir/script.py diff --git a/test cases/unit/98 install all targets/subprojects/subproject/aaa.txt b/test cases/unit/99 install all targets/subprojects/subproject/aaa.txt similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/aaa.txt rename to test cases/unit/99 install all targets/subprojects/subproject/aaa.txt diff --git a/test cases/unit/98 install all targets/subprojects/subproject/bbb.txt b/test cases/unit/99 install all targets/subprojects/subproject/bbb.txt similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/bbb.txt rename to test cases/unit/99 install all targets/subprojects/subproject/bbb.txt diff --git a/test cases/unit/98 install all targets/subprojects/subproject/meson.build b/test cases/unit/99 install all targets/subprojects/subproject/meson.build similarity index 100% rename from test cases/unit/98 install all targets/subprojects/subproject/meson.build rename to test cases/unit/99 install all targets/subprojects/subproject/meson.build diff --git a/tools/dircondenser.py b/tools/dircondenser.py index fcdb1d5dc8d5..b8679a4c78bf 100755 --- a/tools/dircondenser.py +++ b/tools/dircondenser.py @@ -70,6 +70,7 @@ def condense(dirname: str) -> None: os.chdir(curdir) replace_source('run_unittests.py', replacements) replace_source('run_project_tests.py', replacements) + replace_source('run_format_tests.py', replacements) for f in glob('unittests/*.py'): replace_source(f, replacements) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index b5338b834a75..96576b0ee888 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -467,7 +467,7 @@ def test_replace_unencodable_xml_chars_unit(self): ''' if not shutil.which('xmllint'): raise SkipTest('xmllint not installed') - testdir = os.path.join(self.unit_test_dir, '111 replace unencodable xml chars') + testdir = os.path.join(self.unit_test_dir, '112 replace unencodable xml chars') self.init(testdir) tests_command_output = self.run_tests() junit_xml_logs = Path(self.logdir, 'testlog.junit.xml') @@ -627,7 +627,7 @@ def test_forcefallback(self): self.run_tests() def test_implicit_forcefallback(self): - testdir = os.path.join(self.unit_test_dir, '95 implicit force fallback') + testdir = os.path.join(self.unit_test_dir, '96 implicit force fallback') with self.assertRaises(subprocess.CalledProcessError): self.init(testdir) self.init(testdir, extra_args=['--wrap-mode=forcefallback']) @@ -2061,7 +2061,7 @@ def check_pcfile(name, *, relocatable, levels=2): check_pcfile('libvartest2.pc', relocatable=False) self.wipe() - testdir_abs = os.path.join(self.unit_test_dir, '105 pkgconfig relocatable with absolute path') + testdir_abs = os.path.join(self.unit_test_dir, '106 pkgconfig relocatable with absolute path') self.init(testdir_abs) check_pcfile('libsimple.pc', relocatable=True, levels=3) @@ -2143,7 +2143,7 @@ def get_opt(): self.assertDictEqual(original, expected) def test_executable_names(self): - testdir = os.path.join(self.unit_test_dir, '121 executable suffix') + testdir = os.path.join(self.unit_test_dir, '122 executable suffix') self.init(testdir) self.build() exe1 = os.path.join(self.builddir, 'foo' + exe_suffix) @@ -2234,7 +2234,7 @@ def test_options_with_choices_changing(self) -> None: def test_options_listed_in_build_options(self) -> None: """Detect when changed options become listed in build options.""" - testdir = os.path.join(self.unit_test_dir, '113 list build options') + testdir = os.path.join(self.unit_test_dir, '114 list build options') out = self.init(testdir) for line in out.splitlines(): @@ -2472,7 +2472,7 @@ def test_check_module_linking(self): self.assertIn(msg, out) def test_mixed_language_linker_check(self): - testdir = os.path.join(self.unit_test_dir, '96 compiler.links file arg') + testdir = os.path.join(self.unit_test_dir, '97 compiler.links file arg') self.init(testdir) cmds = self.get_meson_log_compiler_checks() self.assertEqual(len(cmds), 5) @@ -4305,7 +4305,7 @@ def test_build_b_options(self) -> None: self.init(srcdir, extra_args=['-Dbuild.b_lto=true']) def test_install_skip_subprojects(self): - testdir = os.path.join(self.unit_test_dir, '91 install skip subprojects') + testdir = os.path.join(self.unit_test_dir, '92 install skip subprojects') self.init(testdir) self.build() @@ -4352,7 +4352,7 @@ def check_installed_files(extra_args, expected): check_installed_files(['--skip-subprojects', 'another'], all_expected) def test_adding_subproject_to_configure_project(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '92 new subproject in configured project') + srcdir = os.path.join(self.unit_test_dir, '93 new subproject in configured project') self.init(srcdir) self.build() self.setconf('-Duse-sub=true') @@ -4402,7 +4402,7 @@ def test_clang_format_check(self): if not shutil.which('clang-format'): raise SkipTest('clang-format not found') - testdir = os.path.join(self.unit_test_dir, '93 clangformat') + testdir = os.path.join(self.unit_test_dir, '94 clangformat') newdir = os.path.join(self.builddir, 'testdir') shutil.copytree(testdir, newdir) self.new_builddir() @@ -4427,7 +4427,7 @@ def test_clang_format_check(self): self.build('clang-format-check') def test_custom_target_implicit_include(self): - testdir = os.path.join(self.unit_test_dir, '94 custominc') + testdir = os.path.join(self.unit_test_dir, '95 custominc') self.init(testdir) self.build() compdb = self.get_compdb() @@ -4467,7 +4467,7 @@ def test_env_flags_to_linker(self) -> None: self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') self.init(testdir) self.build() @@ -4638,7 +4638,7 @@ def test_install_script_dry_run(self): def test_introspect_install_plan(self): - testdir = os.path.join(self.unit_test_dir, '98 install all targets') + testdir = os.path.join(self.unit_test_dir, '99 install all targets') introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json') self.init(testdir) self.assertPathExists(introfile) @@ -4935,7 +4935,7 @@ def test_rust_rlib_linkage(self) -> None: }} ''') - testdir = os.path.join(self.unit_test_dir, '101 rlib linkage') + testdir = os.path.join(self.unit_test_dir, '102 rlib linkage') gen_file = os.path.join(testdir, 'lib.rs') with open(gen_file, 'w', encoding='utf-8') as f: f.write(template.format(0)) @@ -4983,7 +4983,7 @@ def test_bindgen_drops_invalid(self) -> None: return def test_custom_target_name(self): - testdir = os.path.join(self.unit_test_dir, '99 custom target name') + testdir = os.path.join(self.unit_test_dir, '100 custom target name') self.init(testdir) out = self.build() if self.backend is Backend.ninja: @@ -4991,7 +4991,7 @@ def test_custom_target_name(self): self.assertIn('Generating subdir/file.txt with a custom command', out) def test_symlinked_subproject(self): - testdir = os.path.join(self.unit_test_dir, '107 subproject symlink') + testdir = os.path.join(self.unit_test_dir, '108 subproject symlink') subproject_dir = os.path.join(testdir, 'subprojects') subproject = os.path.join(testdir, 'symlinked_subproject') symlinked_subproject = os.path.join(testdir, 'subprojects', 'symlinked_subproject') @@ -5007,7 +5007,7 @@ def test_symlinked_subproject(self): self.build() def test_configure_same_noop(self): - testdir = os.path.join(self.unit_test_dir, '109 configure same noop') + testdir = os.path.join(self.unit_test_dir, '110 configure same noop') args = [ '-Dstring=val', '-Dboolean=true', @@ -5044,7 +5044,7 @@ def test_configure_same_noop(self): oldmtime = newmtime def test_c_cpp_stds(self): - testdir = os.path.join(self.unit_test_dir, '115 c cpp stds') + testdir = os.path.join(self.unit_test_dir, '116 c cpp stds') self.init(testdir) # Invalid values should fail whatever compiler we have with self.assertRaises(subprocess.CalledProcessError): diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index c8d5a538df17..a8608c2b123e 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1369,7 +1369,7 @@ def test_link_arg_fullname(self): see: https://github.com/mesonbuild/meson/issues/9000 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a ''' - testdir = os.path.join(self.unit_test_dir, '97 link full name','libtestprovider') + testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider') oldprefix = self.prefix # install into installdir without using DESTDIR installdir = self.installdir @@ -1382,7 +1382,7 @@ def test_link_arg_fullname(self): self.new_builddir() env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir), 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))} - testdir = os.path.join(self.unit_test_dir, '97 link full name','proguser') + testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser') self.init(testdir,override_envvars=env) # test for link with full path @@ -1788,7 +1788,7 @@ def test_cmake_multilib(self): @skipUnless(is_linux() or is_osx(), 'Test only applicable to Linux and macOS') def test_install_strip(self): - testdir = os.path.join(self.unit_test_dir, '103 strip') + testdir = os.path.join(self.unit_test_dir, '104 strip') self.init(testdir) self.build() @@ -1835,7 +1835,7 @@ def test_isystem_default_removal_with_symlink(self): self.assertFalse(cpp.compiler_args([f'-isystem{symlink}' for symlink in default_symlinks]).to_native()) def test_freezing(self): - testdir = os.path.join(self.unit_test_dir, '110 freeze') + testdir = os.path.join(self.unit_test_dir, '111 freeze') self.init(testdir) self.build() with self.assertRaises(subprocess.CalledProcessError) as e: @@ -1844,7 +1844,7 @@ def test_freezing(self): @skipUnless(is_linux(), "Ninja file differs on different platforms") def test_complex_link_cases(self): - testdir = os.path.join(self.unit_test_dir, '114 complex link cases') + testdir = os.path.join(self.unit_test_dir, '115 complex link cases') self.init(testdir) self.build() with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as f: @@ -1865,5 +1865,5 @@ def test_complex_link_cases(self): 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, '123 pkgsubproj') + testdir = os.path.join(self.unit_test_dir, '124 pkgsubproj') self.init(testdir) diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index ba9cb11530dd..803a1797df2f 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -378,7 +378,7 @@ def test_java_compiler(self): def test_java_classpath(self): if self.backend is not Backend.ninja: raise SkipTest('Jar is only supported with Ninja') - testdir = os.path.join(self.unit_test_dir, '112 classpath') + testdir = os.path.join(self.unit_test_dir, '113 classpath') self.init(testdir) self.build() one_build_path = get_classpath(os.path.join(self.builddir, 'one.jar')) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index aa38b07e0a2e..7c382cfc99e0 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -33,7 +33,7 @@ def test_relative_find_program(self): Tests that find_program() with a relative path does not find the program in current workdir. ''' - testdir = os.path.join(self.unit_test_dir, '100 relative find program') + testdir = os.path.join(self.unit_test_dir, '101 relative find program') self.init(testdir, workdir=testdir) def test_invalid_option_names(self): @@ -92,11 +92,11 @@ def write_file(code: str): interp.process, fname) def test_python_dependency_without_pkgconfig(self): - testdir = os.path.join(self.unit_test_dir, '102 python without pkgconfig') + testdir = os.path.join(self.unit_test_dir, '103 python without pkgconfig') self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'}) def test_debug_function_outputs_to_meson_log(self): - testdir = os.path.join(self.unit_test_dir, '104 debug function') + testdir = os.path.join(self.unit_test_dir, '105 debug function') log_msg = 'This is an example debug output, should only end up in debug log' output = self.init(testdir) @@ -108,7 +108,7 @@ def test_debug_function_outputs_to_meson_log(self): self.assertIn(log_msg, mesonlog) def test_new_subproject_reconfigure(self): - testdir = os.path.join(self.unit_test_dir, '108 new subproject on reconfigure') + testdir = os.path.join(self.unit_test_dir, '109 new subproject on reconfigure') self.init(testdir) self.build() @@ -267,7 +267,7 @@ def test_setup_loaded_modules(self): thing to do as new features are added, but keeping track of them is good. ''' - testdir = os.path.join(self.unit_test_dir, '116 empty project') + testdir = os.path.join(self.unit_test_dir, '117 empty project') self.init(testdir) self._run(self.meson_command + ['--internal', 'regenerate', '--profile-self', testdir, self.builddir]) @@ -282,7 +282,7 @@ def test_setup_loaded_modules(self): def test_meson_package_cache_dir(self): # Copy testdir into temporary directory to not pollute meson source tree. - testdir = os.path.join(self.unit_test_dir, '118 meson package cache dir') + testdir = os.path.join(self.unit_test_dir, '119 meson package cache dir') srcdir = os.path.join(self.builddir, 'srctree') shutil.copytree(testdir, srcdir) builddir = os.path.join(srcdir, '_build') @@ -291,7 +291,7 @@ def test_meson_package_cache_dir(self): def test_cmake_openssl_not_found_bug(self): """Issue #12098""" - testdir = os.path.join(self.unit_test_dir, '119 openssl cmake bug') + testdir = os.path.join(self.unit_test_dir, '120 openssl cmake bug') self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini')) out = self.init(testdir, allow_fail=True) self.assertNotIn('Unhandled python exception', out) @@ -401,7 +401,7 @@ def test_error_configuring_subdir(self): self.assertIn(f'Did you mean to run meson from the directory: "{testdir}"?', out) def test_reconfigure_base_options(self): - testdir = os.path.join(self.unit_test_dir, '122 reconfigure base options') + testdir = os.path.join(self.unit_test_dir, '123 reconfigure base options') out = self.init(testdir, extra_args=['-Db_ndebug=true']) self.assertIn('\nMessage: b_ndebug: true\n', out) self.assertIn('\nMessage: c_std: c89\n', out) @@ -511,7 +511,7 @@ def test_configure_new_option_subproject(self) -> None: self.assertEqual(self.getconf('subproject:new_option'), True) def test_mtest_rebuild_deps(self): - testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') + testdir = os.path.join(self.unit_test_dir, '107 underspecified mtest') self.init(testdir) with self.assertRaises(subprocess.CalledProcessError): diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 7fad513f5271..bfb270f04a7a 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -391,7 +391,7 @@ def test_target_remove_extra_files(self): self.assertDictEqual(out, expected) def test_raw_printer_is_idempotent(self): - test_path = Path(self.unit_test_dir, '120 rewrite') + test_path = Path(self.unit_test_dir, '121 rewrite') meson_build_file = test_path / 'meson.build' # original_contents = meson_build_file.read_bytes() original_contents = meson_build_file.read_text(encoding='utf-8') diff --git a/unittests/windowstests.py b/unittests/windowstests.py index 8448ab1649cc..f602d5f2ef83 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -185,7 +185,7 @@ def test_genvslite(self): if self.backend is not Backend.ninja: raise SkipTest('Test only applies when using the Ninja backend') - testdir = os.path.join(self.unit_test_dir, '117 genvslite') + testdir = os.path.join(self.unit_test_dir, '118 genvslite') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) From a86476c57cfee0fa839d69314d7b2fa76120635b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 9 Jan 2025 16:55:00 +0200 Subject: [PATCH 174/624] Bump version number for rc1. --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index 41917a97db30..01b9abd8901e 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "October 2024" "meson 1.6.0" "User Commands" +.TH MESON "1" "January 2025" "meson 1.7.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index c3e435271952..43b8b5b3c1e9 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -74,7 +74,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.6.99' +version = '1.7.0.rc1' # 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 From 6b99eeb2c99d4af4be2562b25507541bfd842692 Mon Sep 17 00:00:00 2001 From: Kyle Johnson Date: Sat, 11 Jan 2025 17:10:22 -0600 Subject: [PATCH 175/624] docs: add Git to list of projects using meson (#14117) * docs: add git to list of projects using meson git 2.48 introduced support for the Meson build system: https://github.com/git/git/commit/904339edbd80ec5676616af6e072b41804c1c8eb https://github.blog/open-source/git/highlights-from-git-2-48/ * fix capitalization of Git; use Linus's initial revision's description --- docs/markdown/Users.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 6e113f1b59c8..81d084527730 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -53,6 +53,7 @@ topic](https://github.com/topics/meson). - [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 - [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 From dfb449d099d6f3066ca646af197bc7af85059a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Sat, 16 Nov 2024 13:43:48 +0100 Subject: [PATCH 176/624] vulkan system dep: determine version on cross builds Currently, the vulkan system dep detects its vulkan version by building and running: int main() { printf("%i.%i.%i", VK_VERSION_MAJOR(VK_HEADER_VERSION_COMPLETE), VK_VERSION_MINOR(VK_HEADER_VERSION_COMPLETE), VK_VERSION_PATCH(VK_HEADER_VERSION_COMPLETE)); return 0; } this causes cross builds that do not have the possibility of running on the build machine to evaluate the vulkan dependency with an 'Unknown' version. Instead of evaluating beforementioned piece of C code, the new implementation will instead use cc.compute_int to evaluate the three preprocessor macros. This is relativly expensive for cross builds right now but further optimizations can be made. See #13910 for more details. --- mesonbuild/dependencies/ui.py | 46 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 7adac5e75723..fc44037f119b 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -12,7 +12,6 @@ from .. import mlog from .. import mesonlib -from ..compilers.compilers import CrossNoRunException from ..mesonlib import ( Popen_safe, extract_as_list, version_compare_many ) @@ -235,31 +234,28 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. self.link_args.append(lib) if self.is_found: - get_version = '''\ -#include -#include - -int main() { - printf("%i.%i.%i", VK_VERSION_MAJOR(VK_HEADER_VERSION_COMPLETE), - VK_VERSION_MINOR(VK_HEADER_VERSION_COMPLETE), - VK_VERSION_PATCH(VK_HEADER_VERSION_COMPLETE)); - return 0; -} -''' try: - run = self.clib_compiler.run(get_version, environment, extra_args=self.compile_args) - except CrossNoRunException: - run = None - if run and run.compiled and run.returncode == 0: - self.version = run.stdout - elif self.vulkan_sdk: - # fall back to heuristics: detect version number in path - # matches the default install path on Windows - match = re.search(rf'VulkanSDK{re.escape(os.path.sep)}([0-9]+(?:\.[0-9]+)+)', self.vulkan_sdk) - if match: - self.version = match.group(1) - else: - mlog.warning(f'Environment variable VULKAN_SDK={self.vulkan_sdk} is present, but Vulkan version could not be extracted.') + # VK_VERSION_* is deprecated and replaced by VK_API_VERSION_*. We'll continue to use the old one in + # order to support older Vulkan versions that don't have the new one yet, but we might have to update + # this code to also check VK_API_VERSION in the future if they decide to drop the old one at some point. + components = [str(self.clib_compiler.compute_int(f'VK_VERSION_{c}(VK_HEADER_VERSION_COMPLETE)', + low=0, high=None, guess=e, + prefix='#include ', + env=environment, + extra_args=None, + dependencies=None)) + # list containing vulkan version components and their expected value + for c, e in [('MAJOR', 1), ('MINOR', 3), ('PATCH', None)]] + self.version = '.'.join(components) + except mesonlib.EnvironmentException: + if self.vulkan_sdk: + # fall back to heuristics: detect version number in path + # matches the default install path on Windows + match = re.search(rf'VulkanSDK{re.escape(os.path.sep)}([0-9]+(?:\.[0-9]+)+)', self.vulkan_sdk) + if match: + self.version = match.group(1) + else: + mlog.warning(f'Environment variable VULKAN_SDK={self.vulkan_sdk} is present, but Vulkan version could not be extracted.') packages['gl'] = gl_factory = DependencyFactory( 'gl', From 05643006a7ee8a457456f8f0a2cbf46c395ecc25 Mon Sep 17 00:00:00 2001 From: Nikolai Vavilov Date: Wed, 15 Jan 2025 00:38:39 +0200 Subject: [PATCH 177/624] Fix minimum required Apple clang version for C++26 --- mesonbuild/compilers/cpp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 2acc02ffe788..d2eca897b6ae 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -341,9 +341,7 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(AppleCompilerMixin, ClangCPPCompiler): _CPP23_VERSION = '>=13.0.0' - # TODO: We don't know which XCode version will include LLVM 17 yet, so - # use something absurd. - _CPP26_VERSION = '>=99.0.0' + _CPP26_VERSION = '>=16.0.0' class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): From 386fee2305d172ac127b2d055f7557b0af0cbb8c Mon Sep 17 00:00:00 2001 From: meator Date: Fri, 17 Jan 2025 19:57:53 +0100 Subject: [PATCH 178/624] zsh: Add missing flags to 'meson install' Also start flag description with a lowercase letter. --- data/shell-completions/zsh/_meson | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson index f64dfd4f3d44..661b06a4df2b 100644 --- a/data/shell-completions/zsh/_meson +++ b/data/shell-completions/zsh/_meson @@ -215,9 +215,14 @@ local -a meson_commands=( local curcontext="$curcontext" local -a specs=( "$__meson_cd" - '--no-rebuild[Do not rebuild before installing]' - '--only-changed[Do not overwrite files that are older than the copied file]' - '(--quiet -q)'{'--quiet','-q'}'[Do not print every file that was installed]' + '--no-rebuild[do not rebuild before installing]' + '--only-changed[do not overwrite files that are older than the copied file]' + '(--quiet -q)'{'--quiet','-q'}'[do not print every file that was installed]' + '--destdir[set or override DESTDIR environment]: :_directories' + '(--dry-run -d)'{'--dry-run','-d'}'[do not actually install, only print logs]' + '--skip-subprojects[do not install files from given subprojects]: : ' + '--tags[install only targets having one of the given tags]: :_values -s , tag devel runtime python-runtime man doc i18n typelib bin bin-devel tests systemtap' + '--strip[strip targets even if strip option was not set during configure]' ) _arguments \ '(: -)'{'--help','-h'}'[show a help message and quit]' \ From c45f80ac2527e2f739e934b6b7e40c3c4f178575 Mon Sep 17 00:00:00 2001 From: meator Date: Fri, 17 Jan 2025 20:21:04 +0100 Subject: [PATCH 179/624] zsh: Fix 'meson init --language' completion This commit fixes invalid syntax, which leads to (eval):1: number expected _arguments:465: command not found: _ when trying to complete 'meson init --language='. --- data/shell-completions/zsh/_meson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson index 661b06a4df2b..6507b6819b4d 100644 --- a/data/shell-completions/zsh/_meson +++ b/data/shell-completions/zsh/_meson @@ -256,7 +256,7 @@ _arguments \ '(-n --name)'{'-n','--name'}'=[the name of the project (defaults to directory name)]' '(-e --executable)'{'-e','--executable'}'=[the name of the executable target to create (defaults to project name)]' '(-d --deps)'{'-d','--deps'}'=[comma separated list of dependencies]' - '(-l --language)'{'-l','--language'}'=[comma separated list of languages (autodetected based on sources if unset)]:languages:_values , (c cpp cs cuda d fortran java objc objcpp rust)' + '(-l --language)'{'-l','--language'}'=[comma separated list of languages (autodetected based on sources if unset)]:languages:_values -s , language c cpp cs cuda d fortran java objc objcpp rust' '(-b --build)'{'-b','--build'}'[build the project immediately after generation]' '--builddir=[directory for building]:directory:_directories' '(-f --force)'{'-f','--force'}'[overwrite any existing files and directories]' From c07d0295d4816c77f172e8728ac35119bbecddc7 Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 14 Jan 2025 03:47:36 +0000 Subject: [PATCH 180/624] ci: gentoo: drop fakeroot I assume I only added this when copying Arch as a template initially, it certainly isn't needed now, as pointed out by Eli being suspicious of its presence... Signed-off-by: Sam James --- ci/ciimage/gentoo/install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/ciimage/gentoo/install.sh b/ci/ciimage/gentoo/install.sh index 7849ae3c257b..30b0299dac9e 100755 --- a/ci/ciimage/gentoo/install.sh +++ b/ci/ciimage/gentoo/install.sh @@ -44,7 +44,6 @@ pkgs_stable=( # misc app-admin/sudo app-text/doxygen - sys-apps/fakeroot sys-devel/bison sys-devel/gettext From 74bef61a26b6a910ecc826733126fbb1f123a87b Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 14 Jan 2025 04:25:41 +0000 Subject: [PATCH 181/624] ci: opensuse: drop gtk-sharp3-complete I can't find the equivalent of opensuse's "last rites" for package removal but gtk-sharp3-complete appears gone from Factory and I can't find any reference except for one mentioning it failing to build [0]. Drop it from the list of packages to install to fix the image build. [0] https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/message/TXFU2U5N3IMN3QA7VRDFLDC5M7NPKFVP/ Signed-off-by: Sam James --- ci/ciimage/opensuse/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 102bc121f83a..5ee1f6435f42 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -11,7 +11,7 @@ pkgs=( mono-core gtkmm3-devel gtest gmock protobuf-devel wxGTK3-3_2-devel gobject-introspection-devel itstool gtk3-devel java-17-openjdk-devel gtk-doc llvm-devel clang-devel libSDL2-devel graphviz-devel zlib-devel zlib-devel-static #hdf5-devel netcdf-devel libscalapack2-openmpi3-devel libscalapack2-gnu-openmpi3-hpc-devel openmpi3-devel - doxygen vulkan-devel vulkan-validationlayers openssh mercurial gtk-sharp3-complete gtk-sharp2-complete libpcap-devel libgpgme-devel + doxygen vulkan-devel vulkan-validationlayers openssh mercurial libpcap-devel libgpgme-devel libqt5-qtbase-devel libqt5-qttools-devel libqt5-linguist libqt5-qtbase-private-headers-devel 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 From 74aab8a42c479cdeeda9371dbd591a19d070c48e Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sat, 11 Jan 2025 17:13:14 +0100 Subject: [PATCH 182/624] docs: Add a test to validate URLs in markdown/Users.md Avoid piling up dead URLs. --- .github/workflows/website.yml | 2 +- docs/meson.build | 4 +++- docs/validatelinks.py | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 docs/validatelinks.py diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index fdb7d1400919..13c690207a91 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -45,7 +45,7 @@ jobs: - name: Install package run: | sudo apt-get -y install python3-pip ninja-build libjson-glib-dev - pip install hotdoc chevron strictyaml + pip install hotdoc chevron strictyaml aiohttp - uses: actions/cache/save@v4 with: diff --git a/docs/meson.build b/docs/meson.build index 3ad12b7fd71f..0ce884062e96 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,7 +1,7 @@ project('Meson documentation', version: '1.0') yaml_modname = get_option('unsafe_yaml') ? 'yaml' : 'strictyaml' -py = import('python').find_installation('python3', modules: [yaml_modname], required: false) +py = import('python').find_installation('python3', modules: [yaml_modname, 'aiohttp'], required: false) if not py.found() error(f'Cannot build documentation without yaml support') endif @@ -145,3 +145,5 @@ run_target('upload', ], depends: documentation, ) + +test('validate_links', find_program('./validatelinks.py'), args: meson.current_source_dir() / 'markdown' / 'Users.md') diff --git a/docs/validatelinks.py b/docs/validatelinks.py new file mode 100644 index 000000000000..69544ab0e98b --- /dev/null +++ b/docs/validatelinks.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 The Meson development team + +import sys +import re +import aiohttp +import asyncio + +LINK = re.compile(r'\[(?P[A-Za-z0-9 ]+)\]\((?P.*?)\)') + + +async def fetch(session, name, url, timeout): + try: + async with session.get(url, timeout=timeout) as r: + if not r.ok: + return (name, url, r.status) + except Exception as e: + return (name, url, str(e)) + + +async def main(filename): + with open(filename) as f: + text = f.read() + timeout = aiohttp.ClientTimeout(total=60) + async with aiohttp.ClientSession() as session: + tasks = [] + for link in LINK.finditer(text): + name, url = link.groups() + task = asyncio.ensure_future(fetch(session, name, url, timeout)) + tasks.append(task) + responses = asyncio.gather(*tasks) + errors = [r for r in await responses if r is not None] + for name, url, result in errors: + print(f'"{name}" {url} {result}') + if errors: + sys.exit(1) + + +if __name__ == '__main__': + asyncio.run(main(sys.argv[1])) From 38caf841af6a2de9a24f87adeef737075bc9e015 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sat, 11 Jan 2025 17:15:42 +0100 Subject: [PATCH 183/624] docs: Remove dead links in Users.md --- docs/markdown/Users.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 81d084527730..0a97bd5b3b29 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -125,7 +125,6 @@ format files - [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 - - [OcherBook](https://github.com/ccoffing/OcherBook), an open source book reader for Kobo devices - [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 @@ -141,7 +140,6 @@ format files - [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 - - [Paper Rock Scissors](https://github.com/michaelbrockus/paper_rock_scissors), a game with weapons themed at home paper rock scissors style - [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 From 3d751645b0766d3e66cec35579ad4c5a3df68308 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Jan 2025 10:22:49 +0100 Subject: [PATCH 184/624] clippy: skip "linker" blocks in target_sources Signed-off-by: Paolo Bonzini --- mesonbuild/scripts/clippy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/scripts/clippy.py b/mesonbuild/scripts/clippy.py index c4746e3fdb28..6d282e4f81e9 100644 --- a/mesonbuild/scripts/clippy.py +++ b/mesonbuild/scripts/clippy.py @@ -33,7 +33,7 @@ def warn_missing_clippy(self, machine: str) -> None: def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, None, int]]: for src_block in target['target_sources']: - if src_block['language'] == 'rust': + if 'compiler' in src_block and src_block['language'] == 'rust': clippy = getattr(self.tools, src_block['machine']) if not clippy: self.warn_missing_clippy(src_block['machine']) From 887367656f2cbe76df7d8b19f380904be8aa49e2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Jan 2025 10:22:49 +0100 Subject: [PATCH 185/624] ninjabackend: ensure structured_sources are populated before running clippy Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 70122c3794bc..cd35d2fcc300 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -503,6 +503,7 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter self.ninja_filename = 'build.ninja' self.fortran_deps: T.Dict[str, T.Dict[str, File]] = {} self.all_outputs: T.Set[str] = set() + self.all_structured_sources: T.Set[str] = set() self.introspection_data = {} self.created_llvm_ir_rule = PerMachine(False, False) self.rust_crates: T.Dict[str, RustCrate] = {} @@ -1951,7 +1952,6 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: 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) - orderdeps.extend(_ods) else: # The only way to get here is to have only files in the "root" # positional argument, which are all generated into the same @@ -1965,12 +1965,15 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: else: main_rust_file = os.path.join(g.get_subdir(), g.get_outputs()[0]) + _ods = [] for f in target.structured_sources.as_list(): if isinstance(f, File): - orderdeps.append(f.rel_to_builddir(self.build_to_src)) + _ods.append(f.rel_to_builddir(self.build_to_src)) else: - orderdeps.extend([os.path.join(self.build_to_src, f.subdir, s) - for s in f.get_outputs()]) + _ods.extend([os.path.join(self.build_to_src, f.subdir, s) + for s in f.get_outputs()]) + self.all_structured_sources.update(_ods) + orderdeps.extend(_ods) for i in target.get_sources(): if not rustc.can_compile(i): @@ -3715,6 +3718,7 @@ def generate_clippy(self) -> None: for crate in self.rust_crates.values(): if crate.crate_type in {'rlib', 'dylib', 'proc-macro'}: elem.add_dep(crate.target_name) + elem.add_dep(list(self.all_structured_sources)) self.add_build(elem) def generate_scanbuild(self) -> None: From 843895b860dbfdbc4d8669006509adadeb173cbc Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 8 Jan 2025 13:38:23 -0500 Subject: [PATCH 186/624] Fix broken both_libraries transitive dependencies #13837 broke both_lib transitive deps, because the `as_static` and `as_shared` functions return libraries that still contain references to the other lib type. --- mesonbuild/build.py | 31 ++++++++++++------- mesonbuild/dependencies/base.py | 4 +-- .../common/273 both libraries/meson.build | 29 +++++++++++++++++ 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5d35e0833943..c72857d2c1ae 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -6,6 +6,7 @@ from dataclasses import dataclass, field, InitVar from functools import lru_cache import abc +import copy import hashlib import itertools, pathlib import os @@ -1757,7 +1758,7 @@ def extract_targets_as_list(self, kwargs: T.Dict[str, T.Union[LibTypes, T.Sequen lib_list.append(lib) return lib_list - def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: """Base case used by BothLibraries""" return self @@ -2216,12 +2217,16 @@ def is_internal(self) -> bool: return not self.install def set_shared(self, shared_library: SharedLibrary) -> None: - self.both_lib = shared_library + self.both_lib = copy.copy(shared_library) + self.both_lib.both_lib = None - def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + result = self if lib_type == 'shared': - return self.both_lib or self - return self + result = self.both_lib or self + if recursive: + result.link_targets = [t.get(lib_type, True) for t in self.link_targets] + return result class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs @@ -2490,12 +2495,16 @@ def is_linkable_target(self): return True def set_static(self, static_library: StaticLibrary) -> None: - self.both_lib = static_library + self.both_lib = copy.copy(static_library) + self.both_lib.both_lib = None - def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: + result = self if lib_type == 'static': - return self.both_lib or self - return self + result = self.both_lib or self + if recursive: + result.link_targets = [t.get(lib_type, True) for t in self.link_targets] + return result # A shared library that is meant to be used with dlopen rather than linking # into something else. @@ -2542,7 +2551,7 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_libra def __repr__(self) -> str: return f'' - def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: if lib_type == 'static': return self.static if lib_type == 'shared': @@ -2616,7 +2625,7 @@ def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None: pass - def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 9c05419b9e09..af370955770e 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -371,14 +371,14 @@ def generate_link_whole_dependency(self) -> Dependency: def get_as_static(self, recursive: bool) -> InternalDependency: new_dep = copy.copy(self) - new_dep.libraries = [lib.get('static') for lib in self.libraries] + new_dep.libraries = [lib.get('static', recursive) for lib in self.libraries] if recursive: new_dep.ext_deps = [dep.get_as_static(True) for dep in self.ext_deps] return new_dep def get_as_shared(self, recursive: bool) -> InternalDependency: new_dep = copy.copy(self) - new_dep.libraries = [lib.get('shared') for lib in self.libraries] + new_dep.libraries = [lib.get('shared', recursive) for lib in self.libraries] if recursive: new_dep.ext_deps = [dep.get_as_shared(True) for dep in self.ext_deps] return new_dep diff --git a/test cases/common/273 both libraries/meson.build b/test cases/common/273 both libraries/meson.build index 00da1c8e6cf5..789f4205e82e 100644 --- a/test cases/common/273 both libraries/meson.build +++ b/test cases/common/273 both libraries/meson.build @@ -111,3 +111,32 @@ if get_option('default_library') == 'both' and get_option('default_both_librarie ) test('test shared', main_shared) endif + +# Test case for https://github.com/mesonbuild/meson/pull/14098 +if get_option('default_library') == 'shared' + + if get_option('use_dep') + lib_deps = [with_bl_dep.as_static(recursive: true)] + lib_links = [] + else + lib_deps = [] + lib_links = [with_bl.get_static_lib()] + endif + + lib_with_static_dep = library( + 'lib_with_static_dep', + files('src/library.c'), + c_shared_args: ['-DEXPORT'], + link_with: lib_links, + dependencies: lib_deps, + ) + + main_with_static_dep = executable( + 'main_with_static_dep', + files('src/main.c'), + c_args: [f'-DEXPECTED=1'], + link_with: lib_with_static_dep, + ) + test('test static dep', main_with_static_dep) + +endif From 82fedf04033305e2b28db1eea2346018c237d167 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Fri, 11 Oct 2024 17:41:56 +0200 Subject: [PATCH 187/624] interpreterbase: Add disabler exception for `get_variable` method Add an exception to the disabler check to allow objects with a `get_variable` method to not always pick a disabler if their arguments contain one. This mimics the behaviour already in place for calls to function, which has a set of excepted functions. Closes #13717 Signed-off-by: Andrew McNulty --- mesonbuild/interpreterbase/interpreterbase.py | 2 +- test cases/common/158 disabler/meson.build | 5 +++++ test cases/common/158 disabler/subprojects/bar/meson.build | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 test cases/common/158 disabler/subprojects/bar/meson.build diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 5849e9ca9c1d..b408b4b1262a 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -542,7 +542,7 @@ def method_call(self, node: mparser.MethodNode) -> T.Optional[InterpreterObject] method_name = node.name.value (h_args, h_kwargs) = self.reduce_arguments(node.args) (args, kwargs) = self._unholder_args(h_args, h_kwargs) - if is_disabled(args, kwargs): + if is_disabled(args, kwargs) and method_name != 'get_variable': return Disabler() if not isinstance(obj, InterpreterObject): raise InvalidArguments(f'{object_display_name} is not callable.') diff --git a/test cases/common/158 disabler/meson.build b/test cases/common/158 disabler/meson.build index 65ca5fdf5457..3ee26dfc94e8 100644 --- a/test cases/common/158 disabler/meson.build +++ b/test cases/common/158 disabler/meson.build @@ -151,3 +151,8 @@ foreach k, i : {'a': true, 'b': disabler(), 'c': true} endforeach assert(loops == 3, 'Disabler in foreach dict') assert(disablers == 1, 'Disabler in foreach dict') + +# https://github.com/mesonbuild/meson/issues/13717 +bar_subproject = subproject('bar') +bar_dep = bar_subproject.get_variable('bar_dep', disabler()) +assert(not is_disabler(bar_dep)) diff --git a/test cases/common/158 disabler/subprojects/bar/meson.build b/test cases/common/158 disabler/subprojects/bar/meson.build new file mode 100644 index 000000000000..deed3ce57b18 --- /dev/null +++ b/test cases/common/158 disabler/subprojects/bar/meson.build @@ -0,0 +1,2 @@ +project('bar') +bar_dep = declare_dependency() From 91eb6c2289335ade41dd65ad1373c68b9d4ffb14 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 19 Jan 2025 20:43:41 +0200 Subject: [PATCH 188/624] Bump version number for rc2. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 43b8b5b3c1e9..f7d48453aa82 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -74,7 +74,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.7.0.rc1' +version = '1.7.0.rc2' # 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 From 4d4839c6ee73b89f8a6ed394fbfdf498a203d712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Tue, 19 Nov 2024 12:49:17 +0100 Subject: [PATCH 189/624] compilers/clike: Speedup cross_compute_int Expand the expression passed into cross_compute_int using the preprocessor first and then try to evaluate the expanded expression using the host machine compiler and test if the result is valid. Co-authored-by: Charles Brunet --- mesonbuild/compilers/mixins/clike.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index a75cd2a81bf0..19a6bb4875af 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -476,6 +476,21 @@ def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optio if self._compile_int(f'{expression} == {guess}', prefix, env, 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: + try: + expanded, _ = self.get_define(expression, prefix, env, extra_args, dependencies, False) + evaluate_expanded = f''' + #include + #include + int main(void) {{ int expression = {expanded}; printf("%d", expression); return 0; }}''' + run = env.coredata.compilers.build[self.language].run(evaluate_expanded, env) + if run and run.compiled and run.returncode == 0: + if self._compile_int(f'{expression} == {run.stdout}', prefix, env, extra_args, dependencies): + return int(run.stdout) + except mesonlib.EnvironmentException: + pass + # If no bounds are given, compute them in the limit of int32 maxint = 0x7fffffff minint = -0x80000000 From 73c52ddcd901e03092a70d04a548b5a74c6781f4 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 20 Jan 2025 09:58:06 -0500 Subject: [PATCH 190/624] docs: do not require aiohttp to build It pulls in 22 (!!!) dependencies and is only needed in CI for a trivial lint of the HTML docs. This is a big problem for people that simply want to compile the manpage. Let the tests fail at test time if this dependency isn't available. Fixes: 74aab8a42c479cdeeda9371dbd591a19d070c48e --- docs/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/meson.build b/docs/meson.build index 0ce884062e96..c476b59dbaa6 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,7 +1,7 @@ project('Meson documentation', version: '1.0') yaml_modname = get_option('unsafe_yaml') ? 'yaml' : 'strictyaml' -py = import('python').find_installation('python3', modules: [yaml_modname, 'aiohttp'], required: false) +py = import('python').find_installation('python3', modules: [yaml_modname], required: false) if not py.found() error(f'Cannot build documentation without yaml support') endif From 373d1fe668fa59169ec46dda236c72744c62511f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 18 Jan 2025 13:35:19 -0500 Subject: [PATCH 191/624] Remove GCC's -Wunsuffixed-float-constants from warnings This inclusion was a misunderstanding on my part: this warning isn't generally applicable to standard C (it prevents using double literals whatsoever since C doesn't have a suffix for them), but exists to support a GNU C extension. It also has no counterpart in clang. So, remove it, since warning_level=everything doesn't include such things. --- mesonbuild/compilers/mixins/gnu.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 66b01ef512e1..b974474fa953 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -203,6 +203,7 @@ # -Wdeclaration-after-statement # -Wtraditional # -Wtraditional-conversion +# -Wunsuffixed-float-constants gnu_c_warning_args: T.Dict[str, T.List[str]] = { "0.0.0": [ "-Wbad-function-cast", @@ -217,9 +218,6 @@ "4.1.0": [ "-Wc++-compat", ], - "4.5.0": [ - "-Wunsuffixed-float-constants", - ], } # GCC warnings for C++ From 147a089eb54dabc0ae0cf9d81674665de3d5f0a1 Mon Sep 17 00:00:00 2001 From: Kevin Meagher <11620178+kjmeagher@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:05:37 -0600 Subject: [PATCH 192/624] Alphabetize the Docs on Dependencies Persumably this list of was supposed to be alphabetized, but a few of the items were out of order. This confused me because I was looking for numpy, got the the Os and thought it wasn't there. --- docs/markdown/Dependencies.md | 84 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 2e255b209822..f7bf9017f614 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -427,6 +427,34 @@ foreach h : check_headers endforeach ``` +## DIA SDK + +*(added 1.6.0)* + +Microsoft's Debug Interface Access SDK (DIA SDK) is available only on Windows, +when using msvc, clang-cl or clang compiler from Microsoft Visual Studio. + +The DIA SDK runtime is not statically linked to target. The default usage +method requires the runtime DLL (msdiaXXX.dll) to be manually registered in the +OS with `regsrv32.exe` command, so it can be loaded using `CoCreateInstance` +Windows function. + +Alternatively, you can use meson to copy the DIA runtime DLL to your build +directory, and load it dynamically using `NoRegCoCreate` function provided by +the DIA SDK. To facilitate this, you can read DLL path from dependency's +variable 'dll' and use fs module to copy it. Example: + +```meson +dia = dependency('diasdk', required: true) +fs = import('fs') +fs.copyfile(dia.get_variable('dll')) + +conf = configuration_data() +conf.set('msdia_dll_name', fs.name(dia_dll_name)) +``` + +Only the major version is available (eg. version is `14` for msdia140.dll). + ## dl (libdl) *(added 0.62.0)* @@ -448,18 +476,18 @@ providing them instead. GCC will use OpenCoarrays if present to implement coarrays, while Intel and NAG use internal coarray support. -## GPGME - -*(added 0.51.0)* - -`method` may be `auto`, `config-tool` or `pkg-config`. - ## GL This finds the OpenGL library in a way appropriate to the platform. `method` may be `auto`, `pkg-config` or `system`. +## GPGME + +*(added 0.51.0)* + +`method` may be `auto`, `config-tool` or `pkg-config`. + ## GTest and GMock GTest and GMock come as sources that must be compiled as part of your @@ -649,6 +677,14 @@ language-specific, you must specify the requested language using the Meson uses pkg-config to find NetCDF. +## NumPy + +*(added 1.4.0)* + +`method` may be `auto`, `pkg-config`, or `config-tool`. +`dependency('numpy')` supports regular use of the NumPy C API. +Use of `numpy.f2py` for binding Fortran code isn't yet supported. + ## ObjFW *(added 1.5.0)* @@ -694,14 +730,6 @@ The `language` keyword may used. `method` may be `auto`, `pkg-config`, `system` or `cmake`. -## NumPy - -*(added 1.4.0)* - -`method` may be `auto`, `pkg-config`, or `config-tool`. -`dependency('numpy')` supports regular use of the NumPy C API. -Use of `numpy.f2py` for binding Fortran code isn't yet supported. - ## pcap *(added 0.42.0)* @@ -859,34 +887,6 @@ version. *New in 0.54.0* the `system` method. -## DIA SDK - -*(added 1.6.0)* - -Microsoft's Debug Interface Access SDK (DIA SDK) is available only on Windows, -when using msvc, clang-cl or clang compiler from Microsoft Visual Studio. - -The DIA SDK runtime is not statically linked to target. The default usage -method requires the runtime DLL (msdiaXXX.dll) to be manually registered in the -OS with `regsrv32.exe` command, so it can be loaded using `CoCreateInstance` -Windows function. - -Alternatively, you can use meson to copy the DIA runtime DLL to your build -directory, and load it dynamically using `NoRegCoCreate` function provided by -the DIA SDK. To facilitate this, you can read DLL path from dependency's -variable 'dll' and use fs module to copy it. Example: - -```meson -dia = dependency('diasdk', required: true) -fs = import('fs') -fs.copyfile(dia.get_variable('dll')) - -conf = configuration_data() -conf.set('msdia_dll_name', fs.name(dia_dll_name)) -``` - -Only the major version is available (eg. version is `14` for msdia140.dll). -
1: They may appear to be case-insensitive, if the underlying file system happens to be case-insensitive. From 9a9abaf795ef9c701d74105a05b190be658dfd3f Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 20 Jan 2025 16:43:44 -0500 Subject: [PATCH 193/624] linkers: fix rpath padding calculation for non-ascii rpaths are calculated in bytes, and that's also how depfixer processes them. We need to ensure that the ascii padding we use (bytes == unicode) is the correct offset for the install rpath - build rpath (both specified in unicode which then gets converted to bytes). In the event of a unicode install_rpath, we can get into a situation where the install rpath is longer than the padding, since we assumed that the install_rpath was shorter than it actually is -- because we counted the length in characters instead of the length in bytes. This then broke installation for people who e.g. install into a prefix inside their home directory, when their home directory contains multibyte unicode characters. Bug: https://gitlab.gnome.org/GNOME/gnome-builder/-/issues/2280 --- mesonbuild/linkers/linkers.py | 12 ++++++++---- test cases/unit/10 build_rpath/meson.build | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 705e4282138b..176fb3348204 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -720,8 +720,10 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, # 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) - if len(paths) < len(install_rpath): - padding = 'X' * (len(install_rpath) - len(paths)) + paths_length = len(paths.encode('utf-8')) + install_rpath_length = len(install_rpath.encode('utf-8')) + if paths_length < install_rpath_length: + padding = 'X' * (install_rpath_length - paths_length) if not paths: paths = padding else: @@ -1488,8 +1490,10 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, # 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) - if len(paths) < len(install_rpath): - padding = 'X' * (len(install_rpath) - len(paths)) + paths_length = len(paths.encode('utf-8')) + install_rpath_length = len(install_rpath.encode('utf-8')) + if paths_length < install_rpath_length: + padding = 'X' * (install_rpath_length - paths_length) if not paths: paths = padding else: diff --git a/test cases/unit/10 build_rpath/meson.build b/test cases/unit/10 build_rpath/meson.build index c0bc3bd2703f..f53c0f8bd685 100644 --- a/test cases/unit/10 build_rpath/meson.build +++ b/test cases/unit/10 build_rpath/meson.build @@ -8,6 +8,12 @@ executable('prog', 'prog.c', install : true, ) +executable('multibyte_rpath', 'prog.c', + link_with: l, + install_rpath: get_option('prefix') / '⢖⢖⢖⢖⢖', + install: true + ) + executable('progcxx', 'prog.cc', link_with : l, build_rpath : '/foo/bar', From 900a9182872eacd2aa3c9fa6449ea48b18603cd3 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 20 Jan 2025 15:50:44 -0800 Subject: [PATCH 194/624] Fix capitalization of "PyPI" in README.md See https://pypi.org for confirmation of the capitalization. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f3a2657b7d05..3d01b3ff3769 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Latest Meson version supporting previous Python versions: #### Installing from source -Meson is available on [PyPi](https://pypi.python.org/pypi/meson), so +Meson is available on [PyPI](https://pypi.python.org/pypi/meson), so it can be installed with `pip3 install meson`. The exact command to type to install with `pip` can vary between systems, be sure to use the Python 3 version of `pip`. @@ -35,7 +35,7 @@ python3 -m pip install meson For builds using Ninja, Ninja can be downloaded directly from Ninja [GitHub release page](https://github.com/ninja-build/ninja/releases) -or via [PyPi](https://pypi.python.org/pypi/ninja) +or via [PyPI](https://pypi.python.org/pypi/ninja) ```console python3 -m pip install ninja From cef9c33bba566b576d3d5b04d88bb3ebadb7d1e8 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 19 Jan 2025 20:20:01 +0200 Subject: [PATCH 195/624] Revert "interpreterbase: Add disabler exception for `get_variable` method" This reverts commit 82fedf04033305e2b28db1eea2346018c237d167. --- mesonbuild/interpreterbase/interpreterbase.py | 2 +- test cases/common/158 disabler/meson.build | 5 ----- test cases/common/158 disabler/subprojects/bar/meson.build | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 test cases/common/158 disabler/subprojects/bar/meson.build diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index b408b4b1262a..5849e9ca9c1d 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -542,7 +542,7 @@ def method_call(self, node: mparser.MethodNode) -> T.Optional[InterpreterObject] method_name = node.name.value (h_args, h_kwargs) = self.reduce_arguments(node.args) (args, kwargs) = self._unholder_args(h_args, h_kwargs) - if is_disabled(args, kwargs) and method_name != 'get_variable': + if is_disabled(args, kwargs): return Disabler() if not isinstance(obj, InterpreterObject): raise InvalidArguments(f'{object_display_name} is not callable.') diff --git a/test cases/common/158 disabler/meson.build b/test cases/common/158 disabler/meson.build index 3ee26dfc94e8..65ca5fdf5457 100644 --- a/test cases/common/158 disabler/meson.build +++ b/test cases/common/158 disabler/meson.build @@ -151,8 +151,3 @@ foreach k, i : {'a': true, 'b': disabler(), 'c': true} endforeach assert(loops == 3, 'Disabler in foreach dict') assert(disablers == 1, 'Disabler in foreach dict') - -# https://github.com/mesonbuild/meson/issues/13717 -bar_subproject = subproject('bar') -bar_dep = bar_subproject.get_variable('bar_dep', disabler()) -assert(not is_disabler(bar_dep)) diff --git a/test cases/common/158 disabler/subprojects/bar/meson.build b/test cases/common/158 disabler/subprojects/bar/meson.build deleted file mode 100644 index deed3ce57b18..000000000000 --- a/test cases/common/158 disabler/subprojects/bar/meson.build +++ /dev/null @@ -1,2 +0,0 @@ -project('bar') -bar_dep = declare_dependency() From 0b9baf573bf2b1619d7822f67965a35391172625 Mon Sep 17 00:00:00 2001 From: LaserEyess Date: Tue, 21 Jan 2025 18:11:11 -0500 Subject: [PATCH 196/624] docs: clarify use cases for declare_dependency() --- docs/yaml/functions/declare_dependency.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/yaml/functions/declare_dependency.yaml b/docs/yaml/functions/declare_dependency.yaml index 9d085fdf8251..848082d1beb4 100644 --- a/docs/yaml/functions/declare_dependency.yaml +++ b/docs/yaml/functions/declare_dependency.yaml @@ -3,10 +3,12 @@ returns: dep description: | This function returns a [[@dep]] object that behaves like the return value of [[dependency]] but is - internal to the current build. The main use case for this is in + internal to the current build. One use case for this is in subprojects. This allows a subproject to easily specify how it should be used. This makes it interchangeable with the same dependency that - is provided externally by the system. + is provided externally by the system. Another common use case is to + declare project targets as dependencies so they may be used as + dependencies of other build targets. kwargs: compile_args: @@ -49,7 +51,7 @@ kwargs: description: | extra files to add to targets. mostly used for IDE integration. - + version: type: str description: | From c70442eea71b37d6833dc32668922cc5d3a2a332 Mon Sep 17 00:00:00 2001 From: gerioldman Date: Tue, 21 Jan 2025 23:59:31 +0100 Subject: [PATCH 197/624] docs: Add missing release snippet for the TASKING compiler support --- docs/markdown/snippets/tasking_compiler.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/markdown/snippets/tasking_compiler.md diff --git a/docs/markdown/snippets/tasking_compiler.md b/docs/markdown/snippets/tasking_compiler.md new file mode 100644 index 000000000000..bbe29cc4b64b --- /dev/null +++ b/docs/markdown/snippets/tasking_compiler.md @@ -0,0 +1,3 @@ +## Support TASKING VX-Toolset + +Meson now supports the TASKING VX-Toolset compiler family for the Tricore cpu family. \ No newline at end of file From a5d48cc0deeb90be976fc7044337ce9f5f33340e Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 26 Jan 2025 17:56:14 +0200 Subject: [PATCH 198/624] Ask for testing help for the option refactor branch. --- docs/markdown/snippets/optioncaution.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 docs/markdown/snippets/optioncaution.md diff --git a/docs/markdown/snippets/optioncaution.md b/docs/markdown/snippets/optioncaution.md new file mode 100644 index 000000000000..c2d9a877654e --- /dev/null +++ b/docs/markdown/snippets/optioncaution.md @@ -0,0 +1,16 @@ +## Call for testing for next release + +At the beginning of next cycle we aim to merge the [option refactor +branch](https://github.com/mesonbuild/meson/pull/13441). This is a +_huge_ change that will touch pretty much all code. + +The main change it brings is that you can override any builtin option +value for any subproject (even the top one) entirely from the command +line. This means that you can, for example, enable optimizations on +all subprojects but not on the top level project. + +We have done extensive testing and all our tests currently +pass. However it is expected that this will break some workflows. So +please test the branch when it lands and report issues. We want to fix +all regressions as soon as possible, preferably far before the next rc +release. From b1a5ae6635115cefcf12aca432929c95bce40a4a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 26 Jan 2025 20:02:34 +0200 Subject: [PATCH 199/624] Created release notes for 1.7.0. --- docs/markdown/Release-notes-for-1.7.0.md | 155 ++++++++++++++++++ docs/markdown/snippets/atomic-dependency.md | 9 - docs/markdown/snippets/cargo_cap_lints.md | 8 - docs/markdown/snippets/cargo_features.md | 14 -- docs/markdown/snippets/clippy.md | 8 - .../snippets/external_project_devenv.md | 7 - ...xed_sizeof_and_find_library_for_fortran.md | 8 - docs/markdown/snippets/format_from_stdin.md | 4 - docs/markdown/snippets/introspect_machine.md | 5 - docs/markdown/snippets/linearasm_features.md | 3 - docs/markdown/snippets/num-processes.md | 8 - docs/markdown/snippets/optioncaution.md | 16 -- docs/markdown/snippets/rust-2024.md | 5 - docs/markdown/snippets/tasking_compiler.md | 3 - docs/markdown/snippets/test_dependencies.md | 29 ---- docs/markdown/snippets/vcs_tag.md | 4 - docs/sitemap.txt | 1 + 17 files changed, 156 insertions(+), 131 deletions(-) create mode 100644 docs/markdown/Release-notes-for-1.7.0.md delete mode 100644 docs/markdown/snippets/atomic-dependency.md delete mode 100644 docs/markdown/snippets/cargo_cap_lints.md delete mode 100644 docs/markdown/snippets/cargo_features.md delete mode 100644 docs/markdown/snippets/clippy.md delete mode 100644 docs/markdown/snippets/external_project_devenv.md delete mode 100644 docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md delete mode 100644 docs/markdown/snippets/format_from_stdin.md delete mode 100644 docs/markdown/snippets/introspect_machine.md delete mode 100644 docs/markdown/snippets/linearasm_features.md delete mode 100644 docs/markdown/snippets/num-processes.md delete mode 100644 docs/markdown/snippets/optioncaution.md delete mode 100644 docs/markdown/snippets/rust-2024.md delete mode 100644 docs/markdown/snippets/tasking_compiler.md delete mode 100644 docs/markdown/snippets/test_dependencies.md delete mode 100644 docs/markdown/snippets/vcs_tag.md diff --git a/docs/markdown/Release-notes-for-1.7.0.md b/docs/markdown/Release-notes-for-1.7.0.md new file mode 100644 index 000000000000..5024b32c20f1 --- /dev/null +++ b/docs/markdown/Release-notes-for-1.7.0.md @@ -0,0 +1,155 @@ +--- +title: Release 1.7.0 +short-description: Release notes for 1.7.0 +... + +# New features + +Meson 1.7.0 was released on 26 January 2025 + +## Call for testing for next release + +At the beginning of next cycle we aim to merge the [option refactor +branch](https://github.com/mesonbuild/meson/pull/13441). This is a +_huge_ change that will touch pretty much all code. + +The main change it brings is that you can override any builtin option +value for any subproject (even the top one) entirely from the command +line. This means that you can, for example, enable optimizations on +all subprojects but not on the top level project. + +We have done extensive testing and all our tests currently +pass. However it is expected that this will break some workflows. So +please test the branch when it lands and report issues. We want to fix +all regressions as soon as possible, preferably far before the next rc +release. + +## New custom dependency for atomic + +``` +dependency('atomic') +``` + +checks for the availability of the atomic operation library. First, it looks +for the atomic library. If that is not found, then it will try to use what is +provided by the libc. + +## `--cap-lints allow` used for Cargo subprojects + +Similar to Cargo itself, all downloaded Cargo subprojects automatically +add the `--cap-lints allow` compiler argument, thus hiding any warnings +from the compiler. + +Related to this, `warning_level=0` now translates into `--cap-lints allow` +for Rust targets instead of `-A warnings`. + +## Cargo features are resolved globally + +When configuring a Cargo dependency, Meson will now resolve its complete +dependency tree and feature set before generating the subproject AST. +This solves many cases of Cargo subprojects being configured with missing +features that the main project had to enable by hand using e.g. +`default_options: ['foo-rs:feature-default=true']`. + +Note that there could still be issues in the case there are multiple Cargo +entry points. That happens if the main Meson project makes multiple `dependency()` +calls for different Cargo crates that have common dependencies. + +Breaks: This change removes per feature Meson options that were previously +possible to set as shown above or from command line `-Dfoo-rs:feature-foo=true`. + +## Meson can run "clippy" on Rust projects + +Meson now defines a `clippy` target if the project uses the Rust programming +language. The target runs clippy on all Rust sources, using the `clippy-driver` +program from the same Rust toolchain as the `rustc` compiler. + +Using `clippy-driver` as the Rust compiler will now emit a warning, as it +is not meant to be a general-purpose compiler front-end. + +## Devenv support in external project module + +The [external project module](External-Project-module.md) now setups `PATH` and +`LD_LIBRARY_PATH` to be able to run programs. + +`@BINDIR@` is now substitued in arguments and `'--bindir=@PREFIX@/@BINDIR@'` +default argument have been added. + +## Fixed `sizeof` and `find_library` methods for Fortran compilers + +The implementation of the `.sizeof()` method has been fixed for Fortran +compilers (it was previously broken since it would try to compile a C code +snippet). Note that this functionality requires Fortran 2008 support. + +Incidentally this also fixes the `.find_library()` method for Fortran compilers +when the `prefer_static` built-in option is set to true. + +## format command now accept stdin argument + +You can now use `-` argument for `meson format` to read input from stdin +instead of reading it from a file. + +## "machine" entry in target introspection data + +The JSON data returned by `meson introspect --targets` now has a `machine` +entry in each `target_sources` block. The new entry can be one of `build` +or `host` for compiler-built targets, or absent for `custom_target` targets. + +## Add new language Linear Asm + +TI C6000 compiler supports a dialect of TI asm, so we add a new language for it. + +## Control the number of child processes with an environment variable + +Previously, `meson test` checked the `MESON_TESTTHREADS` variable to control +the amount of parallel jobs to run; this was useful when `meson test` is +invoked through `ninja test` for example. With this version, a new variable +`MESON_NUM_PROCESSES` is supported with a broader scope: in addition to +`meson test`, it is also used by the `external_project` module and by +Ninja targets that invoke `clang-tidy`, `clang-format` and `clippy`. + +## Support for Rust 2024 + +Meson can now request the compiler to use the 2024 edition of Rust. Use +`rust_std=2024` to activate it. Rust 2024 requires the 1.85.0 version +(or newer) of the compiler. + +## Support TASKING VX-Toolset + +Meson now supports the TASKING VX-Toolset compiler family for the Tricore cpu family. + +## Test targets no longer built by default + +`meson test` and the `ninja all` rule have been reworked to no longer force +unnecessary rebuilds. + +`meson test` was invoking `ninja all` due to a bug if the chosen set of tests +had no build dependencies. The behavior is now the same as when tests do have +build dependencies, i.e. to only build the actual set of targets that are used +by the test. This change could cause failures when upgrading to Meson 1.7.0, if +the dependencies are not specified correctly in meson.build. Using `ninja test` +has always been guaranteed to "do the right thing" and rebuild `all` as well; +this continues to work. + +`ninja all` does not rebuild all tests anymore; it should be noted that this +change means test programs are no longer guaranteed to have been built, +depending on whether those test programs were *also* defined to build by +default / marked as installable. This avoids building test-only binaries as +part of installing the project (`ninja && ninja install`), which is unnecessary +and has no use case. + +Some users might have been relying on the "all" target building test +dependencies in combination with `meson test --no-rebuild` in order to skip +calling out to ninja when running tests. This might break with this change +because, when given `--no-rebuild`, Meson provides no guarantee that test +dependencies are present and up to date. The recommended workflow is to use +either `ninja test` or `ninja && meson test` but, if you wish to build test +programs and dependencies in a separate stage, you can use for example `ninja +all meson-test-prereq meson-benchmark-prereq` before `meson test --no-rebuild`. +These prereq targets have been available since meson 0.63.0. + +## Install vcs_tag() output + +[[vcs_tag]] now has `install`, `install_dir`, `install_tag` and `install_mode` +keyword arguments to install the generated file. + diff --git a/docs/markdown/snippets/atomic-dependency.md b/docs/markdown/snippets/atomic-dependency.md deleted file mode 100644 index 2eef5e0b1cb0..000000000000 --- a/docs/markdown/snippets/atomic-dependency.md +++ /dev/null @@ -1,9 +0,0 @@ -## New custom dependency for atomic - -``` -dependency('atomic') -``` - -checks for the availability of the atomic operation library. First, it looks -for the atomic library. If that is not found, then it will try to use what is -provided by the libc. diff --git a/docs/markdown/snippets/cargo_cap_lints.md b/docs/markdown/snippets/cargo_cap_lints.md deleted file mode 100644 index 9623ae157209..000000000000 --- a/docs/markdown/snippets/cargo_cap_lints.md +++ /dev/null @@ -1,8 +0,0 @@ -## `--cap-lints allow` used for Cargo subprojects - -Similar to Cargo itself, all downloaded Cargo subprojects automatically -add the `--cap-lints allow` compiler argument, thus hiding any warnings -from the compiler. - -Related to this, `warning_level=0` now translates into `--cap-lints allow` -for Rust targets instead of `-A warnings`. diff --git a/docs/markdown/snippets/cargo_features.md b/docs/markdown/snippets/cargo_features.md deleted file mode 100644 index 26f1bff95d9d..000000000000 --- a/docs/markdown/snippets/cargo_features.md +++ /dev/null @@ -1,14 +0,0 @@ -## Cargo features are resolved globally - -When configuring a Cargo dependency, Meson will now resolve its complete -dependency tree and feature set before generating the subproject AST. -This solves many cases of Cargo subprojects being configured with missing -features that the main project had to enable by hand using e.g. -`default_options: ['foo-rs:feature-default=true']`. - -Note that there could still be issues in the case there are multiple Cargo -entry points. That happens if the main Meson project makes multiple `dependency()` -calls for different Cargo crates that have common dependencies. - -Breaks: This change removes per feature Meson options that were previously -possible to set as shown above or from command line `-Dfoo-rs:feature-foo=true`. diff --git a/docs/markdown/snippets/clippy.md b/docs/markdown/snippets/clippy.md deleted file mode 100644 index 47d02083a0ab..000000000000 --- a/docs/markdown/snippets/clippy.md +++ /dev/null @@ -1,8 +0,0 @@ -## Meson can run "clippy" on Rust projects - -Meson now defines a `clippy` target if the project uses the Rust programming -language. The target runs clippy on all Rust sources, using the `clippy-driver` -program from the same Rust toolchain as the `rustc` compiler. - -Using `clippy-driver` as the Rust compiler will now emit a warning, as it -is not meant to be a general-purpose compiler front-end. diff --git a/docs/markdown/snippets/external_project_devenv.md b/docs/markdown/snippets/external_project_devenv.md deleted file mode 100644 index 1927bd32f4e9..000000000000 --- a/docs/markdown/snippets/external_project_devenv.md +++ /dev/null @@ -1,7 +0,0 @@ -## Devenv support in external project module - -The [external project module](External-Project-module.md) now setups `PATH` and -`LD_LIBRARY_PATH` to be able to run programs. - -`@BINDIR@` is now substitued in arguments and `'--bindir=@PREFIX@/@BINDIR@'` -default argument have been added. diff --git a/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md b/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md deleted file mode 100644 index 6893429e8640..000000000000 --- a/docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md +++ /dev/null @@ -1,8 +0,0 @@ -## Fixed `sizeof` and `find_library` methods for Fortran compilers - -The implementation of the `.sizeof()` method has been fixed for Fortran -compilers (it was previously broken since it would try to compile a C code -snippet). Note that this functionality requires Fortran 2008 support. - -Incidentally this also fixes the `.find_library()` method for Fortran compilers -when the `prefer_static` built-in option is set to true. diff --git a/docs/markdown/snippets/format_from_stdin.md b/docs/markdown/snippets/format_from_stdin.md deleted file mode 100644 index cebe976c41b0..000000000000 --- a/docs/markdown/snippets/format_from_stdin.md +++ /dev/null @@ -1,4 +0,0 @@ -## format command now accept stdin argument - -You can now use `-` argument for `meson format` to read input from stdin -instead of reading it from a file. diff --git a/docs/markdown/snippets/introspect_machine.md b/docs/markdown/snippets/introspect_machine.md deleted file mode 100644 index 9b19bd6a20ca..000000000000 --- a/docs/markdown/snippets/introspect_machine.md +++ /dev/null @@ -1,5 +0,0 @@ -## "machine" entry in target introspection data - -The JSON data returned by `meson introspect --targets` now has a `machine` -entry in each `target_sources` block. The new entry can be one of `build` -or `host` for compiler-built targets, or absent for `custom_target` targets. diff --git a/docs/markdown/snippets/linearasm_features.md b/docs/markdown/snippets/linearasm_features.md deleted file mode 100644 index 571408685ae2..000000000000 --- a/docs/markdown/snippets/linearasm_features.md +++ /dev/null @@ -1,3 +0,0 @@ -## Add new language Linear Asm - -TI C6000 compiler supports a dialect of TI asm, so we add a new language for it. diff --git a/docs/markdown/snippets/num-processes.md b/docs/markdown/snippets/num-processes.md deleted file mode 100644 index 5336377900ce..000000000000 --- a/docs/markdown/snippets/num-processes.md +++ /dev/null @@ -1,8 +0,0 @@ -## Control the number of child processes with an environment variable - -Previously, `meson test` checked the `MESON_TESTTHREADS` variable to control -the amount of parallel jobs to run; this was useful when `meson test` is -invoked through `ninja test` for example. With this version, a new variable -`MESON_NUM_PROCESSES` is supported with a broader scope: in addition to -`meson test`, it is also used by the `external_project` module and by -Ninja targets that invoke `clang-tidy`, `clang-format` and `clippy`. diff --git a/docs/markdown/snippets/optioncaution.md b/docs/markdown/snippets/optioncaution.md deleted file mode 100644 index c2d9a877654e..000000000000 --- a/docs/markdown/snippets/optioncaution.md +++ /dev/null @@ -1,16 +0,0 @@ -## Call for testing for next release - -At the beginning of next cycle we aim to merge the [option refactor -branch](https://github.com/mesonbuild/meson/pull/13441). This is a -_huge_ change that will touch pretty much all code. - -The main change it brings is that you can override any builtin option -value for any subproject (even the top one) entirely from the command -line. This means that you can, for example, enable optimizations on -all subprojects but not on the top level project. - -We have done extensive testing and all our tests currently -pass. However it is expected that this will break some workflows. So -please test the branch when it lands and report issues. We want to fix -all regressions as soon as possible, preferably far before the next rc -release. diff --git a/docs/markdown/snippets/rust-2024.md b/docs/markdown/snippets/rust-2024.md deleted file mode 100644 index b1334d3261ad..000000000000 --- a/docs/markdown/snippets/rust-2024.md +++ /dev/null @@ -1,5 +0,0 @@ -## Support for Rust 2024 - -Meson can now request the compiler to use the 2024 edition of Rust. Use -`rust_std=2024` to activate it. Rust 2024 requires the 1.85.0 version -(or newer) of the compiler. diff --git a/docs/markdown/snippets/tasking_compiler.md b/docs/markdown/snippets/tasking_compiler.md deleted file mode 100644 index bbe29cc4b64b..000000000000 --- a/docs/markdown/snippets/tasking_compiler.md +++ /dev/null @@ -1,3 +0,0 @@ -## Support TASKING VX-Toolset - -Meson now supports the TASKING VX-Toolset compiler family for the Tricore cpu family. \ No newline at end of file diff --git a/docs/markdown/snippets/test_dependencies.md b/docs/markdown/snippets/test_dependencies.md deleted file mode 100644 index f69efa5cea9e..000000000000 --- a/docs/markdown/snippets/test_dependencies.md +++ /dev/null @@ -1,29 +0,0 @@ -## Test targets no longer built by default - -`meson test` and the `ninja all` rule have been reworked to no longer force -unnecessary rebuilds. - -`meson test` was invoking `ninja all` due to a bug if the chosen set of tests -had no build dependencies. The behavior is now the same as when tests do have -build dependencies, i.e. to only build the actual set of targets that are used -by the test. This change could cause failures when upgrading to Meson 1.7.0, if -the dependencies are not specified correctly in meson.build. Using `ninja test` -has always been guaranteed to "do the right thing" and rebuild `all` as well; -this continues to work. - -`ninja all` does not rebuild all tests anymore; it should be noted that this -change means test programs are no longer guaranteed to have been built, -depending on whether those test programs were *also* defined to build by -default / marked as installable. This avoids building test-only binaries as -part of installing the project (`ninja && ninja install`), which is unnecessary -and has no use case. - -Some users might have been relying on the "all" target building test -dependencies in combination with `meson test --no-rebuild` in order to skip -calling out to ninja when running tests. This might break with this change -because, when given `--no-rebuild`, Meson provides no guarantee that test -dependencies are present and up to date. The recommended workflow is to use -either `ninja test` or `ninja && meson test` but, if you wish to build test -programs and dependencies in a separate stage, you can use for example `ninja -all meson-test-prereq meson-benchmark-prereq` before `meson test --no-rebuild`. -These prereq targets have been available since meson 0.63.0. diff --git a/docs/markdown/snippets/vcs_tag.md b/docs/markdown/snippets/vcs_tag.md deleted file mode 100644 index c60996a2ea31..000000000000 --- a/docs/markdown/snippets/vcs_tag.md +++ /dev/null @@ -1,4 +0,0 @@ -## Install vcs_tag() output - -[[vcs_tag]] now has `install`, `install_dir`, `install_tag` and `install_mode` -keyword arguments to install the generated file. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 8d7d157e1506..b5e0f8157cdb 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -88,6 +88,7 @@ index.md Wrap-best-practices-and-tips.md Shipping-prebuilt-binaries-as-wraps.md Release-notes.md + Release-notes-for-1.7.0.md Release-notes-for-1.6.0.md Release-notes-for-1.5.0.md Release-notes-for-1.4.0.md From 897b6fcdf9adfa87fe60f420e1b483f0f49af7a3 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 26 Jan 2025 20:04:15 +0200 Subject: [PATCH 200/624] Bump version number for release. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f7d48453aa82..075a8e76d9ba 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -74,7 +74,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.7.0.rc2' +version = '1.7.0' # 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 From 0cbc4e1e1ae1bd2b10092ccbda30cc2bcc36de6c Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 26 Jan 2025 21:13:56 +0200 Subject: [PATCH 201/624] Bump version number for new development. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 075a8e76d9ba..a17a39fb6198 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -74,7 +74,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.7.0' +version = '1.7.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 From ae9ae8adced6bf2e0f78c752c4d64e2fcfe0844f Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 26 Jan 2025 21:16:13 +0200 Subject: [PATCH 202/624] Add deb package creation script. --- packaging/mpackage.py | 102 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 packaging/mpackage.py diff --git a/packaging/mpackage.py b/packaging/mpackage.py new file mode 100755 index 000000000000..a075e0627439 --- /dev/null +++ b/packaging/mpackage.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +# Converts a release tarball to a Debian package. + +# This script only works on Jussi's private release machine. + +import os, sys, subprocess, re, shutil +import tarfile +from glob import glob +import pathlib + +assert(os.getcwd() == '/home/jpakkane') + +packdir = 'mesonpackaging' +relfile = packdir + '/releases' + +files = glob('meson/dist/*.tar.gz') +assert(len(files) == 1) +infile = files[0] + +with tarfile.open(infile , 'r') as tf: + for e in tf.getmembers(): + if '__pycache__' in e.name or e.name.endswith('.pyc'): + sys.exit('Source archive has Python binary files:' + str(e.name)) + +fname = os.path.split(infile)[1] +tmp = fname.replace('-', '_') +if '0rc' in fname: + version = tmp[6:-7] + base_version = tmp[6:-10] + extension = tmp[-7:] + rcnum = tmp[-8:-7] + dchversion = base_version + '~rc' + rcnum + origname = tmp[:11] + '~rc' + rcnum + '.orig' + extension +else: + origname = tmp[:11] + '.orig.' + tmp[-6:] + version = tmp[6:-7] + dchversion = version +version_lines = pathlib.Path(relfile).read_text().split('\n')[:-1] +prev_ver = version_lines[-1] +version_lines.append(version) +print('Deb orig name is', origname) +print('Version is', version) +print('Previous version is', prev_ver) +assert(prev_ver) +outdir = os.path.join(packdir, version) +origfile = os.path.join(packdir, version, origname) +if not os.path.exists(outdir): + os.mkdir(outdir) + shutil.copyfile(infile, origfile) + subprocess.check_call(['tar', 'xf', origname], cwd=outdir) + extractdir = glob(os.path.join(packdir, version, 'meson-*'))[0] + fromdeb = glob(os.path.join(packdir, prev_ver, 'meson-*/debian'))[0] + todeb = os.path.join(extractdir, 'debian') + shutil.copytree(fromdeb, todeb) + myenv = os.environ.copy() + myenv['EDITOR'] = 'emacs' + subprocess.check_call(['dch', '-v', dchversion + '-1'], cwd=extractdir, env=myenv) + pathlib.Path(relfile).write_text('\n'.join(version_lines) + '\n') +else: + extractdir = glob(os.path.join(packdir, version, 'meson-*'))[0] + print('Outdir already exists') + +subprocess.check_call(['debuild', '-S'], cwd=extractdir) + +subprocess.call(['sudo rm -rf /var/cache/pbuilder/result/*'], shell=True) +subprocess.check_call('sudo pbuilder --build *.dsc 2>&1 | tee buildlog.txt', + shell=True, + cwd=outdir) +subprocess.check_call('sudo dpkg -i /var/cache/pbuilder/result/meson*all.deb', + shell=True) + +if os.path.exists('smoke/build'): + shutil.rmtree('smoke/build') +if os.path.exists('smoke/buildcross'): + shutil.rmtree('smoke/buildcross') +subprocess.check_call(['meson', 'setup', 'build'], cwd='smoke') +subprocess.check_call(['ninja', 'test'], cwd='smoke/build') +subprocess.check_call(['ninja', 'reconfigure'], cwd='smoke/build') +subprocess.check_call(['ninja', 'test'], cwd='smoke/build') +#subprocess.check_call(['/usr/bin/meson', +# 'env2mfile', +# '--cross', +# '--debarch', +# 'armhf', +# '-o', +# 'cross-file.txt'], cwd='smoke') +subprocess.check_call(['/usr/share/meson/debcrossgen', + '--arch', + 'armhf', + '-o', + 'cross-file.txt'], cwd='smoke') +subprocess.check_call(['meson', + 'setup', + 'buildcross', + '--cross-file', + 'cross-file.txt'], cwd='smoke') +subprocess.check_call(['ninja', 'test'], cwd='smoke/buildcross') +subprocess.check_call(['sudo', 'apt-get', '-y', 'remove', 'meson']) +subprocess.call('rm meson-*tar.gz*', shell=True) +subprocess.check_call(['cp', infile, '.']) +subprocess.check_call(['gpg', '--detach-sign', '--armor', fname]) From 1882ae4fc628398e62f3a7d8e7d726aa5a5cde1a Mon Sep 17 00:00:00 2001 From: ajs Date: Wed, 22 Jan 2025 22:34:31 +0530 Subject: [PATCH 203/624] adding c_std=c2y option for clang Signed-off-by: ajs --- docs/markdown/Builtin-options.md | 2 +- docs/markdown/Configuring-a-build-directory.md | 2 +- mesonbuild/compilers/c.py | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 3a912805a057..d64cd85c0fab 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -273,7 +273,7 @@ or compiler being used: | ------ | ------------- | --------------- | ----------- | | c_args | | free-form comma-separated list | C compile arguments to use | | c_link_args | | free-form comma-separated list | C link arguments to use | -| c_std | none | none, c89, c99, c11, c17, c18, c2x, c23, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23 | C language standard to use | +| c_std | none | none, c89, c99, c11, c17, c18, c2x, c23, c2y, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23, gnu2y | C language standard to use | | c_winlibs | see below | free-form comma-separated list | Standard Windows libs to link against | | c_thread_count | 4 | integer value ≥ 0 | Number of threads to use with emcc when using threads | | cpp_args | | free-form comma-separated list | C++ compile arguments to use | diff --git a/docs/markdown/Configuring-a-build-directory.md b/docs/markdown/Configuring-a-build-directory.md index db6fc03ef453..746e4b16b6c4 100644 --- a/docs/markdown/Configuring-a-build-directory.md +++ b/docs/markdown/Configuring-a-build-directory.md @@ -61,7 +61,7 @@ a sample output for a simple project. ------ ------------- --------------- ----------- c_args [] Extra arguments passed to the C compiler c_link_args [] Extra arguments passed to the C linker - c_std c99 [none, c89, c99, c11, c17, c18, c2x, c23, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23] C language standard to use + c_std c99 [none, c89, c99, c11, c17, c18, c2x, c23, c2y, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23, gnu2y] C language standard to use cpp_args [] Extra arguments passed to the C++ compiler cpp_debugstl false [true, false] STL debug mode cpp_link_args [] Extra arguments passed to the C++ linker diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index c75120fee4d0..51fd724c1bc7 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -47,7 +47,7 @@ else: CompilerMixinBase = object -_ALL_STDS = ['c89', 'c9x', 'c90', 'c99', 'c1x', 'c11', 'c17', 'c18', 'c2x', 'c23'] +_ALL_STDS = ['c89', 'c9x', 'c90', 'c99', 'c1x', 'c11', 'c17', 'c18', 'c2x', 'c23', 'c2y'] _ALL_STDS += [f'gnu{std[1:]}' for std in _ALL_STDS] _ALL_STDS += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999', 'iso9899:2011', 'iso9899:2017', 'iso9899:2018'] @@ -115,6 +115,7 @@ class _ClangCStds(CompilerMixinBase): _C18_VERSION = '>=8.0.0' _C2X_VERSION = '>=9.0.0' _C23_VERSION = '>=18.0.0' + _C2Y_VERSION = '>=19.0.0' def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() @@ -129,6 +130,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': stds += ['c2x'] if version_compare(self.version, self._C23_VERSION): stds += ['c23'] + if version_compare(self.version, self._C2Y_VERSION): + stds += ['c2y'] key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' From 79a87d5af7c0b5c1617b11421564cddc1d25ba3d Mon Sep 17 00:00:00 2001 From: ajs Date: Thu, 23 Jan 2025 07:50:27 +0530 Subject: [PATCH 204/624] adding c_std=c2y option for gcc-15 Signed-off-by: ajs --- mesonbuild/compilers/c.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 51fd724c1bc7..0a05b387bcd9 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -277,6 +277,7 @@ class GnuCCompiler(GnuCompiler, CCompiler): _C18_VERSION = '>=8.0.0' _C2X_VERSION = '>=9.0.0' _C23_VERSION = '>=14.0.0' + _C2Y_VERSION = '>=15.0.0' _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, @@ -306,6 +307,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': stds += ['c2x'] if version_compare(self.version, self._C23_VERSION): stds += ['c23'] + if version_compare(self.version, self._C2Y_VERSION): + stds += ['c2y'] key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' From 6ee583e119b432fee03f908547729d5da030397e Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 9 Jan 2025 17:00:09 -0500 Subject: [PATCH 205/624] allow to compare multiple version with version_compare --- docs/markdown/snippets/multiple_version_compare.md | 8 ++++++++ docs/yaml/elementary/str.yml | 6 ++++++ mesonbuild/interpreter/primitives/string.py | 10 ++++++---- test cases/common/35 string operations/meson.build | 5 +++++ test cases/d/11 dub/meson.build | 2 +- test cases/d/14 dub with deps/meson.build | 2 +- test cases/osx/3 has function xcode8/meson.build | 2 +- 7 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/multiple_version_compare.md diff --git a/docs/markdown/snippets/multiple_version_compare.md b/docs/markdown/snippets/multiple_version_compare.md new file mode 100644 index 000000000000..5e8c7582f074 --- /dev/null +++ b/docs/markdown/snippets/multiple_version_compare.md @@ -0,0 +1,8 @@ +## `version_compare` now accept multiple compare strings + +Is it now possible to compare version against multiple values, to check for +a range of version for instance. + +```meson +'1.5'.version_compare('>=1', '<2') +``` diff --git a/docs/yaml/elementary/str.yml b/docs/yaml/elementary/str.yml index 9d059cc092d3..44aa74240943 100644 --- a/docs/yaml/elementary/str.yml +++ b/docs/yaml/elementary/str.yml @@ -306,6 +306,12 @@ methods: It is best to be unambiguous and specify the full revision level to compare. + *Since 1.8.0* multiple versions can be compared: + + ```meson + '3.6'.version_compare('>=3', '<4.0') == true + ``` + posargs: compare_string: type: str diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py index 7cb492da7efc..a224dfac8946 100644 --- a/mesonbuild/interpreter/primitives/string.py +++ b/mesonbuild/interpreter/primitives/string.py @@ -7,7 +7,7 @@ import typing as T -from ...mesonlib import version_compare +from ...mesonlib import version_compare, version_compare_many from ...interpreterbase import ( ObjectHolder, MesonOperator, @@ -169,9 +169,11 @@ def underscorify_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> st return re.sub(r'[^a-zA-Z0-9]', '_', self.held_object) @noKwargs - @typed_pos_args('str.version_compare', str) - def version_compare_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool: - return version_compare(self.held_object, args[0]) + @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: + FeatureNew.single_use('version_compare() with multiple arguments', '1.8.0', self.subproject, location=self.current_node) + return version_compare_many(self.held_object, args[0])[0] @staticmethod def _op_div(this: str, other: str) -> str: diff --git a/test cases/common/35 string operations/meson.build b/test cases/common/35 string operations/meson.build index 27cc0d8c8671..ab77b4947c56 100644 --- a/test cases/common/35 string operations/meson.build +++ b/test cases/common/35 string operations/meson.build @@ -79,6 +79,11 @@ assert(not version_number.version_compare('!=1.2.8'), 'Version_compare neq broke assert(version_number.version_compare('<2.0'), 'Version_compare major less broken') assert(version_number.version_compare('>0.9'), 'Version_compare major greater broken') +assert(version_number.version_compare('>1.2', '<1.3')) +assert(not version_number.version_compare('>1.2', '>1.3')) +assert(not version_number.version_compare('<1.2', '<1.3')) +assert(version_number.version_compare('>1.0', '>1.2')) + assert(' spaces tabs '.strip() == 'spaces tabs', 'Spaces and tabs badly stripped') assert(''' multiline string '''.strip() == '''multiline string''', 'Newlines badly stripped') diff --git a/test cases/d/11 dub/meson.build b/test cases/d/11 dub/meson.build index 91955710e709..cfdb7fa59098 100644 --- a/test cases/d/11 dub/meson.build +++ b/test cases/d/11 dub/meson.build @@ -6,7 +6,7 @@ if not dub_exe.found() endif dub_ver = dub_exe.version() -if dub_ver.version_compare('>1.31.1') and dub_ver.version_compare('<1.35.0') +if dub_ver.version_compare('>1.31.1', '<1.35.0') error('MESON_SKIP_TEST: Incompatible Dub version ' + dub_ver) endif diff --git a/test cases/d/14 dub with deps/meson.build b/test cases/d/14 dub with deps/meson.build index 2e3bce87d37d..c1acfcb1b2fc 100644 --- a/test cases/d/14 dub with deps/meson.build +++ b/test cases/d/14 dub with deps/meson.build @@ -6,7 +6,7 @@ if not dub_exe.found() endif dub_ver = dub_exe.version() -if dub_ver.version_compare('>1.31.1') and dub_ver.version_compare('<1.35.0') +if dub_ver.version_compare('>1.31.1', '<1.35.0') error('MESON_SKIP_TEST: Incompatible Dub version') endif diff --git a/test cases/osx/3 has function xcode8/meson.build b/test cases/osx/3 has function xcode8/meson.build index edd3688f23f7..8836c0ce852c 100644 --- a/test cases/osx/3 has function xcode8/meson.build +++ b/test cases/osx/3 has function xcode8/meson.build @@ -12,7 +12,7 @@ sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/M args_10_12 = ['-mmacosx-version-min=10.13'] + sdk_args # Test requires XCode 8 which has the MacOSX 10.12 SDK -if cc.version().version_compare('>=8.0') and cc.version().version_compare('<8.1') +if cc.version().version_compare('>=8.0', '<8.1') if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include ') error('Should not have found clock_gettime via when targeting Mac OS X 10.11') endif From 9afc6980eafa5c422816a4b260925d69dbcf54d8 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Sat, 11 Jan 2025 23:41:32 +0100 Subject: [PATCH 206/624] Show real error if module exists but import failed --- mesonbuild/interpreter/interpreter.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 02a59e3986d5..806a737ea673 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -5,6 +5,7 @@ from __future__ import annotations import hashlib +import traceback from .. import mparser from .. import environment @@ -622,8 +623,18 @@ def func_import(self, node: mparser.BaseNode, args: T.Tuple[str], if real_modname in self.modules: return self.modules[real_modname] try: - module = importlib.import_module(f'mesonbuild.modules.{real_modname}') - except ImportError: + full_module_path = f'mesonbuild.modules.{real_modname}' + module = importlib.import_module(full_module_path) + except ImportError as e: + if e.name != full_module_path: + if required: + raise e + + mlog.warning(f'Module "{modname}" exists but failed to import.') + + for line in traceback.format_exception(e): + mlog.debug(line) + if required: raise InvalidArguments(f'Module "{modname}" does not exist') ext_module = NotFoundExtensionModule(real_modname) From dcfbd6974c077f345e0351c7668d9441a311b58a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 14:26:42 +0100 Subject: [PATCH 207/624] backends: memoize result of canonicalize_filename --- mesonbuild/backend/backends.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 18caf7bbe8a7..66513720daea 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -825,6 +825,7 @@ def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTa return tuple(result) @staticmethod + @lru_cache(maxsize=None) def canonicalize_filename(fname: str) -> str: parts = Path(fname).parts hashed = '' From 41fe53a9120766b63b560c6c6e49cee3c67fc69d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 16:35:28 +0100 Subject: [PATCH 208/624] backends: remove unused argument from determine_ext_objs The proj_dir_to_build_root argument of determine_ext_objs is always empty, remove it. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 4 ++-- mesonbuild/backend/ninjabackend.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 66513720daea..a6f3fabb6abf 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -472,8 +472,8 @@ def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root) return list(dict.fromkeys(obj_list)), deps - def determine_ext_objs(self, objects: build.ExtractedObjects, proj_dir_to_build_root: str = '') -> T.List[str]: - obj_list, _ = self._flatten_object_list(objects.target, [objects], proj_dir_to_build_root) + 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)) def _flatten_object_list(self, target: build.BuildTarget, diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cd35d2fcc300..dce703fe4060 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3371,7 +3371,7 @@ def get_link_whole_args(self, linker: DynamicLinker, target): objects_from_static_libs: T.List[ExtractedObjects] = [] for dep in target.link_whole_targets: l = dep.extract_all_objects(False) - objects_from_static_libs += self.determine_ext_objs(l, '') + objects_from_static_libs += self.determine_ext_objs(l) objects_from_static_libs.extend(self.flatten_object_list(dep)[0]) return objects_from_static_libs From dbdb2c81d20db8a5ad13f964c41e03deff2a0950 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Jan 2025 10:54:16 +0100 Subject: [PATCH 209/624] backends: remove proj_dir_to_build_root from _determine_ext_objs This is a very hot function, improve the memoization of the results by removing an argument (that is almost always empty, in fact). Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a6f3fabb6abf..159cabbbdc82 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -500,7 +500,12 @@ def _flatten_object_list(self, target: build.BuildTarget, objs, d = self._flatten_object_list(obj.target, obj.objlist, proj_dir_to_build_root) obj_list.extend(objs) deps.extend(d) - obj_list.extend(self._determine_ext_objs(obj, proj_dir_to_build_root)) + new_objs = self._determine_ext_objs(obj) + if proj_dir_to_build_root: + for o in new_objs: + obj_list.append(os.path.join(proj_dir_to_build_root, o)) + else: + obj_list.extend(new_objs) deps.append(obj.target) else: raise MesonException('Unknown data type in object list.') @@ -884,7 +889,7 @@ def object_filename_from_source(self, target: build.BuildTarget, compiler: Compi return os.path.join(targetdir, ret) return ret - def _determine_ext_objs(self, extobj: 'build.ExtractedObjects', proj_dir_to_build_root: str) -> T.List[str]: + def _determine_ext_objs(self, extobj: 'build.ExtractedObjects') -> T.List[str]: result: T.List[str] = [] targetdir = self.get_target_private_dir(extobj.target) @@ -910,7 +915,7 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects', proj_dir_to_buil compiler = extobj.target.compilers[lang] if compiler.get_argument_syntax() == 'msvc': objname = self.get_msvc_pch_objname(lang, pch) - result.append(os.path.join(proj_dir_to_build_root, targetdir, objname)) + result.append(os.path.join(targetdir, objname)) # extobj could contain only objects and no sources if not sources: @@ -937,8 +942,7 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects', proj_dir_to_buil for osrc in sources: compiler = get_compiler_for_source(extobj.target.compilers.values(), osrc) objname = self.object_filename_from_source(extobj.target, compiler, osrc, targetdir) - objpath = os.path.join(proj_dir_to_build_root, objname) - result.append(objpath) + result.append(objname) return result From 1e986554ef2071f59fb796cda5e66c448bde6ac9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Jan 2025 09:07:20 +0100 Subject: [PATCH 210/624] backends: cache file names for ExtractedObjects Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 159cabbbdc82..0c181e56fb53 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -889,6 +889,7 @@ def object_filename_from_source(self, target: build.BuildTarget, compiler: Compi return os.path.join(targetdir, ret) return ret + @lru_cache(maxsize=None) def _determine_ext_objs(self, extobj: 'build.ExtractedObjects') -> T.List[str]: result: T.List[str] = [] From 07f595fa98087352940121c87ba9aca0ebc95996 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Thu, 23 Jan 2025 22:58:00 +0100 Subject: [PATCH 211/624] ninjabackend: Make header deps order-only deps As the comment to get_generated_headers says, these dependencies should be order-only dependencies. Fixes #10882. --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index dce703fe4060..cf9309e258e3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3185,7 +3185,7 @@ def add_header_deps(self, target, ninja_element, header_deps): d = d.rel_to_builddir(self.build_to_src) elif not self.has_dir_part(d): d = os.path.join(self.get_target_private_dir(target), d) - ninja_element.add_dep(d) + ninja_element.add_orderdep(d) def has_dir_part(self, fname: FileOrString) -> bool: # FIXME FIXME: The usage of this is a terrible and unreliable hack From e2d32eb0f73efd75a3d22d007fed45df637e321e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 18 Nov 2024 11:43:37 -0800 Subject: [PATCH 212/624] tests: Add annotations for `assertMesonRaises` --- unittests/failuretests.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/unittests/failuretests.py b/unittests/failuretests.py index e5a3b35ea05e..0dd6c5f650cf 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -1,11 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2021 The Meson development team +from __future__ import annotations import subprocess import tempfile import os import shutil import unittest +import typing as T from contextlib import contextmanager from mesonbuild.mesonlib import ( @@ -75,12 +77,13 @@ def tearDown(self): super().tearDown() windows_proof_rmtree(self.srcdir) - def assertMesonRaises(self, contents, match, *, - extra_args=None, - langs=None, - meson_version=None, - options=None, - override_envvars=None): + def assertMesonRaises(self, contents: str, + match: T.Union[str, T.Pattern[str]], *, + extra_args: T.Optional[T.List[str]] = None, + langs: T.Optional[T.List[str]] = None, + meson_version: T.Optional[str] = None, + options: T.Optional[str] = None, + override_envvars: T.Optional[T.MutableMapping[str, str]] = None) -> None: ''' Assert that running meson configure on the specified @contents raises a error message matching regex @match. From 1d2627ddbfe2972ae5676f60a30f4d4fde7f4e04 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 18 Nov 2024 13:36:58 -0800 Subject: [PATCH 213/624] tests: mock the environment in setUpClass When used as a class decorator VSCode can no longer see that BasePlatformTest is a TestCase. I hate having to adapt the code to the tools, but it's really annoying to not have completion and syntax highlighting. --- unittests/baseplatformtests.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index 3770321925fa..0ac9c9cf0dc9 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -42,7 +42,6 @@ # e.g. for assertXXX helpers. __unittest = True -@mock.patch.dict(os.environ) class BasePlatformTests(TestCase): prefix = '/usr' libdir = 'lib' @@ -87,9 +86,18 @@ def setUpClass(cls) -> None: # VS doesn't have a stable output when no changes are done # XCode backend is untested with unit tests, help welcome! cls.no_rebuild_stdout = [f'UNKNOWN BACKEND {cls.backend.name!r}'] + + cls.env_patch = mock.patch.dict(os.environ) + cls.env_patch.start() + os.environ['COLUMNS'] = '80' os.environ['PYTHONIOENCODING'] = 'utf8' + @classmethod + def tearDownClass(cls) -> None: + super().tearDownClass() + cls.env_patch.stop() + def setUp(self): super().setUp() self.meson_native_files = [] From 82fbf07a44204cd84fd9341450b10e471cf18a39 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 09:15:33 -0700 Subject: [PATCH 214/624] compilers/clang: Move the Mixin for C standards out of the c module We'll want to use this for ObjC as well, so we'll make it public and put it in a public place. --- mesonbuild/compilers/c.py | 42 ++------------------------- mesonbuild/compilers/mixins/clang.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 0a05b387bcd9..33f313ed90ab 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -21,7 +21,7 @@ from .mixins.gnu import GnuCompiler from .mixins.gnu import gnu_common_warning_args, gnu_c_warning_args from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler -from .mixins.clang import ClangCompiler +from .mixins.clang import ClangCompiler, ClangCStds from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler from .mixins.emscripten import EmscriptenMixin @@ -103,43 +103,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': return opts -class _ClangCStds(CompilerMixinBase): - - """Mixin class for clang based compilers for setting C standards. - - This is used by both ClangCCompiler and ClangClCompiler, as they share - the same versions - """ - - _C17_VERSION = '>=6.0.0' - _C18_VERSION = '>=8.0.0' - _C2X_VERSION = '>=9.0.0' - _C23_VERSION = '>=18.0.0' - _C2Y_VERSION = '>=19.0.0' - - def get_options(self) -> 'MutableKeyedOptionDictType': - opts = super().get_options() - stds = ['c89', 'c99', 'c11'] - # https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html - # https://en.wikipedia.org/wiki/Xcode#Latest_versions - if version_compare(self.version, self._C17_VERSION): - stds += ['c17'] - if version_compare(self.version, self._C18_VERSION): - stds += ['c18'] - if version_compare(self.version, self._C2X_VERSION): - stds += ['c2x'] - if version_compare(self.version, self._C23_VERSION): - stds += ['c23'] - if version_compare(self.version, self._C2Y_VERSION): - stds += ['c2y'] - key = self.form_compileropt_key('std') - std_opt = opts[key] - assert isinstance(std_opt, options.UserStdOption), 'for mypy' - std_opt.set_versions(stds, gnu=True) - return opts - - -class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler): +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', @@ -523,7 +487,7 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] return args -class ClangClCCompiler(_ClangCStds, ClangClCompiler, VisualStudioLikeCCompilerMixin, CCompiler): +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, linker: T.Optional['DynamicLinker'] = None, diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 82df325ec01e..3d3325416749 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -10,6 +10,7 @@ import typing as T from ... import mesonlib +from ... import options from ...linkers.linkers import AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuGoldDynamicLinker, \ MoldDynamicLinker, MSVCDynamicLinker from ...options import OptionKey @@ -17,8 +18,14 @@ from .gnu import GnuLikeCompiler if T.TYPE_CHECKING: + from ...coredata import MutableKeyedOptionDictType from ...environment import Environment from ...dependencies import Dependency # noqa: F401 + from ..compilers import Compiler + + CompilerMixinBase = Compiler +else: + CompilerMixinBase = object clang_color_args: T.Dict[str, T.List[str]] = { 'auto': ['-fdiagnostics-color=auto'], @@ -204,3 +211,39 @@ def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default', raise mesonlib.MesonException('clang support for LTO threads requires clang >=4.0') args.append(f'-flto-jobs={threads}') return args + + +class ClangCStds(CompilerMixinBase): + + """Mixin class for clang based compilers for setting C standards. + + This is used by both ClangCCompiler and ClangClCompiler, as they share + the same versions + """ + + _C17_VERSION = '>=6.0.0' + _C18_VERSION = '>=8.0.0' + _C2X_VERSION = '>=9.0.0' + _C23_VERSION = '>=18.0.0' + _C2Y_VERSION = '>=19.0.0' + + def get_options(self) -> MutableKeyedOptionDictType: + opts = super().get_options() + stds = ['c89', 'c99', 'c11'] + # https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html + # https://en.wikipedia.org/wiki/Xcode#Latest_versions + if mesonlib.version_compare(self.version, self._C17_VERSION): + stds += ['c17'] + if mesonlib.version_compare(self.version, self._C18_VERSION): + stds += ['c18'] + if mesonlib.version_compare(self.version, self._C2X_VERSION): + stds += ['c2x'] + if mesonlib.version_compare(self.version, self._C23_VERSION): + stds += ['c23'] + if mesonlib.version_compare(self.version, self._C2Y_VERSION): + stds += ['c2y'] + key = self.form_compileropt_key('std') + std_opt = opts[key] + assert isinstance(std_opt, options.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) + return opts From d309e8d5f7ef4503bc493f498cae46129323f93b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 09:30:29 -0700 Subject: [PATCH 215/624] compilers/objc: Use shared C standards with clang C compiler This means that the two compilers will update together, and that ObjC has the list behavior that C does. --- mesonbuild/compilers/c.py | 9 +++++---- mesonbuild/compilers/objc.py | 37 +++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 33f313ed90ab..0cc980e4200e 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2020 The Meson development team +# Copyright © 2024-2025 Intel Corporation from __future__ import annotations @@ -47,9 +48,9 @@ else: CompilerMixinBase = object -_ALL_STDS = ['c89', 'c9x', 'c90', 'c99', 'c1x', 'c11', 'c17', 'c18', 'c2x', 'c23', 'c2y'] -_ALL_STDS += [f'gnu{std[1:]}' for std in _ALL_STDS] -_ALL_STDS += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999', 'iso9899:2011', 'iso9899:2017', 'iso9899:2018'] +ALL_STDS = ['c89', 'c9x', 'c90', 'c99', 'c1x', 'c11', 'c17', 'c18', 'c2x', 'c23', 'c2y'] +ALL_STDS += [f'gnu{std[1:]}' for std in ALL_STDS] +ALL_STDS += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999', 'iso9899:2011', 'iso9899:2017', 'iso9899:2018'] class CCompiler(CLikeCompiler, Compiler): @@ -98,7 +99,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ - key: options.UserStdOption('C', _ALL_STDS), + key: options.UserStdOption('C', ALL_STDS), }) return opts diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 97550c2ea251..de23e70cf835 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -5,13 +5,13 @@ import typing as T -from .. import options -from ..options import OptionKey +from ..options import OptionKey, UserStdOption +from .c import ALL_STDS from .compilers import Compiler +from .mixins.clang import ClangCompiler, ClangCStds from .mixins.clike import CLikeCompiler from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args -from .mixins.clang import ClangCompiler if T.TYPE_CHECKING: from .. import coredata @@ -34,6 +34,14 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ linker=linker) CLikeCompiler.__init__(self) + def get_options(self) -> coredata.MutableKeyedOptionDictType: + opts = super().get_options() + key = self.form_compileropt_key('std') + opts.update({ + key: UserStdOption('c', ALL_STDS), + }) + return opts + @staticmethod def get_display_language() -> str: return 'Objective-C' @@ -42,6 +50,11 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: code = '#import\nint main(void) { return 0; }\n' return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjc.m', code) + def form_compileropt_key(self, basename: str) -> OptionKey: + if basename == 'std': + return OptionKey(f'c_{basename}', machine=self.for_machine) + return super().form_compileropt_key(basename) + class GnuObjCCompiler(GnuCompiler, ObjCCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, @@ -62,7 +75,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_objc_warning_args))} -class ClangObjCCompiler(ClangCompiler, 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', defines: T.Optional[T.Dict[str, str]] = None, @@ -78,19 +91,9 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ '3': default_warn_args + ['-Wextra', '-Wpedantic'], 'everything': ['-Weverything']} - def get_options(self) -> 'coredata.MutableKeyedOptionDictType': - return self.update_options( - super().get_options(), - self.create_option(options.UserComboOption, - OptionKey('c_std', machine=self.for_machine), - 'C language standard to use', - ['none', 'c89', 'c99', 'c11', 'c17', 'gnu89', 'gnu99', 'gnu11', 'gnu17'], - 'none'), - ) - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: args = [] - std = options.get_value(OptionKey('c_std', machine=self.for_machine)) + std = options.get_value(self.form_compileropt_key('std')) if std != 'none': args.append('-std=' + std) return args @@ -98,3 +101,7 @@ def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T. class AppleClangObjCCompiler(ClangObjCCompiler): """Handle the differences between Apple's clang and vanilla clang.""" + + _C17_VERSION = '>=10.0.0' + _C18_VERSION = '>=11.0.0' + _C2X_VERSION = '>=11.0.0' From a935eeef5028dfc1999aab40577f97a455c091c7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 09:24:43 -0700 Subject: [PATCH 216/624] compilers/clang: split the Std handling for C++ out of the ClangCPPCompiler We'll want to use this for the ObjC++ compiler too. --- mesonbuild/compilers/cpp.py | 19 +++---------------- mesonbuild/compilers/mixins/clang.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index d2eca897b6ae..31a1c93e01be 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -27,7 +27,7 @@ from .mixins.visualstudio import MSVCCompiler, ClangClCompiler from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_cpp_warning_args from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler -from .mixins.clang import ClangCompiler +from .mixins.clang import ClangCompiler, ClangCPPStds from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler from .mixins.emscripten import EmscriptenMixin @@ -218,10 +218,7 @@ def language_stdlib_only_link_flags(self, env: Environment) -> T.List[str]: raise MesonException('Could not detect either libc++ or libstdc++ as your C++ stdlib implementation.') -class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler): - - _CPP23_VERSION = '>=12.0.0' - _CPP26_VERSION = '>=17.0.0' +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', @@ -239,7 +236,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ 'everything': ['-Weverything']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() self.update_options( opts, self.create_option(options.UserComboOption, @@ -256,16 +253,6 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 'STL debug mode', False), ) - cppstd_choices = [ - 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', 'c++20', - ] - if version_compare(self.version, self._CPP23_VERSION): - cppstd_choices.append('c++23') - if version_compare(self.version, self._CPP26_VERSION): - cppstd_choices.append('c++26') - std_opt = opts[self.form_compileropt_key('std')] - assert isinstance(std_opt, options.UserStdOption), 'for mypy' - std_opt.set_versions(cppstd_choices, gnu=True) if self.info.is_windows() or self.info.is_cygwin(): self.update_options( opts, diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 3d3325416749..867b58680209 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -247,3 +247,30 @@ def get_options(self) -> MutableKeyedOptionDictType: assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(stds, gnu=True) return opts + + +class ClangCPPStds(CompilerMixinBase): + + """Mixin class for clang based compilers for setting C++ standards. + + This is used by the ClangCPPCompiler + """ + + _CPP23_VERSION = '>=12.0.0' + _CPP26_VERSION = '>=17.0.0' + + def get_options(self) -> MutableKeyedOptionDictType: + opts = super().get_options() + stds = [ + 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', + 'c++20', + ] + if mesonlib.version_compare(self.version, self._CPP23_VERSION): + stds.append('c++23') + if mesonlib.version_compare(self.version, self._CPP26_VERSION): + stds.append('c++26') + key = self.form_compileropt_key('std') + std_opt = opts[key] + assert isinstance(std_opt, options.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) + return opts From b5ff5931b69dedbcee582ecc74bb8e59fb60e068 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 09:35:11 -0700 Subject: [PATCH 217/624] compilers/objcpp: Use shared C++ standards with ClangCPPStandard --- mesonbuild/compilers/cpp.py | 8 +++---- mesonbuild/compilers/objcpp.py | 38 ++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 31a1c93e01be..e051050e236c 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -45,9 +45,9 @@ else: CompilerMixinBase = object -_ALL_STDS = ['c++98', 'c++0x', 'c++03', 'c++1y', 'c++1z', 'c++11', 'c++14', 'c++17', 'c++2a', 'c++20', 'c++23', 'c++26'] -_ALL_STDS += [f'gnu{std[1:]}' for std in _ALL_STDS] -_ALL_STDS += ['vc++11', 'vc++14', 'vc++17', 'vc++20', 'vc++latest', 'c++latest'] +ALL_STDS = ['c++98', 'c++0x', 'c++03', 'c++1y', 'c++1z', 'c++11', 'c++14', 'c++17', 'c++2a', 'c++20', 'c++23', 'c++26'] +ALL_STDS += [f'gnu{std[1:]}' for std in ALL_STDS] +ALL_STDS += ['vc++11', 'vc++14', 'vc++17', 'vc++20', 'vc++latest', 'c++latest'] def non_msvc_eh_options(eh: str, args: T.List[str]) -> None: @@ -175,7 +175,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ - key: options.UserStdOption('C++', _ALL_STDS), + key: options.UserStdOption('C++', ALL_STDS), }) return opts diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index 973d7bb0cfb8..9fd1196904cd 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -5,13 +5,13 @@ import typing as T -from .. import options -from ..options import OptionKey +from ..options import OptionKey, UserStdOption -from .mixins.clike import CLikeCompiler +from .cpp import ALL_STDS from .compilers import Compiler from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args -from .mixins.clang import ClangCompiler +from .mixins.clang import ClangCompiler, ClangCPPStds +from .mixins.clike import CLikeCompiler if T.TYPE_CHECKING: from .. import coredata @@ -20,6 +20,7 @@ from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice + class ObjCPPCompiler(CLikeCompiler, Compiler): language = 'objcpp' @@ -41,6 +42,19 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: code = '#import\nclass MyClass;int main(void) { return 0; }\n' return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code) + def form_compileropt_key(self, basename: str) -> OptionKey: + if basename == 'std': + return OptionKey(f'cpp_{basename}', machine=self.for_machine) + return super().form_compileropt_key(basename) + + def get_options(self) -> coredata.MutableKeyedOptionDictType: + opts = super().get_options() + key = self.form_compileropt_key('std') + opts.update({ + key: UserStdOption('cpp', ALL_STDS), + }) + return opts + class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, @@ -61,7 +75,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_objc_warning_args))} -class ClangObjCPPCompiler(ClangCompiler, 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', @@ -78,21 +92,9 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ '3': default_warn_args + ['-Wextra', '-Wpedantic'], 'everything': ['-Weverything']} - def get_options(self) -> coredata.MutableKeyedOptionDictType: - return self.update_options( - super().get_options(), - self.create_option(options.UserComboOption, - OptionKey('cpp_std', machine=self.for_machine), - 'C++ language standard to use', - ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++2b', - 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++20', - 'gnu++2b'], - 'none'), - ) - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: args = [] - std = options.get_value(OptionKey('cpp_std', machine=self.for_machine)) + std = options.get_value(self.form_compileropt_key('std')) if std != 'none': args.append('-std=' + std) return args From c7036e4cae4508a73ebc2b923d50d53968de38a4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 10:02:45 -0700 Subject: [PATCH 218/624] compilers/gnu: Pull C Standard handling out of GnuCCompiler So we can re-use it for the ObjC code --- mesonbuild/compilers/c.py | 25 ++++--------------------- mesonbuild/compilers/mixins/gnu.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 0cc980e4200e..70f4dc70e76d 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -19,7 +19,7 @@ from .mixins.ti import TICompiler from .mixins.arm import ArmCompiler, ArmclangCompiler from .mixins.visualstudio import MSVCCompiler, ClangClCompiler -from .mixins.gnu import GnuCompiler +from .mixins.gnu import GnuCompiler, GnuCStds from .mixins.gnu import gnu_common_warning_args, gnu_c_warning_args from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler, ClangCStds @@ -237,12 +237,8 @@ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: return [] -class GnuCCompiler(GnuCompiler, CCompiler): +class GnuCCompiler(GnuCStds, GnuCompiler, CCompiler): - _C18_VERSION = '>=8.0.0' - _C2X_VERSION = '>=9.0.0' - _C23_VERSION = '>=14.0.0' - _C2Y_VERSION = '>=15.0.0' _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, @@ -264,25 +260,12 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_c_warning_args))} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) - stds = ['c89', 'c99', 'c11'] - if version_compare(self.version, self._C18_VERSION): - stds += ['c17', 'c18'] - if version_compare(self.version, self._C2X_VERSION): - stds += ['c2x'] - if version_compare(self.version, self._C23_VERSION): - stds += ['c23'] - if version_compare(self.version, self._C2Y_VERSION): - stds += ['c2y'] - key = self.form_compileropt_key('std') - std_opt = opts[key] - assert isinstance(std_opt, options.UserStdOption), 'for mypy' - std_opt.set_versions(stds, gnu=True) + opts = super().get_options() if self.info.is_windows() or self.info.is_cygwin(): self.update_options( opts, self.create_option(options.UserArrayOption, - key.evolve('c_winlibs'), + self.form_compileropt_key('winlibs'), 'Standard Windows libs to link against', gnu_winlibs), ) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index b974474fa953..1f78ef89d542 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -15,11 +15,12 @@ from ... import mesonlib from ... import mlog -from ...options import OptionKey +from ...options import OptionKey, UserStdOption from mesonbuild.compilers.compilers import CompileCheckMode if T.TYPE_CHECKING: from ..._typing import ImmutableListProtocol + from ...coredata import MutableKeyedOptionDictType from ...environment import Environment from ..compilers import Compiler else: @@ -629,3 +630,30 @@ def use_linker_args(cls, linker: str, version: str) -> T.List[str]: def get_profile_use_args(self) -> T.List[str]: return super().get_profile_use_args() + ['-fprofile-correction'] + + +class GnuCStds(Compiler): + + """Mixin class for gcc based compilers for setting C standards.""" + + _C18_VERSION = '>=8.0.0' + _C2X_VERSION = '>=9.0.0' + _C23_VERSION = '>=14.0.0' + _C2Y_VERSION = '>=15.0.0' + + def get_options(self) -> MutableKeyedOptionDictType: + opts = super().get_options() + stds = ['c89', 'c99', 'c11'] + if mesonlib.version_compare(self.version, self._C18_VERSION): + stds += ['c17', 'c18'] + if mesonlib.version_compare(self.version, self._C2X_VERSION): + stds += ['c2x'] + if mesonlib.version_compare(self.version, self._C23_VERSION): + stds += ['c23'] + if mesonlib.version_compare(self.version, self._C2Y_VERSION): + stds += ['c2y'] + key = self.form_compileropt_key('std') + std_opt = opts[key] + assert isinstance(std_opt, UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) + return opts From 30ca64a25b966c7204325e0338afbbb2c6a64d53 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 10:04:32 -0700 Subject: [PATCH 219/624] compilers/objc: Use Shared GNU C standard handling --- mesonbuild/compilers/objc.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index de23e70cf835..c563933b4a69 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -11,7 +11,7 @@ from .compilers import Compiler from .mixins.clang import ClangCompiler, ClangCStds from .mixins.clike import CLikeCompiler -from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args +from .mixins.gnu import GnuCompiler, GnuCStds, gnu_common_warning_args, gnu_objc_warning_args if T.TYPE_CHECKING: from .. import coredata @@ -56,7 +56,7 @@ def form_compileropt_key(self, basename: str) -> OptionKey: return super().form_compileropt_key(basename) -class GnuObjCCompiler(GnuCompiler, ObjCCompiler): +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', defines: T.Optional[T.Dict[str, str]] = None, @@ -74,6 +74,13 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} + def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: + args = [] + std = options.get_value(self.form_compileropt_key('std')) + if std != 'none': + args.append('-std=' + std) + return args + class ClangObjCCompiler(ClangCStds, ClangCompiler, ObjCCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, From 4f314baaf638d6a566fa295b8779b4cf3ba6d96f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 10:08:49 -0700 Subject: [PATCH 220/624] compilers/gnu: Split Gnu C++ standard handling into a mixin class So we can re-use it in the ObjC++ standards --- mesonbuild/compilers/cpp.py | 17 +++-------------- mesonbuild/compilers/mixins/gnu.py | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index e051050e236c..9626aacebf5c 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -25,7 +25,7 @@ from .mixins.ti import TICompiler from .mixins.arm import ArmCompiler, ArmclangCompiler from .mixins.visualstudio import MSVCCompiler, ClangClCompiler -from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_cpp_warning_args +from .mixins.gnu import GnuCompiler, GnuCPPStds, gnu_common_warning_args, gnu_cpp_warning_args from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler, ClangCPPStds from .mixins.elbrus import ElbrusCompiler @@ -417,7 +417,7 @@ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: return [] -class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler): +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, @@ -437,7 +437,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': key = self.form_compileropt_key('std') - opts = CPPCompiler.get_options(self) + opts = super().get_options() self.update_options( opts, self.create_option(options.UserComboOption, @@ -454,17 +454,6 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 'STL debug mode', False), ) - cppstd_choices = [ - 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', - 'c++2a', 'c++20', - ] - if version_compare(self.version, '>=11.0.0'): - cppstd_choices.append('c++23') - if version_compare(self.version, '>=14.0.0'): - cppstd_choices.append('c++26') - std_opt = opts[key] - assert isinstance(std_opt, options.UserStdOption), 'for mypy' - std_opt.set_versions(cppstd_choices, gnu=True) if self.info.is_windows() or self.info.is_cygwin(): self.update_options( opts, diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 1f78ef89d542..4dc344519a08 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -657,3 +657,28 @@ def get_options(self) -> MutableKeyedOptionDictType: assert isinstance(std_opt, UserStdOption), 'for mypy' std_opt.set_versions(stds, gnu=True) return opts + + +class GnuCPPStds(Compiler): + + """Mixin class for GNU based compilers for setting CPP standards.""" + + _CPP23_VERSION = '>=11.0.0' + _CPP26_VERSION = '>=14.0.0' + + def get_options(self) -> MutableKeyedOptionDictType: + opts = super().get_options() + + stds = [ + 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', + 'c++2a', 'c++20', + ] + if mesonlib.version_compare(self.version, self._CPP23_VERSION): + stds.append('c++23') + if mesonlib.version_compare(self.version, self._CPP26_VERSION): + stds.append('c++26') + key = self.form_compileropt_key('std') + std_opt = opts[key] + assert isinstance(std_opt, UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) + return opts From d650f6eaa57fed940a65b1fa5c15286dc7200f96 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 10:10:52 -0700 Subject: [PATCH 221/624] compilers/objcpp: Use the GnuCPPStdMixin for ObjC++ --- mesonbuild/compilers/objcpp.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index 9fd1196904cd..c7af84b7945b 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -9,7 +9,7 @@ from .cpp import ALL_STDS from .compilers import Compiler -from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args +from .mixins.gnu import GnuCompiler, GnuCPPStds, gnu_common_warning_args, gnu_objc_warning_args from .mixins.clang import ClangCompiler, ClangCPPStds from .mixins.clike import CLikeCompiler @@ -56,7 +56,7 @@ def get_options(self) -> coredata.MutableKeyedOptionDictType: return opts -class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): +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', defines: T.Optional[T.Dict[str, str]] = None, @@ -74,6 +74,13 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} + def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: + args = [] + std = options.get_value(self.form_compileropt_key('std')) + if std != 'none': + args.append('-std=' + std) + return args + class ClangObjCPPCompiler(ClangCPPStds, ClangCompiler, ObjCPPCompiler): From fa7716c3005897c52126c086195c85754a1a0e66 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 25 Sep 2024 09:43:16 -0700 Subject: [PATCH 222/624] compilers: move Apple C Std version handling to a mixin To avoid duplication between C and ObjC --- mesonbuild/compilers/c.py | 8 ++------ mesonbuild/compilers/mixins/apple.py | 11 ++++++++++- mesonbuild/compilers/objc.py | 7 ++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 70f4dc70e76d..8379f81bac70 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -11,7 +11,7 @@ from .. import mlog from ..mesonlib import MesonException, version_compare from .c_function_attributes import C_FUNC_ATTRIBUTES -from .mixins.apple import AppleCompilerMixin +from .mixins.apple import AppleCompilerMixin, AppleCStdsMixin from .mixins.clike import CLikeCompiler from .mixins.ccrx import CcrxCompiler from .mixins.xc16 import Xc16Compiler @@ -157,7 +157,7 @@ class ArmLtdClangCCompiler(ClangCCompiler): id = 'armltdclang' -class AppleClangCCompiler(AppleCompilerMixin, ClangCCompiler): +class AppleClangCCompiler(AppleCompilerMixin, AppleCStdsMixin, ClangCCompiler): """Handle the differences between Apple Clang and Vanilla Clang. @@ -165,10 +165,6 @@ class AppleClangCCompiler(AppleCompilerMixin, ClangCCompiler): C standards were added. """ - _C17_VERSION = '>=10.0.0' - _C18_VERSION = '>=11.0.0' - _C2X_VERSION = '>=11.0.0' - class EmscriptenCCompiler(EmscriptenMixin, ClangCCompiler): diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py index 6ec9239522fe..1056e774969e 100644 --- a/mesonbuild/compilers/mixins/apple.py +++ b/mesonbuild/compilers/mixins/apple.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2024 Intel Corporation +# Copyright © 2024-2025 Intel Corporation """Provides mixins for Apple compilers.""" @@ -59,3 +59,12 @@ def openmp_link_flags(self, env: Environment) -> T.List[str]: def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]: # The objects are prelinked through the compiler, which injects -lSystem return [prelink_name], ['-nostdlib', '-r', '-o', prelink_name] + obj_list + + +class AppleCStdsMixin(Compiler): + + """Provide version overrides for the Apple Compilers.""" + + _C17_VERSION = '>=10.0.0' + _C18_VERSION = '>=11.0.0' + _C2X_VERSION = '>=11.0.0' diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index c563933b4a69..56bc12d45feb 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -9,6 +9,7 @@ from .c import ALL_STDS from .compilers import Compiler +from .mixins.apple import AppleCStdsMixin from .mixins.clang import ClangCompiler, ClangCStds from .mixins.clike import CLikeCompiler from .mixins.gnu import GnuCompiler, GnuCStds, gnu_common_warning_args, gnu_objc_warning_args @@ -105,10 +106,6 @@ def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T. args.append('-std=' + std) return args -class AppleClangObjCCompiler(ClangObjCCompiler): +class AppleClangObjCCompiler(AppleCStdsMixin, ClangObjCCompiler): """Handle the differences between Apple's clang and vanilla clang.""" - - _C17_VERSION = '>=10.0.0' - _C18_VERSION = '>=11.0.0' - _C2X_VERSION = '>=11.0.0' From 19b67fbf29faa3d4a90e05722501be128f6ddbd0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 25 Sep 2024 09:45:21 -0700 Subject: [PATCH 223/624] compilers: split Apple C++ version handling to a mixin --- mesonbuild/compilers/cpp.py | 8 +++----- mesonbuild/compilers/mixins/apple.py | 8 ++++++++ mesonbuild/compilers/objcpp.py | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 9626aacebf5c..46e1ea01b0ce 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -19,7 +19,7 @@ CompileCheckMode, ) from .c_function_attributes import CXX_FUNC_ATTRIBUTES, C_FUNC_ATTRIBUTES -from .mixins.apple import AppleCompilerMixin +from .mixins.apple import AppleCompilerMixin, AppleCPPStdsMixin from .mixins.clike import CLikeCompiler from .mixins.ccrx import CcrxCompiler from .mixins.ti import TICompiler @@ -325,10 +325,8 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): id = 'armltdclang' -class AppleClangCPPCompiler(AppleCompilerMixin, ClangCPPCompiler): - - _CPP23_VERSION = '>=13.0.0' - _CPP26_VERSION = '>=16.0.0' +class AppleClangCPPCompiler(AppleCompilerMixin, AppleCPPStdsMixin, ClangCPPCompiler): + pass class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): diff --git a/mesonbuild/compilers/mixins/apple.py b/mesonbuild/compilers/mixins/apple.py index 1056e774969e..2a0939334f85 100644 --- a/mesonbuild/compilers/mixins/apple.py +++ b/mesonbuild/compilers/mixins/apple.py @@ -68,3 +68,11 @@ class AppleCStdsMixin(Compiler): _C17_VERSION = '>=10.0.0' _C18_VERSION = '>=11.0.0' _C2X_VERSION = '>=11.0.0' + + +class AppleCPPStdsMixin(Compiler): + + """Provide version overrides for the Apple C++ Compilers.""" + + _CPP23_VERSION = '>=13.0.0' + _CPP26_VERSION = '>=16.0.0' diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index c7af84b7945b..de968be42954 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -9,6 +9,7 @@ from .cpp import ALL_STDS from .compilers import Compiler +from .mixins.apple import AppleCPPStdsMixin from .mixins.gnu import GnuCompiler, GnuCPPStds, gnu_common_warning_args, gnu_objc_warning_args from .mixins.clang import ClangCompiler, ClangCPPStds from .mixins.clike import CLikeCompiler @@ -107,6 +108,6 @@ def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T. return args -class AppleClangObjCPPCompiler(ClangObjCPPCompiler): +class AppleClangObjCPPCompiler(AppleCPPStdsMixin, ClangObjCPPCompiler): """Handle the differences between Apple's clang and vanilla clang.""" From 18331db7c1b32e6a8771f0f058c1dd8fce52a1b7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 12 Sep 2024 09:12:44 -0700 Subject: [PATCH 224/624] unit tests: make the test_c_cpp_stds test more robust Check clang-cl as well as cl, and clang as well as gcc. --- unittests/allplatformstests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 96576b0ee888..d5c126e5016c 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5055,7 +5055,7 @@ def test_c_cpp_stds(self): self.setconf('-Dc_std=c++11') env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) - if cc.get_id() == 'msvc': + if cc.get_id() in {'msvc', 'clang-cl'}: # default_option should have selected those self.assertEqual(self.getconf('c_std'), 'c89') self.assertEqual(self.getconf('cpp_std'), 'vc++11') @@ -5068,7 +5068,7 @@ def test_c_cpp_stds(self): # The first supported std should be selected self.setconf('-Dcpp_std=gnu++11,vc++11,c++11') self.assertEqual(self.getconf('cpp_std'), 'vc++11') - elif cc.get_id() == 'gcc': + elif cc.get_id() == 'gcc' or (cc.get_id() == 'clang' and not is_windows()): # default_option should have selected those self.assertEqual(self.getconf('c_std'), 'gnu89') self.assertEqual(self.getconf('cpp_std'), 'gnu++98') From d6e54b499cb2b7a533e6b1ad97637b8155c0a66c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 12 Sep 2024 09:27:31 -0700 Subject: [PATCH 225/624] unit tests: Test ObjC and ObjC++ as well as C and C++ This tests ObjC and ObjC++ both with and without C enabled. I did this because I ran into issues where ObjC only worked when C was enabled, and then a later bug where C was disabled, due to the fact that C and ObjC both use `c_std` and not `objc_std`. --- test cases/unit/116 c cpp stds/meson.build | 21 +++++++++--- test cases/unit/116 c cpp stds/meson.options | 5 +++ unittests/allplatformstests.py | 35 +++++++++++++++++--- 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 test cases/unit/116 c cpp stds/meson.options diff --git a/test cases/unit/116 c cpp stds/meson.build b/test cases/unit/116 c cpp stds/meson.build index 0b15efc08d27..fb68af610008 100644 --- a/test cases/unit/116 c cpp stds/meson.build +++ b/test cases/unit/116 c cpp stds/meson.build @@ -1,6 +1,17 @@ -project('c cpp stds', 'c', 'cpp', - default_options: [ - 'c_std=gnu89,c89', - 'cpp_std=gnu++98,vc++11', - ], +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + +project( + 'c cpp stds', + default_options: [ + 'c_std=gnu89,c89', + 'cpp_std=gnu++98,vc++11', + ], ) + +if get_option('with-c') + add_languages('c', 'cpp', native : false) +endif +if get_option('with-objc') + add_languages('objc', 'objcpp', native : false) +endif diff --git a/test cases/unit/116 c cpp stds/meson.options b/test cases/unit/116 c cpp stds/meson.options new file mode 100644 index 000000000000..7040758810c1 --- /dev/null +++ b/test cases/unit/116 c cpp stds/meson.options @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + +option('with-c', type : 'boolean', value : false) +option('with-objc', type : 'boolean', value : false) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index d5c126e5016c..7c2d3ba61ac3 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5043,9 +5043,11 @@ def test_configure_same_noop(self): olddata = newdata oldmtime = newmtime - def test_c_cpp_stds(self): + def __test_multi_stds(self, test_c: bool = True, test_objc: bool = False) -> None: + assert test_c or test_objc, 'must test something' testdir = os.path.join(self.unit_test_dir, '116 c cpp stds') - self.init(testdir) + self.init(testdir, extra_args=[f'-Dwith-c={str(test_c).lower()}', + f'-Dwith-objc={str(test_objc).lower()}']) # Invalid values should fail whatever compiler we have with self.assertRaises(subprocess.CalledProcessError): self.setconf('-Dc_std=invalid') @@ -5054,7 +5056,19 @@ def test_c_cpp_stds(self): with self.assertRaises(subprocess.CalledProcessError): self.setconf('-Dc_std=c++11') env = get_fake_env() - cc = detect_c_compiler(env, MachineChoice.HOST) + if test_c: + cc = detect_c_compiler(env, MachineChoice.HOST) + if test_objc: + objc = detect_compiler_for(env, 'objc', MachineChoice.HOST, True, '') + assert objc is not None + if test_c and cc.get_argument_syntax() != objc.get_argument_syntax(): + # The test doesn't work correctly in this case because we can + # end up with incompatible stds, like gnu89 with cl.exe for C + # and clang.exe for ObjC + return + if not test_c: + cc = objc + if cc.get_id() in {'msvc', 'clang-cl'}: # default_option should have selected those self.assertEqual(self.getconf('c_std'), 'c89') @@ -5068,7 +5082,7 @@ def test_c_cpp_stds(self): # The first supported std should be selected self.setconf('-Dcpp_std=gnu++11,vc++11,c++11') self.assertEqual(self.getconf('cpp_std'), 'vc++11') - elif cc.get_id() == 'gcc' or (cc.get_id() == 'clang' and not is_windows()): + elif cc.get_id() in {'gcc', 'clang'}: # default_option should have selected those self.assertEqual(self.getconf('c_std'), 'gnu89') self.assertEqual(self.getconf('cpp_std'), 'gnu++98') @@ -5076,6 +5090,19 @@ def test_c_cpp_stds(self): self.setconf('-Dcpp_std=c++11,gnu++11,vc++11') self.assertEqual(self.getconf('cpp_std'), 'c++11') + def test_c_cpp_stds(self) -> None: + self.__test_multi_stds() + + @skip_if_not_language('objc') + @skip_if_not_language('objcpp') + def test_objc_objcpp_stds(self) -> None: + self.__test_multi_stds(test_c=False, test_objc=True) + + @skip_if_not_language('objc') + @skip_if_not_language('objcpp') + def test_c_cpp_objc_objcpp_stds(self) -> None: + self.__test_multi_stds(test_objc=True) + def test_rsp_support(self): env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) From 1eaab0253bea69432d69a1eddab377fbbd9ed74d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 25 Sep 2024 10:28:37 -0700 Subject: [PATCH 226/624] compilers: Check if GCC has support for ObjC and/or ObjC++ Since this is optional, we should not accept that GCC is a valid ObjC or G++ is a valid ObjC++ Compiler unless we've tested that they can actually do a basic compile. This requires fixing a number of tests that have broken assumptions. In some cases I've split tests where issues with one language would hide the other. It would be great if we had a competent test framework that allowed subtests to skip, unfortunately we have python's unittest instead. Because of that we can't avoid extra tests by use of subtests. --- mesonbuild/compilers/detect.py | 6 +++++- unittests/failuretests.py | 19 +++++++++++++------ unittests/machinefiletests.py | 16 +++++++++++++--- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 7bd48d10c320..d4ad4badeefd 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -897,9 +897,13 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine: version = _get_gnu_version_from_defines(defines) comp = objc.GnuObjCCompiler if lang == 'objc' else objcpp.GnuObjCPPCompiler linker = guess_nix_linker(env, compiler, comp, version, for_machine) - return comp( + c = comp( ccache, compiler, version, for_machine, is_cross, info, defines, linker=linker) + if not c.compiles('int main(void) { return 0; }', env)[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 if 'clang' in out: linker = None defines = _get_clang_compiler_defines(compiler, lang) diff --git a/unittests/failuretests.py b/unittests/failuretests.py index 0dd6c5f650cf..18d0c5e70b47 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -241,19 +241,26 @@ def test_dependency_invalid_method(self): ''' self.assertMesonRaises(code, ".* is not a config-tool dependency") - def test_objc_cpp_detection(self): + def test_objc_detection(self) -> None: ''' Test that when we can't detect objc or objcpp, we fail gracefully. ''' env = get_fake_env() try: detect_objc_compiler(env, MachineChoice.HOST) + except EnvironmentException as e: + self.assertRegex(str(e), r"(Unknown compiler|GCC was not built with support)") + else: + raise unittest.SkipTest('Working objective-c Compiler found, cannot test error.') + + def test_objcpp_detection(self) -> None: + env = get_fake_env() + try: detect_objcpp_compiler(env, MachineChoice.HOST) - except EnvironmentException: - code = "add_languages('objc')\nadd_languages('objcpp')" - self.assertMesonRaises(code, "Unknown compiler") - return - raise unittest.SkipTest("objc and objcpp found, can't test detection failure") + except EnvironmentException as e: + self.assertRegex(str(e), r"(Unknown compiler|GCC was not built with support)") + else: + raise unittest.SkipTest('Working objective-c++ Compiler found, cannot test error.') def test_subproject_variables(self): ''' diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 803a1797df2f..e71cd04fe3c0 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -23,7 +23,7 @@ import mesonbuild.environment import mesonbuild.coredata import mesonbuild.modules.gnome - +from mesonbuild import mesonlib from mesonbuild import machinefile from mesonbuild.mesonlib import ( @@ -275,7 +275,12 @@ def cb(comp): if not is_real_gnu_compiler(shutil.which('gcc')): raise SkipTest('Only one compiler found, cannot test.') return 'gcc', 'gcc' - self.helper_for_compiler('objc', cb) + try: + self.helper_for_compiler('objc', cb) + except mesonlib.EnvironmentException as e: + if 'GCC was not built with support for objective-c' in str(e): + raise unittest.SkipTest("GCC doesn't support objective-c, test cannot run") + raise @skip_if_not_language('objcpp') @skip_if_env_set('OBJCXX') @@ -288,7 +293,12 @@ def cb(comp): if not is_real_gnu_compiler(shutil.which('g++')): raise SkipTest('Only one compiler found, cannot test.') return 'g++', 'gcc' - self.helper_for_compiler('objcpp', cb) + try: + self.helper_for_compiler('objcpp', cb) + except mesonlib.EnvironmentException as e: + if 'GCC was not built with support for objective-c++' in str(e): + raise unittest.SkipTest("G++ doesn't support objective-c++, test cannot run") + raise @skip_if_not_language('d') @skip_if_env_set('DC') From c31963ca80997006347619fb07ee28ee9dd1de49 Mon Sep 17 00:00:00 2001 From: insunaa Date: Fri, 24 Jan 2025 22:30:22 +0100 Subject: [PATCH 227/624] compilers/clang++: Add `-fpch-instantiate-templates` to speed up clang++ builds - see https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5168 --- mesonbuild/compilers/cpp.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index d2eca897b6ae..73877a9b22ab 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -332,6 +332,12 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: return [] + def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]: + args = super().get_pch_use_args(pch_dir, header) + if version_compare(self.version, '>=11'): + return ['-fpch-instantiate-templates'] + args + return args + class ArmLtdClangCPPCompiler(ClangCPPCompiler): From d51e20288853f376e9244671604176913f2e4e79 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 27 Jan 2025 16:40:19 -0500 Subject: [PATCH 228/624] fix edge case in testcase expect_error() if error is printed using mlog It catches the exception message itself, but for multi-line exceptions it may be worth print an error() as well as raising, to communicate multiple bits of information. When using the VS backend, this means that we get an actual `ERROR: ...` printed during a successful run, which then breaks msbuild as msbuild parses stdout of successful commands, regexes them for the word "error:" and interprets that as... an error. So a meson project tests example that uses testcase expect_error() and then successfully configures and builds, fails to successfully `meson --internal regenerate`. Sneak around this by doing our own pattern replace to evade msbuild. There is probably a way to tell msbuild to stop doing this, but that would require me understanding the vs backend well enough to patch the xml it generates. No thanks... --- mesonbuild/interpreter/interpreter.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 806a737ea673..c7aef9329bcd 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -4,8 +4,7 @@ from __future__ import annotations -import hashlib -import traceback +import hashlib, io, sys, traceback from .. import mparser from .. import environment @@ -1474,10 +1473,18 @@ def func_expect_error(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: class ExpectErrorObject(ContextManagerObject): def __init__(self, msg: str, how: str, subproject: str) -> None: super().__init__(subproject) + self.old_stdout = sys.stdout + sys.stdout = self.new_stdout = io.StringIO() self.msg = msg self.how = how def __exit__(self, exc_type, exc_val, exc_tb): + sys.stdout = self.old_stdout + for l in self.new_stdout.getvalue().splitlines(): + if 'ERROR:' in l: + print(l.replace('ERROR', 'ERROR (msbuild proof)')) + else: + print(l) if exc_val is None: raise InterpreterException('Expecting an error but code block succeeded') if isinstance(exc_val, mesonlib.MesonException): From 8a9dea2e3745935d55201123288e529acede043e Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 27 Jan 2025 11:20:54 -0500 Subject: [PATCH 229/624] When subproject() fails because downloading is disabled, say what failed It's not especially explanatory to say: ``` meson.build:357:34: ERROR: Automatic wrap-based subproject downloading is disabled ``` But if we instead say this: ``` ERROR: Subproject libsamplerate is buildable: NO meson.build:357:34: ERROR: Automatic wrap-based subproject downloading is disabled ``` It becomes a lot clearer to casual inspection, why it failed. And it matches the way we otherwise report errors for an unbuildable subproject (configure errors). Bug: https://github.com/jacktrip/jacktrip/issues/1380 --- mesonbuild/interpreter/interpreter.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c7aef9329bcd..d05594cbd309 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -916,10 +916,16 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth try: subdir, method = r.resolve(subp_name, force_method) except wrap.WrapException as e: + if force_method is not None: + prefix = force_method.title() + ' subproject' + else: + prefix = 'Subproject' + msg = [prefix, mlog.bold(subp_name), 'is buildable:', mlog.red('NO')] if not required: mlog.log(e) - mlog.log('Subproject ', mlog.bold(subp_name), 'is buildable:', mlog.red('NO'), '(disabling)') + mlog.log(*msg, '(disabling)') return self.disabled_subproject(subp_name, exception=e) + mlog.error(*msg) raise e os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) From b2456ad8bf6857b28c9dc6b5c22570bd43d6e1f0 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 28 Jan 2025 17:47:03 +0900 Subject: [PATCH 230/624] docs: clarify behavior of default_both_libraries option Signed-off-by: Daiki Ueno --- docs/markdown/Builtin-options.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index d64cd85c0fab..ffbab47d879d 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -183,11 +183,16 @@ fails. #### Details for `default_both_libraries` -Since `1.6.0`, you can select the default type of library selected when using -a `both_libraries` object. This can be either 'shared' (default value, compatible -with previous meson versions), 'static', or 'auto'. With auto, the value from -`default_library` option is used, unless it is 'both', in which case 'shared' -is used instead. +Since `1.6.0`, you can specify the default type of library selected when using a +`both_libraries` object with `default_both_libraries`. Note that, unlike +`default_library`, this option does not affect how the library artifacts are +built, but how they are internally linked to the dependent targets within the +same project. + +The possible values of this option are 'shared' (default value, compatible with +previous meson versions), 'static', and 'auto'. With auto, the value from the +`default_library` option is used, unless it is 'both', in which case 'shared' is +used instead. When `default_both_libraries` is 'auto', passing a [[@both_libs]] dependency in [[both_libraries]] will link the static dependency with the static lib, From 1ae7dd1290f768c18e7fb6661f7a669ec0790e18 Mon Sep 17 00:00:00 2001 From: Aditya Vidyadhar Kamath Date: Mon, 27 Jan 2025 05:36:03 -0600 Subject: [PATCH 231/624] Fix to Meson failed to archive shared libraries in AIX. Closes mesonbuild#14185. --- mesonbuild/backend/ninjabackend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cf9309e258e3..58d2e8fae2e4 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3296,6 +3296,12 @@ def get_target_shsym_filename(self, target): def generate_shsym(self, target) -> None: 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() + target.get_outputs()[0] = linker.get_archive_name(target.get_outputs()[0]) + target_file = target.get_outputs()[0] + target_file = os.path.join(self.get_target_dir(target), target_file) symname = self.get_target_shsym_filename(target) elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_file) # The library we will actually link to, which is an import library on Windows (not the DLL) From 3a96b8b03a0b20c0ae36fe83b8a9dd78d0d741c8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Dec 2024 13:56:19 -0800 Subject: [PATCH 232/624] dependencies/detect: make assertions more useful --- mesonbuild/dependencies/detect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/dependencies/detect.py b/mesonbuild/dependencies/detect.py index faf024de9c31..aa62c661df21 100644 --- a/mesonbuild/dependencies/detect.py +++ b/mesonbuild/dependencies/detect.py @@ -59,10 +59,10 @@ def get_dep_identifier(name: str, kwargs: T.Dict[str, T.Any]) -> 'TV_DepID': # All keyword arguments are strings, ints, or lists (or lists of lists) if isinstance(value, list): for i in value: - assert isinstance(i, str) + assert isinstance(i, str), i value = tuple(frozenset(listify(value))) else: - assert isinstance(value, (str, bool, int)) + assert isinstance(value, (str, bool, int)), value identifier = (*identifier, (key, value),) return identifier From cdbd33a4f592166761e02a2054cecdadaacbef60 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Sun, 8 Dec 2024 19:56:58 -0800 Subject: [PATCH 233/624] modules/python: Make sure that all dependency candidates meet interface Because in at least one case it will lack the `log_tried` method. --- mesonbuild/dependencies/python.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index 326e605d8543..c978ad62e82e 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -417,6 +417,9 @@ def set_env(name: str, value: str) -> None: 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. From a47e8f260a7d179462abf7ef607472de44f2f0c5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 10 Dec 2024 08:46:06 -0800 Subject: [PATCH 234/624] dependencies: correctly handle a language of None For whatever reason Meson has always used None == . This doesn't make a lot of sense to me, but it's how things currently work, and our dependency factories should handle that correctly. --- mesonbuild/dependencies/misc.py | 4 +++- mesonbuild/dependencies/mpi.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 7bfe198e0179..8fdf619f0c52 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -29,7 +29,9 @@ def netcdf_factory(env: 'Environment', for_machine: 'mesonlib.MachineChoice', kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']: - language = kwargs.get('language', 'c') + language = kwargs.get('language') + if language is None: + language = 'c' if language not in ('c', 'cpp', 'fortran'): raise DependencyException(f'Language {language} is not supported with NetCDF.') diff --git a/mesonbuild/dependencies/mpi.py b/mesonbuild/dependencies/mpi.py index 2d9e992f6f5a..a259972b0586 100644 --- a/mesonbuild/dependencies/mpi.py +++ b/mesonbuild/dependencies/mpi.py @@ -27,7 +27,9 @@ def mpi_factory(env: 'Environment', for_machine: 'MachineChoice', kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']: - language = kwargs.get('language', 'c') + language = kwargs.get('language') + if language is None: + language = 'c' if language not in {'c', 'cpp', 'fortran'}: # OpenMPI doesn't work without any other languages return [] From 88d8a3a425dc6af66ca01374d901376502bb9f55 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 9 Dec 2024 14:49:02 -0800 Subject: [PATCH 235/624] dependencies: version_compare -> version_compare_many It's possible to get an array of versions here, so we need to handle that. --- mesonbuild/dependencies/dev.py | 2 +- mesonbuild/dependencies/misc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 94f51ff69b12..2725a7bb4e88 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -584,7 +584,7 @@ def __init__(self, environment: 'Environment', kwargs: JNISystemDependencyKW): self.is_found = False return - if 'version' in kwargs and not version_compare(self.version, kwargs['version']): + if 'version' in kwargs 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 diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 8fdf619f0c52..6a2a73f2f0e8 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -392,7 +392,7 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]): req = kwargs.get('version') if req: if self.version: - self.is_found = mesonlib.version_compare(self.version, req) + self.is_found, *_ = mesonlib.version_compare_many(self.version, req) else: mlog.warning('Cannot determine version of curses to compare against.') From 7c625f80d8f381b254a120689499ddd13dcdd02d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 13 Dec 2024 12:07:44 -0800 Subject: [PATCH 236/624] dependencies/dub: Fix arguments passed as strings that should be bools --- mesonbuild/dependencies/dub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index 1c904ab2a5af..009e4f46ffbb 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -328,7 +328,7 @@ def find_package_target(pkg: DubPackDesc) -> bool: for lib in bs['libs']: if os.name != 'nt': # trying to add system libraries by pkg-config - pkgdep = PkgConfigDependency(lib, environment, {'required': 'true', 'silent': 'true'}) + pkgdep = PkgConfigDependency(lib, environment, {'required': True, 'silent': True}) if pkgdep.is_found: for arg in pkgdep.get_compile_args(): self.compile_args.append(arg) From 0b505fcab826cc372e2c8b368f68ff366c1bab84 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 28 Jan 2025 13:44:50 -0500 Subject: [PATCH 237/624] testcase expect_error(): add missing line from rebase It was supposed to preserve the color for debugging meson project tests! Fixes: d51e20288853f376e9244671604176913f2e4e79 --- mesonbuild/interpreter/interpreter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d05594cbd309..6fa569555bf8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1481,6 +1481,7 @@ def __init__(self, msg: str, how: str, subproject: str) -> None: super().__init__(subproject) self.old_stdout = sys.stdout sys.stdout = self.new_stdout = io.StringIO() + sys.stdout.colorize_console = getattr(self.old_stdout, 'colorize_console', None) self.msg = msg self.how = how From 7a40db5a4f68ab2d68dbecd8bdb6570432b76acb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 28 Jan 2025 10:14:57 -0800 Subject: [PATCH 238/624] tests: disable vala generated test on cygwin Which doesn't work with the packaged version of Vala --- test cases/vala/11 generated vapi/meson.build | 4 ++++ test cases/vala/11 generated vapi/test.json | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test cases/vala/11 generated vapi/meson.build b/test cases/vala/11 generated vapi/meson.build index d5f38cad6abd..9e1303d3a8d4 100644 --- a/test cases/vala/11 generated vapi/meson.build +++ b/test cases/vala/11 generated vapi/meson.build @@ -1,5 +1,9 @@ project('vapi-test', ['c', 'vala']) +if host_machine.system() == 'cygwin' + error('MESON_SKIP_TEST Does not work with the Vala currently packaged in cygwin') +endif + gnome = import('gnome') subdir('libfoo') subdir('libbar') diff --git a/test cases/vala/11 generated vapi/test.json b/test cases/vala/11 generated vapi/test.json index 1a742aa89f31..1407afffb2b8 100644 --- a/test cases/vala/11 generated vapi/test.json +++ b/test cases/vala/11 generated vapi/test.json @@ -9,5 +9,6 @@ {"type": "file", "file": "usr/share/vala/vapi/foo-1.0.deps"}, {"type": "file", "file": "usr/share/vala/vapi/bar-1.0.vapi"}, {"type": "file", "file": "usr/share/vala/vapi/bar-1.0.deps"} - ] + ], + "expect_skip_on_jobname": ["cygwin"] } From 7bcf38de60afd5ef9f8bc52ea67e6cb693c3d8a3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 28 Jan 2025 10:18:19 -0800 Subject: [PATCH 239/624] tests: enable gir static lib on cygwin Where it is now working. --- test cases/frameworks/34 gir static lib/test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/frameworks/34 gir static lib/test.json b/test cases/frameworks/34 gir static lib/test.json index c2ba9b2a886e..f6b50c380e20 100644 --- a/test cases/frameworks/34 gir static lib/test.json +++ b/test cases/frameworks/34 gir static lib/test.json @@ -5,5 +5,5 @@ {"type": "file", "platform": "cygwin", "file": "usr/lib/libgirlib.dll.a"}, {"type": "file", "file": "usr/share/gir-1.0/Meson-1.0.gir"} ], - "expect_skip_on_jobname": ["azure", "bionic", "cygwin", "macos", "msys2"] + "expect_skip_on_jobname": ["azure", "bionic", "macos", "msys2"] } From 57c5d00dcdd70bed47c9990dfc6a59026bed03f6 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 16 Jan 2025 10:45:22 -0500 Subject: [PATCH 240/624] Detect files with trailing space On Windows, if you accidently add a space at the end of a file name, like `files('myfile.txt ')`, the file is not reported as missing, because of the normalization performed by the OS. However, ninja will reference it with the trailing space, and will fail because the file does not exist. See https://github.com/python/cpython/issues/115104 for reference. --- mesonbuild/interpreter/interpreter.py | 2 ++ mesonbuild/utils/universal.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6fa569555bf8..66ea24cf3468 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3204,6 +3204,8 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool results: T.List['SourceOutputs'] = [] for s in sources: if isinstance(s, str): + if s.endswith(' '): + raise MesonException(f'{s!r} ends with a space. This is probably an error.') if not strict and s.startswith(self.environment.get_build_dir()): results.append(s) mlog.warning(f'Source item {s!r} cannot be converted to File object, because it is a generated file. ' diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 3ec23e1056d0..edc7e3a243be 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -398,7 +398,7 @@ def __repr__(self) -> str: @staticmethod @lru_cache(maxsize=None) - def from_source_file(source_root: str, subdir: str, fname: str) -> 'File': + def from_source_file(source_root: str, subdir: str, fname: str) -> File: if not os.path.isfile(os.path.join(source_root, subdir, fname)): raise MesonException(f'File {fname} does not exist.') return File(False, subdir, fname) From ae1bb2f87b88a66591a152038f4271b83d575a92 Mon Sep 17 00:00:00 2001 From: Steve Lhomme Date: Mon, 27 Jan 2025 12:52:23 +0100 Subject: [PATCH 241/624] symbolextractor: use -nologo with lib.exe and llvm-lib.exe It should be possible to just use the first line rather than the last. --- mesonbuild/scripts/symbolextractor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index 5c45253d5702..f4d75a48bffd 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -207,7 +207,7 @@ def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str]: # var which is the list of library paths MSVC will search for import # libraries while linking. for lib in (['lib'], get_tool('llvm-lib')): - output, e = call_tool_nowarn(lib + ['-list', impfilename]) + output, e = call_tool_nowarn(lib + ['-list', '-nologo', impfilename]) if output: # The output is a list of DLLs that each symbol exported by the import # library is available in. We only build import libraries that point to From 01584101b7e66c37fa5963eb7d7057e3301ea06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Brzezi=C5=84ski?= Date: Mon, 1 Jul 2024 18:55:13 +0200 Subject: [PATCH 242/624] swift: Fix duplicate SDK include paths causing a compile error Some dependencies can bring include paths pointing to older macOS SDK's. In this case, it was libffi pointing to SDK from 12.0. When the Foundation framework is imported in Swift, swiftc attempts to import the FFI module from the most recent version of the SDK, which causes a compilation error because of conflicting definitions between the two SDK versions. SwiftPM also had this problem: https://github.com/swiftlang/swift-package-manager/pull/6772 The solution on our side is a simplified version of what SwiftPM did. Let's naively look for .sdk paths in the compile args of our dependencies and replace them with the most recent one. I included a test which is confirmed to fail without the workaround added in this patch. This was not tested on anything else than macOS, but I don't expect it to make the situation worse in any case. --- mesonbuild/compilers/swift.py | 34 +++++++++++++++++-- .../swift/9 sdk path from dep/foo.swift | 4 +++ .../swift/9 sdk path from dep/meson.build | 12 +++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test cases/swift/9 sdk path from dep/foo.swift create mode 100644 test cases/swift/9 sdk path from dep/meson.build diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index a2525f927f28..d9268823185b 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -3,14 +3,17 @@ from __future__ import annotations +import re import subprocess, os.path import typing as T -from ..mesonlib import EnvironmentException - +from .. import mlog +from ..mesonlib import EnvironmentException, MesonException from .compilers import Compiler, clike_debug_args + if T.TYPE_CHECKING: + from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker @@ -39,6 +42,17 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic is_cross=is_cross, full_version=full_version, linker=linker) self.version = version + if self.info.is_darwin(): + try: + self.sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'], + universal_newlines=True, + encoding='utf-8', stderr=subprocess.STDOUT).strip() + except subprocess.CalledProcessError as e: + mlog.error("Failed to get Xcode SDK path: " + e.output) + raise MesonException('Xcode license not accepted yet. Run `sudo xcodebuild -license`.') + except FileNotFoundError: + mlog.error('xcrun not found. Install Xcode to compile Swift code.') + raise MesonException('Could not detect Xcode. Please install it to compile Swift code.') def get_pic_args(self) -> T.List[str]: return [] @@ -55,6 +69,22 @@ def get_werror_args(self) -> T.List[str]: def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: return ['-emit-dependencies'] + def get_dependency_compile_args(self, dep: Dependency) -> T.List[str]: + args = dep.get_compile_args() + # Some deps might sneak in a hardcoded path to an older macOS SDK, which can + # cause compilation errors. Let's replace all .sdk paths with the current one. + # SwiftPM does it this way: https://github.com/swiftlang/swift-package-manager/pull/6772 + # Not tested on anything else than macOS for now. + if not self.info.is_darwin(): + return args + pattern = re.compile(r'.*\/MacOSX[^\/]*\.sdk(\/.*|$)') + for i, arg in enumerate(args): + if arg.startswith('-I'): + match = pattern.match(arg) + if match: + args[i] = '-I' + self.sdk_path + match.group(1) + return args + def depfile_for_object(self, objfile: str) -> T.Optional[str]: return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix() diff --git a/test cases/swift/9 sdk path from dep/foo.swift b/test cases/swift/9 sdk path from dep/foo.swift new file mode 100644 index 000000000000..6ca38879f363 --- /dev/null +++ b/test cases/swift/9 sdk path from dep/foo.swift @@ -0,0 +1,4 @@ +// This import is needed for swiftc to implictly import the FFI module +// which will in turn conflict with the dependency's include path and error out +// if we don't manually replace all SDK paths with the newest one. +import Foundation diff --git a/test cases/swift/9 sdk path from dep/meson.build b/test cases/swift/9 sdk path from dep/meson.build new file mode 100644 index 000000000000..4cc44bc725a7 --- /dev/null +++ b/test cases/swift/9 sdk path from dep/meson.build @@ -0,0 +1,12 @@ +project('swift sdk include dir test', 'swift') + +bar_dep = declare_dependency( + # Simulates including 'libffi' from brew as a dep via pkg-config + # Without a workaround that replaces all SDK paths with the most recent one, + # a compile error will occur due to conflicting definitions of the FFI module. + compile_args: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/usr/include/ffi', +) + +foo = static_library('foo', 'foo.swift', + dependencies: [bar_dep], +) From 8493b768ebe4fc4f25f2bc983b663d8ee7db2535 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 28 Jan 2025 15:19:40 -0500 Subject: [PATCH 243/624] Fix call to object_filename_from_source in vs2010 There was a missing parameter in one call. This was caused by b95e1777ddbf0f8aebb56b84a6011468088c06ec --- mesonbuild/backend/vs2010backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 0fb30a7b91b9..e837c89ced0e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1463,7 +1463,7 @@ def add_non_makefile_vcxproj_elements( if self.environment.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, src, target_private_dir) + rel_obj = self.object_filename_from_source(t, compiler, src, target_private_dir) extra_link_args.append(rel_obj) extra_link_args.extend(self.flatten_object_list(t)) From 248a7213f60344bf53b17d97559666be1fa9a9bb Mon Sep 17 00:00:00 2001 From: Steve Lhomme Date: Mon, 27 Jan 2025 12:53:46 +0100 Subject: [PATCH 244/624] symbolextractor: generate symbol files for cross-compiled Windows --- mesonbuild/scripts/symbolextractor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index f4d75a48bffd..b0a07d906553 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -269,7 +269,10 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host # In case of cross builds just always relink. In theory we could # determine the correct toolset, but we would need to use the correct # `nm`, `readelf`, etc, from the cross info which requires refactoring. - dummy_syms(outfilename) + if cross_host == 'windows' and os.path.isfile(impfilename): + windows_syms(impfilename, outfilename) + else: + dummy_syms(outfilename) elif mesonlib.is_linux() or mesonlib.is_hurd(): gnu_syms(libfilename, outfilename) elif mesonlib.is_osx(): From d25fcbb5fc733c2a4cb01b79566e64b84736367d Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Thu, 30 Jan 2025 00:05:58 +0100 Subject: [PATCH 245/624] Mention add_dist_script in meson dist docs (#14186) --- docs/markdown/Creating-releases.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/markdown/Creating-releases.md b/docs/markdown/Creating-releases.md index e0c80af5b0e8..44c127eec57b 100644 --- a/docs/markdown/Creating-releases.md +++ b/docs/markdown/Creating-releases.md @@ -31,6 +31,13 @@ Meson then takes this archive and tests that it works by doing a full `compile` + `test` + `install` cycle. If all these pass, Meson will then create a `SHA-256` checksum file next to the archive. +## Modifying the dist directory before creating the archive + +Modification to the checked out files like generating files or +setting version info can be done with dist scripts: +```meson +[[#meson.add_dist_script]]('distscript.sh') +``` ## Autotools dist VS Meson dist From c616f1ed50ffcd8f513d888c2dace105476a9168 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Tue, 21 Jan 2025 23:14:23 +0000 Subject: [PATCH 246/624] Python: Work around missing rpath in Xcode python3-embed This enables generating Python bindings and linking against `python3-embed` without resorting to later `install_name_tool` changes, as the pkg-config module provided by Xcode doesn't say that Python3.framework requires a rpath entry: $ otool -L /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Python3 /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Python3: @rpath/Python3.framework/Versions/3.9/Python3 (compatibility version 3.9.0, current version 3.9.0) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1933.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0) --- mesonbuild/dependencies/python.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index c978ad62e82e..f45b4f9088ac 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -327,6 +327,12 @@ def __init__(self, name: str, environment: 'Environment', if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'): self.link_args = [] + # 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 + if framework_prefix.startswith('/Applications/Xcode.app/'): + self.link_args += ['-rpath,' + framework_prefix] class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase): From 42c4f746321b19c8295d153ca281b9b5249e5fb4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 17 Dec 2024 11:48:43 -0800 Subject: [PATCH 247/624] utils: Fix nullability of PerMachine from default_missing A Defaultable PerMachine has a type of `None | T`, in other words, they have a base type of `PerMachine[None | T]`, but the purpose of `PerMachine.default_missing()` is to get a `PerMachine[T]` from that `PerMachine[None | T]`, therefore we should ensure that and annotate that. --- mesonbuild/utils/universal.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index edc7e3a243be..c5ed23bd2032 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -576,16 +576,14 @@ class PerMachineDefaultable(PerMachine[T.Optional[_T]]): def __init__(self, build: T.Optional[_T] = None, host: T.Optional[_T] = None) -> None: super().__init__(build, host) - def default_missing(self) -> "PerMachine[_T]": + def default_missing(self) -> PerMachine[_T]: """Default host to build This allows just specifying nothing in the native case, and just host in the cross non-compiler case. """ - freeze = PerMachine(self.build, self.host) - if freeze.host is None: - freeze.host = freeze.build - return freeze + assert self.build is not None, 'Cannot fill in missing when all fields are empty' + return PerMachine(self.build, self.host if self.host is not None else self.build) def __repr__(self) -> str: return f'PerMachineDefaultable({self.build!r}, {self.host!r})' @@ -611,19 +609,17 @@ class PerThreeMachineDefaultable(PerMachineDefaultable[T.Optional[_T]], PerThree def __init__(self) -> None: PerThreeMachine.__init__(self, None, None, None) - def default_missing(self) -> "PerThreeMachine[T.Optional[_T]]": + def default_missing(self) -> PerThreeMachine[_T]: """Default host to build and target to host. This allows just specifying nothing in the native case, just host in the cross non-compiler case, and just target in the native-built cross-compiler case. """ - freeze = PerThreeMachine(self.build, self.host, self.target) - if freeze.host is None: - freeze.host = freeze.build - if freeze.target is None: - freeze.target = freeze.host - return freeze + assert self.build is not None, 'Cannot default a PerMachine when all values are None' + host = self.host if self.host is not None else self.build + target = self.target if self.target is not None else host + return PerThreeMachine(self.build, host, target) def __repr__(self) -> str: return f'PerThreeMachineDefaultable({self.build!r}, {self.host!r}, {self.target!r})' From cb4ac15993db67084ab3b7f0ed8a3edb7a866bba Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 17 Dec 2024 11:58:46 -0800 Subject: [PATCH 248/624] utils: Use dataclasses for PerMachine classes This allows us to simplify the initializers, as well as remove our custom repr methods. --- mesonbuild/utils/universal.py | 37 +++++++++++++---------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index c5ed23bd2032..538b0bd9f01d 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -502,10 +502,10 @@ def get_prefix(self) -> str: return MACHINE_PREFIXES[self.value] +@dataclasses.dataclass(eq=False, order=False) class PerMachine(T.Generic[_T]): - def __init__(self, build: _T, host: _T) -> None: - self.build = build - self.host = host + build: _T + host: _T def __getitem__(self, machine: MachineChoice) -> _T: return [self.build, self.host][machine.value] @@ -513,7 +513,7 @@ def __getitem__(self, machine: MachineChoice) -> _T: def __setitem__(self, machine: MachineChoice, val: _T) -> None: setattr(self, machine.get_lower_case_name(), val) - def miss_defaulting(self) -> "PerMachineDefaultable[T.Optional[_T]]": + def miss_defaulting(self) -> PerMachineDefaultable[T.Optional[_T]]: """Unset definition duplicated from their previous to None This is the inverse of ''default_missing''. By removing defaulted @@ -531,10 +531,8 @@ def assign(self, build: _T, host: _T) -> None: self.build = build self.host = host - def __repr__(self) -> str: - return f'PerMachine({self.build!r}, {self.host!r})' - +@dataclasses.dataclass(eq=False, order=False) class PerThreeMachine(PerMachine[_T]): """Like `PerMachine` but includes `target` too. @@ -542,9 +540,8 @@ class PerThreeMachine(PerMachine[_T]): need to computer the `target` field so we don't bother overriding the `__getitem__`/`__setitem__` methods. """ - def __init__(self, build: _T, host: _T, target: _T) -> None: - super().__init__(build, host) - self.target = target + + target: _T def miss_defaulting(self) -> "PerThreeMachineDefaultable[T.Optional[_T]]": """Unset definition duplicated from their previous to None @@ -566,15 +563,14 @@ def miss_defaulting(self) -> "PerThreeMachineDefaultable[T.Optional[_T]]": def matches_build_machine(self, machine: MachineChoice) -> bool: return self.build == self[machine] - def __repr__(self) -> str: - return f'PerThreeMachine({self.build!r}, {self.host!r}, {self.target!r})' - +@dataclasses.dataclass(eq=False, order=False) class PerMachineDefaultable(PerMachine[T.Optional[_T]]): """Extends `PerMachine` with the ability to default from `None`s. """ - def __init__(self, build: T.Optional[_T] = None, host: T.Optional[_T] = None) -> None: - super().__init__(build, host) + + build: T.Optional[_T] = None + host: T.Optional[_T] = None def default_missing(self) -> PerMachine[_T]: """Default host to build @@ -585,9 +581,6 @@ def default_missing(self) -> PerMachine[_T]: assert self.build is not None, 'Cannot fill in missing when all fields are empty' return PerMachine(self.build, self.host if self.host is not None else self.build) - def __repr__(self) -> str: - return f'PerMachineDefaultable({self.build!r}, {self.host!r})' - @classmethod def default(cls, is_cross: bool, build: _T, host: _T) -> PerMachine[_T]: """Easy way to get a defaulted value @@ -603,11 +596,12 @@ def default(cls, is_cross: bool, build: _T, host: _T) -> PerMachine[_T]: return m.default_missing() +@dataclasses.dataclass(eq=False, order=False) class PerThreeMachineDefaultable(PerMachineDefaultable[T.Optional[_T]], PerThreeMachine[T.Optional[_T]]): """Extends `PerThreeMachine` with the ability to default from `None`s. """ - def __init__(self) -> None: - PerThreeMachine.__init__(self, None, None, None) + + target: T.Optional[_T] = None def default_missing(self) -> PerThreeMachine[_T]: """Default host to build and target to host. @@ -621,9 +615,6 @@ def default_missing(self) -> PerThreeMachine[_T]: target = self.target if self.target is not None else host return PerThreeMachine(self.build, host, target) - def __repr__(self) -> str: - return f'PerThreeMachineDefaultable({self.build!r}, {self.host!r}, {self.target!r})' - def is_sunos() -> bool: return platform.system().lower() == 'sunos' From c0da4a5a7430a84d677d64b17747a27ee838f58b Mon Sep 17 00:00:00 2001 From: wrvsrx Date: Thu, 23 Jan 2025 12:46:27 +0800 Subject: [PATCH 249/624] mformat: try to detect meson.format in source files' parent directories --- mesonbuild/mformat.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index bda89bf3fa10..92729a02cb91 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -961,6 +961,14 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='meson source files' ) +def get_meson_format(sources: T.List[Path]) -> T.Optional[Path]: + for src_file in sources: + for parent in src_file.resolve().parents: + target = parent / 'meson.format' + if target.is_file(): + return target + return None + def run(options: argparse.Namespace) -> int: if options.output and len(options.sources) != 1: raise MesonException('--output argument implies having exactly one source file') @@ -974,10 +982,10 @@ def run(options: argparse.Namespace) -> int: raise MesonException('--inplace argument is not compatible with stdin input') sources: T.List[Path] = options.sources.copy() or [Path(build_filename)] + if not options.configuration: - default_config_path = sources[0].parent / 'meson.format' - if default_config_path.exists(): - options.configuration = default_config_path + options.configuration = get_meson_format(sources) + formatter = Formatter(options.configuration, options.editor_config, options.recursive) while sources: From b825faebaf81937aa929468a354a5490b345cdbd Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 8 Jan 2025 12:15:11 -0500 Subject: [PATCH 250/624] Fix PATH for SharedModule in Windows devenv SharedModule (like Python extension modules) are loaded dynamically. Therefore, they cannot be detected from executable dependencies, and we must call `determine_windows_extra_paths` on them as well. Also, those extra paths need to be computed even if the module or the executable is not installed in the default location. --- mesonbuild/backend/backends.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0c181e56fb53..8d9796db95c6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2008,24 +2008,31 @@ def get_devenv(self) -> mesonlib.EnvironmentVariables: library_paths = set() host_machine = self.environment.machines[MachineChoice.HOST] for t in self.build.get_targets().values(): - in_default_dir = t.should_install() and not t.get_install_dir()[2] - if t.for_machine != MachineChoice.HOST or not in_default_dir: + if t.for_machine is not MachineChoice.HOST or not t.should_install(): continue + + if (host_machine.is_windows() or host_machine.is_cygwin()) and isinstance(t, (build.Executable, build.SharedModule)): + # On windows we cannot rely on rpath to run executables from build + # directory. We have to add in PATH the location of every DLL needed. + library_paths.update(self.determine_windows_extra_paths(t, [])) + + if t.get_install_dir()[2]: + # Do not update paths for target installed in non default location + continue + tdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t)) if isinstance(t, build.Executable): # Add binaries that are going to be installed in bindir into PATH # so they get used by default instead of searching on system when # in developer environment. extra_paths.add(tdir) - if host_machine.is_windows() or host_machine.is_cygwin(): - # On windows we cannot rely on rpath to run executables from build - # directory. We have to add in PATH the location of every DLL needed. - library_paths.update(self.determine_windows_extra_paths(t, [])) + elif isinstance(t, build.SharedLibrary): # Add libraries that are going to be installed in libdir into # LD_LIBRARY_PATH. This allows running system applications using # that library. library_paths.add(tdir) + return self.environment.get_env_for_paths(library_paths, extra_paths) def compiler_to_generator_args(self, target: build.BuildTarget, From ab5e67a57c62c80c16e4d9c71740b01b424a6f30 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 00:42:13 +0100 Subject: [PATCH 251/624] ninjabackend: rust: fix incorrect use of abspath os.path.abspath of the target subdir is not guaranteed to give a sensible answer; depending on what directory you run meson from, it could build an absolute path relative the source directory or the build directory or possibly neither one. In fact, using os.path.abspath is quite rare in Meson's code and generally only done in code like subdir = os.path.abspath(os.path.join(self.sourcedir, target['subdir'])) or ndir1 = os.path.abspath(os.path.realpath(dir1)) While at it, don't use getattr unnecessarily, the cratetype is available. Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 58d2e8fae2e4..5ac5ed6ca7d8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2148,8 +2148,8 @@ def _link_library(libname: str, static: bool, bundle: bool = False): args += ['-C', 'link-arg=' + rpath_arg + ':' + rustc.get_target_libdir()] proc_macro_dylib_path = None - if getattr(target, 'rust_crate_type', '') == 'proc-macro': - proc_macro_dylib_path = os.path.abspath(os.path.join(target.subdir, target.get_filename())) + if cratetype == 'proc-macro': + proc_macro_dylib_path = self.get_target_filename_abs(target) self._add_rust_project_entry(target.name, os.path.abspath(os.path.join(self.environment.build_dir, main_rust_file)), From 4276f1d482095ea003ec39de0fd17ab16dab4b95 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 00:42:13 +0100 Subject: [PATCH 252/624] ninjabackend: rust: remove code duplication Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 5ac5ed6ca7d8..21ca38c0db50 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1993,7 +1993,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: orderdeps.append(fname) if main_rust_file is None: raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report') - target_name = os.path.join(target.subdir, target.get_filename()) + target_name = self.get_target_filename(target) cratetype = target.rust_crate_type args.extend(['--crate-type', cratetype]) @@ -2056,7 +2056,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): # dependency, so that collisions with libraries in rustc's # sysroot don't cause ambiguity d_name = self._get_rust_dependency_name(target, d) - args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))] + args += ['--extern', '{}={}'.format(d_name, self.get_target_filename(d))] project_deps.append(RustDep(d_name, self.rust_crates[d.name].order)) continue From 103501c2741f9ff919bc04a17cb057ace150526c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 00:33:45 +0100 Subject: [PATCH 253/624] ninjabackend: unify building rpath args Implement RustCompiler.build_rpath_args, so that more code can be shared between non-Rust and Rust targets. Then, RustCompiler can override it to convert the arguments to "-C link-arg=" and add the rustup sysroot. Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 60 ++++++++++-------------------- mesonbuild/compilers/rust.py | 14 +++++++ 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 21ca38c0db50..4aadb12cb539 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2123,29 +2123,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): args += ['-C', 'prefer-dynamic'] if isinstance(target, build.SharedLibrary) or has_shared_deps: - # build the usual rpath arguments as well... - - # Set runtime-paths so we can run executables without needing to set - # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. - if has_path_sep(target.name): - # Target names really should not have slashes in them, but - # unfortunately we did not check for that and some downstream projects - # now have them. Once slashes are forbidden, remove this bit. - target_slashname_workaround_dir = os.path.join(os.path.dirname(target.name), - self.get_target_dir(target)) - else: - target_slashname_workaround_dir = self.get_target_dir(target) - rpath_args, target.rpath_dirs_to_remove = ( - rustc.build_rpath_args(self.environment, - self.environment.get_build_dir(), - target_slashname_workaround_dir, - self.determine_rpath_dirs(target), - target.build_rpath, - target.install_rpath)) - # ... but then add rustc's sysroot to account for rustup - # installations - for rpath_arg in rpath_args: - args += ['-C', 'link-arg=' + rpath_arg + ':' + rustc.get_target_libdir()] + args += self.get_build_rpath_args(target, rustc) proc_macro_dylib_path = None if cratetype == 'proc-macro': @@ -3486,6 +3464,24 @@ def generate_prelink(self, target, obj_list): self.add_build(elem) return obj_list + def get_build_rpath_args(self, target: build.BuildTarget, linker: T.Union[Compiler, StaticLinker]) -> T.List[str]: + if has_path_sep(target.name): + # Target names really should not have slashes in them, but + # unfortunately we did not check for that and some downstream projects + # now have them. Once slashes are forbidden, remove this bit. + target_slashname_workaround_dir = os.path.join(os.path.dirname(target.name), + self.get_target_dir(target)) + 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(), + target_slashname_workaround_dir, + self.determine_rpath_dirs(target), + target.build_rpath, + target.install_rpath)) + return rpath_args + def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None): extra_args = extra_args if extra_args is not None else [] stdlib_args = stdlib_args if stdlib_args is not None else [] @@ -3551,23 +3547,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # Set runtime-paths so we can run executables without needing to set # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. - if has_path_sep(target.name): - # Target names really should not have slashes in them, but - # unfortunately we did not check for that and some downstream projects - # now have them. Once slashes are forbidden, remove this bit. - target_slashname_workaround_dir = os.path.join( - os.path.dirname(target.name), - self.get_target_dir(target)) - 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(), - target_slashname_workaround_dir, - self.determine_rpath_dirs(target), - target.build_rpath, - target.install_rpath)) - commands += rpath_args + commands += self.get_build_rpath_args(target, linker) # Add link args to link to all internal libraries (link_with:) and # internal dependencies needed by this target. diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 6b9edb05dcdd..09a1d24def61 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -196,6 +196,20 @@ def get_debug_args(self, is_debug: bool) -> T.List[str]: 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('link-arg=' + arg + ':' + self.get_target_libdir()) + return rustc_rpath_args, to_remove + 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): From 39d5ffc27fe0dae50b0be10ed23cf54e05a6c38b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 13:42:14 +0100 Subject: [PATCH 254/624] rust: stop using --out-dir, move depfile into private directory Since the introduction of dep-info=... it is possible to move the depfile away from the main build directory without using --out-dir. This is less surprising, since the rules for mixing --emit, --out-dir and -o are not really documented. Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4aadb12cb539..cc3b9b3aa55b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2007,9 +2007,9 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: 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('.', '_')] - depfile = os.path.join(target.subdir, target.name + '.d') - args += ['--emit', f'dep-info={depfile}', '--emit', f'link={target_name}'] - args += ['--out-dir', self.get_target_private_dir(target)] + depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d') + args += ['--emit', f'dep-info={depfile}'] + args += ['--emit', f'link'={target_name}'] args += ['-C', 'metadata=' + target.get_id()] args += target.get_extra_args('rust') From a19df7da15848b7b01dfe3cf8f88211529b9143b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 13:42:14 +0100 Subject: [PATCH 255/624] ninjabackend: start adjusting for differences between rustc and rustdoc Add functions to RustCompiler() to account for differences between rustc and "rustdoc --test": rustdoc always generates a binary, does not support -g, and does not need --emit. Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- mesonbuild/compilers/rust.py | 26 ++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cc3b9b3aa55b..73a809cd4711 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2008,8 +2008,8 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: # Rustc replaces - with _. spaces or dots are not allowed, so we replace them with underscores args += ['--crate-name', target.name.replace('-', '_').replace(' ', '_').replace('.', '_')] depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d') - args += ['--emit', f'dep-info={depfile}'] - args += ['--emit', f'link'={target_name}'] + args += rustc.get_dependency_gen_args(target_name, depfile) + args += rustc.get_output_args(target_name) args += ['-C', 'metadata=' + target.get_id()] args += target.get_extra_args('rust') diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 09a1d24def61..ee3155c88bfe 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -170,7 +170,10 @@ def _native_static_libs(self, work_dir: str, source_name: str) -> None: self.native_static_libs = [i for i in match.group(1).split() if i not in exclude] def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: - return ['--dep-info', outfile] + return ['--emit', f'dep-info={outfile}'] + + def get_output_args(self, outputname: str) -> T.List[str]: + return ['--emit', f'link={outputname}'] @functools.lru_cache(maxsize=None) def get_sysroot(self) -> str: @@ -222,9 +225,6 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], return parameter_list - def get_output_args(self, outputname: str) -> T.List[str]: - return ['-o', outputname] - @classmethod def use_linker_args(cls, linker: str, version: str) -> T.List[str]: return ['-C', f'linker={linker}'] @@ -324,3 +324,21 @@ class ClippyRustCompiler(RustCompiler): """ id = 'clippy-driver rustc' + + +class RustdocTestCompiler(RustCompiler): + + """We invoke Rustdoc to run doctests. Some of the flags + are different from rustc and some (e.g. --emit link) are + ignored.""" + + id = 'rustdoc --test' + + def get_debug_args(self, is_debug: bool) -> T.List[str]: + return [] + + def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: + return [] + + def get_output_args(self, outputname: str) -> T.List[str]: + return [] From f021c37191d19bd0d2c12acee4d78bcb91fa1218 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 13:27:43 +0100 Subject: [PATCH 256/624] ninjabackend: remove cratetype variable Since we're going to split generate_rust_target() in multiple functions, eliminate the only variable that spans large parts of it. The cratetype ninja variable has been unused since Meson started invoking rustc directly nine years ago (commit d952812b1, "Fix Rust to work with 1.3 release. Closes #277.", 2015-10-11). Reviewed-by: Dylan Baker Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 73a809cd4711..24758866c6b5 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1994,14 +1994,13 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: if main_rust_file is None: raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report') target_name = self.get_target_filename(target) - cratetype = target.rust_crate_type - args.extend(['--crate-type', cratetype]) + args.extend(['--crate-type', target.rust_crate_type]) # If we're dynamically linking, add those arguments # # Rust is super annoying, calling -C link-arg foo does not work, it has # to be -C link-arg=foo - if cratetype in {'bin', 'dylib'}: + if target.rust_crate_type in {'bin', 'dylib'}: args.extend(rustc.get_linker_always_args()) args += self.generate_basic_compiler_args(target, rustc) @@ -2116,7 +2115,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): and dep.rust_crate_type == 'dylib' for dep in target_deps) - if cratetype in {'dylib', 'proc-macro'} or has_rust_shared_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 # against are dynamic or this is a dynamic library itself, # otherwise we'll end up with multiple implementations of libstd. @@ -2126,12 +2125,12 @@ def _link_library(libname: str, static: bool, bundle: bool = False): args += self.get_build_rpath_args(target, rustc) proc_macro_dylib_path = None - if cratetype == 'proc-macro': + if target.rust_crate_type == 'proc-macro': proc_macro_dylib_path = self.get_target_filename_abs(target) self._add_rust_project_entry(target.name, os.path.abspath(os.path.join(self.environment.build_dir, main_rust_file)), - args, cratetype, target_name, + args, target.rust_crate_type, target_name, bool(target.subproject), proc_macro_dylib_path, project_deps) @@ -2144,7 +2143,6 @@ def _link_library(libname: str, static: bool, bundle: bool = False): element.add_dep(deps) element.add_item('ARGS', args) element.add_item('targetdep', depfile) - element.add_item('cratetype', cratetype) self.add_build(element) if isinstance(target, build.SharedLibrary): self.generate_shsym(target) From d34c37f8a02935945e3619cccc880f99e8ee96d2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Feb 2025 09:42:35 +0100 Subject: [PATCH 257/624] rust: use f-string to build rpath arguments Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/rust.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index ee3155c88bfe..b8588b8d7db6 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -210,7 +210,7 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, rustc_rpath_args = [] for arg in args: rustc_rpath_args.append('-C') - rustc_rpath_args.append('link-arg=' + arg + ':' + self.get_target_libdir()) + rustc_rpath_args.append(f'link-arg={arg}:{self.get_target_libdir()}') return rustc_rpath_args, to_remove def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], From f75e45887c86c8c73a38f387bd93467dda36df3c Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 31 Jan 2025 00:06:48 +0000 Subject: [PATCH 258/624] Python: fix typo in the inserted workaround for missing RPATH See #14169 --- mesonbuild/dependencies/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index f45b4f9088ac..f7417717e175 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -332,7 +332,7 @@ def __init__(self, name: str, environment: 'Environment', framework_prefix = self.variables['PYTHONFRAMEWORKPREFIX'] # Add rpath, will be de-duplicated if necessary if framework_prefix.startswith('/Applications/Xcode.app/'): - self.link_args += ['-rpath,' + framework_prefix] + self.link_args += ['-Wl,-rpath,' + framework_prefix] class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase): From e793b1bc1aa5f44b67199a845c126e3ab459c8c6 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 31 Jan 2025 01:06:55 +0000 Subject: [PATCH 259/624] unittests: Unbreak Python bytecompile tests with Xcode Python Apple sets sys.pycache_prefix to an user-wide cache folder, so it needs to be prepended to the root for the glob to work correctly. --- unittests/pythontests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/unittests/pythontests.py b/unittests/pythontests.py index aaea906ea829..96864ffd837f 100644 --- a/unittests/pythontests.py +++ b/unittests/pythontests.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2021 The Meson development team -import glob, os, pathlib, shutil, subprocess, unittest +import glob, os, pathlib, shutil, subprocess, sys, unittest from run_tests import ( Backend @@ -64,7 +64,12 @@ def _test_bytecompile(self, py2=False): for file in files: realfile = os.path.join(root, file) if file.endswith('.py'): - cached = glob.glob(realfile+'?') + glob.glob(os.path.join(root, '__pycache__', os.path.splitext(file)[0] + '*.pyc')) + # FIXME: relpath must be adjusted for windows path behaviour + if hasattr(sys, "pycache_prefix"): + root = os.path.join(sys.pycache_prefix, os.path.relpath(root, '/')) + else: + root = os.path.join(root, '__pycache__') + cached = glob.glob(realfile+'?') + glob.glob(os.path.join(root, os.path.splitext(file)[0] + '*.pyc')) if py2 and cc.get_id() == 'msvc': # MSVC python installs python2/python3 into the same directory self.assertLength(cached, 4) From 3a4cb94e949a94fdfb921a2df7f898ec8c70811a Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 31 Jan 2025 01:09:01 +0000 Subject: [PATCH 260/624] ci: Update appleclang job to test Xcode-provided Python --- .github/workflows/macos.yml | 18 +++++++++--------- unittests/pythontests.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f5a87c957160..2d2ea39c096a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -32,12 +32,10 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.x' - run: | - python -m pip install --upgrade pip - python -m pip install pytest pytest-xdist pytest-subtests fastjsonschema coverage + 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 - run: brew install pkg-config ninja llvm qt@5 - env: CPPFLAGS: "-I/opt/homebrew/include" @@ -48,12 +46,14 @@ jobs: # These cannot evaluate anything, so we cannot set PATH or SDKROOT here run: | export SDKROOT="$(xcodebuild -version -sdk macosx Path)" - export PATH="$HOME/tools:/opt/homebrew/opt/qt@5/bin:/opt/homebrew/opt/llvm/bin:$PATH" - export PKG_CONFIG_PATH="/opt/homebrew/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH" - ./tools/run_with_cov.py ./run_unittests.py + 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: ./ci/combine_cov.sh + run: | + export PATH="$HOME/Library/Python/3.9/bin:$PATH" + ./ci/combine_cov.sh - name: Upload coverage report uses: codecov/codecov-action@v3 diff --git a/unittests/pythontests.py b/unittests/pythontests.py index 96864ffd837f..c4926c83c450 100644 --- a/unittests/pythontests.py +++ b/unittests/pythontests.py @@ -65,7 +65,7 @@ def _test_bytecompile(self, py2=False): realfile = os.path.join(root, file) if file.endswith('.py'): # FIXME: relpath must be adjusted for windows path behaviour - if hasattr(sys, "pycache_prefix"): + if getattr(sys, "pycache_prefix", None) is not None: root = os.path.join(sys.pycache_prefix, os.path.relpath(root, '/')) else: root = os.path.join(root, '__pycache__') From 0e6e68786ccea602b0343e0e104278352e4ee89a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Feb 2025 11:34:57 -0800 Subject: [PATCH 261/624] minstall: help mypy out with our chown overriding This is an annoying issue to look at, because shutil.chown has (for our purposes) three signatures: ```python chown(path: int | AnyPathLike, uid: int | str, group: None = None) -> None: ... chown(path: int | AnyPathLike, uid: None, group: int | str) -> None: ... chown(path: int | AnyPathLike, uid: int | str, group: int | str) -> None: ... ``` This is a really difficult thing to guarantee from our code. We more or less depend on being able to pass two parameters of `None | int | str`, and it working. In our only caller we do ensure that at least one of the variables is not None, but convincing mypy of this is more work than it's worth. This will show up in our CI only for python >= 3.13. An updated typshed will make this show up for earlier versions, however. Pyright (which is used by the VSCode Python extension) will spot this for earlier versions. I have changed the code in such a way to make our CI turn green. --- mesonbuild/minstall.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 0bb1691ebc61..2db4472d160c 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -139,7 +139,6 @@ def append_to_log(lf: T.TextIO, line: str) -> None: lf.write('\n') lf.flush() - def set_chown(path: str, user: T.Union[str, int, None] = None, group: T.Union[str, int, None] = None, dir_fd: T.Optional[int] = None, follow_symlinks: bool = True) -> None: @@ -150,10 +149,23 @@ def set_chown(path: str, user: T.Union[str, int, None] = None, # Not nice, but better than actually rewriting shutil.chown until # this python bug is fixed: https://bugs.python.org/issue18108 + # This is running into a problem where this may not match any of signatures + # of `shtil.chown`, which (simplified) are: + # chown(path: int | AnyPath, user: int | str, group: None = None) + # chown(path: int | AnyPath, user: None, group: int | str) + # We cannot through easy coercion of the type system force it to say: + # - user is non null and group is null + # - user is null and group is non null + # - user is non null and group is non null + # + # This is checked by the only (current) caller, but let's be sure that the + # call we're making to `shutil.chown` is actually valid. + assert user is not None or group is not None, 'ensure that calls to chown are valid' + if sys.version_info >= (3, 13): # pylint: disable=unexpected-keyword-arg # cannot handle sys.version_info, https://github.com/pylint-dev/pylint/issues/9622 - shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) + shutil.chown(path, user, group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) # type: ignore[call-overload] else: real_os_chown = os.chown From 4aa0e93922355c01c86ad41d985ec24e4ec8a1f6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 28 Aug 2024 10:35:28 -0700 Subject: [PATCH 262/624] options: fix the annotations of _to_tuple --- mesonbuild/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 1566f940c98c..aea38ec3e66a 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -142,7 +142,7 @@ def __setstate__(self, state: T.Dict[str, T.Any]) -> None: def __hash__(self) -> int: return self._hash - def _to_tuple(self) -> T.Tuple[str, str, str, MachineChoice, str]: + def _to_tuple(self) -> T.Tuple[str, MachineChoice, str]: return (self.subproject, self.machine, self.name) def __eq__(self, other: object) -> bool: From b32e4e87b1c88eb235389c4f60f540154d720970 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Aug 2024 09:23:05 -0700 Subject: [PATCH 263/624] options: Add a printable_choices method to UserOption This provides a method to get choices for options in a printable form. The goal is to make refactoring options simpler. --- mesonbuild/mintro.py | 7 ++++--- mesonbuild/options.py | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index b9b09c557427..cd68911ce322 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -315,14 +315,15 @@ def add_keys(opts: 'T.Union[dict[OptionKey, UserOption[Any]], cdata.KeyedOptionD elif isinstance(opt, options.UserBooleanOption): typestr = 'boolean' elif isinstance(opt, options.UserComboOption): - optdict['choices'] = opt.choices + optdict['choices'] = opt.printable_choices() typestr = 'combo' elif isinstance(opt, options.UserIntegerOption): typestr = 'integer' elif isinstance(opt, options.UserArrayOption): typestr = 'array' - if opt.choices: - optdict['choices'] = opt.choices + c = opt.printable_choices() + if c: + optdict['choices'] = c else: raise RuntimeError("Unknown option type") optdict['type'] = typestr diff --git a/mesonbuild/options.py b/mesonbuild/options.py index aea38ec3e66a..d0cf231dcd99 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2013-2024 Contributors to the The Meson project -# Copyright © 2019-2024 Intel Corporation +# Copyright © 2019-2025 Intel Corporation from __future__ import annotations from collections import OrderedDict @@ -265,6 +265,13 @@ def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bo assert isinstance(self.value, (str, int, bool, list)) return self.value + def printable_choices(self) -> T.Optional[T.List[str]]: + if not self.choices: + return None + if isinstance(self.choices, str): + return [self.choices] + return [str(c) for c in self.choices] + # 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. From cd5bc11bb34054131e90a79d764383d55fefa330 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Aug 2024 09:26:59 -0700 Subject: [PATCH 264/624] options: Get rid of the invalid _U type, and use UserOption[_T] --- mesonbuild/compilers/compilers.py | 15 ++++++++------- mesonbuild/options.py | 8 +++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 424bcc19bf6a..011dacfb7781 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2022 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -20,9 +20,7 @@ EnvironmentException, MesonException, Popen_safe_logged, LibType, TemporaryDirectoryWinProof, ) - from ..options import OptionKey - from ..arglist import CompilerArgs if T.TYPE_CHECKING: @@ -37,9 +35,10 @@ from ..dependencies import Dependency CompilerType = T.TypeVar('CompilerType', bound='Compiler') - _T = T.TypeVar('_T') UserOptionType = T.TypeVar('UserOptionType', bound=options.UserOption) +_T = T.TypeVar('_T') + """This file contains the data files of all compilers Meson knows about. To support a new compiler, add its information below. Also add corresponding autodetection code in detect.py.""" @@ -216,19 +215,21 @@ class CompileCheckMode(enum.Enum): MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd'] + @dataclass -class BaseOption(T.Generic[options._T, options._U]): - opt_type: T.Type[options._U] +class BaseOption(T.Generic[_T]): + opt_type: T.Type[options.UserOption[_T]] description: str default: T.Any = None choices: T.Any = None - def init_option(self, name: OptionKey) -> options._U: + def init_option(self, name: OptionKey) -> options.UserOption[_T]: keywords = {'value': self.default} if self.choices: keywords['choices'] = self.choices return self.opt_type(name.name, self.description, **keywords) + BASE_OPTIONS: T.Mapping[OptionKey, BaseOption] = { OptionKey('b_pch'): BaseOption(options.UserBooleanOption, 'Use precompiled headers', True), OptionKey('b_lto'): BaseOption(options.UserBooleanOption, 'Use link time optimization', False), diff --git a/mesonbuild/options.py b/mesonbuild/options.py index d0cf231dcd99..ec1db898058d 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -283,8 +283,6 @@ def set_value(self, newvalue: T.Any) -> bool: self.value = self.validate_value(newvalue) return self.value != oldvalue -_U = T.TypeVar('_U', bound=UserOption[_T]) - class UserStringOption(UserOption[str]): def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING, @@ -525,14 +523,14 @@ def validate_value(self, value: T.Union[str, T.List[str]]) -> str: f'Possible values for option "{self.name}" are {self.choices}') -class BuiltinOption(T.Generic[_T, _U]): +class BuiltinOption(T.Generic[_T]): """Class for a builtin option type. There are some cases that are not fully supported yet. """ - def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *, + def __init__(self, opt_type: T.Type[UserOption[_T]], description: str, default: T.Any, yielding: bool = True, *, choices: T.Any = None, readonly: bool = False): self.opt_type = opt_type self.description = description @@ -541,7 +539,7 @@ def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yield self.yielding = yielding self.readonly = readonly - def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> _U: + def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> UserOption[_T]: """Create an instance of opt_type and return it.""" if value is None: value = self.prefixed_default(name, prefix) From b51840cf18198865e9b91f5b249e7e2e9526a604 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 29 Jan 2025 14:19:28 -0800 Subject: [PATCH 265/624] compilers: fix the UserStdOption name of the C and C++ compilers --- mesonbuild/compilers/c.py | 2 +- mesonbuild/compilers/cpp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 8379f81bac70..ddd8bed32c7e 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -99,7 +99,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ - key: options.UserStdOption('C', ALL_STDS), + key: options.UserStdOption('c', ALL_STDS), }) return opts diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 37d82c395092..fcc5ddef2ff0 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -175,7 +175,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ - key: options.UserStdOption('C++', ALL_STDS), + key: options.UserStdOption('cpp', ALL_STDS), }) return opts From f0a6ba380989c68ecc5af61087157557b329f808 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 30 Jan 2025 09:02:40 -0800 Subject: [PATCH 266/624] compilers: use super().get_options() instead of CompilerClass.get_options() Because the latter doesn't always interact with the MRO correctly. --- mesonbuild/compilers/c.py | 22 +++++++++++----------- mesonbuild/compilers/cpp.py | 16 ++++++++-------- mesonbuild/compilers/fortran.py | 8 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index ddd8bed32c7e..cec6fc3ae746 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -214,7 +214,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ 'everything': ['-Weverything']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -313,7 +313,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ PGICompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() cppstd_choices = ['c89', 'c90', 'c99', 'c11', 'c17', 'c18'] std_opt = opts[self.form_compileropt_key('std')] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -332,7 +332,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ ElbrusCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() stds = ['c89', 'c9x', 'c99', 'gnu89', 'gnu9x', 'gnu99'] stds += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999'] if version_compare(self.version, '>=1.20.00'): @@ -379,7 +379,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ 'everything': default_warn_args + ['-Wextra']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() stds = ['c89', 'c99'] if version_compare(self.version, '>=16.0.0'): stds += ['c11'] @@ -536,7 +536,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ ArmCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -566,7 +566,7 @@ def get_always_args(self) -> T.List[str]: return ['-nologo'] def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -614,7 +614,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ Xc16Compiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -660,7 +660,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ CompCertCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -698,7 +698,7 @@ def get_always_args(self) -> T.List[str]: return [] def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -738,7 +738,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st return mwccarm_instruction_set_args.get(instruction_set, None) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() c_stds = ['c99'] key = self.form_compileropt_key('std') opts[key].choices = ['none'] + c_stds @@ -768,7 +768,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st return mwcceppc_instruction_set_args.get(instruction_set, None) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CCompiler.get_options(self) + opts = super().get_options() c_stds = ['c99'] key = self.form_compileropt_key('std') opts[key].choices = ['none'] + c_stds diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index fcc5ddef2ff0..fd0573ce9987 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -390,7 +390,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ 'everything': ['-Weverything']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') self.update_options( opts, @@ -545,7 +545,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ PGICompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() cppstd_choices = [ 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23', 'gnu++98', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++20' @@ -567,7 +567,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ ElbrusCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() cpp_stds = ['c++98'] if version_compare(self.version, '>=1.20.00'): @@ -649,7 +649,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ 'everything': default_warn_args + ['-Wextra']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() # Every Unix compiler under the sun seems to accept -std=c++03, # with the exception of ICC. Instead of preventing the user from # globally requesting C++03, we transparently remap it to C++98 @@ -923,7 +923,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ ArmCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() std_opt = self.form_compileropt_key('std') assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(['c++03', 'c++11']) @@ -984,7 +984,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ TICompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' @@ -1027,7 +1027,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st return mwccarm_instruction_set_args.get(instruction_set, None) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') opts[key].choices = ['none'] return opts @@ -1056,7 +1056,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st return mwcceppc_instruction_set_args.get(instruction_set, None) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = CPPCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') opts[key].choices = ['none'] return opts diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index e54864dff696..0e41b70e9d7b 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -273,7 +273,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic 'everything': default_warn_args + ['-Wextra', '-Wpedantic', '-fimplicit-none']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = FortranCompiler.get_options(self) + opts = super().get_options() fortran_stds = ['legacy', 'f95', 'f2003'] if version_compare(self.version, '>=4.4.0'): fortran_stds += ['f2008'] @@ -335,7 +335,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic ElbrusCompiler.__init__(self) def get_options(self) -> 'MutableKeyedOptionDictType': - opts = FortranCompiler.get_options(self) + opts = super().get_options() fortran_stds = ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts'] key = self.form_compileropt_key('std') opts[key].choices = ['none'] + fortran_stds @@ -415,7 +415,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic 'everything': ['-warn', 'all']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = FortranCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] return opts @@ -470,7 +470,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic 'everything': ['/warn:all']} def get_options(self) -> 'MutableKeyedOptionDictType': - opts = FortranCompiler.get_options(self) + opts = super().get_options() key = self.form_compileropt_key('std') opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] return opts From fe9f8de1ab52af0a6f4c3a1ce054ee3e2eb7a74c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Aug 2024 10:22:53 -0700 Subject: [PATCH 267/624] compilers: remove Compiler.create_option This saves a *tiny* bit of typing, but at the cost of requiring either the current solution of throwing up our hands and saying "typing is too hard, better to have bugs!" or an extensive amount of `TypedDict`s, `overloads`, and a very new version of mypy. Let's get our type safety back, even if it means writing a little bit more code. --- mesonbuild/compilers/c.py | 40 ++-- mesonbuild/compilers/compilers.py | 4 +- mesonbuild/compilers/cpp.py | 218 ++++++++++++---------- mesonbuild/compilers/cuda.py | 28 +-- mesonbuild/compilers/cython.py | 32 ++-- mesonbuild/compilers/fortran.py | 18 +- mesonbuild/compilers/mixins/emscripten.py | 18 +- mesonbuild/compilers/objc.py | 10 + mesonbuild/compilers/objcpp.py | 15 +- mesonbuild/compilers/rust.py | 15 +- 10 files changed, 217 insertions(+), 181 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index cec6fc3ae746..d8ad9f793ec0 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -123,13 +123,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() if self.info.is_windows() or self.info.is_cygwin(): - self.update_options( - opts, - self.create_option(options.UserArrayOption, - self.form_compileropt_key('winlibs'), - 'Standard Windows libs to link against', - gnu_winlibs), - ) + key = self.form_compileropt_key('winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Windows libraries to link against', + gnu_winlibs) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -258,13 +256,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() if self.info.is_windows() or self.info.is_cygwin(): - self.update_options( - opts, - self.create_option(options.UserArrayOption, - self.form_compileropt_key('winlibs'), - 'Standard Windows libs to link against', - gnu_winlibs), - ) + key = self.form_compileropt_key('winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Windows libraries to link against', + gnu_winlibs) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -408,15 +404,13 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase): """Shared methods that apply to MSVC-like C compilers.""" def get_options(self) -> MutableKeyedOptionDictType: - return self.update_options( - super().get_options(), - self.create_option( - options.UserArrayOption, - self.form_compileropt_key('winlibs'), - 'Standard Windows libs to link against', - msvc_winlibs, - ), - ) + opts = super().get_options() + key = self.form_compileropt_key('winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Windows libraries to link against', + msvc_winlibs) + return opts def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: # need a TypeDict to make this work diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 011dacfb7781..12bcd1e4c972 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -588,8 +588,8 @@ def gen_import_library_args(self, implibname: str) -> T.List[str]: """ return [] - def create_option(self, option_type: T.Type[UserOptionType], option_key: OptionKey, *args: T.Any, **kwargs: T.Any) -> T.Tuple[OptionKey, UserOptionType]: - return option_key, option_type(f'{self.language}_{option_key.name}', *args, **kwargs) + def make_option_name(self, key: OptionKey) -> str: + return f'{self.language}_{key.name}' @staticmethod def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, UserOptionType]) -> MutableKeyedOptionDictType: diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index fd0573ce9987..b17e18b3ed35 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -237,30 +237,32 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - self.update_options( - opts, - self.create_option(options.UserComboOption, - self.form_compileropt_key('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('rtti'), - 'Enable RTTI', - True), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('debugstl'), - 'STL debug mode', - False), - ) + + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + + key = self.form_compileropt_key('rtti') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'Enable RTTI', + True) + + key = self.form_compileropt_key('debugstl') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'STL debug mode', + False) + if self.info.is_windows() or self.info.is_cygwin(): - self.update_options( - opts, - self.create_option(options.UserArrayOption, - self.form_compileropt_key('winlibs'), - 'Standard Windows libs to link against', - gnu_winlibs), - ) + key = self.form_compileropt_key('winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Win libraries to link against', + gnu_winlibs) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -391,15 +393,15 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() + + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + key = self.form_compileropt_key('std') - self.update_options( - opts, - self.create_option(options.UserComboOption, - key.evolve('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - ) std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True) @@ -440,32 +442,34 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_cpp_warning_args))} def get_options(self) -> 'MutableKeyedOptionDictType': - key = self.form_compileropt_key('std') opts = super().get_options() - self.update_options( - opts, - self.create_option(options.UserComboOption, - self.form_compileropt_key('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('rtti'), - 'Enable RTTI', - True), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('debugstl'), - 'STL debug mode', - False), - ) + + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + + key = self.form_compileropt_key('rtti') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'Enable RTTI', + True) + + key = self.form_compileropt_key('debugstl') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'STL debug mode', + False) + if self.info.is_windows() or self.info.is_cygwin(): - self.update_options( - opts, - self.create_option(options.UserArrayOption, - key.evolve('cpp_winlibs'), - 'Standard Windows libs to link against', - gnu_winlibs), - ) + key = key.evolve(name='cpp_winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Win libraries to link against', + gnu_winlibs) + return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -569,6 +573,19 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + + key = self.form_compileropt_key('debugstl') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'STL debug mode', + False) + cpp_stds = ['c++98'] if version_compare(self.version, '>=1.20.00'): cpp_stds += ['c++03', 'c++0x', 'c++11'] @@ -586,18 +603,6 @@ def get_options(self) -> 'MutableKeyedOptionDictType': cpp_stds += ['c++20'] key = self.form_compileropt_key('std') - self.update_options( - opts, - self.create_option(options.UserComboOption, - self.form_compileropt_key('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('debugstl'), - 'STL debug mode', - False), - ) std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(cpp_stds, gnu=True) @@ -650,6 +655,26 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() + + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + + key = self.form_compileropt_key('rtti') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'Enable RTTI', + True) + + key = self.form_compileropt_key('debugstl') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'STL debug mode', + False) + # Every Unix compiler under the sun seems to accept -std=c++03, # with the exception of ICC. Instead of preventing the user from # globally requesting C++03, we transparently remap it to C++98 @@ -666,24 +691,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': c_stds += ['c++2a'] g_stds += ['gnu++2a'] - key = self.form_compileropt_key('std') - self.update_options( - opts, - self.create_option(options.UserComboOption, - self.form_compileropt_key('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('rtti'), - 'Enable RTTI', - True), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('debugstl'), - 'STL debug mode', - False), - ) - std_opt = opts[key] + std_opt = opts[self.form_compileropt_key('std')] assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(c_stds + g_stds) return opts @@ -739,24 +747,28 @@ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: return T.cast('T.List[str]', options.get_value(key)[:]) def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List[str]) -> 'MutableKeyedOptionDictType': - key = self.form_compileropt_key('std') - self.update_options( - opts, - self.create_option(options.UserComboOption, - self.form_compileropt_key('eh'), - 'C++ exception handling type', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - self.create_option(options.UserBooleanOption, - self.form_compileropt_key('rtti'), - 'Enable RTTI', - True), - self.create_option(options.UserArrayOption, - self.form_compileropt_key('winlibs'), - 'Standard Windows libs to link against', - msvc_winlibs), - ) - std_opt = opts[key] + opts = super().get_options() + + key = self.form_compileropt_key('eh') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default') + + key = self.form_compileropt_key('rtti') + opts[key] = options.UserBooleanOption( + self.make_option_name(key), + 'Enable RTTI', + True) + + key = self.form_compileropt_key('winlibs') + opts[key] = options.UserArrayOption( + self.make_option_name(key), + 'Standard Win libraries to link against', + msvc_winlibs) + + std_opt = opts[self.form_compileropt_key('std')] assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(cpp_stds) return opts diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 38a938f24aff..07fda95dd6d5 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -646,18 +646,22 @@ def get_options(self) -> 'MutableKeyedOptionDictType': if version_compare(self.version, self._CPP20_VERSION): cpp_stds += ['c++20'] - return self.update_options( - super().get_options(), - self.create_option(options.UserComboOption, - self.form_compileropt_key('std'), - 'C++ language standard to use with CUDA', - cpp_stds, - 'none'), - self.create_option(options.UserStringOption, - self.form_compileropt_key('ccbindir'), - 'CUDA non-default toolchain directory to use (-ccbin)', - ''), - ) + opts = super().get_options() + + key = self.form_compileropt_key('std') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'C++ language standard to use with CUDA', + cpp_stds, + 'none') + + key = self.form_compileropt_key('ccbindir') + opts[key] = options.UserStringOption( + self.make_option_name(key), + 'CUDA non-default toolchain directory to use (-ccbin)', + '') + + return opts def _to_host_compiler_options(self, master_options: 'KeyedOptionDictType') -> 'KeyedOptionDictType': """ diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index 5cc0200458fa..2d0f21d00aa2 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2021 Intel Corporation +# Copyright © 2021-2024 Intel Corporation from __future__ import annotations """Abstraction for Cython language compilers.""" @@ -67,19 +67,23 @@ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], return new def get_options(self) -> 'MutableKeyedOptionDictType': - return self.update_options( - super().get_options(), - self.create_option(options.UserComboOption, - self.form_compileropt_key('version'), - 'Python version to target', - ['2', '3'], - '3'), - self.create_option(options.UserComboOption, - self.form_compileropt_key('language'), - 'Output C or C++ files', - ['c', 'cpp'], - 'c'), - ) + opts = super().get_options() + + key = self.form_compileropt_key('version') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'Python version to target', + ['2', '3'], + '3') + + key = self.form_compileropt_key('language') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'Output C or C++ files', + ['c', 'cpp'], + 'c') + + return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: args: T.List[str] = [] diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 0e41b70e9d7b..f771e4c40495 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -114,14 +114,16 @@ def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.T return self._has_multi_link_arguments(args, env, 'stop; end program') def get_options(self) -> 'MutableKeyedOptionDictType': - return self.update_options( - super().get_options(), - self.create_option(options.UserComboOption, - self.form_compileropt_key('std'), - 'Fortran language standard to use', - ['none'], - 'none'), - ) + opts = super().get_options() + + key = self.form_compileropt_key('std') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'Fortran language standard to use', + ['none'], + 'none') + + return opts def _compile_int(self, expression: str, prefix: str, env: 'Environment', extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]], diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py index 64315ae96797..fa862056923a 100644 --- a/mesonbuild/compilers/mixins/emscripten.py +++ b/mesonbuild/compilers/mixins/emscripten.py @@ -57,15 +57,15 @@ def thread_link_flags(self, env: 'Environment') -> T.List[str]: return args def get_options(self) -> coredata.MutableKeyedOptionDictType: - return self.update_options( - super().get_options(), - self.create_option( - options.UserIntegerOption, - OptionKey(f'{self.language}_thread_count', machine=self.for_machine), - 'Number of threads to use in web assembly, set to 0 to disable', - (0, None, 4), # Default was picked at random - ), - ) + opts = super().get_options() + + key = OptionKey(f'{self.language}_thread_count', machine=self.for_machine) + opts[key] = options.UserIntegerOption( + self.make_option_name(key), + 'Number of threads to use in web assembly, set to 0 to disable', + (0, None, 4)) # Default was picked at random + + return opts @classmethod def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 56bc12d45feb..262a4c484f2e 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -99,6 +99,16 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ '3': default_warn_args + ['-Wextra', '-Wpedantic'], 'everything': ['-Weverything']} + def form_compileropt_key(self, basename: str) -> OptionKey: + if basename == 'std': + return OptionKey('c_std', machine=self.for_machine) + return super().form_compileropt_key(basename) + + def make_option_name(self, key: OptionKey) -> str: + if key.name == 'std': + return 'c_std' + return super().make_option_name(key) + def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: args = [] std = options.get_value(self.form_compileropt_key('std')) diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index de968be42954..104d0cb82ebe 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -35,6 +35,16 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ linker=linker) CLikeCompiler.__init__(self) + def form_compileropt_key(self, basename: str) -> OptionKey: + if basename == 'std': + return OptionKey('cpp_std', machine=self.for_machine) + return super().form_compileropt_key(basename) + + def make_option_name(self, key: OptionKey) -> str: + if key.name == 'std': + return 'cpp_std' + return super().make_option_name(key) + @staticmethod def get_display_language() -> str: return 'Objective-C++' @@ -43,11 +53,6 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: code = '#import\nclass MyClass;int main(void) { return 0; }\n' return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code) - def form_compileropt_key(self, basename: str) -> OptionKey: - if basename == 'std': - return OptionKey(f'cpp_{basename}', machine=self.for_machine) - return super().form_compileropt_key(basename) - def get_options(self) -> coredata.MutableKeyedOptionDictType: opts = super().get_options() key = self.form_compileropt_key('std') diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index b8588b8d7db6..40c85ee68074 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -234,11 +234,16 @@ def use_linker_args(cls, linker: str, version: str) -> T.List[str]: # use_linker_args method instead. def get_options(self) -> MutableKeyedOptionDictType: - return dict((self.create_option(options.UserComboOption, - self.form_compileropt_key('std'), - 'Rust edition to use', - ['none', '2015', '2018', '2021', '2024'], - 'none'),)) + opts = super().get_options() + + key = self.form_compileropt_key('std') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'Rust edition to use', + ['none', '2015', '2018', '2021', '2024'], + 'none') + + return opts def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: # Rust doesn't have dependency compile arguments so simply return From 0e11b90d6f2f9c3e18cb8ff84b1622640666e826 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Aug 2024 09:47:39 -0700 Subject: [PATCH 268/624] options: use dataclasses for UserOption This reduces code, makes this clearer, and will be a nice step toward the goal of getting everything typesafe. For `UserIntegerOption` this makes a fairly nice, but substantial change in that the old method used a tuple of `(min, value, max)` to pass to the initializer, while all other types just passed `value`. The new `UserIntegerOption` does the same, with keyword arguments for the min and max values. --- mesonbuild/compilers/compilers.py | 6 +- mesonbuild/compilers/cpp.py | 24 ++-- mesonbuild/compilers/cuda.py | 5 +- mesonbuild/compilers/cython.py | 8 +- mesonbuild/compilers/fortran.py | 4 +- mesonbuild/compilers/mixins/emscripten.py | 3 +- mesonbuild/compilers/rust.py | 4 +- mesonbuild/coredata.py | 3 +- mesonbuild/optinterpreter.py | 16 +-- mesonbuild/options.py | 127 ++++++++++------------ 10 files changed, 96 insertions(+), 104 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 12bcd1e4c972..1432afb8573f 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -224,16 +224,16 @@ class BaseOption(T.Generic[_T]): choices: T.Any = None def init_option(self, name: OptionKey) -> options.UserOption[_T]: - keywords = {'value': self.default} + keywords = {} if self.choices: keywords['choices'] = self.choices - return self.opt_type(name.name, self.description, **keywords) + return self.opt_type(name.name, self.description, self.default, **keywords) BASE_OPTIONS: T.Mapping[OptionKey, BaseOption] = { OptionKey('b_pch'): BaseOption(options.UserBooleanOption, 'Use precompiled headers', True), OptionKey('b_lto'): BaseOption(options.UserBooleanOption, 'Use link time optimization', False), - OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', (None, None, 0)), + OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', 0), OptionKey('b_lto_mode'): BaseOption(options.UserComboOption, 'Select between different LTO modes.', 'default', choices=['default', 'thin']), OptionKey('b_thinlto_cache'): BaseOption(options.UserBooleanOption, 'Use LLVM ThinLTO caching for faster incremental builds', False), diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index b17e18b3ed35..7fa9aa833ff2 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -242,8 +242,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -398,8 +398,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('std') std_opt = opts[key] @@ -448,8 +448,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -577,8 +577,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('debugstl') opts[key] = options.UserBooleanOption( @@ -660,8 +660,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -753,8 +753,8 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default') + 'default', + ['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 07fda95dd6d5..aefcc512bce1 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -648,12 +648,13 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() + # XXX: cpp_std is correct, the annotations are wrong key = self.form_compileropt_key('std') opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ language standard to use with CUDA', - cpp_stds, - 'none') + 'none', + cpp_stds) key = self.form_compileropt_key('ccbindir') opts[key] = options.UserStringOption( diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index 2d0f21d00aa2..ba04aea0bced 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -73,15 +73,15 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'Python version to target', - ['2', '3'], - '3') + '3', + ['2', '3']) key = self.form_compileropt_key('language') opts[key] = options.UserComboOption( self.make_option_name(key), 'Output C or C++ files', - ['c', 'cpp'], - 'c') + 'c', + ['c', 'cpp']) return opts diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index f771e4c40495..b004727b4a44 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -120,8 +120,8 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts[key] = options.UserComboOption( self.make_option_name(key), 'Fortran language standard to use', - ['none'], - 'none') + 'none', + ['none']) return opts diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py index fa862056923a..24ffceda63ed 100644 --- a/mesonbuild/compilers/mixins/emscripten.py +++ b/mesonbuild/compilers/mixins/emscripten.py @@ -63,7 +63,8 @@ def get_options(self) -> coredata.MutableKeyedOptionDictType: opts[key] = options.UserIntegerOption( self.make_option_name(key), 'Number of threads to use in web assembly, set to 0 to disable', - (0, None, 4)) # Default was picked at random + 4, # Default was picked at random + min_value=0) return opts diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 40c85ee68074..7fe16c6f8a65 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -240,8 +240,8 @@ def get_options(self) -> MutableKeyedOptionDictType: opts[key] = options.UserComboOption( self.make_option_name(key), 'Rust edition to use', - ['none', '2015', '2018', '2021', '2024'], - 'none') + 'none', + ['none', '2015', '2018', '2021', '2024']) return opts diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index a17a39fb6198..d295858dc60b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -434,7 +434,8 @@ def init_backend_options(self, backend_name: str) -> None: 'backend_max_links', 'Maximum number of linker processes to run or 0 for no ' 'limit', - (0, None, 0))) + 0, + min_value=0)) elif backend_name.startswith('vs'): self.optstore.add_system_option('backend_startup_project', options.UserStringOption( 'backend_startup_project', diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 4688ee4c4f49..99efd511ea79 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -213,7 +213,7 @@ def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None: KwargInfo('value', str, default=''), ) def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption: - return options.UserStringOption(name, description, kwargs['value'], *args) + return options.UserStringOption(name, description, kwargs['value'], None, *args) @typed_kwargs( 'boolean option', @@ -226,7 +226,8 @@ def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPREC ), ) def boolean_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: BooleanArgs) -> options.UserOption: - return options.UserBooleanOption(name, description, kwargs['value'], *args) + yielding, deprecated = args + return options.UserBooleanOption(name, description, kwargs['value'], yielding=yielding, deprecated=deprecated) @typed_kwargs( 'combo option', @@ -238,7 +239,7 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA value = kwargs['value'] if value is None: value = kwargs['choices'][0] - return options.UserComboOption(name, description, choices, value, *args) + return options.UserComboOption(name, description, value, choices, *args) @typed_kwargs( 'integer option', @@ -253,9 +254,8 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA KwargInfo('max', (int, NoneType)), ) def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption: - value = kwargs['value'] - inttuple = (kwargs['min'], kwargs['max'], value) - return options.UserIntegerOption(name, description, inttuple, *args) + return options.UserIntegerOption( + name, description, kwargs['value'], None, *args, min_value=kwargs['min'], max_value=kwargs['max']) @typed_kwargs( 'string array option', @@ -270,8 +270,10 @@ def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _ FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject) else: raise mesonlib.MesonException('Value does not define an array: ' + value) + # XXX: the value of choices is correct, the annotation is wrong. + # the annotation will be fixed in a later commit return options.UserArrayOption(name, description, value, - choices=choices, + choices=choices, # type: ignore[arg-type] yielding=args[0], deprecated=args[1]) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index ec1db898058d..a53cb91a4774 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -7,6 +7,7 @@ from itertools import chain from functools import total_ordering import argparse +import dataclasses import typing as T from .mesonlib import ( @@ -28,7 +29,9 @@ from . import mlog if T.TYPE_CHECKING: - from typing_extensions import TypedDict + from typing_extensions import TypeAlias, TypedDict + + DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] class ArgparseKWs(TypedDict, total=False): @@ -244,19 +247,19 @@ def without_module_prefix(self) -> 'OptionKey': return self +@dataclasses.dataclass class UserOption(T.Generic[_T], HoldableObject): - def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]], - yielding: bool, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__() - self.name = name - self.choices = choices - self.description = description - if not isinstance(yielding, bool): - raise MesonException('Value of "yielding" must be a boolean.') - self.yielding = yielding - self.deprecated = deprecated - self.readonly = False + + name: str + description: str + value_: dataclasses.InitVar[_T] + choices: T.Optional[T.Union[str, T.List[_T]]] = None + yielding: bool = DEFAULT_YIELDING + deprecated: DeprecatedType = False + readonly: bool = dataclasses.field(default=False, init=False) + + def __post_init__(self, value_: _T) -> None: + self.value = self.validate_value(value_) def listify(self, value: T.Any) -> T.List[T.Any]: return [value] @@ -279,27 +282,23 @@ def validate_value(self, value: T.Any) -> _T: raise RuntimeError('Derived option class did not override validate_value.') def set_value(self, newvalue: T.Any) -> bool: - oldvalue = getattr(self, 'value', None) + oldvalue = self.value self.value = self.validate_value(newvalue) return self.value != oldvalue +@dataclasses.dataclass class UserStringOption(UserOption[str]): - def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, None, yielding, deprecated) - self.set_value(value) def validate_value(self, value: T.Any) -> str: if not isinstance(value, str): raise MesonException(f'The value of option "{self.name}" is "{value}", which is not a string.') return value +@dataclasses.dataclass class UserBooleanOption(UserOption[bool]): - def __init__(self, name: str, description: str, value: bool, yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, [True, False], yielding, deprecated) - self.set_value(value) + + choices: T.List[bool] = dataclasses.field(default_factory=lambda: [True, False]) def __bool__(self) -> bool: return self.value @@ -315,20 +314,20 @@ def validate_value(self, value: T.Any) -> bool: return False raise MesonException(f'Option "{self.name}" value {value} is not boolean (true or false).') +@dataclasses.dataclass class UserIntegerOption(UserOption[int]): - def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - min_value, max_value, default_value = value - self.min_value = min_value - self.max_value = max_value - c: T.List[str] = [] - if min_value is not None: - c.append('>=' + str(min_value)) - if max_value is not None: - c.append('<=' + str(max_value)) - choices = ', '.join(c) - super().__init__(name, description, choices, yielding, deprecated) - self.set_value(default_value) + + min_value: T.Optional[int] = None + max_value: T.Optional[int] = None + + def __post_init__(self, value_: int) -> None: + super().__post_init__(value_) + choices: T.List[str] = [] + if self.min_value is not None: + choices.append(f'>= {self.min_value!s}') + if self.max_value is not None: + choices.append(f'<= {self.max_value!s}') + self.choices = ', '.join(choices) def validate_value(self, value: T.Any) -> int: if isinstance(value, str): @@ -354,11 +353,11 @@ class OctalInt(int): def __str__(self) -> str: return oct(int(self)) +@dataclasses.dataclass class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]): - def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, (0, 0o777, value), yielding, deprecated) - self.choices = ['preserve', '0000-0777'] + + min_value: T.Optional[int] = dataclasses.field(default=0, init=False) + max_value: T.Optional[int] = dataclasses.field(default=0o777, init=False) def printable_value(self) -> str: if self.value == 'preserve': @@ -376,17 +375,8 @@ def toint(self, valuestring: T.Union[str, OctalInt]) -> int: except ValueError as e: raise MesonException(f'Invalid mode for option "{self.name}" {e}') +@dataclasses.dataclass class UserComboOption(UserOption[str]): - def __init__(self, name: str, description: str, choices: T.List[str], value: T.Any, - yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, choices, yielding, deprecated) - if not isinstance(self.choices, list): - raise MesonException(f'Combo choices for option "{self.name}" must be an array.') - for i in self.choices: - if not isinstance(i, str): - raise MesonException(f'Combo choice elements for option "{self.name}" must be strings.') - self.set_value(value) def validate_value(self, value: T.Any) -> str: if value not in self.choices: @@ -402,16 +392,12 @@ def validate_value(self, value: T.Any) -> str: value, _type, self.name, optionsstring)) return value +@dataclasses.dataclass class UserArrayOption(UserOption[T.List[str]]): - def __init__(self, name: str, description: str, value: T.Union[str, T.List[str]], - split_args: bool = False, - allow_dups: bool = False, yielding: bool = DEFAULT_YIELDING, - choices: T.Optional[T.List[str]] = None, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, choices if choices is not None else [], yielding, deprecated) - self.split_args = split_args - self.allow_dups = allow_dups - self.set_value(value) + + value_: dataclasses.InitVar[T.Union[str, T.List[str]]] + split_args: bool = False + allow_dups: bool = False def listify(self, value: T.Any) -> T.List[T.Any]: try: @@ -447,13 +433,12 @@ def extend_value(self, value: T.Union[str, T.List[str]]) -> None: self.set_value(self.value + new) +@dataclasses.dataclass class UserFeatureOption(UserComboOption): - static_choices = ['enabled', 'disabled', 'auto'] - def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING, - deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): - super().__init__(name, description, self.static_choices, value, yielding, deprecated) - self.name: T.Optional[str] = None # TODO: Refactor options to all store their name + choices: T.List[str] = dataclasses.field( + # Ensure we get a copy with the lambda + default_factory=lambda: ['enabled', 'disabled', 'auto'], init=False) def is_enabled(self) -> bool: return self.value == 'enabled' @@ -464,6 +449,8 @@ def is_disabled(self) -> bool: def is_auto(self) -> bool: return self.value == 'auto' + +@dataclasses.dataclass(init=False) class UserStdOption(UserComboOption): ''' UserOption specific to c_std and cpp_std options. User can set a list of @@ -484,7 +471,7 @@ def __init__(self, lang: str, all_stds: T.List[str]) -> None: # Map a deprecated std to its replacement. e.g. gnu11 -> c11. self.deprecated_stds: T.Dict[str, str] = {} opt_name = 'cpp_std' if lang == 'c++' else f'{lang}_std' - super().__init__(opt_name, f'{lang} language standard to use', ['none'], 'none') + super().__init__(opt_name, f'{lang} language standard to use', 'none', ['none']) def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None: assert all(std in self.all_stds for std in versions) @@ -531,19 +518,21 @@ class BuiltinOption(T.Generic[_T]): """ def __init__(self, opt_type: T.Type[UserOption[_T]], description: str, default: T.Any, yielding: bool = True, *, - choices: T.Any = None, readonly: bool = False): + choices: T.Any = None, readonly: bool = False, **kwargs: object): self.opt_type = opt_type self.description = description self.default = default self.choices = choices self.yielding = yielding self.readonly = readonly + self.kwargs = kwargs def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> UserOption[_T]: """Create an instance of opt_type and return it.""" if value is None: value = self.prefixed_default(name, prefix) - keywords = {'yielding': self.yielding, 'value': value} + keywords = {'yielding': self.yielding, 'value_': value} + keywords.update(self.kwargs) if self.choices: keywords['choices'] = self.choices o = self.opt_type(name.name, self.description, **keywords) @@ -561,8 +550,6 @@ def _argparse_action(self) -> T.Optional[str]: def _argparse_choices(self) -> T.Any: if self.opt_type is UserBooleanOption: return [True, False] - elif self.opt_type is UserFeatureOption: - return UserFeatureOption.static_choices return self.choices @staticmethod @@ -646,7 +633,7 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi (OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), (OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), (OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), - (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))), + (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', 4, min_value=2)), (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'], yielding=False)), (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])), @@ -659,7 +646,7 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi # Python module (OptionKey('python.bytecompile'), - BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))), + BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', 0, min_value=-1, max_value=2)), (OptionKey('python.install_env'), BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])), (OptionKey('python.platlibdir'), From ba3460eb11bbceaf4fef7352bf286cf27184c99a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 Aug 2024 15:47:20 -0700 Subject: [PATCH 269/624] options: Add an EnumeratedUserOption class This will allow us to take choices out of the UserOption class, which doesn't actually use this attribute. --- mesonbuild/cmake/interpreter.py | 12 ++---- mesonbuild/compilers/c.py | 14 +++---- mesonbuild/compilers/compilers.py | 13 ++++++- mesonbuild/compilers/cpp.py | 28 ++++++-------- mesonbuild/compilers/cuda.py | 3 +- mesonbuild/compilers/cython.py | 4 +- mesonbuild/compilers/fortran.py | 15 +++----- mesonbuild/compilers/rust.py | 2 +- mesonbuild/coredata.py | 2 +- mesonbuild/mconf.py | 2 +- mesonbuild/mintro.py | 2 +- mesonbuild/optinterpreter.py | 17 ++++----- mesonbuild/options.py | 61 +++++++++++++++++++++---------- 13 files changed, 93 insertions(+), 82 deletions(-) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index fafee86abd4f..27ce54e2074d 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -19,6 +19,7 @@ from .traceparser import CMakeTraceParser from .tracetargets import resolve_cmake_trace_targets from .. import mlog, mesonlib +from .. import options from ..mesonlib import MachineChoice, OrderedSet, path_is_in_root, relative_to_if_possible from ..options import OptionKey from ..mesondata import DataFile @@ -533,17 +534,12 @@ def _all_source_suffixes(self) -> 'ImmutableListProtocol[str]': @lru_cache(maxsize=None) def _all_lang_stds(self, lang: str) -> 'ImmutableListProtocol[str]': try: - res = self.env.coredata.optstore.get_value_object(OptionKey(f'{lang}_std', machine=MachineChoice.BUILD)).choices + opt = self.env.coredata.optstore.get_value_object(OptionKey(f'{lang}_std', machine=MachineChoice.BUILD)) + assert isinstance(opt, (options.UserStdOption, options.UserComboOption)), 'for mypy' + return opt.choices or [] except KeyError: return [] - # TODO: Get rid of this once we have proper typing for options - assert isinstance(res, list) - for i in res: - assert isinstance(i, str) - - return res - def process_inter_target_dependencies(self) -> None: # Move the dependencies from all TRANSFER_DEPENDENCIES_FROM to the target to_process = list(self.depends) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index d8ad9f793ec0..4f93ea14e0ad 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -124,7 +124,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() if self.info.is_windows() or self.info.is_cygwin(): key = self.form_compileropt_key('winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Windows libraries to link against', gnu_winlibs) @@ -257,7 +257,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() if self.info.is_windows() or self.info.is_cygwin(): key = self.form_compileropt_key('winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Windows libraries to link against', gnu_winlibs) @@ -406,7 +406,7 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase): def get_options(self) -> MutableKeyedOptionDictType: opts = super().get_options() key = self.form_compileropt_key('winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Windows libraries to link against', msvc_winlibs) @@ -733,9 +733,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - c_stds = ['c99'] - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + c_stds + self._update_language_stds(opts, ['c99']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -763,9 +761,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - c_stds = ['c99'] - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + c_stds + self._update_language_stds(opts, ['c99']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 1432afb8573f..5777f19f1c75 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1359,6 +1359,15 @@ def get_preprocessor(self) -> Compiler: def form_compileropt_key(self, basename: str) -> OptionKey: return OptionKey(f'{self.language}_{basename}', machine=self.for_machine) + def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[str]) -> None: + key = self.form_compileropt_key('std') + std = opts[key] + assert isinstance(std, (options.UserStdOption, options.UserComboOption)), 'for mypy' + if 'none' not in value: + value = ['none'] + value + std.choices = value + + def get_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, @@ -1374,12 +1383,12 @@ def get_global_options(lang: str, comp_options = env.options.get(comp_key, []) link_options = env.options.get(largkey, []) - cargs = options.UserArrayOption( + cargs = options.UserStringArrayOption( f'{lang}_{argkey.name}', description + ' compiler', comp_options, split_args=True, allow_dups=True) - largs = options.UserArrayOption( + largs = options.UserStringArrayOption( f'{lang}_{largkey.name}', description + ' linker', link_options, split_args=True, allow_dups=True) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 7fa9aa833ff2..80f84b38ea4d 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -243,7 +243,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -259,7 +259,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': if self.info.is_windows() or self.info.is_cygwin(): key = self.form_compileropt_key('winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Win libraries to link against', gnu_winlibs) @@ -399,7 +399,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('std') std_opt = opts[key] @@ -449,7 +449,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -465,7 +465,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': if self.info.is_windows() or self.info.is_cygwin(): key = key.evolve(name='cpp_winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Win libraries to link against', gnu_winlibs) @@ -578,7 +578,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('debugstl') opts[key] = options.UserBooleanOption( @@ -661,7 +661,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -691,9 +691,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': c_stds += ['c++2a'] g_stds += ['gnu++2a'] - std_opt = opts[self.form_compileropt_key('std')] - assert isinstance(std_opt, options.UserStdOption), 'for mypy' - std_opt.set_versions(c_stds + g_stds) + self._update_language_stds(opts, c_stds + g_stds) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -754,7 +752,7 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List self.make_option_name(key), 'C++ exception handling type.', 'default', - ['none', 'default', 'a', 's', 'sc']) + choices=['none', 'default', 'a', 's', 'sc']) key = self.form_compileropt_key('rtti') opts[key] = options.UserBooleanOption( @@ -763,7 +761,7 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List True) key = self.form_compileropt_key('winlibs') - opts[key] = options.UserArrayOption( + opts[key] = options.UserStringArrayOption( self.make_option_name(key), 'Standard Win libraries to link against', msvc_winlibs) @@ -1040,8 +1038,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + self._update_language_stds(opts, []) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -1069,8 +1066,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + self._update_language_stds(opts, []) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index aefcc512bce1..6a49d95aeb6d 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -648,13 +648,12 @@ def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - # XXX: cpp_std is correct, the annotations are wrong key = self.form_compileropt_key('std') opts[key] = options.UserComboOption( self.make_option_name(key), 'C++ language standard to use with CUDA', 'none', - cpp_stds) + choices=cpp_stds) key = self.form_compileropt_key('ccbindir') opts[key] = options.UserStringOption( diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index ba04aea0bced..ed0ab31ad376 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -74,14 +74,14 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'Python version to target', '3', - ['2', '3']) + choices=['2', '3']) key = self.form_compileropt_key('language') opts[key] = options.UserComboOption( self.make_option_name(key), 'Output C or C++ files', 'c', - ['c', 'cpp']) + choices=['c', 'cpp']) return opts diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index b004727b4a44..72c9a5a97fdd 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -121,7 +121,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self.make_option_name(key), 'Fortran language standard to use', 'none', - ['none']) + choices=['none']) return opts @@ -281,8 +281,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': fortran_stds += ['f2008'] if version_compare(self.version, '>=8.0.0'): fortran_stds += ['f2018'] - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + fortran_stds + self._update_language_stds(opts, fortran_stds) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -338,9 +337,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - fortran_stds = ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts'] - key = self.form_compileropt_key('std') - opts[key].choices = ['none'] + fortran_stds + self._update_language_stds(opts, ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts']) return opts def get_module_outdir_args(self, path: str) -> T.List[str]: @@ -418,8 +415,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - key = self.form_compileropt_key('std') - opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] + self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -473,8 +469,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - key = self.form_compileropt_key('std') - opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] + self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 7fe16c6f8a65..aacdc07d7ccc 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -241,7 +241,7 @@ def get_options(self) -> MutableKeyedOptionDictType: self.make_option_name(key), 'Rust edition to use', 'none', - ['none', '2015', '2018', '2021', '2024']) + choices=['none', '2015', '2018', '2021', '2024']) return opts diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index d295858dc60b..5469616f8b93 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -599,7 +599,7 @@ def update_project_options(self, project_options: 'MutableKeyedOptionDictType', oldval = self.optstore.get_value_object(key) if type(oldval) is not type(value): self.optstore.set_value(key, value.value) - elif oldval.choices != value.choices: + elif oldval.printable_choices() != value.printable_choices(): # 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. diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 3a6343ba1233..7c2270edcbc3 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -240,7 +240,7 @@ def print_options(self, title: str, opts: 'T.Union[dict[OptionKey, UserOption[An printable_value = '' if isinstance(o, options.UserFeatureOption) and o.is_auto(): printable_value = auto.printable_value() - self.add_option(str(root), o.description, printable_value, o.choices) + self.add_option(str(root), o.description, printable_value, o.printable_choices()) def print_conf(self, pager: bool) -> None: if pager: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index cd68911ce322..cf76b6012ce5 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -319,7 +319,7 @@ def add_keys(opts: 'T.Union[dict[OptionKey, UserOption[Any]], cdata.KeyedOptionD typestr = 'combo' elif isinstance(opt, options.UserIntegerOption): typestr = 'integer' - elif isinstance(opt, options.UserArrayOption): + elif isinstance(opt, options.UserStringArrayOption): typestr = 'array' c = opt.printable_choices() if c: diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 99efd511ea79..8c0d1daec217 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -213,7 +213,7 @@ def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None: KwargInfo('value', str, default=''), ) def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption: - return options.UserStringOption(name, description, kwargs['value'], None, *args) + return options.UserStringOption(name, description, kwargs['value'], *args) @typed_kwargs( 'boolean option', @@ -239,7 +239,7 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA value = kwargs['value'] if value is None: value = kwargs['choices'][0] - return options.UserComboOption(name, description, value, choices, *args) + return options.UserComboOption(name, description, value, *args, choices) @typed_kwargs( 'integer option', @@ -255,7 +255,7 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA ) def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption: return options.UserIntegerOption( - name, description, kwargs['value'], None, *args, min_value=kwargs['min'], max_value=kwargs['max']) + name, description, kwargs['value'], *args, min_value=kwargs['min'], max_value=kwargs['max']) @typed_kwargs( 'string array option', @@ -270,12 +270,11 @@ def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _ FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject) else: raise mesonlib.MesonException('Value does not define an array: ' + value) - # XXX: the value of choices is correct, the annotation is wrong. - # the annotation will be fixed in a later commit - return options.UserArrayOption(name, description, value, - choices=choices, # type: ignore[arg-type] - yielding=args[0], - deprecated=args[1]) + return options.UserStringArrayOption( + name, description, value, + choices=choices, + yielding=args[0], + deprecated=args[1]) @typed_kwargs( 'feature option', diff --git a/mesonbuild/options.py b/mesonbuild/options.py index a53cb91a4774..ae580785ec14 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -253,7 +253,6 @@ class UserOption(T.Generic[_T], HoldableObject): name: str description: str value_: dataclasses.InitVar[_T] - choices: T.Optional[T.Union[str, T.List[_T]]] = None yielding: bool = DEFAULT_YIELDING deprecated: DeprecatedType = False readonly: bool = dataclasses.field(default=False, init=False) @@ -269,11 +268,7 @@ def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bo return self.value def printable_choices(self) -> T.Optional[T.List[str]]: - if not self.choices: - return None - if isinstance(self.choices, str): - return [self.choices] - return [str(c) for c in self.choices] + return None # Check that the input is a valid value and return the # "cleaned" or "native" version. For example the Boolean @@ -287,6 +282,17 @@ def set_value(self, newvalue: T.Any) -> bool: return self.value != oldvalue +@dataclasses.dataclass +class EnumeratedUserOption(UserOption[_T]): + + """A generic UserOption that has enumerated values.""" + + choices: T.List[_T] = dataclasses.field(default_factory=list) + + def printable_choices(self) -> T.Optional[T.List[str]]: + return [str(c) for c in self.choices] + + @dataclasses.dataclass class UserStringOption(UserOption[str]): @@ -296,7 +302,7 @@ def validate_value(self, value: T.Any) -> str: return value @dataclasses.dataclass -class UserBooleanOption(UserOption[bool]): +class UserBooleanOption(EnumeratedUserOption[bool]): choices: T.List[bool] = dataclasses.field(default_factory=lambda: [True, False]) @@ -327,7 +333,10 @@ def __post_init__(self, value_: int) -> None: choices.append(f'>= {self.min_value!s}') if self.max_value is not None: choices.append(f'<= {self.max_value!s}') - self.choices = ', '.join(choices) + self.__choices: str = ', '.join(choices) + + def printable_choices(self) -> T.Optional[T.List[str]]: + return [self.__choices] def validate_value(self, value: T.Any) -> int: if isinstance(value, str): @@ -376,7 +385,7 @@ def toint(self, valuestring: T.Union[str, OctalInt]) -> int: raise MesonException(f'Invalid mode for option "{self.name}" {e}') @dataclasses.dataclass -class UserComboOption(UserOption[str]): +class UserComboOption(EnumeratedUserOption[str]): def validate_value(self, value: T.Any) -> str: if value not in self.choices: @@ -390,15 +399,32 @@ def validate_value(self, value: T.Any) -> str: raise MesonException('Value "{}" (of type "{}") for option "{}" is not one of the choices.' ' Possible choices are (as string): {}.'.format( value, _type, self.name, optionsstring)) + + assert isinstance(value, str), 'for mypy' return value @dataclasses.dataclass -class UserArrayOption(UserOption[T.List[str]]): +class UserArrayOption(UserOption[T.List[_T]]): - value_: dataclasses.InitVar[T.Union[str, T.List[str]]] + value_: dataclasses.InitVar[T.Union[_T, T.List[_T]]] + choices: T.Optional[T.List[_T]] = None split_args: bool = False allow_dups: bool = False + def extend_value(self, value: T.Union[str, T.List[str]]) -> None: + """Extend the value with an additional value.""" + new = self.validate_value(value) + self.set_value(self.value + new) + + def printable_choices(self) -> T.Optional[T.List[str]]: + if self.choices is None: + return None + return [str(c) for c in self.choices] + + +@dataclasses.dataclass +class UserStringArrayOption(UserArrayOption[str]): + def listify(self, value: T.Any) -> T.List[T.Any]: try: return listify_array_value(value, self.split_args) @@ -427,11 +453,6 @@ def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]: ) return newvalue - def extend_value(self, value: T.Union[str, T.List[str]]) -> None: - """Extend the value with an additional value.""" - new = self.validate_value(value) - self.set_value(self.value + new) - @dataclasses.dataclass class UserFeatureOption(UserComboOption): @@ -471,7 +492,7 @@ def __init__(self, lang: str, all_stds: T.List[str]) -> None: # Map a deprecated std to its replacement. e.g. gnu11 -> c11. self.deprecated_stds: T.Dict[str, str] = {} opt_name = 'cpp_std' if lang == 'c++' else f'{lang}_std' - super().__init__(opt_name, f'{lang} language standard to use', 'none', ['none']) + super().__init__(opt_name, f'{lang} language standard to use', 'none', choices=['none']) def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None: assert all(std in self.all_stds for std in versions) @@ -637,7 +658,7 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'], yielding=False)), (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])), - (OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])), + (OptionKey('force_fallback_for'), BuiltinOption(UserStringArrayOption, 'Force fallback for those subprojects', [])), (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)), # Pkgconfig module @@ -660,8 +681,8 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items())) BUILTIN_OPTIONS_PER_MACHINE: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('pkg_config_path'), BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [])), - (OptionKey('cmake_prefix_path'), BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])), + (OptionKey('pkg_config_path'), BuiltinOption(UserStringArrayOption, 'List of additional paths for pkg-config to search', [])), + (OptionKey('cmake_prefix_path'), BuiltinOption(UserStringArrayOption, 'List of additional prefixes for cmake to search', [])), ]) # Special prefix-dependent defaults for installation directories that reside in From 164a18d84f4543669e5cab75490ff3629aa961d4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 30 Aug 2024 10:52:17 -0700 Subject: [PATCH 270/624] options: fix typing of add_to_argparse Which wants a string, but then passes that string to a function that wants an OptionKey, which means that we'll always miss the lookup in BULITIN_DIR_NOPREFIX_OPTIONS, and return the default. The only case this gets used we cast an OptionKey to str, and then pass that. So instead, do the cast inside the function when necessary and pass the OptionKey --- mesonbuild/coredata.py | 6 +++--- mesonbuild/options.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 5469616f8b93..6b8cec63f2d3 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -868,10 +868,10 @@ def save(obj: CoreData, build_dir: str) -> str: def register_builtin_arguments(parser: argparse.ArgumentParser) -> None: for n, b in options.BUILTIN_OPTIONS.items(): - b.add_to_argparse(str(n), parser, '') + b.add_to_argparse(n, parser, '') for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items(): - b.add_to_argparse(str(n), parser, ' (just for host machine)') - b.add_to_argparse(str(n.as_build()), parser, ' (just for build machine)') + b.add_to_argparse(n, parser, ' (just for host machine)') + b.add_to_argparse(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.') diff --git a/mesonbuild/options.py b/mesonbuild/options.py index ae580785ec14..b61babec2e8f 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -589,7 +589,7 @@ def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any: pass return self.default - def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None: + def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: kwargs: ArgparseKWs = {} c = self._argparse_choices() @@ -602,9 +602,9 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi if c and not b: kwargs['choices'] = c kwargs['default'] = argparse.SUPPRESS - kwargs['dest'] = name + kwargs['dest'] = str(name) - cmdline_name = self.argparse_name_to_arg(name) + cmdline_name = self.argparse_name_to_arg(str(name)) parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs) From 177148686c8c207268619d2d58ee021b1d6fd082 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 30 Aug 2024 11:12:57 -0700 Subject: [PATCH 271/624] options: split UserIntegerOption and UserUmaskOption They are very similar, but they are not exactly the same. By splitting them we can get full type safety, and run mypy over the options.py file! --- mesonbuild/mintro.py | 4 ++-- mesonbuild/options.py | 45 ++++++++++++++++++++++++++++--------------- run_mypy.py | 1 + 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index cf76b6012ce5..4b65c5aca048 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -317,7 +317,7 @@ def add_keys(opts: 'T.Union[dict[OptionKey, UserOption[Any]], cdata.KeyedOptionD elif isinstance(opt, options.UserComboOption): optdict['choices'] = opt.printable_choices() typestr = 'combo' - elif isinstance(opt, options.UserIntegerOption): + elif isinstance(opt, (options.UserIntegerOption, options.UserUmaskOption)): typestr = 'integer' elif isinstance(opt, options.UserStringArrayOption): typestr = 'array' @@ -325,7 +325,7 @@ def add_keys(opts: 'T.Union[dict[OptionKey, UserOption[Any]], cdata.KeyedOptionD if c: optdict['choices'] = c else: - raise RuntimeError("Unknown option type") + raise RuntimeError('Unknown option type: ', repr(type(opt))) optdict['type'] = typestr optdict['description'] = opt.description optlist.append(optdict) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index b61babec2e8f..87c31d21c476 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -29,7 +29,7 @@ from . import mlog if T.TYPE_CHECKING: - from typing_extensions import TypeAlias, TypedDict + from typing_extensions import Literal, TypeAlias, TypedDict DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] @@ -320,13 +320,16 @@ def validate_value(self, value: T.Any) -> bool: return False raise MesonException(f'Option "{self.name}" value {value} is not boolean (true or false).') -@dataclasses.dataclass -class UserIntegerOption(UserOption[int]): - min_value: T.Optional[int] = None - max_value: T.Optional[int] = None +class _UserIntegerBase(UserOption[_T]): + + min_value: T.Optional[int] + max_value: T.Optional[int] + + if T.TYPE_CHECKING: + def toint(self, v: str) -> int: ... - def __post_init__(self, value_: int) -> None: + def __post_init__(self, value_: _T) -> None: super().__post_init__(value_) choices: T.List[str] = [] if self.min_value is not None: @@ -338,16 +341,23 @@ def __post_init__(self, value_: int) -> None: def printable_choices(self) -> T.Optional[T.List[str]]: return [self.__choices] - def validate_value(self, value: T.Any) -> int: + def validate_value(self, value: T.Any) -> _T: if isinstance(value, str): - value = self.toint(value) + value = T.cast('_T', self.toint(value)) if not isinstance(value, int): raise MesonException(f'Value {value!r} for option "{self.name}" is not an integer.') if self.min_value is not None and value < self.min_value: raise MesonException(f'Value {value} for option "{self.name}" is less than minimum value {self.min_value}.') if self.max_value is not None and value > self.max_value: raise MesonException(f'Value {value} for option "{self.name}" is more than maximum value {self.max_value}.') - return value + return T.cast('_T', value) + + +@dataclasses.dataclass +class UserIntegerOption(_UserIntegerBase[int]): + + min_value: T.Optional[int] = None + max_value: T.Optional[int] = None def toint(self, valuestring: str) -> int: try: @@ -355,6 +365,7 @@ def toint(self, valuestring: str) -> int: except ValueError: raise MesonException(f'Value string "{valuestring}" for option "{self.name}" is not convertible to an integer.') + class OctalInt(int): # NinjaBackend.get_user_option_args uses str() to converts it to a command line option # UserUmaskOption.toint() uses int(str, 8) to convert it to an integer @@ -362,28 +373,30 @@ class OctalInt(int): def __str__(self) -> str: return oct(int(self)) + @dataclasses.dataclass -class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]): +class UserUmaskOption(_UserIntegerBase[T.Union["Literal['preserve']", OctalInt]]): min_value: T.Optional[int] = dataclasses.field(default=0, init=False) max_value: T.Optional[int] = dataclasses.field(default=0o777, init=False) def printable_value(self) -> str: - if self.value == 'preserve': - return self.value - return format(self.value, '04o') + if isinstance(self.value, int): + return format(self.value, '04o') + return self.value - def validate_value(self, value: T.Any) -> T.Union[str, OctalInt]: + def validate_value(self, value: T.Any) -> T.Union[Literal['preserve'], OctalInt]: if value == 'preserve': return 'preserve' return OctalInt(super().validate_value(value)) - def toint(self, valuestring: T.Union[str, OctalInt]) -> int: + def toint(self, valuestring: str) -> int: try: return int(valuestring, 8) except ValueError as e: raise MesonException(f'Invalid mode for option "{self.name}" {e}') + @dataclasses.dataclass class UserComboOption(EnumeratedUserOption[str]): @@ -581,7 +594,7 @@ def argparse_name_to_arg(name: str) -> str: return '--' + name.replace('_', '-') def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any: - if self.opt_type in [UserComboOption, UserIntegerOption]: + if self.opt_type in {UserComboOption, UserIntegerOption, UserUmaskOption}: return self.default try: return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix] diff --git a/run_mypy.py b/run_mypy.py index 9a4e241ec833..4f7a6317be57 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -79,6 +79,7 @@ 'mesonbuild/msetup.py', 'mesonbuild/mtest.py', 'mesonbuild/optinterpreter.py', + 'mesonbuild/options.py', 'mesonbuild/programs.py', ] additional = [ From 8eba69cbaaa6498ecb0a43fac64e81b7f764d572 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Sep 2024 09:02:15 -0700 Subject: [PATCH 272/624] options: Add a function to compare different option choices This allows us to hide type differences inside the options module, but still get accurate change information. --- mesonbuild/coredata.py | 2 +- mesonbuild/options.py | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6b8cec63f2d3..b9a6b6d5d4da 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -599,7 +599,7 @@ def update_project_options(self, project_options: 'MutableKeyedOptionDictType', oldval = self.optstore.get_value_object(key) if type(oldval) is not type(value): self.optstore.set_value(key, value.value) - elif oldval.printable_choices() != value.printable_choices(): + elif options.choices_are_different(oldval, value): # 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. diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 87c31d21c476..23dc5fc862bc 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -484,7 +484,31 @@ def is_auto(self) -> bool: return self.value == 'auto' -@dataclasses.dataclass(init=False) +_U = T.TypeVar('_U', bound=UserOption) + + +def choices_are_different(a: _U, b: _U) -> bool: + """Are the choices between two options the same? + + :param a: A UserOption[T] + :param b: A second UserOption[T] + :return: True if the choices have changed, otherwise False + """ + if isinstance(a, EnumeratedUserOption): + # We expect `a` and `b` to be of the same type, but can't really annotate it that way. + assert isinstance(b, EnumeratedUserOption), 'for mypy' + return a.choices != b.choices + elif isinstance(a, UserArrayOption): + # We expect `a` and `b` to be of the same type, but can't really annotate it that way. + assert isinstance(b, UserArrayOption), 'for mypy' + return a.choices != b.choices + elif isinstance(a, _UserIntegerBase): + assert isinstance(b, _UserIntegerBase), 'for mypy' + return a.max_value != b.max_value or a.min_value != b.min_value + + return False + + class UserStdOption(UserComboOption): ''' UserOption specific to c_std and cpp_std options. User can set a list of From 9fd5281befe7881c9d1210c9e6865382bc0f2b08 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 6 Sep 2024 11:52:56 -0700 Subject: [PATCH 273/624] options: Replace uses of `UserOption[T.Any]` with a Union of UserOption types The fact that UserOption is generic is really an implementation detail, not something to be used publicly. So by having an `AnyOptionType` alias, we can get better type checking, as can be seen by the patch as a whole. One of the big fixes it replace open-coded equivlalents of `MutableKeydOptionDictType` with that type alias. --- mesonbuild/compilers/compilers.py | 3 +-- mesonbuild/coredata.py | 4 ++-- mesonbuild/mconf.py | 6 ++---- mesonbuild/mintro.py | 4 +--- mesonbuild/optinterpreter.py | 2 +- mesonbuild/options.py | 30 +++++++++++++++++------------- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 5777f19f1c75..0f7ef172f5a6 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -35,7 +35,6 @@ from ..dependencies import Dependency CompilerType = T.TypeVar('CompilerType', bound='Compiler') - UserOptionType = T.TypeVar('UserOptionType', bound=options.UserOption) _T = T.TypeVar('_T') @@ -592,7 +591,7 @@ def make_option_name(self, key: OptionKey) -> str: return f'{self.language}_{key.name}' @staticmethod - def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, UserOptionType]) -> MutableKeyedOptionDictType: + def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, options.AnyOptionType]) -> MutableKeyedOptionDictType: options.update(args) return options diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index b9a6b6d5d4da..c2a7c140a47d 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -60,8 +60,8 @@ class SharedCMDOptions(Protocol): cross_file: T.List[str] native_file: T.List[str] - OptionDictType = T.Union[T.Dict[str, 'options.UserOption[T.Any]'], 'OptionsView'] - MutableKeyedOptionDictType = T.Dict['OptionKey', 'options.UserOption[T.Any]'] + OptionDictType = T.Union[T.Dict[str, options.AnyOptionType], 'OptionsView'] + MutableKeyedOptionDictType = T.Dict['OptionKey', options.AnyOptionType] KeyedOptionDictType = T.Union['options.OptionStore', 'OptionsView'] CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode] # code, args diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 7c2270edcbc3..9d65cc26fa21 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -26,8 +26,6 @@ if T.TYPE_CHECKING: from typing_extensions import Protocol - from typing import Any - from .options import UserOption import argparse class CMDOptions(coredata.SharedCMDOptions, Protocol): @@ -189,7 +187,7 @@ def wrap_text(text: LOGLINE, width: int) -> mlog.TV_LoggableList: items = [l[i] if l[i] else ' ' * four_column[i] for i in range(4)] mlog.log(*items) - def split_options_per_subproject(self, options: 'T.Union[dict[OptionKey, UserOption[Any]], coredata.KeyedOptionDictType]') -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: + def split_options_per_subproject(self, options: T.Union[coredata.MutableKeyedOptionDictType, coredata.KeyedOptionDictType]) -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: result: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = {} for k, o in options.items(): if k.subproject: @@ -227,7 +225,7 @@ def add_section(self, section: str) -> None: self._add_line(mlog.normal_yellow(section + ':'), '', '', '') self.print_margin = 2 - def print_options(self, title: str, opts: 'T.Union[dict[OptionKey, UserOption[Any]], coredata.KeyedOptionDictType]') -> None: + def print_options(self, title: str, opts: T.Union[coredata.MutableKeyedOptionDictType, coredata.KeyedOptionDictType]) -> None: if not opts: return if title: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 4b65c5aca048..383f15473eea 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -30,8 +30,6 @@ if T.TYPE_CHECKING: import argparse - from typing import Any - from .options import UserOption from .interpreter import Interpreter from .mparser import BaseNode @@ -306,7 +304,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s for s in subprojects: core_options[k.evolve(subproject=s)] = v - def add_keys(opts: 'T.Union[dict[OptionKey, UserOption[Any]], cdata.KeyedOptionDictType]', section: str) -> None: + def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, cdata.KeyedOptionDictType], section: str) -> None: for key, opt in sorted(opts.items()): optdict = {'name': str(key), 'value': opt.value, 'section': section, 'machine': key.machine.get_lower_case_name() if coredata.is_per_machine_option(key) else 'any'} diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 8c0d1daec217..9f95925fa696 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -69,7 +69,7 @@ class OptionInterpreter: def __init__(self, optionstore: 'OptionStore', subproject: 'SubProject') -> None: self.options: 'coredata.MutableKeyedOptionDictType' = {} self.subproject = subproject - self.option_types: T.Dict[str, T.Callable[..., options.UserOption]] = { + self.option_types: T.Dict[str, T.Callable[..., options.AnyOptionType]] = { 'string': self.string_parser, 'boolean': self.boolean_parser, 'combo': self.combo_parser, diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 23dc5fc862bc..7aa9a5efa0cf 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -32,6 +32,10 @@ from typing_extensions import Literal, TypeAlias, TypedDict DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] + AnyOptionType: TypeAlias = T.Union[ + 'UserBooleanOption', 'UserComboOption', 'UserFeatureOption', + 'UserIntegerOption', 'UserStdOption', 'UserStringArrayOption', + 'UserStringOption', 'UserUmaskOption'] class ArgparseKWs(TypedDict, total=False): @@ -734,7 +738,7 @@ def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help class OptionStore: def __init__(self) -> None: - self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {} + self.d: T.Dict['OptionKey', AnyOptionType] = {} self.project_options: T.Set[OptionKey] = set() self.module_options: T.Set[OptionKey] = set() from .compilers import all_languages @@ -748,35 +752,35 @@ def ensure_key(self, key: T.Union[OptionKey, str]) -> OptionKey: return OptionKey(key) return key - def get_value_object(self, key: T.Union[OptionKey, str]) -> 'UserOption[T.Any]': + def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: return self.d[self.ensure_key(key)] def get_value(self, key: T.Union[OptionKey, str]) -> 'T.Any': return self.get_value_object(key).value - def add_system_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]') -> None: + def add_system_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_key(key) if '.' in key.name: raise MesonException(f'Internal error: non-module option has a period in its name {key.name}.') self.add_system_option_internal(key, valobj) - def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]') -> None: + def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_key(key) assert isinstance(valobj, UserOption) self.d[key] = valobj - def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]') -> None: + def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_key(key) if not key.name.startswith(language + '_'): raise MesonException(f'Internal error: all compiler option names must start with language prefix. ({key.name} vs {language}_)') self.add_system_option(key, valobj) - def add_project_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]') -> None: + def add_project_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_key(key) self.d[key] = valobj self.project_options.add(key) - def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]') -> None: + def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_key(key) if key.name.startswith('build.'): raise MesonException('FATAL internal error: somebody goofed option handling.') @@ -790,7 +794,7 @@ def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool: return self.d[key].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: 'UserOption[T.Any]') -> None: + def set_value_object(self, key: T.Union[OptionKey, str], new_object: AnyOptionType) -> None: key = self.ensure_key(key) self.d[key] = new_object @@ -807,20 +811,20 @@ def __repr__(self) -> str: def keys(self) -> T.KeysView[OptionKey]: return self.d.keys() - def values(self) -> T.ValuesView[UserOption[T.Any]]: + def values(self) -> T.ValuesView[AnyOptionType]: return self.d.values() - def items(self) -> T.ItemsView['OptionKey', 'UserOption[T.Any]']: + def items(self) -> T.ItemsView['OptionKey', AnyOptionType]: return self.d.items() # FIXME: this method must be deleted and users moved to use "add_xxx_option"s instead. - def update(self, **kwargs: UserOption[T.Any]) -> None: + def update(self, **kwargs: AnyOptionType) -> None: self.d.update(**kwargs) - def setdefault(self, k: OptionKey, o: UserOption[T.Any]) -> UserOption[T.Any]: + def setdefault(self, k: OptionKey, o: AnyOptionType) -> AnyOptionType: return self.d.setdefault(k, o) - def get(self, o: OptionKey, default: T.Optional[UserOption[T.Any]] = None) -> T.Optional[UserOption[T.Any]]: + def get(self, o: OptionKey, default: T.Optional[AnyOptionType] = None) -> T.Optional[AnyOptionType]: return self.d.get(o, default) def is_project_option(self, key: OptionKey) -> bool: From df9b0757e2f0628507455e205d7f219d05d2fdd6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 6 Feb 2025 10:50:59 -0800 Subject: [PATCH 274/624] interpreter: fix swapped casts We end up mixing Shared and Static so that we cast to Static args for Shared Libraries and vice versa. --- mesonbuild/interpreter/interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 66ea24cf3468..db8ad80570fe 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3327,9 +3327,9 @@ def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVararg default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject)) assert isinstance(default_library, str), 'for mypy' if default_library == 'shared': - return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary) + return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.SharedLibrary) elif default_library == 'static': - return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary) + return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.StaticLibrary) elif default_library == 'both': return self.build_both_libraries(node, args, kwargs) else: From e42cd6aff6fcbf5556261d874e731613d9345378 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Tue, 11 Feb 2025 03:10:29 +0100 Subject: [PATCH 275/624] ninja backend: Use swiftc flag -working-directory if available This makes SourceKit-LSP work using a meson-generated compile_commands.json. --- mesonbuild/backend/ninjabackend.py | 18 ++++++++++++------ mesonbuild/compilers/swift.py | 8 +++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 24758866c6b5..7b0e1dc83f07 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2434,12 +2434,18 @@ def generate_rust_compile_rules(self, compiler) -> None: def generate_swift_compile_rules(self, compiler) -> None: rule = self.compiler_to_rule_name(compiler) - full_exe = self.environment.get_build_command() + [ - '--internal', - 'dirchanger', - '$RUNDIR', - ] - invoc = full_exe + compiler.get_exelist() + wd_args = compiler.get_working_directory_args('$RUNDIR') + + if wd_args is not None: + invoc = compiler.get_exelist() + ['-working-directory', '$RUNDIR'] + else: + full_exe = self.environment.get_build_command() + [ + '--internal', + 'dirchanger', + '$RUNDIR', + ] + invoc = full_exe + compiler.get_exelist() + command = invoc + ['$ARGS', '$in'] description = 'Compiling Swift source $in' self.add_rule(NinjaRule(rule, command, [], description)) diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index d9268823185b..91c17f0bade1 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -8,7 +8,7 @@ import typing as T from .. import mlog -from ..mesonlib import EnvironmentException, MesonException +from ..mesonlib import EnvironmentException, MesonException, version_compare from .compilers import Compiler, clike_debug_args @@ -115,6 +115,12 @@ def get_include_args(self, path: str, is_system: bool) -> T.List[str]: def get_compile_only_args(self) -> T.List[str]: return ['-c'] + def get_working_directory_args(self, path: str) -> T.Optional[T.List[str]]: + if version_compare(self.version, '<4.2'): + return None + + return ['-working-directory', path] + 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): From 0ce5c8142917cdece30407d284fdea1087e55e43 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Wed, 8 Jan 2025 23:21:16 +0100 Subject: [PATCH 276/624] Fix CMake import's linker args sorting algorithm mangling -framework arguments --- mesonbuild/dependencies/cmake.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py index 4a722157ff56..42cffd595c4c 100644 --- a/mesonbuild/dependencies/cmake.py +++ b/mesonbuild/dependencies/cmake.py @@ -546,7 +546,7 @@ def _detect_dep(self, name: str, package_version: str, modules: T.List[T.Tuple[s # Make sure all elements in the lists are unique and sorted incDirs = sorted(set(incDirs)) compileOptions = sorted(set(compileOptions)) - libraries = sorted(set(libraries)) + libraries = sort_link_args(libraries) mlog.debug(f'Include Dirs: {incDirs}') mlog.debug(f'Compiler Options: {compileOptions}') @@ -654,3 +654,27 @@ def __call__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any], lang @staticmethod def log_tried() -> str: return CMakeDependency.log_tried() + + +def sort_link_args(args: T.List[str]) -> T.List[str]: + itr = iter(args) + result: T.Set[T.Union[T.Tuple[str], T.Tuple[str, str]]] = set() + + while True: + try: + arg = next(itr) + except StopIteration: + break + + if arg == '-framework': + # Frameworks '-framework ...' are two arguments that need to stay together + try: + arg2 = next(itr) + except StopIteration: + raise MesonException(f'Linker arguments contain \'-framework\' with no argument value: {args}') + + result.add((arg, arg2)) + else: + result.add((arg,)) + + return [x for xs in sorted(result) for x in xs] From 9a2127051604adc5c21a0a0bebffc4d81c08a429 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Tue, 11 Feb 2025 20:40:04 +0100 Subject: [PATCH 277/624] Actually use return value of SwiftCompiler.get_working_directory_args --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7b0e1dc83f07..cabb7be04445 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2437,7 +2437,7 @@ def generate_swift_compile_rules(self, compiler) -> None: wd_args = compiler.get_working_directory_args('$RUNDIR') if wd_args is not None: - invoc = compiler.get_exelist() + ['-working-directory', '$RUNDIR'] + invoc = compiler.get_exelist() + wd_args else: full_exe = self.environment.get_build_command() + [ '--internal', From ab9c79bd1cda545c3ce18b0c62b820f6d85221d1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 11 Feb 2025 16:03:15 -0800 Subject: [PATCH 278/624] modules/pkgconfig: use host machine to calculate install prefix The `mesonlib.is_*` functions are not correct to use here, since they are for the build machine, not the host machine. This means if the build machine if Linux but the host is Haiku, then pkg-config files willb e installed into $libdir/pkgconfig, instead of $prefix/develop/lib/pkgconfig --- mesonbuild/modules/pkgconfig.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 1bdf82931a94..974d2521d52e 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -701,10 +701,11 @@ def parse_variable_list(vardict: T.Dict[str, str]) -> T.List[T.Tuple[str, str]]: pcfile = filebase + '.pc' pkgroot = pkgroot_name = kwargs['install_dir'] or default_install_dir if pkgroot is None: - if mesonlib.is_freebsd(): + m = state.environment.machines.host + if m.is_freebsd(): pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('prefix'))), 'libdata', 'pkgconfig') pkgroot_name = os.path.join('{prefix}', 'libdata', 'pkgconfig') - elif mesonlib.is_haiku(): + elif m.is_haiku(): pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('prefix'))), 'develop', 'lib', 'pkgconfig') pkgroot_name = os.path.join('{prefix}', 'develop', 'lib', 'pkgconfig') else: From 5e7b1a9d1decdb8773656ce187bbdb3c39987689 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 29 Jan 2025 11:40:27 -0800 Subject: [PATCH 279/624] options: Add a TypeAlias for option values This gives us a simpler way to annotate things returning an option value, and gives us an easy single place to change that will affect everywhere if the option value types are changed. --- mesonbuild/coredata.py | 10 +++++----- mesonbuild/interpreter/interpreter.py | 8 ++++---- mesonbuild/interpreter/kwargs.py | 14 +++++++------- mesonbuild/interpreter/type_checking.py | 9 +++++---- mesonbuild/options.py | 1 + 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index c2a7c140a47d..fcf93a7e665b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2013-2024 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -43,7 +43,7 @@ from .mesonlib import FileOrString from .cmake.traceparser import CMakeCacheEntry from .interpreterbase import SubProject - from .options import UserOption + from .options import UserOption, ElementaryOptionValues class SharedCMDOptions(Protocol): @@ -442,7 +442,7 @@ def init_backend_options(self, backend_name: str) -> None: 'Default project to execute in Visual Studio', '')) - def get_option(self, key: OptionKey) -> T.Union[T.List[str], str, int, bool]: + def get_option(self, key: OptionKey) -> ElementaryOptionValues: try: v = self.optstore.get_value(key) return v @@ -916,7 +916,7 @@ class OptionsView(abc.Mapping): # python 3.8 or typing_extensions original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, UserOption[Any]]'] subproject: T.Optional[str] = None - overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = dataclasses.field(default_factory=dict) + overrides: T.Optional[T.Mapping[OptionKey, ElementaryOptionValues]] = dataclasses.field(default_factory=dict) def __getitem__(self, key: OptionKey) -> options.UserOption: # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal(). @@ -960,7 +960,7 @@ def get_value(self, key: T.Union[str, OptionKey]): key = OptionKey(key) return self[key].value - def set_value(self, key: T.Union[str, OptionKey], value: T.Union[str, int, bool, T.List[str]]): + def set_value(self, key: T.Union[str, OptionKey], value: ElementaryOptionValues): if isinstance(key, str): key = OptionKey(key) self.overrides[key] = value diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index db8ad80570fe..f6e1bfa0145d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2021 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -1668,7 +1668,7 @@ def notfound_program(self, args: T.List[mesonlib.FileOrString]) -> ExternalProgr # 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, T.Union[str, int, bool, T.List[str]]]] = None, + default_options: T.Optional[T.Dict[OptionKey, options.ElementaryOptionValues]] = None, required: bool = True, silent: bool = True, wanted: T.Union[str, T.List[str]] = '', search_dirs: T.Optional[T.List[str]] = None, @@ -1699,7 +1699,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], return progobj def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: MachineChoice, - default_options: T.Optional[T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]]], + default_options: T.Optional[T.Dict[OptionKey, options.ElementaryOptionValues]], required: bool, search_dirs: T.Optional[T.List[str]], wanted: T.Union[str, T.List[str]], @@ -1767,7 +1767,7 @@ def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executab return True def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString], - default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]], + default_options: T.Dict[OptionKey, options.ElementaryOptionValues], 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', diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 87f121e90b0f..20cafcdf0a46 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2021 The Meson Developers -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021-2025 Intel Corporation +# Copyright © 2021-2025 Intel Corporation from __future__ import annotations """Keyword Argument type annotations.""" @@ -209,7 +209,7 @@ class Project(TypedDict): version: T.Optional[FileOrString] meson_version: T.Optional[str] - default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] + default_options: T.Dict[OptionKey, options.ElementaryOptionValues] license: T.List[str] license_files: T.List[str] subproject_dir: str @@ -239,7 +239,7 @@ class Summary(TypedDict): class FindProgram(ExtractRequired, ExtractSearchDirs): - default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] + default_options: T.Dict[OptionKey, options.ElementaryOptionValues] native: MachineChoice version: T.List[str] @@ -312,13 +312,13 @@ class ConfigureFile(TypedDict): class Subproject(ExtractRequired): - default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] + default_options: T.Dict[OptionKey, options.ElementaryOptionValues] version: T.List[str] class DoSubproject(ExtractRequired): - default_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] + default_options: T.Dict[OptionKey, options.ElementaryOptionValues] version: T.List[str] cmake_options: T.List[str] options: T.Optional[CMakeSubprojectOptions] @@ -346,7 +346,7 @@ class _BaseBuildTarget(TypedDict): name_suffix: T.Optional[str] native: MachineChoice objects: T.List[build.ObjectTypes] - override_options: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] + override_options: T.Dict[OptionKey, options.ElementaryOptionValues] depend_files: NotRequired[T.List[File]] resources: T.List[str] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index ed34be950065..08046411cd57 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2021 Intel Corporation +# Copyright © 2021-2025 Intel Corporation """Helpers for strict type checking.""" @@ -27,6 +27,7 @@ from ..build import ObjectTypes from ..interpreterbase import TYPE_var + from ..options import ElementaryOptionValues from ..mesonlib import EnvInitValueType _FullEnvInitValueType = T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], EnvInitValueType, str, None] @@ -292,11 +293,11 @@ def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: default=[], ) -def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, T.Union[str, int, bool, T.List[str]]]]) -> T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]]: +def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, ElementaryOptionValues]]) -> T.Dict[OptionKey, ElementaryOptionValues]: if isinstance(raw, str): raw = [raw] if isinstance(raw, list): - output: T.Dict[OptionKey, T.Union[str, int, bool, T.List[str]]] = {} + output: T.Dict[OptionKey, ElementaryOptionValues] = {} for each in raw: k, v = split_equal_string(each) output[OptionKey.from_string(k)] = v @@ -304,7 +305,7 @@ def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, T.Uni return {OptionKey.from_string(k): v for k, v in raw.items()} -OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.Dict[str, T.Union[str, int, bool, T.List[str]]], T.List[str]]] = KwargInfo( +OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.Dict[str, ElementaryOptionValues], T.List[str]]] = KwargInfo( 'override_options', (str, ContainerTypeInfo(list, str), ContainerTypeInfo(dict, (str, int, bool, list))), default={}, diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 7aa9a5efa0cf..c31254d232d4 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -36,6 +36,7 @@ 'UserBooleanOption', 'UserComboOption', 'UserFeatureOption', 'UserIntegerOption', 'UserStdOption', 'UserStringArrayOption', 'UserStringOption', 'UserUmaskOption'] + ElementaryOptionValues: TypeAlias = T.Union[str, int, bool, T.List[str]] class ArgparseKWs(TypedDict, total=False): From ea678ed82938ceac00682b2695b57193d36b71b4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 29 Jan 2025 15:04:00 -0800 Subject: [PATCH 280/624] build: fix typing of `Target.get_option` Which can return any of `ElementaryOptionValues`, but is currently typed as if it only returns `str | int | bool`. --- mesonbuild/build.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c72857d2c1ae..82d97fd2db10 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -49,6 +49,7 @@ from .mesonlib import ExecutableSerialisation, FileMode, FileOrString from .modules import ModuleState from .mparser import BaseNode + from .options import ElementaryOptionValues GeneratedTypes = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] @@ -679,10 +680,8 @@ def set_option_overrides(self, option_overrides: T.Dict[OptionKey, str]) -> None def get_options(self) -> coredata.OptionsView: return self.options - def get_option(self, key: 'OptionKey') -> T.Union[str, int, bool]: - # TODO: if it's possible to annotate get_option or validate_option_value - # in the future we might be able to remove the cast here - return T.cast('T.Union[str, int, bool]', self.options.get_value(key)) + def get_option(self, key: OptionKey) -> ElementaryOptionValues: + return self.options.get_value(key) @staticmethod def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[OptionKey, str]: From d37d649b08b832d52fa684bc0506829fb40d5261 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 14 Apr 2024 12:58:30 +0300 Subject: [PATCH 281/624] Make all Meson level options overridable per subproject. --- .../markdown/Configuring-a-build-directory.md | 37 + docs/markdown/snippets/optionrefactor.md | 19 + mesonbuild/ast/introspection.py | 3 +- mesonbuild/backend/backends.py | 47 +- mesonbuild/backend/ninjabackend.py | 62 +- mesonbuild/backend/vs2010backend.py | 25 +- mesonbuild/backend/xcodebackend.py | 9 +- mesonbuild/build.py | 69 +- mesonbuild/cmake/common.py | 8 +- mesonbuild/cmake/executor.py | 5 +- mesonbuild/compilers/c.py | 120 ++-- mesonbuild/compilers/compilers.py | 179 +++-- mesonbuild/compilers/cpp.py | 216 +++--- mesonbuild/compilers/cuda.py | 49 +- mesonbuild/compilers/cython.py | 14 +- mesonbuild/compilers/fortran.py | 21 +- mesonbuild/compilers/mixins/clike.py | 2 +- mesonbuild/compilers/mixins/elbrus.py | 11 +- mesonbuild/compilers/mixins/emscripten.py | 3 +- mesonbuild/compilers/mixins/islinker.py | 3 +- mesonbuild/compilers/objc.py | 19 +- mesonbuild/compilers/objcpp.py | 19 +- mesonbuild/compilers/rust.py | 9 +- mesonbuild/compilers/vala.py | 4 +- mesonbuild/coredata.py | 309 +++++---- mesonbuild/dependencies/boost.py | 2 + mesonbuild/dependencies/pkgconfig.py | 6 +- mesonbuild/dependencies/qt.py | 7 +- mesonbuild/environment.py | 61 +- mesonbuild/interpreter/compiler.py | 6 +- mesonbuild/interpreter/dependencyfallbacks.py | 12 +- mesonbuild/interpreter/interpreter.py | 141 ++-- mesonbuild/interpreter/interpreterobjects.py | 7 +- mesonbuild/interpreter/kwargs.py | 4 +- mesonbuild/interpreter/type_checking.py | 13 - mesonbuild/linkers/linkers.py | 19 +- mesonbuild/mconf.py | 64 +- mesonbuild/mesonmain.py | 20 + mesonbuild/mintro.py | 10 +- mesonbuild/modules/gnome.py | 1 + mesonbuild/modules/rust.py | 4 +- mesonbuild/msetup.py | 56 +- mesonbuild/options.py | 635 ++++++++++++++++-- test cases/common/40 options/meson.build | 8 +- .../common/87 default options/meson.build | 2 +- .../subprojects/sub1/meson.build | 1 + .../subprojects/sub1/meson_options.txt | 1 + test cases/unit/123 persp options/meson.build | 24 + .../unit/123 persp options/meson.options | 1 + .../subprojects/sub1/meson.build | 22 + .../subprojects/sub1/meson.options | 1 + .../123 persp options/subprojects/sub1/sub1.c | 6 + .../subprojects/sub2/meson.build | 21 + .../subprojects/sub2/meson.options | 1 + .../123 persp options/subprojects/sub2/sub2.c | 6 + test cases/unit/123 persp options/toplevel.c | 6 + unittests/allplatformstests.py | 93 ++- unittests/baseplatformtests.py | 2 + unittests/linuxliketests.py | 53 ++ unittests/machinefiletests.py | 4 +- unittests/optiontests.py | 184 +++++ unittests/platformagnostictests.py | 22 +- unittests/windowstests.py | 12 +- 63 files changed, 1971 insertions(+), 829 deletions(-) create mode 100644 docs/markdown/snippets/optionrefactor.md create mode 100644 test cases/unit/123 persp options/meson.build create mode 100644 test cases/unit/123 persp options/meson.options create mode 100644 test cases/unit/123 persp options/subprojects/sub1/meson.build create mode 100644 test cases/unit/123 persp options/subprojects/sub1/meson.options create mode 100644 test cases/unit/123 persp options/subprojects/sub1/sub1.c create mode 100644 test cases/unit/123 persp options/subprojects/sub2/meson.build create mode 100644 test cases/unit/123 persp options/subprojects/sub2/meson.options create mode 100644 test cases/unit/123 persp options/subprojects/sub2/sub2.c create mode 100644 test cases/unit/123 persp options/toplevel.c create mode 100644 unittests/optiontests.py diff --git a/docs/markdown/Configuring-a-build-directory.md b/docs/markdown/Configuring-a-build-directory.md index 746e4b16b6c4..974b7ae998d2 100644 --- a/docs/markdown/Configuring-a-build-directory.md +++ b/docs/markdown/Configuring-a-build-directory.md @@ -119,3 +119,40 @@ by invoking [`meson configure`](Commands.md#configure) with the project source directory or the path to the root `meson.build`. In this case, Meson will print the default values of all options similar to the example output from above. + +## Per project subproject options rewrite (Since 1.8) + +A common requirement when building large projects with many +subprojects is to build some (or all) subprojects with project options +that are different from the "main project". This has been sort of +possible in a limited way but is now natively supported. Per project +options can be added, changed and removed at runtime using the command +line, in other words, without editing existing `meson.build` files. + +Starting with version 1.8 you can specify per-project option settings. +These can be specified for every top level (i.e. not project) options. +Suppose you have a project that has a single subproject called +`numbercruncher` that does heavy computation. During development you +want to build that subproject with optimizations enabled but your main +project without optimizations. This can be done by specifying a custom +value to the given subproject: + + meson configure -Dnumbercruncher:optimization=3 + +Another case might be that you want to build with warnings as errors, +but some subproject does not support it. To configure `werror` per +subproject you can do: + + meson configure -Dwerror=true -Dnaughty:werror=false + +You can also specify a different value on the top level project. For +example you could enable optimizations on all subprojects but not the +top level project: + + meson configure -Doptimization=2 -D:optimization=0 + +Note the colon after the second `D`. + +Subproject specific values can be removed with -U + + meson configure -Usubproject:optionnname diff --git a/docs/markdown/snippets/optionrefactor.md b/docs/markdown/snippets/optionrefactor.md new file mode 100644 index 000000000000..53dbdbc42a33 --- /dev/null +++ b/docs/markdown/snippets/optionrefactor.md @@ -0,0 +1,19 @@ +## Per project subproject options rewrite + +You can now define per-subproject values for all shared configuration +options. As an example you might want to enable optimizations on only +one subproject: + + meson configure -Dnumbercruncher:optimization=3 + +Subproject specific values can be removed with -U + + meson configure -Unumbercruncher:optimization + +This is a major change in how options are handled, and the +implementation will evolve over the next few releases of Meson. If +this change causes an error in your builds, please [report an issue on +GitHub](https://github.com/mesonbuild/meson/issues/new). + +We have tried to keep backwards compatibility as much as possible, but +this may lead to some build breakage. diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 6bc6286f24f3..69ffc55f7e2e 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -128,7 +128,8 @@ def _str_list(node: T.Any) -> T.Optional[T.List[str]]: def_opts = self.flatten_args(kwargs.get('default_options', [])) _project_default_options = mesonlib.stringlistify(def_opts) - self.project_default_options = cdata.create_options_dict(_project_default_options, self.subproject) + 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) self.coredata.set_default_options(self.default_options, self.subproject, self.environment) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8d9796db95c6..a6a42e9334bf 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -26,7 +26,7 @@ from .. import mlog from ..compilers import LANGUAGES_USING_LDFLAGS, detect, lang_suffixes from ..mesonlib import ( - File, MachineChoice, MesonException, OrderedSet, + File, MachineChoice, MesonException, MesonBugException, OrderedSet, ExecutableSerialisation, EnvironmentException, classify_unity_sources, get_compiler_for_source ) @@ -424,7 +424,7 @@ def generate_unity_files(self, target: build.BuildTarget, unity_src: str) -> T.L abs_files: T.List[str] = [] result: T.List[mesonlib.File] = [] compsrcs = classify_unity_sources(target.compilers.values(), unity_src) - unity_size = target.get_option(OptionKey('unity_size')) + unity_size = self.get_target_option(target, 'unity_size') assert isinstance(unity_size, int), 'for mypy' def init_language_file(suffix: str, unity_file_number: int) -> T.TextIO: @@ -878,7 +878,8 @@ def object_filename_from_source(self, target: build.BuildTarget, compiler: Compi object_suffix = machine.get_object_suffix() # For the TASKING compiler, in case of LTO or prelinking the object suffix has to be .mil if compiler.get_id() == 'tasking': - if target.get_option(OptionKey('b_lto')) or (isinstance(target, build.StaticLibrary) and target.prelink): + use_lto = self.get_target_option(target, 'b_lto') + if use_lto or (isinstance(target, build.StaticLibrary) and target.prelink): if not source.rsplit('.', 1)[1] in lang_suffixes['c']: if isinstance(target, build.StaticLibrary) and not target.prelink: raise EnvironmentException('Tried using MIL linking for a static library with a assembly file. This can only be done if the static library is prelinked or disable \'b_lto\'.') @@ -925,10 +926,10 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects') -> T.List[str]: # With unity builds, sources don't map directly to objects, # we only support extracting all the objects in this mode, # so just return all object files. - if extobj.target.is_unity: + if self.is_unity(extobj.target): compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources) sources = [] - unity_size = extobj.target.get_option(OptionKey('unity_size')) + unity_size = self.get_target_option(extobj.target, 'unity_size') assert isinstance(unity_size, int), 'for mypy' for comp, srcs in compsrcs.items(): @@ -981,7 +982,7 @@ def create_msvc_pch_implementation(self, target: build.BuildTarget, lang: str, p def target_uses_pch(self, target: build.BuildTarget) -> bool: try: - return T.cast('bool', target.get_option(OptionKey('b_pch'))) + return T.cast('bool', self.get_target_option(target, 'b_pch')) except (KeyError, AttributeError): return False @@ -1007,7 +1008,6 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com # starting from hard-coded defaults followed by build options and so on. commands = compiler.compiler_args() - copt_proxy = target.get_options() # First, the trivial ones that are impossible to override. # # Add -nostdinc/-nostdinc++ if needed; can't be overridden @@ -1015,22 +1015,22 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com # Add things like /NOLOGO or -pipe; usually can't be overridden commands += compiler.get_always_args() # warning_level is a string, but mypy can't determine that - commands += compiler.get_warn_args(T.cast('str', target.get_option(OptionKey('warning_level')))) + commands += compiler.get_warn_args(T.cast('str', self.get_target_option(target, 'warning_level'))) # Add -Werror if werror=true is set in the build options set on the # command-line or default_options inside project(). This only sets the # action to be done for warnings if/when they are emitted, so it's ok # to set it after or get_warn_args(). - if target.get_option(OptionKey('werror')): + if self.get_target_option(target, 'werror'): 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(copt_proxy) + commands += compiler.get_option_compile_args(target, self.environment, target.subproject) - optimization = target.get_option(OptionKey('optimization')) + optimization = self.get_target_option(target, 'optimization') assert isinstance(optimization, str), 'for mypy' commands += compiler.get_optimization_args(optimization) - debug = target.get_option(OptionKey('debug')) + debug = self.get_target_option(target, 'debug') assert isinstance(debug, bool), 'for mypy' commands += compiler.get_debug_args(debug) @@ -1755,7 +1755,7 @@ def generate_target_install(self, d: InstallData) -> None: # TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more # fine-grained stripping of static archives. can_strip = not isinstance(t, build.StaticLibrary) - should_strip = can_strip and t.get_option(OptionKey('strip')) + should_strip = can_strip and self.get_target_option(t, 'strip') assert isinstance(should_strip, bool), 'for mypy' # Install primary build output (library/executable/jar, etc) # Done separately because of strip/aliases/rpath @@ -2092,3 +2092,24 @@ def compile_target_to_generator(self, target: build.CompileTarget) -> build.Gene all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated) return self.compiler_to_generator(target, target.compiler, all_sources, target.output_templ, target.depends) + + def is_unity(self, target: build.BuildTarget) -> bool: + if isinstance(target, build.CompileTarget): + return False + val = self.get_target_option(target, 'unity') + if val == 'on': + return True + if val == 'off': + return False + if val == 'subprojects': + return target.subproject != '' + raise MesonException(f'Internal error: invalid option type for "unity": {val}') + + def get_target_option(self, target: build.BuildTarget, name: T.Union[str, OptionKey]) -> T.Union[str, int, bool, T.List[str]]: + if isinstance(name, str): + key = OptionKey(name, subproject=target.subproject) + elif isinstance(name, OptionKey): + key = name + else: + raise MesonBugException('Internal error: invalid option type.') + return self.environment.coredata.get_option_for_target(target, key) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cabb7be04445..57d73ed1810c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -638,7 +638,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) outfile.write('# Do not edit by hand.\n\n') outfile.write('ninja_required_version = 1.8.2\n\n') - num_pools = self.environment.coredata.optstore.get_value('backend_max_links') + num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') if num_pools > 0: outfile.write(f'''pool link_pool depth = {num_pools} @@ -671,8 +671,8 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) self.generate_dist() mlog.log_timestamp("Dist generated") key = OptionKey('b_coverage') - if (key in self.environment.coredata.optstore and - self.environment.coredata.optstore.get_value(key)): + 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) 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): @@ -957,7 +957,7 @@ def generate_target(self, target) -> None: # Generate rules for building the remaining source files in this target outname = self.get_target_filename(target) obj_list = [] - is_unity = target.is_unity + is_unity = self.is_unity(target) header_deps = [] unity_src = [] unity_deps = [] # Generated sources that must be built before compiling a Unity target. @@ -1117,7 +1117,9 @@ def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool: cpp = target.compilers['cpp'] if cpp.get_id() != 'msvc': return False - cppversion = target.get_option(OptionKey('cpp_std', machine=target.for_machine)) + cppversion = self.get_target_option(target, OptionKey('cpp_std', + machine=target.for_machine, + subproject=target.subproject)) if cppversion not in ('latest', 'c++latest', 'vc++latest'): return False if not mesonlib.current_vs_supports_modules(): @@ -1725,7 +1727,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ valac_outputs.append(vala_c_file) args = self.generate_basic_compiler_args(target, valac) - args += valac.get_colorout_args(target.get_option(OptionKey('b_colorout'))) + args += valac.get_colorout_args(self.get_target_option(target, 'b_colorout')) # Tell Valac to output everything in our private directory. Sadly this # means it will also preserve the directory components of Vala sources # found inside the build tree (generated sources). @@ -1737,7 +1739,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ # Outputted header hname = os.path.join(self.get_target_dir(target), target.vala_header) args += ['--header', hname] - if target.is_unity: + 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. @@ -1803,14 +1805,14 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ args: T.List[str] = [] args += cython.get_always_args() - args += cython.get_debug_args(target.get_option(OptionKey('debug'))) - args += cython.get_optimization_args(target.get_option(OptionKey('optimization'))) - args += cython.get_option_compile_args(target.get_options()) + 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 += 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') - ext = target.get_option(OptionKey('cython_language', machine=target.for_machine)) + ext = self.get_target_option(target, OptionKey('cython_language', machine=target.for_machine)) pyx_sources = [] # Keep track of sources we're adding to build @@ -1933,10 +1935,9 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: # Rust compiler takes only the main file as input and # figures out what other files are needed via import # statements and magic. - base_proxy = target.get_options() args = rustc.compiler_args() # Compiler args for compiling this target - args += compilers.get_base_compile_args(base_proxy, rustc, self.environment) + args += compilers.get_base_compile_args(target, rustc, self.environment) self.generate_generator_list_rules(target) # dependencies need to cause a relink, they're not just for ordering @@ -2017,8 +2018,8 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: # https://github.com/rust-lang/rust/issues/39016 if not isinstance(target, build.StaticLibrary): try: - buildtype = target.get_option(OptionKey('buildtype')) - crt = target.get_option(OptionKey('b_vscrt')) + 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 @@ -2301,7 +2302,7 @@ def _rsp_options(self, tool: T.Union['Compiler', 'StaticLinker', 'DynamicLinker' return options def generate_static_link_rules(self) -> None: - num_pools = self.environment.coredata.optstore.get_value('backend_max_links') + num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') if 'java' in self.environment.coredata.compilers.host: self.generate_java_link() for for_machine in MachineChoice: @@ -2349,7 +2350,7 @@ def generate_static_link_rules(self) -> None: self.add_rule(NinjaRule(rule, cmdlist, args, description, **options, extra=pool)) def generate_dynamic_link_rules(self) -> None: - num_pools = self.environment.coredata.optstore.get_value('backend_max_links') + num_pools = self.environment.coredata.optstore.get_value_for('backend_max_links') for for_machine in MachineChoice: complist = self.environment.coredata.compilers[for_machine] for langname, compiler in complist.items(): @@ -2832,11 +2833,10 @@ def get_link_debugfile_args(self, linker: T.Union[Compiler, StaticLinker], targe return [] def generate_llvm_ir_compile(self, target, src: FileOrString): - base_proxy = target.get_options() compiler = get_compiler_for_source(target.compilers.values(), src) commands = compiler.compiler_args() # Compiler args for compiling this target - commands += compilers.get_base_compile_args(base_proxy, compiler, self.environment) + commands += compilers.get_base_compile_args(target, compiler, self.environment) if isinstance(src, File): if src.is_built: src_filename = os.path.join(src.subdir, src.fname) @@ -2892,7 +2892,6 @@ def _generate_single_compile(self, target: build.BuildTarget, compiler: Compiler return commands def _generate_single_compile_base_args(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': - base_proxy = target.get_options() # Create an empty commands list, and start adding arguments from # various sources in the order in which they must override each other commands = compiler.compiler_args() @@ -2901,7 +2900,7 @@ def _generate_single_compile_base_args(self, target: build.BuildTarget, compiler # Add compiler args for compiling this target derived from 'base' build # options passed on the command-line, in default_options, etc. # These have the lowest priority. - commands += compilers.get_base_compile_args(base_proxy, + commands += compilers.get_base_compile_args(target, compiler, self.environment) return commands @@ -3312,7 +3311,7 @@ def get_target_type_link_args(self, target, linker): 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): - commands += linker.get_std_shared_module_link_args(target.get_options()) + commands += linker.get_std_shared_module_link_args(target) else: commands += linker.get_std_shared_lib_link_args() # All shared libraries are PIC @@ -3511,20 +3510,19 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # 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.get_options()) + commands += linker.get_base_link_args(target, linker, self.environment) else: - commands += compilers.get_base_link_args(target.get_options(), + commands += compilers.get_base_link_args(target, linker, - isinstance(target, build.SharedModule), - self.environment.get_build_dir()) + self.environment) # 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 commands += linker.get_linker_always_args() # Add buildtype linker args: optimization level, etc. - commands += linker.get_optimization_link_args(target.get_option(OptionKey('optimization'))) + commands += linker.get_optimization_link_args(self.get_target_option(target, 'optimization')) # Add /DEBUG and the pdb filename when using MSVC - if target.get_option(OptionKey('debug')): + if self.get_target_option(target, 'debug'): commands += self.get_link_debugfile_args(linker, target) debugfile = self.get_link_debugfile_name(linker, target) if debugfile is not None: @@ -3595,7 +3593,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. # # 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.get_options()) + commands += linker.get_option_link_args(target, self.environment) dep_targets = [] dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) @@ -3679,7 +3677,9 @@ def generate_gcov_clean(self) -> None: gcda_elem.add_item('description', 'Deleting gcda files') self.add_build(gcda_elem) - def get_user_option_args(self) -> T.List[str]: + def get_user_option_args(self, shut_up_pylint: bool = True) -> T.List[str]: + if shut_up_pylint: + return [] cmds = [] for k, v in self.environment.coredata.optstore.items(): if self.environment.coredata.optstore.is_project_option(k): @@ -3827,7 +3827,7 @@ def generate_ending(self) -> None: elem.add_dep(self.generate_custom_target_clean(ctlist)) if OptionKey('b_coverage') in self.environment.coredata.optstore and \ - self.environment.coredata.optstore.get_value('b_coverage'): + self.environment.coredata.optstore.get_value_for('b_coverage'): self.generate_gcov_clean() elem.add_dep('clean-gcda') elem.add_dep('clean-gcno') diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index e837c89ced0e..101508315ab7 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -271,7 +271,7 @@ def generate(self, self.debug = self.environment.coredata.get_option(OptionKey('debug')) try: self.sanitize = self.environment.coredata.get_option(OptionKey('b_sanitize')) - except MesonException: + except KeyError: self.sanitize = 'none' sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') projlist = self.generate_projects(vslite_ctx) @@ -996,9 +996,9 @@ def get_args_defines_and_inc_dirs(self, target, compiler, generated_files_includ for l, comp in target.compilers.items(): if l in file_args: file_args[l] += compilers.get_base_compile_args( - target.get_options(), comp, self.environment) + target, comp, self.environment) file_args[l] += comp.get_option_compile_args( - target.get_options()) + target, self.environment, 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(): @@ -1012,7 +1012,7 @@ def get_args_defines_and_inc_dirs(self, target, compiler, generated_files_includ # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. for lang in file_args.keys(): - file_args[lang] += target.get_option(OptionKey(f'{lang}_args', machine=target.for_machine)) + file_args[lang] += self.get_target_option(target, OptionKey(f'{lang}_args', machine=target.for_machine)) for args in file_args.values(): # This is where Visual Studio will insert target_args, target_defines, # etc, which are added later from external deps (see below). @@ -1302,7 +1302,7 @@ def add_non_makefile_vcxproj_elements( if True in ((dep.name == 'openmp') for dep in target.get_external_deps()): ET.SubElement(clconf, 'OpenMPSupport').text = 'true' # CRT type; debug or release - vscrt_type = target.get_option(OptionKey('b_vscrt')) + vscrt_type = self.get_target_option(target, 'b_vscrt') vscrt_val = compiler.get_crt_val(vscrt_type, self.buildtype) if vscrt_val == 'mdd': ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' @@ -1340,7 +1340,7 @@ def add_non_makefile_vcxproj_elements( # Exception handling has to be set in the xml in addition to the "AdditionalOptions" because otherwise # cl will give warning D9025: overriding '/Ehs' with cpp_eh value if 'cpp' in target.compilers: - eh = target.get_option(OptionKey('cpp_eh', machine=target.for_machine)) + eh = self.environment.coredata.get_option_for_target(target, OptionKey('cpp_eh', machine=target.for_machine)) if eh == 'a': ET.SubElement(clconf, 'ExceptionHandling').text = 'Async' elif eh == 's': @@ -1358,10 +1358,10 @@ def add_non_makefile_vcxproj_elements( ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines) ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true' # Warning level - warning_level = T.cast('str', target.get_option(OptionKey('warning_level'))) + warning_level = T.cast('str', self.get_target_option(target, 'warning_level')) warning_level = 'EnableAllWarnings' if warning_level == 'everything' else 'Level' + str(1 + int(warning_level)) ET.SubElement(clconf, 'WarningLevel').text = warning_level - if target.get_option(OptionKey('werror')): + if self.get_target_option(target, 'werror'): ET.SubElement(clconf, 'TreatWarningAsError').text = 'true' # Optimization flags o_flags = split_o_flags_args(build_args) @@ -1402,7 +1402,7 @@ def add_non_makefile_vcxproj_elements( ET.SubElement(link, 'GenerateDebugInformation').text = 'false' if not isinstance(target, build.StaticLibrary): if isinstance(target, build.SharedModule): - extra_link_args += compiler.get_std_shared_module_link_args(target.get_options()) + extra_link_args += compiler.get_std_shared_module_link_args(target) # Add link args added using add_project_link_arguments() extra_link_args += self.build.get_project_link_args(compiler, target.subproject, target.for_machine) # Add link args added using add_global_link_arguments() @@ -1435,7 +1435,7 @@ def add_non_makefile_vcxproj_elements( # 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.get_options()) + extra_link_args += compiler.get_option_link_args(target, self.environment, 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 @@ -1534,7 +1534,8 @@ def add_non_makefile_vcxproj_elements( # /nologo ET.SubElement(link, 'SuppressStartupBanner').text = 'true' # /release - if not target.get_option(OptionKey('debug')): + addchecksum = self.get_target_option(target, 'buildtype') != 'debug' + if addchecksum: ET.SubElement(link, 'SetChecksum').text = 'true' # Visual studio doesn't simply allow the src files of a project to be added with the 'Condition=...' attribute, @@ -1596,7 +1597,7 @@ def gen_vcxproj(self, target: build.BuildTarget, ofname: str, guid: str, vslite_ raise MesonException(f'Unknown target type for {target.get_basename()}') (sources, headers, objects, _languages) = self.split_sources(target.sources) - if target.is_unity: + if self.is_unity(target): sources = self.generate_unity_files(target, sources) if target.for_machine is MachineChoice.BUILD: platform = self.build_platform diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 0e40d0239e5a..9f2e0a1b628f 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1686,9 +1686,8 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non if compiler is None: continue # Start with warning args - warn_args = compiler.get_warn_args(target.get_option(OptionKey('warning_level'))) - copt_proxy = target.get_options() - std_args = compiler.get_option_compile_args(copt_proxy) + 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) # 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() @@ -1736,9 +1735,9 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non if target.suffix: suffix = '.' + target.suffix settings_dict.add_item('EXECUTABLE_SUFFIX', suffix) - settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', BOOL2XCODEBOOL[target.get_option(OptionKey('debug'))]) + settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', BOOL2XCODEBOOL[self.get_target_option(target, 'debug')]) settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO') - opt_flag = OPT2XCODEOPT[target.get_option(OptionKey('optimization'))] + opt_flag = OPT2XCODEOPT[self.get_target_option(target, 'optimization')] if opt_flag is not None: settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', opt_flag) if target.has_pch: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 82d97fd2db10..44241ec2c829 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -49,7 +49,6 @@ from .mesonlib import ExecutableSerialisation, FileMode, FileOrString from .modules import ModuleState from .mparser import BaseNode - from .options import ElementaryOptionValues GeneratedTypes = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList'] LibTypes = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex'] @@ -422,10 +421,6 @@ class ExtractedObjects(HoldableObject): recursive: bool = True pch: bool = False - def __post_init__(self) -> None: - if self.target.is_unity: - self.check_unity_compatible() - def __repr__(self) -> str: r = '<{0} {1!r}: {2}>' return r.format(self.__class__.__name__, self.target.name, self.srclist) @@ -537,12 +532,6 @@ def type_suffix(self) -> str: pass def __post_init__(self, overrides: T.Optional[T.Dict[OptionKey, str]]) -> None: - if overrides: - ovr = {k.evolve(machine=self.for_machine) if k.lang else k: v - for k, v in overrides.items()} - else: - ovr = {} - self.options = coredata.OptionsView(self.environment.coredata.optstore, self.subproject, ovr) # XXX: this should happen in the interpreter if has_path_sep(self.name): # Fix failing test 53 when this becomes an error. @@ -657,34 +646,13 @@ def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None: # set, use the value of 'install' if it's enabled. self.build_by_default = True - self.set_option_overrides(self.parse_overrides(kwargs)) - - def is_compiler_option_hack(self, key): - # FIXME this method must be deleted when OptionsView goes away. - # At that point the build target only stores the original string. - # The decision on how to use those pieces of data is done elsewhere. - from .compilers import all_languages - if '_' not in key.name: - return False - prefix = key.name.split('_')[0] - return prefix in all_languages - - def set_option_overrides(self, option_overrides: T.Dict[OptionKey, str]) -> None: - self.options.overrides = {} - for k, v in option_overrides.items(): - if self.is_compiler_option_hack(k): - self.options.overrides[k.evolve(machine=self.for_machine)] = v - else: - self.options.overrides[k] = v - - def get_options(self) -> coredata.OptionsView: - return self.options + self.raw_overrides = self.parse_overrides(kwargs) - def get_option(self, key: OptionKey) -> ElementaryOptionValues: - return self.options.get_value(key) + 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[OptionKey, str]: + 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 @@ -692,15 +660,13 @@ def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[OptionKey, str]: if isinstance(opts, dict): return T.cast('T.Dict[OptionKey, str]', opts) - result: T.Dict[OptionKey, str] = {} + 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) - key = OptionKey.from_string(k.strip()) - v = v.strip() - result[key] = v + result[k] = v return result def is_linkable_target(self) -> bool: @@ -832,11 +798,6 @@ def __repr__(self): def __str__(self): return f"{self.name}" - @property - def is_unity(self) -> bool: - unity_opt = self.get_option(OptionKey('unity')) - return unity_opt == 'on' or (unity_opt == 'subprojects' and self.subproject != '') - def validate_install(self): if self.for_machine is MachineChoice.BUILD and self.install: if self.environment.is_cross_build(): @@ -1020,8 +981,7 @@ def process_compilers(self) -> T.List[str]: self.compilers['c'] = self.all_compilers['c'] if 'cython' in self.compilers: key = OptionKey('cython_language', machine=self.for_machine) - value = self.get_option(key) - + value = self.environment.coredata.optstore.get_value_for(key) try: self.compilers[value] = self.all_compilers[value] except KeyError: @@ -1057,7 +1017,7 @@ def process_link_depends(self, sources): 'Link_depends arguments must be strings, Files, ' 'or a Custom Target, or lists thereof.') - def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedTypes']]) -> ExtractedObjects: + def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedTypes']], is_unity: bool) -> ExtractedObjects: sources_set = set(self.sources) generated_set = set(self.generated) @@ -1080,7 +1040,10 @@ def extract_objects(self, srclist: T.List[T.Union['FileOrString', 'GeneratedType obj_gen.append(src) else: raise MesonException(f'Object extraction arguments must be strings, Files or targets (got {type(src).__name__}).') - return ExtractedObjects(self, obj_src, obj_gen) + eobjs = ExtractedObjects(self, obj_src, obj_gen) + if is_unity: + eobjs.check_unity_compatible() + return eobjs def extract_all_objects(self, recursive: bool = True) -> ExtractedObjects: return ExtractedObjects(self, self.sources, self.generated, self.objects, @@ -1262,7 +1225,7 @@ def _extract_pic_pie(self, kwargs: T.Dict[str, T.Any], arg: str, option: str) -> if kwargs.get(arg) is not None: val = T.cast('bool', kwargs[arg]) elif k in self.environment.coredata.optstore: - val = self.environment.coredata.optstore.get_value(k) + val = self.environment.coredata.optstore.get_value_for(k.name, k.subproject) else: val = False @@ -1973,7 +1936,7 @@ def __init__( kwargs): key = OptionKey('b_pie') if 'pie' not in kwargs and key in environment.coredata.optstore: - kwargs['pie'] = environment.coredata.optstore.get_value(key) + kwargs['pie'] = environment.coredata.optstore.get_value_for(key) super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) self.win_subsystem = kwargs.get('win_subsystem') or 'console' @@ -2877,10 +2840,6 @@ def __init__(self, def type_suffix(self) -> str: return "@compile" - @property - def is_unity(self) -> bool: - return False - def _add_output(self, f: File) -> None: plainname = os.path.basename(f.fname) basename = os.path.splitext(plainname)[0] diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py index d9ff559971f7..e3ba76b9e6e4 100644 --- a/mesonbuild/cmake/common.py +++ b/mesonbuild/cmake/common.py @@ -52,14 +52,14 @@ ] def cmake_is_debug(env: 'Environment') -> bool: - if OptionKey('b_vscrt') in env.coredata.optstore: - is_debug = env.coredata.get_option(OptionKey('buildtype')) == 'debug' - if env.coredata.optstore.get_value('b_vscrt') in {'mdd', 'mtd'}: + if 'b_vscrt' in env.coredata.optstore: + is_debug = env.coredata.optstore.get_value_for('buildtype') == 'debug' + if env.coredata.optstore.get_value_for('b_vscrt') in {'mdd', 'mtd'}: is_debug = True return is_debug else: # Don't directly assign to is_debug to make mypy happy - debug_opt = env.coredata.get_option(OptionKey('debug')) + debug_opt = env.coredata.optstore.get_value_for('debug') assert isinstance(debug_opt, bool) return debug_opt diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py index cbe75f36c688..0c704f94ab0f 100644 --- a/mesonbuild/cmake/executor.py +++ b/mesonbuild/cmake/executor.py @@ -11,7 +11,6 @@ from .. import mlog from ..mesonlib import PerMachine, Popen_safe, version_compare, is_windows -from ..options import OptionKey from ..programs import find_external_program, NonExistingExternalProgram if T.TYPE_CHECKING: @@ -52,7 +51,9 @@ def __init__(self, environment: 'Environment', version: str, for_machine: Machin self.cmakebin = None return - self.prefix_paths = self.environment.coredata.optstore.get_value(OptionKey('cmake_prefix_path', machine=self.for_machine)) + prefpath = self.environment.coredata.optstore.get_value_for('cmake_prefix_path') + assert isinstance(prefpath, list) + self.prefix_paths = prefpath if self.prefix_paths: self.extra_cmake_args += ['-DCMAKE_PREFIX_PATH={}'.format(';'.join(self.prefix_paths))] diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 4f93ea14e0ad..4f2bd2fae27f 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -8,6 +8,7 @@ import typing as T from .. import options +from ..options import OptionKey from .. import mlog from ..mesonlib import MesonException, version_compare from .c_function_attributes import C_FUNC_ATTRIBUTES @@ -36,13 +37,14 @@ ) if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice from .compilers import CompileCheckMode + from ..build import BuildTarget CompilerMixinBase = Compiler else: @@ -130,20 +132,19 @@ def get_options(self) -> 'MutableKeyedOptionDictType': gnu_winlibs) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', 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. - key = self.form_compileropt_key('winlibs') - libs = options.get_value(key).copy() - assert isinstance(libs, list) + retval = self.get_compileropt_value('winlibs', env, target, subproject) + assert isinstance(retval, list) + libs: T.List[str] = retval.copy() for l in libs: assert isinstance(l, str) return libs @@ -219,15 +220,15 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c90', 'c99', 'c11'], gnu=True) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] @@ -263,20 +264,22 @@ def get_options(self) -> 'MutableKeyedOptionDictType': gnu_winlibs) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + key = OptionKey('c_std', machine=self.for_machine) + std = self.get_compileropt_value(key, env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', 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 - key = self.form_compileropt_key('winlibs') - libs: T.List[str] = options.get_value(key).copy() - assert isinstance(libs, list) + retval = self.get_compileropt_value('winlibs', env, target, subproject) + + assert isinstance(retval, list) + libs: T.List[str] = retval.copy() for l in libs: assert isinstance(l, str) return libs @@ -385,10 +388,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(stds, gnu=True) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args @@ -412,11 +415,10 @@ def get_options(self) -> MutableKeyedOptionDictType: msvc_winlibs) return opts - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - # need a TypeDict to make this work - key = self.form_compileropt_key('winlibs') - libs = options.get_value(key).copy() - assert isinstance(libs, list) + 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) + assert isinstance(retval, list) + libs: T.List[str] = retval.copy() for l in libs: assert isinstance(l, str) return libs @@ -449,12 +451,12 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(stds, gnu=True, gnu_deprecated=True) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + # As of MVSC 16.8, /std:c11 and /std:c17 are the only valid C standard options. - if std == 'c11': + if std in {'c11'}: args.append('/std:c11') elif std in {'c17', 'c18'}: args.append('/std:c17') @@ -471,9 +473,9 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic full_version=full_version) ClangClCompiler.__init__(self, target) - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - key = self.form_compileropt_key('std') - std = options.get_value(key) + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != "none": return [f'/clang:-std={std}'] return [] @@ -503,10 +505,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99', 'c11']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + std = self.get_compileropt_value('winlibs', env, 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) elif std != 'none': @@ -537,10 +539,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99', 'c11']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('--' + std) return args @@ -570,10 +572,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std == 'c89': args.append('-lang=c') elif std == 'c99': @@ -618,10 +620,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-ansi') args.append('-std=' + std) @@ -661,7 +663,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] def get_no_optimization_args(self) -> T.List[str]: @@ -702,10 +704,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('--' + std) return args @@ -736,10 +738,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['c99']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-lang') args.append(std) @@ -764,10 +766,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['c99']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-lang ' + std) return args diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 0f7ef172f5a6..e4c7f77441a2 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -256,12 +256,16 @@ def init_option(self, name: OptionKey) -> options.UserOption[_T]: base_options = {key: base_opt.init_option(key) for key, base_opt in BASE_OPTIONS.items()} -def option_enabled(boptions: T.Set[OptionKey], options: 'KeyedOptionDictType', - option: OptionKey) -> bool: +def option_enabled(boptions: T.Set[OptionKey], + target: 'BuildTarget', + env: 'Environment', + option: T.Union[str, OptionKey]) -> bool: + if isinstance(option, str): + option = OptionKey(option) try: if option not in boptions: return False - ret = options.get_value(option) + ret = env.coredata.get_option_for_target(target, option) assert isinstance(ret, bool), 'must return bool' # could also be str return ret except KeyError: @@ -271,7 +275,7 @@ def option_enabled(boptions: T.Set[OptionKey], options: 'KeyedOptionDictType', def get_option_value(options: 'KeyedOptionDictType', opt: OptionKey, fallback: '_T') -> '_T': """Get the value of an option, or the fallback value.""" try: - v: '_T' = options.get_value(opt) + v: '_T' = options.get_value(opt) # type: ignore [assignment] except (KeyError, AttributeError): return fallback @@ -279,37 +283,75 @@ def get_option_value(options: 'KeyedOptionDictType', opt: OptionKey, fallback: ' # Mypy doesn't understand that the above assert ensures that v is type _T return v +def get_option_value_for_target(env: 'Environment', target: 'BuildTarget', opt: OptionKey, fallback: '_T') -> '_T': + """Get the value of an option, or the fallback value.""" + try: + v = env.coredata.get_option_for_target(target, opt) + except (KeyError, AttributeError): + return fallback + + assert isinstance(v, type(fallback)), f'Should have {type(fallback)!r} but was {type(v)!r}' + # Mypy doesn't understand that the above assert ensures that v is type _T + return v + + +def get_target_option_value(target: 'BuildTarget', + env: 'Environment', + opt: T.Union[OptionKey, str], + fallback: '_T') -> '_T': + """Get the value of an option, or the fallback value.""" + try: + v = env.coredata.get_option_for_target(target, opt) + except KeyError: + return fallback + + assert isinstance(v, type(fallback)), f'Should have {type(fallback)!r} but was {type(v)!r}' + # Mypy doesn't understand that the above assert ensures that v is type _T + return v + -def are_asserts_disabled(options: KeyedOptionDictType) -> bool: +def are_asserts_disabled(target: 'BuildTarget', env: 'Environment') -> bool: """Should debug assertions be disabled - :param options: OptionDictionary + :param target: a target to check for + :param env: the environment :return: whether to disable assertions or not """ - return (options.get_value('b_ndebug') == 'true' or - (options.get_value('b_ndebug') == 'if-release' and - options.get_value('buildtype') in {'release', 'plain'})) + return (env.coredata.get_option_for_target(target, 'b_ndebug') == 'true' or + (env.coredata.get_option_for_target(target, 'b_ndebug') == 'if-release' and + env.coredata.get_option_for_target(target, 'buildtype') in {'release', 'plain'})) +def are_asserts_disabled_for_subproject(subproject: str, env: 'Environment') -> bool: + return (env.coredata.get_option_for_subproject('b_ndebug', subproject) == 'true' or + (env.coredata.get_option_for_subproject('b_ndebug', subproject) == 'if-release' and + env.coredata.get_option_for_subproject('buildtype', subproject) in {'release', 'plain'})) -def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler', env: 'Environment') -> T.List[str]: + +def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Environment') -> T.List[str]: args: T.List[str] = [] try: - if options.get_value(OptionKey('b_lto')): + 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( - threads=get_option_value(options, OptionKey('b_lto_threads'), 0), - mode=get_option_value(options, OptionKey('b_lto_mode'), 'default'))) + threads=num_threads, + mode=ltomode)) except (KeyError, AttributeError): pass try: - args += compiler.get_colorout_args(options.get_value(OptionKey('b_colorout'))) - except (KeyError, AttributeError): + clrout = env.coredata.get_option_for_target(target, 'b_colorout') + assert isinstance(clrout, str) + args += compiler.get_colorout_args(clrout) + except KeyError: pass try: - args += compiler.sanitizer_compile_args(options.get_value(OptionKey('b_sanitize'))) - except (KeyError, AttributeError): + sanitize = env.coredata.get_option_for_target(target, 'b_sanitize') + assert isinstance(sanitize, str) + args += compiler.sanitizer_compile_args(sanitize) + except KeyError: pass try: - pgo_val = options.get_value(OptionKey('b_pgo')) + pgo_val = env.coredata.get_option_for_target(target, 'b_pgo') if pgo_val == 'generate': args.extend(compiler.get_profile_generate_args()) elif pgo_val == 'use': @@ -317,21 +359,23 @@ def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler', except (KeyError, AttributeError): pass try: - if options.get_value(OptionKey('b_coverage')): + if env.coredata.get_option_for_target(target, 'b_coverage'): args += compiler.get_coverage_args() except (KeyError, AttributeError): pass try: - args += compiler.get_assert_args(are_asserts_disabled(options), env) - except (KeyError, AttributeError): + args += compiler.get_assert_args(are_asserts_disabled(target, env), env) + except KeyError: pass # This does not need a try...except - if option_enabled(compiler.base_options, options, OptionKey('b_bitcode')): + if option_enabled(compiler.base_options, target, env, 'b_bitcode'): args.append('-fembed-bitcode') try: + crt_val = env.coredata.get_option_for_target(target, 'b_vscrt') + assert isinstance(crt_val, str) + buildtype = env.coredata.get_option_for_target(target, 'buildtype') + assert isinstance(buildtype, str) try: - crt_val = options.get_value(OptionKey('b_vscrt')) - buildtype = options.get_value(OptionKey('buildtype')) args += compiler.get_crt_compile_args(crt_val, buildtype) except AttributeError: pass @@ -339,31 +383,38 @@ def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler', pass return args -def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler', - is_shared_module: bool, build_dir: str) -> T.List[str]: +def get_base_link_args(target: 'BuildTarget', + linker: 'Compiler', + env: 'Environment') -> T.List[str]: args: T.List[str] = [] + build_dir = env.get_build_dir() try: - if options.get_value('b_lto'): - if options.get_value('werror'): + if env.coredata.get_option_for_target(target, 'b_lto'): + if env.coredata.get_option_for_target(target, 'werror'): args.extend(linker.get_werror_args()) thinlto_cache_dir = None - if get_option_value(options, OptionKey('b_thinlto_cache'), False): - thinlto_cache_dir = get_option_value(options, OptionKey('b_thinlto_cache_dir'), '') + cachedir_key = OptionKey('b_thinlto_cache') + if get_option_value_for_target(env, target, cachedir_key, False): + 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') + 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( - threads=get_option_value(options, OptionKey('b_lto_threads'), 0), - mode=get_option_value(options, OptionKey('b_lto_mode'), 'default'), + threads=num_threads, + mode=lto_mode, thinlto_cache_dir=thinlto_cache_dir)) except (KeyError, AttributeError): pass try: - args += linker.sanitizer_link_args(options.get_value('b_sanitize')) - except (KeyError, AttributeError): + sanitizer = env.coredata.get_option_for_target(target, 'b_sanitize') + assert isinstance(sanitizer, str) + args += linker.sanitizer_link_args(sanitizer) + except KeyError: pass try: - pgo_val = options.get_value('b_pgo') + pgo_val = env.coredata.get_option_for_target(target, 'b_pgo') if pgo_val == 'generate': args.extend(linker.get_profile_generate_args()) elif pgo_val == 'use': @@ -371,16 +422,16 @@ def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler', except (KeyError, AttributeError): pass try: - if options.get_value('b_coverage'): + if env.coredata.get_option_for_target(target, 'b_coverage'): args += linker.get_coverage_link_args() except (KeyError, AttributeError): pass - as_needed = option_enabled(linker.base_options, options, OptionKey('b_asneeded')) - bitcode = option_enabled(linker.base_options, options, OptionKey('b_bitcode')) + as_needed = option_enabled(linker.base_options, target, env, 'b_asneeded') + bitcode = option_enabled(linker.base_options, target, env, 'b_bitcode') # Shared modules cannot be built with bitcode_bundle because # -bitcode_bundle is incompatible with -undefined and -bundle - if bitcode and not is_shared_module: + if bitcode and not target.typename == 'shared module': args.extend(linker.bitcode_args()) elif as_needed: # -Wl,-dead_strip_dylibs is incompatible with bitcode @@ -389,18 +440,23 @@ def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler', # Apple's ld (the only one that supports bitcode) does not like -undefined # arguments or -headerpad_max_install_names when bitcode is enabled if not bitcode: + from ..build import SharedModule args.extend(linker.headerpad_args()) - if (not is_shared_module and - option_enabled(linker.base_options, options, OptionKey('b_lundef'))): + if (not isinstance(target, SharedModule) and + option_enabled(linker.base_options, target, env, 'b_lundef')): args.extend(linker.no_undefined_link_args()) else: args.extend(linker.get_allow_undefined_link_args()) try: + crt_val = env.coredata.get_option_for_target(target, 'b_vscrt') + assert isinstance(crt_val, str) + buildtype = env.coredata.get_option_for_target(target, 'buildtype') + assert isinstance(buildtype, str) try: - crt_val = options.get_value(OptionKey('b_vscrt')) - buildtype = options.get_value(OptionKey('buildtype')) - args += linker.get_crt_link_args(crt_val, buildtype) + crtargs = linker.get_crt_link_args(crt_val, buildtype) + assert isinstance(crtargs, list) + args += crtargs except AttributeError: pass except KeyError: @@ -598,11 +654,11 @@ def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey def get_options(self) -> 'MutableKeyedOptionDictType': return {} - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - return self.linker.get_option_args(options) + 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 check_header(self, hname: str, prefix: str, env: 'Environment', *, extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None, @@ -894,8 +950,8 @@ def get_link_debugfile_args(self, targetfile: str) -> T.List[str]: def get_std_shared_lib_link_args(self) -> T.List[str]: return self.linker.get_std_shared_lib_args() - def get_std_shared_module_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - return self.linker.get_std_shared_module_args(options) + def get_std_shared_module_link_args(self, target: 'BuildTarget') -> T.List[str]: + return self.linker.get_std_shared_module_args(target) def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: return self.linker.get_link_whole_for(args) @@ -1358,6 +1414,19 @@ def get_preprocessor(self) -> Compiler: def form_compileropt_key(self, basename: str) -> OptionKey: return OptionKey(f'{self.language}_{basename}', machine=self.for_machine) + def get_compileropt_value(self, + key: T.Union[str, OptionKey], + env: Environment, + target: T.Optional[BuildTarget], + subproject: T.Optional[str] = None + ) -> T.Union[str, int, bool, T.List[str]]: + if isinstance(key, str): + key = self.form_compileropt_key(key) + if target: + return env.coredata.get_option_for_target(target, key) + else: + return env.coredata.get_option_for_subproject(key, subproject) + def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[str]) -> None: key = self.form_compileropt_key('std') std = opts[key] @@ -1370,12 +1439,12 @@ def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[ def get_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, - env: 'Environment') -> dict[OptionKey, options.UserOption[T.Any]]: + 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 = argkey.evolve(f'{lang}_link_args') - envkey = argkey.evolve(f'{lang}_env_args') + 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 @@ -1383,12 +1452,12 @@ def get_global_options(lang: str, link_options = env.options.get(largkey, []) cargs = options.UserStringArrayOption( - f'{lang}_{argkey.name}', + argkey.name, description + ' compiler', comp_options, split_args=True, allow_dups=True) largs = options.UserStringArrayOption( - f'{lang}_{largkey.name}', + largkey.name, description + ' linker', link_options, split_args=True, allow_dups=True) @@ -1400,6 +1469,6 @@ def get_global_options(lang: str, # autotools compatibility. largs.extend_value(comp_options) - opts: dict[OptionKey, options.UserOption[T.Any]] = {argkey: cargs, largkey: largs} + opts: dict[OptionKey, options.AnyOptionType] = {argkey: cargs, largkey: largs} return opts diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 80f84b38ea4d..b21a62e44806 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -3,7 +3,6 @@ from __future__ import annotations -import copy import functools import os.path import typing as T @@ -35,12 +34,13 @@ from .mixins.metrowerks import mwccarm_instruction_set_args, mwcceppc_instruction_set_args if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice + from ..build import BuildTarget CompilerMixinBase = CLikeCompiler else: CompilerMixinBase = object @@ -265,18 +265,24 @@ def get_options(self) -> 'MutableKeyedOptionDictType': gnu_winlibs) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + + std = self.get_compileropt_value('std', env, target, subproject) + 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) + + assert isinstance(std, str) + assert isinstance(rtti, bool) + assert isinstance(eh, str) + assert isinstance(debugstl, bool) if std != 'none': args.append(self._find_best_cpp_std(std)) - key = self.form_compileropt_key('eh') - non_msvc_eh_options(options.get_value(key), args) + non_msvc_eh_options(eh, args) - key = self.form_compileropt_key('debugstl') - if options.get_value(key): + if debugstl: args.append('-D_GLIBCXX_DEBUG=1') # We can't do _LIBCPP_DEBUG because it's unreliable unless libc++ was built with it too: @@ -285,18 +291,17 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] if version_compare(self.version, '>=18'): args.append('-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG') - key = self.form_compileropt_key('rtti') - if not options.get_value(key): + if not rtti: args.append('-fno-rtti') return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', 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. - key = self.form_compileropt_key('winlibs') - libs = options.get_value(key).copy() - assert isinstance(libs, list) + retval = self.get_compileropt_value('winlibs', env, target, subproject) + assert isinstance(retval, list) + libs = retval[:] for l in libs: assert isinstance(l, str) return libs @@ -363,10 +368,10 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ info, linker=linker, defines=defines, full_version=full_version) - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append(self._find_best_cpp_std(std)) return args @@ -407,19 +412,20 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) - key = self.form_compileropt_key('eh') - non_msvc_eh_options(options.get_value(key), args) + eh = self.get_compileropt_value('eh', env, target, subproject) + assert isinstance(eh, str) + non_msvc_eh_options(eh, args) return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] @@ -472,32 +478,37 @@ def get_options(self) -> 'MutableKeyedOptionDictType': return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - stdkey = self.form_compileropt_key('std') - ehkey = self.form_compileropt_key('eh') - rttikey = self.form_compileropt_key('rtti') - debugstlkey = self.form_compileropt_key('debugstl') - std = options.get_value(stdkey) + std = self.get_compileropt_value('std', env, target, subproject) + 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) + + assert isinstance(std, str) + assert isinstance(rtti, bool) + assert isinstance(eh, str) + assert isinstance(debugstl, bool) + if std != 'none': args.append(self._find_best_cpp_std(std)) - non_msvc_eh_options(options.get_value(ehkey), args) + non_msvc_eh_options(eh, args) - if not options.get_value(rttikey): + if not rtti: args.append('-fno-rtti') - if options.get_value(debugstlkey): + if debugstl: args.append('-D_GLIBCXX_DEBUG=1') return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', 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. - key = self.form_compileropt_key('winlibs') - libs = options.get_value(key).copy() - assert isinstance(libs, list) + retval = self.get_compileropt_value('winlibs', env, target, subproject) + assert isinstance(retval, list) + libs: T.List[str] = retval[:] for l in libs: assert isinstance(l, str) return libs @@ -621,18 +632,21 @@ def has_function(self, funcname: str, prefix: str, env: 'Environment', *, dependencies=dependencies) # Elbrus C++ compiler does not support RTTI, so don't check for it. - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append(self._find_best_cpp_std(std)) - key = self.form_compileropt_key('eh') - non_msvc_eh_options(options.get_value(key), args) + eh = self.get_compileropt_value('eh', env, target, subproject) + assert isinstance(eh, str) - key = self.form_compileropt_key('debugstl') - if options.get_value(key): + non_msvc_eh_options(eh, args) + + debugstl = self.get_compileropt_value('debugstl', env, target, subproject) + assert isinstance(debugstl, str) + if debugstl: args.append('-D_GLIBCXX_DEBUG=1') return args @@ -694,25 +708,34 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, c_stds + g_stds) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + + std = self.get_compileropt_value('std', env, target, subproject) + 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) + + assert isinstance(std, str) + assert isinstance(rtti, bool) + assert isinstance(eh, str) + assert isinstance(debugstl, bool) + if std != 'none': remap_cpp03 = { 'c++03': 'c++98', 'gnu++03': 'gnu++98' } args.append('-std=' + remap_cpp03.get(std, std)) - if options.get_value(key.evolve('eh')) == 'none': + if eh == 'none': args.append('-fno-exceptions') - if not options.get_value(key.evolve('rtti')): + if rtti: args.append('-fno-rtti') - if options.get_value(key.evolve('debugstl')): + if debugstl: args.append('-D_GLIBCXX_DEBUG=1') return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] @@ -739,10 +762,14 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): 'c++latest': (False, "latest"), } - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: # need a typeddict for this key = self.form_compileropt_key('winlibs') - return T.cast('T.List[str]', options.get_value(key)[:]) + if target: + value = env.coredata.get_option_for_target(target, key) + else: + value = env.coredata.get_option_for_subproject(key, subproject) + return T.cast('T.List[str]', value)[:] def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List[str]) -> 'MutableKeyedOptionDictType': opts = super().get_options() @@ -771,11 +798,17 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List std_opt.set_versions(cpp_stds) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - eh = options.get_value(self.form_compileropt_key('eh')) + std = self.get_compileropt_value('std', env, target, subproject) + eh = self.get_compileropt_value('eh', env, target, subproject) + rtti = self.get_compileropt_value('rtti', env, target, subproject) + + assert isinstance(std, str) + assert isinstance(rtti, bool) + assert isinstance(eh, str) + if eh == 'default': args.append('/EHsc') elif eh == 'none': @@ -783,10 +816,10 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] else: args.append('/EH' + eh) - if not options.get_value(self.form_compileropt_key('rtti')): + if not rtti: args.append('/GR-') - permissive, ver = self.VC_VERSION_MAP[options.get_value(key)] + permissive, ver = self.VC_VERSION_MAP[std] if ver is not None: args.append(f'/std:c++{ver}') @@ -800,7 +833,6 @@ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. return Compiler.get_compiler_check_args(self, mode) - class CPP11AsCPP14Mixin(CompilerMixinBase): """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14. @@ -808,25 +840,25 @@ class CPP11AsCPP14Mixin(CompilerMixinBase): This is a limitation of Clang and MSVC that ICL doesn't share. """ - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', 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. - key = self.form_compileropt_key('std') - if options.get_value(key) in {'vc++11', 'c++11'}: + stdkey = self.form_compileropt_key('std') + if target is not None: + std = env.coredata.get_option_for_target(target, stdkey) + else: + std = env.coredata.get_option_for_subproject(stdkey, subproject) + 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) - # Don't mutate anything we're going to change, we need to use - # deepcopy since we're messing with members, and we can't simply - # copy the members because the option proxy doesn't support it. - options = copy.deepcopy(options) - if options.get_value(key) == 'vc++11': - options.set_value(key, 'vc++14') - else: - options.set_value(key, 'c++14') - return super().get_option_compile_args(options) + original_args = super().get_option_compile_args(target, env, subproject) + std_mapping = {'/std:c++11': '/std:c++14', + '/std:c++14': '/std:vc++14'} + processed_args = [std_mapping.get(x, x) for x in original_args] + return processed_args class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, MSVCCompiler, CPPCompiler): @@ -859,14 +891,12 @@ def get_options(self) -> 'MutableKeyedOptionDictType': cpp_stds.extend(['c++20', 'vc++20']) return self._get_options_impl(super().get_options(), cpp_stds) - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - key = self.form_compileropt_key('std') - if options.get_value(key) != 'none' and version_compare(self.version, '<19.00.24210'): + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + std = self.get_compileropt_value('std', env, 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) - options = copy.copy(options) - options.set_value(key, 'none') - args = super().get_option_compile_args(options) + args = super().get_option_compile_args(target, env, subproject) if version_compare(self.version, '<19.11'): try: @@ -939,17 +969,17 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++03', 'c++11']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std == 'c++11': args.append('--cpp11') elif std == 'c++03': args.append('--cpp') return args - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: @@ -969,7 +999,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_always_args(self) -> T.List[str]: return ['-nologo', '-lang=cpp'] - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] def get_compile_only_args(self) -> T.List[str]: @@ -978,7 +1008,7 @@ def get_compile_only_args(self) -> T.List[str]: def get_output_args(self, outputname: str) -> T.List[str]: return [f'-output=obj={outputname}'] - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: @@ -1001,10 +1031,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++03']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('--' + std) return args @@ -1012,7 +1042,7 @@ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str] def get_always_args(self) -> T.List[str]: return [] - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] class C2000CPPCompiler(TICPPCompiler): @@ -1041,10 +1071,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, []) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-lang') args.append(std) @@ -1069,10 +1099,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, []) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-lang ' + std) return args diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 6a49d95aeb6d..284f28486163 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -8,20 +8,18 @@ import string import typing as T -from .. import coredata from .. import options from .. import mlog from ..mesonlib import ( EnvironmentException, Popen_safe, is_windows, LibType, version_compare ) -from ..options import OptionKey from .compilers import Compiler if T.TYPE_CHECKING: from .compilers import CompileCheckMode from ..build import BuildTarget - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency from ..environment import Environment # noqa: F401 from ..envconfig import MachineInfo @@ -553,7 +551,7 @@ def sanity_check(self, work_dir: str, env: 'Environment') -> None: # 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(env.coredata.optstore) + flags += self.get_ccbin_args(None, env, '') # If cross-compiling, we can't run the sanity check, only compile it. if self.is_cross and not env.has_exe_wrapper(): @@ -663,35 +661,26 @@ def get_options(self) -> 'MutableKeyedOptionDictType': return opts - def _to_host_compiler_options(self, master_options: 'KeyedOptionDictType') -> 'KeyedOptionDictType': - """ - Convert an NVCC Option set to a host compiler's option set. - """ - - # We must strip the -std option from the host compiler option set, as NVCC has - # its own -std flag that may not agree with the host compiler's. - host_options = {key: master_options.get(key, opt) for key, opt in self.host_compiler.get_options().items()} - std_key = OptionKey(f'{self.host_compiler.language}_std', machine=self.for_machine) - overrides = {std_key: 'none'} - # To shut up mypy. - return coredata.OptionsView(host_options, overrides=overrides) - - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - args = self.get_ccbin_args(options) + 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) # 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(): - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('--std=' + std) - return args + self._to_host_flags(self.host_compiler.get_option_compile_args(self._to_host_compiler_options(options))) + try: + host_compiler_args = self.host_compiler.get_option_compile_args(target, env, subproject) + except KeyError: + host_compiler_args = [] + return args + self._to_host_flags(host_compiler_args) - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: - args = self.get_ccbin_args(options) - return args + self._to_host_flags(self.host_compiler.get_option_link_args(self._to_host_compiler_options(options)), Phase.LINKER) + 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_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, suffix: str, soversion: str, @@ -801,9 +790,15 @@ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: 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, ccoptions: 'KeyedOptionDictType') -> T.List[str]: + def get_ccbin_args(self, + target: 'T.Optional[BuildTarget]', + env: 'Environment', + subproject: T.Optional[str] = None) -> T.List[str]: key = self.form_compileropt_key('ccbindir') - ccbindir = ccoptions.get_value(key) + if target: + ccbindir = env.coredata.get_option_for_target(target, key) + else: + ccbindir = env.coredata.get_option_for_subproject(key, subproject) if isinstance(ccbindir, str) and ccbindir != '': return [self._shield_nvcc_list_arg('-ccbin='+ccbindir, False)] else: diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index ed0ab31ad376..27cad5502f90 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -11,8 +11,9 @@ from .compilers import Compiler if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..environment import Environment + from ..build import BuildTarget class CythonCompiler(Compiler): @@ -85,13 +86,14 @@ def get_options(self) -> 'MutableKeyedOptionDictType': return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('version') - version = options.get_value(key) + version = self.get_compileropt_value('version', env, target, subproject) + assert isinstance(version, str) args.append(f'-{version}') - key = self.form_compileropt_key('language') - lang = options.get_value(key) + + lang = self.get_compileropt_value('language', env, target, subproject) + assert isinstance(lang, str) if lang == 'cpp': args.append('--cplus') return args diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 72c9a5a97fdd..088551872259 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -27,12 +27,13 @@ ) if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice + from ..build import BuildTarget class FortranCompiler(CLikeCompiler, Compiler): @@ -284,10 +285,10 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, fortran_stds) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args @@ -418,11 +419,11 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} + assert isinstance(std, str) if std != 'none': args.append('-stand=' + stds[std]) return args @@ -472,11 +473,11 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} + assert isinstance(std, str) if std != 'none': args.append('/stand:' + stds[std]) return args diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 19a6bb4875af..4792a8a03d17 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -378,7 +378,7 @@ def _get_basic_compiler_args(self, env: 'Environment', mode: CompileCheckMode) - 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) + cargs += self.get_crt_compile_args(crt_val, buildtype) # type: ignore[arg-type] except (KeyError, AttributeError): pass diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 5818d8dee0b5..4bf0826271cd 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -18,7 +18,7 @@ if T.TYPE_CHECKING: from ...environment import Environment - from ...coredata import KeyedOptionDictType + from ...build import BuildTarget class ElbrusCompiler(GnuLikeCompiler): @@ -83,9 +83,14 @@ def get_pch_suffix(self) -> str: # Actually it's not supported for now, but probably will be supported in future return 'pch' - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - std = options.get_value(OptionKey(f'{self.language}_std', machine=self.for_machine)) + key = OptionKey(f'{self.language}_std', machine=self.for_machine) + if target: + std = env.coredata.get_option_for_target(target, key) + else: + std = env.coredata.get_option_for_subproject(key, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py index 24ffceda63ed..c5b2e6daceef 100644 --- a/mesonbuild/compilers/mixins/emscripten.py +++ b/mesonbuild/compilers/mixins/emscripten.py @@ -51,7 +51,8 @@ def _get_compile_output(self, dirname: str, mode: CompileCheckMode) -> str: def thread_link_flags(self, env: 'Environment') -> T.List[str]: args = ['-pthread'] - count: int = env.coredata.optstore.get_value(OptionKey(f'{self.language}_thread_count', machine=self.for_machine)) + count = env.coredata.optstore.get_value(OptionKey(f'{self.language}_thread_count', machine=self.for_machine)) + assert isinstance(count, int) if count: args.append(f'-sPTHREAD_POOL_SIZE={count}') return args diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 8d17a94b2d16..6c9daf3fce58 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -19,6 +19,7 @@ from ...coredata import KeyedOptionDictType from ...environment import Environment from ...compilers.compilers import Compiler + from ...build import BuildTarget 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 @@ -58,7 +59,7 @@ def get_linker_always_args(self) -> T.List[str]: def get_linker_lib_prefix(self) -> str: return '' - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: BuildTarget, env: Environment, 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]: diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 262a4c484f2e..b133d47c4dcf 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -20,6 +20,7 @@ from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice + from ..build import BuildTarget class ObjCCompiler(CLikeCompiler, Compiler): @@ -75,14 +76,18 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: - args = [] - std = options.get_value(self.form_compileropt_key('std')) + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + key = OptionKey('c_std', machine=self.for_machine) + if target: + std = env.coredata.get_option_for_target(target, key) + else: + std = env.coredata.get_option_for_subproject(key, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args - 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', @@ -109,9 +114,11 @@ def make_option_name(self, key: OptionKey) -> str: return 'c_std' return super().make_option_name(key) - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - std = options.get_value(self.form_compileropt_key('std')) + key = OptionKey('c_std', machine=self.for_machine) + std = self.get_compileropt_value(key, env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index 104d0cb82ebe..743bbb9cde2c 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -20,6 +20,7 @@ from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice + from ..build import BuildTarget class ObjCPPCompiler(CLikeCompiler, Compiler): @@ -80,14 +81,18 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: - args = [] - std = options.get_value(self.form_compileropt_key('std')) + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + key = OptionKey('cpp_std', machine=self.for_machine) + if target: + std = env.coredata.get_option_for_target(target, key) + else: + std = env.coredata.get_option_for_subproject(key, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args - class ClangObjCPPCompiler(ClangCPPStds, ClangCompiler, ObjCPPCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, @@ -105,9 +110,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ '3': default_warn_args + ['-Wextra', '-Wpedantic'], 'everything': ['-Weverything']} - def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - std = options.get_value(self.form_compileropt_key('std')) + key = OptionKey('cpp_std', machine=self.for_machine) + std = self.get_compileropt_value(key, env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('-std=' + std) return args diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index aacdc07d7ccc..3acc30e5458c 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -15,12 +15,13 @@ from .compilers import Compiler, clike_debug_args if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..envconfig import MachineInfo from ..environment import Environment # noqa: F401 from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice from ..dependencies import Dependency + from ..build import BuildTarget rust_optimization_args: T.Dict[str, T.List[str]] = { @@ -251,10 +252,10 @@ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: # provided by the linker flags. return [] - def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args = [] - key = self.form_compileropt_key('std') - std = options.get_value(key) + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) if std != 'none': args.append('--edition=' + std) return args diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 35c7a682e013..28861a60d348 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -14,11 +14,11 @@ if T.TYPE_CHECKING: from ..arglist import CompilerArgs - from ..coredata import KeyedOptionDictType from ..envconfig import MachineInfo from ..environment import Environment from ..mesonlib import MachineChoice from ..dependencies import Dependency + from ..build import BuildTarget class ValaCompiler(Compiler): @@ -141,7 +141,7 @@ def thread_flags(self, env: 'Environment') -> T.List[str]: def thread_link_flags(self, env: 'Environment') -> T.List[str]: return [] - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] def build_wrapper_args(self, env: 'Environment', diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index fcf93a7e665b..f41b5aef2bc1 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -10,9 +10,9 @@ import pickle, os, uuid import sys from itertools import chain -from pathlib import PurePath from collections import OrderedDict, abc import dataclasses +import textwrap from .mesonlib import ( MesonBugException, @@ -34,7 +34,6 @@ if T.TYPE_CHECKING: import argparse from typing_extensions import Protocol - from typing import Any from . import dependencies from .compilers.compilers import Compiler, CompileResult, RunResult, CompileCheckMode @@ -43,7 +42,8 @@ from .mesonlib import FileOrString from .cmake.traceparser import CMakeCacheEntry from .interpreterbase import SubProject - from .options import UserOption, ElementaryOptionValues + from .options import ElementaryOptionValues + from .build import BuildTarget class SharedCMDOptions(Protocol): @@ -149,13 +149,13 @@ class DependencyCache: def __init__(self, builtins: 'KeyedOptionDictType', for_machine: MachineChoice): self.__cache: T.MutableMapping[TV_DepID, DependencySubCache] = OrderedDict() self.__builtins = builtins - self.__pkg_conf_key = OptionKey('pkg_config_path', machine=for_machine) - self.__cmake_key = OptionKey('cmake_prefix_path', machine=for_machine) + self.__pkg_conf_key = options.OptionKey('pkg_config_path') + self.__cmake_key = options.OptionKey('cmake_prefix_path') def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[str, ...]: data: T.Dict[DependencyCacheType, T.List[str]] = { - DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins.get_value(self.__pkg_conf_key)), - DependencyCacheType.CMAKE: stringlistify(self.__builtins.get_value(self.__cmake_key)), + 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.OTHER: [], } assert type_ in data, 'Someone forgot to update subkey calculations for a new type' @@ -259,9 +259,10 @@ def __init__(self, cmd_options: SharedCMDOptions, scratch_dir: str, meson_comman self.meson_command = meson_command self.target_guids = {} self.version = version - self.optstore = options.OptionStore() + self.sp_option_overrides: T.Dict[str, str] = {} self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross') self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict()) + self.optstore = options.OptionStore(self.is_cross_build()) # Stores the (name, hash) of the options file, The name will be either # "meson_options.txt" or "meson.options". @@ -288,7 +289,7 @@ def __init__(self, cmd_options: SharedCMDOptions, scratch_dir: str, meson_comman # 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.init_builtins() @staticmethod def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]: @@ -317,15 +318,15 @@ def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: # in this case we've been passed some kind of pipe, copy # the contents of that file into the meson private (scratch) # directory so that it can be re-read when wiping/reconfiguring - copy = os.path.join(scratch_dir, f'{uuid.uuid4()}.{ftype}.ini') + fcopy = os.path.join(scratch_dir, f'{uuid.uuid4()}.{ftype}.ini') with open(f, encoding='utf-8') as rf: - with open(copy, 'w', encoding='utf-8') as wf: + with open(fcopy, 'w', encoding='utf-8') as wf: wf.write(rf.read()) - real.append(copy) + real.append(fcopy) # Also replace the command line argument, as the pipe # probably won't exist on reconfigure - filenames[i] = copy + filenames[i] = fcopy continue if sys.platform != 'win32': paths = [ @@ -355,62 +356,13 @@ def builtin_options_libdir_cross_fixup(self) -> None: if self.cross_files: options.BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib' - def sanitize_prefix(self, prefix: str) -> str: - prefix = os.path.expanduser(prefix) - if not os.path.isabs(prefix): - raise MesonException(f'prefix value {prefix!r} must be an absolute path') - if prefix.endswith('/') or prefix.endswith('\\'): - # On Windows we need to preserve the trailing slash if the - # string is of type 'C:\' because 'C:' is not an absolute path. - if len(prefix) == 3 and prefix[1] == ':': - pass - # If prefix is a single character, preserve it since it is - # the root directory. - elif len(prefix) == 1: - pass - else: - prefix = prefix[:-1] - return prefix - - def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any: - ''' - If the option is an installation directory option, the value is an - absolute path and resides within prefix, return the value - as a path relative to the prefix. Otherwise, return it as is. - - This way everyone can do f.ex, get_option('libdir') and usually get - the library directory relative to prefix, even though it really - should not be relied upon. - ''' - try: - value = PurePath(value) - except TypeError: - return value - if option.name.endswith('dir') and value.is_absolute() and \ - option not in options.BUILTIN_DIR_NOPREFIX_OPTIONS: - try: - # Try to relativize the path. - value = value.relative_to(prefix) - except ValueError: - # Path is not relative, let’s keep it as is. - pass - if '..' in value.parts: - raise MesonException( - f'The value of the \'{option}\' option is \'{value}\' but ' - 'directory options are not allowed to contain \'..\'.\n' - f'If you need a path outside of the {prefix!r} prefix, ' - 'please use an absolute path.' - ) - # .as_posix() keeps the posix-like file separators Meson uses. - return value.as_posix() - - def init_builtins(self, subproject: str) -> None: + 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.evolve(subproject=subproject), opt) + 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(subproject=subproject, machine=for_machine), opt) + self.add_builtin_option(self.optstore, key.evolve(machine=for_machine), opt) @staticmethod def add_builtin_option(opts_map: 'MutableKeyedOptionDictType', key: OptionKey, @@ -443,67 +395,49 @@ def init_backend_options(self, backend_name: str) -> None: '')) def get_option(self, key: OptionKey) -> ElementaryOptionValues: - try: - v = self.optstore.get_value(key) - return v - except KeyError: - pass + return self.optstore.get_value_for(key.name, key.subproject) - try: - v = self.optstore.get_value_object(key.as_root()) - if v.yielding: - return v.value - except KeyError: - pass + def get_option_object_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> options.AnyOptionType: + return self.get_option_for_subproject(key, target.subproject) - raise MesonException(f'Tried to get unknown builtin option {str(key)}') + def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> ElementaryOptionValues: + if isinstance(key, str): + assert ':' not in key + newkey = OptionKey(key, target.subproject) + else: + newkey = key + if newkey.subproject != target.subproject: + # FIXME: this should be an error. The caller needs to ensure that + # 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) + override = target.get_override(newkey.name) + if override is not None: + return option_object.validate_value(override) + return value + + def get_option_for_subproject(self, key: T.Union[str, OptionKey], subproject) -> ElementaryOptionValues: + if isinstance(key, str): + key = OptionKey(key, subproject=subproject) + if key.subproject != subproject: + # This should be an error, fix before merging. + key = key.evolve(subproject=subproject) + return self.optstore.get_value_for(key) + + def get_option_object_for_subproject(self, key: T.Union[str, OptionKey], subproject) -> options.AnyOptionType: + #keyname = key.name + if key.subproject != subproject: + # This should be an error, fix before merging. + key = key.evolve(subproject=subproject) + return self.optstore.get_value_object_for(key) def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: dirty = False - if self.optstore.is_builtin_option(key): - if key.name == 'prefix': - value = self.sanitize_prefix(value) - else: - prefix = self.optstore.get_value('prefix') - value = self.sanitize_dir_option_value(prefix, key, value) - try: - opt = self.optstore.get_value_object(key) + changed = self.optstore.set_value(key, value, first_invocation) except KeyError: raise MesonException(f'Tried to set unknown builtin option {str(key)}') - - if opt.deprecated is True: - mlog.deprecation(f'Option {key.name!r} is deprecated') - elif isinstance(opt.deprecated, list): - for v in opt.listify(value): - if v in opt.deprecated: - mlog.deprecation(f'Option {key.name!r} value {v!r} is deprecated') - elif isinstance(opt.deprecated, dict): - def replace(v): - 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}') - return newvalue - return v - newvalue = [replace(v) for v in opt.listify(value)] - value = ','.join(newvalue) - elif isinstance(opt.deprecated, str): - # Option is deprecated and replaced by another. Note that a project - # option could be replaced by a built-in or module option, which is - # why we use OptionKey.from_string(newname) instead of - # key.evolve(newname). We set the value on both the old and new names, - # assuming they accept the same value. That could for example be - # achieved by adding the values from old option as deprecated on the - # new option, for example in the case of boolean option is replaced - # by a feature option with a different name. - newname = opt.deprecated - newkey = OptionKey.from_string(newname).evolve(subproject=key.subproject) - mlog.deprecation(f'Option {key.name!r} is replaced by {newname!r}') - dirty |= self.set_option(newkey, value, first_invocation) - - changed = opt.set_value(value) - if changed and opt.readonly and not first_invocation: - raise MesonException(f'Tried modify read only option {str(key)!r}') dirty |= changed if key.name == 'buildtype': @@ -519,7 +453,7 @@ def clear_cache(self) -> None: def get_nondefault_buildtype_args(self) -> T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]]: result: T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]] = [] - value = self.optstore.get_value('buildtype') + value = self.optstore.get_value_for('buildtype') if value == 'plain': opt = 'plain' debug = False @@ -538,8 +472,8 @@ def get_nondefault_buildtype_args(self) -> T.List[T.Union[T.Tuple[str, str, str] else: assert value == 'custom' return [] - actual_opt = self.optstore.get_value('optimization') - actual_debug = self.optstore.get_value('debug') + actual_opt = self.optstore.get_value_for('optimization') + actual_debug = self.optstore.get_value_for('debug') if actual_opt != opt: result.append(('optimization', actual_opt, opt)) if actual_debug != debug: @@ -574,6 +508,8 @@ def _set_others_from_buildtype(self, value: str) -> bool: return dirty def is_per_machine_option(self, optname: OptionKey) -> bool: + if isinstance(optname, str): + optname = OptionKey.from_string(optname) if optname.as_host() in options.BUILTIN_OPTIONS_PER_MACHINE: return True return self.optstore.is_compiler_option(optname) @@ -585,8 +521,8 @@ def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.List[str def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]: # mypy cannot analyze type of OptionKey - key = OptionKey(f'{lang}_link_args', machine=for_machine) - return T.cast('T.List[str]', self.optstore.get_value(key)) + linkkey = OptionKey(f'{lang}_link_args', machine=for_machine) + return T.cast('T.List[str]', self.optstore.get_value_for(linkkey)) def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None: for key, value in project_options.items(): @@ -611,7 +547,8 @@ def update_project_options(self, project_options: 'MutableKeyedOptionDictType', fatal=False) # Find any extranious keys for this project and remove them - for key in self.optstore.keys() - project_options.keys(): + potential_removed_keys = self.optstore.keys() - project_options.keys() + for key in potential_removed_keys: if self.optstore.is_project_option(key) and key.subproject == subproject: self.optstore.remove(key) @@ -620,12 +557,15 @@ def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) return False return len(self.cross_files) > 0 - def copy_build_options_from_regular_ones(self) -> bool: + 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(k) - dirty |= self.optstore.set_value(k.as_build(), o.value) + o = self.optstore.get_value_object_for(k.name) + dirty |= self.optstore.set_value(k, True, o.value) for bk, bv in self.optstore.items(): if bk.machine is MachineChoice.BUILD: hk = bk.as_host() @@ -645,16 +585,17 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' pfk = OptionKey('prefix') if pfk in opts_to_set: prefix = self.sanitize_prefix(opts_to_set[pfk]) - dirty |= self.optstore.set_value('prefix', prefix) for key in options.BUILTIN_DIR_NOPREFIX_OPTIONS: if key not in opts_to_set: - dirty |= self.optstore.set_value(key, options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix)) + val = options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix) + tmpkey = options.convert_oldkey(key) + dirty |= self.optstore.set_option(tmpkey, val) unknown_options: T.List[OptionKey] = [] for k, v in opts_to_set.items(): if k == pfk: continue - elif k in self.optstore: + 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) @@ -679,6 +620,54 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' return dirty + def can_set_per_sb(self, keystr): + return True + + def set_options_from_configure_strings(self, D_args) -> bool: + dirty = False + for entry in D_args: + if '=' not in entry: + raise MesonException(f'A -D argument must be of form "name=value" instead of {entry}') + key, val = entry.split('=', 1) + if key in self.sp_option_overrides: + self.sp_option_overrides[key] = val + dirty = True + else: + dirty |= self.set_options({OptionKey(key): val}) + return dirty + + def create_sp_options(self, A_args) -> bool: + if A_args is None: + return False + dirty = False + for entry in A_args: + keystr, valstr = entry.split('=', 1) + if ':' not in keystr: + raise MesonException(f'Option to add override has no subproject: {entry}') + if not self.can_set_per_sb(keystr): + raise MesonException(f'Option {keystr} can not be set per subproject.') + if keystr in self.sp_option_overrides: + raise MesonException(f'Override {keystr} already exists.') + key = self.optstore.split_keystring(keystr) + original_key = key.copy_with(subproject=None) + if not self.optstore.has_option(original_key): + raise MesonException('Tried to override a nonexisting key.') + self.sp_option_overrides[keystr] = valstr + dirty = True + return dirty + + def remove_sp_options(self, U_args) -> bool: + dirty = False + if U_args is None: + return False + for entry in U_args: + if entry in self.sp_option_overrides: + del self.sp_option_overrides[entry] + dirty = True + else: + pass # Deleting a non-existing key ok, I guess? + return dirty + def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None: from .compilers import base_options @@ -688,6 +677,8 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # 'optimization' if it is in default_options. options: T.MutableMapping[OptionKey, T.Any] = OrderedDict() for k, v in default_options.items(): + if isinstance(k, str): + k = OptionKey.from_string(k) if not subproject or k.subproject == subproject: options[k] = v options.update(env.options) @@ -701,6 +692,8 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], options = OrderedDict() for k, v in env.options.items(): + if isinstance(k, str): + k = OptionKey.from_string(k) # If this is a subproject, don't use other subproject options if k.subproject and k.subproject != subproject: continue @@ -709,7 +702,7 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # Always test this using the HOST machine, as many builtin options # are not valid for the BUILD machine, but the yielding value does # not differ between them even when they are valid for both. - if subproject and self.optstore.is_builtin_option(k) and self.optstore.get_value_object(k.evolve(subproject='', machine=MachineChoice.HOST)).yielding: + if subproject and self.optstore.is_builtin_option(k) and self.optstore.get_value_object(k.evolve(subproject=None, machine=MachineChoice.HOST)).yielding: continue # Skip base, compiler, and backend options, they are handled when # adding languages and setting backend. @@ -729,16 +722,18 @@ def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, if value is not None: o.set_value(value) if not subproject: - self.optstore.set_value_object(k, o) # override compiler option on reconfigure - self.optstore.setdefault(k, o) - - if subproject: - sk = k.evolve(subproject=subproject) - value = env.options.get(sk) or value - if value is not None: - o.set_value(value) - self.optstore.set_value_object(sk, o) # override compiler option on reconfigure - self.optstore.setdefault(sk, o) + # FIXME, add augment + #self.optstore[k] = o # override compiler option on reconfigure + pass + + comp_key = OptionKey(f'{k.name}', None, for_machine) + 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) + elif lang == 'objcpp' and k.name == 'cpp_std': + self.optstore.add_compiler_option('cpp', comp_key, o) + else: + self.optstore.add_compiler_option(lang, comp_key, o) def add_lang_args(self, lang: str, comp: T.Type['Compiler'], for_machine: MachineChoice, env: 'Environment') -> None: @@ -755,7 +750,6 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env, subproject) - enabled_opts: T.List[OptionKey] = [] for key in comp.base_options: if subproject: skey = key.evolve(subproject=subproject) @@ -765,22 +759,24 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, self.optstore.add_system_option(skey, copy.deepcopy(compilers.base_options[key])) if skey in env.options: self.optstore.set_value(skey, env.options[skey]) - enabled_opts.append(skey) elif subproject and key in env.options: self.optstore.set_value(skey, env.options[key]) - enabled_opts.append(skey) - if subproject and key not in self.optstore: - self.optstore.add_system_option(key, copy.deepcopy(self.optstore.get_value_object(skey))) + # FIXME + #if subproject and not self.optstore.has_option(key): + # self.optstore[key] = copy.deepcopy(self.optstore[skey]) elif skey in env.options: self.optstore.set_value(skey, env.options[skey]) elif subproject and key in env.options: self.optstore.set_value(skey, env.options[key]) - self.emit_base_options_warnings(enabled_opts) + self.emit_base_options_warnings() - def emit_base_options_warnings(self, enabled_opts: T.List[OptionKey]) -> None: - if OptionKey('b_bitcode') in enabled_opts: - mlog.warning('Base option \'b_bitcode\' is enabled, which is incompatible with many linker options. Incompatible options such as \'b_asneeded\' have been disabled.', fatal=False) - mlog.warning('Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.', fatal=False) + def emit_base_options_warnings(self) -> None: + bcodekey = OptionKey('b_bitcode') + if bcodekey in self.optstore and self.optstore.get_value(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') @@ -875,17 +871,14 @@ def register_builtin_arguments(parser: argparse.ArgumentParser) -> None: 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[OptionKey, str]: +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.') - k = OptionKey.from_string(key) - if subproject: - k = k.evolve(subproject=subproject) - result[k] = value + result[key] = value return result def parse_cmd_line_options(args: SharedCMDOptions) -> None: @@ -904,7 +897,7 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None: cmdline_name = options.BuiltinOption.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 + args.cmd_line_options[key.name] = value delattr(args, name) @dataclasses.dataclass @@ -914,7 +907,7 @@ class OptionsView(abc.Mapping): # TODO: the typing here could be made more explicit using a TypeDict from # python 3.8 or typing_extensions - original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, UserOption[Any]]'] + original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, options.AnyOptionType]'] subproject: T.Optional[str] = None overrides: T.Optional[T.Mapping[OptionKey, ElementaryOptionValues]] = dataclasses.field(default_factory=dict) diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 870c0b16b2c3..0c613205f5d2 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -582,7 +582,9 @@ def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.L vscrt = '' try: crt_val = self.env.coredata.optstore.get_value('b_vscrt') + assert isinstance(crt_val, str) buildtype = self.env.coredata.optstore.get_value('buildtype') + assert isinstance(buildtype, str) vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0] except (KeyError, IndexError, AttributeError): pass diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index 447b69ea070f..94e0893563a9 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -256,7 +256,9 @@ def _check_pkgconfig(self, pkgbin: ExternalProgram) -> T.Optional[str]: def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables: env = EnvironmentVariables() key = OptionKey('pkg_config_path', machine=self.for_machine) - extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:] + pathlist = self.env.coredata.optstore.get_value_for(key) + assert isinstance(pathlist, list) + extra_paths: T.List[str] = pathlist[:] if uninstalled: bpath = self.env.get_build_dir() if bpath is not None: @@ -419,7 +421,7 @@ def _search_libs(self, libs_in: ImmutableListProtocol[str], raw_libs_in: Immutab # # 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)) + 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 = self._convert_mingw_paths(pkg_config_path) prefix_libpaths = OrderedSet(sort_libpaths(list(prefix_libpaths), pkg_config_path)) system_libpaths: OrderedSet[str] = OrderedSet() diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index 1b60deb8afd2..a3a938828f17 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -19,7 +19,6 @@ from .factory import DependencyFactory from .. import mlog from .. import mesonlib -from ..options import OptionKey if T.TYPE_CHECKING: from ..compilers import Compiler @@ -297,9 +296,9 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]): # Use the buildtype by default, but look at the b_vscrt option if the # compiler supports it. - is_debug = self.env.coredata.get_option(OptionKey('buildtype')) == 'debug' - if OptionKey('b_vscrt') in self.env.coredata.optstore: - if self.env.coredata.optstore.get_value('b_vscrt') in {'mdd', 'mtd'}: + is_debug = self.env.coredata.optstore.get_value_for('buildtype') == 'debug' + if 'b_vscrt' in self.env.coredata.optstore: + if self.env.coredata.optstore.get_value_for('b_vscrt') in {'mdd', 'mtd'}: is_debug = True modules_lib_suffix = _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], is_debug) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index c09d7e312ab7..7c18104ee345 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -46,6 +46,7 @@ from .compilers import Compiler from .wrap.wrap import Resolver from . import cargo + from .build import BuildTarget CompilersDict = T.Dict[str, Compiler] @@ -624,6 +625,8 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # 'optimization' and 'debug' keys, it override them. self.options: T.MutableMapping[OptionKey, T.Union[str, T.List[str]]] = collections.OrderedDict() + self.machinestore = machinefile.MachineFileStore(self.coredata.config_files, self.coredata.cross_files, self.source_dir) + ## Read in native file(s) to override build machine configuration if self.coredata.config_files is not None: @@ -660,9 +663,6 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared self.properties = properties.default_missing() self.cmakevars = cmakevars.default_missing() - # Command line options override those from cross/native files - self.options.update(cmd_options.cmd_line_options) - # Take default value from env if not set in cross/native files or command line. self._set_default_options_from_env() self._set_default_binaries_from_env() @@ -691,6 +691,17 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # Store a global state of Cargo dependencies self.cargo: T.Optional[cargo.Interpreter] = None + def mfilestr2key(self, machine_file_string: str, section_subproject: str, machine: MachineChoice): + key = OptionKey.from_string(machine_file_string) + assert key.machine == MachineChoice.HOST + if key.subproject: + raise MesonException('Do not set subproject options in [built-in options] section, use [subproject:built-in options] instead.') + if section_subproject: + key = key.evolve(subproject=section_subproject) + if machine == MachineChoice.BUILD: + return key.evolve(machine=machine) + return key + def _load_machine_file_options(self, config: 'ConfigParser', properties: Properties, machine: MachineChoice) -> None: """Read the contents of a Machine file and put it in the options store.""" @@ -700,8 +711,9 @@ def _load_machine_file_options(self, config: 'ConfigParser', properties: Propert paths = config.get('paths') if paths: mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.') - for k, v in paths.items(): - self.options[OptionKey.from_string(k).evolve(machine=machine)] = v + for strk, v in paths.items(): + k = self.mfilestr2key(strk, None, machine) + self.options[k] = v # Next look for compiler options in the "properties" section, this is # also deprecated, and these will also be overwritten by the "built-in @@ -710,35 +722,34 @@ def _load_machine_file_options(self, config: 'ConfigParser', properties: Propert for lang in compilers.all_languages: deprecated_properties.add(lang + '_args') deprecated_properties.add(lang + '_link_args') - for k, v in properties.properties.copy().items(): - if k in deprecated_properties: - mlog.deprecation(f'{k} in the [properties] section of the machine file is deprecated, use the [built-in options] section.') - self.options[OptionKey.from_string(k).evolve(machine=machine)] = v - del properties.properties[k] + for strk, v in properties.properties.copy().items(): + if strk in deprecated_properties: + mlog.deprecation(f'{strk} in the [properties] section of the machine file is deprecated, use the [built-in options] section.') + k = self.mfilestr2key(strk, None, machine) + self.options[k] = v + del properties.properties[strk] for section, values in config.items(): if ':' in section: - subproject, section = section.split(':') + section_subproject, section = section.split(':') else: - subproject = '' + section_subproject = '' if section == 'built-in options': - for k, v in values.items(): - key = OptionKey.from_string(k) + for strk, v in values.items(): + key = self.mfilestr2key(strk, section_subproject, machine) # If we're in the cross file, and there is a `build.foo` warn about that. Later we'll remove it. if machine is MachineChoice.HOST and key.machine is not machine: mlog.deprecation('Setting build machine options in cross files, please use a native file instead, this will be removed in meson 2.0', once=True) - if key.subproject: - raise MesonException('Do not set subproject options in [built-in options] section, use [subproject:built-in options] instead.') - self.options[key.evolve(subproject=subproject, machine=machine)] = v + self.options[key] = v elif section == 'project options' and machine is MachineChoice.HOST: # Project options are only for the host machine, we don't want # to read these from the native file - for k, v in values.items(): + for strk, v in values.items(): # Project options are always for the host machine - key = OptionKey.from_string(k) + key = self.mfilestr2key(strk, section_subproject, machine) if key.subproject: raise MesonException('Do not set subproject options in [built-in options] section, use [subproject:built-in options] instead.') - self.options[key.evolve(subproject=subproject)] = v + self.options[key] = v def _set_default_options_from_env(self) -> None: opts: T.List[T.Tuple[str, str]] = ( @@ -1024,3 +1035,13 @@ def get_env_for_paths(self, library_paths: T.Set[str], extra_paths: T.Set[str]) if extra_paths: env.prepend('PATH', list(extra_paths)) return env + + def determine_option_value(self, key: T.Union[str, 'OptionKey'], target: T.Optional['BuildTarget'], subproject: T.Optional[str]) -> T.List[str]: + if target is None and subproject is None: + raise RuntimeError('Internal error, option value determination is missing arguments.') + if isinstance(key, str): + key = OptionKey(key) + if target: + return self.coredata.get_option_for_target(target, key) + else: + return self.coredata.get_option_for_subproject(key, subproject) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 6f52c0e07ecc..92f63f0d762e 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -11,7 +11,6 @@ import typing as T from .. import build -from .. import coredata from .. import dependencies from .. import options from .. import mesonlib @@ -270,10 +269,9 @@ def _determine_args(self, kwargs: BaseCompileKW, 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']: - opts = coredata.OptionsView(self.environment.coredata.optstore, self.subproject) - args += self.compiler.get_option_compile_args(opts) + args += self.compiler.get_option_compile_args(None, self.interpreter.environment, self.subproject) if mode is CompileCheckMode.LINK: - args.extend(self.compiler.get_option_link_args(opts)) + args.extend(self.compiler.get_option_link_args(None, self.interpreter.environment, self.subproject)) if kwargs.get('werror', False): args.extend(self.compiler.get_werror_args()) args.extend(kwargs['args']) diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index f7a1b99fe783..6f5a0e47e378 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -4,6 +4,8 @@ from __future__ import annotations +import copy + from .interpreterobjects import extract_required_kwarg from .. import mlog from .. import dependencies @@ -23,8 +25,11 @@ class DependencyFallbacksHolder(MesonInterpreterObject): - def __init__(self, interpreter: 'Interpreter', names: T.List[str], allow_fallback: T.Optional[bool] = None, - default_options: T.Optional[T.Dict[OptionKey, str]] = None) -> None: + 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: super().__init__(subproject=interpreter.subproject) self.interpreter = interpreter self.subproject = interpreter.subproject @@ -123,7 +128,8 @@ def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs if static is not None and 'default_library' not in default_options: default_library = 'static' if static else 'shared' mlog.log(f'Building fallback subproject with default_library={default_library}') - default_options[OptionKey('default_library')] = default_library + default_options = copy.copy(default_options) + default_options['default_library'] = default_library func_kwargs['default_options'] = default_options # Configure the subproject diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f6e1bfa0145d..4b023a8aed69 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -299,10 +299,12 @@ def __init__( 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.copy() + 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 else: self.default_project_options = {} - self.project_default_options: T.Dict[OptionKey, str] = {} + self.project_default_options: T.List[str] = [] self.build_func_dict() self.build_holder_map() self.user_defined_options = user_defined_options @@ -878,13 +880,15 @@ def disabled_subproject(self, subp_name: str, disabled_feature: T.Optional[str] return sub def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = 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 = {k.evolve(subproject=subp_name): v for k, v in kwargs['default_options'].items()} + default_options = kwargs['default_options'] if subp_name == '': raise InterpreterException('Subproject name must not be empty.') @@ -959,7 +963,7 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth raise e def _do_subproject_meson(self, subp_name: str, subdir: str, - default_options: T.Dict[OptionKey, str], + default_options: T.List[str], kwargs: kwtypes.DoSubproject, ast: T.Optional[mparser.CodeBlockNode] = None, build_def_files: T.Optional[T.List[str]] = None, @@ -1019,21 +1023,21 @@ def _do_subproject_meson(self, subp_name: str, subdir: str, return self.subprojects[subp_name] def _do_subproject_cmake(self, subp_name: str, subdir: str, - default_options: T.Dict[OptionKey, str], + default_options: T.List[str], kwargs: kwtypes.DoSubproject) -> SubprojectHolder: from ..cmake import CMakeInterpreter with mlog.nested(subp_name): - prefix = self.coredata.optstore.get_value('prefix') + prefix = self.coredata.optstore.get_value_for('prefix') from ..modules.cmake import CMakeSubprojectOptions - options = kwargs.get('options') or CMakeSubprojectOptions() - cmake_options = kwargs.get('cmake_options', []) + options.cmake_options + 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.initialise(cmake_options) cm_int.analyse() # Generate a meson ast and execute it with the normal do_subproject_meson - ast = cm_int.pretend_to_be_meson(options.target_options) + ast = cm_int.pretend_to_be_meson(kw_opts.target_options) result = self._do_subproject_meson( subp_name, subdir, default_options, kwargs, ast, @@ -1046,7 +1050,7 @@ def _do_subproject_cmake(self, subp_name: str, subdir: str, return result def _do_subproject_cargo(self, subp_name: str, subdir: str, - default_options: T.Dict[OptionKey, str], + default_options: T.List[str], kwargs: kwtypes.DoSubproject) -> SubprojectHolder: from .. import cargo FeatureNew.single_use('Cargo subproject', '1.3.0', self.subproject, location=self.current_node) @@ -1061,46 +1065,13 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, # FIXME: Are there other files used by cargo interpreter? [os.path.join(subdir, 'Cargo.toml')]) - def get_option_internal(self, optname: str) -> options.UserOption: - key = OptionKey.from_string(optname).evolve(subproject=self.subproject) - - if not self.environment.coredata.optstore.is_project_option(key): - for opts in [self.coredata.optstore, compilers.base_options]: - v = opts.get(key) - if v is None or v.yielding: - v = opts.get(key.as_root()) - if v is not None: - assert isinstance(v, options.UserOption), 'for mypy' - return v - - try: - opt = self.coredata.optstore.get_value_object(key) - if opt.yielding and key.subproject and key.as_root() in self.coredata.optstore: - popt = self.coredata.optstore.get_value_object(key.as_root()) - if type(opt) is type(popt): - opt = popt - else: - # Get class name, then option type as a string - opt_type = opt.__class__.__name__[4:][:-6].lower() - popt_type = popt.__class__.__name__[4:][:-6].lower() - # This is not a hard error to avoid dependency hell, the workaround - # when this happens is to simply set the subproject's option directly. - mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield ' - 'to parent option of type {3!r}, ignoring parent value. ' - 'Use -D{2}:{0}=value to set the value for this option manually' - '.'.format(optname, opt_type, self.subproject, popt_type), - location=self.current_node) - return opt - except KeyError: - pass - - raise InterpreterException(f'Tried to access unknown option {optname!r}.') - @typed_pos_args('get_option', str) @noKwargs def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']: optname = args[0] + if optname == 'optimization' and self.subproject == 'sub2': + pass if ':' in optname: raise InterpreterException('Having a colon in option name is forbidden, ' 'projects are not allowed to directly access ' @@ -1109,15 +1080,19 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None: raise InterpreterException(f'Invalid option name {optname!r}') - opt = self.get_option_internal(optname) - if isinstance(opt, options.UserFeatureOption): - opt.name = optname - return opt - elif isinstance(opt, options.UserOption): - if isinstance(opt.value, str): - return P_OBJ.OptionString(opt.value, f'{{{optname}}}') - return opt.value - return opt + value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject)) + if isinstance(value_object, options.UserFeatureOption): + ocopy = copy.copy(value_object) + ocopy.name = optname + ocopy.value = value + return ocopy + elif isinstance(value_object, options.UserOption): + if isinstance(value_object.value, str): + return P_OBJ.OptionString(value, f'{{{optname}}}') + return value + ocopy = copy.copy(value_object) + ocopy.value = value + return ocopy @typed_pos_args('configuration_data', optargs=[dict]) @noKwargs @@ -1220,28 +1195,22 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str else: self.coredata.options_files[self.subproject] = None - if self.subproject: - self.project_default_options = {k.evolve(subproject=self.subproject): v - for k, v in kwargs['default_options'].items()} - else: - self.project_default_options = kwargs['default_options'] - - # Do not set default_options on reconfigure otherwise it would override - # values previously set from command line. That means that changing - # default_options in a project will trigger a reconfigure but won't - # have any effect. - # - # If this is the first invocation we always need to initialize - # builtins, if this is a subproject that is new in a re-invocation we - # need to initialize builtins for that + 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): - default_options = self.project_default_options.copy() - default_options.update(self.default_project_options) - self.coredata.init_builtins(self.subproject) - self.coredata.initialized_subprojects.add(self.subproject) - else: - default_options = {} - self.coredata.set_default_options(default_options, self.subproject, self.environment) + 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.project_default_options, + self.user_defined_options.cmd_line_options) + self.coredata.initialized_subprojects.add(self.subproject) if not self.is_subproject(): self.build.project_name = proj_name @@ -1353,8 +1322,8 @@ def func_add_languages(self, node: mparser.FunctionNode, args: T.Tuple[T.List[st mlog.warning('add_languages is missing native:, assuming languages are wanted for both host and build.', location=node) - success = self.add_languages(langs, False, MachineChoice.BUILD) - success &= self.add_languages(langs, required, MachineChoice.HOST) + success = self.add_languages(langs, required, MachineChoice.HOST) + success &= self.add_languages(langs, False, MachineChoice.BUILD) return success def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> T.List[str]: @@ -1428,7 +1397,14 @@ def _print_summary(self) -> None: values['Cross files'] = self.user_defined_options.cross_file if self.user_defined_options.native_file: values['Native files'] = self.user_defined_options.native_file - sorted_options = sorted(self.user_defined_options.cmd_line_options.items()) + + def compatibility_sort_helper(s): + if isinstance(s, tuple): + s = s[0] + if isinstance(s, str): + return s + return s.name + sorted_options = sorted(self.user_defined_options.cmd_line_options.items(), key=compatibility_sort_helper) values.update({str(k): v for k, v in sorted_options}) if values: self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': None}) @@ -1566,15 +1542,6 @@ def add_languages_for(self, args: T.List[str], required: bool, for_machine: Mach # update new values from commandline, if it applies self.coredata.process_compiler_options(lang, comp, self.environment, self.subproject) - # Add per-subproject compiler options. They inherit value from main project. - 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) - if for_machine == MachineChoice.HOST or self.environment.is_cross_build(): logger_fun = mlog.log else: diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index f4a2b4107ed3..2df5aafc9a98 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -94,7 +94,7 @@ def __init__(self, option: options.UserFeatureOption, interpreter: 'Interpreter' 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('auto_features')) + auto = T.cast('options.UserFeatureOption', self.env.coredata.optstore.get_value_object_for('auto_features')) self.held_object = copy.copy(auto) self.held_object.name = option.name self.methods.update({'enabled': self.enabled_method, @@ -958,7 +958,10 @@ def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: @noKwargs @typed_pos_args('extract_objects', varargs=(mesonlib.File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) def extract_objects_method(self, args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects: - return self._target_object.extract_objects(args[0]) + 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 != '')) + return tobj.extract_objects(args[0], is_unity) @noPosargs @typed_kwargs( diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 20cafcdf0a46..61cff9aea350 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -209,7 +209,7 @@ class Project(TypedDict): version: T.Optional[FileOrString] meson_version: T.Optional[str] - default_options: T.Dict[OptionKey, options.ElementaryOptionValues] + default_options: T.List[str] license: T.List[str] license_files: T.List[str] subproject_dir: str @@ -318,7 +318,7 @@ class Subproject(ExtractRequired): class DoSubproject(ExtractRequired): - default_options: T.Dict[OptionKey, options.ElementaryOptionValues] + default_options: T.List[str] version: T.List[str] cmake_options: T.List[str] options: T.Optional[CMakeSubprojectOptions] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 08046411cd57..6519258b997d 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -16,7 +16,6 @@ from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo from ..mesonlib import (File, FileMode, MachineChoice, listify, has_path_sep, EnvironmentVariables) -from ..options import OptionKey from ..programs import ExternalProgram # Helper definition for type checks that are `Optional[T]` @@ -293,24 +292,12 @@ def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: default=[], ) -def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, ElementaryOptionValues]]) -> T.Dict[OptionKey, ElementaryOptionValues]: - if isinstance(raw, str): - raw = [raw] - if isinstance(raw, list): - output: T.Dict[OptionKey, ElementaryOptionValues] = {} - for each in raw: - k, v = split_equal_string(each) - output[OptionKey.from_string(k)] = v - return output - return {OptionKey.from_string(k): v for k, v in raw.items()} - OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.Dict[str, ElementaryOptionValues], T.List[str]]] = 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'}, ) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 176fb3348204..0dc2c0bf599d 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -14,9 +14,10 @@ from ..arglist import CompilerArgs if T.TYPE_CHECKING: - from ..coredata import KeyedOptionDictType from ..environment import Environment from ..mesonlib import MachineChoice + from ..build import BuildTarget + from ..compilers import Compiler class StaticLinker: @@ -38,7 +39,10 @@ def can_linker_accept_rsp(self) -> bool: """ return mesonlib.is_windows() - def get_base_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_base_link_args(self, + target: 'BuildTarget', + linker: 'Compiler', + env: 'Environment') -> T.List[str]: """Like compilers.get_base_link_args, but for the static linker.""" return [] @@ -68,7 +72,7 @@ def thread_link_flags(self, env: 'Environment') -> T.List[str]: def openmp_flags(self, env: Environment) -> T.List[str]: return [] - def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] @classmethod @@ -174,7 +178,10 @@ def get_lib_prefix(self) -> str: # XXX: is use_ldflags a compiler or a linker attribute? - def get_option_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + 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]: return [] def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: @@ -201,7 +208,7 @@ def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: def get_std_shared_lib_args(self) -> T.List[str]: return [] - def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_std_shared_module_args(self, Target: 'BuildTarget') -> T.List[str]: return self.get_std_shared_lib_args() def get_pie_args(self) -> T.List[str]: @@ -788,7 +795,7 @@ def get_asneeded_args(self) -> T.List[str]: def get_allow_undefined_args(self) -> T.List[str]: return self._apply_prefix('-undefined,dynamic_lookup') - def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_std_shared_module_args(self, target: 'BuildTarget') -> T.List[str]: return ['-bundle'] + self._apply_prefix('-undefined,dynamic_lookup') def get_pie_args(self) -> T.List[str]: diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 9d65cc26fa21..e486df7c1e32 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -33,6 +33,7 @@ class CMDOptions(coredata.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] @@ -46,6 +47,8 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: 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=[], + help='Remove a subproject option.') def stringify(val: T.Any) -> str: if isinstance(val, bool): @@ -230,15 +233,15 @@ def print_options(self, title: str, opts: T.Union[coredata.MutableKeyedOptionDic return if title: self.add_title(title) - auto = T.cast('options.UserFeatureOption', self.coredata.optstore.get_value_object('auto_features')) + #auto = T.cast('options.UserFeatureOption', self.coredata.optstore.get_value_for('auto_features')) for k, o in sorted(opts.items()): printable_value = o.printable_value() - root = k.as_root() - if o.yielding and k.subproject and root in self.coredata.optstore: - printable_value = '' - if isinstance(o, options.UserFeatureOption) and o.is_auto(): - printable_value = auto.printable_value() - self.add_option(str(root), o.description, printable_value, o.printable_choices()) + #root = k.as_root() + #if o.yielding and k.subproject and root in self.coredata.options: + # printable_value = '' + #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()) def print_conf(self, pager: bool) -> None: if pager: @@ -265,7 +268,7 @@ def print_default_values_warning() -> None: test_options: 'coredata.MutableKeyedOptionDictType' = {} core_options: 'coredata.MutableKeyedOptionDictType' = {} module_options: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = collections.defaultdict(dict) - for k, v in self.coredata.optstore.items(): + for k, v in self.coredata.optstore.options.items(): if k in dir_option_names: dir_options[k] = v elif k in test_option_names: @@ -288,9 +291,9 @@ def print_default_values_warning() -> None: show_build_options = self.default_values_only or self.build.environment.is_cross_build() self.add_section('Main project options') - self.print_options('Core options', host_core_options['']) - if show_build_options: - self.print_options('', build_core_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('', {})) @@ -323,6 +326,7 @@ def print_default_values_warning() -> None: print_default_values_warning() self.print_nondefault_buildtype_options() + self.print_augments() def print_nondefault_buildtype_options(self) -> None: mismatching = self.coredata.get_nondefault_buildtype_args() @@ -333,8 +337,36 @@ def print_nondefault_buildtype_options(self) -> None: for m in mismatching: mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}') + def print_sp_overrides(self) -> None: + if self.coredata.sp_option_overrides: + mlog.log('\nThe folowing options have per-subproject overrides:') + for k, v in self.coredata.sp_option_overrides.items(): + mlog.log(f'{k:21}{v:10}') + + def print_augments(self) -> None: + 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}') + 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 + +def is_print_only(options: CMDOptions) -> bool: + if has_option_flags(options): + return False + if options.clearcache: + return False + return True + def run_impl(options: CMDOptions, builddir: str) -> int: - print_only = not options.cmd_line_options and not options.clearcache + print_only = is_print_only(options) c = None try: c = Conf(builddir) @@ -345,8 +377,12 @@ def run_impl(options: CMDOptions, builddir: str) -> int: return 0 save = False - if options.cmd_line_options: - save = c.set_options(options.cmd_line_options) + 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) if options.clearcache: c.clear_cache() diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 2c1ca97a386f..7ec66fce795f 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -234,6 +234,25 @@ def set_meson_command(mainfile: str) -> None: from . import mesonlib mesonlib.set_meson_command(mainfile) +def validate_original_args(args): + import mesonbuild.options + import itertools + + def has_startswith(coll, target): + for entry in coll: + if entry.startswith(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.BuiltinOption.argparse_name_to_arg(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.') + + def run(original_args: T.List[str], mainfile: str) -> int: if os.environ.get('MESON_SHOW_DEPRECATIONS'): # workaround for https://bugs.python.org/issue34624 @@ -281,6 +300,7 @@ def run(original_args: T.List[str], mainfile: str) -> int: return run_script_command(args[1], args[2:]) set_meson_command(mainfile) + validate_original_args(args) return CommandLineParser().run(args) def main() -> int: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 383f15473eea..8ec2b1f11e6d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -336,7 +336,15 @@ def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, cdata.KeyedOptionDi 'compiler', ) add_keys(dir_options, 'directory') - add_keys({k: v for k, v in coredata.optstore.items() if coredata.optstore.is_project_option(k)}, 'user') + + def project_option_key_to_introname(key: OptionKey) -> OptionKey: + assert key.subproject is not None + if key.subproject == '': + return key.evolve(subproject=None) + return key + + add_keys({project_option_key_to_introname(k): v + for k, v in coredata.optstore.items() if coredata.optstore.is_project_option(k)}, 'user') add_keys(test_options, 'test') return optlist diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e0c1214d0851..8bde6e0917f2 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -911,6 +911,7 @@ def _get_langs_compilers_flags(state: 'ModuleState', langs_compilers: T.List[T.T cflags += state.project_args[lang] if OptionKey('b_sanitize') in compiler.base_options: sanitize = state.environment.coredata.optstore.get_value('b_sanitize') + assert isinstance(sanitize, str) cflags += compiler.sanitizer_compile_args(sanitize) sanitize = sanitize.split(',') # These must be first in ldflags diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 5072e503ec9e..3638964f28ef 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -12,7 +12,7 @@ from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary) -from ..compilers.compilers import are_asserts_disabled, lang_suffixes +from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator @@ -238,7 +238,7 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu # 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( state.environment.get_source_dir(), state.environment.get_build_dir())]) - if are_asserts_disabled(state.environment.coredata.optstore): + if are_asserts_disabled_for_subproject(state.subproject, state.environment): clang_args.append('-DNDEBUG') for de in kwargs['dependencies']: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index e634c05ab5aa..e2646f9e13d0 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -27,6 +27,7 @@ class CMDOptions(SharedCMDOptions, Protocol): 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. * @@ -187,6 +188,44 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - with mesonlib.BuildDirLock(self.build_dir): 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_project_options + errlist: T.List[str] = [] + permitted_unknowns = ['b_vscrt', 'b_lto', 'b_lundef'] + permitlist: T.List[str] = [] + for opt in pending: + # Due to backwards compatibility setting build options in non-cross + # builds is permitted and is a no-op. This should be made + # a hard error. + if not coredata.is_cross_build() and opt.is_for_build(): + continue + # 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: + continue + if coredata.optstore.is_compiler_option(opt): + continue + if opt.name in permitted_unknowns: + permitlist.append(opt.name) + continue + keystr = str(opt) + if keystr in cmd_line_options: + errlist.append(f'"{keystr}"') + if errlist: + errstr = ', '.join(errlist) + raise MesonException(f'Unknown options: {errstr}') + if permitlist: + # This is needed due to backwards compatibility. + # It was permitted to define some command line options that + # were not used. This can be seen as a bug, since + # if you define -Db_lto but the compiler class does not + # support it, this option gets silently swallowed. + # So at least print a message about it. + optstr = ','.join(permitlist) + mlog.warning(f'Some command line options went unused: {optstr}', fatal=False) + + 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. @@ -242,6 +281,9 @@ def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.O cdf = env.dump_coredata() self.finalize_postconf_hooks(b, intr) + self.check_unused_options(env.coredata, + intr.user_defined_options.cmd_line_options, + intr.subprojects) if self.options.profile: localvars = locals() fname = f'profile-{intr.backend.name}-backend.log' @@ -275,9 +317,9 @@ def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.O # collect warnings about unsupported build configurations; must be done after full arg processing # by Interpreter() init, but this is most visible at the end - if env.coredata.optstore.get_value('backend') == 'xcode': + if env.coredata.optstore.get_value_for('backend') == 'xcode': mlog.warning('xcode backend is currently unmaintained, patches welcome') - if env.coredata.optstore.get_value('layout') == 'flat': + if env.coredata.optstore.get_value_for('layout') == 'flat': mlog.warning('-Dlayout=flat is unsupported and probably broken. It was a failed experiment at ' 'making Windows build artifacts runnable while uninstalled, due to PATH considerations, ' 'but was untested by CI and anyways breaks reasonable use of conflicting targets in different subdirs. ' @@ -321,17 +363,17 @@ 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(OptionKey('genvslite')) + genvsliteval = options.cmd_line_options.pop('genvslite') # type: ignore [call-overload] # 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 = OptionKey('backend') + k_backend = 'backend' if k_backend in options.cmd_line_options.keys(): - if options.cmd_line_options[k_backend] != 'ninja': + if options.cmd_line_options[k_backend] != 'ninja': # type: ignore [index] 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' + options.cmd_line_options[k_backend] = 'ninja' # type: ignore [index] buildtypes_list = coredata.get_genvs_default_buildtype_list() vslite_ctx = {} @@ -358,7 +400,7 @@ def run(options: T.Union[CMDOptions, T.List[str]]) -> int: # lie options.pager = False - if OptionKey('genvslite') in options.cmd_line_options.keys(): + if 'genvslite' in options.cmd_line_options.keys(): run_genvslite_setup(options) else: app = MesonApp(options) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index c31254d232d4..7c22332eab6d 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -8,6 +8,10 @@ from functools import total_ordering import argparse import dataclasses +import itertools +import os +import pathlib + import typing as T from .mesonlib import ( @@ -23,6 +27,7 @@ default_sbindir, default_sysconfdir, MesonException, + MesonBugException, listify_array_value, MachineChoice, ) @@ -45,6 +50,8 @@ class ArgparseKWs(TypedDict, total=False): default: str choices: T.List + OptionValueType: TypeAlias = T.Union[str, int, bool, T.List[str]] + DEFAULT_YIELDING = False # Can't bind this near the class method it seems, sadly. @@ -54,7 +61,6 @@ class ArgparseKWs(TypedDict, total=False): genvslitelist = ['vs2022'] buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'] - # This is copied from coredata. There is no way to share this, because this # is used in the OptionKey constructor, and the coredata lists are # OptionKeys... @@ -98,6 +104,8 @@ class ArgparseKWs(TypedDict, total=False): 'vsenv', } +_BAD_VALUE = 'Qwert Zuiopü' + @total_ordering class OptionKey: @@ -111,16 +119,23 @@ class OptionKey: __slots__ = ['name', 'subproject', 'machine', '_hash'] name: str - subproject: str + subproject: T.Optional[str] # None is global, empty string means top level project machine: MachineChoice _hash: int - def __init__(self, name: str, subproject: str = '', + def __init__(self, + name: str, + subproject: T.Optional[str] = None, machine: MachineChoice = MachineChoice.HOST): + if not isinstance(machine, MachineChoice): + raise MesonException(f'Internal error, bad machine type: {machine}') + if not isinstance(name, str): + raise MesonBugException(f'Key name is not a string: {name}') # the _type option to the constructor is kinda private. We want to be # able to save the state and avoid the lookup function when # pickling/unpickling, but we need to be able to calculate it when # constructing a new OptionKey + assert ':' not in name object.__setattr__(self, 'name', name) object.__setattr__(self, 'subproject', subproject) object.__setattr__(self, 'machine', machine) @@ -160,6 +175,10 @@ def __eq__(self, other: object) -> bool: def __lt__(self, other: object) -> bool: if isinstance(other, OptionKey): + if self.subproject is None: + return other.subproject is not None + elif other.subproject is None: + return False return self._to_tuple() < other._to_tuple() return NotImplemented @@ -167,7 +186,7 @@ def __str__(self) -> str: out = self.name if self.machine is MachineChoice.BUILD: out = f'build.{out}' - if self.subproject: + if self.subproject is not None: out = f'{self.subproject}:{out}' return out @@ -181,10 +200,11 @@ def from_string(cls, raw: str) -> 'OptionKey': This takes strings like `mysubproject:build.myoption` and Creates an OptionKey out of them. """ + assert isinstance(raw, str) try: subproject, raw2 = raw.split(':') except ValueError: - subproject, raw2 = '', raw + subproject, raw2 = None, raw for_machine = MachineChoice.HOST try: @@ -202,7 +222,9 @@ def from_string(cls, raw: str) -> 'OptionKey': return cls(opt, subproject, for_machine) - def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None, + def evolve(self, + name: T.Optional[str] = None, + subproject: T.Optional[str] = _BAD_VALUE, machine: T.Optional[MachineChoice] = None) -> 'OptionKey': """Create a new copy of this key, but with altered members. @@ -214,14 +236,14 @@ def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = Non """ # We have to be a little clever with lang here, because lang is valid # as None, for non-compiler options - return OptionKey( - name if name is not None else self.name, - subproject if subproject is not None else self.subproject, - machine if machine is not None else self.machine, - ) + return OptionKey(name if name is not None else self.name, + subproject if subproject != _BAD_VALUE else self.subproject, # None is a valid value so it can'the default value in method declaration. + machine if machine is not None else self.machine) def as_root(self) -> 'OptionKey': """Convenience method for key.evolve(subproject='').""" + if self.subproject is None or self.subproject == '': + return self return self.evolve(subproject='') def as_build(self) -> 'OptionKey': @@ -232,11 +254,6 @@ def as_host(self) -> 'OptionKey': """Convenience method for key.evolve(machine=MachineChoice.HOST).""" return self.evolve(machine=MachineChoice.HOST) - def is_project_hack_for_optionsview(self) -> bool: - """This method will be removed once we can delete OptionsView.""" - import sys - sys.exit('FATAL internal error. This should not make it into an actual release. File a bug.') - def has_module_prefix(self) -> bool: return '.' in self.name @@ -251,6 +268,11 @@ def without_module_prefix(self) -> 'OptionKey': return self.evolve(newname) return self + def is_for_build(self) -> bool: + return self.machine is MachineChoice.BUILD + +if T.TYPE_CHECKING: + OptionStringLikeDict: TypeAlias = T.Dict[T.Union[OptionKey, str], str] @dataclasses.dataclass class UserOption(T.Generic[_T], HoldableObject): @@ -286,7 +308,6 @@ def set_value(self, newvalue: T.Any) -> bool: self.value = self.validate_value(newvalue) return self.value != oldvalue - @dataclasses.dataclass class EnumeratedUserOption(UserOption[_T]): @@ -298,7 +319,6 @@ def printable_choices(self) -> T.Optional[T.List[str]]: return [str(c) for c in self.choices] -@dataclasses.dataclass class UserStringOption(UserOption[str]): def validate_value(self, value: T.Any) -> str: @@ -649,7 +669,6 @@ def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help cmdline_name = self.argparse_name_to_arg(str(name)) parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs) - # Update `docs/markdown/Builtin-options.md` after changing the options below # Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required. # Please also update completion scripts in $MESONSRC/data/shell-completions/ @@ -737,52 +756,138 @@ def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help OptionKey('python.purelibdir'): {}, } + class OptionStore: - def __init__(self) -> None: - self.d: T.Dict['OptionKey', AnyOptionType] = {} + DEFAULT_DEPENDENTS = {'plain': ('plain', False), + 'debug': ('0', True), + 'debugoptimized': ('2', True), + 'release': ('3', False), + 'minsize': ('s', True), + } + + def __init__(self, is_cross: bool) -> None: + self.options: T.Dict['OptionKey', 'AnyOptionType'] = {} 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.build_options = None + self.project_options = set() + self.augments: T.Dict[str, str] = {} + self.pending_project_options: T.Dict[OptionKey, str] = {} + self.is_cross = is_cross - def __len__(self) -> int: - return len(self.d) + def clear_pending(self) -> None: + self.pending_project_options = {} - def ensure_key(self, key: T.Union[OptionKey, str]) -> OptionKey: + def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey: if isinstance(key, str): return OptionKey(key) + # FIXME. When not cross building all "build" options need to fall back + # to "host" options due to how the old code worked. + # + # This is NOT how it should be. + # + # This needs to be changed to that trying to add or access "build" keys + # is a hard error and fix issues that arise. + # + # I did not do this yet, because it would make this MR even + # more massive than it already is. Later then. + if not self.is_cross and key.machine == MachineChoice.BUILD: + key = key.evolve(machine=MachineChoice.HOST) return key - def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: - return self.d[self.ensure_key(key)] - - def get_value(self, key: T.Union[OptionKey, str]) -> 'T.Any': + def get_value(self, key: T.Union[OptionKey, str]) -> 'OptionValueType': return self.get_value_object(key).value + def __len__(self) -> int: + return len(self.options) + + def get_value_object_for(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.evolve(subproject='') + 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(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 = key.evolve(subproject=None) + if parent_key not in self.options: + raise KeyError(f'Tried to access nonexistant project parent option {parent_key}.') + return self.options[parent_key] + return potential + + def get_value_object_and_value_for(self, key: OptionKey) -> 'T.Tuple[AnyOptionType, OptionValueType]': + 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) + + def get_value_for(self, name: 'T.Union[OptionKey, str]', subproject: T.Optional[str] = None) -> 'OptionValueType': + if isinstance(name, str): + key = OptionKey(name, subproject) + else: + assert subproject is None + key = name + vobject, resolved_value = self.get_value_object_and_value_for(key) + return resolved_value + + def num_options(self) -> int: + basic = len(self.options) + build = len(self.build_options) if self.build_options else 0 + return basic + build + def add_system_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_key(key) + key = self.ensure_and_validate_key(key) if '.' in key.name: raise MesonException(f'Internal error: non-module option has a period in its name {key.name}.') self.add_system_option_internal(key, valobj) def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_key(key) + key = self.ensure_and_validate_key(key) assert isinstance(valobj, UserOption) - self.d[key] = valobj + if not isinstance(valobj.name, str): + assert isinstance(valobj.name, str) + if key not in self.options: + self.options[key] = valobj + pval = self.pending_project_options.pop(key, None) + if pval is not None: + self.set_option(key, pval) def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_key(key) + key = self.ensure_and_validate_key(key) if not key.name.startswith(language + '_'): raise MesonException(f'Internal error: all compiler option names must start with language prefix. ({key.name} vs {language}_)') self.add_system_option(key, valobj) def add_project_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_key(key) - self.d[key] = valobj - self.project_options.add(key) + key = self.ensure_and_validate_key(key) + assert key.subproject is not None + pval = self.pending_project_options.pop(key, None) + if key in self.options: + raise MesonException(f'Internal error: tried to add a project option {key} that already exists.') + else: + self.options[key] = valobj + self.project_options.add(key) + if pval is not None: + self.set_option(key, pval) def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_key(key) + key = self.ensure_and_validate_key(key) if key.name.startswith('build.'): raise MesonException('FATAL internal error: somebody goofed option handling.') if not key.name.startswith(modulename + '.'): @@ -790,43 +895,244 @@ def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valob self.add_system_option_internal(key, valobj) self.module_options.add(key) - def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool: - key = self.ensure_key(key) - return self.d[key].set_value(new_value) + def sanitize_prefix(self, prefix: str) -> str: + prefix = os.path.expanduser(prefix) + if not os.path.isabs(prefix): + raise MesonException(f'prefix value {prefix!r} must be an absolute path') + if prefix.endswith('/') or prefix.endswith('\\'): + # On Windows we need to preserve the trailing slash if the + # string is of type 'C:\' because 'C:' is not an absolute path. + if len(prefix) == 3 and prefix[1] == ':': + pass + # If prefix is a single character, preserve it since it is + # the root directory. + elif len(prefix) == 1: + pass + else: + prefix = prefix[:-1] + return prefix + + def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any: + ''' + If the option is an installation directory option, the value is an + absolute path and resides within prefix, return the value + as a path relative to the prefix. Otherwise, return it as is. + + This way everyone can do f.ex, get_option('libdir') and usually get + the library directory relative to prefix, even though it really + should not be relied upon. + ''' + try: + value = pathlib.PurePath(value) + except TypeError: + return value + if option.name.endswith('dir') and value.is_absolute() and \ + option not in BUILTIN_DIR_NOPREFIX_OPTIONS: + try: + # Try to relativize the path. + value = value.relative_to(prefix) + except ValueError: + # Path is not relative, let’s keep it as is. + pass + if '..' in value.parts: + raise MesonException( + f"The value of the '{option}' option is '{value}' but " + "directory options are not allowed to contain '..'.\n" + f"If you need a path outside of the {prefix!r} prefix, " + "please use an absolute path." + ) + # .as_posix() keeps the posix-like file separators Meson uses. + return value.as_posix() + + def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any', first_invocation: bool = False) -> bool: + key = self.ensure_and_validate_key(key) + if key.name == 'prefix': + new_value = self.sanitize_prefix(new_value) + elif self.is_builtin_option(key): + prefix = self.get_value_for('prefix') + assert isinstance(prefix, str) + new_value = self.sanitize_dir_option_value(prefix, key, new_value) + if key not in self.options: + raise MesonException(f'Unknown options: "{key.name}" not found.') + + valobj = self.options[key] + old_value = valobj.value + changed = valobj.set_value(new_value) + + if valobj.readonly and changed and not first_invocation: + raise MesonException(f'Tried to modify read only option {str(key)!r}') + + if key.name == 'prefix' and first_invocation and changed: + assert isinstance(old_value, str) + self.reset_prefixed_options(old_value, new_value) + + if changed: + self.set_dependents(key, new_value) + + return changed + + def set_dependents(self, key: OptionKey, value: str) -> None: + if key.name != 'buildtype': + return + opt, debug = self.DEFAULT_DEPENDENTS[value] + dkey = key.evolve(name='debug') + optkey = key.evolve(name='optimization') + self.options[dkey].set_value(debug) + self.options[optkey].set_value(opt) + + def set_option(self, key: OptionKey, new_value: str, first_invocation: bool = False) -> bool: + assert isinstance(key, OptionKey) + # FIXME, dupe of set_value + # Remove one of the two before merging to master. + if key.name == 'prefix': + new_value = self.sanitize_prefix(new_value) + elif self.is_builtin_option(key): + prefix = self.get_value_for('prefix') + assert isinstance(prefix, str) + new_value = self.sanitize_dir_option_value(prefix, key, new_value) + opt = self.get_value_object_for(key) + if opt.deprecated is True: + mlog.deprecation(f'Option {key.name!r} 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') + elif isinstance(opt.deprecated, dict): + def replace(v: T.Any) -> T.Any: + 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}') + 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}') + # 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 + + old_value = opt.value + changed = opt.set_value(new_value) + + if opt.readonly and changed: + raise MesonException(f'Tried modify read only option {str(key)!r}') + + if key.name == 'prefix' and first_invocation and changed: + assert isinstance(old_value, str), 'for mypy' + self.reset_prefixed_options(old_value, new_value) + + if changed: + self.set_dependents(key, new_value) + + 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) + if o in self.options: + return self.set_value(o, new_value) + o = o.evolve(subproject='') + return self.set_value(o, new_value) + + def set_subproject_options(self, subproject: str, + spcall_default_options: str, + project_default_options: str) -> None: + for o in itertools.chain(spcall_default_options, project_default_options): + keystr, valstr = o.split('=', 1) + assert ':' not in keystr + keystr = f'{subproject}:{keystr}' + if keystr not in self.augments: + self.augments[keystr] = valstr + + def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[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] + dirty = True + return dirty + + def reset_prefixed_options(self, old_prefix: str, new_prefix: str) -> None: + for optkey, prefix_mapping in BUILTIN_DIR_NOPREFIX_OPTIONS.items(): + valobj = self.options[optkey] + new_value = valobj.value + if new_prefix not in prefix_mapping: + new_value = BUILTIN_OPTIONS[optkey].default + else: + if old_prefix in prefix_mapping: + # Only reset the value if it has not been changed from the default. + if prefix_mapping[old_prefix] == valobj.value: + new_value = prefix_mapping[new_prefix] + else: + 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_key(key) - self.d[key] = new_object + 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_option_from_meson_file(self, key: OptionKey) -> 'T.Tuple[AnyOptionType, OptionValueType]': + assert isinstance(key, OptionKey) + (value_object, value) = self.get_value_object_and_value_for(key) + return (value_object, value) def remove(self, key: OptionKey) -> None: - del self.d[key] + del self.options[key] + try: + self.project_options.remove(key) + except KeyError: + pass - def __contains__(self, key: OptionKey) -> bool: - key = self.ensure_key(key) - return key in self.d + def __contains__(self, key: T.Union[str, OptionKey]) -> bool: + key = self.ensure_and_validate_key(key) + return key in self.options def __repr__(self) -> str: - return repr(self.d) + return repr(self.options) def keys(self) -> T.KeysView[OptionKey]: - return self.d.keys() + return self.options.keys() def values(self) -> T.ValuesView[AnyOptionType]: - return self.d.values() + return self.options.values() - def items(self) -> T.ItemsView['OptionKey', AnyOptionType]: - return self.d.items() + 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.d.update(**kwargs) + self.options.update(**kwargs) def setdefault(self, k: OptionKey, o: AnyOptionType) -> AnyOptionType: - return self.d.setdefault(k, o) + return self.options.setdefault(k, o) - def get(self, o: OptionKey, default: T.Optional[AnyOptionType] = None) -> T.Optional[AnyOptionType]: - return self.d.get(o, default) + 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.""" @@ -857,7 +1163,11 @@ def is_base_option(self, key: OptionKey) -> bool: def is_backend_option(self, key: OptionKey) -> bool: """Convenience method to check if this is a backend option.""" - return key.name.startswith('backend_') + if isinstance(key, str): + name: str = key + else: + name = key.name + return name.startswith('backend_') def is_compiler_option(self, key: OptionKey) -> bool: """Convenience method to check if this is a compiler option.""" @@ -872,3 +1182,218 @@ def is_compiler_option(self, key: OptionKey) -> bool: 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]]: + 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) + + def first_handle_prefix(self, + project_default_options: T.Union[T.List[str], OptionStringLikeDict], + cmd_line_options: T.Union[T.List[str], OptionStringLikeDict], + native_file_options: T.Union[T.List[str], OptionStringLikeDict]) \ + -> T.Tuple[T.Union[T.List[str], OptionStringLikeDict], + T.Union[T.List[str], OptionStringLikeDict], + T.Union[T.List[str], OptionStringLikeDict]]: + prefix = None + (possible_prefix, nopref_project_default_options) = self.prefix_split_options(project_default_options) + prefix = prefix if possible_prefix is None else possible_prefix + (possible_prefix, nopref_native_file_options) = self.prefix_split_options(native_file_options) + prefix = prefix if possible_prefix is None else possible_prefix + (possible_prefix, nopref_cmd_line_options) = self.prefix_split_options(cmd_line_options) + prefix = prefix if possible_prefix is None else possible_prefix + + if prefix is not None: + self.hard_reset_from_prefix(prefix) + return (nopref_project_default_options, nopref_cmd_line_options, nopref_native_file_options) + + def hard_reset_from_prefix(self, prefix: str) -> None: + prefix = self.sanitize_prefix(prefix) + for optkey, prefix_mapping in BUILTIN_DIR_NOPREFIX_OPTIONS.items(): + valobj = self.options[optkey] + if prefix in prefix_mapping: + new_value = prefix_mapping[prefix] + else: + new_value = BUILTIN_OPTIONS[optkey].default + valobj.set_value(new_value) + 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: T.Union[T.List[str], OptionStringLikeDict], + native_file_options_in: T.Union[T.List[str], OptionStringLikeDict]) -> None: + first_invocation = True + (project_default_options, cmd_line_options, native_file_options) = self.first_handle_prefix(project_default_options_in, + cmd_line_options_in, + native_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(native_file_options, dict) + for keystr, valstr in native_file_options.items(): + if isinstance(keystr, str): + # FIXME, standardise on Key or string. + key = OptionKey.from_string(keystr) + else: + key = keystr + if key.subproject is not None: + #self.pending_project_options[key] = valstr + augstr = str(key) + self.augments[augstr] = valstr + elif key in self.options: + self.set_value(key, valstr, first_invocation) + else: + proj_key = key.evolve(subproject='') + if proj_key in self.options: + self.options[proj_key].set_value(valstr) + else: + self.pending_project_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 fucntion can not handle the difference between the two + # and defaults to A. + assert isinstance(keystr, str) + key = OptionKey.from_string(keystr) + # Due to backwards compatibility we ignore all cross options when building + # natively. + if not self.is_cross and key.is_for_build(): + continue + if key.subproject is not None: + self.pending_project_options[key] = valstr + elif key in self.options: + self.set_option(key, valstr, first_invocation) + else: + # Setting a project option with default_options. + # Argubly this should 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.evolve(subproject='') + if self.is_project_option(proj_key): + self.set_option(proj_key, valstr) + else: + self.pending_project_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 + # Due to backwards compatibility we ignore all cross options when building + # natively. + if not self.is_cross and key.is_for_build(): + continue + if key in self.options: + self.set_value(key, valstr, True) + elif key.subproject is None: + projectkey = key.evolve(subproject='') + if projectkey in self.options: + self.options[projectkey].set_value(valstr) + else: + # 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. + assert isinstance(keystr, str) + if ':' not in keystr and not self.is_compiler_option(key) and not self.is_base_option(key): + raise MesonException(f'Unknown options: "{keystr}"') + self.pending_project_options[key] = valstr + else: + self.pending_project_options[key] = valstr + + 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 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_value(key, valstr, is_first_invocation) + else: + self.pending_project_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) + if key.subproject != subproject: + continue + self.pending_project_options.pop(key, None) + if key in self.options: + self.set_value(key, valstr, is_first_invocation) + else: + self.augments[str(key)] = valstr diff --git a/test cases/common/40 options/meson.build b/test cases/common/40 options/meson.build index de4a7d50db14..ed7668fde36b 100644 --- a/test cases/common/40 options/meson.build +++ b/test cases/common/40 options/meson.build @@ -18,7 +18,7 @@ if get_option('array_opt') != ['one', 'two'] endif # If the default changes, update test cases/unit/13 reconfigure -if get_option('b_lto') != false +if get_option('b_pch') != true error('Incorrect value in base option.') endif @@ -30,8 +30,10 @@ if get_option('integer_opt') != 3 error('Incorrect value in integer option.') endif -if get_option('neg_int_opt') != -3 - error('Incorrect value in negative integer option.') +negint = get_option('neg_int_opt') + +if negint != -3 and negint != -10 + error('Incorrect value @0@ in negative integer option.'.format(negint)) endif if get_option('CaseSenSiTivE') != 'Some CAPS' diff --git a/test cases/common/87 default options/meson.build b/test cases/common/87 default options/meson.build index 51b5cdac9fc8..1b482f1e38be 100644 --- a/test cases/common/87 default options/meson.build +++ b/test cases/common/87 default options/meson.build @@ -30,4 +30,4 @@ assert(w_level == '3', 'warning level "' + w_level + '" instead of "3"') # assert(cc.compiles('int foobar;', no_builtin_args : true), 'No_builtin did not disable builtins.') # endif -subproject('sub1') +subproject('sub1', default_options: 'func_test_option=true') diff --git a/test cases/common/87 default options/subprojects/sub1/meson.build b/test cases/common/87 default options/subprojects/sub1/meson.build index de0dc216c115..d6f79609592c 100644 --- a/test cases/common/87 default options/subprojects/sub1/meson.build +++ b/test cases/common/87 default options/subprojects/sub1/meson.build @@ -1,3 +1,4 @@ project('sub1') assert(get_option('test_option') == false) +assert(get_option('func_test_option') == true) diff --git a/test cases/common/87 default options/subprojects/sub1/meson_options.txt b/test cases/common/87 default options/subprojects/sub1/meson_options.txt index fc96f5e099c3..37ce4d4bb79e 100644 --- a/test cases/common/87 default options/subprojects/sub1/meson_options.txt +++ b/test cases/common/87 default options/subprojects/sub1/meson_options.txt @@ -1 +1,2 @@ option('test_option', type : 'boolean', value : true, description : 'Test option. Superproject overrides default to "false"') +option('func_test_option', type : 'boolean', value : false, description : 'Test option. Superproject overrides default to "true"') diff --git a/test cases/unit/123 persp options/meson.build b/test cases/unit/123 persp options/meson.build new file mode 100644 index 000000000000..2df4205e4884 --- /dev/null +++ b/test cases/unit/123 persp options/meson.build @@ -0,0 +1,24 @@ +project('toplevel', 'c') + +round = get_option('round') +opt = get_option('optimization') +if round == 1 + assert(opt == '1') +elif round == 2 + assert(opt == '1') +elif round == 3 + assert(opt == '1') +elif round == 4 + assert(opt == '3') +elif round == 5 + assert(opt == '3') +elif round == 6 + assert(opt == '3', opt) +else + assert(false, 'Invalid round number') +endif + +executable('toplevel', 'toplevel.c') + +subproject('sub1') +subproject('sub2') diff --git a/test cases/unit/123 persp options/meson.options b/test cases/unit/123 persp options/meson.options new file mode 100644 index 000000000000..2bfd08d362e3 --- /dev/null +++ b/test cases/unit/123 persp options/meson.options @@ -0,0 +1 @@ +option('round', type: 'integer', value: 1, description: 'The test round.') diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.build b/test cases/unit/123 persp options/subprojects/sub1/meson.build new file mode 100644 index 000000000000..5b176189ca1f --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub1/meson.build @@ -0,0 +1,22 @@ +project('sub1', 'c') + +round = get_option('round') +opt = get_option('optimization') +if round == 1 + assert(opt == '1') +elif round == 2 + assert(opt == '1') +elif round == 3 + assert(opt == '1') +elif round == 4 + assert(opt == '1') +elif round == 5 + assert(opt == '1') +elif round == 6 + assert(opt == '2') +else + assert(false, 'Invalid round number') +endif + + +executable('sub1', 'sub1.c') diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.options b/test cases/unit/123 persp options/subprojects/sub1/meson.options new file mode 100644 index 000000000000..ba5661a27c8b --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub1/meson.options @@ -0,0 +1 @@ +option('round', type: 'integer', value: 1, description: 'The test round.', yield: true) diff --git a/test cases/unit/123 persp options/subprojects/sub1/sub1.c b/test cases/unit/123 persp options/subprojects/sub1/sub1.c new file mode 100644 index 000000000000..4e4b87372ad6 --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub1/sub1.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("This is subproject 1.\n"); + return 0; +} diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.build b/test cases/unit/123 persp options/subprojects/sub2/meson.build new file mode 100644 index 000000000000..e8935bc521b9 --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub2/meson.build @@ -0,0 +1,21 @@ +project('sub2', 'c') + +round = get_option('round') +opt = get_option('optimization') +if round == 1 + assert(opt == '1') +elif round == 2 + assert(opt == '3') +elif round == 3 + assert(opt == '2') +elif round == 4 + assert(opt == '2') +elif round == 5 + assert(opt == '1') +elif round == 6 + assert(opt == '2') +else + assert(false, 'Invalid round number') +endif + +executable('sub2', 'sub2.c') diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.options b/test cases/unit/123 persp options/subprojects/sub2/meson.options new file mode 100644 index 000000000000..ba5661a27c8b --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub2/meson.options @@ -0,0 +1 @@ +option('round', type: 'integer', value: 1, description: 'The test round.', yield: true) diff --git a/test cases/unit/123 persp options/subprojects/sub2/sub2.c b/test cases/unit/123 persp options/subprojects/sub2/sub2.c new file mode 100644 index 000000000000..4e4b87372ad6 --- /dev/null +++ b/test cases/unit/123 persp options/subprojects/sub2/sub2.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("This is subproject 1.\n"); + return 0; +} diff --git a/test cases/unit/123 persp options/toplevel.c b/test cases/unit/123 persp options/toplevel.c new file mode 100644 index 000000000000..5748d6b37aef --- /dev/null +++ b/test cases/unit/123 persp options/toplevel.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("This is the top level project.\n"); + return 0; +} diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7c2d3ba61ac3..4c878e38a0a0 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -279,27 +279,44 @@ def test_prefix_dependent_defaults(self): testdir = os.path.join(self.common_test_dir, '1 trivial') expected = { '/opt': {'prefix': '/opt', - 'bindir': 'bin', 'datadir': 'share', 'includedir': 'include', + 'bindir': 'bin', + 'datadir': 'share', + 'includedir': 'include', 'infodir': 'share/info', - 'libexecdir': 'libexec', 'localedir': 'share/locale', - 'localstatedir': 'var', 'mandir': 'share/man', - 'sbindir': 'sbin', 'sharedstatedir': 'com', - 'sysconfdir': 'etc'}, + 'libexecdir': 'libexec', + 'localedir': 'share/locale', + 'localstatedir': 'var', + 'mandir': 'share/man', + 'sbindir': 'sbin', + 'sharedstatedir': 'com', + 'sysconfdir': 'etc', + }, '/usr': {'prefix': '/usr', - 'bindir': 'bin', 'datadir': 'share', 'includedir': 'include', + 'bindir': 'bin', + 'datadir': 'share', + 'includedir': 'include', 'infodir': 'share/info', - 'libexecdir': 'libexec', 'localedir': 'share/locale', - 'localstatedir': '/var', 'mandir': 'share/man', - 'sbindir': 'sbin', 'sharedstatedir': '/var/lib', - 'sysconfdir': '/etc'}, + 'libexecdir': 'libexec', + 'localedir': 'share/locale', + 'localstatedir': '/var', + 'mandir': 'share/man', + 'sbindir': 'sbin', + 'sharedstatedir': '/var/lib', + 'sysconfdir': '/etc', + }, '/usr/local': {'prefix': '/usr/local', - 'bindir': 'bin', 'datadir': 'share', - 'includedir': 'include', 'infodir': 'share/info', + 'bindir': 'bin', + 'datadir': 'share', + 'includedir': 'include', + 'infodir': 'share/info', 'libexecdir': 'libexec', 'localedir': 'share/locale', - 'localstatedir': '/var/local', 'mandir': 'share/man', - 'sbindir': 'sbin', 'sharedstatedir': '/var/local/lib', - 'sysconfdir': 'etc'}, + 'localstatedir': '/var/local', + 'mandir': 'share/man', + 'sbindir': 'sbin', + 'sharedstatedir': '/var/local/lib', + 'sysconfdir': 'etc', + }, # N.B. We don't check 'libdir' as it's platform dependent, see # default_libdir(): } @@ -317,7 +334,7 @@ def test_prefix_dependent_defaults(self): name = opt['name'] value = opt['value'] if name in expected[prefix]: - self.assertEqual(value, expected[prefix][name]) + self.assertEqual(value, expected[prefix][name], f'For option {name} and prefix {prefix}.') self.wipe() def test_default_options_prefix_dependent_defaults(self): @@ -338,25 +355,27 @@ def test_default_options_prefix_dependent_defaults(self): 'sysconfdir': '/etc', 'localstatedir': '/var', 'sharedstatedir': '/sharedstate'}, + '--sharedstatedir=/var/state': {'prefix': '/usr', 'sysconfdir': '/etc', 'localstatedir': '/var', 'sharedstatedir': '/var/state'}, + '--sharedstatedir=/var/state --prefix=/usr --sysconfdir=sysconf': {'prefix': '/usr', 'sysconfdir': 'sysconf', 'localstatedir': '/var', 'sharedstatedir': '/var/state'}, } - for args in expected: - self.init(testdir, extra_args=args.split(), default_args=False) + for argument_string, expected_values in expected.items(): + self.init(testdir, extra_args=argument_string.split(), default_args=False) opts = self.introspect('--buildoptions') for opt in opts: name = opt['name'] value = opt['value'] - if name in expected[args]: - self.assertEqual(value, expected[args][name]) + if name in expected_values: + self.assertEqual(value, expected_values[name], f'For option {name}, Meson arg: {argument_string}') self.wipe() def test_clike_get_library_dirs(self): @@ -2627,7 +2646,7 @@ def test_command_line(self): 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('set_sub_opt'), True) + self.assertEqual(obj.optstore.get_value(OptionKey('set_sub_opt', '')), True) self.assertEqual(obj.optstore.get_value(OptionKey('subp_opt', 'subp')), 'default3') self.wipe() @@ -2737,7 +2756,7 @@ def test_command_line(self): self.init(testdir, extra_args=['-Dset_percent_opt=myoption%', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.optstore.get_value('set_percent_opt'), 'myoption%') + self.assertEqual(obj.optstore.get_value(OptionKey('set_percent_opt', '')), 'myoption%') self.wipe() # Setting a 2nd time the same option should override the first value @@ -2951,7 +2970,10 @@ def test_reconfigure(self): self.assertRegex(out, 'opt2 val2') self.assertRegex(out, 'opt3 val3') self.assertRegex(out, 'opt4 default4') - self.assertRegex(out, 'sub1:werror true') + # Per subproject options are stored in augments, + # not in the options themselves so these status + # messages are no longer printed. + #self.assertRegex(out, 'sub1:werror true') self.build() self.run_tests() @@ -2965,7 +2987,7 @@ def test_reconfigure(self): self.assertRegex(out, 'opt2 val2') self.assertRegex(out, 'opt3 val3') self.assertRegex(out, 'opt4 val4') - self.assertRegex(out, 'sub1:werror true') + #self.assertRegex(out, 'sub1:werror true') self.assertTrue(Path(self.builddir, '.gitignore').exists()) self.build() self.run_tests() @@ -3194,7 +3216,8 @@ def test_identity_cross(self): self.new_builddir() self.init(testdir) - def test_introspect_buildoptions_without_configured_build(self): + # Disabled for now as the introspection format needs to change to add augments. + def DISABLED_test_introspect_buildoptions_without_configured_build(self): testdir = os.path.join(self.unit_test_dir, '58 introspect buildoptions') testfile = os.path.join(testdir, 'meson.build') res_nb = self.introspect_directory(testfile, ['--buildoptions'] + self.meson_args) @@ -3513,7 +3536,8 @@ def test_introspect_meson_info(self): self.assertEqual(res1['error'], False) self.assertEqual(res1['build_files_updated'], True) - def test_introspect_config_update(self): + # Disabled for now as the introspection file format needs to change to have augments. + def DISABLE_test_introspect_config_update(self): testdir = os.path.join(self.unit_test_dir, '56 introspection') introfile = os.path.join(self.builddir, 'meson-info', 'intro-buildoptions.json') self.init(testdir) @@ -4444,7 +4468,10 @@ def test_custom_target_implicit_include(self): matches += 1 self.assertEqual(matches, 1) - def test_env_flags_to_linker(self) -> None: + # This test no longer really makes sense. Linker flags are set in options + # when it is set up. Changing the compiler after the fact does not really + # make sense and is not supported. + def DISABLED_test_env_flags_to_linker(self) -> None: # Compilers that act as drivers should add their compiler flags to the # linker, those that do not shouldn't with mock.patch.dict(os.environ, {'CFLAGS': '-DCFLAG', 'LDFLAGS': '-flto'}): @@ -4454,17 +4481,17 @@ def test_env_flags_to_linker(self) -> None: cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') cc_type = type(cc) - # Test a compiler that acts as a linker + # The compiler either invokes the linker or doesn't. Act accordingly. with mock.patch.object(cc_type, 'INVOKES_LINKER', True): cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) self.assertEqual(sorted(link_args), sorted(['-DCFLAG', '-flto'])) - # And one that doesn't - with mock.patch.object(cc_type, 'INVOKES_LINKER', False): - cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') - link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) - self.assertEqual(sorted(link_args), sorted(['-flto'])) + ## And one that doesn't + #with mock.patch.object(cc_type, 'INVOKES_LINKER', False): + # cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') + # link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) + # self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: testdir = os.path.join(self.unit_test_dir, '99 install all targets') diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index 0ac9c9cf0dc9..73682e03a12d 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -299,6 +299,8 @@ def setconf(self, arg: T.Sequence[str], will_build: bool = True) -> None: else: arg = list(arg) self._run(self.mconf_command + arg + [self.builddir]) + if will_build: + self.build() def getconf(self, optname: str): opts = self.introspect('--buildoptions') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index a8608c2b123e..2b5643620950 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1867,3 +1867,56 @@ def test_complex_link_cases(self): def test_top_options_in_sp(self): testdir = os.path.join(self.unit_test_dir, '124 pkgsubproj') self.init(testdir) + + def check_has_flag(self, compdb, src, argument): + for i in compdb: + if src in i['file']: + self.assertIn(argument, i['command']) + return + self.assertTrue(False, f'Source {src} not found in compdb') + + def test_persp_options(self): + testdir = os.path.join(self.unit_test_dir, '123 persp options') + self.init(testdir, extra_args='-Doptimization=1') + compdb = self.get_compdb() + mainsrc = 'toplevel.c' + sub1src = 'sub1.c' + sub2src = 'sub2.c' + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O1') + + # Set subproject option to O2 + self.setconf(['-Dround=2', '-D', 'sub2:optimization=3']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O3') + + # Change an already set override. + self.setconf(['-Dround=3', '-D', 'sub2:optimization=2']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O2') + + # Set top level option to O3 + self.setconf(['-Dround=4', '-D:optimization=3']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O2') + + # Unset subproject + self.setconf(['-Dround=5', '-U', 'sub2:optimization']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O1') + + # Set global value + self.setconf(['-Dround=6', '-D', 'optimization=2']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O2') + self.check_has_flag(compdb, sub2src, '-O2') diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index e71cd04fe3c0..9aa1eb4d1a80 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -546,7 +546,9 @@ def test_builtin_options_subprojects(self): elif each['name'] == 'sub:default_library': self.assertEqual(each['value'], 'static') found += 1 - self.assertEqual(found, 4, 'Did not find all three sections') + # FIXME: check that the subproject option has beeb added + # 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 diff --git a/unittests/optiontests.py b/unittests/optiontests.py new file mode 100644 index 000000000000..bbf9c0e054b9 --- /dev/null +++ b/unittests/optiontests.py @@ -0,0 +1,184 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Meson project contributors + +from mesonbuild.options import * + +import unittest + + +class OptionTests(unittest.TestCase): + + def test_basic(self): + optstore = OptionStore(False) + name = 'someoption' + default_value = 'somevalue' + new_value = 'new_value' + vo = UserStringOption(name, 'An option of some sort', default_value) + optstore.add_system_option(name, vo) + self.assertEqual(optstore.get_value_for(name), default_value) + optstore.set_option(OptionKey.from_string(name), new_value) + self.assertEqual(optstore.get_value_for(name), new_value) + + def test_toplevel_project(self): + optstore = OptionStore(False) + name = 'someoption' + default_value = 'somevalue' + new_value = 'new_value' + k = OptionKey(name) + vo = UserStringOption(k.name, 'An option of some sort', 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([f'someoption={new_value}'], {}, {}) + self.assertEqual(optstore.get_value_for(k), new_value) + + def test_parsing(self): + s1 = OptionKey.from_string('sub:optname') + s1_expected = OptionKey('optname', 'sub', MachineChoice.HOST) + self.assertEqual(s1, s1_expected) + self.assertEqual(str(s1), 'sub:optname') + + s2 = OptionKey.from_string('optname') + s2_expected = OptionKey('optname', None, MachineChoice.HOST) + self.assertEqual(s2, s2_expected) + + self.assertEqual(str(s2), 'optname') + + s3 = OptionKey.from_string(':optname') + s3_expected = OptionKey('optname', '', MachineChoice.HOST) + self.assertEqual(s3, s3_expected) + self.assertEqual(str(s3), ':optname') + + def test_subproject_for_system(self): + optstore = OptionStore(False) + name = 'someoption' + default_value = 'somevalue' + vo = UserStringOption(name, 'An option of some sort', default_value) + optstore.add_system_option(name, vo) + self.assertEqual(optstore.get_value_for(name, 'somesubproject'), default_value) + + def test_reset(self): + optstore = OptionStore(False) + name = 'someoption' + original_value = 'original' + reset_value = 'reset' + vo = UserStringOption(name, 'An option set twice', original_value) + optstore.add_system_option(name, vo) + self.assertEqual(optstore.get_value_for(name), original_value) + self.assertEqual(optstore.num_options(), 1) + vo2 = UserStringOption(name, 'An option set twice', reset_value) + optstore.add_system_option(name, vo2) + self.assertEqual(optstore.get_value_for(name), original_value) + self.assertEqual(optstore.num_options(), 1) + + def test_project_nonyielding(self): + optstore = OptionStore(False) + name = 'someoption' + top_value = 'top' + sub_value = 'sub' + vo = UserStringOption(name, 'A top level option', top_value, False) + optstore.add_project_option(OptionKey(name, ''), vo) + self.assertEqual(optstore.get_value_for(name, ''), top_value, False) + self.assertEqual(optstore.num_options(), 1) + vo2 = UserStringOption(name, 'A subproject option', sub_value) + optstore.add_project_option(OptionKey(name, 'sub'), vo2) + self.assertEqual(optstore.get_value_for(name, ''), top_value) + self.assertEqual(optstore.get_value_for(name, 'sub'), sub_value) + self.assertEqual(optstore.num_options(), 2) + + def test_project_yielding(self): + optstore = OptionStore(False) + name = 'someoption' + top_value = 'top' + sub_value = 'sub' + vo = UserStringOption(name, 'A top level option', top_value) + optstore.add_project_option(OptionKey(name, ''), vo) + self.assertEqual(optstore.get_value_for(name, ''), top_value) + self.assertEqual(optstore.num_options(), 1) + vo2 = UserStringOption(name, 'A subproject option', sub_value, True) + optstore.add_project_option(OptionKey(name, 'sub'), vo2) + self.assertEqual(optstore.get_value_for(name, ''), top_value) + self.assertEqual(optstore.get_value_for(name, 'sub'), top_value) + self.assertEqual(optstore.num_options(), 2) + + def test_augments(self): + optstore = OptionStore(False) + name = 'cpp_std' + sub_name = 'sub' + sub2_name = 'sub2' + top_value = 'c++11' + aug_value = 'c++23' + + co = UserComboOption(name, + 'C++ language standard to use', + top_value, + choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23']) + optstore.add_system_option(name, co) + 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) + + # First augment a subproject + optstore.set_from_configure_command([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) + + optstore.set_from_configure_command([], [f'{sub_name}:{name}']) + 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}'], []) + 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}']) + 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) + + def test_augment_set_sub(self): + optstore = OptionStore(False) + name = 'cpp_std' + sub_name = 'sub' + sub2_name = 'sub2' + top_value = 'c++11' + aug_value = 'c++23' + set_value = 'c++20' + + co = UserComboOption(name, + 'C++ language standard to use', + top_value, + 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}'], []) + self.assertEqual(optstore.get_value_for(name), top_value) + self.assertEqual(optstore.get_value_for(name, sub_name), set_value) + + def test_subproject_call_options(self): + optstore = OptionStore(False) + name = 'cpp_std' + default_value = 'c++11' + override_value = 'c++14' + unused_value = 'c++20' + subproject = 'sub' + + co = UserComboOption(name, + 'C++ language standard to use', + default_value, + choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'], + ) + optstore.add_system_option(name, co) + optstore.set_subproject_options(subproject, [f'cpp_std={override_value}'], [f'cpp_std={unused_value}']) + self.assertEqual(optstore.get_value_for(name), default_value) + self.assertEqual(optstore.get_value_for(name, subproject), override_value) + + # Trying again should change nothing + optstore.set_subproject_options(subproject, [f'cpp_std={unused_value}'], [f'cpp_std={unused_value}']) + self.assertEqual(optstore.get_value_for(name), default_value) + self.assertEqual(optstore.get_value_for(name, subproject), override_value) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 7c382cfc99e0..f787805a52b7 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -37,7 +37,7 @@ def test_relative_find_program(self): self.init(testdir, workdir=testdir) def test_invalid_option_names(self): - store = OptionStore() + store = OptionStore(False) interp = OptionInterpreter(store, '') def write_file(code: str): @@ -71,7 +71,7 @@ def write_file(code: str): def test_option_validation(self): """Test cases that are not catch by the optinterpreter itself.""" - store = OptionStore() + store = OptionStore(False) interp = OptionInterpreter(store, '') def write_file(code: str): @@ -173,16 +173,16 @@ def test_change_backend(self): # Change backend option is not allowed with self.assertRaises(subprocess.CalledProcessError) as cm: self.setconf('-Dbackend=none') - self.assertIn("ERROR: Tried modify read only option 'backend'", cm.exception.stdout) + self.assertIn("ERROR: Tried to modify read only option 'backend'", cm.exception.stdout) - # Reconfigure with a different backend is not allowed - with self.assertRaises(subprocess.CalledProcessError) as cm: - self.init(testdir, extra_args=['--reconfigure', '--backend=none']) - self.assertIn("ERROR: Tried modify read only option 'backend'", cm.exception.stdout) + # Check that the new value was not written in the store. + self.assertEqual(self.getconf('backend'), 'ninja') # Wipe with a different backend is allowed self.init(testdir, extra_args=['--wipe', '--backend=none']) + self.assertEqual(self.getconf('backend'), 'none') + def test_validate_dirs(self): testdir = os.path.join(self.common_test_dir, '1 trivial') @@ -407,10 +407,10 @@ def test_reconfigure_base_options(self): self.assertIn('\nMessage: c_std: c89\n', out) out = self.init(testdir, extra_args=['--reconfigure', '-Db_ndebug=if-release', '-Dsub:b_ndebug=false', '-Dc_std=c99', '-Dsub:c_std=c11']) - self.assertIn('\nMessage: b_ndebug: if-release\n', out) - self.assertIn('\nMessage: c_std: c99\n', out) - self.assertIn('\nsub| Message: b_ndebug: false\n', out) - self.assertIn('\nsub| Message: c_std: c11\n', out) + self.assertIn('\n b_ndebug : if-release\n', out) + self.assertIn('\n c_std : c99\n', out) + self.assertIn('\n sub:b_ndebug: false\n', out) + self.assertIn('\n sub:c_std : c11\n', out) def test_setup_with_unknown_option(self): testdir = os.path.join(self.common_test_dir, '1 trivial') diff --git a/unittests/windowstests.py b/unittests/windowstests.py index f602d5f2ef83..9506a75efc98 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -251,9 +251,15 @@ def test_genvslite(self): env=current_env) # Check this has actually built the appropriate exes - output_debug = subprocess.check_output(str(os.path.join(self.builddir+'_debug', 'genvslite.exe'))) - self.assertEqual( output_debug, b'Debug\r\n' ) - output_release = subprocess.check_output(str(os.path.join(self.builddir+'_release', 'genvslite.exe'))) + exe_path = str(os.path.join(self.builddir+'_debug', 'genvslite.exe')) + self.assertTrue(os.path.exists(exe_path)) + rc = subprocess.run([exe_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.assertEqual(rc.returncode, 0, rc.stdout + rc.stderr) + output_debug = rc.stdout + self.assertEqual(output_debug, b'Debug\r\n' ) + exe_path = str(os.path.join(self.builddir+'_release', 'genvslite.exe')) + self.assertTrue(os.path.exists(exe_path)) + output_release = subprocess.check_output([exe_path]) self.assertEqual( output_release, b'Non-debug\r\n' ) finally: From 7f8bef1ad7199fb05e58cc6d7a49b64a3c24c757 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 15 Feb 2025 12:59:10 +0200 Subject: [PATCH 282/624] Permit more missing b options. Closes #14254. --- mesonbuild/msetup.py | 2 +- unittests/allplatformstests.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index e2646f9e13d0..ac0ea7f06c77 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -191,7 +191,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Any, all_subprojects: T.Any) -> None: pending = coredata.optstore.pending_project_options errlist: T.List[str] = [] - permitted_unknowns = ['b_vscrt', 'b_lto', 'b_lundef'] + permitted_unknowns = ['b_vscrt', 'b_lto', 'b_lundef', 'b_ndebug'] permitlist: T.List[str] = [] for opt in pending: # Due to backwards compatibility setting build options in non-cross diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 4c878e38a0a0..865b5e34e565 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5138,3 +5138,8 @@ def test_rsp_support(self): 'link', 'lld-link', 'mwldarm', 'mwldeppc', 'optlink', 'xilink', } self.assertEqual(cc.linker.get_accepts_rsp(), has_rsp) + + def test_nonexisting_bargs(self): + testdir = os.path.join(self.unit_test_dir, '117 empty project') + args = ['-Db_ndebug=if_release'] + self.init(testdir, extra_args=args) From eca1ac18dc1978b15b500c9f1710c05cb1ccc0ec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 09:29:22 +0100 Subject: [PATCH 283/624] c: add -Wno-vla-larger-than to the exceptions for -Wno* When supplying -Wno-vla-larger-than to compiler.get_supported_arguments, meson will inject -Wvla-larger-than as an argument which considered invalid by GCC, as the converse argument is -Wvla-larger-than=. Just like CLikeCompiler._has_multi_arguments special-cases -Wno-attributes=, do the same for -Wno-vla-larger-than. Resolves: https://github.com/mesonbuild/meson/issues/14208 Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/mixins/clike.py | 14 ++++++++++---- test cases/common/104 has arg/meson.build | 3 +++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 4792a8a03d17..385883432591 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1285,10 +1285,16 @@ def _has_multi_arguments(self, args: T.List[str], env: 'Environment', code: str) # some compilers, e.g. GCC, don't warn for unsupported warning-disable # flags, so when we are testing a flag like "-Wno-forgotten-towel", also # check the equivalent enable flag too "-Wforgotten-towel". - # Make an exception for -Wno-attributes=x as -Wattributes=x is invalid - # for GCC at least. - if arg.startswith('-Wno-') and not arg.startswith('-Wno-attributes='): - new_args.append('-W' + arg[5:]) + 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 + if arg.startswith('-Wno-attributes='): + pass + elif arg == '-Wno-vla-larger-than': + new_args.append('-Wvla-larger-than=1000') + else: + new_args.append('-W' + arg[5:]) if arg.startswith('-Wl,'): mlog.warning(f'{arg} looks like a linker argument, ' 'but has_argument and other similar methods only ' diff --git a/test cases/common/104 has arg/meson.build b/test cases/common/104 has arg/meson.build index c85ec9f25ab8..466bed9dfe13 100644 --- a/test cases/common/104 has arg/meson.build +++ b/test cases/common/104 has arg/meson.build @@ -56,6 +56,9 @@ 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 cc.get_id() == 'clang' and cc.version().version_compare('<=4.0.0') From 5442f04f47f01a7b2fb768ff9500e21810a23f35 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 17 Feb 2025 20:23:00 +0200 Subject: [PATCH 284/624] Improve error message on nonexisting options. --- mesonbuild/interpreter/interpreter.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 4b023a8aed69..9c37ffb256d8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1080,7 +1080,12 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None: raise InterpreterException(f'Invalid option name {optname!r}') - value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject)) + try: + value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject)) + except KeyError: + 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) ocopy.name = optname From 1b54239a88261cbb679a342162d72632cd6b8093 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Mon, 17 Feb 2025 05:59:58 +0100 Subject: [PATCH 285/624] Add restat = 1 to Swift compile rule The swift compiler does not update the modification time of output object files when the code has not changed. Without this, Swift targets may continuously be rebuilt. --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 57d73ed1810c..ce82d7845fa0 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2449,7 +2449,7 @@ def generate_swift_compile_rules(self, compiler) -> None: command = invoc + ['$ARGS', '$in'] description = 'Compiling Swift source $in' - self.add_rule(NinjaRule(rule, command, [], description)) + self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1')) def use_dyndeps_for_fortran(self) -> bool: '''Use the new Ninja feature for scanning dependencies during build, From df79a515715ab5af963ba26376601b3bfff30a43 Mon Sep 17 00:00:00 2001 From: Campbell Jones Date: Fri, 9 Aug 2024 23:30:28 -0400 Subject: [PATCH 286/624] build: Optimize transitive link dep resolution In large repositories, transitive link dependency resolution using the current recursive algorithm can result in enough duplicate calls to cause the full system memory space to be used up. This commit simplifies link dep resolution by converting the currently used recursive algorithm to an iterative one that avoids performing work more than once. If a target's direct dependencies have already been processed, that target will not be processed again. These changes result in multiple orders of magnitude of improvements to dep resolution time and memory usage in the worst case. Co-authored-by: Xavier Claessens --- mesonbuild/backend/backends.py | 4 ++-- mesonbuild/build.py | 35 ++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a6a42e9334bf..baf83e84968c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1199,14 +1199,14 @@ def determine_windows_extra_paths( result: T.Set[str] = set() prospectives: T.Set[build.BuildTargetTypes] = set() if isinstance(target, build.BuildTarget): - prospectives.update(target.get_transitive_link_deps()) + prospectives.update(target.get_all_link_deps()) # External deps result.update(self.extract_dll_paths(target)) for bdep in extra_bdeps: prospectives.add(bdep) if isinstance(bdep, build.BuildTarget): - prospectives.update(bdep.get_transitive_link_deps()) + prospectives.update(bdep.get_all_link_deps()) # Internal deps for ld in prospectives: dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld)) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 44241ec2c829..3bc7d964122e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2,7 +2,7 @@ # Copyright 2012-2017 The Meson development team from __future__ import annotations -from collections import defaultdict, OrderedDict +from collections import defaultdict, deque, OrderedDict from dataclasses import dataclass, field, InitVar from functools import lru_cache import abc @@ -1049,15 +1049,29 @@ def extract_all_objects(self, recursive: bool = True) -> ExtractedObjects: return ExtractedObjects(self, self.sources, self.generated, self.objects, recursive, pch=True) - def get_all_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: - return self.get_transitive_link_deps() - @lru_cache(maxsize=None) - def get_transitive_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: - result: T.List[Target] = [] - for i in self.link_targets: - result += i.get_all_link_deps() - return result + def get_all_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: + """ Get all shared libraries dependencies + This returns all shared libraries in the entire dependency tree. Those + are libraries needed at runtime which is different from the set needed + at link time, see get_dependencies() for that. + """ + result: OrderedSet[BuildTargetTypes] = OrderedSet() + stack: T.Deque[BuildTargetTypes] = deque() + stack.appendleft(self) + while stack: + t = stack.pop() + if t in result: + continue + if isinstance(t, CustomTargetIndex): + stack.appendleft(t.target) + continue + if isinstance(t, SharedLibrary): + result.add(t) + if isinstance(t, BuildTarget): + stack.extendleft(t.link_targets) + stack.extendleft(t.link_whole_targets) + return list(result) def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: return self.get_transitive_link_deps_mapping(prefix) @@ -2415,9 +2429,6 @@ def get_debug_filename(self) -> T.Optional[str]: """ return self.debug_filename - def get_all_link_deps(self): - return [self] + self.get_transitive_link_deps() - def get_aliases(self) -> T.List[T.Tuple[str, str, str]]: """ If the versioned library name is libfoo.so.0.100.0, aliases are: From d995cbce0f1e9454fdfe467e47c2423aa5217fd3 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 20 Feb 2025 14:05:05 +0200 Subject: [PATCH 287/624] Fix reconfiguration on --wipe. Closes #14279. --- mesonbuild/options.py | 5 ++--- unittests/allplatformstests.py | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 7c22332eab6d..838ccb794d1e 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1305,7 +1305,7 @@ def initialize_from_top_level_project_call(self, # 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 fucntion can not handle the difference between the two + # The key parsing function can not handle the difference between the two # and defaults to A. assert isinstance(keystr, str) key = OptionKey.from_string(keystr) @@ -1350,8 +1350,7 @@ def initialize_from_top_level_project_call(self, # # Some base options (sanitizers etc) might get added later. # Permitting them all is not strictly correct. - assert isinstance(keystr, str) - if ':' not in keystr and not self.is_compiler_option(key) and not self.is_base_option(key): + if not self.is_compiler_option(key) and not self.is_base_option(key): raise MesonException(f'Unknown options: "{keystr}"') self.pending_project_options[key] = valstr else: diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 865b5e34e565..634a860addde 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5143,3 +5143,8 @@ def test_nonexisting_bargs(self): testdir = os.path.join(self.unit_test_dir, '117 empty project') args = ['-Db_ndebug=if_release'] self.init(testdir, extra_args=args) + + def test_wipe_with_args(self): + testdir = os.path.join(self.common_test_dir, '1 trivial') + self.init(testdir, extra_args=['-Dc_args=-DSOMETHING']) + self.init(testdir, extra_args=['--wipe']) From 4386e2afe17f8a399d7c202476261c760cdce1e3 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 21 Feb 2025 12:45:50 +0200 Subject: [PATCH 288/624] Permit all unknown b_ options. Closes #14254. --- mesonbuild/msetup.py | 7 ++++--- unittests/platformagnostictests.py | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index ac0ea7f06c77..6753b02559ec 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -191,7 +191,6 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Any, all_subprojects: T.Any) -> None: pending = coredata.optstore.pending_project_options errlist: T.List[str] = [] - permitted_unknowns = ['b_vscrt', 'b_lto', 'b_lundef', 'b_ndebug'] permitlist: T.List[str] = [] for opt in pending: # Due to backwards compatibility setting build options in non-cross @@ -204,8 +203,10 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: if opt.subproject and opt.subproject not in all_subprojects: continue if coredata.optstore.is_compiler_option(opt): + permitlist.append(opt.name) continue - if opt.name in permitted_unknowns: + # Ditto for base options. + if coredata.optstore.is_base_option(opt): permitlist.append(opt.name) continue keystr = str(opt) @@ -222,7 +223,7 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: # support it, this option gets silently swallowed. # So at least print a message about it. optstr = ','.join(permitlist) - mlog.warning(f'Some command line options went unused: {optstr}', fatal=False) + mlog.warning(f'The following command line option(s) were not used: {optstr}', fatal=False) coredata.optstore.clear_pending() diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index f787805a52b7..990936e85ed5 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -415,9 +415,12 @@ def test_reconfigure_base_options(self): def test_setup_with_unknown_option(self): testdir = os.path.join(self.common_test_dir, '1 trivial') - for option in ('not_an_option', 'b_not_an_option'): - out = self.init(testdir, extra_args=['--wipe', f'-D{option}=1'], allow_fail=True) - self.assertIn(f'ERROR: Unknown options: "{option}"', out) + out = self.init(testdir, extra_args=['--wipe', '-Dnot_an_option=1'], allow_fail=True) + self.assertIn('ERROR: Unknown options: "not_an_option"', out) + + out = self.init(testdir, extra_args=['--wipe', '-Db_not_an_option=1']) + self.assertIn('WARNING: The following command line option(s) were not used: b_not_an_option', out) + def test_configure_new_option(self) -> None: """Adding a new option without reconfiguring should work.""" From 8389be04fe427c66634de7da73aaf8e572baa785 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 22 Feb 2025 14:40:08 +0200 Subject: [PATCH 289/624] Fix yielding when top project does not define the option. Closes #14281. --- mesonbuild/options.py | 8 +++++++- unittests/optiontests.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 838ccb794d1e..8cad1d1ca429 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -810,7 +810,13 @@ def get_value_object_for(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType: assert key.subproject is not None if potential is not None and potential.yielding: parent_key = key.evolve(subproject='') - parent_option = self.options[parent_key] + 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): diff --git a/unittests/optiontests.py b/unittests/optiontests.py index bbf9c0e054b9..94d52ae2ede3 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -100,6 +100,22 @@ def test_project_yielding(self): self.assertEqual(optstore.get_value_for(name, 'sub'), top_value) self.assertEqual(optstore.num_options(), 2) + def test_project_yielding_not_defined_in_top_project(self): + optstore = OptionStore(False) + top_name = 'a_name' + top_value = 'top' + sub_name = 'different_name' + sub_value = 'sub' + vo = UserStringOption(top_name, 'A top level option', top_value) + optstore.add_project_option(OptionKey(top_name, ''), vo) + self.assertEqual(optstore.get_value_for(top_name, ''), top_value) + self.assertEqual(optstore.num_options(), 1) + vo2 = UserStringOption(sub_name, 'A subproject option', sub_value, True) + optstore.add_project_option(OptionKey(sub_name, 'sub'), vo2) + self.assertEqual(optstore.get_value_for(top_name, ''), top_value) + self.assertEqual(optstore.get_value_for(sub_name, 'sub'), sub_value) + self.assertEqual(optstore.num_options(), 2) + def test_augments(self): optstore = OptionStore(False) name = 'cpp_std' From f44689645ebfea6ec2a5cc27f5755c557019185a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 24 Feb 2025 13:45:49 +0200 Subject: [PATCH 290/624] Use override value when setting up Cython language. Closes #14284. --- mesonbuild/build.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 3bc7d964122e..55e829fdea11 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -980,8 +980,14 @@ def process_compilers(self) -> T.List[str]: if 'vala' in self.compilers and 'c' not in self.compilers: self.compilers['c'] = self.all_compilers['c'] if 'cython' in self.compilers: - key = OptionKey('cython_language', machine=self.for_machine) - value = self.environment.coredata.optstore.get_value_for(key) + # Not great, but we can't ask for the override value from "the system" + # because this object is currently being constructed so it is not + # yet placed in the data store. Grab it directly from override strings + # instead. + value = self.get_override('cython_language') + if value is None: + key = OptionKey('cython_language', machine=self.for_machine) + value = self.environment.coredata.optstore.get_value_for(key) try: self.compilers[value] = self.all_compilers[value] except KeyError: From 97db0bd0937eb978131aa18155522c0b3f6da3ff Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Thu, 20 Feb 2025 23:26:07 -0100 Subject: [PATCH 291/624] Add a new test for `override_options: ['cython_language': 'cpp'])` --- test cases/cython/4 override_options/foo_cpp.pyx | 2 ++ test cases/cython/4 override_options/meson.build | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test cases/cython/4 override_options/foo_cpp.pyx create mode 100644 test cases/cython/4 override_options/meson.build diff --git a/test cases/cython/4 override_options/foo_cpp.pyx b/test cases/cython/4 override_options/foo_cpp.pyx new file mode 100644 index 000000000000..01ff64d97d3d --- /dev/null +++ b/test cases/cython/4 override_options/foo_cpp.pyx @@ -0,0 +1,2 @@ +def the_answer(): + return 43 diff --git a/test cases/cython/4 override_options/meson.build b/test cases/cython/4 override_options/meson.build new file mode 100644 index 000000000000..896993c0be12 --- /dev/null +++ b/test cases/cython/4 override_options/meson.build @@ -0,0 +1,11 @@ +project('my project', 'cython', 'cpp', + default_options : ['buildtype=release']) + +py = import('python').find_installation(pure: false) + +py.extension_module( + 'foo', + 'foo_cpp.pyx', + override_options : ['cython_language=cpp'], +) + From 910bfb997b2fc7d2d4840fb911beae018bf13909 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 24 Feb 2025 14:00:20 +0200 Subject: [PATCH 292/624] Remove unused variable left over from option refactor. --- mesonbuild/build.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 55e829fdea11..8d3d4052d201 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict, deque, OrderedDict -from dataclasses import dataclass, field, InitVar +from dataclasses import dataclass, field from functools import lru_cache import abc import copy @@ -521,7 +521,6 @@ class Target(HoldableObject, metaclass=abc.ABCMeta): install: bool = False build_always_stale: bool = False extra_files: T.List[File] = field(default_factory=list) - override_options: InitVar[T.Optional[T.Dict[OptionKey, str]]] = None @abc.abstractproperty def typename(self) -> str: @@ -531,7 +530,7 @@ def typename(self) -> str: def type_suffix(self) -> str: pass - def __post_init__(self, overrides: T.Optional[T.Dict[OptionKey, str]]) -> None: + def __post_init__(self) -> None: # XXX: this should happen in the interpreter if has_path_sep(self.name): # Fix failing test 53 when this becomes an error. From 5bbd030290db1408ab1d7025072750f086642f85 Mon Sep 17 00:00:00 2001 From: Eniek <121483853+E-niek@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:54:11 +0100 Subject: [PATCH 293/624] Tutorial.md typo fix --- docs/markdown/Tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Tutorial.md b/docs/markdown/Tutorial.md index 7aff164a266a..355dd125a1a0 100644 --- a/docs/markdown/Tutorial.md +++ b/docs/markdown/Tutorial.md @@ -120,7 +120,7 @@ use GTK+. The new version looks like this. #include // -// Should provided the active view for a GTK application +// Should provide the active view for a GTK application // static void activate(GtkApplication* app, gpointer user_data) { From 1412abc516e56b7d90f36fc75acb3e4b8c294e30 Mon Sep 17 00:00:00 2001 From: Tom Evers Date: Thu, 27 Feb 2025 11:44:28 +0100 Subject: [PATCH 294/624] Fixes issue 14089 (detection of Intel compiler) --- mesonbuild/compilers/c.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 4f2bd2fae27f..85eea2f1c9c9 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -497,10 +497,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() key = self.form_compileropt_key('std') - # To shut up mypy. - if isinstance(opts, dict): - raise RuntimeError('This is a transitory issue that should not happen. Please report with full backtrace.') - std_opt = opts.get_value_object(key) + std_opt = opts[key] assert isinstance(std_opt, options.UserStdOption), 'for mypy' std_opt.set_versions(['c89', 'c99', 'c11']) return opts From d3c863997513be81c0c52ef209a57360b771d9e1 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 27 Feb 2025 08:21:13 +0100 Subject: [PATCH 295/624] mtest: add option to slice tests Executing tests can take a very long time. As an example, the Git test suite on Windows takes around 4 hours to execute. The Git project has been working around the issue by splitting up CI jobs into multiple slices: one job creates the build artifacts, and then we spawn N test jobs with those artifacts, where each test job executes 1/Nth of the tests. This can be scripted rather easily by using `meson test --list`, selecting every Nth line, but there may be other projects that have a similar need. Wire up a new option "--slice i/n" to `meson test` that does implements this logic. Signed-off-by: Patrick Steinhardt --- docs/markdown/Unit-tests.md | 5 ++++ docs/markdown/snippets/test-slicing.md | 6 +++++ man/meson.1 | 5 +++- mesonbuild/mtest.py | 31 +++++++++++++++++++++- test cases/unit/124 test slice/meson.build | 12 +++++++++ test cases/unit/124 test slice/test.py | 0 unittests/allplatformstests.py | 29 ++++++++++++++++++++ 7 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/test-slicing.md create mode 100644 test cases/unit/124 test slice/meson.build create mode 100644 test cases/unit/124 test slice/test.py diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 13f6093f2e7b..5a7b19940b39 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -206,6 +206,11 @@ name(s), the test name(s) must be contained in the suite(s). This however is redundant-- it would be more useful to specify either specific test names or suite(s). +Since version *1.8.0*, you can pass `--slice i/n` to split up the set of tests +into `n` slices and execute the `ith` such slice. This allows you to distribute +a set of long-running tests across multiple machines to decrease the overall +runtime of tests. + ### Other test options Sometimes you need to run the tests multiple times, which is done like this: diff --git a/docs/markdown/snippets/test-slicing.md b/docs/markdown/snippets/test-slicing.md new file mode 100644 index 000000000000..180b9ace513d --- /dev/null +++ b/docs/markdown/snippets/test-slicing.md @@ -0,0 +1,6 @@ +## New option to execute a slice of tests + +When tests take a long time to run a common strategy is to slice up the tests +into multiple sets, where each set is executed on a separate machine. You can +now use the `--slice i/n` argument for `meson test` to create `n` slices and +execute the `ith` slice. diff --git a/man/meson.1 b/man/meson.1 index 01b9abd8901e..deb320dbc7f6 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -328,6 +328,9 @@ a multiplier to use for test timeout values (usually something like 100 for Valg .TP \fB\-\-setup\fR use the specified test setup +.Tp +\fB\-\-slice SLICE/NUM_SLICES\fR +Split tests into NUM_SLICES slices and execute slice number SLICE. (Since 1.8.0) .SH The wrap command @@ -410,7 +413,7 @@ Manage the packagefiles overlay .B meson rewrite modifies the project definition. - + .B meson rewrite [ .I options .B ] [ diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index bb58f617cca5..58cfef30ec83 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -99,6 +99,29 @@ def uniwidth(s: str) -> int: result += UNIWIDTH_MAPPING[w] return result +def test_slice(arg: str) -> T.Tuple[int, int]: + values = arg.split('/') + if len(values) != 2: + raise argparse.ArgumentTypeError("value does not conform to format 'SLICE/NUM_SLICES'") + + try: + nrslices = int(values[1]) + except ValueError: + raise argparse.ArgumentTypeError('NUM_SLICES is not an integer') + if nrslices <= 0: + raise argparse.ArgumentTypeError('NUM_SLICES is not a positive integer') + + try: + subslice = int(values[0]) + except ValueError: + raise argparse.ArgumentTypeError('SLICE is not an integer') + if subslice <= 0: + raise argparse.ArgumentTypeError('SLICE is not a positive integer') + if subslice > nrslices: + raise argparse.ArgumentTypeError('SLICE exceeds NUM_SLICES') + + return subslice, nrslices + # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ def add_arguments(parser: argparse.ArgumentParser) -> None: @@ -149,12 +172,13 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Arguments to pass to the specified test(s) or all tests') parser.add_argument('--max-lines', default=100, dest='max_lines', type=int, help='Maximum number of lines to show from a long test log. Since 1.5.0.') + parser.add_argument('--slice', default=None, type=test_slice, metavar='SLICE/NUM_SLICES', + help='Split tests into NUM_SLICES slices and execute slice SLICE. Since 1.8.0.') parser.add_argument('args', nargs='*', help='Optional list of test names to run. "testname" to run all tests with that name, ' '"subprojname:testname" to specifically run "testname" from "subprojname", ' '"subprojname:" to run all tests defined by "subprojname".') - def print_safe(s: str) -> None: end = '' if s[-1] == '\n' else '\n' try: @@ -1977,6 +2001,11 @@ def get_tests(self, errorfile: T.Optional[T.IO] = None) -> T.List[TestSerialisat tests = [t for t in self.tests if self.test_suitable(t)] if self.options.args: tests = list(self.tests_from_args(tests)) + if self.options.slice: + our_slice, nslices = self.options.slice + if nslices > len(tests): + raise MesonException(f'number of slices ({nslices}) exceeds number of tests ({len(tests)})') + tests = tests[our_slice - 1::nslices] if not tests: print('No suitable tests defined.', file=errorfile) diff --git a/test cases/unit/124 test slice/meson.build b/test cases/unit/124 test slice/meson.build new file mode 100644 index 000000000000..a41c2f62d7ff --- /dev/null +++ b/test cases/unit/124 test slice/meson.build @@ -0,0 +1,12 @@ +project('test_slice') + +python = import('python').find_installation('python3') + +foreach i : range(10) + test('test-' + (i + 1).to_string(), + python, + args: [ + meson.current_source_dir() / 'test.py' + ], + ) +endforeach diff --git a/test cases/unit/124 test slice/test.py b/test cases/unit/124 test slice/test.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 634a860addde..8bbe7c644108 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5130,6 +5130,35 @@ def test_objc_objcpp_stds(self) -> None: def test_c_cpp_objc_objcpp_stds(self) -> None: self.__test_multi_stds(test_objc=True) + def test_slice(self): + testdir = os.path.join(self.unit_test_dir, '124 test slice') + self.init(testdir) + self.build() + + for arg, expectation in {'1/1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + '1/2': [1, 3, 5, 7, 9], + '2/2': [2, 4, 6, 8, 10], + '1/10': [1], + '2/10': [2], + '10/10': [10], + }.items(): + output = self._run(self.mtest_command + ['--slice=' + arg]) + tests = sorted([ int(x[5:]) for x in re.findall(r'test-[0-9]*', output) ]) + self.assertEqual(tests, expectation) + + for arg, expectation in {'': 'error: argument --slice: value does not conform to format \'SLICE/NUM_SLICES\'', + '0': 'error: argument --slice: value does not conform to format \'SLICE/NUM_SLICES\'', + '0/1': 'error: argument --slice: SLICE is not a positive integer', + 'a/1': 'error: argument --slice: SLICE is not an integer', + '1/0': 'error: argument --slice: NUM_SLICES is not a positive integer', + '1/a': 'error: argument --slice: NUM_SLICES is not an integer', + '2/1': 'error: argument --slice: SLICE exceeds NUM_SLICES', + '1/11': 'ERROR: number of slices (11) exceeds number of tests (10)', + }.items(): + with self.assertRaises(subprocess.CalledProcessError) as cm: + self._run(self.mtest_command + ['--slice=' + arg]) + self.assertIn(expectation, cm.exception.output) + def test_rsp_support(self): env = get_fake_env() cc = detect_c_compiler(env, MachineChoice.HOST) From 23a9a25779db59b213a3dd546fb77625ede95841 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jan 2025 11:41:36 +0100 Subject: [PATCH 296/624] mtest: filter summary lines without any results After a test run finishes we print a summary that sums up test counts by type, e.g. failed or skipped tests. In many cases though it is expected that most of the counts will be zero, and thus the summary is needlessly cluttered with irrelevant lines. This list of mostly-irrelevant results will grow in a subsequent commit where we introduce "Ignored" test results. Prepare for this by filtering results. Instead of unconditionally printing every result, we will now only print those results where we have at least one counted test. The exception is "Ok:" and "Fail:", which will always be printed. Suggested-by: Paolo Bonzini Signed-off-by: Patrick Steinhardt --- mesonbuild/mtest.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 58cfef30ec83..cd5c5f100276 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -23,7 +23,6 @@ import subprocess import shlex import sys -import textwrap import time import typing as T import unicodedata @@ -1832,15 +1831,21 @@ def format(self, result: TestRun, colorize: bool, return prefix + left + middle + right def summary(self) -> str: - return textwrap.dedent(''' - Ok: {:<4} - Expected Fail: {:<4} - Fail: {:<4} - Unexpected Pass: {:<4} - Skipped: {:<4} - Timeout: {:<4} - ''').format(self.success_count, self.expectedfail_count, self.fail_count, - self.unexpectedpass_count, self.skip_count, self.timeout_count) + results = { + 'Ok: ': self.success_count, + 'Expected Fail: ': self.expectedfail_count, + 'Fail: ': self.fail_count, + 'Unexpected Pass: ': self.unexpectedpass_count, + 'Skipped: ': self.skip_count, + 'Timeout: ': self.timeout_count, + } + + summary = [] + for result, count in results.items(): + if count > 0 or result.startswith('Ok:') or result.startswith('Fail:'): + summary.append(result + '{:<4}'.format(count)) + + return '\n{}\n'.format('\n'.join(summary)) def total_failure_count(self) -> int: return self.fail_count + self.unexpectedpass_count + self.timeout_count From 4526a75e25c8f4e908e6c142cf6658bac0acb5e3 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jan 2025 11:42:17 +0100 Subject: [PATCH 297/624] mtest: move `console_mode` property into TestRun class Move the `console_mode` property into the TestRun Class. This will be required by a subsequent commit where we start to ignore test results for parsed interactive tests. Signed-off-by: Patrick Steinhardt --- mesonbuild/mtest.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index cd5c5f100276..8e490d5ff883 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -948,7 +948,8 @@ def __new__(cls, test: TestSerialisation, *args: T.Any, **kwargs: T.Any) -> T.An return super().__new__(TestRun.PROTOCOL_TO_CLASS[test.protocol]) def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str], - name: str, timeout: T.Optional[int], is_parallel: bool, verbose: bool): + name: str, timeout: T.Optional[int], is_parallel: bool, verbose: bool, + interactive: bool): self.res = TestResult.PENDING self.test = test self._num: T.Optional[int] = None @@ -968,6 +969,7 @@ def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str], self.junit: T.Optional[et.ElementTree] = None self.is_parallel = is_parallel self.verbose = verbose + self.interactive = interactive self.warnings: T.List[str] = [] def start(self, cmd: T.List[str]) -> None: @@ -982,6 +984,15 @@ def num(self) -> int: self._num = TestRun.TEST_NUM return self._num + @property + def console_mode(self) -> ConsoleUser: + if self.interactive: + return ConsoleUser.INTERACTIVE + elif self.direct_stdout: + return ConsoleUser.STDOUT + else: + return ConsoleUser.LOGGER + @property def direct_stdout(self) -> bool: return self.verbose and not self.is_parallel and not self.needs_parsing @@ -1474,14 +1485,11 @@ def __init__(self, test: TestSerialisation, env: T.Dict[str, str], name: str, is_parallel = test.is_parallel and self.options.num_processes > 1 and not self.options.interactive verbose = (test.verbose or self.options.verbose) and not self.options.quiet - self.runobj = TestRun(test, env, name, timeout, is_parallel, verbose) + self.runobj = TestRun(test, env, name, timeout, is_parallel, verbose, self.options.interactive) - if self.options.interactive: - self.console_mode = ConsoleUser.INTERACTIVE - elif self.runobj.direct_stdout: - self.console_mode = ConsoleUser.STDOUT - else: - self.console_mode = ConsoleUser.LOGGER + @property + def console_mode(self) -> ConsoleUser: + return self.runobj.console_mode def _get_test_cmd(self) -> T.Optional[T.List[str]]: testentry = self.test.fname[0] From 7fd17ab12854ff5339f3309156cd9b705f517fd0 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Jan 2025 11:42:34 +0100 Subject: [PATCH 298/624] mtest: introduce ignored tests When running tests in interactive mode then the standard file streams will remain connected to the executing terminal so that the user can interact with the tests. This has the consequence that Meson itself does not have access to those streams anymore, which is problematic for any of the test types that require parsing, like for example with the TAP protocol. This means that Meson is essentially flying blind in those cases because the test result cannot be determined by parsing the exit code of the test, but can only reliably be derived from the parsed output. One obvious solution to this problem would be to splice the test output so that both Meson and the user's terminal have access to it. But when running in interactive mode it is quite likely that the test itself will actually be driven from the command line, and the chance is high that the resulting data on stdout cannot be parsed as properly anymore. This is for example the case in the Git project, where interactive mode is typically used to drop the user into a shell or invoke a debugger. So continuing to treat the output as properly formatted output that can be parsed is likely a dead end in many use cases. Instead, we introduce a new "IGNORED" test result: when executing tests in interactive mode, and when the test type indicates that it requires parsing, we will not try to parse the test at all but mark the test result as ignored instead. Suggested-by: Paolo Bonzini Signed-off-by: Patrick Steinhardt --- mesonbuild/mtest.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 8e490d5ff883..5a5d257582a5 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -266,6 +266,7 @@ class TestResult(enum.Enum): EXPECTEDFAIL = 'EXPECTEDFAIL' UNEXPECTEDPASS = 'UNEXPECTEDPASS' ERROR = 'ERROR' + IGNORED = 'IGNORED' @staticmethod def maxlen() -> int: @@ -287,7 +288,7 @@ def was_killed(self) -> bool: def colorize(self, s: str) -> mlog.AnsiDecorator: if self.is_bad(): decorator = mlog.red - elif self in (TestResult.SKIP, TestResult.EXPECTEDFAIL): + elif self in (TestResult.SKIP, TestResult.IGNORED, TestResult.EXPECTEDFAIL): decorator = mlog.yellow elif self.is_finished(): decorator = mlog.green @@ -857,7 +858,8 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: {TestResult.INTERRUPT, TestResult.ERROR})), failures=str(sum(1 for r in test.results if r.result in {TestResult.FAIL, TestResult.UNEXPECTEDPASS, TestResult.TIMEOUT})), - skipped=str(sum(1 for r in test.results if r.result is TestResult.SKIP)), + skipped=str(sum(1 for r in test.results if r.result in + {TestResult.SKIP, TestResult.IGNORED})), time=str(test.duration), ) @@ -867,6 +869,9 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: testcase = et.SubElement(suite, 'testcase', name=str(subtest), classname=suitename) if subtest.result is TestResult.SKIP: et.SubElement(testcase, 'skipped') + elif subtest.result is TestResult.IGNORED: + skip = et.SubElement(testcase, 'skipped') + skip.text = 'Test output was not parsed.' elif subtest.result is TestResult.ERROR: et.SubElement(testcase, 'error') elif subtest.result is TestResult.FAIL: @@ -902,6 +907,10 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: if test.res is TestResult.SKIP: et.SubElement(testcase, 'skipped') suite.attrib['skipped'] = str(int(suite.attrib['skipped']) + 1) + elif test.res is TestResult.IGNORED: + skip = et.SubElement(testcase, 'skipped') + skip.text = 'Test output was not parsed.' + suite.attrib['skipped'] = str(int(suite.attrib['skipped']) + 1) elif test.res is TestResult.ERROR: et.SubElement(testcase, 'error') suite.attrib['errors'] = str(int(suite.attrib['errors']) + 1) @@ -1001,7 +1010,7 @@ def get_results(self) -> str: if self.results: # running or succeeded passed = sum(x.result.is_ok() for x in self.results) - ran = sum(x.result is not TestResult.SKIP for x in self.results) + ran = sum(x.result not in {TestResult.SKIP, TestResult.IGNORED} for x in self.results) if passed == ran: return f'{passed} subtests passed' else: @@ -1021,6 +1030,8 @@ def get_details(self) -> str: def _complete(self) -> None: if self.res == TestResult.RUNNING: self.res = TestResult.OK + if self.needs_parsing and self.console_mode is ConsoleUser.INTERACTIVE: + self.res = TestResult.IGNORED assert isinstance(self.res, TestResult) if self.should_fail and self.res in (TestResult.OK, TestResult.FAIL): self.res = TestResult.UNEXPECTEDPASS if self.res is TestResult.OK else TestResult.EXPECTEDFAIL @@ -1638,6 +1649,7 @@ def __init__(self, options: argparse.Namespace): self.unexpectedpass_count = 0 self.success_count = 0 self.skip_count = 0 + self.ignored_count = 0 self.timeout_count = 0 self.test_count = 0 self.name_max_len = 0 @@ -1781,6 +1793,8 @@ def process_test_result(self, result: TestRun) -> None: self.timeout_count += 1 elif result.res is TestResult.SKIP: self.skip_count += 1 + elif result.res is TestResult.IGNORED: + self.ignored_count += 1 elif result.res is TestResult.OK: self.success_count += 1 elif result.res in {TestResult.FAIL, TestResult.ERROR, TestResult.INTERRUPT}: @@ -1845,6 +1859,7 @@ def summary(self) -> str: 'Fail: ': self.fail_count, 'Unexpected Pass: ': self.unexpectedpass_count, 'Skipped: ': self.skip_count, + 'Ignored: ': self.ignored_count, 'Timeout: ': self.timeout_count, } From de7d8f9e48b7801659c15f03489d1b5a2fbe7d50 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 27 Feb 2025 08:23:20 +0100 Subject: [PATCH 299/624] test: fix hang when running tests that need parsing with `--interactive` When running tests with `--interactive` we don't redirect stdin, stdout or stderr and instead pass them on to the user's console. This redirect causes us to hang in case the test in question needs parsing, like it is the case for TAP output, because we cannot read the process's stdout. Fix this hang by not parsing output when running in interactive mode. Signed-off-by: Patrick Steinhardt --- mesonbuild/mtest.py | 2 +- test cases/unit/124 interactive tap/meson.build | 4 ++++ test cases/unit/124 interactive tap/script.py | 5 +++++ unittests/allplatformstests.py | 8 ++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test cases/unit/124 interactive tap/meson.build create mode 100755 test cases/unit/124 interactive tap/script.py diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 5a5d257582a5..673f433fd103 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1621,7 +1621,7 @@ async def _run_cmd(self, harness: 'TestHarness', cmd: T.List[str]) -> None: env=self.runobj.env, cwd=self.test.workdir) - if self.runobj.needs_parsing: + if self.runobj.needs_parsing and self.console_mode is not ConsoleUser.INTERACTIVE: parse_coro = self.runobj.parse(harness, p.stdout_lines()) parse_task = asyncio.ensure_future(parse_coro) else: diff --git a/test cases/unit/124 interactive tap/meson.build b/test cases/unit/124 interactive tap/meson.build new file mode 100644 index 000000000000..30518db6f403 --- /dev/null +++ b/test cases/unit/124 interactive tap/meson.build @@ -0,0 +1,4 @@ +project('interactive TAP output') + +test_script = find_program('script.py') +test('main', test_script, protocol: 'tap') diff --git a/test cases/unit/124 interactive tap/script.py b/test cases/unit/124 interactive tap/script.py new file mode 100755 index 000000000000..873a4ae8168b --- /dev/null +++ b/test cases/unit/124 interactive tap/script.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +print('''1..2 +ok 1 +not ok 2''') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 8bbe7c644108..57aa6315d214 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5177,3 +5177,11 @@ def test_wipe_with_args(self): testdir = os.path.join(self.common_test_dir, '1 trivial') self.init(testdir, extra_args=['-Dc_args=-DSOMETHING']) self.init(testdir, extra_args=['--wipe']) + + def test_interactive_tap(self): + testdir = os.path.join(self.unit_test_dir, '124 interactive tap') + self.init(testdir, extra_args=['--wrap-mode=forcefallback']) + output = self._run(self.mtest_command + ['--interactive']) + self.assertRegex(output, r'Ok:\s*0') + self.assertRegex(output, r'Fail:\s*0') + self.assertRegex(output, r'Ignored:\s*1') From 95dd7499f32742011523ec763be53eefa221f883 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 09:23:12 -0800 Subject: [PATCH 300/624] modules/rust: Update bindgen target error checking for bindgen >= 0.71 Which has both changed the error message and relaxed the check. In theory we wouldn't hit this unless you have a very old bindgen and a very new rustc. --- mesonbuild/modules/rust.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 3638964f28ef..34a3f68d8aed 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2020-2024 Intel Corporation +# Copyright © 2020-2025 Intel Corporation from __future__ import annotations import itertools @@ -259,9 +259,11 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu _, _, err = mesonlib.Popen_safe( T.cast('T.List[str]', self._bindgen_bin.get_command()) + ['--rust-target', self._bindgen_rust_target]) - # Sometimes this is "invalid Rust target" and sometimes "invalid - # rust target" - if 'Got an invalid' in err: + # < 0.71: Sometimes this is "invalid Rust target" and + # sometimes "invalid # rust target" + # >= 0.71: error: invalid value '...' for '--rust-target ': "..." is not a valid Rust target, accepted values are of the form ... + # It's also much harder to hit this in 0.71 than in previous versions + if 'Got an invalid' in err or 'is not a valid Rust target' in err: self._bindgen_rust_target = None name: str From a7d248d58054b763fd6114d73a0587565a0d1646 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 09:48:52 -0800 Subject: [PATCH 301/624] modules/rust: set --rust-edition for bindgen >= 0.71 But only if the user hasn't manually set it. --- mesonbuild/modules/rust.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 34a3f68d8aed..0891243eb7c7 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -66,6 +66,7 @@ def __init__(self, interpreter: Interpreter) -> None: self._bindgen_rust_target: T.Optional[str] = interpreter.compilers.host['rust'].version else: self._bindgen_rust_target = None + self._bindgen_set_std = False self.methods.update({ 'test': self.test, 'bindgen': self.bindgen, @@ -266,6 +267,10 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu if 'Got an invalid' in err or 'is not a valid Rust target' in err: self._bindgen_rust_target = None + # TODO: Executable needs to learn about get_version + if isinstance(self._bindgen_bin, ExternalProgram): + self._bindgen_set_std = mesonlib.version_compare(self._bindgen_bin.get_version(), '>= 0.71') + name: str if isinstance(header, File): name = header.fname @@ -335,6 +340,11 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu kwargs['args'] + inline_wrapper_args 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') + assert isinstance(rust_std, str), 'for mypy' + if rust_std != 'none': + cmd.extend(['--rust-edition', rust_std]) cmd.append('--') cmd.extend(kwargs['c_args']) cmd.extend(clang_args) From 5d648a112fe8ee3bfb637d9db133c05ef3b184b8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 09:34:44 -0800 Subject: [PATCH 302/624] compilers/detect: Split -beta and -nightly suffixes from rustc Store both a full version with the nightly and beta suffixes, and the version as just X.Y.Z. This sort of distinction is why full_version exists. This fixes an issue with the bindgen module where we pass invalid version of X.Y.Z-beta and X.Y.Z-nightly. --- mesonbuild/compilers/detect.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index d4ad4badeefd..784e00d3dc76 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1047,7 +1047,11 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust popen_exceptions[join_args(compiler + arg)] = e continue - version = search_version(out) + # Full version contains the "-nightly" or "-beta" suffixes, but version + # should just be X.Y.Z + full_version = search_version(out) + version = full_version.split('-', 1)[0] + cls: T.Type[RustCompiler] = rust.RustCompiler # Clippy is a wrapper around rustc, but it doesn't have rustc in its @@ -1063,7 +1067,8 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust except OSError as e: popen_exceptions[join_args(compiler + arg)] = e continue - version = search_version(out) + full_version = search_version(out) + version = full_version.split('-', 1)[0] cls = rust.ClippyRustCompiler mlog.deprecation( @@ -1143,7 +1148,7 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust env.coredata.add_lang_args(cls.language, cls, for_machine, env) return cls( compiler, version, for_machine, is_cross, info, - linker=linker) + linker=linker, full_version=full_version) _handle_exceptions(popen_exceptions, compilers) raise EnvironmentException('Unreachable code (exception to make mypy happy)') From 8ebc8c1878852f914172b99135d2960429402204 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 09:39:24 -0800 Subject: [PATCH 303/624] modules/rust: use 'nightly' as the bindgen version if the compiler is nightly This adds tracking for both nightly and beta to the rust compiler. --- mesonbuild/compilers/rust.py | 2 ++ mesonbuild/modules/rust.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 3acc30e5458c..475b7a402aa8 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -100,6 +100,8 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic if 'link' in self.linker.id: 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 def needs_static_linker(self) -> bool: return False diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 0891243eb7c7..c455ea227996 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -24,6 +24,7 @@ if T.TYPE_CHECKING: from . import ModuleState from ..build import IncludeDirs, LibTypes + from ..compilers.rust import RustCompiler from ..dependencies import Dependency, ExternalLibrary from ..interpreter import Interpreter from ..interpreter import kwargs as _kwargs @@ -58,12 +59,14 @@ class RustModule(ExtensionModule): """A module that holds helper functions for rust.""" INFO = ModuleInfo('rust', '0.57.0', stabilized='1.0.0') + _bindgen_rust_target: T.Optional[str] def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None if 'rust' in interpreter.compilers.host: - self._bindgen_rust_target: T.Optional[str] = interpreter.compilers.host['rust'].version + rustc = T.cast('RustCompiler', interpreter.compilers.host['rust']) + self._bindgen_rust_target = 'nightly' if rustc.is_nightly else rustc.version else: self._bindgen_rust_target = None self._bindgen_set_std = False From 25f7e33a22967af8c8f91c46029a8fd4d5d78e78 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 18 Feb 2025 13:37:04 +0200 Subject: [PATCH 304/624] Maintain bw compatibility for requesting bad options. Closes: #14255. --- mesonbuild/interpreter/interpreter.py | 11 ++++++++--- mesonbuild/options.py | 8 ++++++++ unittests/optiontests.py | 5 +++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9c37ffb256d8..0b16266f7a2e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1083,9 +1083,14 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], try: value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject)) except KeyError: - if self.subproject: - raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.') - raise MesonException(f'Option {optname} does not exist.') + if self.coredata.optstore.is_base_option(optname): + # Due to backwards compatibility return the default + # option for base options instead of erroring out. + value_object, value = self.get_default_for_b_option(optname) + 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) ocopy.name = optname diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 8cad1d1ca429..8d4f22fdcf61 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1107,6 +1107,14 @@ def get_option_from_meson_file(self, key: OptionKey) -> 'T.Tuple[AnyOptionType, (value_object, value) = self.get_value_object_and_value_for(key) return (value_object, value) + def get_default_for_b_option(self, keyname: str) -> T.Tuple[AnyOptionType, OptionValueType]: + assert keyname.startswith('b_') + from .compilers.compilers import BASE_OPTIONS + for bkey, bvalue in BASE_OPTIONS.items(): + if bkey.name == keyname: + return (T.cast('AnyOptionType', bvalue), bvalue.default) + raise MesonBugException(f'Requested base option {keyname} which does not exist.') + def remove(self, key: OptionKey) -> None: del self.options[key] try: diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 94d52ae2ede3..845eba0b9636 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -198,3 +198,8 @@ def test_subproject_call_options(self): optstore.set_subproject_options(subproject, [f'cpp_std={unused_value}'], [f'cpp_std={unused_value}']) self.assertEqual(optstore.get_value_for(name), default_value) self.assertEqual(optstore.get_value_for(name, subproject), override_value) + + def test_b_default(self): + optstore = OptionStore(False) + _, value = optstore.get_default_for_b_option('b_vscrt') + self.assertEqual(value, 'from_buildtype') From 9300c3af91373b129deaa0255a9eb2f1ab2b309e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 10:41:40 -0800 Subject: [PATCH 305/624] options: replace OptionValueType with ElementaryOptionValue --- mesonbuild/options.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 8d4f22fdcf61..2e847a6435a4 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -50,8 +50,6 @@ class ArgparseKWs(TypedDict, total=False): default: str choices: T.List - OptionValueType: TypeAlias = T.Union[str, int, bool, T.List[str]] - DEFAULT_YIELDING = False # Can't bind this near the class method it seems, sadly. @@ -797,7 +795,7 @@ def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey: key = key.evolve(machine=MachineChoice.HOST) return key - def get_value(self, key: T.Union[OptionKey, str]) -> 'OptionValueType': + def get_value(self, key: T.Union[OptionKey, str]) -> ElementaryOptionValues: return self.get_value_object(key).value def __len__(self) -> int: @@ -833,7 +831,7 @@ def get_value_object_for(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType: return self.options[parent_key] return potential - def get_value_object_and_value_for(self, key: OptionKey) -> 'T.Tuple[AnyOptionType, OptionValueType]': + def get_value_object_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 @@ -843,7 +841,7 @@ def get_value_object_and_value_for(self, key: OptionKey) -> 'T.Tuple[AnyOptionTy computed_value = vobject.validate_value(self.augments[keystr]) return (vobject, computed_value) - def get_value_for(self, name: 'T.Union[OptionKey, str]', subproject: T.Optional[str] = None) -> 'OptionValueType': + def get_value_for(self, name: 'T.Union[OptionKey, str]', subproject: T.Optional[str] = None) -> ElementaryOptionValues: if isinstance(name, str): key = OptionKey(name, subproject) else: @@ -1102,7 +1100,7 @@ def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: key = self.ensure_and_validate_key(key) return self.options[key] - def get_option_from_meson_file(self, key: OptionKey) -> 'T.Tuple[AnyOptionType, OptionValueType]': + def get_option_from_meson_file(self, key: OptionKey) -> T.Tuple[AnyOptionType, ElementaryOptionValues]: assert isinstance(key, OptionKey) (value_object, value) = self.get_value_object_and_value_for(key) return (value_object, value) From d9f3f6c0b27476edc84ccddecb373af88fde1053 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 12:47:48 -0800 Subject: [PATCH 306/624] use ElementaryOptionValues instead of open coding... again --- mesonbuild/backend/backends.py | 3 ++- mesonbuild/compilers/compilers.py | 2 +- mesonbuild/modules/__init__.py | 3 ++- mesonbuild/options.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index baf83e84968c..88b3357be57d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -41,6 +41,7 @@ from ..interpreter import Interpreter, Test from ..linkers.linkers import StaticLinker from ..mesonlib import FileMode, FileOrString + from ..options import ElementaryOptionValues from typing_extensions import TypedDict, NotRequired @@ -2105,7 +2106,7 @@ def is_unity(self, target: build.BuildTarget) -> bool: return target.subproject != '' raise MesonException(f'Internal error: invalid option type for "unity": {val}') - def get_target_option(self, target: build.BuildTarget, name: T.Union[str, OptionKey]) -> T.Union[str, int, bool, T.List[str]]: + def get_target_option(self, target: build.BuildTarget, name: T.Union[str, OptionKey]) -> ElementaryOptionValues: if isinstance(name, str): key = OptionKey(name, subproject=target.subproject) elif isinstance(name, OptionKey): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index e4c7f77441a2..7e061ba471aa 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1419,7 +1419,7 @@ def get_compileropt_value(self, env: Environment, target: T.Optional[BuildTarget], subproject: T.Optional[str] = None - ) -> T.Union[str, int, bool, T.List[str]]: + ) -> options.ElementaryOptionValues: if isinstance(key, str): key = self.form_compileropt_key(key) if target: diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index f9374cc1d48c..fefe41a44949 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -20,6 +20,7 @@ from ..interpreterbase import TYPE_var, TYPE_kwargs from ..programs import OverrideProgram from ..dependencies import Dependency + from ..options import ElementaryOptionValues class ModuleState: """Object passed to all module methods. @@ -132,7 +133,7 @@ def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'External self._interpreter.func_test(self.current_node, real_args, kwargs) def get_option(self, name: str, subproject: str = '', - machine: MachineChoice = MachineChoice.HOST) -> T.Union[T.List[str], str, int, bool]: + machine: MachineChoice = MachineChoice.HOST) -> ElementaryOptionValues: return self.environment.coredata.get_option(OptionKey(name, subproject, machine)) def is_user_defined_option(self, name: str, subproject: str = '', diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 2e847a6435a4..4295f476b10d 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -288,7 +288,7 @@ def __post_init__(self, value_: _T) -> None: def listify(self, value: T.Any) -> T.List[T.Any]: return [value] - def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bool]]]: + def printable_value(self) -> ElementaryOptionValues: assert isinstance(self.value, (str, int, bool, list)) return self.value From 3f430886dc29f2f80ca1556b9e0518f2a182f988 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 10:52:39 -0800 Subject: [PATCH 307/624] coredata: delete the OptionsView This also makes KeyedOptionDictType obsolete and it's removed --- mesonbuild/compilers/compilers.py | 4 +- mesonbuild/compilers/mixins/islinker.py | 4 +- mesonbuild/coredata.py | 71 ++----------------------- mesonbuild/mconf.py | 6 +-- mesonbuild/mintro.py | 2 +- 5 files changed, 11 insertions(+), 76 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 7e061ba471aa..14f0a330d380 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -26,7 +26,7 @@ if T.TYPE_CHECKING: from .. import coredata from ..build import BuildTarget, DFeatures - from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType + from ..coredata import MutableKeyedOptionDictType from ..envconfig import MachineInfo from ..environment import Environment from ..linkers import RSPFileSyntax @@ -272,7 +272,7 @@ def option_enabled(boptions: T.Set[OptionKey], return False -def get_option_value(options: 'KeyedOptionDictType', opt: OptionKey, fallback: '_T') -> '_T': +def get_option_value(options: options.OptionStore, opt: OptionKey, fallback: '_T') -> '_T': """Get the value of an option, or the fallback value.""" try: v: '_T' = options.get_value(opt) # type: ignore [assignment] diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 6c9daf3fce58..44040a7a2824 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -16,10 +16,10 @@ from ...mesonlib import EnvironmentException, MesonException, is_windows if T.TYPE_CHECKING: - from ...coredata import KeyedOptionDictType from ...environment import Environment from ...compilers.compilers import Compiler from ...build import BuildTarget + from ...options import OptionStore 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 @@ -71,7 +71,7 @@ def get_link_debugfile_args(self, targetfile: str) -> T.List[str]: def get_std_shared_lib_link_args(self) -> T.List[str]: return [] - def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]: + def get_std_shared_module_args(self, options: OptionStore) -> T.List[str]: return self.get_std_shared_lib_link_args() def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f41b5aef2bc1..ef761f54fb06 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -10,8 +10,7 @@ import pickle, os, uuid import sys from itertools import chain -from collections import OrderedDict, abc -import dataclasses +from collections import OrderedDict import textwrap from .mesonlib import ( @@ -60,9 +59,8 @@ class SharedCMDOptions(Protocol): cross_file: T.List[str] native_file: T.List[str] - OptionDictType = T.Union[T.Dict[str, options.AnyOptionType], 'OptionsView'] + OptionDictType = T.Dict[str, options.AnyOptionType] MutableKeyedOptionDictType = T.Dict['OptionKey', options.AnyOptionType] - KeyedOptionDictType = T.Union['options.OptionStore', 'OptionsView'] CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode] # code, args RunCheckCacheKey = T.Tuple[str, T.Tuple[str, ...]] @@ -146,7 +144,7 @@ class DependencyCache: successfully lookup by providing a simple get/put interface. """ - def __init__(self, builtins: 'KeyedOptionDictType', for_machine: MachineChoice): + 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') @@ -900,69 +898,6 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None: args.cmd_line_options[key.name] = value delattr(args, name) -@dataclasses.dataclass -class OptionsView(abc.Mapping): - '''A view on an options dictionary for a given subproject and with overrides. - ''' - - # TODO: the typing here could be made more explicit using a TypeDict from - # python 3.8 or typing_extensions - original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, options.AnyOptionType]'] - subproject: T.Optional[str] = None - overrides: T.Optional[T.Mapping[OptionKey, ElementaryOptionValues]] = dataclasses.field(default_factory=dict) - - def __getitem__(self, key: OptionKey) -> options.UserOption: - # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal(). - # We should try to share the code somehow. - key = key.evolve(subproject=self.subproject) - if not isinstance(self.original_options, options.OptionStore): - # This is only used by CUDA currently. - # This entire class gets removed when option refactor - # is finished. - if '_' in key.name or key.lang is not None: - is_project_option = False - else: - sys.exit(f'FAIL {key}.') - else: - is_project_option = self.original_options.is_project_option(key) - if not is_project_option: - opt = self.original_options.get(key) - if opt is None or opt.yielding: - key2 = key.as_root() - # This hack goes away once wi start using OptionStore - # to hold overrides. - if isinstance(self.original_options, options.OptionStore): - if key2 not in self.original_options: - raise KeyError(f'{key} {key2}') - opt = self.original_options.get_value_object(key2) - else: - opt = self.original_options[key2] - else: - opt = self.original_options[key] - if opt.yielding: - opt = self.original_options.get(key.as_root(), opt) - if self.overrides: - override_value = self.overrides.get(key.as_root()) - if override_value is not None: - opt = copy.copy(opt) - opt.set_value(override_value) - return opt - - def get_value(self, key: T.Union[str, OptionKey]): - if isinstance(key, str): - key = OptionKey(key) - return self[key].value - - def set_value(self, key: T.Union[str, OptionKey], value: ElementaryOptionValues): - if isinstance(key, str): - key = OptionKey(key) - self.overrides[key] = value - - def __iter__(self) -> T.Iterator[OptionKey]: - return iter(self.original_options) - - def __len__(self) -> int: - return len(self.original_options) FORBIDDEN_TARGET_NAMES = frozenset({ 'clean', diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index e486df7c1e32..3fb759bf8da0 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2014-2016 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -190,7 +190,7 @@ def wrap_text(text: LOGLINE, width: int) -> mlog.TV_LoggableList: items = [l[i] if l[i] else ' ' * four_column[i] for i in range(4)] mlog.log(*items) - def split_options_per_subproject(self, options: T.Union[coredata.MutableKeyedOptionDictType, coredata.KeyedOptionDictType]) -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: + def split_options_per_subproject(self, options: T.Union[coredata.MutableKeyedOptionDictType, options.OptionStore]) -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: result: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = {} for k, o in options.items(): if k.subproject: @@ -228,7 +228,7 @@ def add_section(self, section: str) -> None: self._add_line(mlog.normal_yellow(section + ':'), '', '', '') self.print_margin = 2 - def print_options(self, title: str, opts: T.Union[coredata.MutableKeyedOptionDictType, coredata.KeyedOptionDictType]) -> None: + def print_options(self, title: str, opts: T.Union[coredata.MutableKeyedOptionDictType, options.OptionStore]) -> None: if not opts: return if title: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 8ec2b1f11e6d..55cf53d06fab 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -304,7 +304,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s for s in subprojects: core_options[k.evolve(subproject=s)] = v - def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, cdata.KeyedOptionDictType], section: str) -> None: + def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, options.OptionStore], section: str) -> None: for key, opt in sorted(opts.items()): optdict = {'name': str(key), 'value': opt.value, 'section': section, 'machine': key.machine.get_lower_case_name() if coredata.is_per_machine_option(key) else 'any'} From e196f848c229a268428142d31b82065575881143 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 10:55:52 -0800 Subject: [PATCH 308/624] compilers: delete dead code after option refactor --- mesonbuild/compilers/compilers.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 14f0a330d380..c927293fb7c2 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -272,17 +272,6 @@ def option_enabled(boptions: T.Set[OptionKey], return False -def get_option_value(options: options.OptionStore, opt: OptionKey, fallback: '_T') -> '_T': - """Get the value of an option, or the fallback value.""" - try: - v: '_T' = options.get_value(opt) # type: ignore [assignment] - except (KeyError, AttributeError): - return fallback - - assert isinstance(v, type(fallback)), f'Should have {type(fallback)!r} but was {type(v)!r}' - # Mypy doesn't understand that the above assert ensures that v is type _T - return v - def get_option_value_for_target(env: 'Environment', target: 'BuildTarget', opt: OptionKey, fallback: '_T') -> '_T': """Get the value of an option, or the fallback value.""" try: @@ -295,21 +284,6 @@ def get_option_value_for_target(env: 'Environment', target: 'BuildTarget', opt: return v -def get_target_option_value(target: 'BuildTarget', - env: 'Environment', - opt: T.Union[OptionKey, str], - fallback: '_T') -> '_T': - """Get the value of an option, or the fallback value.""" - try: - v = env.coredata.get_option_for_target(target, opt) - except KeyError: - return fallback - - assert isinstance(v, type(fallback)), f'Should have {type(fallback)!r} but was {type(v)!r}' - # Mypy doesn't understand that the above assert ensures that v is type _T - return v - - def are_asserts_disabled(target: 'BuildTarget', env: 'Environment') -> bool: """Should debug assertions be disabled From f827010184c524efcacd8153c85c3d8019bd61bb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 11:33:18 -0800 Subject: [PATCH 309/624] coredata: remove dead code from option refactor --- mesonbuild/coredata.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index ef761f54fb06..f9462db38896 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -395,9 +395,6 @@ def init_backend_options(self, backend_name: str) -> None: def get_option(self, key: OptionKey) -> ElementaryOptionValues: return self.optstore.get_value_for(key.name, key.subproject) - def get_option_object_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> options.AnyOptionType: - return self.get_option_for_subproject(key, target.subproject) - def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> ElementaryOptionValues: if isinstance(key, str): assert ':' not in key @@ -423,13 +420,6 @@ def get_option_for_subproject(self, key: T.Union[str, OptionKey], subproject) -> key = key.evolve(subproject=subproject) return self.optstore.get_value_for(key) - def get_option_object_for_subproject(self, key: T.Union[str, OptionKey], subproject) -> options.AnyOptionType: - #keyname = key.name - if key.subproject != subproject: - # This should be an error, fix before merging. - key = key.evolve(subproject=subproject) - return self.optstore.get_value_object_for(key) - def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: dirty = False try: From 8546e408c67a783b5b04063834eff55f3e53eaf2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 26 Feb 2025 11:53:54 -0800 Subject: [PATCH 310/624] optstore: remove num_options It's only used for unittests, so define it as a helper in the unit test module instead --- mesonbuild/options.py | 5 ----- unittests/optiontests.py | 22 ++++++++++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 4295f476b10d..79ba30b95894 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -850,11 +850,6 @@ def get_value_for(self, name: 'T.Union[OptionKey, str]', subproject: T.Optional[ vobject, resolved_value = self.get_value_object_and_value_for(key) return resolved_value - def num_options(self) -> int: - basic = len(self.options) - build = len(self.build_options) if self.build_options else 0 - return basic + build - def add_system_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) if '.' in key.name: diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 845eba0b9636..c733f15d2e83 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -6,6 +6,12 @@ import unittest +def num_options(store: OptionStore) -> int: + basic = len(store.options) + build = len(store.build_options) if store.build_options else 0 + return basic + build + + class OptionTests(unittest.TestCase): def test_basic(self): @@ -64,11 +70,11 @@ def test_reset(self): vo = UserStringOption(name, 'An option set twice', original_value) optstore.add_system_option(name, vo) self.assertEqual(optstore.get_value_for(name), original_value) - self.assertEqual(optstore.num_options(), 1) + self.assertEqual(num_options(optstore), 1) vo2 = UserStringOption(name, 'An option set twice', reset_value) optstore.add_system_option(name, vo2) self.assertEqual(optstore.get_value_for(name), original_value) - self.assertEqual(optstore.num_options(), 1) + self.assertEqual(num_options(optstore), 1) def test_project_nonyielding(self): optstore = OptionStore(False) @@ -78,12 +84,12 @@ def test_project_nonyielding(self): vo = UserStringOption(name, 'A top level option', top_value, False) optstore.add_project_option(OptionKey(name, ''), vo) self.assertEqual(optstore.get_value_for(name, ''), top_value, False) - self.assertEqual(optstore.num_options(), 1) + self.assertEqual(num_options(optstore), 1) vo2 = UserStringOption(name, 'A subproject option', sub_value) optstore.add_project_option(OptionKey(name, 'sub'), vo2) self.assertEqual(optstore.get_value_for(name, ''), top_value) self.assertEqual(optstore.get_value_for(name, 'sub'), sub_value) - self.assertEqual(optstore.num_options(), 2) + self.assertEqual(num_options(optstore), 2) def test_project_yielding(self): optstore = OptionStore(False) @@ -93,12 +99,12 @@ def test_project_yielding(self): vo = UserStringOption(name, 'A top level option', top_value) optstore.add_project_option(OptionKey(name, ''), vo) self.assertEqual(optstore.get_value_for(name, ''), top_value) - self.assertEqual(optstore.num_options(), 1) + self.assertEqual(num_options(optstore), 1) vo2 = UserStringOption(name, 'A subproject option', sub_value, True) optstore.add_project_option(OptionKey(name, 'sub'), vo2) self.assertEqual(optstore.get_value_for(name, ''), top_value) self.assertEqual(optstore.get_value_for(name, 'sub'), top_value) - self.assertEqual(optstore.num_options(), 2) + self.assertEqual(num_options(optstore), 2) def test_project_yielding_not_defined_in_top_project(self): optstore = OptionStore(False) @@ -109,12 +115,12 @@ def test_project_yielding_not_defined_in_top_project(self): vo = UserStringOption(top_name, 'A top level option', top_value) optstore.add_project_option(OptionKey(top_name, ''), vo) self.assertEqual(optstore.get_value_for(top_name, ''), top_value) - self.assertEqual(optstore.num_options(), 1) + self.assertEqual(num_options(optstore), 1) vo2 = UserStringOption(sub_name, 'A subproject option', sub_value, True) optstore.add_project_option(OptionKey(sub_name, 'sub'), vo2) self.assertEqual(optstore.get_value_for(top_name, ''), top_value) self.assertEqual(optstore.get_value_for(sub_name, 'sub'), sub_value) - self.assertEqual(optstore.num_options(), 2) + self.assertEqual(num_options(optstore), 2) def test_augments(self): optstore = OptionStore(False) From e4de716d504a56cd9f87683334facc82aea3d200 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 28 Feb 2025 01:25:25 +0200 Subject: [PATCH 311/624] Actually fix base option default values. --- mesonbuild/interpreter/interpreter.py | 7 ++++--- mesonbuild/options.py | 4 ++-- unittests/optiontests.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 0b16266f7a2e..b0bda85562d8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1081,12 +1081,13 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], raise InterpreterException(f'Invalid option name {optname!r}') try: - value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject)) + optkey = options.OptionKey(optname, self.subproject) + value_object, value = self.coredata.optstore.get_option_from_meson_file(optkey) except KeyError: - if self.coredata.optstore.is_base_option(optname): + if self.coredata.optstore.is_base_option(optkey): # Due to backwards compatibility return the default # option for base options instead of erroring out. - value_object, value = self.get_default_for_b_option(optname) + return self.coredata.optstore.get_default_for_b_option(optname) else: if self.subproject: raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.') diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 79ba30b95894..0bb32b733911 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1100,12 +1100,12 @@ def get_option_from_meson_file(self, key: OptionKey) -> T.Tuple[AnyOptionType, E (value_object, value) = self.get_value_object_and_value_for(key) return (value_object, value) - def get_default_for_b_option(self, keyname: str) -> T.Tuple[AnyOptionType, OptionValueType]: + def get_default_for_b_option(self, keyname: str) -> ElementaryOptionValues: assert keyname.startswith('b_') from .compilers.compilers import BASE_OPTIONS for bkey, bvalue in BASE_OPTIONS.items(): if bkey.name == keyname: - return (T.cast('AnyOptionType', bvalue), bvalue.default) + return T.cast('ElementaryOptionValues', bvalue.default) raise MesonBugException(f'Requested base option {keyname} which does not exist.') def remove(self, key: OptionKey) -> None: diff --git a/unittests/optiontests.py b/unittests/optiontests.py index c733f15d2e83..28abca110f9a 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -207,5 +207,5 @@ def test_subproject_call_options(self): def test_b_default(self): optstore = OptionStore(False) - _, value = optstore.get_default_for_b_option('b_vscrt') + value = optstore.get_default_for_b_option('b_vscrt') self.assertEqual(value, 'from_buildtype') From 3005eaa0210791897309a7b773a37d0f2b80add4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 27 Feb 2025 15:43:00 -0800 Subject: [PATCH 312/624] tests: add a DSL test for get_option with an unset b_ option --- test cases/common/40 options/meson.build | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test cases/common/40 options/meson.build b/test cases/common/40 options/meson.build index ed7668fde36b..3849d54eaeb4 100644 --- a/test cases/common/40 options/meson.build +++ b/test cases/common/40 options/meson.build @@ -1,4 +1,13 @@ -project('options', 'c', meson_version : '>= 1.0.0') +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2025 Intel Corporation + +project('options', meson_version : '>= 1.0.0') + +# This must happen before any language is added, or we wont be sure that the +# compiler didn't cause b_lto to be initialized +assert(get_option('b_lto') == false, 'Unused b_ option does not have default value') + +add_languages('c', required : true) if get_option('testoption') != 'optval' error('Incorrect value to test option') From f41c0f5436e62108644fdfc4d5768807b499e8a2 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Fri, 28 Feb 2025 02:44:51 -0500 Subject: [PATCH 313/624] tests: work around broken PyPy globbing by removing inert code We check to see if a test extension exists, and if so add its path to sys.path... immediately before unconditionally importing it, obviously. This raises the question: why bother checking? Can it ever fail? What if it does fail, do we simply not add path entries and then expect the rest of the test file to work??? The whole thing seems silly and useless. In fact it can fail, if the python interpreter and/or standard library is broken. This is the case for the initial release of PyPy 3.11, in which `'*tachyon*'` fails to glob correctly (and `'tachyon*'` would work, funnily enough -- this is valid too for our use case although still just as pointless). Delete the useless check. It's technically correct to delete it, and it *also* works around the PyPy breakage as a bonus. Closes: https://github.com/mesonbuild/meson/issues/14307 --- test cases/python/4 custom target depends extmodule/blaster.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test cases/python/4 custom target depends extmodule/blaster.py b/test cases/python/4 custom target depends extmodule/blaster.py index 65b6493df890..939010080c09 100644 --- a/test cases/python/4 custom target depends extmodule/blaster.py +++ b/test cases/python/4 custom target depends extmodule/blaster.py @@ -7,8 +7,7 @@ from pathlib import Path filedir = Path(os.path.dirname(__file__)).resolve() -if list(filedir.glob('ext/*tachyon*')): - sys.path.insert(0, (filedir / 'ext').as_posix()) +sys.path.insert(0, (filedir / 'ext').as_posix()) if hasattr(os, 'add_dll_directory'): os.add_dll_directory(filedir / 'ext' / 'lib') From 9af9c6b5b8fb71be41428a53fab5ec026ae88ee1 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 6 Feb 2025 12:30:08 +0100 Subject: [PATCH 314/624] Skip symlinks in _make_tree_writable() Trying to chmod a symlink results in trying to chmod the file or directory it points to, not the symlink itself which has no permissions. Either a symlink points to within the tree we're making writable in which case it'll be handled eventually by os.walk() or it points outside of the tree we're making writable in which case we don't want to touch it. Let's avoid touching files outside of the tree by simply skipping symlinks in _make_tree_writable(). --- mesonbuild/utils/universal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 538b0bd9f01d..dda35cc2b0fd 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1902,7 +1902,7 @@ def _make_tree_writable(topdir: T.Union[str, Path]) -> None: os.chmod(d, os.stat(d).st_mode | stat.S_IWRITE | stat.S_IREAD) for fname in files: fpath = os.path.join(d, fname) - if os.path.isfile(fpath): + if not os.path.islink(fpath) and os.path.isfile(fpath): os.chmod(fpath, os.stat(fpath).st_mode | stat.S_IWRITE | stat.S_IREAD) From be6540e6fdcf1ee88939a74521caa3813b039199 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Sun, 16 Feb 2025 22:04:32 +0100 Subject: [PATCH 315/624] Fix Swift targets with same module name as source file name --- mesonbuild/backend/ninjabackend.py | 9 ++++++--- .../swift/13 file name matches module name/Library.swift | 1 + .../13 file name matches module name/Library2.swift | 1 + .../swift/13 file name matches module name/main.swift | 4 ++++ .../swift/13 file name matches module name/meson.build | 4 ++++ 5 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 test cases/swift/13 file name matches module name/Library.swift create mode 100644 test cases/swift/13 file name matches module name/Library2.swift create mode 100644 test cases/swift/13 file name matches module name/main.swift create mode 100644 test cases/swift/13 file name matches module name/meson.build diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ce82d7845fa0..74fb86c5e3dd 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2218,7 +2218,6 @@ def generate_swift_target(self, target) -> None: raise InvalidArguments(f'Swift target {target.get_basename()} contains a non-swift source file.') 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_compile_only_args() compile_args += swiftc.get_module_args(module_name) for i in reversed(target.get_include_dirs()): basedir = i.get_curdir() @@ -2266,12 +2265,16 @@ def generate_swift_target(self, target) -> None: elem = NinjaBuildElement(self.all_outputs, rel_objects, rulename, abssrc) elem.add_dep(in_module_files + rel_generated) elem.add_dep(abs_headers) - elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes) + elem.add_item('ARGS', swiftc.get_compile_only_args() + compile_args + header_imports + abs_generated + module_includes) elem.add_item('RUNDIR', rundir) self.add_build(elem) + + # -g makes swiftc create a .o file with potentially the same name as one of the compile target generated ones. + mod_gen_args = [el for el in compile_args if el != '-g'] + elem = NinjaBuildElement(self.all_outputs, out_module_name, rulename, abssrc) elem.add_dep(in_module_files + rel_generated) - elem.add_item('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args()) + elem.add_item('ARGS', swiftc.get_mod_gen_args() + mod_gen_args + abs_generated + module_includes) elem.add_item('RUNDIR', rundir) self.add_build(elem) if isinstance(target, build.StaticLibrary): diff --git a/test cases/swift/13 file name matches module name/Library.swift b/test cases/swift/13 file name matches module name/Library.swift new file mode 100644 index 000000000000..617952f4ef7e --- /dev/null +++ b/test cases/swift/13 file name matches module name/Library.swift @@ -0,0 +1 @@ +public func callMe() {} diff --git a/test cases/swift/13 file name matches module name/Library2.swift b/test cases/swift/13 file name matches module name/Library2.swift new file mode 100644 index 000000000000..7172b249b678 --- /dev/null +++ b/test cases/swift/13 file name matches module name/Library2.swift @@ -0,0 +1 @@ +public func callMe2() {} diff --git a/test cases/swift/13 file name matches module name/main.swift b/test cases/swift/13 file name matches module name/main.swift new file mode 100644 index 000000000000..d5e8a0e9c2b1 --- /dev/null +++ b/test cases/swift/13 file name matches module name/main.swift @@ -0,0 +1,4 @@ +import Library + +callMe() +callMe2() diff --git a/test cases/swift/13 file name matches module name/meson.build b/test cases/swift/13 file name matches module name/meson.build new file mode 100644 index 000000000000..34af03313f81 --- /dev/null +++ b/test cases/swift/13 file name matches module name/meson.build @@ -0,0 +1,4 @@ +project('file name matches module name', 'swift') + +lib = static_library('Library', 'Library.swift', 'Library2.swift') +executable('program', 'main.swift', link_with: [lib]) From b1b49a237b24d3fd772853c7db85035d28bc32ef Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Tue, 25 Feb 2025 21:26:32 +0100 Subject: [PATCH 316/624] swift: Add swift_std compiler option --- mesonbuild/compilers/swift.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 91c17f0bade1..b04ccbd0e541 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -7,12 +7,14 @@ import subprocess, os.path import typing as T -from .. import mlog +from .. import mlog, options from ..mesonlib import EnvironmentException, MesonException, version_compare from .compilers import Compiler, clike_debug_args if T.TYPE_CHECKING: + from .. import build + from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment @@ -115,6 +117,31 @@ def get_include_args(self, path: str, is_system: bool) -> T.List[str]: def get_compile_only_args(self) -> T.List[str]: return ['-c'] + def get_options(self) -> MutableKeyedOptionDictType: + opts = super().get_options() + + key = self.form_compileropt_key('std') + opts[key] = options.UserComboOption( + self.make_option_name(key), + 'Swift language version.', + 'none', + # List them with swiftc -frontend -swift-version '' + choices=['none', '4', '4.2', '5', '6']) + + return opts + + def get_option_compile_args(self, target: build.BuildTarget, env: Environment, subproject: T.Optional[str] = None + ) -> T.List[str]: + args: T.List[str] = [] + + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) + + if std != 'none': + args += ['-swift-version', std] + + return args + def get_working_directory_args(self, path: str) -> T.Optional[T.List[str]]: if version_compare(self.version, '<4.2'): return None From a8316977f11ea5767a49c6ee888ae02d89964b90 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 13 Feb 2025 07:55:30 -0500 Subject: [PATCH 317/624] Add cache to OptionKey OptionKey objects are used extensively. We want them with a simple API, but they also need to be optimized to not compromise meson performances. Since this is an immutable object, it is possible to cache the OptionKey object creation. We need to do it using the __new__ to make the caching mechanism transparent. Fixes #14245 --- mesonbuild/options.py | 50 +++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 0bb32b733911..f685924685b4 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -103,6 +103,8 @@ class ArgparseKWs(TypedDict, total=False): } _BAD_VALUE = 'Qwert Zuiopü' +_optionkey_cache: T.Dict[T.Tuple[str, str, MachineChoice], OptionKey] = {} + @total_ordering class OptionKey: @@ -114,26 +116,42 @@ class OptionKey: internally easier to reason about and produce. """ - __slots__ = ['name', 'subproject', 'machine', '_hash'] + __slots__ = ('name', 'subproject', 'machine', '_hash') name: str - subproject: T.Optional[str] # None is global, empty string means top level project + subproject: T.Optional[str] # None is global, empty string means top level project machine: MachineChoice _hash: int - def __init__(self, - name: str, - subproject: T.Optional[str] = None, - machine: MachineChoice = MachineChoice.HOST): + def __new__(cls, + name: str = '', + subproject: T.Optional[str] = None, + machine: MachineChoice = MachineChoice.HOST) -> OptionKey: + """The use of the __new__ method allows to add a transparent cache + to the OptionKey object creation, without breaking its API. + """ + if not name: + return super().__new__(cls) # for unpickling, do not cache now + + tuple_ = (name, subproject, machine) + try: + return _optionkey_cache[tuple_] + except KeyError: + instance = super().__new__(cls) + instance._init(name, subproject, machine) + _optionkey_cache[tuple_] = instance + return instance + + def _init(self, name: str, subproject: T.Optional[str], machine: MachineChoice) -> None: + # We don't use the __init__ method, because it would be called after __new__ + # while we need __new__ to initialise the object before populating the cache. + if not isinstance(machine, MachineChoice): raise MesonException(f'Internal error, bad machine type: {machine}') if not isinstance(name, str): raise MesonBugException(f'Key name is not a string: {name}') - # the _type option to the constructor is kinda private. We want to be - # able to save the state and avoid the lookup function when - # pickling/unpickling, but we need to be able to calculate it when - # constructing a new OptionKey assert ':' not in name + object.__setattr__(self, 'name', name) object.__setattr__(self, 'subproject', subproject) object.__setattr__(self, 'machine', machine) @@ -150,15 +168,9 @@ def __getstate__(self) -> T.Dict[str, T.Any]: } def __setstate__(self, state: T.Dict[str, T.Any]) -> None: - """De-serialize the state of a pickle. - - This is very clever. __init__ is not a constructor, it's an - initializer, therefore it's safe to call more than once. We create a - state in the custom __getstate__ method, which is valid to pass - splatted to the initializer. - """ - # Mypy doesn't like this, because it's so clever. - self.__init__(**state) # type: ignore + # Here, the object is created using __new__() + self._init(**state) + _optionkey_cache[(self.name, self.subproject, self.machine)] = self def __hash__(self) -> int: return self._hash From 70c7bc45fe515a5d6868305b3f59be9cd3e48fe7 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Fri, 14 Feb 2025 08:15:28 -0500 Subject: [PATCH 318/624] Replace OptionKey.evolve(...) with specialized functions This removes a few comparisons, and one function call. --- mesonbuild/coredata.py | 4 ++-- mesonbuild/options.py | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f9462db38896..8dbf16513b41 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -594,7 +594,7 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' # refactor they will get per-subproject values. really_unknown = [] for uo in unknown_options: - topkey = uo.evolve(subproject='') + topkey = uo.as_root() if topkey not in self.optstore: really_unknown.append(uo) unknown_options = really_unknown @@ -696,7 +696,7 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # adding languages and setting backend. if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): continue - if self.optstore.is_base_option(k) and k.as_root() in base_options: + if self.optstore.is_base_option(k) and (k in base_options or k.as_root() in base_options): # set_options will report unknown base options continue options[k] = v diff --git a/mesonbuild/options.py b/mesonbuild/options.py index f685924685b4..61a34df3a860 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -250,17 +250,15 @@ def evolve(self, subproject if subproject != _BAD_VALUE else self.subproject, # None is a valid value so it can'the default value in method declaration. machine if machine is not None else self.machine) - def as_root(self) -> 'OptionKey': + def as_root(self) -> OptionKey: """Convenience method for key.evolve(subproject='').""" - if self.subproject is None or self.subproject == '': - return self return self.evolve(subproject='') - def as_build(self) -> 'OptionKey': + def as_build(self) -> OptionKey: """Convenience method for key.evolve(machine=MachineChoice.BUILD).""" return self.evolve(machine=MachineChoice.BUILD) - def as_host(self) -> 'OptionKey': + def as_host(self) -> OptionKey: """Convenience method for key.evolve(machine=MachineChoice.HOST).""" return self.evolve(machine=MachineChoice.HOST) @@ -804,7 +802,7 @@ def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey: # I did not do this yet, because it would make this MR even # more massive than it already is. Later then. if not self.is_cross and key.machine == MachineChoice.BUILD: - key = key.evolve(machine=MachineChoice.HOST) + key = key.as_host() return key def get_value(self, key: T.Union[OptionKey, str]) -> ElementaryOptionValues: @@ -819,7 +817,7 @@ def get_value_object_for(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType: if self.is_project_option(key): assert key.subproject is not None if potential is not None and potential.yielding: - parent_key = key.evolve(subproject='') + parent_key = key.as_root() try: parent_option = self.options[parent_key] except KeyError: @@ -837,7 +835,7 @@ def get_value_object_for(self, key: 'T.Union[OptionKey, str]') -> AnyOptionType: return potential else: if potential is None: - parent_key = key.evolve(subproject=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}.') return self.options[parent_key] @@ -1047,7 +1045,7 @@ def set_option_from_string(self, keystr: T.Union[OptionKey, str], new_value: str o = OptionKey.from_string(keystr) if o in self.options: return self.set_value(o, new_value) - o = o.evolve(subproject='') + o = o.as_root() return self.set_value(o, new_value) def set_subproject_options(self, subproject: str, @@ -1308,7 +1306,7 @@ def initialize_from_top_level_project_call(self, elif key in self.options: self.set_value(key, valstr, first_invocation) else: - proj_key = key.evolve(subproject='') + proj_key = key.as_root() if proj_key in self.options: self.options[proj_key].set_value(valstr) else: @@ -1341,7 +1339,7 @@ def initialize_from_top_level_project_call(self, # Argubly this should 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.evolve(subproject='') + proj_key = key.as_root() if self.is_project_option(proj_key): self.set_option(proj_key, valstr) else: @@ -1359,7 +1357,7 @@ def initialize_from_top_level_project_call(self, if key in self.options: self.set_value(key, valstr, True) elif key.subproject is None: - projectkey = key.evolve(subproject='') + projectkey = key.as_root() if projectkey in self.options: self.options[projectkey].set_value(valstr) else: From a9a37de9a4c479041b7f5d6e3127240505f6768a Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Fri, 14 Feb 2025 08:42:41 -0500 Subject: [PATCH 319/624] Remove total_ordering from OptionKey It makes object initialisation slower... --- mesonbuild/options.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 61a34df3a860..25e2a9f43ff3 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -5,7 +5,6 @@ from __future__ import annotations from collections import OrderedDict from itertools import chain -from functools import total_ordering import argparse import dataclasses import itertools @@ -106,7 +105,6 @@ class ArgparseKWs(TypedDict, total=False): _optionkey_cache: T.Dict[T.Tuple[str, str, MachineChoice], OptionKey] = {} -@total_ordering class OptionKey: """Represents an option key in the various option dictionaries. @@ -183,6 +181,11 @@ def __eq__(self, other: object) -> bool: return self._to_tuple() == other._to_tuple() return NotImplemented + def __ne__(self, other: object) -> bool: + if isinstance(other, OptionKey): + return self._to_tuple() != other._to_tuple() + return NotImplemented + def __lt__(self, other: object) -> bool: if isinstance(other, OptionKey): if self.subproject is None: @@ -192,6 +195,33 @@ def __lt__(self, other: object) -> bool: return self._to_tuple() < other._to_tuple() return NotImplemented + def __le__(self, other: object) -> bool: + if isinstance(other, OptionKey): + if self.subproject is None and other.subproject is not None: + return True + elif self.subproject is not None and other.subproject is None: + return False + return self._to_tuple() <= other._to_tuple() + return NotImplemented + + def __gt__(self, other: object) -> bool: + if isinstance(other, OptionKey): + if other.subproject is None: + return self.subproject is not None + elif self.subproject is None: + return False + return self._to_tuple() > other._to_tuple() + return NotImplemented + + def __ge__(self, other: object) -> bool: + if isinstance(other, OptionKey): + if self.subproject is None and other.subproject is not None: + return False + elif self.subproject is not None and other.subproject is None: + return True + return self._to_tuple() >= other._to_tuple() + return NotImplemented + def __str__(self) -> str: out = self.name if self.machine is MachineChoice.BUILD: From 0431b0bbbd63379acbf0d2cfec787224c58e5ce8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 28 Feb 2025 12:18:25 -0800 Subject: [PATCH 320/624] environment: delete dead code from option refactor --- mesonbuild/environment.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 7c18104ee345..c5e8ba5ccc25 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2020 The Meson development team -# Copyright © 2023 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -46,7 +46,6 @@ from .compilers import Compiler from .wrap.wrap import Resolver from . import cargo - from .build import BuildTarget CompilersDict = T.Dict[str, Compiler] @@ -1035,13 +1034,3 @@ def get_env_for_paths(self, library_paths: T.Set[str], extra_paths: T.Set[str]) if extra_paths: env.prepend('PATH', list(extra_paths)) return env - - def determine_option_value(self, key: T.Union[str, 'OptionKey'], target: T.Optional['BuildTarget'], subproject: T.Optional[str]) -> T.List[str]: - if target is None and subproject is None: - raise RuntimeError('Internal error, option value determination is missing arguments.') - if isinstance(key, str): - key = OptionKey(key) - if target: - return self.coredata.get_option_for_target(target, key) - else: - return self.coredata.get_option_for_subproject(key, subproject) From 8152d6f7952e2c1e821a24ba51e9286c725ff272 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 1 Oct 2024 10:14:23 -0700 Subject: [PATCH 321/624] environment: fix missing argument and return type annotations --- mesonbuild/environment.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index c5e8ba5ccc25..03e257766c4e 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -78,7 +78,8 @@ 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): +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): @@ -91,7 +92,8 @@ def detect_gcovr(gcovr_exe: str = 'gcovr', min_version: str = '3.3', log: bool = return gcovr_exe, found return None, None -def detect_lcov(lcov_exe: str = 'lcov', log: bool = False): +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): @@ -104,7 +106,7 @@ def detect_lcov(lcov_exe: str = 'lcov', log: bool = False): return lcov_exe, found return None, None -def detect_llvm_cov(suffix: T.Optional[str] = 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 == '': @@ -121,7 +123,7 @@ def detect_llvm_cov(suffix: T.Optional[str] = None): return tool return None -def compute_llvm_suffix(coredata: coredata.CoreData): +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' @@ -139,7 +141,8 @@ def compute_llvm_suffix(coredata: coredata.CoreData): # 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'): +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 @@ -162,7 +165,7 @@ def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.Optional[T.List 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.Tuple[T.List[str], str]: +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) @@ -188,6 +191,7 @@ def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) 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 @@ -532,7 +536,7 @@ def detect_machine_info(compilers: T.Optional[CompilersDict] = None) -> MachineI # 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): +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 @@ -906,7 +910,7 @@ def is_object(self, fname: 'mesonlib.FileOrString') -> bool: return is_object(fname) @lru_cache(maxsize=None) - def is_library(self, fname: mesonlib.FileOrString): + 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]]: @@ -997,9 +1001,10 @@ def get_compiler_system_include_dirs(self, for_machine: MachineChoice) -> T.List return [] return comp.get_default_include_dirs() - def need_exe_wrapper(self, for_machine: MachineChoice = MachineChoice.HOST): + def need_exe_wrapper(self, for_machine: MachineChoice = MachineChoice.HOST) -> bool: value = self.properties[for_machine].get('needs_exe_wrapper', None) if value is not None: + assert isinstance(value, bool), 'for mypy' return value if not self.is_cross_build(): return False From fe43247c723e9a186898b992b07abc5c9a43754e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 1 Oct 2024 10:18:04 -0700 Subject: [PATCH 322/624] environment: fix minor typing issues These include things like not narrowing unions and boolean error handling --- mesonbuild/environment.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 03e257766c4e..d124a608b582 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -44,6 +44,7 @@ from configparser import ConfigParser from .compilers import Compiler + from .compilers.mixins.visualstudio import VisualStudioLikeCompiler from .wrap.wrap import Resolver from . import cargo @@ -53,6 +54,11 @@ build_filename = 'meson.build' +def _as_str(val: object) -> str: + assert isinstance(val, str), 'for mypy' + return val + + def _get_env_var(for_machine: MachineChoice, is_cross: bool, var_name: str) -> T.Optional[str]: """ Returns the exact env var and the value. @@ -338,6 +344,7 @@ def detect_windows_arch(compilers: CompilersDict) -> str: # 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'}): @@ -869,7 +876,12 @@ def create_new_coredata(self, options: coredata.SharedCMDOptions) -> None: # re-initialized with project options by the interpreter during # build file parsing. # meson_command is used by the regenchecker script, which runs meson - self.coredata = coredata.CoreData(options, self.scratch_dir, mesonlib.get_meson_command()) + meson_command = mesonlib.get_meson_command() + if meson_command is None: + meson_command = [] + else: + meson_command = meson_command.copy() + self.coredata = coredata.CoreData(options, self.scratch_dir, meson_command) self.first_invocation = True def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool: @@ -950,25 +962,25 @@ def get_static_lib_dir(self) -> str: return self.get_libdir() def get_prefix(self) -> str: - return self.coredata.get_option(OptionKey('prefix')) + return _as_str(self.coredata.get_option(OptionKey('prefix'))) def get_libdir(self) -> str: - return self.coredata.get_option(OptionKey('libdir')) + return _as_str(self.coredata.get_option(OptionKey('libdir'))) def get_libexecdir(self) -> str: - return self.coredata.get_option(OptionKey('libexecdir')) + return _as_str(self.coredata.get_option(OptionKey('libexecdir'))) def get_bindir(self) -> str: - return self.coredata.get_option(OptionKey('bindir')) + return _as_str(self.coredata.get_option(OptionKey('bindir'))) def get_includedir(self) -> str: - return self.coredata.get_option(OptionKey('includedir')) + return _as_str(self.coredata.get_option(OptionKey('includedir'))) def get_mandir(self) -> str: - return self.coredata.get_option(OptionKey('mandir')) + return _as_str(self.coredata.get_option(OptionKey('mandir'))) def get_datadir(self) -> str: - return self.coredata.get_option(OptionKey('datadir')) + return _as_str(self.coredata.get_option(OptionKey('datadir'))) def get_compiler_system_lib_dirs(self, for_machine: MachineChoice) -> T.List[str]: for comp in self.coredata.compilers[for_machine].values(): @@ -986,8 +998,8 @@ def get_compiler_system_lib_dirs(self, for_machine: MachineChoice) -> T.List[str p, out, _ = Popen_safe(comp.get_exelist() + ['-print-search-dirs']) if p.returncode != 0: raise mesonlib.MesonException('Could not calculate system search dirs') - out = out.split('\n')[index].lstrip('libraries: =').split(':') - return [os.path.normpath(p) for p in out] + split = out.split('\n')[index].lstrip('libraries: =').split(':') + return [os.path.normpath(p) for p in split] def get_compiler_system_include_dirs(self, for_machine: MachineChoice) -> T.List[str]: for comp in self.coredata.compilers[for_machine].values(): @@ -1016,7 +1028,7 @@ def get_exe_wrapper(self) -> T.Optional[ExternalProgram]: return self.exe_wrapper def has_exe_wrapper(self) -> bool: - return self.exe_wrapper and self.exe_wrapper.found() + return self.exe_wrapper is not None and self.exe_wrapper.found() def get_env_for_paths(self, library_paths: T.Set[str], extra_paths: T.Set[str]) -> mesonlib.EnvironmentVariables: env = mesonlib.EnvironmentVariables() From dfa118547247dc862310052f75fcdbc020089c24 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 1 Oct 2024 10:30:58 -0700 Subject: [PATCH 323/624] machinefile: ensure that arrays are single deep arrays for strings We don't actually allow anything except elementry types (`str`, `bool`, `int`, `array[str]`) here, so we can tighten the typing. This in turn helps us to simplify the typing in environment.py --- mesonbuild/machinefile.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/mesonbuild/machinefile.py b/mesonbuild/machinefile.py index a3aeae522713..b39a47217ce2 100644 --- a/mesonbuild/machinefile.py +++ b/mesonbuild/machinefile.py @@ -12,12 +12,8 @@ from .mesonlib import MesonException if T.TYPE_CHECKING: - from typing_extensions import TypeAlias - from .coredata import StrOrBytesPath - - SectionT: TypeAlias = T.Union[str, int, bool, T.List[str], T.List['SectionT']] - + from .options import ElementaryOptionValues class CmdLineFileParser(configparser.ConfigParser): def __init__(self) -> None: @@ -36,8 +32,8 @@ def optionxform(self, optionstr: str) -> str: class MachineFileParser(): def __init__(self, filenames: T.List[str], sourcedir: str) -> None: self.parser = CmdLineFileParser() - self.constants: T.Dict[str, SectionT] = {'True': True, 'False': False} - self.sections: T.Dict[str, T.Dict[str, SectionT]] = {} + self.constants: T.Dict[str, ElementaryOptionValues] = {'True': True, 'False': False} + self.sections: T.Dict[str, T.Dict[str, ElementaryOptionValues]] = {} for fname in filenames: try: @@ -62,9 +58,9 @@ def __init__(self, filenames: T.List[str], sourcedir: str) -> None: continue self.sections[s] = self._parse_section(s) - def _parse_section(self, s: str) -> T.Dict[str, SectionT]: + def _parse_section(self, s: str) -> T.Dict[str, ElementaryOptionValues]: self.scope = self.constants.copy() - section: T.Dict[str, SectionT] = {} + section: T.Dict[str, ElementaryOptionValues] = {} for entry, value in self.parser.items(s): if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry: raise MesonException(f'Malformed variable name {entry!r} in machine file.') @@ -83,7 +79,7 @@ def _parse_section(self, s: str) -> T.Dict[str, SectionT]: self.scope[entry] = res return section - def _evaluate_statement(self, node: mparser.BaseNode) -> SectionT: + def _evaluate_statement(self, node: mparser.BaseNode) -> ElementaryOptionValues: if isinstance(node, (mparser.StringNode)): return node.value elif isinstance(node, mparser.BooleanNode): @@ -93,7 +89,9 @@ def _evaluate_statement(self, node: mparser.BaseNode) -> SectionT: elif isinstance(node, mparser.ParenthesizedNode): return self._evaluate_statement(node.inner) elif isinstance(node, mparser.ArrayNode): - return [self._evaluate_statement(arg) for arg in node.args.arguments] + a = [self._evaluate_statement(arg) for arg in node.args.arguments] + assert all(isinstance(s, str) for s in a), 'for mypy' + return T.cast('T.List[str]', a) elif isinstance(node, mparser.IdNode): return self.scope[node.value] elif isinstance(node, mparser.ArithmeticNode): @@ -109,7 +107,7 @@ def _evaluate_statement(self, node: mparser.BaseNode) -> SectionT: return os.path.join(l, r) raise MesonException('Unsupported node type') -def parse_machine_files(filenames: T.List[str], sourcedir: str) -> T.Dict[str, T.Dict[str, SectionT]]: +def parse_machine_files(filenames: T.List[str], sourcedir: str) -> T.Dict[str, T.Dict[str, ElementaryOptionValues]]: parser = MachineFileParser(filenames, sourcedir) return parser.sections From f0795e14c55dfbad2291136090ee964cce2c38ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 1 Oct 2024 10:41:59 -0700 Subject: [PATCH 324/624] environment: make fully type safe This as much as anything is to stop lying to envconfig about the potential types it will be given. --- mesonbuild/compilers/compilers.py | 2 ++ mesonbuild/envconfig.py | 15 ++++++++++++--- mesonbuild/environment.py | 10 +++++----- run_mypy.py | 1 + 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index c927293fb7c2..18535be42fef 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1424,6 +1424,8 @@ def get_global_options(lang: str, 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, diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 4055b21761c5..7cbef8928fa1 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -13,6 +13,9 @@ from . import mlog from pathlib import Path +if T.TYPE_CHECKING: + from .options import ElementaryOptionValues + # These classes contains all the data pulled from configuration files (native # and cross file currently), and also assists with the reading environment @@ -153,7 +156,7 @@ class CMakeSkipCompilerTest(Enum): class Properties: def __init__( self, - properties: T.Optional[T.Dict[str, T.Optional[T.Union[str, bool, int, T.List[str]]]]] = None, + properties: T.Optional[T.Dict[str, ElementaryOptionValues]] = None, ): self.properties = properties or {} @@ -270,7 +273,13 @@ def __repr__(self) -> str: return f'' @classmethod - def from_literal(cls, literal: T.Dict[str, str]) -> 'MachineInfo': + def from_literal(cls, raw: T.Dict[str, ElementaryOptionValues]) -> 'MachineInfo': + # We don't have enough type information to be sure of what we loaded + # So we need to accept that this might have ElementaryOptionValues, but + # then ensure that it's actually strings, since that's what the + # [*_machine] section should have. + assert all(isinstance(v, str) for v in raw.values()), 'for mypy' + literal = T.cast('T.Dict[str, str]', raw) minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'} if set(literal) < minimum_literal: raise EnvironmentException( @@ -389,7 +398,7 @@ class BinaryTable: def __init__( self, - binaries: T.Optional[T.Dict[str, T.Union[str, T.List[str]]]] = None, + binaries: T.Optional[T.Mapping[str, ElementaryOptionValues]] = None, ): self.binaries: T.Dict[str, T.List[str]] = {} if binaries: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index d124a608b582..76eb5617bbf9 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -41,10 +41,9 @@ from mesonbuild import envconfig if T.TYPE_CHECKING: - from configparser import ConfigParser - from .compilers import Compiler from .compilers.mixins.visualstudio import VisualStudioLikeCompiler + from .options import ElementaryOptionValues from .wrap.wrap import Resolver from . import cargo @@ -633,7 +632,7 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # # Note that order matters because of 'buildtype', if it is after # 'optimization' and 'debug' keys, it override them. - self.options: T.MutableMapping[OptionKey, T.Union[str, T.List[str]]] = collections.OrderedDict() + self.options: T.MutableMapping[OptionKey, ElementaryOptionValues] = collections.OrderedDict() self.machinestore = machinefile.MachineFileStore(self.coredata.config_files, self.coredata.cross_files, self.source_dir) @@ -701,7 +700,7 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # Store a global state of Cargo dependencies self.cargo: T.Optional[cargo.Interpreter] = None - def mfilestr2key(self, machine_file_string: str, section_subproject: str, machine: MachineChoice): + def mfilestr2key(self, machine_file_string: str, section_subproject: str, machine: MachineChoice) -> OptionKey: key = OptionKey.from_string(machine_file_string) assert key.machine == MachineChoice.HOST if key.subproject: @@ -712,7 +711,8 @@ def mfilestr2key(self, machine_file_string: str, section_subproject: str, machin return key.evolve(machine=machine) return key - def _load_machine_file_options(self, config: 'ConfigParser', properties: Properties, machine: MachineChoice) -> None: + def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, ElementaryOptionValues]], + properties: Properties, machine: MachineChoice) -> None: """Read the contents of a Machine file and put it in the options store.""" # Look for any options in the deprecated paths section, warn about diff --git a/run_mypy.py b/run_mypy.py index 4f7a6317be57..afa5531cde24 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -36,6 +36,7 @@ # 'mesonbuild/coredata.py', 'mesonbuild/depfile.py', 'mesonbuild/envconfig.py', + 'mesonbuild/environment.py', 'mesonbuild/interpreter/compiler.py', 'mesonbuild/interpreter/mesonmain.py', 'mesonbuild/interpreter/interpreterobjects.py', From b7b45bb161529382cb0fcfbccec0fc6fe0d51a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corentin=20No=C3=ABl?= Date: Thu, 20 Feb 2025 22:12:10 +0100 Subject: [PATCH 325/624] modules/gnome: Allow to specify the doc-format argument Add the support for the doc-format argument if g-ir-scanner supports it. Ignore the argument otherwise as this is a no-op in such cases. --- docs/markdown/Gnome-module.md | 1 + mesonbuild/modules/gnome.py | 5 +++++ test cases/frameworks/7 gnome/gir/meson.build | 1 + 3 files changed, 7 insertions(+) diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 013e8c8241ce..7248ca51118b 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -93,6 +93,7 @@ There are several keyword arguments. Many of these map directly to the `g-ir-scanner` tool so see its documentation for more information. * `dependencies`: deps to use during introspection scanning +* `doc_format`: (*Added 1.8.0*) format of the inline documentation * `extra_args`: command line arguments to pass to gir compiler * `env`: (*Added 1.2.0*) environment variables to set, such as `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`, diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 8bde6e0917f2..dffc61574887 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -82,6 +82,7 @@ class GenerateGir(TypedDict): build_by_default: bool dependencies: T.List[Dependency] + doc_format: T.Optional[str] export_packages: T.List[str] extra_args: T.List[str] fatal_warnings: bool @@ -1103,6 +1104,7 @@ def _get_scanner_ldflags(ldflags: T.Iterable[str]) -> T.Iterable[str]: _EXTRA_ARGS_KW, ENV_KW.evolve(since='1.2.0'), KwargInfo('dependencies', ContainerTypeInfo(list, Dependency), default=[], listify=True), + KwargInfo('doc_format', (str, NoneType), since='1.8.0'), KwargInfo('export_packages', ContainerTypeInfo(list, str), default=[], listify=True), KwargInfo('fatal_warnings', bool, default=False, since='0.55.0'), KwargInfo('header', ContainerTypeInfo(list, str), default=[], listify=True), @@ -1208,6 +1210,9 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut scan_command += ['--sources-top-dirs', os.path.join(state.environment.get_source_dir(), state.root_subdir)] scan_command += ['--sources-top-dirs', os.path.join(state.environment.get_build_dir(), state.root_subdir)] + if kwargs['doc_format'] is not None and self._gir_has_option('--doc-format'): + scan_command += ['--doc-format', kwargs['doc_format']] + if '--warn-error' in scan_command: FeatureDeprecated.single_use('gnome.generate_gir argument --warn-error', '0.55.0', state.subproject, 'Use "fatal_warnings" keyword argument', state.current_node) diff --git a/test cases/frameworks/7 gnome/gir/meson.build b/test cases/frameworks/7 gnome/gir/meson.build index d2ceaee4256b..70db496b8b2b 100644 --- a/test cases/frameworks/7 gnome/gir/meson.build +++ b/test cases/frameworks/7 gnome/gir/meson.build @@ -47,6 +47,7 @@ gnome.generate_gir( includes : ['GObject-2.0', 'MesonDep1-1.0'], # dep1_dep pulls in dep2_dep for us dependencies : [[fake_dep, dep1_dep]], + doc_format: 'gtk-doc-markdown', install : true, build_by_default : true, ) From 92c517ea69e0578515d15061e7ec32f518733acf Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:06:40 +0900 Subject: [PATCH 326/624] output PACKAGE_NOT_FOUND_MESSAGE as warning when CMake package is not found --- mesonbuild/dependencies/cmake.py | 11 ++++++++++- mesonbuild/dependencies/data/CMakeLists.txt | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py index 42cffd595c4c..0c350214ba54 100644 --- a/mesonbuild/dependencies/cmake.py +++ b/mesonbuild/dependencies/cmake.py @@ -414,7 +414,16 @@ def _detect_dep(self, name: str, package_version: str, modules: T.List[T.Tuple[s # Whether the package is found or not is always stored in PACKAGE_FOUND self.is_found = self.traceparser.var_to_bool('PACKAGE_FOUND') if not self.is_found: - return + not_found_message = self.traceparser.get_cmake_var('PACKAGE_NOT_FOUND_MESSAGE') + if len(not_found_message) > 0: + mlog.warning( + 'CMake reported that the package {} was not found with the following reason:\n' + '{}'.format(name, not_found_message[0])) + else: + mlog.warning( + 'CMake reported that the package {} was not found, ' + 'even though Meson\'s preliminary check succeeded.'.format(name)) + raise self._gen_exception('PACKAGE_FOUND is false') # Try to detect the version vers_raw = self.traceparser.get_cmake_var('PACKAGE_VERSION') diff --git a/mesonbuild/dependencies/data/CMakeLists.txt b/mesonbuild/dependencies/data/CMakeLists.txt index d682cb8246dc..4e7838ebbaee 100644 --- a/mesonbuild/dependencies/data/CMakeLists.txt +++ b/mesonbuild/dependencies/data/CMakeLists.txt @@ -100,3 +100,9 @@ if(${_packageName}_FOUND OR ${PACKAGE_NAME}_FOUND) set(PACKAGE_DEFINITIONS "${${definitions}}") set(PACKAGE_LIBRARIES "${${libs}}") endif() + +if(${_packageName}_NOT_FOUND_MESSAGE) + set(PACKAGE_NOT_FOUND_MESSAGE "${${_packageName}_NOT_FOUND_MESSAGE}") +elseif(${PACKAGE_NAME}_NOT_FOUND_MESSAGE) + set(PACKAGE_NOT_FOUND_MESSAGE "${${PACKAGE_NAME}_NOT_FOUND_MESSAGE}") +endif() From 25abe40343d549d0fa5e6e50a09ce05647e3145a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Nov 2024 12:12:10 +0100 Subject: [PATCH 327/624] arglist: optimize __init__() "Inline" CompilerArgs.__iter__() into CompilerArgs.__init__(), so that replace list(Iterable) is replaced by the much faster list(List). Before: ncalls tottime cumtime 19268 0.163 3.586 arglist.py:97(__init__) After: ncalls tottime cumtime 18674 0.211 3.442 arglist.py:97(__init__) Signed-off-by: Paolo Bonzini --- mesonbuild/arglist.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mesonbuild/arglist.py b/mesonbuild/arglist.py index 34456581f780..2e2542ef9464 100644 --- a/mesonbuild/arglist.py +++ b/mesonbuild/arglist.py @@ -97,6 +97,12 @@ class CompilerArgs(T.MutableSequence[str]): def __init__(self, compiler: T.Union['Compiler', 'StaticLinker'], iterable: T.Optional[T.Iterable[str]] = None): self.compiler = compiler + + if isinstance(iterable, CompilerArgs): + iterable.flush_pre_post() + # list(iter(x)) is over two times slower than list(x), so + # pass the underlying list to list() directly, instead of an iterator + iterable = iterable._container self._container: T.List[str] = list(iterable) if iterable is not None else [] self.pre: T.Deque[str] = collections.deque() self.post: T.Deque[str] = collections.deque() @@ -140,6 +146,7 @@ def flush_pre_post(self) -> None: self.post.clear() def __iter__(self) -> T.Iterator[str]: + # see also __init__, where this method is essentially inlined self.flush_pre_post() return iter(self._container) From 0ce21ac78a09e70735ba63e873ac8587a41af140 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 7 Nov 2024 12:12:10 +0100 Subject: [PATCH 328/624] arglist: optimize flush_pre_post(), and __iadd__() with it Unless an argument is marked as Dedup.OVERRIDDEN, pre_flush_set and post_flush_set will always be empty and the loops in flush_pre_post() will not be doing anything interesting: for a in self.pre: dedup = self._can_dedup(a) if a not in pre_flush_set: # This just makes new a copy of self.pre new.append(a) if dedup is Dedup.OVERRIDDEN: # this never happens pre_flush_set.add(a) for a in reversed(self.post): dedup = self._can_dedup(a) if a not in post_flush_set: # Here self.post is reversed twice post_flush.appendleft(a) if dedup is Dedup.OVERRIDDEN: # this never happens post_flush_set.add(a) new.extend(post_flush) In this case it's possible to avoid expensive calls and loops, instead relying as much on Python builtins as possible. Track whether any options have that flag and if not just concatenate pre, _container and post. Before: ncalls tottime cumtime 45127 0.251 4.530 arglist.py:142(__iter__) 81866 3.623 5.013 arglist.py:108(flush_pre_post) 76618 3.793 5.338 arglist.py:273(__iadd__) After: 35647 0.156 0.627 arglist.py:160(__iter__) 78998 2.627 3.603 arglist.py:116(flush_pre_post) 73774 3.605 5.049 arglist.py:292(__iadd__) The time in __iadd__ is reduced because it calls __iter__, which flushes pre and post. Signed-off-by: Paolo Bonzini --- mesonbuild/arglist.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/mesonbuild/arglist.py b/mesonbuild/arglist.py index 2e2542ef9464..3d9bb722a933 100644 --- a/mesonbuild/arglist.py +++ b/mesonbuild/arglist.py @@ -104,14 +104,25 @@ def __init__(self, compiler: T.Union['Compiler', 'StaticLinker'], # pass the underlying list to list() directly, instead of an iterator iterable = iterable._container self._container: T.List[str] = list(iterable) if iterable is not None else [] + self.pre: T.Deque[str] = collections.deque() self.post: T.Deque[str] = collections.deque() + self.needs_override_check: bool = False # Flush the saved pre and post list into the _container list # # This correctly deduplicates the entries after _can_dedup definition # Note: This function is designed to work without delete operations, as deletions are worsening the performance a lot. def flush_pre_post(self) -> None: + if not self.needs_override_check: + if self.pre: + self._container[0:0] = self.pre + self.pre.clear() + if self.post: + self._container.extend(self.post) + self.post.clear() + return + new: T.List[str] = [] pre_flush_set: T.Set[str] = set() post_flush: T.Deque[str] = collections.deque() @@ -133,17 +144,15 @@ def flush_pre_post(self) -> None: #pre and post will overwrite every element that is in the container #only copy over args that are in _container but not in the post flush or pre flush set - if pre_flush_set or post_flush_set: - for a in self._container: - if a not in post_flush_set and a not in pre_flush_set: - new.append(a) - else: - new.extend(self._container) + for a in self._container: + if a not in post_flush_set and a not in pre_flush_set: + new.append(a) new.extend(post_flush) self._container = new self.pre.clear() self.post.clear() + self.needs_override_check = False def __iter__(self) -> T.Iterator[str]: # see also __init__, where this method is essentially inlined @@ -295,6 +304,8 @@ def __iadd__(self, args: T.Iterable[str]) -> 'CompilerArgs': # Argument already exists and adding a new instance is useless if arg in self._container or arg in self.pre or arg in self.post: continue + elif dedup is Dedup.OVERRIDDEN: + self.needs_override_check = True if self._should_prepend(arg): tmp_pre.appendleft(arg) else: From efd4193afb3ed5635909f2b88224c541aad978e7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 07:53:40 +0100 Subject: [PATCH 329/624] arglist: post is only appended to, make it a list self.post is only ever appended to on the right hand. However, it is then reversed twice in flush_pre_post(), by using "for a in reversed.post()" and appendleft() within the loop. It would be tempting to use appendleft() in __iadd__ to avoid the call to reversed(), but that is not a good idea because the loop of flush_pre_post() is part of a slow path. It's rather more important to use a fast extend-with-list-argument in the fast path where needs_override_check if False. For clarity, and to remove the temptation, make "post" a list instead of a deque. Signed-off-by: Paolo Bonzini --- mesonbuild/arglist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/arglist.py b/mesonbuild/arglist.py index 3d9bb722a933..4f4d18c55485 100644 --- a/mesonbuild/arglist.py +++ b/mesonbuild/arglist.py @@ -106,7 +106,7 @@ def __init__(self, compiler: T.Union['Compiler', 'StaticLinker'], self._container: T.List[str] = list(iterable) if iterable is not None else [] self.pre: T.Deque[str] = collections.deque() - self.post: T.Deque[str] = collections.deque() + self.post: T.List[str] = [] self.needs_override_check: bool = False # Flush the saved pre and post list into the _container list From d16d5397e57919b50513bab13519fdbba7815c63 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 1 Mar 2025 21:36:10 +0530 Subject: [PATCH 330/624] docs: Mention deprecation of path(), document full_path() --- docs/markdown/Python-module.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md index 1ea5e64853b3..66081762d795 100644 --- a/docs/markdown/Python-module.md +++ b/docs/markdown/Python-module.md @@ -87,10 +87,22 @@ added methods. str py_installation.path() ``` -*Added 0.50.0* +*(since 0.50.0)* -Works like the path method of other `ExternalProgram` objects. Was not -provided prior to 0.50.0 due to a bug. +*Deprecated in 0.55: use `full_path()` instead* + +Works like the path method of `ExternalProgram` objects. Was not provided prior +to 0.50.0 due to a bug. + +#### `full_path()` + +```meson +str py_installation.full_path() +``` + +*(since 0.55.0)* + +Works like the `full_path()` method of `ExternalProgram` objects: [[external_program.full_path]] #### `extension_module()` From 2ed0d5ed3bda03283fac5780794f43967e691749 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 28 Feb 2025 09:31:00 -0800 Subject: [PATCH 331/624] options: use an OptionKey for `get_default_for_b_option` We have the OptionKey in the one caller that exists already, and this allows us to do a hash lookup instead of a linear walk. --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/options.py | 12 ++++++------ unittests/optiontests.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b0bda85562d8..c8c05f0bde34 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1087,7 +1087,7 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], if self.coredata.optstore.is_base_option(optkey): # Due to backwards compatibility return the default # option for base options instead of erroring out. - return self.coredata.optstore.get_default_for_b_option(optname) + return self.coredata.optstore.get_default_for_b_option(optkey) else: if self.subproject: raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.') diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 25e2a9f43ff3..ab4fcbe0c8cc 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1140,13 +1140,13 @@ def get_option_from_meson_file(self, key: OptionKey) -> T.Tuple[AnyOptionType, E (value_object, value) = self.get_value_object_and_value_for(key) return (value_object, value) - def get_default_for_b_option(self, keyname: str) -> ElementaryOptionValues: - assert keyname.startswith('b_') + def get_default_for_b_option(self, key: OptionKey) -> ElementaryOptionValues: + assert self.is_base_option(key) from .compilers.compilers import BASE_OPTIONS - for bkey, bvalue in BASE_OPTIONS.items(): - if bkey.name == keyname: - return T.cast('ElementaryOptionValues', bvalue.default) - raise MesonBugException(f'Requested base option {keyname} which does not exist.') + try: + return T.cast('ElementaryOptionValues', 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] diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 28abca110f9a..971c247e54b9 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -207,5 +207,5 @@ def test_subproject_call_options(self): def test_b_default(self): optstore = OptionStore(False) - value = optstore.get_default_for_b_option('b_vscrt') + value = optstore.get_default_for_b_option(OptionKey('b_vscrt')) self.assertEqual(value, 'from_buildtype') From 294eca343d072a18a22d9a0cf932a8e97bdb16ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 28 Feb 2025 09:18:01 -0800 Subject: [PATCH 332/624] options: store the default value in the class Initially this is just used for getting builtin option default values, but it could be extended to show the default value of an option in the summary and in the introspection API. --- mesonbuild/options.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index ab4fcbe0c8cc..a84712783510 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -33,7 +33,7 @@ from . import mlog if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, Final, TypeAlias, TypedDict DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] AnyOptionType: TypeAlias = T.Union[ @@ -324,6 +324,8 @@ class UserOption(T.Generic[_T], HoldableObject): 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]: return [value] From daf4774cea27e223ca338310912734c05d0f4d54 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 09:21:51 -0800 Subject: [PATCH 333/624] coredata: subproject should be None for checking in base_options These options are stored as a "global" options, ie, subproject=None. Therefore, the call to `as_root()` could never be correct, and only the `k in base_options` check could ever be correct. Since these options are not supposed to be set here in the root project or in subprojects, we want to check for the global option and skip. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 8dbf16513b41..b581dc051ff2 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -696,7 +696,7 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # adding languages and setting backend. if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): continue - if self.optstore.is_base_option(k) and (k in base_options or k.as_root() in base_options): + if self.optstore.is_base_option(k) and k.evolve(subproject=None) in base_options: # set_options will report unknown base options continue options[k] = v From 71304684024bcdec102212117ff1ae3880c0318c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 12 Feb 2025 09:30:04 -0800 Subject: [PATCH 334/624] compilers: Remove the BaseOption type This class only served one purpose, to avoid typing the name of the option twice. Unfortunately the way it was implemented made getting the type checking right difficult, and required storing the same data twice. This patch replaces this approach with a dictionary comprehension that creates the OptionKey from the UserOption. This allows us to initialize a single dictionary once, avoid typing the name twice, delete lines of code, and get better type safety. As an added bonus, it means that the exported data from the module can be marked module constant, ie, ALL_CAPS. --- mesonbuild/compilers/__init__.py | 4 +- mesonbuild/compilers/compilers.py | 67 +++++++++++++------------------ mesonbuild/coredata.py | 6 +-- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 1d2d34c47fe3..98ec13faea20 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -7,7 +7,7 @@ 'RunResult', 'all_languages', - 'base_options', + 'BASE_OPTIONS', 'clib_langs', 'clink_langs', 'c_suffixes', @@ -48,7 +48,7 @@ Compiler, RunResult, all_languages, - base_options, + BASE_OPTIONS, clib_langs, clink_langs, c_suffixes, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 18535be42fef..5b640781ea0d 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -214,48 +214,35 @@ class CompileCheckMode(enum.Enum): MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd'] - -@dataclass -class BaseOption(T.Generic[_T]): - opt_type: T.Type[options.UserOption[_T]] - description: str - default: T.Any = None - choices: T.Any = None - - def init_option(self, name: OptionKey) -> options.UserOption[_T]: - keywords = {} - if self.choices: - keywords['choices'] = self.choices - return self.opt_type(name.name, self.description, self.default, **keywords) - - -BASE_OPTIONS: T.Mapping[OptionKey, BaseOption] = { - OptionKey('b_pch'): BaseOption(options.UserBooleanOption, 'Use precompiled headers', True), - OptionKey('b_lto'): BaseOption(options.UserBooleanOption, 'Use link time optimization', False), - OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', 0), - OptionKey('b_lto_mode'): BaseOption(options.UserComboOption, 'Select between different LTO modes.', 'default', - choices=['default', 'thin']), - OptionKey('b_thinlto_cache'): BaseOption(options.UserBooleanOption, 'Use LLVM ThinLTO caching for faster incremental builds', False), - OptionKey('b_thinlto_cache_dir'): BaseOption(options.UserStringOption, 'Directory to store ThinLTO cache objects', ''), - OptionKey('b_sanitize'): BaseOption(options.UserComboOption, 'Code sanitizer to use', 'none', - choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']), - OptionKey('b_lundef'): BaseOption(options.UserBooleanOption, 'Use -Wl,--no-undefined when linking', True), - OptionKey('b_asneeded'): BaseOption(options.UserBooleanOption, 'Use -Wl,--as-needed when linking', True), - OptionKey('b_pgo'): BaseOption(options.UserComboOption, 'Use profile guided optimization', 'off', - choices=['off', 'generate', 'use']), - OptionKey('b_coverage'): BaseOption(options.UserBooleanOption, 'Enable coverage tracking.', False), - OptionKey('b_colorout'): BaseOption(options.UserComboOption, 'Use colored output', 'always', - choices=['auto', 'always', 'never']), - OptionKey('b_ndebug'): BaseOption(options.UserComboOption, 'Disable asserts', 'false', choices=['true', 'false', 'if-release']), - OptionKey('b_staticpic'): BaseOption(options.UserBooleanOption, 'Build static libraries as position independent', True), - OptionKey('b_pie'): BaseOption(options.UserBooleanOption, 'Build executables as position independent', False), - OptionKey('b_bitcode'): BaseOption(options.UserBooleanOption, 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), - OptionKey('b_vscrt'): BaseOption(options.UserComboOption, 'VS run-time library type to use.', 'from_buildtype', - choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), +BASE_OPTIONS: T.Mapping[OptionKey, options.AnyOptionType] = { + OptionKey(o.name): o for o in T.cast('T.List[options.AnyOptionType]', [ + options.UserBooleanOption('b_pch', 'Use precompiled headers', True), + options.UserBooleanOption('b_lto', 'Use link time optimization', False), + options.UserIntegerOption('b_lto_threads', 'Use multiple threads for Link Time Optimization', 0), + options.UserComboOption('b_lto_mode', 'Select between different LTO modes.', 'default', choices=['default', 'thin']), + options.UserBooleanOption('b_thinlto_cache', 'Use LLVM ThinLTO caching for faster incremental builds', False), + options.UserStringOption('b_thinlto_cache_dir', 'Directory to store ThinLTO cache objects', ''), + options.UserComboOption( + 'b_sanitize', 'Code sanitizer to use', 'none', + choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']), + options.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), + options.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), + options.UserComboOption( + 'b_pgo', 'Use profile guided optimization', 'off', choices=['off', 'generate', 'use']), + options.UserBooleanOption('b_coverage', 'Enable coverage tracking.', False), + options.UserComboOption( + 'b_colorout', 'Use colored output', 'always', choices=['auto', 'always', 'never']), + options.UserComboOption( + 'b_ndebug', 'Disable asserts', 'false', choices=['true', 'false', 'if-release']), + options.UserBooleanOption('b_staticpic', 'Build static libraries as position independent', True), + options.UserBooleanOption('b_pie', 'Build executables as position independent', False), + options.UserBooleanOption('b_bitcode', 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), + options.UserComboOption( + 'b_vscrt', 'VS run-time library type to use.', 'from_buildtype', + choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), + ]) } -base_options = {key: base_opt.init_option(key) for key, base_opt in BASE_OPTIONS.items()} - def option_enabled(boptions: T.Set[OptionKey], target: 'BuildTarget', env: 'Environment', diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index b581dc051ff2..977e7b83f039 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -657,7 +657,7 @@ def remove_sp_options(self, U_args) -> bool: return dirty def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None: - from .compilers import base_options + from .compilers import BASE_OPTIONS # Main project can set default options on subprojects, but subprojects # can only set default options on themselves. @@ -696,7 +696,7 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # adding languages and setting backend. if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): continue - if self.optstore.is_base_option(k) and k.evolve(subproject=None) in base_options: + if self.optstore.is_base_option(k) and k.evolve(subproject=None) in BASE_OPTIONS: # set_options will report unknown base options continue options[k] = v @@ -744,7 +744,7 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, else: skey = key if skey not in self.optstore: - self.optstore.add_system_option(skey, copy.deepcopy(compilers.base_options[key])) + self.optstore.add_system_option(skey, copy.deepcopy(compilers.BASE_OPTIONS[key])) if skey in env.options: self.optstore.set_value(skey, env.options[skey]) elif subproject and key in env.options: From c2d795735fa1c46c54d6aed4d4a30f36a1f853cb Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 3 Mar 2025 11:50:12 -0500 Subject: [PATCH 335/624] docs: document release process and security keys Bug: https://github.com/mesonbuild/meson/issues/13446 --- docs/markdown/Releasing.md | 24 ++++++++++++++++++++++++ docs/sitemap.txt | 1 + 2 files changed, 25 insertions(+) create mode 100644 docs/markdown/Releasing.md diff --git a/docs/markdown/Releasing.md b/docs/markdown/Releasing.md new file mode 100644 index 000000000000..488ca91a12c5 --- /dev/null +++ b/docs/markdown/Releasing.md @@ -0,0 +1,24 @@ +--- +short-description: Release Policy +... + +# Releasing a new Meson version + +For each new meson release, several different artifacts are created: + +- Github Releases: + - canonical source tarball, PGP signed: `packaging/builddist.sh` + - Windows installer: `packaging/createmsi.py` + - macOS installer: `packaging/createpkg.py` +- PyPI: + - pip install-compatible release, as produced by builddist.sh +- Debian package: `packaging/mpackage.py` + +# Release team + + +- Jussi Pakkanen. PGP key: [19E2D6D9B46D8DAA6288F877C24E631BABB1FE70](https://keyserver.ubuntu.com/pks/lookup?search=0x19E2D6D9B46D8DAA6288F877C24E631BABB1FE70&op=index) +- Eli Schwartz. PGP key: [BD27B07A5EF45C2ADAF70E0484818A6819AF4A9B](https://keyserver.ubuntu.com/pks/lookup?search=0xBD27B07A5EF45C2ADAF70E0484818A6819AF4A9B&op=index) +- Dylan Baker. PGP key: [71C4B75620BC75708B4BDB254C95FAAB3EB073EC](https://keyserver.ubuntu.com/pks/lookup?search=0x71C4B75620BC75708B4BDB254C95FAAB3EB073EC&op=index) + +The default release manager for new versions of Meson is Jussi Pakkanen. Starting with meson 1.8.0, the release team has been expanded with fallback options to reduce the bus factor, but but will continue to be done by Jussi when possible. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index b5e0f8157cdb..910df48ee158 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -147,5 +147,6 @@ index.md Contributing.md Yaml-RefMan.md MesonCI.md + Releasing.md legal.md Videos.md From 2073cf3c884c21c2a6951d3f5111c5c303cd60ef Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 09:38:28 -0800 Subject: [PATCH 336/624] options: Fix nullability of OptionKey.subproject As a cleanup, this causes the cache to use the same tuple layout that the `_to_tuple` method does, which makes it easier to ensure everything is right. --- mesonbuild/options.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index a84712783510..f81683eca52a 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -42,6 +42,8 @@ 'UserStringOption', 'UserUmaskOption'] ElementaryOptionValues: TypeAlias = T.Union[str, int, bool, T.List[str]] + _OptionKeyTuple: TypeAlias = T.Tuple[T.Optional[str], MachineChoice, str] + class ArgparseKWs(TypedDict, total=False): action: str @@ -102,7 +104,7 @@ class ArgparseKWs(TypedDict, total=False): } _BAD_VALUE = 'Qwert Zuiopü' -_optionkey_cache: T.Dict[T.Tuple[str, str, MachineChoice], OptionKey] = {} +_optionkey_cache: T.Dict[_OptionKeyTuple, OptionKey] = {} class OptionKey: @@ -131,7 +133,7 @@ def __new__(cls, if not name: return super().__new__(cls) # for unpickling, do not cache now - tuple_ = (name, subproject, machine) + tuple_: _OptionKeyTuple = (subproject, machine, name) try: return _optionkey_cache[tuple_] except KeyError: @@ -168,12 +170,12 @@ def __getstate__(self) -> T.Dict[str, T.Any]: def __setstate__(self, state: T.Dict[str, T.Any]) -> None: # Here, the object is created using __new__() self._init(**state) - _optionkey_cache[(self.name, self.subproject, self.machine)] = self + _optionkey_cache[self._to_tuple()] = self def __hash__(self) -> int: return self._hash - def _to_tuple(self) -> T.Tuple[str, MachineChoice, str]: + def _to_tuple(self) -> _OptionKeyTuple: return (self.subproject, self.machine, self.name) def __eq__(self, other: object) -> bool: From 84f64b2378807837eecfdc42feb5ff83446433ff Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 12:06:28 -0800 Subject: [PATCH 337/624] msetup: remove bad warning about unused options This is just a bad warning, while it *could* give the user useful information, it often doesn't since it can get values to warn about from: - environment variables - the command line - machine files - `project(default_options : ...)` - `subproject(default_options : ...)` - `dependency(default_options : ...)` The problem of course is that user may have no control over these values. 3 of them are hardcoded into the meson.build files, so the user can't do anything about them. And there are legitimate reasons to have unused values in those, like setting defaults for a language only used on specific platforms. Environment variables may be set by the distro (NixOS sets them for any enabled language, so just having a D compiler causes `DFLAGS` to be set, for example). They likely don't want to special case "only set the environment variables if the project is going to use them". For machine files it limits the utility of the files, since the user needs to be sure that they don't include any options that wont be used. Finally, the command line could be altered by wrapper scripts, or simply programmed to insert options that *may* be used but aren't required. like setting `objc_args` regardless of whether ObjectivC bindings are generated. However, passing completely unknown builtin options should be an error, as it was before the optionrefactor --- mesonbuild/msetup.py | 19 ++++--------------- unittests/platformagnostictests.py | 13 ++++++++----- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 6753b02559ec..26e92ee5de34 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2018 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -189,9 +189,9 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - 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: + from mesonbuild.compilers import BASE_OPTIONS pending = coredata.optstore.pending_project_options errlist: T.List[str] = [] - permitlist: T.List[str] = [] for opt in pending: # Due to backwards compatibility setting build options in non-cross # builds is permitted and is a no-op. This should be made @@ -203,11 +203,9 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: if opt.subproject and opt.subproject not in all_subprojects: continue if coredata.optstore.is_compiler_option(opt): - permitlist.append(opt.name) continue - # Ditto for base options. - if coredata.optstore.is_base_option(opt): - permitlist.append(opt.name) + if (coredata.optstore.is_base_option(opt) and + opt.evolve(subproject=None, machine=mesonlib.MachineChoice.HOST) in BASE_OPTIONS): continue keystr = str(opt) if keystr in cmd_line_options: @@ -215,15 +213,6 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: if errlist: errstr = ', '.join(errlist) raise MesonException(f'Unknown options: {errstr}') - if permitlist: - # This is needed due to backwards compatibility. - # It was permitted to define some command line options that - # were not used. This can be seen as a bug, since - # if you define -Db_lto but the compiler class does not - # support it, this option gets silently swallowed. - # So at least print a message about it. - optstr = ','.join(permitlist) - mlog.warning(f'The following command line option(s) were not used: {optstr}', fatal=False) coredata.optstore.clear_pending() diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 990936e85ed5..5707553ba318 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2021 The Meson development team -# Copyright © 2024 Intel Corporation +# Copyright © 2024-2025 Intel Corporation from __future__ import annotations import json @@ -415,11 +415,14 @@ def test_reconfigure_base_options(self): def test_setup_with_unknown_option(self): testdir = os.path.join(self.common_test_dir, '1 trivial') - out = self.init(testdir, extra_args=['--wipe', '-Dnot_an_option=1'], allow_fail=True) - self.assertIn('ERROR: Unknown options: "not_an_option"', out) + 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) - out = self.init(testdir, extra_args=['--wipe', '-Db_not_an_option=1']) - self.assertIn('WARNING: The following command line option(s) were not used: b_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) def test_configure_new_option(self) -> None: From a2007d031cf421fabc9158ce393ea7f1063d174d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 09:56:05 -0800 Subject: [PATCH 338/624] options: remove get_option_from_meson_file This is just a wrapper around `get_value_object_and_value_for`, doing needless work of unpacking a tuple, then repacking it. --- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/options.py | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c8c05f0bde34..1ff6aa4b1d58 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1082,7 +1082,7 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], try: optkey = options.OptionKey(optname, self.subproject) - value_object, value = self.coredata.optstore.get_option_from_meson_file(optkey) + value_object, value = self.coredata.optstore.get_value_object_and_value_for(optkey) except KeyError: if self.coredata.optstore.is_base_option(optkey): # Due to backwards compatibility return the default diff --git a/mesonbuild/options.py b/mesonbuild/options.py index f81683eca52a..4a4d40f22d57 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1139,11 +1139,6 @@ def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: key = self.ensure_and_validate_key(key) return self.options[key] - def get_option_from_meson_file(self, key: OptionKey) -> T.Tuple[AnyOptionType, ElementaryOptionValues]: - assert isinstance(key, OptionKey) - (value_object, value) = self.get_value_object_and_value_for(key) - return (value_object, value) - def get_default_for_b_option(self, key: OptionKey) -> ElementaryOptionValues: assert self.is_base_option(key) from .compilers.compilers import BASE_OPTIONS From e2d3b1c278b7a9dfa82b9047c810164bce9634c6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 09:59:20 -0800 Subject: [PATCH 339/624] OptionStore: remove unused build_options attribute --- mesonbuild/options.py | 1 - unittests/optiontests.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 4a4d40f22d57..c3f2ff36adab 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -813,7 +813,6 @@ def __init__(self, is_cross: bool) -> None: self.module_options: T.Set[OptionKey] = set() from .compilers import all_languages self.all_languages = set(all_languages) - self.build_options = None self.project_options = set() self.augments: T.Dict[str, str] = {} self.pending_project_options: T.Dict[OptionKey, str] = {} diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 971c247e54b9..6b8eb4a855d2 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -7,9 +7,7 @@ def num_options(store: OptionStore) -> int: - basic = len(store.options) - build = len(store.build_options) if store.build_options else 0 - return basic + build + return len(store.options) class OptionTests(unittest.TestCase): From b1eab89a0c71181f1d90d231b51aa711597463d0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 08:07:02 -0800 Subject: [PATCH 340/624] coredata: delete a bunch of unused methods Added by the optionrefactor changes, but not used --- mesonbuild/coredata.py | 49 ------------------------------------------ mesonbuild/mconf.py | 6 ------ 2 files changed, 55 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 977e7b83f039..8fd8e31cab13 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -257,7 +257,6 @@ def __init__(self, cmd_options: SharedCMDOptions, scratch_dir: str, meson_comman self.meson_command = meson_command self.target_guids = {} self.version = version - self.sp_option_overrides: T.Dict[str, str] = {} self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross') self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict()) self.optstore = options.OptionStore(self.is_cross_build()) @@ -608,54 +607,6 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' return dirty - def can_set_per_sb(self, keystr): - return True - - def set_options_from_configure_strings(self, D_args) -> bool: - dirty = False - for entry in D_args: - if '=' not in entry: - raise MesonException(f'A -D argument must be of form "name=value" instead of {entry}') - key, val = entry.split('=', 1) - if key in self.sp_option_overrides: - self.sp_option_overrides[key] = val - dirty = True - else: - dirty |= self.set_options({OptionKey(key): val}) - return dirty - - def create_sp_options(self, A_args) -> bool: - if A_args is None: - return False - dirty = False - for entry in A_args: - keystr, valstr = entry.split('=', 1) - if ':' not in keystr: - raise MesonException(f'Option to add override has no subproject: {entry}') - if not self.can_set_per_sb(keystr): - raise MesonException(f'Option {keystr} can not be set per subproject.') - if keystr in self.sp_option_overrides: - raise MesonException(f'Override {keystr} already exists.') - key = self.optstore.split_keystring(keystr) - original_key = key.copy_with(subproject=None) - if not self.optstore.has_option(original_key): - raise MesonException('Tried to override a nonexisting key.') - self.sp_option_overrides[keystr] = valstr - dirty = True - return dirty - - def remove_sp_options(self, U_args) -> bool: - dirty = False - if U_args is None: - return False - for entry in U_args: - if entry in self.sp_option_overrides: - del self.sp_option_overrides[entry] - dirty = True - else: - pass # Deleting a non-existing key ok, I guess? - return dirty - def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None: from .compilers import BASE_OPTIONS diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 3fb759bf8da0..0811062f371e 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -337,12 +337,6 @@ def print_nondefault_buildtype_options(self) -> None: for m in mismatching: mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}') - def print_sp_overrides(self) -> None: - if self.coredata.sp_option_overrides: - mlog.log('\nThe folowing options have per-subproject overrides:') - for k, v in self.coredata.sp_option_overrides.items(): - mlog.log(f'{k:21}{v:10}') - def print_augments(self) -> None: if self.coredata.optstore.augments: mlog.log('\nCurrently set option augments:') From f2e57cc2ca62b2c0eeba324b3e6955eeab9d3d2c Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 3 Mar 2025 18:59:44 -0500 Subject: [PATCH 341/624] docs: update dead link in Users.md The project in question still exists but no longer has a dedicated domain; instead it uses a group website in theory for multiple projects hosted by the same group (but in practice still just the one). Apparently changed almost 2 years ago. See: https://github.com/theimpossibleastronaut/rmw/commit/0f53e40861c46916597990bfe2c95ca3e5cfb0a3 --- docs/markdown/Users.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 0a97bd5b3b29..9014510f9784 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -152,7 +152,7 @@ format files - [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://remove-to-waste.info), safe-remove utility for the command line + - [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 From ec43d02577015694a7906c3d8e99cd418d8fc5ee Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 12:41:31 -0800 Subject: [PATCH 342/624] coredata: fix arguments that are swapped This should be `(key, value, is_first_invocation)`, but is instance `(key, is_first_invocation, value)` --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 8fd8e31cab13..efcaf935af7a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -552,7 +552,7 @@ def copy_build_options_from_regular_ones(self, shut_up_pylint: bool = True) -> b 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_value(k, True, o.value) + dirty |= self.optstore.set_value(k, o.value, True) for bk, bv in self.optstore.items(): if bk.machine is MachineChoice.BUILD: hk = bk.as_host() From 751e8f1530dcf411f5e4d8acc758635316a77bcb Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 07:43:12 -0800 Subject: [PATCH 343/624] coredata: fix handling of prefix Which was improperly updated by the option store refactor. Fixes: #14329 --- mesonbuild/coredata.py | 5 ++--- test cases/linuxlike/3 linker script/meson.build | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index efcaf935af7a..7bc3cc21896e 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -571,12 +571,11 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' # Set prefix first because it's needed to sanitize other options pfk = OptionKey('prefix') if pfk in opts_to_set: - prefix = self.sanitize_prefix(opts_to_set[pfk]) + 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) - tmpkey = options.convert_oldkey(key) - dirty |= self.optstore.set_option(tmpkey, val) + dirty |= self.optstore.set_option(key, val) unknown_options: T.List[OptionKey] = [] for k, v in opts_to_set.items(): diff --git a/test cases/linuxlike/3 linker script/meson.build b/test cases/linuxlike/3 linker script/meson.build index 660858792cb2..634b70b2cbc4 100644 --- a/test cases/linuxlike/3 linker script/meson.build +++ b/test cases/linuxlike/3 linker script/meson.build @@ -1,4 +1,4 @@ -project('linker script', 'c') +project('linker script', 'c', default_options : {'prefix': '/tmp'}) # Solaris 11.4 ld supports --version-script only when you also specify # -z gnu-version-script-compat From f8c3f9e909e0743f6504b8878b71170154128fac Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Fri, 16 Aug 2024 21:07:38 +0300 Subject: [PATCH 344/624] dependencies/dub: First try to describe local project The current approach of determining dub dependencies is by specifying a name and, optionally, a version. Dub will then be called to generate a json summary of the package and code in meson will parse that and extract relevant information. This can be insufficient because dub packages can provide multiple configurations for multiple use-cases, examples include providing a configuration for an executable and a configuration for a library. As a practical example, the dub package itself provides an application configuration and multiple library configurations, the json description of dub will, by default, be for the application configuration which will make dub as a library unusable in meson. This can be solved without modifying the meson build interface by having dub describe the entire local project and collecting dependencies information from that. This way dub will generate information based on the project's 'dub.json' file, which is free to require dependencies in any way accepted by dub, by specifying configurations, by modifying compilation flags etc. This is all transparent to meson as dub's main purpose is to provide a path to the library file generated by the dependency in addition to other command-line arguments for the compiler. This change will, however, require that projects that want to build with meson also provided a 'dub.json' file in which dependency information is recorded. Failure to do so will not break existing projects that didn't use a 'dub.json', but they will be limited to what the previous implementation offered. Projects that already have a 'dub.json' should be fine, so long as the file is valid and the information in it matches the one in 'meson.build'. For example for a 'dependency()' call in 'meson.build' that dependency must exist in 'dub.json', otherwise the call will now fail when it worked previously. Using a 'dub.json' also has as a consequence that the version of the dependencies that are found are the ones specified in 'dub.selections.json', which can be helpful for projects that already provide a 'dub.json' in addition to 'meson.build' to de-duplicate code. In terms of other code changes: - multiple version requirements for a dub dependency now work, though they can only be used when a 'dub.json' is present in which case the version of dependencies is already pinned by 'dub.selections.json' - the 'd/11 dub' test case has been changed to auto-generate the 'dub.json' config outside of the source directory, as the auto-generated file triggers warning when parsed by dub, which upsets the new code as the warnings interfere with the legitimate output. Signed-off-by: Andrei Horodniceanu --- ci/ciimage/opensuse/install.sh | 11 +- ci/ciimage/ubuntu-rolling/install.sh | 11 +- mesonbuild/dependencies/dub.py | 151 +++++++++++------- test cases/d/11 dub/meson.build | 4 +- .../d/17 dub and meson project/.gitignore | 2 + .../d/17 dub and meson project/dub.json | 11 ++ .../dub.selections.json | 7 + .../d/17 dub and meson project/meson.build | 32 ++++ .../multi-configuration/.gitignore | 2 + .../multi-configuration/dub.json | 14 ++ .../multi-configuration/dub.selections.json | 5 + .../multi-configuration/source/app.d | 1 + .../multi-configuration/source/foo.d | 0 .../d/17 dub and meson project/source/app.d | 1 + .../d/17 dub and meson project/x/y/z/dub.json | 6 + .../x/y/z/dub.selections.json | 6 + .../x/y/z/meson.build | 6 + .../133 dub missing dependency/dub.json | 3 + .../dub.selections.json | 5 + .../133 dub missing dependency/meson.build | 17 ++ .../133 dub missing dependency/source/app.d | 1 + .../133 dub missing dependency/test.json | 8 + 22 files changed, 231 insertions(+), 73 deletions(-) create mode 100644 test cases/d/17 dub and meson project/.gitignore create mode 100644 test cases/d/17 dub and meson project/dub.json create mode 100644 test cases/d/17 dub and meson project/dub.selections.json create mode 100644 test cases/d/17 dub and meson project/meson.build create mode 100644 test cases/d/17 dub and meson project/multi-configuration/.gitignore create mode 100644 test cases/d/17 dub and meson project/multi-configuration/dub.json create mode 100644 test cases/d/17 dub and meson project/multi-configuration/dub.selections.json create mode 100644 test cases/d/17 dub and meson project/multi-configuration/source/app.d create mode 100644 test cases/d/17 dub and meson project/multi-configuration/source/foo.d create mode 100644 test cases/d/17 dub and meson project/source/app.d create mode 100644 test cases/d/17 dub and meson project/x/y/z/dub.json create mode 100644 test cases/d/17 dub and meson project/x/y/z/dub.selections.json create mode 100644 test cases/d/17 dub and meson project/x/y/z/meson.build create mode 100644 test cases/failing/133 dub missing dependency/dub.json create mode 100644 test cases/failing/133 dub missing dependency/dub.selections.json create mode 100644 test cases/failing/133 dub missing dependency/meson.build create mode 100644 test cases/failing/133 dub missing dependency/source/app.d create mode 100644 test cases/failing/133 dub missing dependency/test.json diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 5ee1f6435f42..43b0f7c9bfed 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -42,11 +42,12 @@ echo 'ulimit -n -S 10000' >> /ci/env_vars.sh source /ci/env_vars.sh -dub_fetch urld -dub build --deep urld --arch=x86_64 --compiler=dmd --build=debug -dub_fetch dubtestproject -dub build dubtestproject:test1 --compiler=dmd -dub build dubtestproject:test2 --compiler=dmd +dub_fetch dubtestproject@1.2.0 +dub build dubtestproject:test1 --compiler=dmd --arch=x86_64 +dub build dubtestproject:test2 --compiler=dmd --arch=x86_64 +dub build dubtestproject:test3 --compiler=dmd --arch=x86_64 +dub_fetch urld@3.0.0 +dub build urld --compiler=dmd --arch=x86_64 # Cleanup zypper --non-interactive clean --all diff --git a/ci/ciimage/ubuntu-rolling/install.sh b/ci/ciimage/ubuntu-rolling/install.sh index 212ed020ace5..1c0891c239a4 100755 --- a/ci/ciimage/ubuntu-rolling/install.sh +++ b/ci/ciimage/ubuntu-rolling/install.sh @@ -50,11 +50,12 @@ install_python_packages hotdoc echo 'ulimit -n -S 10000' >> /ci/env_vars.sh ulimit -n -S 10000 # dub stuff -dub_fetch urld -dub build --deep urld --arch=x86_64 --compiler=gdc --build=debug -dub_fetch dubtestproject -dub build dubtestproject:test1 --compiler=ldc2 -dub build dubtestproject:test2 --compiler=ldc2 +dub_fetch dubtestproject@1.2.0 +dub build dubtestproject:test1 --compiler=ldc2 --arch=x86_64 +dub build dubtestproject:test2 --compiler=ldc2 --arch=x86_64 +dub build dubtestproject:test3 --compiler=gdc --arch=x86_64 +dub_fetch urld@3.0.0 +dub build urld --compiler=gdc --arch=x86_64 # Remove debian version of Rust and install latest with rustup. # This is needed to get the cross toolchain as well. diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index 009e4f46ffbb..6b6d2830675f 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -5,10 +5,11 @@ from .base import ExternalDependency, DependencyException, DependencyTypeName from .pkgconfig import PkgConfigDependency -from ..mesonlib import (Popen_safe, join_args, version_compare) +from ..mesonlib import (Popen_safe, join_args, version_compare, version_compare_many) from ..options import OptionKey from ..programs import ExternalProgram from .. import mlog +from enum import Enum import re import os import json @@ -56,6 +57,10 @@ class FindTargetEntry(TypedDict): search: str artifactPath: str +class DubDescriptionSource(Enum): + Local = 'local' + External = 'external' + class DubDependency(ExternalDependency): # dub program and version class_dubbin: T.Optional[T.Tuple[ExternalProgram, str]] = None @@ -87,14 +92,13 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. if DubDependency.class_dubbin is None: if self.required: raise DependencyException('DUB not found.') - self.is_found = False return (self.dubbin, dubver) = DubDependency.class_dubbin # pylint: disable=unpacking-non-sequence assert isinstance(self.dubbin, ExternalProgram) - # Check if Dub's compatibility with Meson + # Check Dub's compatibility with Meson self._search_in_cache = version_compare(dubver, '<=1.31.1') self._use_cache_describe = version_compare(dubver, '>=1.35.0') self._dub_has_build_deep = version_compare(dubver, '>=1.35.0') @@ -108,20 +112,11 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. mlog.warning(f'DUB dependency {name} not found because Dub {dubver} ' "is not compatible with Meson. (Can't locate artifacts in DUB's cache)." ' Upgrade to Dub >= 1.35') - self.is_found = False return mlog.debug('Determining dependency {!r} with DUB executable ' '{!r}'.format(name, self.dubbin.get_path())) - # if an explicit version spec was stated, use this when querying Dub - main_pack_spec = name - if 'version' in kwargs: - version_spec = kwargs['version'] - if isinstance(version_spec, list): - version_spec = " ".join(version_spec) - main_pack_spec = f'{name}@{version_spec}' - # we need to know the target architecture dub_arch = self.compiler.arch @@ -135,37 +130,11 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. elif dub_buildtype == 'minsize': dub_buildtype = 'release' - # A command that might be useful in case of missing DUB package - def dub_build_deep_command() -> str: - if self._dub_has_build_deep: - cmd = ['dub', 'build', '--deep'] - else: - cmd = ['dub', 'run', '--yes', 'dub-build-deep', '--'] - - return join_args(cmd + [ - main_pack_spec, - '--arch=' + dub_arch, - '--compiler=' + self.compiler.get_exelist()[-1], - '--build=' + dub_buildtype - ]) - - # Ask dub for the package - describe_cmd = [ - 'describe', main_pack_spec, '--arch=' + dub_arch, - '--build=' + dub_buildtype, '--compiler=' + self.compiler.get_exelist()[-1] - ] - ret, res, err = self._call_dubbin(describe_cmd) - - if ret != 0: - mlog.debug('DUB describe failed: ' + err) - if 'locally' in err: - mlog.error(mlog.bold(main_pack_spec), 'is not present locally. You may try the following command:') - mlog.log(mlog.bold(dub_build_deep_command())) - self.is_found = False + result = self._get_dub_description(dub_arch, dub_buildtype) + if result is None: return - + description, build_cmd, description_source = result dub_comp_id = self._ID_MAP[self.compiler.get_id()] - description: DubDescription = json.loads(res) self.compile_args = [] self.link_args = self.raw_link_args = [] @@ -204,7 +173,7 @@ def find_package_target(pkg: DubPackDesc) -> bool: mlog.error(mlog.bold(pack_id), 'not found') mlog.log('You may try the following command to install the necessary DUB libraries:') - mlog.log(mlog.bold(dub_build_deep_command())) + mlog.log(mlog.bold(build_cmd)) return False @@ -223,33 +192,44 @@ def find_package_target(pkg: DubPackDesc) -> bool: # 4. Add other build settings (imports, versions etc.) # 1 - self.is_found = False packages: T.Dict[str, DubPackDesc] = {} + found_it = False for pkg in description['packages']: packages[pkg['name']] = pkg if not pkg['active']: continue - if pkg['targetType'] == 'dynamicLibrary': - mlog.error('DUB dynamic library dependencies are not supported.') - self.is_found = False - return - # check that the main dependency is indeed a library if pkg['name'] == name: - self.is_found = True - if pkg['targetType'] not in ['library', 'sourceLibrary', 'staticLibrary']: - mlog.error(mlog.bold(name), "found but it isn't a library") - self.is_found = False + mlog.error(mlog.bold(name), "found but it isn't a static library, it is:", + pkg['targetType']) return + if self.version_reqs is not None: + ver = pkg['version'] + if not version_compare_many(ver, self.version_reqs)[0]: + mlog.error(mlog.bold(f'{name}@{ver}'), + 'does not satisfy all version requirements of:', + ' '.join(self.version_reqs)) + return + + found_it = True self.version = pkg['version'] self.pkg = pkg + if not found_it: + mlog.error(f'Could not find {name} in DUB description.') + if description_source is DubDescriptionSource.Local: + mlog.log('Make sure that the dependency is registered for your dub project by running:') + mlog.log(mlog.bold(f'dub add {name}')) + elif description_source is DubDescriptionSource.External: + # `dub describe pkg` did not contain the pkg + raise RuntimeError(f'`dub describe` succeeded but it does not contains {name}') + return + if name not in targets: - self.is_found = False if self.pkg['targetType'] == 'sourceLibrary': # source libraries have no associated targets, # but some build settings like import folders must be found from the package object. @@ -258,10 +238,7 @@ def find_package_target(pkg: DubPackDesc) -> bool: # (See openssl DUB package for example of sourceLibrary) mlog.error('DUB targets of type', mlog.bold('sourceLibrary'), 'are not supported.') else: - mlog.error('Could not find target description for', mlog.bold(main_pack_spec)) - - if not self.is_found: - mlog.error(f'Could not find {name} in DUB description') + mlog.error('Could not find target description for', mlog.bold(self.name)) return # Current impl only supports static libraries @@ -269,19 +246,17 @@ def find_package_target(pkg: DubPackDesc) -> bool: # 2 if not find_package_target(self.pkg): - self.is_found = False return # 3 for link_dep in targets[name]['linkDependencies']: pkg = packages[link_dep] if not find_package_target(pkg): - self.is_found = False return if show_buildtype_warning: mlog.log('If it is not suitable, try the following command and reconfigure Meson with', mlog.bold('--clearcache')) - mlog.log(mlog.bold(dub_build_deep_command())) + mlog.log(mlog.bold(build_cmd)) # 4 bs = targets[name]['buildSettings'] @@ -345,6 +320,60 @@ def find_package_target(pkg: DubPackDesc) -> bool: # fallback self.link_args.append('-l'+lib) + self.is_found = True + + # Get the dub description needed to resolve the dependency and a + # build command that can be used to build the dependency in case it is + # not present. + def _get_dub_description(self, dub_arch: str, dub_buildtype: str) -> T.Optional[T.Tuple[DubDescription, str, DubDescriptionSource]]: + def get_build_command() -> T.List[str]: + if self._dub_has_build_deep: + cmd = ['dub', 'build', '--deep'] + else: + cmd = ['dub', 'run', '--yes', 'dub-build-deep', '--'] + + return cmd + [ + '--arch=' + dub_arch, + '--compiler=' + self.compiler.get_exelist()[-1], + '--build=' + dub_buildtype, + ] + + # Ask dub for the package + describe_cmd = [ + 'describe', '--arch=' + dub_arch, + '--build=' + dub_buildtype, '--compiler=' + self.compiler.get_exelist()[-1] + ] + helper_build = join_args(get_build_command()) + source = DubDescriptionSource.Local + ret, res, err = self._call_dubbin(describe_cmd) + if ret == 0: + return (json.loads(res), helper_build, source) + + pack_spec = self.name + if self.version_reqs is not None: + if len(self.version_reqs) > 1: + mlog.error('Multiple version requirements are not supported for raw dub dependencies.') + mlog.error("Please specify only an exact version like '1.2.3'") + raise DependencyException('Multiple version requirements are not solvable for raw dub depencies') + elif len(self.version_reqs) == 1: + pack_spec += '@' + self.version_reqs[0] + + describe_cmd = [ + 'describe', pack_spec, '--arch=' + dub_arch, + '--build=' + dub_buildtype, '--compiler=' + self.compiler.get_exelist()[-1] + ] + helper_build = join_args(get_build_command() + [pack_spec]) + source = DubDescriptionSource.External + ret, res, err = self._call_dubbin(describe_cmd) + if ret == 0: + return (json.loads(res), helper_build, source) + + mlog.debug('DUB describe failed: ' + err) + if 'locally' in err: + mlog.error(mlog.bold(pack_spec), 'is not present locally. You may try the following command:') + mlog.log(mlog.bold(helper_build)) + return None + # This function finds the target of the provided JSON package, built for the right # compiler, architecture, configuration... # It returns (target|None, {compatibilities}) @@ -469,7 +498,7 @@ def _get_comp_versions_to_find(self, dub_comp_id: str) -> T.List[str]: def _call_dubbin(self, args: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str, str]: assert isinstance(self.dubbin, ExternalProgram) - p, out, err = Popen_safe(self.dubbin.get_command() + args, env=env) + p, out, err = Popen_safe(self.dubbin.get_command() + args, env=env, cwd=self.env.get_source_dir()) return p.returncode, out.strip(), err.strip() def _call_compbin(self, args: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str, str]: diff --git a/test cases/d/11 dub/meson.build b/test cases/d/11 dub/meson.build index cfdb7fa59098..fe79e046eb78 100644 --- a/test cases/d/11 dub/meson.build +++ b/test cases/d/11 dub/meson.build @@ -17,7 +17,7 @@ test('test urld', test_exe) # If you want meson to generate/update a dub.json file dlang = import('dlang') -dlang.generate_dub_file(meson.project_name().to_lower(), meson.source_root(), +dlang.generate_dub_file(meson.project_name().to_lower(), meson.build_root(), authors: 'Meson Team', description: 'Test executable', copyright: 'Copyright © 2018, Meson Team', @@ -25,4 +25,4 @@ dlang.generate_dub_file(meson.project_name().to_lower(), meson.source_root(), sourceFiles: 'test.d', targetType: 'executable', dependencies: urld_dep -) \ No newline at end of file +) diff --git a/test cases/d/17 dub and meson project/.gitignore b/test cases/d/17 dub and meson project/.gitignore new file mode 100644 index 000000000000..0036ff3946de --- /dev/null +++ b/test cases/d/17 dub and meson project/.gitignore @@ -0,0 +1,2 @@ +17-dub-meson-project* +lib17-dub-meson-project* diff --git a/test cases/d/17 dub and meson project/dub.json b/test cases/d/17 dub and meson project/dub.json new file mode 100644 index 000000000000..f04e51b551c4 --- /dev/null +++ b/test cases/d/17 dub and meson project/dub.json @@ -0,0 +1,11 @@ +{ + "name": "17-dub-meson-project", + "dependencies": { + "urld": ">=3.0.0 <3.0.1", + "dubtestproject:test3": "1.2.0", + ":multi-configuration": "*" + }, + "subPackages": [ + "multi-configuration" + ] +} diff --git a/test cases/d/17 dub and meson project/dub.selections.json b/test cases/d/17 dub and meson project/dub.selections.json new file mode 100644 index 000000000000..ad72f31683c5 --- /dev/null +++ b/test cases/d/17 dub and meson project/dub.selections.json @@ -0,0 +1,7 @@ +{ + "fileVersion": 1, + "versions": { + "dubtestproject": "1.2.0", + "urld": "3.0.0" + } +} diff --git a/test cases/d/17 dub and meson project/meson.build b/test cases/d/17 dub and meson project/meson.build new file mode 100644 index 000000000000..9070e6c720ff --- /dev/null +++ b/test cases/d/17 dub and meson project/meson.build @@ -0,0 +1,32 @@ +project('Meson integration with dub.json', 'd') + +dub_exe = find_program('dub', required : false) +if not dub_exe.found() + error('MESON_SKIP_TEST: Dub not found') +endif + +dub_ver = dub_exe.version() +if not dub_ver.version_compare('>=1.35.0') + error('MESON_SKIP_TEST: test requires dub >=1.35.0') +endif + +# Multiple versions supported +urld = dependency('urld', method: 'dub', version: [ '>=3.0.0', '<3.0.1' ]) + +# The version we got is the one in dub.selections.json +version = urld.version() +if version != '3.0.0' + error(f'Expected urld version to be the one selected in dub.selections.json but got @version@') +endif + +# dependency calls from subdirectories respect meson.project_source_root()/dub.selections.json +subdir('x/y/z') + +# dependencies respect their configuration selected in dub.json +run_command(dub_exe, 'build', '--deep', ':multi-configuration', + '--compiler', meson.get_compiler('d').cmd_array()[0], + '--arch', host_machine.cpu_family(), + '--root', meson.project_source_root(), + '--config', 'lib', + check: true) +found = dependency('17-dub-meson-project:multi-configuration', method: 'dub') diff --git a/test cases/d/17 dub and meson project/multi-configuration/.gitignore b/test cases/d/17 dub and meson project/multi-configuration/.gitignore new file mode 100644 index 000000000000..61763ee20b1b --- /dev/null +++ b/test cases/d/17 dub and meson project/multi-configuration/.gitignore @@ -0,0 +1,2 @@ +libmulti-configuration* +multi-configuration* diff --git a/test cases/d/17 dub and meson project/multi-configuration/dub.json b/test cases/d/17 dub and meson project/multi-configuration/dub.json new file mode 100644 index 000000000000..373176d9ecc8 --- /dev/null +++ b/test cases/d/17 dub and meson project/multi-configuration/dub.json @@ -0,0 +1,14 @@ +{ + "name": "multi-configuration", + "configurations": { + "app": { + "targetType": "executable" + }, + "lib": { + "targetType": "library", + "excludedSourceFiles": [ + "source/app.d" + ] + } + } +} diff --git a/test cases/d/17 dub and meson project/multi-configuration/dub.selections.json b/test cases/d/17 dub and meson project/multi-configuration/dub.selections.json new file mode 100644 index 000000000000..322586b10676 --- /dev/null +++ b/test cases/d/17 dub and meson project/multi-configuration/dub.selections.json @@ -0,0 +1,5 @@ +{ + "fileVersion": 1, + "versions": { + } +} diff --git a/test cases/d/17 dub and meson project/multi-configuration/source/app.d b/test cases/d/17 dub and meson project/multi-configuration/source/app.d new file mode 100644 index 000000000000..d66321b3c581 --- /dev/null +++ b/test cases/d/17 dub and meson project/multi-configuration/source/app.d @@ -0,0 +1 @@ +void main () {} diff --git a/test cases/d/17 dub and meson project/multi-configuration/source/foo.d b/test cases/d/17 dub and meson project/multi-configuration/source/foo.d new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/d/17 dub and meson project/source/app.d b/test cases/d/17 dub and meson project/source/app.d new file mode 100644 index 000000000000..d66321b3c581 --- /dev/null +++ b/test cases/d/17 dub and meson project/source/app.d @@ -0,0 +1 @@ +void main () {} diff --git a/test cases/d/17 dub and meson project/x/y/z/dub.json b/test cases/d/17 dub and meson project/x/y/z/dub.json new file mode 100644 index 000000000000..dc0ef515964a --- /dev/null +++ b/test cases/d/17 dub and meson project/x/y/z/dub.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "dubtestproject:test3": "*" + }, + "name": "dub-respects-project-root-subdir" +} diff --git a/test cases/d/17 dub and meson project/x/y/z/dub.selections.json b/test cases/d/17 dub and meson project/x/y/z/dub.selections.json new file mode 100644 index 000000000000..daf7a2de9823 --- /dev/null +++ b/test cases/d/17 dub and meson project/x/y/z/dub.selections.json @@ -0,0 +1,6 @@ +{ + "fileVersion": 1, + "versions": { + "dubtestproject": "1.3.0" + } +} diff --git a/test cases/d/17 dub and meson project/x/y/z/meson.build b/test cases/d/17 dub and meson project/x/y/z/meson.build new file mode 100644 index 000000000000..99943b8a723e --- /dev/null +++ b/test cases/d/17 dub and meson project/x/y/z/meson.build @@ -0,0 +1,6 @@ +root = meson.project_source_root() +dep = dependency('dubtestproject:test3', method: 'dub') +version = dep.version() +if version != '1.2.0' + error(f'Expected urld version to be the one selected in "@root@/dub.selections.json" but got @version@') +endif diff --git a/test cases/failing/133 dub missing dependency/dub.json b/test cases/failing/133 dub missing dependency/dub.json new file mode 100644 index 000000000000..1ad9ddcc1f19 --- /dev/null +++ b/test cases/failing/133 dub missing dependency/dub.json @@ -0,0 +1,3 @@ +{ + "name": "132-missing-dep" +} diff --git a/test cases/failing/133 dub missing dependency/dub.selections.json b/test cases/failing/133 dub missing dependency/dub.selections.json new file mode 100644 index 000000000000..322586b10676 --- /dev/null +++ b/test cases/failing/133 dub missing dependency/dub.selections.json @@ -0,0 +1,5 @@ +{ + "fileVersion": 1, + "versions": { + } +} diff --git a/test cases/failing/133 dub missing dependency/meson.build b/test cases/failing/133 dub missing dependency/meson.build new file mode 100644 index 000000000000..fcccb3b415d4 --- /dev/null +++ b/test cases/failing/133 dub missing dependency/meson.build @@ -0,0 +1,17 @@ +project('Dub dependency not in dub.json') + +if not add_languages('d', required: false) + error('MESON_SKIP_TEST test requires D compiler') +endif + +dub_exe = find_program('dub', required : false) +if not dub_exe.found() + error('MESON_SKIP_TEST: Dub not found') +endif + +dub_ver = dub_exe.version() +if not dub_ver.version_compare('>=1.35.0') + error('MESON_SKIP_TEST: test requires dub >=1.35.0') +endif + +dep = dependency('urld', method: 'dub') # not in dub.json diff --git a/test cases/failing/133 dub missing dependency/source/app.d b/test cases/failing/133 dub missing dependency/source/app.d new file mode 100644 index 000000000000..d66321b3c581 --- /dev/null +++ b/test cases/failing/133 dub missing dependency/source/app.d @@ -0,0 +1 @@ +void main () {} diff --git a/test cases/failing/133 dub missing dependency/test.json b/test cases/failing/133 dub missing dependency/test.json new file mode 100644 index 000000000000..e58dbc7aabee --- /dev/null +++ b/test cases/failing/133 dub missing dependency/test.json @@ -0,0 +1,8 @@ +{ + "stdout": [ + { + "line": "test cases/failing/132 dub missing dependency/meson.build:17:6: ERROR: Dependency \"urld\" not found", + "line": "dub add urld" + } + ] +} From fbed6e9bd6569b98a9dcdfccf4ae72d0c791111a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 14:41:19 -0800 Subject: [PATCH 345/624] unittests: use subtests for test_Builtin_options_documented --- unittests/allplatformstests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 57aa6315d214..1c5b7c58811f 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -334,7 +334,8 @@ def test_prefix_dependent_defaults(self): name = opt['name'] value = opt['value'] if name in expected[prefix]: - self.assertEqual(value, expected[prefix][name], f'For option {name} and prefix {prefix}.') + with self.subTest(prefix=prefix, option=name): + self.assertEqual(value, expected[prefix][name], f'For option {name} and prefix {prefix}.') self.wipe() def test_default_options_prefix_dependent_defaults(self): From c227f38560dba23b606ff3336def167c1506283d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 10:40:17 -0800 Subject: [PATCH 346/624] options: remove BuiltinOption._argparse_choices Since ba3460eb11bbceaf4fef7352bf286cf27184c99a, the workaround this was applying was unnecessary. --- mesonbuild/options.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index c3f2ff36adab..78d2bef53858 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -672,11 +672,6 @@ def _argparse_action(self) -> T.Optional[str]: return 'store_true' return None - def _argparse_choices(self) -> T.Any: - if self.opt_type is UserBooleanOption: - return [True, False] - return self.choices - @staticmethod def argparse_name_to_arg(name: str) -> str: if name == 'warning_level': @@ -696,7 +691,7 @@ def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any: def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: kwargs: ArgparseKWs = {} - c = self._argparse_choices() + c = self.choices b = self._argparse_action() h = self.description if not b: From f2ead0092f699356809732968a6d3d082b4989ae Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 10:52:11 -0800 Subject: [PATCH 347/624] options: split UserOption -> Argparse conversion out of BuiltinOption This is the first step do deleting the BuiltinOption altogether. --- mesonbuild/coredata.py | 8 ++--- mesonbuild/mesonmain.py | 2 +- mesonbuild/options.py | 72 +++++++++++++++++++---------------------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7bc3cc21896e..84b76f615fbc 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -802,10 +802,10 @@ def save(obj: CoreData, build_dir: str) -> str: def register_builtin_arguments(parser: argparse.ArgumentParser) -> None: for n, b in options.BUILTIN_OPTIONS.items(): - b.add_to_argparse(n, parser, '') + options.option_to_argparse(b, n, parser, '') for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items(): - b.add_to_argparse(n, parser, ' (just for host machine)') - b.add_to_argparse(n.as_build(), parser, ' (just for build machine)') + 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.') @@ -832,7 +832,7 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None: value = getattr(args, name, None) if value is not None: if key in args.cmd_line_options: - cmdline_name = options.BuiltinOption.argparse_name_to_arg(name) + 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 diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 7ec66fce795f..8a8678a9a944 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -246,7 +246,7 @@ def has_startswith(coll, target): #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.BuiltinOption.argparse_name_to_arg(optionkey.name) + longarg = mesonbuild.options.argparse_name_to_arg(optionkey.name) shortarg = f'-D{optionkey.name}' if has_startswith(args, longarg) and has_startswith(args, shortarg): sys.exit( diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 78d2bef53858..c4c445af1a51 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -655,56 +655,50 @@ def __init__(self, opt_type: T.Type[UserOption[_T]], description: str, default: def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> UserOption[_T]: """Create an instance of opt_type and return it.""" if value is None: - value = self.prefixed_default(name, prefix) + value = _argparse_prefixed_default(self, name, prefix) keywords = {'yielding': self.yielding, 'value_': value} keywords.update(self.kwargs) if self.choices: keywords['choices'] = self.choices - o = self.opt_type(name.name, self.description, **keywords) + # This will be fixed when the BuiltinOption class is deleted + o = self.opt_type(name.name, self.description, **keywords) # type: ignore o.readonly = self.readonly return o - def _argparse_action(self) -> T.Optional[str]: - # If the type is a boolean, the presence of the argument in --foo form - # is to enable it. Disabling happens by using -Dfoo=false, which is - # parsed under `args.projectoptions` and does not hit this codepath. - if isinstance(self.default, bool): - return 'store_true' - return None - @staticmethod - def argparse_name_to_arg(name: str) -> str: - if name == 'warning_level': - return '--warnlevel' - else: - return '--' + name.replace('_', '-') +def argparse_name_to_arg(name: str) -> str: + if name == 'warning_level': + return '--warnlevel' + return '--' + name.replace('_', '-') - def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any: - if self.opt_type in {UserComboOption, UserIntegerOption, UserUmaskOption}: - return self.default - try: - return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix] - except KeyError: - pass - return self.default - def add_to_argparse(self, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: - kwargs: ArgparseKWs = {} +def _argparse_prefixed_default(opt: BuiltinOption, name: OptionKey, prefix: str = '') -> ElementaryOptionValues: + if opt.opt_type in {UserComboOption, UserIntegerOption, UserUmaskOption}: + return opt.default + try: + return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix] + except KeyError: + return T.cast('ElementaryOptionValues', opt.default) + + +def option_to_argparse(option: BuiltinOption, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: + kwargs: ArgparseKWs = {} + + c = option.choices + b = 'store_true' if isinstance(option.default, bool) else None + h = option.description + if not b: + h = '{} (default: {}).'.format(h.rstrip('.'), _argparse_prefixed_default(option, name)) + else: + kwargs['action'] = b + if c and not b: + kwargs['choices'] = c + kwargs['default'] = argparse.SUPPRESS + kwargs['dest'] = str(name) + + cmdline_name = argparse_name_to_arg(str(name)) + parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs) - c = self.choices - b = self._argparse_action() - h = self.description - if not b: - h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name)) - else: - kwargs['action'] = b - if c and not b: - kwargs['choices'] = c - kwargs['default'] = argparse.SUPPRESS - kwargs['dest'] = str(name) - - cmdline_name = self.argparse_name_to_arg(str(name)) - parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs) # Update `docs/markdown/Builtin-options.md` after changing the options below # Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required. From 45ad46624557c40d19c64cbe33f9ff23f4f1f359 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 15:04:08 -0800 Subject: [PATCH 348/624] coredata: fix annotations for add_builtin_option --- mesonbuild/coredata.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 84b76f615fbc..fb2c07521aa8 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -362,20 +362,20 @@ def init_builtins(self) -> None: self.add_builtin_option(self.optstore, key.evolve(machine=for_machine), opt) @staticmethod - def add_builtin_option(opts_map: 'MutableKeyedOptionDictType', key: OptionKey, + def add_builtin_option(optstore: options.OptionStore, key: OptionKey, opt: 'options.BuiltinOption') -> None: if key.subproject: if opt.yielding: # This option is global and not per-subproject return - value = opts_map.get_value(key.as_root()) + value = optstore.get_value(key.as_root()) else: value = None if key.has_module_prefix(): modulename = key.get_module_prefix() - opts_map.add_module_option(modulename, key, opt.init_option(key, value, options.default_prefix())) + optstore.add_module_option(modulename, key, opt.init_option(key, value, options.default_prefix())) else: - opts_map.add_system_option(key, opt.init_option(key, value, options.default_prefix())) + optstore.add_system_option(key, opt.init_option(key, value, options.default_prefix())) def init_backend_options(self, backend_name: str) -> None: if backend_name == 'ninja': From 51e51eb40c089cc594525864a981b8b9aca10646 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 11:01:35 -0800 Subject: [PATCH 349/624] options: allow readonly to be set in the initializer Which will be used when BuiltinOption is removed --- mesonbuild/optinterpreter.py | 2 +- mesonbuild/options.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 9f95925fa696..c33306d5cdec 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -239,7 +239,7 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA value = kwargs['value'] if value is None: value = kwargs['choices'][0] - return options.UserComboOption(name, description, value, *args, choices) + return options.UserComboOption(name, description, value, *args, choices=choices) @typed_kwargs( 'integer option', diff --git a/mesonbuild/options.py b/mesonbuild/options.py index c4c445af1a51..05a6c28355c8 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -322,7 +322,7 @@ class UserOption(T.Generic[_T], HoldableObject): value_: dataclasses.InitVar[_T] yielding: bool = DEFAULT_YIELDING deprecated: DeprecatedType = False - readonly: bool = dataclasses.field(default=False, init=False) + readonly: bool = dataclasses.field(default=False) def __post_init__(self, value_: _T) -> None: self.value = self.validate_value(value_) From 9c252002658660c48e7b05cd280a4c751a54250c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 Mar 2025 11:26:32 -0800 Subject: [PATCH 350/624] options: Remove BuiltinOption class It basically served two purposes, to allow us to initialize UserOptions, and to convert those options into Argparse values. The latter has been transformed into a standalone function, the former can be easily achieved without this class. This reduces LOC, simplifies setup, and helps with our type safety. --- mesonbuild/coredata.py | 19 ++-- mesonbuild/options.py | 196 +++++++++++++++++------------------------ unittests/datatests.py | 10 ++- 3 files changed, 102 insertions(+), 123 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index fb2c07521aa8..cf32621461f6 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -17,6 +17,7 @@ MesonBugException, MesonException, MachineChoice, PerMachine, PerMachineDefaultable, + default_prefix, stringlistify, pickle_load ) @@ -363,19 +364,23 @@ def init_builtins(self) -> None: @staticmethod def add_builtin_option(optstore: options.OptionStore, key: OptionKey, - opt: 'options.BuiltinOption') -> None: + 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 - value = optstore.get_value(key.as_root()) else: - value = None - if key.has_module_prefix(): - modulename = key.get_module_prefix() - optstore.add_module_option(modulename, key, opt.init_option(key, value, options.default_prefix())) + 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.init_option(key, value, options.default_prefix())) + optstore.add_system_option(key, opt) def init_backend_options(self, backend_name: str) -> None: if backend_name == 'ninja': diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 05a6c28355c8..d7997ffcceaa 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -302,12 +302,6 @@ def get_module_prefix(self) -> T.Optional[str]: return self.name.split('.', 1)[0] return None - def without_module_prefix(self) -> 'OptionKey': - if self.has_module_prefix(): - newname = self.name.split('.', 1)[1] - return self.evolve(newname) - return self - def is_for_build(self) -> bool: return self.machine is MachineChoice.BUILD @@ -635,60 +629,32 @@ def validate_value(self, value: T.Union[str, T.List[str]]) -> str: f'Possible values for option "{self.name}" are {self.choices}') -class BuiltinOption(T.Generic[_T]): - - """Class for a builtin option type. - - There are some cases that are not fully supported yet. - """ - - def __init__(self, opt_type: T.Type[UserOption[_T]], description: str, default: T.Any, yielding: bool = True, *, - choices: T.Any = None, readonly: bool = False, **kwargs: object): - self.opt_type = opt_type - self.description = description - self.default = default - self.choices = choices - self.yielding = yielding - self.readonly = readonly - self.kwargs = kwargs - - def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> UserOption[_T]: - """Create an instance of opt_type and return it.""" - if value is None: - value = _argparse_prefixed_default(self, name, prefix) - keywords = {'yielding': self.yielding, 'value_': value} - keywords.update(self.kwargs) - if self.choices: - keywords['choices'] = self.choices - # This will be fixed when the BuiltinOption class is deleted - o = self.opt_type(name.name, self.description, **keywords) # type: ignore - o.readonly = self.readonly - return o - - def argparse_name_to_arg(name: str) -> str: if name == 'warning_level': return '--warnlevel' return '--' + name.replace('_', '-') -def _argparse_prefixed_default(opt: BuiltinOption, name: OptionKey, prefix: str = '') -> ElementaryOptionValues: - if opt.opt_type in {UserComboOption, UserIntegerOption, UserUmaskOption}: - return opt.default +def argparse_prefixed_default(opt: AnyOptionType, name: OptionKey, prefix: str = '') -> ElementaryOptionValues: + if isinstance(opt, (UserComboOption, UserIntegerOption, UserUmaskOption)): + return T.cast('ElementaryOptionValues', opt.default) try: return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix] except KeyError: return T.cast('ElementaryOptionValues', opt.default) -def option_to_argparse(option: BuiltinOption, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: +def option_to_argparse(option: AnyOptionType, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: kwargs: ArgparseKWs = {} - c = option.choices + if isinstance(option, (EnumeratedUserOption, UserArrayOption)): + c = option.choices + else: + c = None b = 'store_true' if isinstance(option.default, bool) else None h = option.description if not b: - h = '{} (default: {}).'.format(h.rstrip('.'), _argparse_prefixed_default(option, name)) + h = '{} (default: {}).'.format(h.rstrip('.'), argparse_prefixed_default(option, name)) else: kwargs['action'] = b if c and not b: @@ -703,79 +669,79 @@ def option_to_argparse(option: BuiltinOption, name: OptionKey, parser: argparse. # Update `docs/markdown/Builtin-options.md` after changing the options below # Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required. # Please also update completion scripts in $MESONSRC/data/shell-completions/ -BUILTIN_DIR_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())), - (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')), - (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', default_datadir())), - (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', default_includedir())), - (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', default_infodir())), - (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())), - (OptionKey('licensedir'), BuiltinOption(UserStringOption, 'Licenses directory', '')), - (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())), - (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', default_localedir())), - (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')), - (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', default_mandir())), - (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', default_sbindir())), - (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')), - (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', default_sysconfdir())), -]) - -BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')), - (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist, - readonly=True)), - (OptionKey('genvslite'), - BuiltinOption( - UserComboOption, - 'Setup multiple buildtype-suffixed ninja-backend build directories, ' - 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them', - 'vs2022', - choices=genvslitelist) - ), - (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug', - choices=buildtypelist)), - (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Enable debug symbols and other information', True)), - (OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], - yielding=False)), - (OptionKey('default_both_libraries'), BuiltinOption(UserComboOption, 'Default library type for both_libraries', 'shared', choices=['shared', 'static', 'auto'])), - (OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), - (OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), - (OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), - (OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])), - (OptionKey('prefer_static'), BuiltinOption(UserBooleanOption, 'Whether to try static linking before shared linking', False)), - (OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), - (OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), - (OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), - (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', 4, min_value=2)), - (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'], yielding=False)), - (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), - (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])), - (OptionKey('force_fallback_for'), BuiltinOption(UserStringArrayOption, 'Force fallback for those subprojects', [])), - (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)), - - # Pkgconfig module - (OptionKey('pkgconfig.relocatable'), - BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)), - - # Python module - (OptionKey('python.bytecompile'), - BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', 0, min_value=-1, max_value=2)), - (OptionKey('python.install_env'), - BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])), - (OptionKey('python.platlibdir'), - BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')), - (OptionKey('python.purelibdir'), - BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')), - (OptionKey('python.allow_limited_api'), - BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)), -]) +BUILTIN_DIR_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in [ + UserStringOption('prefix', 'Installation prefix', default_prefix()), + UserStringOption('bindir', 'Executable directory', 'bin'), + UserStringOption('datadir', 'Data file directory', default_datadir()), + UserStringOption('includedir', 'Header file directory', default_includedir()), + UserStringOption('infodir', 'Info page directory', default_infodir()), + UserStringOption('libdir', 'Library directory', default_libdir()), + UserStringOption('licensedir', 'Licenses directory', ''), + UserStringOption('libexecdir', 'Library executable directory', default_libexecdir()), + UserStringOption('localedir', 'Locale data directory', default_localedir()), + UserStringOption('localstatedir', 'Localstate data directory', 'var'), + UserStringOption('mandir', 'Manual page directory', default_mandir()), + UserStringOption('sbindir', 'System executable directory', default_sbindir()), + UserStringOption('sharedstatedir', 'Architecture-independent data directory', 'com'), + UserStringOption('sysconfdir', 'Sysconf data directory', default_sysconfdir()), + ] +} + +BUILTIN_CORE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in T.cast('T.List[AnyOptionType]', [ + UserFeatureOption('auto_features', "Override value of all 'auto' features", 'auto'), + UserComboOption('backend', 'Backend to use', 'ninja', choices=backendlist, readonly=True), + UserComboOption( + 'genvslite', + 'Setup multiple buildtype-suffixed ninja-backend build directories, ' + 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them', + 'vs2022', + choices=genvslitelist + ), + 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_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('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('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), + + # Pkgconfig module + UserBooleanOption('pkgconfig.relocatable', 'Generate pkgconfig files as relocatable', False), + + # Python module + UserIntegerOption('python.bytecompile', 'Whether to compile bytecode', 0, min_value=-1, max_value=2), + UserComboOption('python.install_env', 'Which python environment to install to', 'prefix', + choices=['auto', 'prefix', 'system', 'venv']), + 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), + ]) +} BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items())) -BUILTIN_OPTIONS_PER_MACHINE: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('pkg_config_path'), BuiltinOption(UserStringArrayOption, 'List of additional paths for pkg-config to search', [])), - (OptionKey('cmake_prefix_path'), BuiltinOption(UserStringArrayOption, 'List of additional prefixes for cmake to search', [])), -]) +BUILTIN_OPTIONS_PER_MACHINE: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in [ + UserStringArrayOption('pkg_config_path', 'List of additional paths for pkg-config to search', []), + UserStringArrayOption('cmake_prefix_path', 'List of additional prefixes for cmake to search', []), + ] +} # Special prefix-dependent defaults for installation directories that reside in # a path outside of the prefix in FHS and common usage. @@ -1291,7 +1257,9 @@ def hard_reset_from_prefix(self, prefix: str) -> None: if prefix in prefix_mapping: new_value = prefix_mapping[prefix] else: - new_value = BUILTIN_OPTIONS[optkey].default + _v = BUILTIN_OPTIONS[optkey].default + assert isinstance(_v, str), 'for mypy' + new_value = _v valobj.set_value(new_value) self.options[OptionKey('prefix')].set_value(prefix) diff --git a/unittests/datatests.py b/unittests/datatests.py index cb6542db8f71..173f718ea428 100644 --- a/unittests/datatests.py +++ b/unittests/datatests.py @@ -138,9 +138,15 @@ def test_builtin_options_documented(self): self.assertEqual(len(found_entries & options), 0) found_entries |= options + # TODO: put the module name back in the OptionKey + def remove_module_name(key: OptionKey) -> OptionKey: + if '.' in key.name: + return key.evolve(name=key.name.split('.', 1)[1]) + return key + self.assertEqual(found_entries, { - *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS), - *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE), + *(str(remove_module_name(k)) for k in mesonbuild.options.BUILTIN_OPTIONS), + *(str(remove_module_name(k)) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE), }) # Check that `buildtype` table inside `Core options` matches how From 1be473417a7634cd41fcee6abc71821798ae4de0 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 6 Mar 2025 08:16:28 -0500 Subject: [PATCH 351/624] doc: fix sentence about cross-compilation. Fixes #14338 --- docs/markdown/Cross-compilation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index 0cfef71b9cd0..64196f3bde10 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -85,7 +85,7 @@ their user interface much more complex. The most complicated case is when you cross-compile a cross compiler. As an example you can, on a Linux machine, generate a cross compiler -that runs on Windows but produces binaries on MIPS Linux. In this case +that runs on Windows but produces binaries for MIPS Linux. In this case *build machine* is x86 Linux, *host machine* is x86 Windows and *target machine* is MIPS Linux. This setup is known as the [Canadian Cross](https://en.wikipedia.org/wiki/Cross_compiler#Canadian_Cross). From f70945d1184a68be46bd38f0a4672d2bc88e99f2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 13:34:38 +0100 Subject: [PATCH 352/624] mesonlib: allow using lazy_property with "__"-named properties These are given a name that is different from __func.__name__, so store the name when the descriptor is placed in the class. Signed-off-by: Paolo Bonzini --- mesonbuild/utils/universal.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index dda35cc2b0fd..45c0b7c13a95 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -2379,10 +2379,17 @@ class lazy_property(T.Generic[_T]): Due to Python's MRO that means that the calculated value will be found before this property, speeding up subsequent lookups. """ - def __init__(self, func: T.Callable[[T.Any], _T]): + def __init__(self, func: T.Callable[[T.Any], _T]) -> None: + self.__name: T.Optional[str] = None self.__func = func + def __set_name__(self, owner: T.Any, name: str) -> None: + if self.__name is None: + self.__name = name + else: + assert name == self.__name + def __get__(self, instance: object, cls: T.Type) -> _T: value = self.__func(instance) - setattr(instance, self.__func.__name__, value) + setattr(instance, self.__name, value) return value From e0fd2ffae55a6679655e12e4e4880c530184140f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 12:35:57 +0100 Subject: [PATCH 353/624] build: cache target id at time of first use --- mesonbuild/build.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8d3d4052d201..9f3af9fba585 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -25,7 +25,7 @@ extract_as_list, typeslistify, stringlistify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, PerMachineDefaultable, - MesonBugException, EnvironmentVariables, pickle_load, + MesonBugException, EnvironmentVariables, pickle_load, lazy_property, ) from .options import OptionKey @@ -627,13 +627,17 @@ def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str: return subdir_part + '@@' + my_id return my_id - def get_id(self) -> str: + @lazy_property + def id(self) -> str: name = self.name if getattr(self, 'name_suffix_set', False): name += '.' + self.suffix return self.construct_id_from_path( self.subdir, 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'] From e7a405c7e2a384e68c39ee3d6397a974df4df36f Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 6 Mar 2025 07:46:33 +0100 Subject: [PATCH 354/624] get_llvm_tool_names: add llvm 20 this fixes the "frameworks: 15 llvm" tests with llvm 20.1 --- mesonbuild/environment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 76eb5617bbf9..51696667855f 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -206,6 +206,8 @@ def get_llvm_tool_names(tool: str) -> T.List[str]: # 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', From f64cda3d80f34db42cc59fefd3abbe904fc3231a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 12:57:40 -0800 Subject: [PATCH 355/624] unittests: Use more subtests --- unittests/linuxliketests.py | 78 +++++++++++++++++------------- unittests/optiontests.py | 32 ++++++------ unittests/platformagnostictests.py | 20 +++++--- 3 files changed, 73 insertions(+), 57 deletions(-) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 2b5643620950..08eb4b93a1fe 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -43,7 +43,7 @@ from run_tests import ( - get_fake_env + get_fake_env, Backend, ) from .baseplatformtests import BasePlatformTests @@ -1876,47 +1876,57 @@ def check_has_flag(self, compdb, src, argument): self.assertTrue(False, f'Source {src} not found in compdb') def test_persp_options(self): + if self.backend is not Backend.ninja: + raise SkipTest(f'{self.backend.name!r} backend can\'t install files') + testdir = os.path.join(self.unit_test_dir, '123 persp options') - self.init(testdir, extra_args='-Doptimization=1') - compdb = self.get_compdb() - mainsrc = 'toplevel.c' - sub1src = 'sub1.c' - sub2src = 'sub2.c' - self.check_has_flag(compdb, mainsrc, '-O1') - self.check_has_flag(compdb, sub1src, '-O1') - self.check_has_flag(compdb, sub2src, '-O1') + + with self.subTest('init'): + self.init(testdir, extra_args='-Doptimization=1') + compdb = self.get_compdb() + mainsrc = 'toplevel.c' + sub1src = 'sub1.c' + sub2src = 'sub2.c' + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O1') # Set subproject option to O2 - self.setconf(['-Dround=2', '-D', 'sub2:optimization=3']) - compdb = self.get_compdb() - self.check_has_flag(compdb, mainsrc, '-O1') - self.check_has_flag(compdb, sub1src, '-O1') - self.check_has_flag(compdb, sub2src, '-O3') + with self.subTest('set subproject option'): + self.setconf(['-Dround=2', '-D', 'sub2:optimization=3']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O3') # Change an already set override. - self.setconf(['-Dround=3', '-D', 'sub2:optimization=2']) - compdb = self.get_compdb() - self.check_has_flag(compdb, mainsrc, '-O1') - self.check_has_flag(compdb, sub1src, '-O1') - self.check_has_flag(compdb, sub2src, '-O2') + with self.subTest('change subproject option'): + self.setconf(['-Dround=3', '-D', 'sub2:optimization=2']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O1') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O2') # Set top level option to O3 - self.setconf(['-Dround=4', '-D:optimization=3']) - compdb = self.get_compdb() - self.check_has_flag(compdb, mainsrc, '-O3') - self.check_has_flag(compdb, sub1src, '-O1') - self.check_has_flag(compdb, sub2src, '-O2') + with self.subTest('change main project option'): + self.setconf(['-Dround=4', '-D:optimization=3']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O2') # Unset subproject - self.setconf(['-Dround=5', '-U', 'sub2:optimization']) - compdb = self.get_compdb() - self.check_has_flag(compdb, mainsrc, '-O3') - self.check_has_flag(compdb, sub1src, '-O1') - self.check_has_flag(compdb, sub2src, '-O1') + with self.subTest('unset subproject option'): + self.setconf(['-Dround=5', '-U', 'sub2:optimization']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O1') + self.check_has_flag(compdb, sub2src, '-O1') # Set global value - self.setconf(['-Dround=6', '-D', 'optimization=2']) - compdb = self.get_compdb() - self.check_has_flag(compdb, mainsrc, '-O3') - self.check_has_flag(compdb, sub1src, '-O2') - self.check_has_flag(compdb, sub2src, '-O2') + with self.subTest('set global option'): + self.setconf(['-Dround=6', '-D', 'optimization=2']) + compdb = self.get_compdb() + self.check_has_flag(compdb, mainsrc, '-O3') + self.check_has_flag(compdb, sub1src, '-O2') + self.check_has_flag(compdb, sub2src, '-O2') diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 6b8eb4a855d2..3db769d66e8b 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -36,21 +36,23 @@ def test_toplevel_project(self): self.assertEqual(optstore.get_value_for(k), new_value) def test_parsing(self): - s1 = OptionKey.from_string('sub:optname') - s1_expected = OptionKey('optname', 'sub', MachineChoice.HOST) - self.assertEqual(s1, s1_expected) - self.assertEqual(str(s1), 'sub:optname') - - s2 = OptionKey.from_string('optname') - s2_expected = OptionKey('optname', None, MachineChoice.HOST) - self.assertEqual(s2, s2_expected) - - self.assertEqual(str(s2), 'optname') - - s3 = OptionKey.from_string(':optname') - s3_expected = OptionKey('optname', '', MachineChoice.HOST) - self.assertEqual(s3, s3_expected) - self.assertEqual(str(s3), ':optname') + with self.subTest('subproject'): + s1 = OptionKey.from_string('sub:optname') + s1_expected = OptionKey('optname', 'sub', MachineChoice.HOST) + self.assertEqual(s1, s1_expected) + self.assertEqual(str(s1), 'sub:optname') + + with self.subTest('plain name'): + s2 = OptionKey.from_string('optname') + s2_expected = OptionKey('optname', None, MachineChoice.HOST) + self.assertEqual(s2, s2_expected) + self.assertEqual(str(s2), 'optname') + + with self.subTest('root project'): + s3 = OptionKey.from_string(':optname') + s3_expected = OptionKey('optname', '', MachineChoice.HOST) + self.assertEqual(s3, s3_expected) + self.assertEqual(str(s3), ':optname') def test_subproject_for_system(self): optstore = OptionStore(False) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 5707553ba318..cb6a931fc602 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -167,21 +167,25 @@ def test_change_backend(self): self.init(testdir) # no-op change works - self.setconf(f'--backend=ninja') - self.init(testdir, extra_args=['--reconfigure', '--backend=ninja']) + with self.subTest('set the option to the same value'): + self.setconf('--backend=ninja') + self.init(testdir, extra_args=['--reconfigure', '--backend=ninja']) # Change backend option is not allowed - with self.assertRaises(subprocess.CalledProcessError) as cm: - self.setconf('-Dbackend=none') - self.assertIn("ERROR: Tried to modify read only option 'backend'", cm.exception.stdout) + 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) # Check that the new value was not written in the store. - self.assertEqual(self.getconf('backend'), 'ninja') + with self.subTest('option is stored correctly'): + self.assertEqual(self.getconf('backend'), 'ninja') # Wipe with a different backend is allowed - self.init(testdir, extra_args=['--wipe', '--backend=none']) + with self.subTest('Changing the backend with wipe'): + self.init(testdir, extra_args=['--wipe', '--backend=none']) - self.assertEqual(self.getconf('backend'), 'none') + self.assertEqual(self.getconf('backend'), 'none') def test_validate_dirs(self): testdir = os.path.join(self.common_test_dir, '1 trivial') From 3df6762f542a8bd6506a96bb2690a3d91d1a5ebc Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 13:49:06 -0800 Subject: [PATCH 356/624] environment: fix nullability of function parameter --- mesonbuild/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 51696667855f..da29d6c3c641 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -702,7 +702,7 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # Store a global state of Cargo dependencies self.cargo: T.Optional[cargo.Interpreter] = None - def mfilestr2key(self, machine_file_string: str, section_subproject: str, machine: MachineChoice) -> OptionKey: + def mfilestr2key(self, machine_file_string: str, section_subproject: T.Optional[str], machine: MachineChoice) -> OptionKey: key = OptionKey.from_string(machine_file_string) assert key.machine == MachineChoice.HOST if key.subproject: From 4227354100d54b45df8877e20f4e727183f7c867 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 23:43:41 -0800 Subject: [PATCH 357/624] interpreter: Remove debug code from option refactor --- mesonbuild/interpreter/interpreter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1ff6aa4b1d58..a118b3c59790 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1070,8 +1070,6 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']: optname = args[0] - if optname == 'optimization' and self.subproject == 'sub2': - pass if ':' in optname: raise InterpreterException('Having a colon in option name is forbidden, ' 'projects are not allowed to directly access ' From 22d970d9006e731874585495601c981587eb0ecf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 14:59:09 -0800 Subject: [PATCH 358/624] options: delete unused set_subproject_options --- mesonbuild/options.py | 10 ---------- unittests/optiontests.py | 23 ----------------------- 2 files changed, 33 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index d7997ffcceaa..3a3e5e9b7f99 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1036,16 +1036,6 @@ def set_option_from_string(self, keystr: T.Union[OptionKey, str], new_value: str o = o.as_root() return self.set_value(o, new_value) - def set_subproject_options(self, subproject: str, - spcall_default_options: str, - project_default_options: str) -> None: - for o in itertools.chain(spcall_default_options, project_default_options): - keystr, valstr = o.split('=', 1) - assert ':' not in keystr - keystr = f'{subproject}:{keystr}' - if keystr not in self.augments: - self.augments[keystr] = valstr - def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool: dirty = False D_args = [] if D_args is None else D_args diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 3db769d66e8b..274a74a6e1f7 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -182,29 +182,6 @@ def test_augment_set_sub(self): self.assertEqual(optstore.get_value_for(name), top_value) self.assertEqual(optstore.get_value_for(name, sub_name), set_value) - def test_subproject_call_options(self): - optstore = OptionStore(False) - name = 'cpp_std' - default_value = 'c++11' - override_value = 'c++14' - unused_value = 'c++20' - subproject = 'sub' - - co = UserComboOption(name, - 'C++ language standard to use', - default_value, - choices=['c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++23'], - ) - optstore.add_system_option(name, co) - optstore.set_subproject_options(subproject, [f'cpp_std={override_value}'], [f'cpp_std={unused_value}']) - self.assertEqual(optstore.get_value_for(name), default_value) - self.assertEqual(optstore.get_value_for(name, subproject), override_value) - - # Trying again should change nothing - optstore.set_subproject_options(subproject, [f'cpp_std={unused_value}'], [f'cpp_std={unused_value}']) - self.assertEqual(optstore.get_value_for(name), default_value) - self.assertEqual(optstore.get_value_for(name, subproject), override_value) - def test_b_default(self): optstore = OptionStore(False) value = optstore.get_default_for_b_option(OptionKey('b_vscrt')) From f89a587753618a73996e2b44f46bdd11707fce2e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Mar 2025 08:59:27 +0100 Subject: [PATCH 359/624] clang-tidy: build pch files before running clang-tidy "ninja clang-tidy" will not work if you are using pch files and have not run a full build yet: the pch files will not yet be built and there is no dependency between the pch targets and the clang-tidy target. Fixes: #13499 --- mesonbuild/backend/ninjabackend.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 74fb86c5e3dd..2ae69631161f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -4,7 +4,7 @@ from __future__ import annotations -from collections import OrderedDict +from collections import defaultdict, OrderedDict from dataclasses import dataclass from enum import Enum, unique from functools import lru_cache @@ -503,6 +503,7 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter self.ninja_filename = 'build.ninja' self.fortran_deps: T.Dict[str, T.Dict[str, File]] = {} self.all_outputs: T.Set[str] = set() + self.all_pch: T.Dict[str, T.Set[str]] = defaultdict(set) self.all_structured_sources: T.Set[str] = set() self.introspection_data = {} self.created_llvm_ir_rule = PerMachine(False, False) @@ -3271,6 +3272,7 @@ def generate_pch(self, target, header_deps=None): elem.add_item('ARGS', commands) elem.add_item('DEPFILE', dep) self.add_build(elem) + self.all_pch[compiler.id].update(objs + [dst]) return pch_objects def get_target_shsym_filename(self, target): @@ -3727,7 +3729,7 @@ def generate_scanbuild(self) -> None: elem.add_item('pool', 'console') self.add_build(elem) - def generate_clangtool(self, name: str, extra_arg: T.Optional[str] = None) -> None: + def generate_clangtool(self, name: str, extra_arg: T.Optional[str] = None, need_pch: bool = False) -> None: target_name = 'clang-' + name extra_args = [] if extra_arg: @@ -3747,6 +3749,8 @@ def generate_clangtool(self, name: str, extra_arg: T.Optional[str] = None) -> No elem = self.create_phony_target(target_name, 'CUSTOM_COMMAND', 'PHONY') elem.add_item('COMMAND', cmd) elem.add_item('pool', 'console') + if need_pch: + elem.add_dep(list(self.all_pch['clang'])) self.add_build(elem) def generate_clangformat(self) -> None: @@ -3758,10 +3762,10 @@ def generate_clangformat(self) -> None: def generate_clangtidy(self) -> None: if not environment.detect_clangtidy(): return - self.generate_clangtool('tidy') + self.generate_clangtool('tidy', need_pch=True) if not environment.detect_clangapply(): return - self.generate_clangtool('tidy', 'fix') + self.generate_clangtool('tidy', 'fix', need_pch=True) def generate_tags(self, tool: str, target_name: str) -> None: import shutil From 8cc1a394b53d92be4d6127c4adb4c493486fef65 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Mar 2025 09:24:44 +0100 Subject: [PATCH 360/624] clang-tidy: do not add target if PCHs are not in clang format If a project uses PCH and they are for e.g. GCC, it will not help to build them before running clang-tidy. Just skip creating the clang-tidy and clang-tidy-fix targets in that case. --- mesonbuild/backend/ninjabackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2ae69631161f..316d2253c6a6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3743,6 +3743,9 @@ def generate_clangtool(self, name: str, extra_arg: T.Optional[str] = None, need_ return if target_name in self.all_outputs: return + if need_pch and not set(self.all_pch.keys()) <= {'clang'}: + return + cmd = self.environment.get_build_command() + \ ['--internal', 'clang' + name, self.environment.source_dir, self.environment.build_dir] + \ extra_args From e132a60ad1e1b486f1f528b4c129d084add5d86c Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 3 Mar 2025 12:00:59 -0500 Subject: [PATCH 361/624] docs: describe the support policy for python versions Bug: https://github.com/mesonbuild/meson/issues/14322 --- docs/markdown/Contributing.md | 51 +++++++++++++++++++++++++++++++++-- docs/markdown/FAQ.md | 18 +++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Contributing.md b/docs/markdown/Contributing.md index 8f796ab65530..729dabf5cca0 100644 --- a/docs/markdown/Contributing.md +++ b/docs/markdown/Contributing.md @@ -501,11 +501,58 @@ those are simple. - indent 4 spaces, no tabs ever - brace always on the same line as if/for/else/function definition -## External dependencies +## Dependency support policy The goal of Meson is to be as easily usable as possible. The user experience should be "get Python3 and Ninja, run", even on -Windows. Unfortunately this means that we can't have dependencies on +Windows. + +Additionally, Meson is popularly used in many core infrastructure packages in a +Unix (and particularly, Linux) userland. This includes: +- package managers, such as pacman (Arch Linux) and portage (Gentoo) +- init systems (systemd, openrc, dinit) +- graphics stacks (xorg, wayland, libdrm, Mesa, gtk) + +As such it needs to be able to run early on when bootstrapping a system from +scratch. + +### Python + +We will always support all non EOL versions of CPython. Yes, there are people +out there using and depending on every old version of python. In fact, there +are people using and depending on systems that had a brand new python at the +time of release, but with a much longer support cycle than Python itself. We +need to balance the tradeoff between supporting those systems and being able to +improve our own codebase and code quality. + +Meson will also be *honest* about what versions of python it supports. When a +version of CPython becomes EOL, it becomes eligible to be removed from our +support policy. We cannot guarantee continued support forever for software that +is not supported by its own developers, even if some deprecated LTS systems out +there still ship it. However, that doesn't mean we will drop support for those +versions simply because they are old. If we are not using new functionality +from new python versions, we will continue to mark Meson as compatible with the +older version -- and test it in CI! + +(Note that contrary to popular belief, it is actually easier to test support +for very old versions of python than it is to drop support for it. We already +have the CI setup necessary for testing. Upgrading the CI to use newer versions +of python, on the other hand, represents mildly painful administrative work +that has to be done.) + +So, in order to start requiring a newer version of python, one should check a +few factors: +- are the older versions being dropped, already EOL? [Python EOL chart](https://endoflife.date/python) +- document the new minimum version of corresponding OSes +- rationalize the benefit of the change in terms of improvements to development + and maintenance of Meson. What new language features will be unlocked by the + upgrade, that Meson will be able to make good use of? Not every version has + new features requiring an upgrade, and not every new feature is so great we + need to drop everything to use it + +### External dependencies + +Unfortunately this also means that we can't have dependencies on projects outside of Python's standard library. This applies only to core functionality, though. For additional helper programs etc the use of external dependencies may be ok. If you feel that you are dealing diff --git a/docs/markdown/FAQ.md b/docs/markdown/FAQ.md index 810e9da23e95..9daf9ae98298 100644 --- a/docs/markdown/FAQ.md +++ b/docs/markdown/FAQ.md @@ -333,6 +333,24 @@ that could fulfill these requirements: Out of these we have chosen Python because it is the best fit for our needs. +## Do you at least support my ancient python install? + +Yes! :) We have a relatively sedate version support policy. You can read about +it in the [Contributing documentation](Contributing.md#python) + +We are also willing to support old versions of meson as LTS releases, +particularly, if it is the final version to support a given python version. If +you have a use case, please discuss it with us and be willing to help backport +bug fixes. + +- python 3.5: [supported through Meson 0.56.2](Release-notes-for-0.56.0.md#python-35-support-will-be-dropped-in-the-next-release) +- python 3.6: [supported through Meson 0.61.5](Release-notes-for-0.61.0.md#python-36-support-will-be-dropped-in-the-next-release) +- python 3.7: currently actively supported by Meson + +We encourage projects to support a wide range of Meson versions if they are not +actually using the latest features anyway. In many, many cases it is quite +practical to support e.g. Meson 0.61. + ## But I really want a version of Meson that doesn't use python! Ecosystem diversity is good. We encourage interested users to write this From 6b985228779f272aaa0a5bbae77448261cf29c70 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 3 Mar 2025 12:40:18 -0500 Subject: [PATCH 362/624] docs: reorganize the main landing page and add more links It can sometimes be hard to find content without digging through menus. Try to guide people towards the specific content they are probably looking for. --- docs/markdown/index.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/markdown/index.md b/docs/markdown/index.md index 427cc0508f6e..8827c8df4734 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -29,6 +29,21 @@ code. Are you an absolute beginner when it comes to programming? No worries, read [this beginner guide](SimpleStart.md) to get started. +## Tutorials + +- [Get it](Getting-meson.md) +- [Build a Gtk app from scratch](Tutorial.md) +- [Build a SDL app from scratch](GuiTutorial.md) +- [How do I do X in Meson?](howtox.md) + +## Manual + +- [Usage manual](Manual.md) +- [API reference manual](Reference-manual.md) +- [Modules documentation](Modules.md) +- [Frequently Asked Questions](FAQ.md) +- [Release Notes](Release-notes.md) + ## Community The easiest way for most people to connect to other Meson developers is @@ -42,6 +57,16 @@ Google Groups) and the [Discussions](https://github.com/mesonbuild/meson/discussions) section of the Meson GitHub repository. +### Development + +All development on Meson is done on the [GitHub +project](https://github.com/mesonbuild/meson). Instructions for +contributing can be found on the [contribution page](Contributing.md). + +You do not need to sign a CLA to contribute to Meson. + +The release process is separately covered at [Releasing](Releasing.md). + ### [Projects using Meson](Users.md) Many projects are using Meson and they're @@ -51,12 +76,3 @@ converting existing projects to Meson. [A short list of Meson users can be found here](Users.md) but there are many more. We would love to hear about your success stories too and how things could be improved too! - -## Development - -All development on Meson is done on the [GitHub -project](https://github.com/mesonbuild/meson). Instructions for -contributing can be found on the [contribution page](Contributing.md). - - -You do not need to sign a CLA to contribute to Meson. From 2afe0a4475f901c0b9d80a465c2c3b16f0952495 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 3 Mar 2025 16:14:53 -0500 Subject: [PATCH 363/624] docs: provide details about how to fund Meson development At the moment it turns out this mostly means... me. But this was discussed publicly, as requested by a third party, and as per the newly added page this is open to anyone in the Project who wishes to let it be known that they accept donations. Closes: https://github.com/mesonbuild/meson/issues/14262 --- .github/FUNDING.yml | 2 ++ docs/markdown/Donating.md | 15 +++++++++++++++ docs/sitemap.txt | 1 + 3 files changed, 18 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 docs/markdown/Donating.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000000..9b70bd171b6f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [eli-schwartz] +custom: ['https://mesonbuild.com/Donating.html'] diff --git a/docs/markdown/Donating.md b/docs/markdown/Donating.md new file mode 100644 index 000000000000..a7f823ebaf52 --- /dev/null +++ b/docs/markdown/Donating.md @@ -0,0 +1,15 @@ +--- +short-description: Donating to support Meson development +... + +# Donating to support Meson development + +Currently, meson takes good advantage of various hosting resources for OSS, and +does not have any real project expenses. As a result, the project does not +directly accept donations. + +The best way to fund Meson development is to directly donate to the core +maintainers team, whether as a "thank you" for the work we have already done or +to help make it easier for us to spend more time working on improving Meson. The current Meson maintainers offer ways to sponsor them: + +- [Eli Schwartz](https://github.com/eli-schwartz) has enabled [GitHub Sponsors](https://github.com/sponsors/eli-schwartz) diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 910df48ee158..21f495e1d1da 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -134,6 +134,7 @@ index.md Contact-information.md Continuous-Integration.md Design-rationale.md + Donating.md IndepthTutorial.md In-the-press.md Mixing-build-systems.md From 9c125a26dbf1b6ecc674df2bd1abc02ca48da2cd Mon Sep 17 00:00:00 2001 From: co63oc Date: Sat, 8 Mar 2025 16:44:15 +0800 Subject: [PATCH 364/624] Fix typos in comments --- mesonbuild/backend/xcodebackend.py | 2 +- mesonbuild/mparser.py | 2 +- mesonbuild/scripts/python_info.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 9f2e0a1b628f..7108e72a71e6 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1792,7 +1792,7 @@ def normalize_header_search_paths(self, header_dirs) -> PbxArray: header_arr = PbxArray() for i in header_dirs: np = os.path.normpath(i) - # Make sure Xcode will not split single path into separate entries, escaping space with a slash is not enought + # Make sure Xcode will not split single path into separate entries, escaping space with a slash is not enough item = f'"\\\"{np}\\\""' if ' ' in np else f'"{np}"' header_arr.add_item(item) return header_arr diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 0ffaceb7c7ad..f1c60712c92c 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -1102,7 +1102,7 @@ def codeblock(self) -> CodeBlockNode: e.ast = block raise - # Remaining whitespaces will not be catched since there are no more nodes + # Remaining whitespaces will not be caught since there are no more nodes for ws_token in self.current_ws: block.append_whitespaces(ws_token) self.current_ws = [] diff --git a/mesonbuild/scripts/python_info.py b/mesonbuild/scripts/python_info.py index 6aab380451a9..b006bc38acb0 100755 --- a/mesonbuild/scripts/python_info.py +++ b/mesonbuild/scripts/python_info.py @@ -100,7 +100,7 @@ def links_against_libpython(): except Exception: pass -# pypy supports modules targetting the limited api but +# pypy supports modules targeting the limited api but # does not use a special suffix to distinguish them: # https://doc.pypy.org/en/latest/cpython_differences.html#permitted-abi-tags-in-extensions if is_pypy: From 528696300afd3236e173242e1e09d1ac80dfec81 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Jan 2025 15:10:09 +0100 Subject: [PATCH 365/624] mesonlib: extract and optimize is_parent_path Introduce an alternative to os.path.commonpath(name, path) == path, which is a common idiom throughout Meson. Call it is_parent_path just like the existing static method in Generator. It is a bit faster and handles drives on Windows without the need for an exception handler. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 10 +++------- mesonbuild/backend/ninjabackend.py | 4 ++-- mesonbuild/build.py | 12 ++---------- mesonbuild/scripts/gtkdochelper.py | 7 +++++-- mesonbuild/utils/universal.py | 21 +++++++++++++++++++++ 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 88b3357be57d..9326127e66cb 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -28,7 +28,8 @@ from ..mesonlib import ( File, MachineChoice, MesonException, MesonBugException, OrderedSet, ExecutableSerialisation, EnvironmentException, - classify_unity_sources, get_compiler_for_source + classify_unity_sources, get_compiler_for_source, + is_parent_path, ) from ..options import OptionKey @@ -797,12 +798,7 @@ def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTar ): continue - try: - commonpath = os.path.commonpath((libdir, srcdir)) - except ValueError: # when paths are on different drives on Windows - commonpath = '' - - if commonpath == srcdir: + 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)) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 316d2253c6a6..251e63fd8548 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -31,7 +31,7 @@ File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine, ProgressBar, quote_arg ) -from ..mesonlib import get_compiler_for_source, has_path_sep +from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path from ..options import OptionKey from .backends import CleanTrees from ..build import GeneratedList, InvalidArguments @@ -1713,7 +1713,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ # Check if the vala file is in a subdir of --basedir abs_srcbasedir = os.path.join(self.environment.get_source_dir(), target.get_subdir()) abs_vala_file = os.path.join(self.environment.get_build_dir(), vala_file) - if PurePath(os.path.commonpath((abs_srcbasedir, abs_vala_file))) == PurePath(abs_srcbasedir): + if is_parent_path(abs_srcbasedir, abs_vala_file): vala_c_subdir = PurePath(abs_vala_file).parent.relative_to(abs_srcbasedir) vala_c_file = os.path.join(str(vala_c_subdir), vala_c_file) else: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 9f3af9fba585..6463c6f9eb2c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -24,7 +24,7 @@ File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, extract_as_list, typeslistify, stringlistify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, - PerMachineDefaultable, + is_parent_path, PerMachineDefaultable, MesonBugException, EnvironmentVariables, pickle_load, lazy_property, ) from .options import OptionKey @@ -1824,14 +1824,6 @@ def get_arglist(self, inname: str) -> T.List[str]: basename = os.path.splitext(plainname)[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] - @staticmethod - def is_parent_path(parent: str, trial: str) -> bool: - try: - common = os.path.commonpath((parent, trial)) - except ValueError: # Windows on different drives - return False - return pathlib.PurePath(common) == pathlib.PurePath(parent) - def process_files(self, files: T.Iterable[T.Union[str, File, 'CustomTarget', 'CustomTargetIndex', 'GeneratedList']], state: T.Union['Interpreter', 'ModuleState'], preserve_path_from: T.Optional[str] = None, @@ -1861,7 +1853,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, 'CustomTarget', 'Cu for f in fs: if preserve_path_from: abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir) - if not self.is_parent_path(preserve_path_from, abs_f): + 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) diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py index 06844289fc1a..a0b090584f63 100644 --- a/mesonbuild/scripts/gtkdochelper.py +++ b/mesonbuild/scripts/gtkdochelper.py @@ -7,7 +7,10 @@ import subprocess import shutil import argparse -from ..mesonlib import MesonException, Popen_safe, is_windows, is_cygwin, split_args +from ..mesonlib import ( + MesonException, Popen_safe, is_windows, is_cygwin, is_parent_path, + split_args, +) from . import destdir_join import typing as T @@ -112,7 +115,7 @@ def build_gtkdoc(source_root: str, build_root: str, doc_subdir: str, src_subdirs # FIXME: Use mesonlib.File objects so we don't need to do this if not os.path.isabs(f): f = os.path.join(doc_src, f) - elif os.path.commonpath([f, build_root]) == build_root: + elif is_parent_path(build_root, f): continue shutil.copyfile(f, os.path.join(abs_out, os.path.basename(f))) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 45c0b7c13a95..3481aaa88962 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -124,6 +124,7 @@ class _VerPickleLoadable(Protocol): 'is_netbsd', 'is_openbsd', 'is_osx', + 'is_parent_path', 'is_qnx', 'is_sunos', 'is_windows', @@ -1113,6 +1114,26 @@ def determine_worker_count(varnames: T.Optional[T.List[str]] = None) -> int: num_workers = 1 return num_workers +def is_parent_path(parent: str, trial: str) -> bool: + '''Checks if @trial is a file under the directory @parent. Both @trial and @parent should be + adequately normalized, though empty and '.' segments in @parent and @trial are accepted + and discarded, matching the behavior of os.path.commonpath. Either both or none should + be absolute.''' + assert os.path.isabs(parent) == os.path.isabs(trial) + if is_windows(): + parent = parent.replace('\\', '/') + trial = trial.replace('\\', '/') + + split_parent = parent.split('/') + split_trial = trial.split('/') + + split_parent = [c for c in split_parent if c and c != '.'] + split_trial = [c for c in split_trial if c and c != '.'] + + components = len(split_parent) + return len(split_trial) >= components and split_trial[:components] == split_parent + + def has_path_sep(name: str, sep: str = '/\\') -> bool: 'Checks if any of the specified @sep path separators are in @name' for each in sep: From e3dfcf6cba97f00a51d34f0feb67fe88d098fff5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 12:54:05 +0100 Subject: [PATCH 366/624] interpreter: memoize result of validate_within_subproject This saves about 1% of execution time; path manipulation is expensive. About 1% more could be saved by avoiding usage of "foo in bar.parents", which is an expensive way to say "bar.startswith(foo)". Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 51 ++++++++++++++++----------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a118b3c59790..dd91474eb45c 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -292,6 +292,7 @@ def __init__( self.sanity_check_ast() self.builtin.update({'meson': MesonMain(self.build, self)}) self.processed_buildfiles: T.Set[str] = set() + self.validated_cache: T.Set[str] = set() self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen self.subprojects: T.Dict[str, SubprojectHolder] = {} @@ -3129,27 +3130,37 @@ def validate_installable_file(fpath: Path) -> bool: # subproject files, as long as they are scheduled to be installed. if validate_installable_file(norm): return - norm = Path(os.path.abspath(Path(srcdir, subdir, fname))) - if os.path.isdir(norm): - inputtype = 'directory' - else: - inputtype = 'file' - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents: - return - if srcdir not in norm.parents: - # Grabbing files outside the source tree is ok. - # This is for vendor stuff like: - # - # /opt/vendorsdk/src/file_with_license_restrictions.c - return - project_root = Path(srcdir, self.root_subdir) - subproject_dir = project_root / self.subproject_dir - if norm == project_root: + + def do_validate_within_subproject(norm: Path) -> None: + if os.path.isdir(norm): + inputtype = 'directory' + else: + inputtype = 'file' + if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents: + return + if srcdir not in norm.parents: + # Grabbing files outside the source tree is ok. + # This is for vendor stuff like: + # + # /opt/vendorsdk/src/file_with_license_restrictions.c + return + project_root = Path(srcdir, self.root_subdir) + subproject_dir = project_root / self.subproject_dir + if norm == project_root: + return + if project_root not in norm.parents: + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.') + if subproject_dir == norm or subproject_dir in norm.parents: + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.') + + fname = os.path.join(subdir, fname) + if fname in self.validated_cache: return - if project_root not in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.') - if subproject_dir == norm or subproject_dir in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.') + + # Use os.path.abspath() to eliminate .. segments, but do not resolve symlinks + norm = Path(os.path.abspath(Path(srcdir, fname))) + do_validate_within_subproject(norm) + self.validated_cache.add(fname) @T.overload def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = True) -> T.List['mesonlib.File']: ... From a25e3e58aae434ce0788b947fcbe7ddbbff9f0e2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 13:53:06 +0100 Subject: [PATCH 367/624] interpreter: do not use pathlib in validate_within_subproject pathlib's implementation of Path iteration is expensive; "foo.parents" has quadratic complexity when building it: return self._path._from_parsed_parts(self._drv, self._root, self._tail[:-idx - 1]) and comparisons performed by "path in file.parents" potentially have the same issue. Use the newly introduced "is_parent_path" function to check whether a file is under a path in the same way. This removes Path from its hottest use. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index dd91474eb45c..ff93ac679482 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -19,7 +19,7 @@ from ..wrap import wrap, WrapMode from .. import mesonlib from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject, - FileMode, MachineChoice, listify, + FileMode, MachineChoice, is_parent_path, listify, extract_as_list, has_path_sep, path_is_in_root, PerMachine) from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram @@ -3109,8 +3109,8 @@ def check_clang_asan_lundef(self) -> None: # subproject than it is defined in (due to e.g. a # declare_dependency). def validate_within_subproject(self, subdir, fname): - srcdir = Path(self.environment.source_dir) - builddir = Path(self.environment.build_dir) + srcdir = self.environment.source_dir + builddir = self.environment.build_dir if isinstance(fname, P_OBJ.DependencyVariableString): def validate_installable_file(fpath: Path) -> bool: installablefiles: T.Set[Path] = set() @@ -3131,34 +3131,36 @@ def validate_installable_file(fpath: Path) -> bool: if validate_installable_file(norm): return - def do_validate_within_subproject(norm: Path) -> None: + def do_validate_within_subproject(norm: str) -> None: if os.path.isdir(norm): inputtype = 'directory' else: inputtype = 'file' - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents: + if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and is_parent_path(builddir, norm): return - if srcdir not in norm.parents: + + if not is_parent_path(srcdir, norm): # Grabbing files outside the source tree is ok. # This is for vendor stuff like: # # /opt/vendorsdk/src/file_with_license_restrictions.c return - project_root = Path(srcdir, self.root_subdir) - subproject_dir = project_root / self.subproject_dir - if norm == project_root: - return - if project_root not in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.') - if subproject_dir == norm or subproject_dir in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.') + + project_root = os.path.join(srcdir, self.root_subdir) + if not is_parent_path(project_root, norm): + name = os.path.basename(norm) + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} outside current (sub)project.') + + subproject_dir = os.path.join(project_root, self.subproject_dir) + if is_parent_path(subproject_dir, norm): + name = os.path.basename(norm) + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} from a nested subproject.') fname = os.path.join(subdir, fname) if fname in self.validated_cache: return - # Use os.path.abspath() to eliminate .. segments, but do not resolve symlinks - norm = Path(os.path.abspath(Path(srcdir, fname))) + norm = os.path.abspath(os.path.join(srcdir, fname)) do_validate_within_subproject(norm) self.validated_cache.add(fname) From 88fbd177c45ed4c92ffb7c8a0eab8204476d5c92 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 28 Jan 2025 16:02:25 -0500 Subject: [PATCH 368/624] Add cache to coredata.get_external_link_args --- mesonbuild/coredata.py | 2 ++ unittests/allplatformstests.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index cf32621461f6..1097d69ec554 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -9,6 +9,7 @@ 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 @@ -511,6 +512,7 @@ def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.List[str key = OptionKey(f'{lang}_args', machine=for_machine) return T.cast('T.List[str]', self.optstore.get_value(key)) + @lru_cache(maxsize=None) def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]: # mypy cannot analyze type of OptionKey linkkey = OptionKey(f'{lang}_link_args', machine=for_machine) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1c5b7c58811f..7f4e1e7883b2 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4484,12 +4484,15 @@ def DISABLED_test_env_flags_to_linker(self) -> None: # The compiler either invokes the linker or doesn't. Act accordingly. with mock.patch.object(cc_type, 'INVOKES_LINKER', True): + env.coredata.get_external_link_args.cache_clear() cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) self.assertEqual(sorted(link_args), sorted(['-DCFLAG', '-flto'])) + ## And one that doesn't #with mock.patch.object(cc_type, 'INVOKES_LINKER', False): + # env.coredata.get_external_link_args.cache_clear() # cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') # link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) # self.assertEqual(sorted(link_args), sorted(['-flto'])) From f8530da1fea055f5fcd3224108add1256c3c9ea2 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 28 Jan 2025 16:23:44 -0500 Subject: [PATCH 369/624] Optimize canonicalize_filename Using str.split is faster than Path.parts --- mesonbuild/backend/backends.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 9326127e66cb..1e5c00803fb3 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -829,7 +829,9 @@ def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTa @staticmethod @lru_cache(maxsize=None) def canonicalize_filename(fname: str) -> str: - parts = Path(fname).parts + if os.path.altsep is not None: + fname = fname.replace(os.path.altsep, os.path.sep) + parts = fname.split(os.path.sep) hashed = '' if len(parts) > 5: temp = '/'.join(parts[-5:]) From 52dd8815525113371d10413a7253a3f66eec8679 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 28 Jan 2025 16:49:10 -0500 Subject: [PATCH 370/624] Optimize CLikeCompiler._get_file_from_list() --- mesonbuild/compilers/mixins/clike.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 385883432591..b163407f7aa8 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1090,17 +1090,17 @@ def tuple_key(x: str) -> T.Tuple[int, ...]: return sorted(filtered, key=tuple_key, reverse=True) @classmethod - def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[Path]: - f = Path(directory) / pattern.format(libname) + def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[str]: + f = os.path.join(directory, pattern.format(libname)) # Globbing for OpenBSD if '*' in pattern: # NOTE: globbing matches directories and broken symlinks # so we have to do an isfile test on it later - return [Path(x) for x in cls._sort_shlibs_openbsd(glob.glob(str(f)))] + return cls._sort_shlibs_openbsd(glob.glob(f)) return [f] @staticmethod - def _get_file_from_list(env: Environment, paths: T.List[Path]) -> T.Optional[Path]: + 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 @@ -1108,16 +1108,16 @@ def _get_file_from_list(env: Environment, paths: T.List[Path]) -> T.Optional[Pat architecture. ''' for p in paths: - if p.is_file(): + if os.path.isfile(p): if env.machines.host.is_darwin() and env.machines.build.is_darwin(): # Run `lipo` and check if the library supports the arch we want - archs = mesonlib.darwin_get_object_archs(str(p)) + archs = mesonlib.darwin_get_object_archs(p) if not archs or env.machines.host.cpu_family not in archs: mlog.debug(f'Rejected {p}, supports {archs} but need {env.machines.host.cpu_family}') continue - return p + return Path(p) return None From f39637e1648060bddcdd4372983b8d9ca844caf9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 13 Jan 2025 17:48:46 +0100 Subject: [PATCH 371/624] compilers/rust: implement has_argument checks We're about to convert the `b_sanitize` option into a free-form array whose value gets verified via a compiler check. This conversion will also impact the Rust toolchain, which does not yet know to check for multiple arguments at once. Implement both `has_multi_arguments()` and `has_multi_link_arguments()` to prepare the code accordingly. --- mesonbuild/compilers/rust.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 475b7a402aa8..011c28f1d848 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2022 The Meson development team +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations @@ -12,7 +13,7 @@ from .. import options from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged from ..options import OptionKey -from .compilers import Compiler, clike_debug_args +from .compilers import Compiler, CompileCheckMode, clike_debug_args if T.TYPE_CHECKING: from ..coredata import MutableKeyedOptionDictType @@ -323,6 +324,12 @@ def get_rust_tool(self, name: str, env: Environment) -> T.List[str]: 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_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) class ClippyRustCompiler(RustCompiler): From 35ebae55412feed9d91e78138e02f0cf42681d65 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 27 Feb 2025 08:29:56 +0100 Subject: [PATCH 372/624] compilers/cuda: implement has_argument checks Same as the preceding commit, the CUDA toolchain does not yet know to perform compile checks for multiple arguments. Backfill required functions. --- mesonbuild/compilers/cuda.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 284f28486163..b987a9bb23dd 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2017 The Meson development team +# Copyright © 2024 Intel Corporation from __future__ import annotations @@ -14,10 +15,9 @@ EnvironmentException, Popen_safe, is_windows, LibType, version_compare ) -from .compilers import Compiler +from .compilers import Compiler, CompileCheckMode if T.TYPE_CHECKING: - from .compilers import CompileCheckMode from ..build import BuildTarget from ..coredata import MutableKeyedOptionDictType from ..dependencies import Dependency @@ -812,3 +812,11 @@ def get_profile_use_args(self) -> T.List[str]: def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: return self.host_compiler.get_assert_args(disable, env) + + def has_multi_arguments(self, args: T.List[str], env: Environment) -> 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) + + def has_multi_link_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]: + args = self._to_host_flags(self.linker.fatal_warnings() + args, phase=Phase.LINKER) + return self.compiles('int main(void) { return 0; }', env, extra_args=args, mode=CompileCheckMode.LINK) From 42a8cfc32b71b0d0c8c75e1337482b4a86922efa Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 8 Mar 2025 15:28:51 +0100 Subject: [PATCH 373/624] compilers/cuda: fix checking for multiple linker args When checking for multiple linker args we convert the supplied args to flags that the compiler understands. But besides these supplied args, we also try to convert linker flags that convert warnings into errors. This mechanism causes an error though because we don't know to convert these flags to linker flags: gcc: error: unrecognized command-line option '--warning-as-error'; did you mean '--warn-no-error'? ----------- ERROR: Linker nvcc does not support sanitizer arguments ['-Xcompiler=-fsanitize=address\\,undefined'] As you can see, the flag is passed to the underlying compiler, not to the underlying linker. The obvious fix would be to convert them to linker flags, which we can do by using `-Xlinker=` instead of `-Xcompiler=`. But that is incorrect, too: /nix/store/j7p46r8v9gcpbxx89pbqlh61zhd33gzv-binutils-2.43.1/bin/ld: unrecognized option '--warning-as-error' /nix/store/j7p46r8v9gcpbxx89pbqlh61zhd33gzv-binutils-2.43.1/bin/ld: use the --help option for usage information collect2: error: ld returned 1 exit status ----------- ERROR: Linker nvcc does not support sanitizer arguments ['-Xcompiler=-fsanitize=address\\,undefined'] Now we ended up passing the flag to the underlying linker, but the `--warning-as-error` flag isn't known by it. What we really ought to do is to pass on the flag to nvlink, which is the linker driver that controls the underlying linker. Do so by using `-Xnvlink=`, which fixes the bug. Signed-off-by: Patrick Steinhardt --- mesonbuild/compilers/cuda.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index b987a9bb23dd..134cd4ede82c 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -818,5 +818,6 @@ def has_multi_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bo return self.compiles('int main(void) { return 0; }', env, extra_args=args, mode=CompileCheckMode.COMPILE) def has_multi_link_arguments(self, args: T.List[str], env: Environment) -> T.Tuple[bool, bool]: - args = self._to_host_flags(self.linker.fatal_warnings() + args, phase=Phase.LINKER) + 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) From e629d191b99119c360605b056b954fff0905c83f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 27 Feb 2025 08:36:47 +0100 Subject: [PATCH 374/624] compilers: check for sanitizer arguments via a compiler check The `b_sanitize` option is used to specify which sanitizers to use. This option is implemented as a combo option, where it only allows a specific set of hardcoded choices. This implementation isn't quite scalable: - The number of available sanitizers is steadily growing, so we have to always catch up with what sanitizers exist out there. - Sanitizers can be combined more freely nowadays, but we only allow to combine the "address" and "undefined" sanitizers. - A hardcoded list is not a good match given that a choice existing as an option does not mean that it is supported by the compiler in the first place. Instead of hardcoding available options, it is way more future proof to instead allow arbitrary values and perform a compiler check. This makes us support new sanitizers readily while also providing good feedback to our users why a specific option might not be allowed. Implement the compiler checks for sanitizers as a first step. Note that this does not yet loosen the set of allowed sanitizers as we only accept hardcoded values as specified by the combo option. This restriction will be lifted in the next commit. --- mesonbuild/compilers/compilers.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 5b640781ea0d..27bc44b41623 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -308,7 +308,13 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env try: sanitize = env.coredata.get_option_for_target(target, 'b_sanitize') assert isinstance(sanitize, str) - args += compiler.sanitizer_compile_args(sanitize) + sanitize_args = compiler.sanitizer_compile_args(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]: + raise MesonException(f'Compiler {compiler.name_string()} does not support sanitizer arguments {sanitize_args}') + args.extend(sanitize_args) except KeyError: pass try: @@ -371,7 +377,13 @@ def get_base_link_args(target: 'BuildTarget', try: sanitizer = env.coredata.get_option_for_target(target, 'b_sanitize') assert isinstance(sanitizer, str) - args += linker.sanitizer_link_args(sanitizer) + sanitizer_args = linker.sanitizer_link_args(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]: + raise MesonException(f'Linker {linker.name_string()} does not support sanitizer arguments {sanitizer_args}') + args.extend(sanitizer_args) except KeyError: pass try: From 43ea11ea49948635b1d672fef1bd397233b65b19 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 7 Mar 2025 11:41:18 +0100 Subject: [PATCH 375/624] compilers: convert `b_sanitize` to a free-form array option In the preceding commit we have started to perform compiler checks for the value of `b_sanitize`, which allows us to detect sanitizers that aren't supported by the compiler toolchain. But we haven't yet loosened the option itself to accept arbitrary values, so until now it's still only possible to pass sanitizer combinations known by Meson, which is quite restrictive. Lift that restriction by adapting the `b_sanitize` option to become a free-form array. Like this, users can pass whatever combination of comma-separated sanitizers to Meson, which will then figure out whether that combination is supported via the compiler checks. This lifts a couple of restrictions and makes the supporting infrastructure way more future proof. A couple of notes regarding backwards compatibility: - All previous values of `b_sanitize` will remain valid as the syntax for free-form array values and valid combo choices is the same. We also treat 'none' specially so that we know to convert it into an empty array. - Even though the option has been converted into a free-form array, callers of `get_option('b_sanitize')` continue to get a string as value. We may eventually want to introduce a kwarg to alter this behaviour, but for now it is expected to be good enough for most use cases. Fixes #8283 Fixes #7761 Fixes #5154 Fixes #1582 Co-authored-by: Dylan Baker Signed-off-by: Patrick Steinhardt --- docs/markdown/Builtin-options.md | 18 +++++++++++---- docs/markdown/snippets/b_sanitizer_changes.md | 17 ++++++++++++++ mesonbuild/backend/vs2010backend.py | 3 ++- mesonbuild/compilers/compilers.py | 16 ++++++++------ mesonbuild/compilers/cuda.py | 6 ++--- mesonbuild/compilers/mixins/gnu.py | 11 +++++----- mesonbuild/compilers/mixins/islinker.py | 3 ++- mesonbuild/compilers/mixins/visualstudio.py | 11 +++++----- mesonbuild/interpreter/interpreter.py | 17 ++++++++++---- mesonbuild/linkers/linkers.py | 22 +++++++++++-------- mesonbuild/modules/gnome.py | 4 ++-- test cases/unit/125 sanitizers/meson.build | 8 +++++++ unittests/allplatformstests.py | 5 +++-- unittests/linuxliketests.py | 22 +++++++++++++++++++ 14 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 docs/markdown/snippets/b_sanitizer_changes.md create mode 100644 test cases/unit/125 sanitizers/meson.build diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index ffbab47d879d..e1686f8d2512 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -231,10 +231,20 @@ available on all platforms or with all compilers: | b_pie | false | true, false | Build position-independent executables (since 0.49.0) | | b_vscrt | from_buildtype | none, md, mdd, mt, mtd, from_buildtype, static_from_buildtype | VS runtime library to use (since 0.48.0) (static_from_buildtype since 0.56.0) | -The value of `b_sanitize` can be one of: `none`, `address`, `thread`, -`undefined`, `memory`, `leak`, `address,undefined`, but note that some -compilers might not support all of them. For example Visual Studio -only supports the address sanitizer. +The default and possible values of sanitizers changed in 1.8. Before 1.8 they +were string values, and restricted to a specific subset of values: `none`, +`address`, `thread`, `undefined`, `memory`, `leak`, or `address,undefined`. In +1.8 it was changed to a free form array of sanitizers, which are checked by a +compiler and linker check. For backwards compatibility reasons +`get_option('b_sanitize')` continues to return a string with the array values +separated by a comma. Furthermore: + + - If the `b_sanitize` option is empty, the `'none'` string is returned. + + - If it contains only the values `'address'` and `'undefined'`, they are + always returned as the `'address,undefined'` string, in this order. + + - Otherwise, the array elements are returned in undefined order. \* < 0 means disable, == 0 means automatic selection, > 0 sets a specific number to use diff --git a/docs/markdown/snippets/b_sanitizer_changes.md b/docs/markdown/snippets/b_sanitizer_changes.md new file mode 100644 index 000000000000..f726d700ae32 --- /dev/null +++ b/docs/markdown/snippets/b_sanitizer_changes.md @@ -0,0 +1,17 @@ +## Changes to the b_sanitize option + +Before 1.8 the `b_sanitize` option was a combo option, which is an enumerated +set of values. In 1.8 this was changed to a free-form array of options where +available sanitizers are not hardcoded anymore but instead verified via a +compiler check. + +This solves a number of longstanding issues such as: + + - Sanitizers may be supported by a compiler, but not on a specific platform + (OpenBSD). + - New sanitizers are not recognized by Meson. + - Using sanitizers in previously-unsupported combinations. + +To not break backwards compatibility, calling `get_option('b_sanitize')` +continues to return the configured value as a string, with a guarantee that +`address,undefined` remains ordered. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 101508315ab7..feef3a72125c 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2014-2016 The Meson development team +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations import copy @@ -272,7 +273,7 @@ def generate(self, try: self.sanitize = self.environment.coredata.get_option(OptionKey('b_sanitize')) except KeyError: - self.sanitize = 'none' + self.sanitize = [] sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') projlist = self.generate_projects(vslite_ctx) self.gen_testproj() diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 27bc44b41623..f0744f80bb91 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -222,9 +222,7 @@ class CompileCheckMode(enum.Enum): options.UserComboOption('b_lto_mode', 'Select between different LTO modes.', 'default', choices=['default', 'thin']), options.UserBooleanOption('b_thinlto_cache', 'Use LLVM ThinLTO caching for faster incremental builds', False), options.UserStringOption('b_thinlto_cache_dir', 'Directory to store ThinLTO cache objects', ''), - options.UserComboOption( - 'b_sanitize', 'Code sanitizer to use', 'none', - choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']), + options.UserStringArrayOption('b_sanitize', 'Code sanitizer to use', []), options.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), options.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), options.UserComboOption( @@ -307,7 +305,9 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env pass try: sanitize = env.coredata.get_option_for_target(target, 'b_sanitize') - assert isinstance(sanitize, str) + assert isinstance(sanitize, list) + if sanitize == ['none']: + sanitize = [] sanitize_args = compiler.sanitizer_compile_args(sanitize) # We consider that if there are no sanitizer arguments returned, then # the language doesn't support them. @@ -376,7 +376,9 @@ def get_base_link_args(target: 'BuildTarget', pass try: sanitizer = env.coredata.get_option_for_target(target, 'b_sanitize') - assert isinstance(sanitizer, str) + assert isinstance(sanitizer, list) + if sanitizer == ['none']: + sanitizer = [] sanitizer_args = linker.sanitizer_link_args(sanitizer) # We consider that if there are no sanitizer arguments returned, then # the language doesn't support them. @@ -1044,10 +1046,10 @@ def get_lto_link_args(self, *, 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: str) -> T.List[str]: + def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]: return [] - def sanitizer_link_args(self, value: str) -> T.List[str]: + def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]: return self.linker.sanitizer_args(value) def get_asneeded_args(self) -> T.List[str]: diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 134cd4ede82c..612643c8f787 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2017 The Meson development team -# Copyright © 2024 Intel Corporation +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations @@ -700,10 +700,10 @@ def get_optimization_args(self, optimization_level: str) -> T.List[str]: # 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: str) -> T.List[str]: + 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_link_args(self, value: str) -> T.List[str]: + 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 get_debug_args(self, is_debug: bool) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 4dc344519a08..70fd9ee7d8d2 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019-2022 The meson development team +# Copyright © 2023 Intel Corporation from __future__ import annotations @@ -495,11 +496,11 @@ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T. # for their specific arguments return ['-flto'] - def sanitizer_compile_args(self, value: str) -> T.List[str]: - if value == 'none': - return [] - args = ['-fsanitize=' + value] - if 'address' in value: # for -fsanitize=address,undefined + def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]: + if not value: + return value + args = ['-fsanitize=' + ','.join(value)] + if 'address' in value: args.append('-fno-omit-frame-pointer') return args diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 44040a7a2824..3f3561972188 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The Meson development team +# Copyright © 2023 Intel Corporation from __future__ import annotations @@ -37,7 +38,7 @@ class BasicLinkerIsCompilerMixin(Compiler): functionality itself. """ - def sanitizer_link_args(self, value: str) -> T.List[str]: + def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]: return [] def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default', diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 30127eca479c..275e7ab0a3c9 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019 The meson development team +# Copyright © 2023 Intel Corporation from __future__ import annotations @@ -166,12 +167,10 @@ def get_compile_only_args(self) -> T.List[str]: def get_no_optimization_args(self) -> T.List[str]: return ['/Od', '/Oi-'] - def sanitizer_compile_args(self, value: str) -> T.List[str]: - if value == 'none': - return [] - if value != 'address': - raise mesonlib.MesonException('VS only supports address sanitizer at the moment.') - return ['/fsanitize=address'] + def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]: + if not value: + return value + return [f'/fsanitize={",".join(value)}'] def get_output_args(self, outputname: str) -> T.List[str]: if self.mode == 'PREPROCESSOR': diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ff93ac679482..42e6243e1eb0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1068,14 +1068,14 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, @typed_pos_args('get_option', str) @noKwargs - def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], - kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']: + def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], + kwargs: kwtypes.FuncGetOption) -> T.Union[options.UserOption, 'TYPE_var']: optname = args[0] + if ':' in optname: raise InterpreterException('Having a colon in option name is forbidden, ' 'projects are not allowed to directly access ' 'options of other subprojects.') - if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None: raise InterpreterException(f'Invalid option name {optname!r}') @@ -1096,6 +1096,15 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], ocopy.name = optname ocopy.value = value return ocopy + elif optname == 'b_sanitize': + assert isinstance(value_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 + # likely good enough for most usecases. + if not value: + return 'none' + return ','.join(sorted(value)) elif isinstance(value_object, options.UserOption): if isinstance(value_object.value, str): return P_OBJ.OptionString(value, f'{{{optname}}}') @@ -3090,7 +3099,7 @@ def check_clang_asan_lundef(self) -> None: 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') != 'none'): + self.coredata.optstore.get_value('b_sanitize')): value = self.coredata.optstore.get_value('b_sanitize') mlog.warning(textwrap.dedent(f'''\ Trying to use {value} sanitizer on Clang with b_lundef. diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 0dc2c0bf599d..b114d498bbba 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2022 The Meson development team +# Copyright © 2023 Intel Corporation from __future__ import annotations @@ -223,7 +224,7 @@ def get_lto_args(self) -> T.List[str]: def get_thinlto_cache_args(self, path: str) -> T.List[str]: return [] - def sanitizer_args(self, value: str) -> T.List[str]: + def sanitizer_args(self, value: T.List[str]) -> T.List[str]: return [] def get_asneeded_args(self) -> T.List[str]: @@ -599,6 +600,9 @@ def get_std_shared_lib_args(self) -> T.List[str]: def get_search_args(self, dirname: str) -> T.List[str]: return ['-L' + dirname] + def sanitizer_args(self, value: T.List[str]) -> T.List[str]: + return [] + class GnuLikeDynamicLinkerMixin(DynamicLinkerBase): @@ -654,10 +658,10 @@ def get_allow_undefined_args(self) -> T.List[str]: def get_lto_args(self) -> T.List[str]: return ['-flto'] - def sanitizer_args(self, value: str) -> T.List[str]: - if value == 'none': - return [] - return ['-fsanitize=' + value] + def sanitizer_args(self, value: T.List[str]) -> T.List[str]: + if not value: + return value + return [f'-fsanitize={",".join(value)}'] def get_coverage_args(self) -> T.List[str]: return ['--coverage'] @@ -811,10 +815,10 @@ def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: def get_coverage_args(self) -> T.List[str]: return ['--coverage'] - def sanitizer_args(self, value: str) -> T.List[str]: - if value == 'none': - return [] - return ['-fsanitize=' + value] + def sanitizer_args(self, value: T.List[str]) -> T.List[str]: + if not value: + return value + return [f'-fsanitize={",".join(value)}'] def no_undefined_args(self) -> T.List[str]: # We used to emit -undefined,error, but starting with Xcode 15 / diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index dffc61574887..e3b1f3d7c55e 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2015-2016 The Meson development team +# Copyright © 2023-2024 Intel Corporation '''This module provides helper functions for Gnome/GLib related functionality such as gobject-introspection, gresources and gtk-doc''' @@ -912,9 +913,8 @@ def _get_langs_compilers_flags(state: 'ModuleState', langs_compilers: T.List[T.T cflags += state.project_args[lang] if OptionKey('b_sanitize') in compiler.base_options: sanitize = state.environment.coredata.optstore.get_value('b_sanitize') - assert isinstance(sanitize, str) + assert isinstance(sanitize, list) cflags += compiler.sanitizer_compile_args(sanitize) - sanitize = sanitize.split(',') # These must be first in ldflags if 'address' in sanitize: internal_ldflags += ['-lasan'] diff --git a/test cases/unit/125 sanitizers/meson.build b/test cases/unit/125 sanitizers/meson.build new file mode 100644 index 000000000000..b42fb35d4968 --- /dev/null +++ b/test cases/unit/125 sanitizers/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2023-2024 Intel Corporation + +project('sanitizer', 'c', meson_version : '>= 1.8') + +summary({ + 'value': get_option('b_sanitize'), +}, section: 'summary') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7f4e1e7883b2..84d5245d9551 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2021 The Meson development team +# Copyright © 2023-2024 Intel Corporation import subprocess import re @@ -2770,7 +2771,7 @@ def test_command_line(self): 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('b_sanitize'), ['thread']) self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dbar']) self.setconf(['--bindir=bar', '--bindir=foo', '-Dbuildtype=release', '-Dbuildtype=plain', @@ -2779,7 +2780,7 @@ def test_command_line(self): 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('b_sanitize'), ['address']) self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dfoo']) self.wipe() except KeyError: diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 08eb4b93a1fe..01862b61e5ae 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1930,3 +1930,25 @@ def test_persp_options(self): self.check_has_flag(compdb, mainsrc, '-O3') self.check_has_flag(compdb, sub1src, '-O2') self.check_has_flag(compdb, sub2src, '-O2') + + def test_sanitizers(self): + testdir = os.path.join(self.unit_test_dir, '125 sanitizers') + + with self.subTest('no b_sanitize value'): + try: + out = self.init(testdir) + self.assertRegex(out, 'value *: *none') + finally: + self.wipe() + + for value, expected in { '': 'none', + 'none': 'none', + 'address': 'address', + 'undefined,address': 'address,undefined', + 'address,undefined': 'address,undefined' }.items(): + with self.subTest('b_sanitize=' + value): + try: + out = self.init(testdir, extra_args=['-Db_sanitize=' + value]) + self.assertRegex(out, 'value *: *' + expected) + finally: + self.wipe() From 4fa52925459dac650bf053715987ee7beb3b23c1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 08:14:38 -0800 Subject: [PATCH 376/624] coredata: replace get_option with optstore.get_value_for This is an old method, that is now just a wrapper around the OptionStore method, that doesn't add any value. It's also an option related method attached to the CoreData instead of the OptionStore, so useless and a layering violation. --- mesonbuild/ast/introspection.py | 4 ++-- mesonbuild/backend/backends.py | 10 +++++----- mesonbuild/backend/ninjabackend.py | 6 +++--- mesonbuild/backend/vs2010backend.py | 20 +++++++++---------- mesonbuild/backend/xcodebackend.py | 4 ++-- mesonbuild/build.py | 8 ++++---- mesonbuild/cmake/common.py | 2 +- mesonbuild/cmake/interpreter.py | 2 +- mesonbuild/coredata.py | 3 --- mesonbuild/dependencies/base.py | 4 ++-- mesonbuild/dependencies/boost.py | 2 +- mesonbuild/dependencies/dub.py | 2 +- mesonbuild/dependencies/misc.py | 2 +- mesonbuild/dependencies/python.py | 4 ++-- mesonbuild/dependencies/scalapack.py | 2 +- mesonbuild/environment.py | 14 ++++++------- mesonbuild/interpreter/compiler.py | 4 ++-- mesonbuild/interpreter/dependencyfallbacks.py | 6 +++--- mesonbuild/interpreter/interpreter.py | 18 ++++++++--------- mesonbuild/interpreter/mesonmain.py | 6 +++--- mesonbuild/mcompile.py | 4 ++-- mesonbuild/mdevenv.py | 6 +++--- mesonbuild/mdist.py | 4 ++-- mesonbuild/minit.py | 2 +- mesonbuild/minstall.py | 4 ++-- mesonbuild/mintro.py | 2 +- mesonbuild/modules/__init__.py | 2 +- mesonbuild/modules/cmake.py | 6 +++--- mesonbuild/modules/external_project.py | 8 ++++---- mesonbuild/modules/gnome.py | 8 ++++---- mesonbuild/modules/hotdoc.py | 2 +- mesonbuild/modules/i18n.py | 2 +- mesonbuild/modules/pkgconfig.py | 10 +++++----- mesonbuild/modules/python.py | 6 +++--- mesonbuild/mtest.py | 4 ++-- mesonbuild/munstable_coredata.py | 2 +- mesonbuild/scripts/regen_checker.py | 2 +- 37 files changed, 97 insertions(+), 100 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 69ffc55f7e2e..b042eb37a07c 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2018 The Meson development team -# Copyright © 2024 Intel Corporation +# Copyright © 2024-2025 Intel Corporation # This class contains the basic functionality needed to run any interpreter # or an interpreter-based tool @@ -313,7 +313,7 @@ def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[BaseNode]: 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.get_option(OptionKey('default_library')) + default_library = self.coredata.optstore.get_value_for(OptionKey('default_library')) if default_library == 'shared': return self.build_target(node, args, kwargs, SharedLibrary) elif default_library == 'static': diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 1e5c00803fb3..feb002e54665 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -373,7 +373,7 @@ def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) if isinstance(target, build.RunTarget): # this produces no output, only a dummy top-level name dirname = '' - elif self.environment.coredata.get_option(OptionKey('layout')) == 'mirror': + elif self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror': dirname = target.get_subdir() else: dirname = 'meson-out' @@ -815,7 +815,7 @@ def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTar def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex] ) -> T.Tuple[str, ...]: result: OrderedSet[str] - if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror': + if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror': # Need a copy here result = OrderedSet(target.get_link_dep_subdirs()) else: @@ -1335,7 +1335,7 @@ def construct_target_rel_paths(self, t: T.Union[build.Target, build.CustomTarget def generate_depmf_install(self, d: InstallData) -> None: depmf_path = self.build.dep_manifest_name if depmf_path is None: - option_dir = self.environment.coredata.get_option(OptionKey('licensedir')) + option_dir = self.environment.coredata.optstore.get_value_for(OptionKey('licensedir')) assert isinstance(option_dir, str), 'for mypy' if option_dir: depmf_path = os.path.join(option_dir, 'depmf.json') @@ -1666,7 +1666,7 @@ def create_install_data(self) -> InstallData: # TODO go through all candidates, like others strip_bin = [detect.defaults['strip'][0]] - umask = self.environment.coredata.get_option(OptionKey('install_umask')) + umask = self.environment.coredata.optstore.get_value_for(OptionKey('install_umask')) assert isinstance(umask, (str, int)), 'for mypy' d = InstallData(self.environment.get_source_dir(), @@ -1698,7 +1698,7 @@ def guess_install_tag(self, fname: str, outdir: T.Optional[str] = None) -> T.Opt bindir = Path(prefix, self.environment.get_bindir()) libdir = Path(prefix, self.environment.get_libdir()) incdir = Path(prefix, self.environment.get_includedir()) - _ldir = self.environment.coredata.get_option(OptionKey('localedir')) + _ldir = self.environment.coredata.optstore.get_value_for(OptionKey('localedir')) assert isinstance(_ldir, str), 'for mypy' localedir = Path(prefix, _ldir) dest_path = Path(prefix, outdir, Path(fname).name) if outdir else Path(prefix, fname) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 251e63fd8548..f698a2b01a03 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -614,7 +614,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) # 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.coredata.get_option(OptionKey('vsenv')): + if self.environment.coredata.optstore.get_value_for(OptionKey('vsenv')): builddir = Path(self.environment.get_build_dir()) try: # For prettier printing, reduce to a relative path. If @@ -1340,9 +1340,9 @@ def generate_install(self) -> None: def generate_tests(self) -> None: self.serialize_tests() cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild'] - if not self.environment.coredata.get_option(OptionKey('stdsplit')): + if not self.environment.coredata.optstore.get_value_for(OptionKey('stdsplit')): cmd += ['--no-stdsplit'] - if self.environment.coredata.get_option(OptionKey('errorlogs')): + if self.environment.coredata.optstore.get_value_for(OptionKey('errorlogs')): cmd += ['--print-errorlogs'] elem = self.create_phony_target('test', 'CUSTOM_COMMAND', ['all', 'meson-test-prereq', 'PHONY']) elem.add_item('COMMAND', cmd) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index feef3a72125c..59ddb9c26477 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -267,11 +267,11 @@ def generate(self, else: raise MesonException('Unsupported Visual Studio platform: ' + build_machine) - self.buildtype = self.environment.coredata.get_option(OptionKey('buildtype')) - self.optimization = self.environment.coredata.get_option(OptionKey('optimization')) - self.debug = self.environment.coredata.get_option(OptionKey('debug')) + self.buildtype = self.environment.coredata.optstore.get_value_for(OptionKey('buildtype')) + self.optimization = self.environment.coredata.optstore.get_value_for(OptionKey('optimization')) + self.debug = self.environment.coredata.optstore.get_value_for(OptionKey('debug')) try: - self.sanitize = self.environment.coredata.get_option(OptionKey('b_sanitize')) + self.sanitize = self.environment.coredata.optstore.get_value_for(OptionKey('b_sanitize')) except KeyError: self.sanitize = [] sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') @@ -422,7 +422,7 @@ def generate_solution(self, sln_filename: str, projlist: T.List[Project]) -> Non ofile.write('# Visual Studio %s\n' % self.sln_version_comment) prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n' for prj in projlist: - if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror': + if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror': self.generate_solution_dirs(ofile, prj[1].parents) target = self.build.targets[prj[0]] lang = 'default' @@ -1791,7 +1791,7 @@ def path_normalize_add(path, lis): # build system as possible. self.add_target_deps(root, target) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) - if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror': + if self.environment.coredata.optstore.get_value_for(OptionKey('layout')) == 'mirror': self.gen_vcxproj_filters(target, ofname) return True @@ -1960,9 +1960,9 @@ def gen_testproj(self): meson_build_dir_for_buildtype = build_dir_tail[:-2] + buildtype # Get the buildtype suffixed 'builddir_[debug/release/etc]' from 'builddir_vs', for example. proj_to_build_dir_for_buildtype = str(os.path.join(proj_to_multiconfigured_builds_parent_dir, meson_build_dir_for_buildtype)) test_cmd = f'{nmake_base_meson_command} test -C "{proj_to_build_dir_for_buildtype}" --no-rebuild' - if not self.environment.coredata.get_option(OptionKey('stdsplit')): + if not self.environment.coredata.optstore.get_value_for(OptionKey('stdsplit')): test_cmd += ' --no-stdsplit' - if self.environment.coredata.get_option(OptionKey('errorlogs')): + if self.environment.coredata.optstore.get_value_for(OptionKey('errorlogs')): test_cmd += ' --print-errorlogs' condition = f'\'$(Configuration)|$(Platform)\'==\'{buildtype}|{self.platform}\'' prop_group = ET.SubElement(root, 'PropertyGroup', Condition=condition) @@ -1984,9 +1984,9 @@ def gen_testproj(self): ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' # FIXME: No benchmarks? test_command = self.environment.get_build_command() + ['test', '--no-rebuild'] - if not self.environment.coredata.get_option(OptionKey('stdsplit')): + if not self.environment.coredata.optstore.get_value_for(OptionKey('stdsplit')): test_command += ['--no-stdsplit'] - if self.environment.coredata.get_option(OptionKey('errorlogs')): + if self.environment.coredata.optstore.get_value_for(OptionKey('errorlogs')): test_command += ['--print-errorlogs'] self.serialize_tests() self.add_custom_build(root, 'run_tests', '"%s"' % ('" "'.join(test_command))) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 7108e72a71e6..e5c631fff920 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -230,7 +230,7 @@ class XCodeBackend(backends.Backend): def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): super().__init__(build, interpreter) self.project_uid = self.environment.coredata.lang_guids['default'].replace('-', '')[:24] - self.buildtype = T.cast('str', self.environment.coredata.get_option(OptionKey('buildtype'))) + self.buildtype = T.cast('str', self.environment.coredata.optstore.get_value_for(OptionKey('buildtype'))) self.project_conflist = self.gen_id() self.maingroup_id = self.gen_id() self.all_id = self.gen_id() @@ -272,7 +272,7 @@ def gen_id(self) -> str: @functools.lru_cache(maxsize=None) def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str: - dirname = os.path.join(target.get_subdir(), T.cast('str', self.environment.coredata.get_option(OptionKey('buildtype')))) + 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 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6463c6f9eb2c..a883c3b28fb3 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1726,7 +1726,7 @@ def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None: 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]: - bl_type = self.environment.coredata.get_option(OptionKey('default_both_libraries')) + bl_type = self.environment.coredata.optstore.get_value_for(OptionKey('default_both_libraries')) if bl_type == 'auto': if isinstance(self, StaticLibrary): bl_type = 'static' @@ -2026,7 +2026,7 @@ def post_init(self) -> None: machine.is_windows() and ('cs' in self.compilers or self.uses_rust() or self.get_using_msvc()) # .pdb file is created only when debug symbols are enabled - and self.environment.coredata.get_option(OptionKey("debug")) + and self.environment.coredata.optstore.get_value_for(OptionKey("debug")) ) if create_debug_file: # If the target is has a standard exe extension (i.e. 'foo.exe'), @@ -2308,14 +2308,14 @@ def determine_filenames(self): # 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.get_option(OptionKey("debug")) + 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 = '' # Import library is called foo.lib import_filename_tpl = '{0.prefix}{0.name}.lib' # .pdb file is only created when debug symbols are enabled - create_debug_file = self.environment.coredata.get_option(OptionKey("debug")) + 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 diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py index e3ba76b9e6e4..7644c0b966f6 100644 --- a/mesonbuild/cmake/common.py +++ b/mesonbuild/cmake/common.py @@ -105,7 +105,7 @@ def _flags_to_list(raw: str) -> T.List[str]: return res def cmake_get_generator_args(env: 'Environment') -> T.List[str]: - backend_name = env.coredata.get_option(OptionKey('backend')) + backend_name = env.coredata.optstore.get_value_for(OptionKey('backend')) assert isinstance(backend_name, str) assert backend_name in backend_generator_map return ['-G', backend_generator_map[backend_name]] diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 27ce54e2074d..53f4f193e6da 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -828,7 +828,7 @@ def configure(self, extra_cmake_options: T.List[str]) -> CMakeExecutor: 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 - buildtype = T.cast('str', self.env.coredata.get_option(OptionKey('buildtype'))) + buildtype = T.cast('str', self.env.coredata.optstore.get_value_for(OptionKey('buildtype'))) if buildtype in BUILDTYPE_MAP: cmake_args += [f'-DCMAKE_BUILD_TYPE={BUILDTYPE_MAP[buildtype]}'] trace_args = self.trace.trace_args() diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 1097d69ec554..f1286aa2ec90 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -397,9 +397,6 @@ def init_backend_options(self, backend_name: str) -> None: 'Default project to execute in Visual Studio', '')) - def get_option(self, key: OptionKey) -> ElementaryOptionValues: - return self.optstore.get_value_for(key.name, key.subproject) - def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> ElementaryOptionValues: if isinstance(key, str): assert ':' not in key diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index af370955770e..38bfc0822731 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2013-2018 The Meson development team -# Copyright © 2024 Intel Corporation +# Copyright © 2024-2025 Intel Corporation # This file contains the detection logic for external dependencies. # Custom logic for several other packages are in separate files. @@ -403,7 +403,7 @@ def __init__(self, type_name: DependencyTypeName, environment: 'Environment', kw self.version_reqs: T.Optional[T.List[str]] = version_reqs self.required = kwargs.get('required', True) self.silent = kwargs.get('silent', False) - self.static = kwargs.get('static', self.env.coredata.get_option(OptionKey('prefer_static'))) + self.static = kwargs.get('static', self.env.coredata.optstore.get_value_for(OptionKey('prefer_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') diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 0c613205f5d2..662f9851b18c 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -341,7 +341,7 @@ def get_link_args(self) -> T.List[str]: class BoostDependency(SystemDependency): def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None: super().__init__('boost', environment, kwargs, language='cpp') - buildtype = environment.coredata.get_option(OptionKey('buildtype')) + buildtype = environment.coredata.optstore.get_value_for(OptionKey('buildtype')) assert isinstance(buildtype, str) self.debug = buildtype.startswith('debug') self.multithreading = kwargs.get('threading', 'multi') == 'multi' diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index 6b6d2830675f..ac137e399efd 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -121,7 +121,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. dub_arch = self.compiler.arch # we need to know the build type as well - dub_buildtype = str(environment.coredata.get_option(OptionKey('buildtype'))) + dub_buildtype = str(environment.coredata.optstore.get_value_for(OptionKey('buildtype'))) # MESON types: choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), # DUB types: debug (default), plain, release, release-debug, release-nobounds, unittest, profile, profile-gc, # docs, ddox, cov, unittest-cov, syntax and custom diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 6a2a73f2f0e8..3ab2194e31ce 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -572,7 +572,7 @@ def shaderc_factory(env: 'Environment', shared_libs = ['shaderc'] static_libs = ['shaderc_combined', 'shaderc_static'] - if kwargs.get('static', env.coredata.get_option(OptionKey('prefer_static'))): + if kwargs.get('static', env.coredata.optstore.get_value_for(OptionKey('prefer_static'))): c = [functools.partial(PkgConfigDependency, name, env, kwargs) for name in static_libs + shared_libs] else: diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index f7417717e175..ca02d6743090 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -244,9 +244,9 @@ def get_windows_link_args(self, limited_api: bool) -> T.Optional[T.List[str]]: # Python itself (except with pybind11, which has an ugly # hack to work around this) - so emit a warning to explain # the cause of the expected link error. - buildtype = self.env.coredata.get_option(OptionKey('buildtype')) + buildtype = self.env.coredata.optstore.get_value_for(OptionKey('buildtype')) assert isinstance(buildtype, str) - debug = self.env.coredata.get_option(OptionKey('debug')) + debug = self.env.coredata.optstore.get_value_for(OptionKey('debug')) # `debugoptimized` buildtype may not set debug=True currently, see gh-11645 is_debug_build = debug or buildtype == 'debug' vscrt_debug = False diff --git a/mesonbuild/dependencies/scalapack.py b/mesonbuild/dependencies/scalapack.py index e50338710dd3..c04d1f51fb47 100644 --- a/mesonbuild/dependencies/scalapack.py +++ b/mesonbuild/dependencies/scalapack.py @@ -28,7 +28,7 @@ def scalapack_factory(env: 'Environment', for_machine: 'MachineChoice', candidates: T.List['DependencyGenerator'] = [] if DependencyMethods.PKGCONFIG in methods: - static_opt = kwargs.get('static', env.coredata.get_option(OptionKey('prefer_static'))) + static_opt = kwargs.get('static', 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)) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index da29d6c3c641..5640b9de3196 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -964,25 +964,25 @@ def get_static_lib_dir(self) -> str: return self.get_libdir() def get_prefix(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('prefix'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('prefix'))) def get_libdir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('libdir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('libdir'))) def get_libexecdir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('libexecdir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('libexecdir'))) def get_bindir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('bindir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('bindir'))) def get_includedir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('includedir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('includedir'))) def get_mandir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('mandir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('mandir'))) def get_datadir(self) -> str: - return _as_str(self.coredata.get_option(OptionKey('datadir'))) + return _as_str(self.coredata.optstore.get_value_for(OptionKey('datadir'))) def get_compiler_system_lib_dirs(self, for_machine: MachineChoice) -> T.List[str]: for comp in self.coredata.compilers[for_machine].values(): diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 92f63f0d762e..303af124043a 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2021 The Meson development team -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021-2025 Intel Corporation from __future__ import annotations import collections @@ -716,7 +716,7 @@ def find_library_method(self, args: T.Tuple[str], kwargs: 'FindLibraryKW') -> 'd search_dirs = extract_search_dirs(kwargs) - prefer_static = self.environment.coredata.get_option(OptionKey('prefer_static')) + prefer_static = self.environment.coredata.optstore.get_value_for(OptionKey('prefer_static')) if kwargs['static'] is True: libtype = mesonlib.LibType.STATIC elif kwargs['static'] is False: diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index 6f5a0e47e378..0ebfe3bd578f 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2021-2024 The Meson Developers -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021-2025 Intel Corporation from __future__ import annotations @@ -326,8 +326,8 @@ def lookup(self, kwargs: TYPE_nkwargs, force_fallback: bool = False) -> Dependen return self._notfound_dependency() # Check if usage of the subproject fallback is forced - wrap_mode = WrapMode.from_string(self.coredata.get_option(OptionKey('wrap_mode'))) - force_fallback_for = self.coredata.get_option(OptionKey('force_fallback_for')) + wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) + 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 self.forcefallback = (force_fallback or diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 42e6243e1eb0..14de76888637 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1135,10 +1135,10 @@ def set_backend(self) -> None: if OptionKey('genvslite') in self.user_defined_options.cmd_line_options: # Use of the '--genvslite vsxxxx' option ultimately overrides any '--backend xxx' # option the user may specify. - backend_name = self.coredata.get_option(OptionKey('genvslite')) + backend_name = self.coredata.optstore.get_value_for(OptionKey('genvslite')) self.backend = backends.get_genvslite_backend(backend_name, self.build, self) else: - backend_name = self.coredata.get_option(OptionKey('backend')) + backend_name = self.coredata.optstore.get_value_for(OptionKey('backend')) self.backend = backends.get_backend_from_name(backend_name, self.build, self) if self.backend is None: @@ -1291,7 +1291,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str # Load wrap files from this (sub)project. subprojects_dir = os.path.join(self.subdir, spdirname) if not self.is_subproject(): - wrap_mode = WrapMode.from_string(self.coredata.get_option(OptionKey('wrap_mode'))) + wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) self.environment.wrap_resolver = wrap.Resolver(self.environment.get_source_dir(), subprojects_dir, self.subproject, wrap_mode) else: assert self.environment.wrap_resolver is not None, 'for mypy' @@ -1306,9 +1306,9 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str # self.set_backend() otherwise it wouldn't be able to detect which # vs backend version we need. But after setting default_options in case # the project sets vs backend by default. - backend = self.coredata.get_option(OptionKey('backend')) + backend = self.coredata.optstore.get_value_for(OptionKey('backend')) assert backend is None or isinstance(backend, str), 'for mypy' - vsenv = self.coredata.get_option(OptionKey('vsenv')) + vsenv = self.coredata.optstore.get_value_for(OptionKey('vsenv')) assert isinstance(vsenv, bool), 'for mypy' force_vsenv = vsenv or backend.startswith('vs') mesonlib.setup_vsenv(force_vsenv) @@ -1702,7 +1702,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return ExternalProgram('meson', self.environment.get_build_command(), silent=True) fallback = None - wrap_mode = WrapMode.from_string(self.coredata.get_option(OptionKey('wrap_mode'))) + wrap_mode = WrapMode.from_string(self.coredata.optstore.get_value_for(OptionKey('wrap_mode'))) if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: fallback = self.environment.wrap_resolver.find_program_provider(args) if fallback and wrap_mode == WrapMode.forcefallback: @@ -3279,9 +3279,9 @@ def add_target(self, name: str, tobj: build.Target) -> None: 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.get_option(OptionKey('default_both_libraries')) + preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_both_libraries')) if preferred_library == 'auto': - preferred_library = self.coredata.get_option(OptionKey('default_library')) + preferred_library = self.coredata.optstore.get_value_for(OptionKey('default_library')) if preferred_library == 'both': preferred_library = 'shared' @@ -3322,7 +3322,7 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source return build.BothLibraries(shared_lib, static_lib, preferred_library) def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library): - default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject)) + default_library = self.coredata.optstore.get_value_for(OptionKey('default_library', subproject=self.subproject)) assert isinstance(default_library, str), 'for mypy' if default_library == 'shared': return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.SharedLibrary) diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index c82f933450c1..8ede6916abcd 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2021 The Meson development team -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021-2025 Intel Corporation from __future__ import annotations import copy @@ -311,7 +311,7 @@ def get_compiler_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> 'Compil @noPosargs @noKwargs def is_unity_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: - optval = self.interpreter.environment.coredata.get_option(OptionKey('unity')) + optval = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('unity')) return optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()) @noPosargs @@ -360,7 +360,7 @@ def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency] dep.name = name optkey = OptionKey('default_library', subproject=self.interpreter.subproject) - default_library = self.interpreter.coredata.get_option(optkey) + default_library = self.interpreter.coredata.optstore.get_value_for(optkey) assert isinstance(default_library, str), 'for mypy' static = kwargs['static'] if static is None: diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py index 2f5708c86521..cfaeb7960b9b 100644 --- a/mesonbuild/mcompile.py +++ b/mesonbuild/mcompile.py @@ -355,14 +355,14 @@ def run(options: 'argparse.Namespace') -> int: b = build.load(options.wd) cdata = b.environment.coredata - need_vsenv = T.cast('bool', cdata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', cdata.optstore.get_value_for(OptionKey('vsenv'))) if setup_vsenv(need_vsenv): mlog.log(mlog.green('INFO:'), 'automatically activated MSVC compiler environment') cmd: T.List[str] = [] env: T.Optional[T.Dict[str, str]] = None - backend = cdata.get_option(OptionKey('backend')) + backend = cdata.optstore.get_value_for(OptionKey('backend')) assert isinstance(backend, str) mlog.log(mlog.green('INFO:'), 'autodetecting backend as', backend) if backend == 'ninja': diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 8c6ce2031d45..4962d96c6bf9 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -84,9 +84,9 @@ def bash_completion_files(b: build.Build, install_data: 'InstallData') -> T.List dep = PkgConfigDependency('bash-completion', b.environment, {'required': False, 'silent': True, 'version': '>=2.10'}) if dep.found(): - prefix = b.environment.coredata.get_option(OptionKey('prefix')) + prefix = b.environment.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(prefix, str), 'for mypy' - datadir = b.environment.coredata.get_option(OptionKey('datadir')) + datadir = b.environment.coredata.optstore.get_value_for(OptionKey('datadir')) assert isinstance(datadir, str), 'for mypy' datadir_abs = os.path.join(prefix, datadir) completionsdir = dep.get_variable(pkgconfig='completionsdir', pkgconfig_define=(('datadir', datadir_abs),)) @@ -164,7 +164,7 @@ def run(options: argparse.Namespace) -> int: b = build.load(options.builddir) workdir = options.workdir or options.builddir - need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', b.environment.coredata.optstore.get_value_for(OptionKey('vsenv'))) setup_vsenv(need_vsenv) # Call it before get_env to get vsenv vars as well dump_fmt = options.dump_format if options.dump else None devenv, varnames = get_env(b, dump_fmt) diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 17329009b40a..0361606a5cf5 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2017 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -374,7 +374,7 @@ def run(options: argparse.Namespace) -> int: if not buildfile.is_file(): raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') b = build.load(options.wd) - need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', b.environment.coredata.optstore.get_value_for(OptionKey('vsenv'))) setup_vsenv(need_vsenv) src_root = b.environment.source_dir bld_root = b.environment.build_dir diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 70f184d02ebd..192c75a68739 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -193,7 +193,7 @@ def run(options: Arguments) -> int: raise SystemExit b = build.load(options.builddir) - need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', b.environment.coredata.optstore.get_value_for(OptionKey('vsenv'))) vsenv_active = mesonlib.setup_vsenv(need_vsenv) if vsenv_active: mlog.log(mlog.green('INFO:'), 'automatically activated MSVC compiler environment') diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 2db4472d160c..b4d59af7533c 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -869,9 +869,9 @@ def run(opts: 'ArgumentType') -> int: sys.exit('Install data not found. Run this command in build directory root.') if not opts.no_rebuild: b = build.load(opts.wd) - need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', b.environment.coredata.optstore.get_value_for(OptionKey('vsenv'))) setup_vsenv(need_vsenv) - backend = T.cast('str', b.environment.coredata.get_option(OptionKey('backend'))) + backend = T.cast('str', b.environment.coredata.optstore.get_value_for(OptionKey('backend'))) if not rebuild_all(opts.wd, backend): sys.exit(-1) os.chdir(opts.wd) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 55cf53d06fab..87ed66ec8a15 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -164,7 +164,7 @@ def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[s return plan def get_target_dir(coredata: cdata.CoreData, subdir: str) -> str: - if coredata.get_option(OptionKey('layout')) == 'flat': + if coredata.optstore.get_value_for(OptionKey('layout')) == 'flat': return 'meson-out' else: return subdir diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index fefe41a44949..67d16661a90f 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -134,7 +134,7 @@ def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'External def get_option(self, name: str, subproject: str = '', machine: MachineChoice = MachineChoice.HOST) -> ElementaryOptionValues: - return self.environment.coredata.get_option(OptionKey(name, subproject, machine)) + return self.environment.coredata.optstore.get_value_for(OptionKey(name, subproject, machine)) def is_user_defined_option(self, name: str, subproject: str = '', machine: MachineChoice = MachineChoice.HOST, diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 2168aaa516c0..1802584cb5b7 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -305,7 +305,7 @@ def write_basic_package_version_file(self, state: ModuleState, args: TYPE_var, k pkgroot = pkgroot_name = kwargs['install_dir'] if pkgroot is None: - pkgroot = os.path.join(state.environment.coredata.get_option(OptionKey('libdir')), 'cmake', name) + pkgroot = os.path.join(state.environment.coredata.optstore.get_value_for(OptionKey('libdir')), 'cmake', name) pkgroot_name = os.path.join('{libdir}', 'cmake', name) template_file = os.path.join(self.cmake_root, 'Modules', f'BasicConfigVersion-{compatibility}.cmake.in') @@ -376,14 +376,14 @@ def configure_package_config_file(self, state: ModuleState, args: TYPE_var, kwar install_dir = kwargs['install_dir'] if install_dir is None: - install_dir = os.path.join(state.environment.coredata.get_option(OptionKey('libdir')), 'cmake', name) + install_dir = os.path.join(state.environment.coredata.optstore.get_value_for(OptionKey('libdir')), 'cmake', name) conf = kwargs['configuration'] if isinstance(conf, dict): FeatureNew.single_use('cmake.configure_package_config_file dict as configuration', '0.62.0', state.subproject, location=state.current_node) conf = build.ConfigurationData(conf) - prefix = state.environment.coredata.get_option(OptionKey('prefix')) + prefix = state.environment.coredata.optstore.get_value_for(OptionKey('prefix')) abs_install_dir = install_dir if not os.path.isabs(abs_install_dir): abs_install_dir = os.path.join(prefix, install_dir) diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py index 57a1819a3e9c..339d0003fd4a 100644 --- a/mesonbuild/modules/external_project.py +++ b/mesonbuild/modules/external_project.py @@ -75,16 +75,16 @@ def __init__(self, self.src_dir = Path(self.env.get_source_dir(), self.subdir) self.build_dir = Path(self.env.get_build_dir(), self.subdir, 'build') self.install_dir = Path(self.env.get_build_dir(), self.subdir, 'dist') - _p = self.env.coredata.get_option(OptionKey('prefix')) + _p = self.env.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(_p, str), 'for mypy' self.prefix = Path(_p) - _l = self.env.coredata.get_option(OptionKey('libdir')) + _l = self.env.coredata.optstore.get_value_for(OptionKey('libdir')) assert isinstance(_l, str), 'for mypy' self.libdir = Path(_l) - _l = self.env.coredata.get_option(OptionKey('bindir')) + _l = self.env.coredata.optstore.get_value_for(OptionKey('bindir')) assert isinstance(_l, str), 'for mypy' self.bindir = Path(_l) - _i = self.env.coredata.get_option(OptionKey('includedir')) + _i = self.env.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(_i, str), 'for mypy' self.includedir = Path(_i) self.name = self.src_dir.name diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e3b1f3d7c55e..926017a334b9 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -522,7 +522,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri if gresource: # Only one target for .gresource files return ModuleReturnValue(target_c, [target_c]) - install_dir = kwargs['install_dir'] or state.environment.coredata.get_option(OptionKey('includedir')) + install_dir = kwargs['install_dir'] or state.environment.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(install_dir, str), 'for mypy' target_h = GResourceHeaderTarget( f'{target_name}_h', @@ -1655,7 +1655,7 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Un targets = [] install_header = kwargs['install_header'] - install_dir = kwargs['install_dir'] or state.environment.coredata.get_option(OptionKey('includedir')) + install_dir = kwargs['install_dir'] or state.environment.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(install_dir, str), 'for mypy' output = namebase + '.c' @@ -1967,7 +1967,7 @@ def _make_mkenum_impl( ) -> build.CustomTarget: real_cmd: T.List[T.Union[str, 'ToolType']] = [self._find_tool(state, 'glib-mkenums')] real_cmd.extend(cmd) - _install_dir = install_dir or state.environment.coredata.get_option(OptionKey('includedir')) + _install_dir = install_dir or state.environment.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(_install_dir, str), 'for mypy' return CustomTarget( @@ -2179,7 +2179,7 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener cmd.append(gir_file) vapi_output = library + '.vapi' - datadir = state.environment.coredata.get_option(OptionKey('datadir')) + datadir = state.environment.coredata.optstore.get_value_for(OptionKey('datadir')) assert isinstance(datadir, str), 'for mypy' install_dir = kwargs['install_dir'] or os.path.join(datadir, 'vala', 'vapi') diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 50b5fe6f2fed..5099b41bec60 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -331,7 +331,7 @@ def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation for path in self.include_paths: self.cmd.extend(['--include-path', path]) - if self.state.environment.coredata.get_option(OptionKey('werror', subproject=self.state.subproject)): + if self.state.environment.coredata.optstore.get_value_for(OptionKey('werror', subproject=self.state.subproject)): self.cmd.append('--fatal-warnings') self.generate_hotdoc_config() diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 551e0b36fab6..2e59b25666f4 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -278,7 +278,7 @@ def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') - targets.append(pottarget) install = kwargs['install'] - install_dir = kwargs['install_dir'] or state.environment.coredata.get_option(OptionKey('localedir')) + install_dir = kwargs['install_dir'] or state.environment.coredata.optstore.get_value_for(OptionKey('localedir')) assert isinstance(install_dir, str), 'for mypy' if not languages: languages = read_linguas(path.join(state.environment.source_dir, state.subdir)) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 974d2521d52e..cc0450a523b4 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -490,7 +490,7 @@ def _generate_pkgconfig_file(self, state: ModuleState, deps: DependenciesHelper, srcdir = PurePath(state.environment.get_source_dir()) else: outdir = state.environment.scratch_dir - prefix = PurePath(_as_str(coredata.get_option(OptionKey('prefix')))) + prefix = PurePath(_as_str(coredata.optstore.get_value_for(OptionKey('prefix')))) if pkgroot: pkgroot_ = PurePath(pkgroot) if not pkgroot_.is_absolute(): @@ -507,7 +507,7 @@ def _generate_pkgconfig_file(self, state: ModuleState, deps: DependenciesHelper, if optname == 'prefix': ofile.write('prefix={}\n'.format(self._escape(prefix))) else: - dirpath = PurePath(_as_str(coredata.get_option(OptionKey(optname)))) + dirpath = PurePath(_as_str(coredata.optstore.get_value_for(OptionKey(optname)))) ofile.write('{}={}\n'.format(optname, self._escape('${prefix}' / dirpath))) if uninstalled and not dataonly: ofile.write('srcdir={}\n'.format(self._escape(srcdir))) @@ -703,13 +703,13 @@ def parse_variable_list(vardict: T.Dict[str, str]) -> T.List[T.Tuple[str, str]]: if pkgroot is None: m = state.environment.machines.host if m.is_freebsd(): - pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('prefix'))), 'libdata', 'pkgconfig') + pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'libdata', 'pkgconfig') pkgroot_name = os.path.join('{prefix}', 'libdata', 'pkgconfig') elif m.is_haiku(): - pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('prefix'))), 'develop', 'lib', 'pkgconfig') + pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'develop', 'lib', 'pkgconfig') pkgroot_name = os.path.join('{prefix}', 'develop', 'lib', 'pkgconfig') else: - pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('libdir'))), 'pkgconfig') + pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('libdir'))), 'pkgconfig') pkgroot_name = os.path.join('{libdir}', 'pkgconfig') relocatable = state.get_option('pkgconfig.relocatable') self._generate_pkgconfig_file(state, deps, subdirs, name, description, url, diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 1b7a05640374..2a7e685c1435 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -113,7 +113,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): _ExternalProgramHolder.__init__(self, python, interpreter) info = python.info - prefix = self.interpreter.environment.coredata.get_option(OptionKey('prefix')) + prefix = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('prefix')) assert isinstance(prefix, str), 'for mypy' self.variables = info['variables'] self.suffix = info['suffix'] @@ -169,7 +169,7 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], self.current_node) limited_api_version = kwargs.pop('limited_api') - allow_limited_api = self.interpreter.environment.coredata.get_option(OptionKey('python.allow_limited_api')) + allow_limited_api = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.allow_limited_api')) if limited_api_version != '' and allow_limited_api: target_suffix = self.limited_api_suffix @@ -374,7 +374,7 @@ def __init__(self, interpreter: 'Interpreter') -> None: def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]: backend = self.interpreter.backend ret = [] - optlevel = self.interpreter.environment.coredata.get_option(OptionKey('python.bytecompile')) + optlevel = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.bytecompile')) if optlevel == -1: return ret if not any(PythonExternalProgram.run_bytecompile.values()): diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 673f433fd103..9d16e6358a57 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -2273,11 +2273,11 @@ def run(options: argparse.Namespace) -> int: return 1 b = build.load(options.wd) - need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + need_vsenv = T.cast('bool', b.environment.coredata.optstore.get_value_for(OptionKey('vsenv'))) setup_vsenv(need_vsenv) if not options.no_rebuild: - backend = b.environment.coredata.get_option(OptionKey('backend')) + backend = b.environment.coredata.optstore.get_value_for(OptionKey('backend')) if backend == 'none': # nothing to build... options.no_rebuild = True diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py index 409b514b608e..b647dd8fcfb4 100644 --- a/mesonbuild/munstable_coredata.py +++ b/mesonbuild/munstable_coredata.py @@ -53,7 +53,7 @@ def run(options): print('') coredata = cdata.load(options.builddir) - backend = coredata.get_option(OptionKey('backend')) + backend = coredata.optstore.get_value_for(OptionKey('backend')) for k, v in sorted(coredata.__dict__.items()): if k in {'backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'}: # use `meson configure` to view these diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py index fc69ed7d50a1..9874607334e6 100644 --- a/mesonbuild/scripts/regen_checker.py +++ b/mesonbuild/scripts/regen_checker.py @@ -44,7 +44,7 @@ def run(args: T.List[str]) -> int: with open(coredata_file, 'rb') as f: coredata = pickle.load(f) assert isinstance(coredata, CoreData) - backend = coredata.get_option(OptionKey('backend')) + backend = coredata.optstore.get_value_for(OptionKey('backend')) assert isinstance(backend, str) regen_timestamp = os.stat(dumpfile).st_mtime if need_regen(regeninfo, regen_timestamp): From 251f74dcf3fea706ed373b717257f82592b6a1de Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 08:24:08 -0800 Subject: [PATCH 377/624] coredata: remove get_option_for_subproject This is just a wrapper around `OptionStore.get_option_for`, but without taking an `OptionKey`. This complicates the subproject passing, since `OptionKey` is designed to encapsulate the option name and subproject. --- mesonbuild/compilers/compilers.py | 10 ++++++---- mesonbuild/compilers/cpp.py | 8 ++++---- mesonbuild/compilers/cuda.py | 4 ++-- mesonbuild/compilers/mixins/elbrus.py | 6 +++--- mesonbuild/compilers/objc.py | 4 ++-- mesonbuild/compilers/objcpp.py | 4 ++-- mesonbuild/coredata.py | 8 -------- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index f0744f80bb91..a73697cddfb8 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -280,10 +280,12 @@ def are_asserts_disabled(target: 'BuildTarget', env: 'Environment') -> bool: (env.coredata.get_option_for_target(target, 'b_ndebug') == 'if-release' and env.coredata.get_option_for_target(target, 'buildtype') in {'release', 'plain'})) + def are_asserts_disabled_for_subproject(subproject: str, env: 'Environment') -> bool: - return (env.coredata.get_option_for_subproject('b_ndebug', subproject) == 'true' or - (env.coredata.get_option_for_subproject('b_ndebug', subproject) == 'if-release' and - env.coredata.get_option_for_subproject('buildtype', subproject) in {'release', 'plain'})) + key = OptionKey('b_ndebug', subproject) + return (env.coredata.optstore.get_value_for(key) == 'true' or + (env.coredata.optstore.get_value_for(key) == 'if-release' and + env.coredata.optstore.get_value_for(key.evolve(name='buildtype')) in {'release', 'plain'})) def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Environment') -> T.List[str]: @@ -1400,7 +1402,7 @@ def get_compileropt_value(self, if target: return env.coredata.get_option_for_target(target, key) else: - return env.coredata.get_option_for_subproject(key, subproject) + return env.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') diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index b21a62e44806..cbb91e439a18 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -764,11 +764,11 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: # need a typeddict for this - key = self.form_compileropt_key('winlibs') + key = self.form_compileropt_key('winlibs').evolve(subproject=subproject) if target: value = env.coredata.get_option_for_target(target, key) else: - value = env.coredata.get_option_for_subproject(key, subproject) + value = env.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': @@ -845,11 +845,11 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub # 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') + stdkey = self.form_compileropt_key('std').evolve(subproject=subproject) if target is not None: std = env.coredata.get_option_for_target(target, stdkey) else: - std = env.coredata.get_option_for_subproject(stdkey, subproject) + std = env.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', diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 612643c8f787..b67aa8715acf 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -794,11 +794,11 @@ def get_ccbin_args(self, target: 'T.Optional[BuildTarget]', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: - key = self.form_compileropt_key('ccbindir') + key = self.form_compileropt_key('ccbindir').evolve(subproject=subproject) if target: ccbindir = env.coredata.get_option_for_target(target, key) else: - ccbindir = env.coredata.get_option_for_subproject(key, subproject) + ccbindir = env.coredata.optstore.get_value_for(key) if isinstance(ccbindir, str) and ccbindir != '': return [self._shield_nvcc_list_arg('-ccbin='+ccbindir, False)] else: diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 4bf0826271cd..eac68bd94860 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -85,11 +85,11 @@ def get_pch_suffix(self) -> str: def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = OptionKey(f'{self.language}_std', machine=self.for_machine) + key = OptionKey(f'{self.language}_std', subproject=subproject, machine=self.for_machine) if target: std = env.coredata.get_option_for_target(target, key) else: - std = env.coredata.get_option_for_subproject(key, subproject) + std = env.coredata.optstore.get_value_for(key) assert isinstance(std, str) if std != 'none': args.append('-std=' + std) diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index b133d47c4dcf..776f5037d7ea 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -78,11 +78,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = OptionKey('c_std', machine=self.for_machine) + key = OptionKey('c_std', subproject=subproject, machine=self.for_machine) if target: std = env.coredata.get_option_for_target(target, key) else: - std = env.coredata.get_option_for_subproject(key, subproject) + std = env.coredata.optstore.get_value_for(key) assert isinstance(std, str) if std != 'none': args.append('-std=' + std) diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index 743bbb9cde2c..b38fdb60d689 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -83,11 +83,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - key = OptionKey('cpp_std', machine=self.for_machine) + key = OptionKey('cpp_std', subproject=subproject, machine=self.for_machine) if target: std = env.coredata.get_option_for_target(target, key) else: - std = env.coredata.get_option_for_subproject(key, subproject) + std = env.coredata.optstore.get_value_for(key) assert isinstance(std, str) if std != 'none': args.append('-std=' + std) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f1286aa2ec90..6bf8705c5d22 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -414,14 +414,6 @@ def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionK return option_object.validate_value(override) return value - def get_option_for_subproject(self, key: T.Union[str, OptionKey], subproject) -> ElementaryOptionValues: - if isinstance(key, str): - key = OptionKey(key, subproject=subproject) - if key.subproject != subproject: - # This should be an error, fix before merging. - key = key.evolve(subproject=subproject) - return self.optstore.get_value_for(key) - def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: dirty = False try: From c56e42c11986bac4ab30a02d5269ae96c0cb4c1c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 10:04:55 -0800 Subject: [PATCH 378/624] options: merge set_value and set_option Which are basically the same, except for handling of deprecated options, and various bugs that only existed in one implementation or the other. --- mesonbuild/coredata.py | 18 ++++---- mesonbuild/options.py | 68 ++++++++++-------------------- unittests/platformagnostictests.py | 4 +- 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6bf8705c5d22..7cc69a5a71cf 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -417,7 +417,7 @@ def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionK def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: dirty = False try: - changed = self.optstore.set_value(key, value, first_invocation) + changed = self.optstore.set_option(key, value, first_invocation) except KeyError: raise MesonException(f'Tried to set unknown builtin option {str(key)}') dirty |= changed @@ -484,8 +484,8 @@ def _set_others_from_buildtype(self, value: str) -> bool: assert value == 'custom' return False - dirty |= self.optstore.set_value('optimization', opt) - dirty |= self.optstore.set_value('debug', debug) + dirty |= self.optstore.set_option(OptionKey('optimization'), opt) + dirty |= self.optstore.set_option(OptionKey('debug'), debug) return dirty @@ -517,7 +517,7 @@ def update_project_options(self, project_options: 'MutableKeyedOptionDictType', oldval = self.optstore.get_value_object(key) if type(oldval) is not type(value): - self.optstore.set_value(key, value.value) + self.optstore.set_option(key, value.value) elif options.choices_are_different(oldval, value): # If the choices have changed, use the new value, but attempt # to keep the old options. If they are not valid keep the new @@ -548,7 +548,7 @@ def copy_build_options_from_regular_ones(self, shut_up_pylint: bool = True) -> b 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_value(k, o.value, True) + 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() @@ -692,16 +692,16 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, if skey not in self.optstore: self.optstore.add_system_option(skey, copy.deepcopy(compilers.BASE_OPTIONS[key])) if skey in env.options: - self.optstore.set_value(skey, env.options[skey]) + self.optstore.set_option(skey, env.options[skey]) elif subproject and key in env.options: - self.optstore.set_value(skey, env.options[key]) + self.optstore.set_option(skey, env.options[key]) # FIXME #if subproject and not self.optstore.has_option(key): # self.optstore[key] = copy.deepcopy(self.optstore[skey]) elif skey in env.options: - self.optstore.set_value(skey, env.options[skey]) + self.optstore.set_option(skey, env.options[skey]) elif subproject and key in env.options: - self.optstore.set_value(skey, env.options[key]) + self.optstore.set_option(skey, env.options[key]) self.emit_base_options_warnings() def emit_base_options_warnings(self) -> None: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 3a3e5e9b7f99..234085346401 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -941,53 +941,27 @@ def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any # .as_posix() keeps the posix-like file separators Meson uses. return value.as_posix() - def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any', first_invocation: bool = False) -> bool: - key = self.ensure_and_validate_key(key) - if key.name == 'prefix': - new_value = self.sanitize_prefix(new_value) - elif self.is_builtin_option(key): - prefix = self.get_value_for('prefix') - assert isinstance(prefix, str) - new_value = self.sanitize_dir_option_value(prefix, key, new_value) - if key not in self.options: - raise MesonException(f'Unknown options: "{key.name}" not found.') - - valobj = self.options[key] - old_value = valobj.value - changed = valobj.set_value(new_value) - - if valobj.readonly and changed and not first_invocation: - raise MesonException(f'Tried to modify read only option {str(key)!r}') - - if key.name == 'prefix' and first_invocation and changed: - assert isinstance(old_value, str) - self.reset_prefixed_options(old_value, new_value) - - if changed: - self.set_dependents(key, new_value) - - return changed - - def set_dependents(self, key: OptionKey, value: str) -> None: - if key.name != 'buildtype': - return + def _set_dependents(self, key: OptionKey, value: str) -> None: opt, debug = self.DEFAULT_DEPENDENTS[value] dkey = key.evolve(name='debug') optkey = key.evolve(name='optimization') self.options[dkey].set_value(debug) self.options[optkey].set_value(opt) - def set_option(self, key: OptionKey, new_value: str, first_invocation: bool = False) -> bool: - assert isinstance(key, OptionKey) - # FIXME, dupe of set_value - # Remove one of the two before merging to master. + def set_option(self, key: OptionKey, new_value: ElementaryOptionValues, first_invocation: bool = False) -> bool: if key.name == 'prefix': + assert isinstance(new_value, str), 'for mypy' new_value = self.sanitize_prefix(new_value) elif self.is_builtin_option(key): prefix = self.get_value_for('prefix') - assert isinstance(prefix, str) + assert isinstance(prefix, str), 'for mypy' new_value = self.sanitize_dir_option_value(prefix, key, new_value) - opt = self.get_value_object_for(key) + + try: + opt = self.get_value_object_for(key) + except KeyError: + raise MesonException(f'Unknown options: "{key!s}" not found.') + if opt.deprecated is True: mlog.deprecation(f'Option {key.name!r} is deprecated') elif isinstance(opt.deprecated, list): @@ -1014,15 +988,17 @@ def replace(v: T.Any) -> T.Any: old_value = opt.value changed = opt.set_value(new_value) - if opt.readonly and changed: - raise MesonException(f'Tried modify read only option {str(key)!r}') + if opt.readonly and changed and not first_invocation: + raise MesonException(f'Tried to modify read only option {str(key)!r}') 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: - self.set_dependents(key, new_value) + if changed and key.name == 'buildtype': + assert isinstance(new_value, str), 'for mypy' + self._set_dependents(key, new_value) return changed @@ -1032,9 +1008,9 @@ def set_option_from_string(self, keystr: T.Union[OptionKey, str], new_value: str else: o = OptionKey.from_string(keystr) if o in self.options: - return self.set_value(o, new_value) + return self.set_option(o, new_value) o = o.as_root() - return self.set_value(o, new_value) + return self.set_option(o, new_value) def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool: dirty = False @@ -1279,7 +1255,7 @@ def initialize_from_top_level_project_call(self, augstr = str(key) self.augments[augstr] = valstr elif key in self.options: - self.set_value(key, valstr, first_invocation) + self.set_option(key, valstr, first_invocation) else: proj_key = key.as_root() if proj_key in self.options: @@ -1330,7 +1306,7 @@ def initialize_from_top_level_project_call(self, if not self.is_cross and key.is_for_build(): continue if key in self.options: - self.set_value(key, valstr, True) + self.set_option(key, valstr, True) elif key.subproject is None: projectkey = key.as_root() if projectkey in self.options: @@ -1371,7 +1347,7 @@ def initialize_from_subproject_call(self, # 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_value(key, valstr, is_first_invocation) + self.set_option(key, valstr, is_first_invocation) else: self.pending_project_options.pop(key, None) aug_str = f'{subproject}:{keystr}' @@ -1385,6 +1361,6 @@ def initialize_from_subproject_call(self, continue self.pending_project_options.pop(key, None) if key in self.options: - self.set_value(key, valstr, is_first_invocation) + self.set_option(key, valstr, is_first_invocation) else: self.augments[str(key)] = valstr diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index cb6a931fc602..00106089cdac 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -451,7 +451,7 @@ def test_configure_removed_option(self) -> None: 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 options: ":neg_int_opt"', e.exception.stdout) def test_configure_option_changed_constraints(self) -> None: """Changing the constraints of an option without reconfiguring should work.""" @@ -491,7 +491,7 @@ def test_configure_options_file_deleted(self) -> None: 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 options: ":neg_int_opt"', e.exception.stdout) def test_configure_options_file_added(self) -> None: """A new project option file should be detected.""" From b19f6701025e37ca52e78eae7075cdc5a626f0ab Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 5 Mar 2025 12:30:12 -0800 Subject: [PATCH 379/624] options: inline _set_dependenents --- mesonbuild/options.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 234085346401..01532b41b7ff 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -941,13 +941,6 @@ def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any # .as_posix() keeps the posix-like file separators Meson uses. return value.as_posix() - def _set_dependents(self, key: OptionKey, value: str) -> None: - opt, debug = self.DEFAULT_DEPENDENTS[value] - dkey = key.evolve(name='debug') - optkey = key.evolve(name='optimization') - self.options[dkey].set_value(debug) - self.options[optkey].set_value(opt) - def set_option(self, key: OptionKey, new_value: ElementaryOptionValues, first_invocation: bool = False) -> bool: if key.name == 'prefix': assert isinstance(new_value, str), 'for mypy' @@ -998,7 +991,11 @@ def replace(v: T.Any) -> T.Any: if changed and key.name == 'buildtype': assert isinstance(new_value, str), 'for mypy' - self._set_dependents(key, new_value) + 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) return changed From 8689c944515d4401b1bc4dbc75910288aed006c5 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Fri, 13 Dec 2024 14:34:22 -0500 Subject: [PATCH 380/624] linkers: revert a binutils bug workaround, sort of More specifically, the bug had been fixed shortly before we implemented this workaround. It's documented as only necessary for the binutils brand specifically, and was fixed upstream in 2.28. We can avoid producing these arguments at all on newer (post-2016) versions of binutils, so gate this behind a version check. This can significantly reduce the size of compiler command lines in some cases, as it encodes the full build directory. It also helps that one person who decides to put commas into their build directory name (which `-Wl,...` interprets as multiple arguments, rather than a single directory string). Bug: https://github.com/mesonbuild/meson/pull/1898 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=20535 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=16936 --- mesonbuild/linkers/linkers.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index b114d498bbba..ba642119f9f0 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -748,11 +748,16 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, return (args, rpath_dirs_to_remove) # Rpaths to use while linking must be absolute. These are not - # written to the binary. Needed only with GNU ld: + # written to the binary. Needed only with GNU ld, and only for + # versions before 2.28: + # https://sourceware.org/bugzilla/show_bug.cgi?id=20535 # https://sourceware.org/bugzilla/show_bug.cgi?id=16936 # Not needed on Windows or other platforms that don't use RPATH # https://github.com/mesonbuild/meson/issues/1897 # + # In 2.28 and on, $ORIGIN tokens inside of -rpath are respected, + # so we do not need to duplicate it in -rpath-link. + # # In addition, this linker option tends to be quite long and some # compilers have trouble dealing with it. That's why we will include # one option per folder, like this: @@ -762,8 +767,9 @@ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, # ...instead of just one single looooong option, like this: # # -Wl,-rpath-link,/path/to/folder1:/path/to/folder2:... - for p in rpath_paths: - args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) + if self.id in {'ld.bfd', 'ld.gold'} and mesonlib.version_compare(self.version, '<2.28'): + for p in rpath_paths: + args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) return (args, rpath_dirs_to_remove) From 0be18b0d87bae8655a79958a14e06db5061cf401 Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Tue, 11 Mar 2025 18:45:52 +0200 Subject: [PATCH 381/624] tests: support DC being more than plain dmd or ldc2 Dub is very thorough about what it encodes in a build id even collecting the compiler path. Therefore, if dub is not provided the exact same compiler path that meson got during setup, the dependency would not be found and the test would fail. Signed-off-by: Andrei Horodniceanu --- test cases/d/14 dub with deps/meson.build | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test cases/d/14 dub with deps/meson.build b/test cases/d/14 dub with deps/meson.build index c1acfcb1b2fc..f7fbd0aedcc2 100644 --- a/test cases/d/14 dub with deps/meson.build +++ b/test cases/d/14 dub with deps/meson.build @@ -12,12 +12,9 @@ endif if meson.get_compiler('d').get_id() == 'gcc' error('MESON_SKIP_TEST: can\'t build dependencies with GDC') -elif meson.get_compiler('d').get_id() == 'llvm' - dc = 'ldc2' -elif meson.get_compiler('d').get_id() == 'dmd' - dc = 'dmd' endif +dc = meson.get_compiler('d').cmd_array()[0] arch = host_machine.cpu_family() if host_machine.system() == 'windows' From be842e80743b4531560ba54da1c070f7f79e9515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corentin=20No=C3=ABl?= Date: Wed, 12 Mar 2025 10:28:11 +0100 Subject: [PATCH 382/624] modules/gnome: header argument of generate_gir accepts an array of strings The behavioral change is there since be1d013453e3df3b83da0c91f5211c822d4da4d7 so align the documentation with what is allowed here. --- docs/markdown/Gnome-module.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 7248ca51118b..e8953efc9cd5 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -106,7 +106,8 @@ There are several keyword arguments. Many of these map directly to the * `identifier_prefix`: the identifier prefix for the gir object, e.g. `Gtk` * `includes`: list of gir names to be included, can also be a GirTarget -* `header`: *(Added 0.43.0)* name of main c header to include for the library, e.g. `glib.h` +* `header`: *(Added 0.43.0)* name of main c header to include for the library, + e.g. `glib.h`, (*Since 0.61.0*) a list of headers is allowed * `include_directories`: extra include paths to look for gir files * `install`: if true, install the generated files * `install_gir`: (*Added 0.61.0*) overrides `install`, whether to install the From 77dd0429cfb0b5309b44df4acc5380aa014c5bd1 Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Tue, 11 Mar 2025 12:12:14 +0200 Subject: [PATCH 383/624] mesonbuild/compilers/detect.py: Support Open D ldc and dmd Signed-off-by: Andrei Horodniceanu --- mesonbuild/compilers/detect.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 784e00d3dc76..bce0be8a8eed 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1191,7 +1191,11 @@ def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compile version = search_version(out) full_version = out.split('\n', 1)[0] - if 'LLVM D compiler' in out: + # The OpenD fork should stay close enough to upstream D (in + # the areas that interest us) to allow supporting them both + # without much hassle. + # See: https://github.com/orgs/opendlang/discussions/56 + if 'LLVM D compiler' in out or 'LLVM Open D compiler' in out: cls = d.LLVMDCompiler # LDC seems to require a file # We cannot use NamedTemporaryFile on windows, its documented From fa3816a2eeecf4c2ee86bff0c1d92f9a4354c98b Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 12 Mar 2025 16:17:30 -0400 Subject: [PATCH 384/624] Move flatten_kwargs to IntrospectionInterpreter It is not used elsewhere --- mesonbuild/ast/interpreter.py | 11 ----------- mesonbuild/ast/introspection.py | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 5edd9b3d972c..30fc521835db 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -424,16 +424,5 @@ def flatten_args(self, args_raw: T.Union[TYPE_var, T.Sequence[TYPE_var]], includ flattened_args += [i] return flattened_args - def flatten_kwargs(self, kwargs: T.Dict[str, TYPE_var], include_unknown_args: bool = False) -> T.Dict[str, TYPE_var]: - flattened_kwargs = {} - for key, val in kwargs.items(): - if isinstance(val, BaseNode): - resolved = self.resolve_node(val, include_unknown_args) - if resolved is not None: - flattened_kwargs[key] = resolved - elif isinstance(val, (str, bool, int, float)) or include_unknown_args: - flattened_kwargs[key] = val - return flattened_kwargs - def evaluate_testcase(self, node: TestCaseClauseNode) -> Disabler | None: return Disabler(subproject=self.subproject) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index b042eb37a07c..a5b70eca078a 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -390,3 +390,14 @@ def extract_subproject_dir(self) -> T.Optional[str]: if isinstance(val, StringNode): return val.value return None + + def flatten_kwargs(self, kwargs: T.Dict[str, TYPE_var], include_unknown_args: bool = False) -> T.Dict[str, TYPE_var]: + flattened_kwargs = {} + for key, val in kwargs.items(): + if isinstance(val, BaseNode): + resolved = self.resolve_node(val, include_unknown_args) + if resolved is not None: + flattened_kwargs[key] = resolved + elif isinstance(val, (str, bool, int, float)) or include_unknown_args: + flattened_kwargs[key] = val + return flattened_kwargs From e65c879925219685cbcbb9a8367006b8c687167e Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 12 Mar 2025 17:27:36 -0400 Subject: [PATCH 385/624] Extract common func_subdir functions to InterpreterBase --- mesonbuild/ast/interpreter.py | 28 ++----------- mesonbuild/interpreter/interpreter.py | 36 ++++------------- mesonbuild/interpreterbase/interpreterbase.py | 40 +++++++++++++++++++ 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 30fc521835db..878fb34ed15b 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -89,7 +89,6 @@ class AstInterpreter(InterpreterBase): def __init__(self, source_root: str, subdir: str, subproject: SubProject, visitors: T.Optional[T.List[AstVisitor]] = None): super().__init__(source_root, subdir, subproject) self.visitors = visitors if visitors is not None else [] - self.processed_buildfiles: T.Set[str] = set() self.assignments: T.Dict[str, BaseNode] = {} self.assign_vals: T.Dict[str, T.Any] = {} self.reverse_assignment: T.Dict[str, BaseNode] = {} @@ -174,33 +173,14 @@ def func_subdir(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str sys.stderr.write(f'Unable to evaluate subdir({args}) in AstInterpreter --> Skipping\n') return - prev_subdir = self.subdir - subdir = os.path.join(prev_subdir, args[0]) - absdir = os.path.join(self.source_root, subdir) - buildfilename = os.path.join(subdir, environment.build_filename) - absname = os.path.join(self.source_root, buildfilename) - symlinkless_dir = os.path.realpath(absdir) - build_file = os.path.join(symlinkless_dir, 'meson.build') - if build_file in self.processed_buildfiles: + subdir, is_new = self._resolve_subdir(self.source_root, args[0]) + if not is_new: sys.stderr.write('Trying to enter {} which has already been visited --> Skipping\n'.format(args[0])) return - self.processed_buildfiles.add(build_file) - if not os.path.isfile(absname): + if not self._evaluate_subdir(self.source_root, subdir, self.visitors): + buildfilename = os.path.join(subdir, environment.build_filename) sys.stderr.write(f'Unable to find build file {buildfilename} --> Skipping\n') - return - code = self.read_buildfile(absname, buildfilename) - try: - codeblock = mparser.Parser(code, absname).parse() - except mesonlib.MesonException as me: - me.file = absname - raise me - - self.subdir = subdir - for i in self.visitors: - codeblock.accept(i) - self.evaluate_codeblock(codeblock) - self.subdir = prev_subdir def method_call(self, node: BaseNode) -> bool: return True diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 14de76888637..1b378e304b55 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -284,14 +284,12 @@ def __init__( self.modules: T.Dict[str, NewExtensionModule] = {} self.subproject_dir = subproject_dir self.relaxations = relaxations or set() - self.build_def_files: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() if ast is None: self.load_root_meson_file() else: self.ast = ast self.sanity_check_ast() self.builtin.update({'meson': MesonMain(self.build, self)}) - self.processed_buildfiles: T.Set[str] = set() self.validated_cache: T.Set[str] = set() self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen @@ -2461,39 +2459,21 @@ def func_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'kwtyp raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().') if args[0] == '': raise InvalidArguments("The argument given to subdir() is the empty string ''. This is prohibited.") + if os.path.isabs(args[0]): + raise InvalidArguments('Subdir argument must be a relative path.') for i in kwargs['if_found']: if not i.found(): return - prev_subdir = self.subdir - subdir = os.path.join(prev_subdir, args[0]) - if os.path.isabs(subdir): - raise InvalidArguments('Subdir argument must be a relative path.') - absdir = os.path.join(self.environment.get_source_dir(), subdir) - symlinkless_dir = os.path.realpath(absdir) - build_file = os.path.join(symlinkless_dir, 'meson.build') - if build_file in self.processed_buildfiles: + subdir, is_new = self._resolve_subdir(self.environment.get_source_dir(), args[0]) + if not is_new: raise InvalidArguments(f'Tried to enter directory "{subdir}", which has already been visited.') - self.processed_buildfiles.add(build_file) - self.subdir = subdir + os.makedirs(os.path.join(self.environment.build_dir, subdir), exist_ok=True) - buildfilename = os.path.join(self.subdir, environment.build_filename) - self.build_def_files.add(buildfilename) - absname = os.path.join(self.environment.get_source_dir(), buildfilename) - if not os.path.isfile(absname): - self.subdir = prev_subdir + + if 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}'") - code = self.read_buildfile(absname, buildfilename) - try: - codeblock = mparser.Parser(code, absname).parse() - except mesonlib.MesonException as me: - me.file = absname - raise me - try: - self.evaluate_codeblock(codeblock) - except SubdirDoneRequest: - pass - self.subdir = prev_subdir # This is either ignored on basically any OS nowadays, or silently gets # ignored (Solaris) or triggers an "illegal operation" error (FreeBSD). diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 5849e9ca9c1d..e94ba70dca21 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -39,6 +39,7 @@ if T.TYPE_CHECKING: from .baseobjects import InterpreterObjectTypeVar, SubProject, TYPE_kwargs, TYPE_var + from ..ast import AstVisitor from ..interpreter import Interpreter HolderMapType = T.Dict[ @@ -74,6 +75,8 @@ def __init__(self, source_root: str, subdir: str, subproject: 'SubProject'): # Holder maps store a mapping from an HoldableObject to a class ObjectHolder self.holder_map: HolderMapType = {} self.bound_holder_map: HolderMapType = {} + self.build_def_files: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() + self.processed_buildfiles: T.Set[str] = set() self.subdir = subdir self.root_subdir = subdir self.subproject = subproject @@ -668,3 +671,40 @@ def get_variable(self, varname: str) -> InterpreterObject: def validate_extraction(self, buildtarget: mesonlib.HoldableObject) -> None: raise InterpreterException('validate_extraction is not implemented in this context (please file a bug)') + + def _resolve_subdir(self, rootdir: str, new_subdir: str) -> T.Tuple[str, bool]: + subdir = os.path.join(self.subdir, new_subdir) + absdir = os.path.join(rootdir, subdir) + symlinkless_dir = os.path.realpath(absdir) + build_file = os.path.join(symlinkless_dir, environment.build_filename) + if build_file in self.processed_buildfiles: + return subdir, False + self.processed_buildfiles.add(build_file) + return subdir, True + + def _evaluate_subdir(self, rootdir: str, subdir: str, visitors: T.Optional[T.Iterable[AstVisitor]] = None) -> bool: + buildfilename = os.path.join(subdir, environment.build_filename) + self.build_def_files.add(buildfilename) + + absname = os.path.join(rootdir, buildfilename) + if not os.path.isfile(absname): + return False + + code = self.read_buildfile(absname, buildfilename) + try: + codeblock = mparser.Parser(code, absname).parse() + except mesonlib.MesonException as me: + me.file = absname + raise me + try: + prev_subdir = self.subdir + self.subdir = subdir + if visitors: + for visitor in visitors: + codeblock.accept(visitor) + self.evaluate_codeblock(codeblock) + except SubdirDoneRequest: + pass + finally: + self.subdir = prev_subdir + return True From 7679c164dc357650a15643b3c0a5d89ac7a90e89 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 12 Mar 2025 17:35:53 -0400 Subject: [PATCH 386/624] Move variables to InterpreterBase subproject_dir, environment, and coredata --- mesonbuild/ast/interpreter.py | 4 ++-- mesonbuild/ast/introspection.py | 11 +++-------- mesonbuild/interpreter/interpreter.py | 5 +---- mesonbuild/interpreterbase/interpreterbase.py | 7 +++++-- unittests/datatests.py | 2 +- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 878fb34ed15b..cd8156a3f235 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -86,8 +86,8 @@ class MockRunTarget(MesonInterpreterObject): class AstInterpreter(InterpreterBase): - def __init__(self, source_root: str, subdir: str, subproject: SubProject, visitors: T.Optional[T.List[AstVisitor]] = None): - super().__init__(source_root, subdir, subproject) + 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] = {} diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index a5b70eca078a..9e00c8d251d0 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -55,16 +55,11 @@ def __init__(self, subproject: SubProject = SubProject(''), subproject_dir: str = 'subprojects', env: T.Optional[environment.Environment] = None): - super().__init__(source_root, subdir, subproject, visitors=visitors) - options = IntrospectionHelper(cross_file) + env_ = env or environment.Environment(source_root, None, options) + super().__init__(source_root, subdir, subproject, subproject_dir, env_, visitors=visitors) + self.cross_file = cross_file - if env is None: - self.environment = environment.Environment(source_root, None, options) - else: - self.environment = env - self.subproject_dir = subproject_dir - self.coredata = self.environment.get_coredata() self.backend = backend self.default_options = {OptionKey('backend'): self.backend} self.project_data: T.Dict[str, T.Any] = {} diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1b378e304b55..1247dfe3b80c 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -274,15 +274,12 @@ def __init__( relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, user_defined_options: T.Optional[coredata.SharedCMDOptions] = None, ) -> None: - super().__init__(_build.environment.get_source_dir(), subdir, subproject) + super().__init__(_build.environment.get_source_dir(), subdir, subproject, subproject_dir, _build.environment) self.active_projectname = '' self.build = _build - self.environment = self.build.environment - self.coredata = self.environment.get_coredata() self.backend = backend self.summary: T.Dict[str, 'Summary'] = {} self.modules: T.Dict[str, NewExtensionModule] = {} - self.subproject_dir = subproject_dir self.relaxations = relaxations or set() if ast is None: self.load_root_meson_file() diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index e94ba70dca21..2dcb81b0fc69 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -68,7 +68,7 @@ def __init__(self, op_type: str) -> None: class InterpreterBase: - def __init__(self, source_root: str, subdir: str, subproject: 'SubProject'): + def __init__(self, source_root: str, subdir: str, subproject: SubProject, subproject_dir: str, env: environment.Environment): self.source_root = source_root self.funcs: FunctionType = {} self.builtin: T.Dict[str, InterpreterObject] = {} @@ -80,6 +80,9 @@ def __init__(self, source_root: str, subdir: str, subproject: 'SubProject'): self.subdir = subdir self.root_subdir = subdir self.subproject = subproject + self.subproject_dir = subproject_dir + self.environment = env + self.coredata = env.get_coredata() self.variables: T.Dict[str, InterpreterObject] = {} self.argument_depth = 0 self.current_lineno = -1 @@ -685,7 +688,7 @@ def _resolve_subdir(self, rootdir: str, new_subdir: str) -> T.Tuple[str, bool]: def _evaluate_subdir(self, rootdir: str, subdir: str, visitors: T.Optional[T.Iterable[AstVisitor]] = None) -> bool: buildfilename = os.path.join(subdir, environment.build_filename) self.build_def_files.add(buildfilename) - + absname = os.path.join(rootdir, buildfilename) if not os.path.isfile(absname): return False diff --git a/unittests/datatests.py b/unittests/datatests.py index 173f718ea428..bd83b81f86de 100644 --- a/unittests/datatests.py +++ b/unittests/datatests.py @@ -246,5 +246,5 @@ def test_all_functions_defined_in_ast_interpreter(self): del os.environ['MESON_RUNNING_IN_PROJECT_TESTS'] env = get_fake_env() interp = Interpreter(FakeBuild(env)) - astint = AstInterpreter('.', '', '') + astint = AstInterpreter('.', '', '', '', env) self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys())) From a6af244442a2f3b80e8fb2ccb1649eb09cf25541 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 12 Mar 2025 18:02:46 -0400 Subject: [PATCH 387/624] Move options loading to InterpreterBase --- mesonbuild/ast/introspection.py | 11 ++---- mesonbuild/interpreter/interpreter.py | 31 ++--------------- mesonbuild/interpreterbase/interpreterbase.py | 34 ++++++++++++++++++- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 9e00c8d251d0..0d6470adccfc 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -10,7 +10,7 @@ import os import typing as T -from .. import compilers, environment, mesonlib, optinterpreter, options +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 @@ -112,14 +112,7 @@ def _str_list(node: T.Any) -> T.Optional[T.List[str]]: proj_license_files = _str_list(kwargs.get('license_files', None)) or [] self.project_data = {'descriptive_name': proj_name, 'version': proj_vers, 'license': proj_license, 'license_files': proj_license_files} - optfile = os.path.join(self.source_root, self.subdir, 'meson.options') - if not os.path.exists(optfile): - optfile = os.path.join(self.source_root, self.subdir, 'meson_options.txt') - if os.path.exists(optfile): - oi = optinterpreter.OptionInterpreter(self.coredata.optstore, self.subproject) - oi.process(optfile) - assert isinstance(proj_name, str), 'for mypy' - self.coredata.update_project_options(oi.options, T.cast('SubProject', proj_name)) + self._load_option_file() def_opts = self.flatten_args(kwargs.get('default_options', [])) _project_default_options = mesonlib.stringlistify(def_opts) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1247dfe3b80c..e4183bf87149 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -4,7 +4,7 @@ from __future__ import annotations -import hashlib, io, sys, traceback +import io, sys, traceback from .. import mparser from .. import environment @@ -13,7 +13,6 @@ from .. import mlog from .. import options from .. import build -from .. import optinterpreter from .. import compilers from .. import envconfig from ..wrap import wrap, WrapMode @@ -1181,33 +1180,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str else: mesonlib.project_meson_versions[self.subproject] = mesonlib.NoProjectVersion() - # Load "meson.options" before "meson_options.txt", and produce a warning if - # it is being used with an old version. I have added check that if both - # exist the warning isn't raised - option_file = os.path.join(self.source_root, self.subdir, 'meson.options') - old_option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') - - if os.path.exists(option_file): - if os.path.exists(old_option_file): - if os.path.samefile(option_file, old_option_file): - mlog.debug("Not warning about meson.options with version minimum < 1.1 because meson_options.txt also exists") - else: - raise MesonException("meson.options and meson_options.txt both exist, but are not the same file.") - else: - FeatureNew.single_use('meson.options file', '1.1', self.subproject, 'Use meson_options.txt instead') - else: - option_file = old_option_file - if os.path.exists(option_file): - with open(option_file, 'rb') as f: - # We want fast not cryptographically secure, this is just to - # see if the option file has changed - self.coredata.options_files[self.subproject] = (option_file, hashlib.sha1(f.read()).hexdigest()) - oi = optinterpreter.OptionInterpreter(self.environment.coredata.optstore, self.subproject) - oi.process(option_file) - self.coredata.update_project_options(oi.options, self.subproject) - self.add_build_def_file(option_file) - else: - self.coredata.options_files[self.subproject] = None + self._load_option_file() self.project_default_options = kwargs['default_options'] if isinstance(self.project_default_options, str): diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 2dcb81b0fc69..53d62c385a65 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -27,13 +27,14 @@ SubdirDoneRequest, ) +from .. import mlog from .decorators import FeatureNew from .disabler import Disabler, is_disabled from .helpers import default_resolve_key, flatten, resolve_second_level_holders, stringifyUserArguments from .operator import MesonOperator from ._unholder import _unholder -import os, copy, re, pathlib +import os, copy, hashlib, re, pathlib import typing as T import textwrap @@ -675,6 +676,37 @@ def get_variable(self, varname: str) -> InterpreterObject: 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 + + # Load "meson.options" before "meson_options.txt", and produce a warning if + # it is being used with an old version. I have added check that if both + # exist the warning isn't raised + option_file = os.path.join(self.source_root, self.subdir, 'meson.options') + old_option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') + + if os.path.exists(option_file): + if os.path.exists(old_option_file): + if os.path.samefile(option_file, old_option_file): + mlog.debug("Not warning about meson.options with version minimum < 1.1 because meson_options.txt also exists") + else: + raise mesonlib.MesonException("meson.options and meson_options.txt both exist, but are not the same file.") + else: + FeatureNew.single_use('meson.options file', '1.1', self.subproject, 'Use meson_options.txt instead') + else: + option_file = old_option_file + if os.path.exists(option_file): + with open(option_file, 'rb') as f: + # We want fast not cryptographically secure, this is just to + # see if the option file has changed + self.coredata.options_files[self.subproject] = (option_file, hashlib.sha1(f.read()).hexdigest()) + oi = optinterpreter.OptionInterpreter(self.environment.coredata.optstore, self.subproject) + oi.process(option_file) + self.coredata.update_project_options(oi.options, self.subproject) + self.build_def_files.add(option_file) + else: + self.coredata.options_files[self.subproject] = None + def _resolve_subdir(self, rootdir: str, new_subdir: str) -> T.Tuple[str, bool]: subdir = os.path.join(self.subdir, new_subdir) absdir = os.path.join(rootdir, subdir) From e14983026c231aed1376f13727940ef7af37a7cf Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 13 Mar 2025 16:05:06 -0400 Subject: [PATCH 388/624] Add subTests to test_introspect_json_dump --- unittests/allplatformstests.py | 186 ++++++++++++++++----------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 84d5245d9551..fbb1c91b51d4 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3399,100 +3399,100 @@ def assertKeyTypes(key_type_list, obj, strict: bool = True): src_to_id.update({os.path.relpath(src, testdir): i['id'] for src in group.get('sources', [])}) - # Check Tests and benchmarks - tests_to_find = ['test case 1', 'test case 2', 'benchmark 1'] - deps_to_find = {'test case 1': [src_to_id['t1.cpp']], - 'test case 2': [src_to_id['t2.cpp'], src_to_id['t3.cpp']], - 'benchmark 1': [out_to_id['file2'], out_to_id['file3'], out_to_id['file4'], src_to_id['t3.cpp']]} - for i in res['benchmarks'] + res['tests']: - assertKeyTypes(test_keylist, i) - if i['name'] in tests_to_find: - tests_to_find.remove(i['name']) - self.assertEqual(sorted(i['depends']), - sorted(deps_to_find[i['name']])) - self.assertListEqual(tests_to_find, []) - - # Check buildoptions - buildopts_to_find = {'cpp_std': 'c++11'} - for i in res['buildoptions']: - assertKeyTypes(buildoptions_keylist, i) - valid_type = False - for j in buildoptions_typelist: - if i['type'] == j[0]: - self.assertIsInstance(i['value'], j[1]) - assertKeyTypes(j[2], i, strict=False) - valid_type = True - break - - self.assertIn(i['section'], buildoptions_sections) - self.assertIn(i['machine'], buildoptions_machines) - self.assertTrue(valid_type) - if i['name'] in buildopts_to_find: - self.assertEqual(i['value'], buildopts_to_find[i['name']]) - buildopts_to_find.pop(i['name'], None) - self.assertDictEqual(buildopts_to_find, {}) - - # Check buildsystem_files - bs_files = ['meson.build', 'meson_options.txt', 'sharedlib/meson.build', 'staticlib/meson.build'] - bs_files = [os.path.join(testdir, x) for x in bs_files] - self.assertPathListEqual(list(sorted(res['buildsystem_files'])), list(sorted(bs_files))) - - # Check dependencies - dependencies_to_find = ['threads'] - for i in res['dependencies']: - assertKeyTypes(dependencies_typelist, i) - if i['name'] in dependencies_to_find: - dependencies_to_find.remove(i['name']) - self.assertListEqual(dependencies_to_find, []) - - # Check projectinfo - self.assertDictEqual(res['projectinfo'], { - 'version': '1.2.3', - 'license': ['unknown'], - 'license_files': [], - 'descriptive_name': 'introspection', - 'subproject_dir': 'subprojects', - 'subprojects': [] - }) - - # Check targets - targets_to_find = { - 'sharedTestLib': ('shared library', True, False, 'sharedlib/meson.build', - [os.path.join(testdir, 'sharedlib', 'shared.cpp')]), - 'staticTestLib': ('static library', True, False, 'staticlib/meson.build', - [os.path.join(testdir, 'staticlib', 'static.c')]), - 'custom target test 1': ('custom', False, False, 'meson.build', - [os.path.join(testdir, 'cp.py')]), - 'custom target test 2': ('custom', False, False, 'meson.build', - name_to_out['custom target test 1']), - 'test1': ('executable', True, True, 'meson.build', - [os.path.join(testdir, 't1.cpp')]), - 'test2': ('executable', True, False, 'meson.build', - [os.path.join(testdir, 't2.cpp')]), - 'test3': ('executable', True, False, 'meson.build', - [os.path.join(testdir, 't3.cpp')]), - 'custom target test 3': ('custom', False, False, 'meson.build', - name_to_out['test3']), - } - for i in res['targets']: - assertKeyTypes(targets_typelist, i) - if i['name'] in targets_to_find: - tgt = targets_to_find[i['name']] - self.assertEqual(i['type'], tgt[0]) - self.assertEqual(i['build_by_default'], tgt[1]) - self.assertEqual(i['installed'], tgt[2]) - self.assertPathEqual(i['defined_in'], os.path.join(testdir, tgt[3])) - targets_to_find.pop(i['name'], None) - for j in i['target_sources']: - if 'compiler' in j: - if j['language'] == 'unknown': - assertKeyTypes(targets_sources_unknown_lang_typelist, j) + with self.subTest('Check Tests and Benchmarks'): + tests_to_find = ['test case 1', 'test case 2', 'benchmark 1'] + deps_to_find = {'test case 1': [src_to_id['t1.cpp']], + 'test case 2': [src_to_id['t2.cpp'], src_to_id['t3.cpp']], + 'benchmark 1': [out_to_id['file2'], out_to_id['file3'], out_to_id['file4'], src_to_id['t3.cpp']]} + for i in res['benchmarks'] + res['tests']: + assertKeyTypes(test_keylist, i) + if i['name'] in tests_to_find: + tests_to_find.remove(i['name']) + self.assertEqual(sorted(i['depends']), + sorted(deps_to_find[i['name']])) + self.assertListEqual(tests_to_find, []) + + with self.subTest('Check buildoptions'): + buildopts_to_find = {'cpp_std': 'c++11'} + for i in res['buildoptions']: + assertKeyTypes(buildoptions_keylist, i) + valid_type = False + for j in buildoptions_typelist: + if i['type'] == j[0]: + self.assertIsInstance(i['value'], j[1]) + assertKeyTypes(j[2], i, strict=False) + valid_type = True + break + + self.assertIn(i['section'], buildoptions_sections) + self.assertIn(i['machine'], buildoptions_machines) + self.assertTrue(valid_type) + if i['name'] in buildopts_to_find: + self.assertEqual(i['value'], buildopts_to_find[i['name']]) + buildopts_to_find.pop(i['name'], None) + self.assertDictEqual(buildopts_to_find, {}) + + with self.subTest('Check buildsystem_files'): + bs_files = ['meson.build', 'meson_options.txt', 'sharedlib/meson.build', 'staticlib/meson.build'] + bs_files = [os.path.join(testdir, x) for x in bs_files] + self.assertPathListEqual(list(sorted(res['buildsystem_files'])), list(sorted(bs_files))) + + with self.subTest('Check dependencies'): + dependencies_to_find = ['threads'] + for i in res['dependencies']: + assertKeyTypes(dependencies_typelist, i) + if i['name'] in dependencies_to_find: + dependencies_to_find.remove(i['name']) + self.assertListEqual(dependencies_to_find, []) + + with self.subTest('Check projectinfo'): + self.assertDictEqual(res['projectinfo'], { + 'version': '1.2.3', + 'license': ['unknown'], + 'license_files': [], + 'descriptive_name': 'introspection', + 'subproject_dir': 'subprojects', + 'subprojects': [] + }) + + with self.subTest('Check targets'): + targets_to_find = { + 'sharedTestLib': ('shared library', True, False, 'sharedlib/meson.build', + [os.path.join(testdir, 'sharedlib', 'shared.cpp')]), + 'staticTestLib': ('static library', True, False, 'staticlib/meson.build', + [os.path.join(testdir, 'staticlib', 'static.c')]), + 'custom target test 1': ('custom', False, False, 'meson.build', + [os.path.join(testdir, 'cp.py')]), + 'custom target test 2': ('custom', False, False, 'meson.build', + name_to_out['custom target test 1']), + 'test1': ('executable', True, True, 'meson.build', + [os.path.join(testdir, 't1.cpp')]), + 'test2': ('executable', True, False, 'meson.build', + [os.path.join(testdir, 't2.cpp')]), + 'test3': ('executable', True, False, 'meson.build', + [os.path.join(testdir, 't3.cpp')]), + 'custom target test 3': ('custom', False, False, 'meson.build', + name_to_out['test3']), + } + for i in res['targets']: + assertKeyTypes(targets_typelist, i) + if i['name'] in targets_to_find: + tgt = targets_to_find[i['name']] + self.assertEqual(i['type'], tgt[0]) + self.assertEqual(i['build_by_default'], tgt[1]) + self.assertEqual(i['installed'], tgt[2]) + self.assertPathEqual(i['defined_in'], os.path.join(testdir, tgt[3])) + targets_to_find.pop(i['name'], None) + for j in i['target_sources']: + if 'compiler' in j: + if j['language'] == 'unknown': + assertKeyTypes(targets_sources_unknown_lang_typelist, j) + else: + assertKeyTypes(targets_sources_typelist, j) + self.assertEqual(j['sources'], [os.path.normpath(f) for f in tgt[4]]) else: - assertKeyTypes(targets_sources_typelist, j) - self.assertEqual(j['sources'], [os.path.normpath(f) for f in tgt[4]]) - else: - assertKeyTypes(target_sources_linker_typelist, j) - self.assertDictEqual(targets_to_find, {}) + assertKeyTypes(target_sources_linker_typelist, j) + self.assertDictEqual(targets_to_find, {}) def test_introspect_file_dump_equals_all(self): testdir = os.path.join(self.unit_test_dir, '56 introspection') From d17df82efa5dd0b60b9b2a25f8a5e2f34477af6a Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 13 Mar 2025 16:35:21 -0400 Subject: [PATCH 389/624] Move Interpreter.read_buildfile to base class --- mesonbuild/interpreter/interpreter.py | 5 ----- mesonbuild/interpreterbase/interpreterbase.py | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e4183bf87149..a17c76c5f55b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -328,11 +328,6 @@ def _redetect_machines(self) -> None: self.builtin['target_machine'] = \ OBJ.MachineHolder(self.build.environment.machines.target, self) - def load_root_meson_file(self) -> None: - build_filename = os.path.join(self.subdir, environment.build_filename) - self.build_def_files.add(build_filename) - super().load_root_meson_file() - def build_func_dict(self) -> None: self.funcs.update({'add_global_arguments': self.func_add_global_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments, diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 53d62c385a65..2bdb5ef2e3d8 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -109,7 +109,9 @@ def read_buildfile(self, fname: str, errname: str) -> str: raise InvalidCode.from_node(f'Build file failed to parse as unicode: {e}', node=node) def load_root_meson_file(self) -> None: - mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename) + build_filename = os.path.join(self.subdir, environment.build_filename) + self.build_def_files.add(build_filename) + mesonfile = os.path.join(self.source_root, build_filename) if not os.path.isfile(mesonfile): raise InvalidArguments(f'Missing Meson file in {mesonfile}') code = self.read_buildfile(mesonfile, mesonfile) From ad04f4ead91590ef17f8ec999b18bae2a41576e5 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Wed, 19 Mar 2025 17:21:37 +0100 Subject: [PATCH 390/624] hdf5: don't throw if the pkg-config doesn't support --list-all --- mesonbuild/dependencies/hdf5.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py index 62265302564c..7595c7cc61fb 100644 --- a/mesonbuild/dependencies/hdf5.py +++ b/mesonbuild/dependencies/hdf5.py @@ -153,10 +153,14 @@ def hdf5_factory(env: 'Environment', for_machine: 'MachineChoice', pkgconfig_files = OrderedSet(['hdf5', 'hdf5-serial']) pkg = PkgConfigInterface.instance(env, for_machine, silent=False) if pkg: - # some distros put hdf5-1.2.3.pc with version number in .pc filename. - for mod in pkg.list_all(): - if mod.startswith('hdf5'): - pkgconfig_files.add(mod) + try: + # old hdf5 versions put version number in .pc filename, e.g., hdf5-1.2.3.pc. + for mod in pkg.list_all(): + if mod.startswith('hdf5'): + pkgconfig_files.add(mod) + except DependencyException: + # use just the standard files if pkg-config --list-all fails + pass for mod in pkgconfig_files: candidates.append(functools.partial(HDF5PkgConfigDependency, mod, env, kwargs, language)) From d8f68703b792ff6e9cfc2674fdf9bbd53ac170ca Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 20 Mar 2025 17:27:05 +0100 Subject: [PATCH 391/624] ci: stop installing python2 for the msys2 jobs, it's gone MSYS2 dropped Python 2 in https://github.com/msys2/MINGW-packages/pull/23713 --- .github/workflows/msys2.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index 3b518fee7607..9101e6b9d1f5 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -79,7 +79,6 @@ jobs: mingw-w64-${{ matrix.MSYS2_ARCH }}-libxml2 mingw-w64-${{ matrix.MSYS2_ARCH }}-ninja mingw-w64-${{ matrix.MSYS2_ARCH }}-pkg-config - mingw-w64-${{ matrix.MSYS2_ARCH }}-python2 mingw-w64-${{ matrix.MSYS2_ARCH }}-python mingw-w64-${{ matrix.MSYS2_ARCH }}-python-lxml mingw-w64-${{ matrix.MSYS2_ARCH }}-python-setuptools From e5286aefd962df25dee58556c4ce6dafa4bfd1c3 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 20 Mar 2025 12:16:06 -0400 Subject: [PATCH 392/624] handle monorepo license files specified in project() via ../ We should simply remap these to elide the ../ as it's pretty obviously the natural expectation of using ../ to fetch files from outside the project and then drop them *into* the project. Monorepos will likely have a single license file (or set) under which the monorepo is licensed. But there will be many components, each of which may use a different build system, which are "standalone" for the most part. We already support this case as long as you build from the monorepo, but the resulting license file gets installed to ``` {licensedir}/../ ``` which is silly and unhelpful. Bug: https://github.com/apache/arrow/issues/36411 --- mesonbuild/backend/backends.py | 6 +++--- mesonbuild/build.py | 9 ++++++++- test cases/common/42 subproject/meson.build | 3 ++- test cases/common/42 subproject/test.json | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index feb002e54665..a960537a85c0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1354,9 +1354,9 @@ def generate_depmf_install(self, d: InstallData) -> None: d.data.append(InstallDataBase(ifilename, ofilename, out_name, None, '', tag='devel', data_type='depmf')) for m in self.build.dep_manifest.values(): - for ifilename, name in m.license_files: - ofilename = os.path.join(odirname, name.relative_name()) - out_name = os.path.join(out_dir, name.relative_name()) + for ifilename, name in m.license_mapping(): + ofilename = os.path.join(odirname, name) + out_name = os.path.join(out_dir, name) d.data.append(InstallDataBase(ifilename, ofilename, out_name, None, m.subproject, tag='devel', data_type='depmf')) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a883c3b28fb3..6e233a382de0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -223,11 +223,18 @@ class DepManifest: license_files: T.List[T.Tuple[str, File]] subproject: str + def license_mapping(self) -> T.List[T.Tuple[str, str]]: + ret = [] + for ifilename, name in self.license_files: + fname = os.path.join(*(x for x in pathlib.PurePath(os.path.normpath(name.fname)).parts if x != '..')) + ret.append((ifilename, os.path.join(name.subdir, fname))) + return ret + def to_json(self) -> T.Dict[str, T.Union[str, T.List[str]]]: return { 'version': self.version, 'license': self.license, - 'license_files': [l[1].relative_name() for l in self.license_files], + 'license_files': [l[1] for l in self.license_mapping()], } diff --git a/test cases/common/42 subproject/meson.build b/test cases/common/42 subproject/meson.build index c2c0122f1f2e..bc24adbcc9c5 100644 --- a/test cases/common/42 subproject/meson.build +++ b/test cases/common/42 subproject/meson.build @@ -1,7 +1,8 @@ project('subproj user', 'c', version : '2.3.4', license : 'mylicense', - license_files: 'mylicense.txt', + # also grab the meson license to test monorepo support + license_files: ['mylicense.txt', '../../../COPYING'], ) assert(meson.project_name() == 'subproj user', 'Incorrect project name') diff --git a/test cases/common/42 subproject/test.json b/test cases/common/42 subproject/test.json index 949cb79fa0cc..e469052f4396 100644 --- a/test cases/common/42 subproject/test.json +++ b/test cases/common/42 subproject/test.json @@ -4,6 +4,7 @@ {"type": "pdb", "file": "usr/bin/user"}, {"type": "file", "file": "usr/share/sublib/sublib.depmf"}, {"type": "file", "file": "usr/share/sublib/mylicense.txt"}, + {"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"} ] From 992ec923a7b81845d0c2a0fe9374fd9cf8d054fa Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 19 Mar 2025 10:35:06 -0400 Subject: [PATCH 393/624] man page: Running without 'setup' is deprecated Move the mention of the (deprecated) default command mode down near the end of the page, instead of presenting it right up front. --- man/meson.1 | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index deb320dbc7f6..af8233cde04a 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -40,19 +40,6 @@ your build dir. After that you just run the build command. Meson will autodetect changes in your source tree and regenerate all files needed to build the project. -The setup command is the default operation. If no actual command is -specified, Meson will assume you meant to do a setup. That means -that you can set up a build directory without the setup command -like this: - -.B meson [ -.I options -.B ] [ -.I build directory -.B ] [ -.I source directory -.B ] - .SS "options:" .TP \fB\-\-version\fR @@ -658,6 +645,13 @@ try to read configuration from .editorconfig \fB-o OUTPUT, --output OUTPUT\fR output file (implies having exactly one input) +.SH When no command is specified + +If you run Meson without a subcommand, it will assume you meant +\fBmeson setup\fR. However, this syntax is deprecated, and Meson +will print a warning message if it is used. You should always use +\fBmeson setup\fR explicitly, instead of relying on the default. + .SH EXIT STATUS .TP From f164ec692f3d173284d3671ecc908cf9b7b67f79 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Fri, 21 Mar 2025 18:10:10 +0000 Subject: [PATCH 394/624] CI: Fix filemode tests with cygwin 3.6.0 Put cygwin filemode tests back under the sourcedir Remove inheritable permissions from the sourcedir For :reasons:, the unit tests which check file mode were built in the tempdir. Instead, remove inheritable permissions from the working directory (which the GitHub VM image has set for some reaons), since they can interfere with getting exactly the file mode you asked for. Partially reverts 04ae1cfb7999e25f476f84572ff0ad853629346c --- .github/workflows/cygwin.yml | 4 ++++ unittests/baseplatformtests.py | 12 ------------ unittests/linuxliketests.py | 6 ------ 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 441637c154f7..d641b185050b 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -41,6 +41,10 @@ jobs: MESON_CI_JOBNAME: cygwin-${{ matrix.NAME }} steps: + # remove inheritable permissions since they break assumptions testsuite + # makes about file modes + - run: icacls . /inheritance:r /T /C + - uses: actions/cache/restore@v4 id: restore-cache with: diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index 73682e03a12d..5fff212f1440 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -125,18 +125,6 @@ def new_builddir(self): newdir = os.path.realpath(newdir) self.change_builddir(newdir) - def new_builddir_in_tempdir(self): - # Can't keep the builddir inside the source tree for the umask tests: - # https://github.com/mesonbuild/meson/pull/5546#issuecomment-509666523 - # And we can't do this for all tests because it causes the path to be - # a short-path which breaks other tests: - # https://github.com/mesonbuild/meson/pull/9497 - newdir = tempfile.mkdtemp() - # In case the directory is inside a symlinked directory, find the real - # path otherwise we might not find the srcdir from inside the builddir. - newdir = os.path.realpath(newdir) - self.change_builddir(newdir) - def _open_meson_log(self) -> io.TextIOWrapper: log = os.path.join(self.logdir, 'meson-log.txt') return open(log, encoding='utf-8') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 01862b61e5ae..03b57b9f8bec 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -590,8 +590,6 @@ def test_installed_modes(self): Test that files installed by these tests have the correct permissions. Can't be an ordinary test because our installed_files.txt is very basic. ''' - if is_cygwin(): - self.new_builddir_in_tempdir() # Test file modes testdir = os.path.join(self.common_test_dir, '12 data') self.init(testdir) @@ -644,8 +642,6 @@ def test_installed_modes_extended(self): ''' Test that files are installed with correct permissions using install_mode. ''' - if is_cygwin(): - self.new_builddir_in_tempdir() testdir = os.path.join(self.common_test_dir, '190 install_mode') self.init(testdir) self.build() @@ -684,8 +680,6 @@ def test_install_umask(self): install umask of 022, regardless of the umask at time the worktree was checked out or the build was executed. ''' - if is_cygwin(): - self.new_builddir_in_tempdir() # Copy source tree to a temporary directory and change permissions # there to simulate a checkout with umask 002. orig_testdir = os.path.join(self.unit_test_dir, '26 install umask') From 67a903bfd46658e11e18e0d41989de3c2d9b5082 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 21 Mar 2025 20:15:14 -0300 Subject: [PATCH 395/624] options: Make sure the gnu99 deprecation is only printed once Cuts down the spam radically when building with MSVC. --- mesonbuild/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 01532b41b7ff..f219ebda5b8a 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -623,7 +623,7 @@ def validate_value(self, value: T.Union[str, T.List[str]]) -> str: f'However, the deprecated {std} std currently falls back to {newstd}.\n' + 'This will be an error in meson 2.0.\n' + 'If the project supports both GNU and MSVC compilers, a value such as\n' + - '"c_std=gnu11,c11" specifies that GNU is preferred but it can safely fallback to plain c11.') + '"c_std=gnu11,c11" specifies that GNU is preferred but it can safely fallback to plain c11.', once=True) return newstd raise MesonException(f'None of values {candidates} are supported by the {self.lang.upper()} compiler. ' + f'Possible values for option "{self.name}" are {self.choices}') From 17117c2732c33ebec39e3fec2ecd5a3515dc7d71 Mon Sep 17 00:00:00 2001 From: Ross Burton Date: Wed, 20 Nov 2024 13:09:38 +0000 Subject: [PATCH 396/624] dependencies/dev: prepend sysroot when searching for GTest sources Don't hardcode paths in /usr when looking for the GTest sources, as in cross-compile or other builds with a sysroot this will find the host sources, not ones that we want to use in the sysroot. Closes #12690. --- mesonbuild/dependencies/dev.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 2725a7bb4e88..0c8886b81a04 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -56,7 +56,9 @@ class GTestDependencySystem(SystemDependency): def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> None: super().__init__(name, environment, kwargs, language='cpp') self.main = kwargs.get('main', False) - self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src'] + + sysroot = environment.properties[self.for_machine].get_sys_root() or '' + self.src_dirs = [sysroot + '/usr/src/gtest/src', sysroot + '/usr/src/googletest/googletest/src'] if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})): self.is_found = False return From 5d5f0ce8c3d4c0c0e83d1e33597748a9a2212bcb Mon Sep 17 00:00:00 2001 From: Khairul Azhar Kasmiran Date: Sat, 22 Mar 2025 16:32:30 +0800 Subject: [PATCH 397/624] Fix doc for `meson test --print-errorlogs` [skip ci] --- docs/markdown/Unit-tests.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 5a7b19940b39..e89f393a2cae 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -266,6 +266,11 @@ be specified *(added 0.52.0)*: $ meson test --gdb --gdb-path /path/to/gdb testname ``` +Meson can print the error logs produced by failing tests via the +`--print-errorlogs` option. The logs can include stack traces and environmental +variables. This is especially useful when you run the tests on GitHub, Travis, +Jenkins and the like: + ```console $ meson test --print-errorlogs ``` @@ -280,11 +285,6 @@ shell is spawned if it fails *(added 1.5.0)*: $ meson test --interactive testname ``` -Meson will report the output produced by the failing tests along with -other useful information as the environmental variables. This is -useful, for example, when you run the tests on Travis-CI, Jenkins and -the like. - By default, the output from tests will be limited to the last 100 lines. The maximum number of lines to show can be configured with the `--max-lines` option *(added 1.5.0)*: From 4c52a68517521b2f79780b58927030b02c7a53b4 Mon Sep 17 00:00:00 2001 From: "hpkfft.com" Date: Sat, 22 Mar 2025 00:18:14 -0700 Subject: [PATCH 398/624] docs: Fix apt-get installation command line --- docs/markdown/Quick-guide.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/markdown/Quick-guide.md b/docs/markdown/Quick-guide.md index 14914567fda3..9b5cc14090c8 100644 --- a/docs/markdown/Quick-guide.md +++ b/docs/markdown/Quick-guide.md @@ -35,11 +35,10 @@ generate native VS and Xcode project files.* Installation using package manager -- -Ubuntu: +Debian or Ubuntu: ```console -$ sudo apt-get install python3 python3-pip python3-setuptools \ - python3-wheel ninja-build +$ sudo apt-get install python3 ninja-build meson ``` *Due to our frequent release cycle and development speed, distro packaged software may quickly become outdated.* @@ -47,13 +46,19 @@ Installation using Python -- Requirements: **pip3** -The best way to receive the most up-to-date version of Mesonbuild. +This is the best way to receive the most up-to-date version of Mesonbuild. + +First, install dependencies using the package manager: +```console +$ sudo apt-get install python3 python3-pip python3-setuptools \ + python3-wheel ninja-build +``` -Install as a local user (recommended): +Then, install meson as a local user (recommended): ```console $ pip3 install --user meson ``` -Install as root: +Or, install meson as root: ```console # pip3 install meson ``` From cfb5a48e075ad0da9341b35b24872634784fccf8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Mar 2025 08:49:26 +0100 Subject: [PATCH 399/624] linkers: darwin: do not use -bundle for shared_modules Both dynamic libraries and bundles these days can be dynamically loaded using the dl APIs (e.g. dlopen, dlclose). It is not possible to include bundles on a linker command line as if they were shared libraries, but that's pretty much the only difference. However, bundles fail the Apple verification for iOS: 2025-02-10 13:54:09.095 ERROR: Validation failed (409) The binary is invalid. The executable 'Runner.app/Frameworks/numpy._core._operand_flag_tests.framework/numpy._core._operand_flag_tests' has type 'BUNDLE' that is not valid. Only 'EXECUTE' is permitted. 2025-02-10 13:54:09.096 ERROR: Validation failed (409) Missing load commands. The executable at 'Runner.app/Frameworks/numpy._core._operand_flag_tests.framework' does not have the necessary load commands. Try rebuilding the app with the latest Xcode version. If you are using third party development tools, contact the provider. So switch to -dynamiclib for shared modules as well. Fixes: #14240 --- docs/markdown/Builtin-options.md | 3 +-- mesonbuild/linkers/linkers.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index e1686f8d2512..56e30885cde5 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -272,8 +272,7 @@ with `b_asneeded`, so that option will be silently disabled. [[shared_module]]s will not have bitcode embedded because `-Wl,-bitcode_bundle` is incompatible with -both `-bundle` and `-Wl,-undefined,dynamic_lookup` which are necessary -for shared modules to work. +`-Wl,-undefined,dynamic_lookup` which is necessary for shared modules to work. ## Compiler options diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index ba642119f9f0..45eb6e8c3c85 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -806,7 +806,7 @@ def get_allow_undefined_args(self) -> T.List[str]: return self._apply_prefix('-undefined,dynamic_lookup') def get_std_shared_module_args(self, target: 'BuildTarget') -> T.List[str]: - return ['-bundle'] + self._apply_prefix('-undefined,dynamic_lookup') + return ['-dynamiclib'] + self._apply_prefix('-undefined,dynamic_lookup') def get_pie_args(self) -> T.List[str]: return [] From 1447fff171a5f6e76f9b56504f4f983a152eda79 Mon Sep 17 00:00:00 2001 From: Paul Caprioli Date: Tue, 25 Mar 2025 20:49:27 -0700 Subject: [PATCH 400/624] docs: Clarify string building with absolute paths --- docs/markdown/Syntax.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index 59ec5f7ba07d..05f503880e23 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -150,6 +150,8 @@ combined = str1 + '_' + str2 # combined is now abc_xyz You can concatenate any two strings using `/` as an operator to build paths. This will always use `/` as the path separator on all platforms. +If any one of the individual segments is an absolute path, all segments before +it are dropped. For example: ```meson joined = '/usr/share' / 'projectname' # => /usr/share/projectname From 8afce56719272bb5367c5ae304b3a935962a5391 Mon Sep 17 00:00:00 2001 From: yexiaochuan Date: Mon, 24 Mar 2025 00:28:00 +0800 Subject: [PATCH 401/624] envconfig: fix unhandled exception when cross-file lacks required keys Fix the unhandled KeyError exception that occurs when cross-compilation configuration files are missing required parameters (such as 'endian'). This issue was introduced in commit b0d2a92 (PR #11692), where the key validation logic didn't properly handle the relationship between provided and required keys: - Previously, the code used `set(literal) < minimum_literal` to check if provided keys were a strict subset of the required keys in minimum_literal - This validation logic broke down when the provided keys weren't a strict subset anymore, but rather an overlapping set with disjoint elements on both sides - When required keys were missing, the code continued execution and later threw an unhandled KeyError when trying to access the non-existent keys Changed the condition check from: if set(literal) < minimum_literal: to: if minimum_literal - set(literal): This new check specifically identifies keys that are "present in required but not present in provided", providing users with clear error messages instead of raising unhandled exceptions. This change also removes the implicit requirement that "provided keys must not contain any keys not present in the required set" - allowing for optional keys to exist in the provided configuration. Fixes #14385 --- mesonbuild/envconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 7cbef8928fa1..c877a7c5a78e 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -281,7 +281,7 @@ def from_literal(cls, raw: T.Dict[str, ElementaryOptionValues]) -> 'MachineInfo' assert all(isinstance(v, str) for v in raw.values()), 'for mypy' literal = T.cast('T.Dict[str, str]', raw) minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'} - if set(literal) < minimum_literal: + if minimum_literal - set(literal): raise EnvironmentException( f'Machine info is currently {literal}\n' + 'but is missing {}.'.format(minimum_literal - set(literal))) From 19482e4553775d4612f5fcc1c7c124774e9a0f79 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 26 Mar 2025 11:26:21 -0400 Subject: [PATCH 402/624] select the correct meson_command for pyinstaller builds, even on not-Windows We have previously updated the python_command handling to correctly detect PyInstaller builds without assuming that it is only Windows, back in commit c39ee881a1959ae37aded8e0c24cec23b2bd6a90. But the fix was incomplete. This affects e.g. running --internal scripts such as symbolextractor. The issue in this case is slightly more subtle. While sys.executable won't be a valid python so it intuitively falls over quite badly when trying to run it as one, getting the original command is broken in a more interesting way. PyInstaller sets `sys.argv[0]` to the basename of the executable, which then got absolutized to a nonexistent file in the current working directory. Fixes: https://github.com/mesonbuild/meson/issues/14412 --- mesonbuild/mesonmain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 8a8678a9a944..6a88501d4564 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -305,7 +305,7 @@ def run(original_args: T.List[str], mainfile: str) -> int: def main() -> int: # Always resolve the command path so Ninja can find it for regen, tests, etc. - if 'meson.exe' in sys.executable: + if getattr(sys, 'frozen', False): assert os.path.isabs(sys.executable) launcher = sys.executable else: From 6cc848dafdf5696430a0fa68b5717fb788e70820 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 18:00:58 +0100 Subject: [PATCH 403/624] rust: new target rustdoc Another rust tool, another rough copy of the code to run clippy. Apart from the slightly different command lines, the output is in a directory and test targets are skipped. Knowing the output directory can be useful, so print that on successful execution of rustdoc. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 16 +++++ mesonbuild/scripts/rustdoc.py | 101 +++++++++++++++++++++++++++++ unittests/allplatformstests.py | 18 +++++ 3 files changed, 135 insertions(+) create mode 100644 mesonbuild/scripts/rustdoc.py diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index f698a2b01a03..9ca092c5a40d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3716,6 +3716,21 @@ def generate_clippy(self) -> None: elem.add_dep(list(self.all_structured_sources)) self.add_build(elem) + def generate_rustdoc(self) -> None: + if 'rustdoc' in self.all_outputs or not self.have_language('rust'): + return + + cmd = self.environment.get_build_command() + \ + ['--internal', 'rustdoc', self.environment.build_dir] + elem = self.create_phony_target('rustdoc', 'CUSTOM_COMMAND', 'PHONY') + elem.add_item('COMMAND', cmd) + elem.add_item('pool', 'console') + for crate in self.rust_crates.values(): + if crate.crate_type in {'rlib', 'dylib', 'proc-macro'}: + elem.add_dep(crate.target_name) + elem.add_dep(list(self.all_structured_sources)) + self.add_build(elem) + def generate_scanbuild(self) -> None: if not environment.detect_scanbuild(): return @@ -3789,6 +3804,7 @@ def generate_utils(self) -> None: self.generate_clangformat() self.generate_clangtidy() self.generate_clippy() + self.generate_rustdoc() self.generate_tags('etags', 'TAGS') self.generate_tags('ctags', 'ctags') self.generate_tags('cscope', 'cscope') diff --git a/mesonbuild/scripts/rustdoc.py b/mesonbuild/scripts/rustdoc.py new file mode 100644 index 000000000000..f5f74c4e3300 --- /dev/null +++ b/mesonbuild/scripts/rustdoc.py @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 The Meson development team + +from __future__ import annotations +from collections import defaultdict +import os +import tempfile +import typing as T + +from .run_tool import run_tool_on_targets, run_with_buffered_output +from .. import build, mlog +from ..mesonlib import MachineChoice, PerMachine +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) + if returncode == 0: + print(mlog.green('Generated'), os.path.join('doc', crate)) + return returncode + +class Rustdoc: + def __init__(self, build: build.Build, tempdir: str, subprojects: T.Set[str]) -> None: + self.tools: PerMachine[T.List[str]] = PerMachine([], []) + self.warned: T.DefaultDict[str, bool] = defaultdict(lambda: False) + self.tempdir = tempdir + self.subprojects = subprojects + for machine in MachineChoice: + 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) + + def warn_missing_rustdoc(self, machine: str) -> None: + if self.warned[machine]: + return + mlog.warning(f'rustdoc not found for {machine} machine') + self.warned[machine] = True + + def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, None, int]]: + if target['subproject'] is not None and target['subproject'] not in self.subprojects: + return + + for src_block in target['target_sources']: + if 'compiler' in src_block and src_block['language'] == 'rust': + rustdoc = getattr(self.tools, src_block['machine']) + if not rustdoc: + self.warn_missing_rustdoc(src_block['machine']) + continue + + cmdlist = list(rustdoc) + prev = None + crate_name = None + is_test = False + for arg in src_block['parameters']: + if prev: + if prev == '--crate-name': + cmdlist.extend((prev, arg)) + crate_name = arg + prev = None + continue + + if arg == '--test': + is_test = True + break + elif arg in {'--crate-name', '--emit', '--out-dir', '-l'}: + prev = arg + elif arg != '-g' and not arg.startswith('-l'): + cmdlist.append(arg) + + if is_test: + # --test has a completely different meaning for rustc and rustdoc; + # when using rust.test(), only the non-test target is documented + continue + if crate_name: + cmdlist.extend(src_block['sources']) + # Assume documentation is generated for the developer's use + cmdlist.append('--document-private-items') + cmdlist.append('-o') + cmdlist.append('doc') + yield run_and_confirm_success(cmdlist, crate_name) + else: + print(mlog.yellow('Skipping'), target['name'], '(no crate name)') + +def get_nonwrap_subprojects(build_data: build.Build) -> T.Set[str]: + wrap_resolver = wrap.Resolver( + build_data.environment.get_source_dir(), + build_data.subproject_dir, + wrap_mode=WrapMode.nodownload) + return set(sp + for sp in build_data.environment.coredata.initialized_subprojects + if sp and (sp not in wrap_resolver.wraps or wrap_resolver.wraps[sp].type is None)) + +def run(args: T.List[str]) -> int: + os.chdir(args[0]) + build_data = build.load(os.getcwd()) + subproject_list = get_nonwrap_subprojects(build_data) + with tempfile.TemporaryDirectory() as d: + return run_tool_on_targets(Rustdoc(build_data, d, subproject_list)) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index fbb1c91b51d4..a189065f6672 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4916,6 +4916,24 @@ def output_name(name, type_): with self.subTest(key='{}.{}'.format(data_type, file)): self.assertEqual(res[data_type][file], details) + @skip_if_not_language('rust') + @unittest.skipIf(not shutil.which('rustdoc'), 'Test requires rustdoc') + def test_rustdoc(self) -> None: + if self.backend is not Backend.ninja: + raise unittest.SkipTest('Rust is only supported with ninja currently') + try: + with tempfile.TemporaryDirectory() as tmpdir: + testdir = os.path.join(tmpdir, 'a') + shutil.copytree(os.path.join(self.rust_test_dir, '9 unit tests'), + testdir) + self.init(testdir) + self.build('rustdoc') + except PermissionError: + # When run under Windows CI, something (virus scanner?) + # holds on to the git files so cleaning up the dir + # fails sometimes. + pass + @skip_if_not_language('rust') @unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver') def test_rust_clippy(self) -> None: From 7875bc226137c07ae7b8c3678bfb76999984b627 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 08:46:56 +0100 Subject: [PATCH 404/624] docs: add release notes for "ninja rustdoc" Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/rustdoc.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/markdown/snippets/rustdoc.md diff --git a/docs/markdown/snippets/rustdoc.md b/docs/markdown/snippets/rustdoc.md new file mode 100644 index 000000000000..b0b64aaeb7b1 --- /dev/null +++ b/docs/markdown/snippets/rustdoc.md @@ -0,0 +1,6 @@ +## Meson can run "rustdoc" on Rust projects + +Meson now defines a `rustdoc` target if the project +uses the Rust programming language. The target runs rustdoc on all Rust +sources, using the `rustdoc` program from the same Rust toolchain as the +`rustc` compiler. From dfefd838a846dd944d8a3f91200b6c54f0ffd198 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 16:39:40 +0100 Subject: [PATCH 405/624] ninjabackend: split out generation of rustc arguments Allow reusing the code for doctests. In particular, the sources are shared between the two cases. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 73 +++++++++++++++++++----------- mesonbuild/compilers/rust.py | 2 + 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9ca092c5a40d..a3bc89c4952b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1931,24 +1931,12 @@ def _get_rust_dependency_name(self, target: build.BuildTarget, dependency: LibTy # in Rust return target.rust_dependency_map.get(dependency.name, dependency.name).replace('-', '_') - def generate_rust_target(self, target: build.BuildTarget) -> None: - rustc = target.compilers['rust'] + def generate_rust_sources(self, target: build.BuildTarget) -> T.Tuple[T.List[str], str]: + orderdeps: T.List[str] = [] + # Rust compiler takes only the main file as input and # figures out what other files are needed via import # statements and magic. - args = rustc.compiler_args() - # Compiler args for compiling this target - args += compilers.get_base_compile_args(target, rustc, self.environment) - self.generate_generator_list_rules(target) - - # dependencies need to cause a relink, they're not just for ordering - deps: T.List[str] = [] - - # Dependencies for rust-project.json - project_deps: T.List[RustDep] = [] - - orderdeps: T.List[str] = [] - main_rust_file = None if target.structured_sources: if target.structured_sources.needs_copy(): @@ -1978,14 +1966,10 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: orderdeps.extend(_ods) 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.') if main_rust_file is None: main_rust_file = i.rel_to_builddir(self.build_to_src) 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.') if isinstance(g, GeneratedList): fname = os.path.join(self.get_target_private_dir(target), i) else: @@ -1993,26 +1977,35 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: if main_rust_file is None: main_rust_file = fname orderdeps.append(fname) - if main_rust_file is None: - raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report') + + return orderdeps, main_rust_file + + def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, + depfile: T.Optional[str] = None) -> T.List[str]: + # Compiler args for compiling this target + args = compilers.get_base_compile_args(target, rustc, self.environment) + target_name = self.get_target_filename(target) args.extend(['--crate-type', target.rust_crate_type]) # If we're dynamically linking, add those arguments - # - # Rust is super annoying, calling -C link-arg foo does not work, it has - # to be -C link-arg=foo if target.rust_crate_type in {'bin', 'dylib'}: args.extend(rustc.get_linker_always_args()) 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('.', '_')] - depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d') - args += rustc.get_dependency_gen_args(target_name, depfile) + if depfile: + args += rustc.get_dependency_gen_args(target_name, depfile) args += rustc.get_output_args(target_name) args += ['-C', 'metadata=' + target.get_id()] 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[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. @@ -2126,6 +2119,33 @@ def _link_library(libname: str, static: bool, bundle: bool = False): if isinstance(target, build.SharedLibrary) or has_shared_deps: args += self.get_build_rpath_args(target, rustc) + return deps, project_deps, args + + def generate_rust_target(self, target: build.BuildTarget) -> None: + rustc = 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.') + + 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') + + 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, depfile) + + deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) + args += deps_args + proc_macro_dylib_path = None if target.rust_crate_type == 'proc-macro': proc_macro_dylib_path = self.get_target_filename_abs(target) @@ -2142,6 +2162,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): if orderdeps: element.add_orderdep(orderdeps) if deps: + # dependencies need to cause a relink, they're not just for ordering element.add_dep(deps) element.add_item('ARGS', args) element.add_item('targetdep', depfile) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 011c28f1d848..210544ed8597 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -279,6 +279,8 @@ def get_colorout_args(self, colortype: str) -> T.List[str]: 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 From 2c10d77c3c7b2b9ad3464ef37bc6eb01fb34deb0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Nov 2024 09:50:36 +0100 Subject: [PATCH 406/624] compilers: introduce get_exe() and get_exe_args() This will be used by rustdoc tests because the Test objects takes a single string for the command and everything else goes in the args. But apart from this, the need to split the executable from the arguments is common so create new methods to do it. While at it, fix brokenness in the handling of the zig compiler, which is checking against "zig" but failing to detect e.g. "/usr/bin/zig". Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 5 ++--- mesonbuild/cmake/toolchain.py | 3 ++- mesonbuild/compilers/compilers.py | 6 ++++++ mesonbuild/compilers/detect.py | 4 ++-- mesonbuild/compilers/rust.py | 2 +- mesonbuild/dependencies/dev.py | 2 +- mesonbuild/interpreter/interpreter.py | 7 +++---- mesonbuild/linkers/linkers.py | 6 ++++++ 8 files changed, 23 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a960537a85c0..8a788783766c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2078,9 +2078,8 @@ def compiler_to_generator(self, target: build.BuildTarget, Some backends don't support custom compilers. This is a convenience method to convert a Compiler to a Generator. ''' - exelist = compiler.get_exelist() - exe = programs.ExternalProgram(exelist[0]) - args = exelist[1:] + 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(), [output_templ], depfile='@PLAINNAME@.d', diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 9eb961c52bc7..d410886ecf25 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -9,6 +9,7 @@ from .common import language_map, cmake_get_generator_args from .. import mlog +import os.path import shutil import typing as T from enum import Enum @@ -198,7 +199,7 @@ def is_cmdline_option(compiler: 'Compiler', arg: str) -> bool: if compiler.get_argument_syntax() == 'msvc': return arg.startswith('/') else: - if compiler.exelist[0] == 'zig' and arg in {'ar', 'cc', 'c++', 'dlltool', 'lib', 'ranlib', 'objcopy', 'rc'}: + if os.path.basename(compiler.get_exe()) == 'zig' and arg in {'ar', 'cc', 'c++', 'dlltool', 'lib', 'ranlib', 'objcopy', 'rc'}: return True return arg.startswith('-') diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index a73697cddfb8..9fbde0e49306 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -521,6 +521,12 @@ def get_id(self) -> str: def get_modes(self) -> T.List[Compiler]: return self.modes + def get_exe(self) -> str: + return self.exelist[0] + + def get_exe_args(self) -> T.List[str]: + return self.exelist[1:] + def get_linker_id(self) -> str: # There is not guarantee that we have a dynamic linker instance, as # some languages don't have separate linkers and compilers. In those diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index bce0be8a8eed..9dab06a85a63 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1099,7 +1099,7 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust extra_args: T.Dict[str, T.Union[str, bool]] = {} always_args: T.List[str] = [] if is_link_exe: - compiler.extend(cls.use_linker_args(cc.linker.exelist[0], '')) + compiler.extend(cls.use_linker_args(cc.linker.get_exe(), '')) extra_args['direct'] = True extra_args['machine'] = cc.linker.machine else: @@ -1131,7 +1131,7 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust # inserts the correct prefix itself. assert isinstance(linker, linkers.VisualStudioLikeLinkerMixin) linker.direct = True - compiler.extend(cls.use_linker_args(linker.exelist[0], '')) + compiler.extend(cls.use_linker_args(linker.get_exe(), '')) else: # On linux and macos rust will invoke the c compiler for # linking, on windows it will use lld-link or link.exe. diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 210544ed8597..17908860e56f 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -314,7 +314,7 @@ def get_rust_tool(self, name: str, env: Environment) -> T.List[str]: exelist = rustup_exelist + [name] else: exelist = [name] - args = self.exelist[1:] + 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], diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 0c8886b81a04..8f0f1baae323 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -593,7 +593,7 @@ def __init__(self, environment: 'Environment', kwargs: JNISystemDependencyKW): self.java_home = environment.properties[self.for_machine].get_java_home() if not self.java_home: - self.java_home = pathlib.Path(shutil.which(self.javac.exelist[0])).resolve().parents[1] + self.java_home = pathlib.Path(shutil.which(self.javac.get_exe())).resolve().parents[1] if m.is_darwin(): problem_java_prefix = pathlib.Path('/System/Library/Frameworks/JavaVM.framework/Versions') if problem_java_prefix in self.java_home.parents: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a17c76c5f55b..24c63bc1a5f3 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -798,13 +798,12 @@ def run_command_impl(self, if not cmd.found(): raise InterpreterException(f'command {cmd.get_name()!r} not found or not executable') elif isinstance(cmd, compilers.Compiler): - exelist = cmd.get_exelist() - cmd = exelist[0] + expanded_args = cmd.get_exe_args() + cmd = cmd.get_exe() prog = ExternalProgram(cmd, silent=True) if not prog.found(): raise InterpreterException(f'Program {cmd!r} not found or not executable') cmd = prog - expanded_args = exelist[1:] else: if isinstance(cmd, mesonlib.File): cmd = cmd.absolute_path(srcdir, builddir) @@ -823,7 +822,7 @@ def run_command_impl(self, expanded_args.append(a.get_path()) elif isinstance(a, compilers.Compiler): FeatureNew.single_use('Compiler object as a variadic argument to `run_command`', '0.61.0', self.subproject, location=self.current_node) - prog = ExternalProgram(a.exelist[0], silent=True) + prog = ExternalProgram(a.get_exe(), silent=True) if not prog.found(): raise InterpreterException(f'Program {cmd!r} not found or not executable') expanded_args.append(prog.get_path()) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 45eb6e8c3c85..58753e63b56b 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -31,6 +31,9 @@ def __init__(self, exelist: T.List[str]): def get_id(self) -> str: return self.id + def get_exe(self) -> str: + return self.exelist[0] + def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs: return CompilerArgs(self, args) @@ -152,6 +155,9 @@ def __repr__(self) -> str: def get_id(self) -> str: return self.id + def get_exe(self) -> str: + return self.exelist[0] + def get_version_string(self) -> str: return f'({self.id} {self.version})' From b13cd1051685d835179ad1396cbbeaba1fec4f96 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 12:10:37 +0100 Subject: [PATCH 407/624] build, interpreter: associate a doctest target to a BuildTarget A doctest target is a separate build target (with its own linker arguments, including dependencies) that is built and added as a unit test whenever the parent target is built. The doctest's target is not accessible via ninja. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 3 +- mesonbuild/interpreter/interpreter.py | 32 +++++++++++--------- mesonbuild/interpreter/interpreterobjects.py | 5 +++ mesonbuild/modules/rust.py | 3 +- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6e233a382de0..2302f17e413b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -43,7 +43,7 @@ from .backend.backends import Backend from .compilers import Compiler from .interpreter.interpreter import SourceOutputs, Interpreter - from .interpreter.interpreterobjects import Test + from .interpreter.interpreterobjects import Test, Doctest from .interpreterbase import SubProject from .linkers.linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString @@ -730,6 +730,7 @@ def __init__( self.name_prefix_set = False self.name_suffix_set = False self.filename = 'no_name' + self.doctests: T.Optional[Doctest] = None # The debugging information file this target will generate self.debug_filename = None # The list of all files outputted by this target. Useful in cases such diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 24c63bc1a5f3..3c542cc4e538 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -130,6 +130,7 @@ ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str] + TestClass = T.TypeVar('TestClass', bound=Test) def _project_version_validator(value: T.Union[T.List, str, mesonlib.File, None]) -> T.Optional[str]: if isinstance(value, list): @@ -2212,7 +2213,8 @@ def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariables, T.Dict[str, 'TY def make_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], - kwargs: 'kwtypes.BaseTest') -> Test: + kwargs: 'kwtypes.BaseTest', + klass: T.Type[TestClass] = Test) -> TestClass: name = args[0] if ':' in name: mlog.deprecation(f'":" is not allowed in test name "{name}", it has been replaced with "_"', @@ -2242,20 +2244,20 @@ def make_test(self, node: mparser.BaseNode, s = ':' + s suite.append(prj.replace(' ', '_').replace(':', '_') + s) - return Test(name, - prj, - suite, - exe, - kwargs['depends'], - kwargs.get('is_parallel', False), - kwargs['args'], - env, - kwargs['should_fail'], - kwargs['timeout'], - kwargs['workdir'], - kwargs['protocol'], - kwargs['priority'], - kwargs['verbose']) + return klass(name, + prj, + suite, + exe, + kwargs['depends'], + kwargs.get('is_parallel', False), + kwargs['args'], + env, + kwargs['should_fail'], + kwargs['timeout'], + kwargs['workdir'], + kwargs['protocol'], + kwargs['priority'], + kwargs['verbose']) def add_test(self, node: mparser.BaseNode, args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]], diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 2df5aafc9a98..a2fadbefc2f2 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -809,6 +809,11 @@ def get_exe(self) -> T.Union[ExternalProgram, build.Executable, build.CustomTarg def get_name(self) -> str: return self.name + +class Doctest(Test): + target: T.Optional[build.BuildTarget] = None + + class NullSubprojectInterpreter(HoldableObject): pass diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index c455ea227996..873bc8929f12 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -29,6 +29,7 @@ from ..interpreter import Interpreter from ..interpreter import kwargs as _kwargs from ..interpreter.interpreter import SourceInputs, SourceOutputs + from ..interpreter.interpreterobjects import Test from ..programs import OverrideProgram from ..interpreter.type_checking import SourcesVarargsType @@ -188,7 +189,7 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func new_target_kwargs ) - test = self.interpreter.make_test( + test: Test = self.interpreter.make_test( self.interpreter.current_node, (name, new_target), tkwargs) return ModuleReturnValue(None, [new_target, test]) From 54118b6fbf5a508ee57d940d672ad2654a1cdf2c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 14:42:29 +0100 Subject: [PATCH 408/624] ninjabackend: generate command line for rust doctests Adjust get_rust_compiler_args() to accept the crate-type externally, because rustdoc tests are an executable but are compiled with the parent target's --crate-type. Apart from that, the rustdoc arguments are very similar to the parent target, and are handled by the same functions that were split out of generate_rust_target. This concludes the backend implementation of doctests, only leaving the implementation of a doctest() function in the rust module. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 18 ++++++++++++++---- mesonbuild/compilers/rust.py | 11 ++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a3bc89c4952b..2faf8c1d328a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -45,6 +45,7 @@ from ..linkers.linkers import DynamicLinker, StaticLinker from ..compilers.cs import CsCompiler from ..compilers.fortran import FortranCompiler + from ..compilers.rust import RustCompiler from ..mesonlib import FileOrString from .backends import TargetIntrospectionData @@ -1980,13 +1981,13 @@ def generate_rust_sources(self, target: build.BuildTarget) -> T.Tuple[T.List[str return orderdeps, main_rust_file - def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, + def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, src_crate_type: str, depfile: T.Optional[str] = None) -> T.List[str]: # Compiler args for compiling this target args = compilers.get_base_compile_args(target, rustc, self.environment) target_name = self.get_target_filename(target) - args.extend(['--crate-type', target.rust_crate_type]) + args.extend(['--crate-type', src_crate_type]) # If we're dynamically linking, add those arguments if target.rust_crate_type in {'bin', 'dylib'}: @@ -2122,7 +2123,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): return deps, project_deps, args def generate_rust_target(self, target: build.BuildTarget) -> None: - rustc = target.compilers['rust'] + rustc = T.cast('RustCompiler', target.compilers['rust']) self.generate_generator_list_rules(target) for i in target.get_sources(): @@ -2141,7 +2142,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: 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, depfile) + args += self.get_rust_compiler_args(target, rustc, target.rust_crate_type, depfile) deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) args += deps_args @@ -2171,6 +2172,15 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: self.generate_shsym(target) self.create_target_source_introspection(target, rustc, args, [main_rust_file], []) + if target.doctests: + assert target.doctests.target is not None + rustdoc = rustc.get_rustdoc(self.environment) + 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) + args += deps_args + target.doctests.cmd_args = args.to_native() + [main_rust_file] + target.doctests.cmd_args + @staticmethod def get_rule_suffix(for_machine: MachineChoice) -> str: return PerMachine('_FOR_BUILD', '')[for_machine] diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 17908860e56f..249ae1fc77e5 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -107,7 +107,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic 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, environment: Environment) -> None: source_name = os.path.join(work_dir, 'sanity.rs') output_name = os.path.join(work_dir, 'rusttest') cmdlist = self.exelist.copy() @@ -333,6 +333,15 @@ def has_multi_link_arguments(self, args: T.List[str], env: Environment) -> T.Tup args = self.linker.fatal_warnings() + args return self.compiles('fn main { std::process::exit(0) };\n', env, 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) + if not exelist: + return None + + return RustdocTestCompiler(exelist, self.version, self.for_machine, + self.is_cross, self.info, linker=self.linker) + class ClippyRustCompiler(RustCompiler): """Clippy is a linter that wraps Rustc. From 7b166c0a41615f1e3e51d6fbb132959ff0d77536 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Nov 2024 10:05:16 +0100 Subject: [PATCH 409/624] interpreter, rust: move "args" out of BaseTest rust.doctest() will have to typecheck that to a list of strings, no other argument types are allowed. Extract the field out of BaseTest, placing it in FuncBenchmark and the rust modules's FuncTest. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/kwargs.py | 5 ++++- mesonbuild/interpreter/type_checking.py | 9 ++++++--- mesonbuild/modules/rust.py | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 61cff9aea350..c0b74405f6f1 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -19,6 +19,9 @@ from ..programs import ExternalProgram from .type_checking import PkgConfigDefineType, SourcesVarargsType +if T.TYPE_CHECKING: + TestArgs = T.Union[str, File, build.Target, ExternalProgram] + class FuncAddProjectArgs(TypedDict): """Keyword Arguments for the add_*_arguments family of arguments. @@ -38,7 +41,6 @@ class BaseTest(TypedDict): """Shared base for the Rust module.""" - args: T.List[T.Union[str, File, build.Target, ExternalProgram]] should_fail: bool timeout: int workdir: T.Optional[str] @@ -52,6 +54,7 @@ class FuncBenchmark(BaseTest): """Keyword Arguments shared between `test` and `benchmark`.""" + args: T.List[TestArgs] protocol: Literal['exitcode', 'tap', 'gtest', 'rust'] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 6519258b997d..1c9953ee4153 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -472,9 +472,7 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus PRESERVE_PATH_KW: KwargInfo[bool] = KwargInfo('preserve_path', bool, default=False, since='0.63.0') -TEST_KWS: T.List[KwargInfo] = [ - KwargInfo('args', ContainerTypeInfo(list, (str, File, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram)), - listify=True, default=[]), +TEST_KWS_NO_ARGS: T.List[KwargInfo] = [ KwargInfo('should_fail', bool, default=False), KwargInfo('timeout', int, default=30), KwargInfo('workdir', (str, NoneType), default=None, @@ -491,6 +489,11 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus KwargInfo('verbose', bool, default=False, since='0.62.0'), ] +TEST_KWS: T.List[KwargInfo] = TEST_KWS_NO_ARGS + [ + KwargInfo('args', ContainerTypeInfo(list, (str, File, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram)), + listify=True, default=[]), +] + # Cannot have a default value because we need to check that rust_crate_type and # rust_abi are mutually exclusive. RUST_CRATE_TYPE_KW: KwargInfo[T.Union[str, None]] = KwargInfo( diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 873bc8929f12..70b206032390 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -37,6 +37,7 @@ class FuncTest(_kwargs.BaseTest): + args: T.List[_kwargs.TestArgs] dependencies: T.List[T.Union[Dependency, ExternalLibrary]] is_parallel: bool link_with: T.List[LibTypes] From d9af002fa2531e25f1ba81f20eda42c2659d49ea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Mar 2025 10:54:31 +0100 Subject: [PATCH 410/624] compilers: rust: fix derivation of RustdocTestCompiler Pass down the full_version, otherwise assigning "self.is_beta" fails. Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/rust.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 249ae1fc77e5..bfc83a57a00d 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -340,7 +340,8 @@ def get_rustdoc(self, env: 'Environment') -> T.Optional[RustdocTestCompiler]: return None return RustdocTestCompiler(exelist, self.version, self.for_machine, - self.is_cross, self.info, linker=self.linker) + self.is_cross, self.info, full_version=self.full_version, + linker=self.linker) class ClippyRustCompiler(RustCompiler): From efcef42f9d1b94e332827854299125be35038466 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 23:01:25 +0100 Subject: [PATCH 411/624] rust: unit tests: do not use deprecated rust_crate_type Signed-off-by: Paolo Bonzini --- test cases/rust/9 unit tests/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build index aa9da679693e..4d04ee892213 100644 --- a/test cases/rust/9 unit tests/meson.build +++ b/test cases/rust/9 unit tests/meson.build @@ -49,7 +49,7 @@ exe = executable('rust_exe', ['test2.rs', 'test.rs'], build_by_default : false) rust = import('rust') rust.test('rust_test_from_exe', exe, should_fail : true) -lib = static_library('rust_static', ['test.rs'], build_by_default : false, rust_crate_type : 'lib') +lib = static_library('rust_static', ['test.rs'], build_by_default : false, rust_abi: 'c') rust.test('rust_test_from_static', lib, args: ['--skip', 'test_add_intentional_fail']) lib = shared_library('rust_shared', ['test.rs'], build_by_default : false) From 00dc6fa4dfee880a3c3c17f658e6d0b98a93bab6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Nov 2024 14:33:15 +0100 Subject: [PATCH 412/624] rust: extract common parts of rust.test and rust.doctest Signed-off-by: Paolo Bonzini --- mesonbuild/modules/rust.py | 54 +++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 70b206032390..7e599505dac0 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -35,14 +35,17 @@ from typing_extensions import TypedDict, Literal - class FuncTest(_kwargs.BaseTest): + ArgsType = T.TypeVar('ArgsType') - args: T.List[_kwargs.TestArgs] + class FuncRustTest(_kwargs.BaseTest, T.Generic[ArgsType]): + args: T.List[ArgsType] dependencies: T.List[T.Union[Dependency, ExternalLibrary]] is_parallel: bool link_with: T.List[LibTypes] rust_args: T.List[str] + FuncTest = FuncRustTest[_kwargs.TestArgs] + class FuncBindgen(TypedDict): args: T.List[str] @@ -56,6 +59,18 @@ class FuncBindgen(TypedDict): bindgen_version: T.List[str] +RUST_TEST_KWS: T.List[KwargInfo] = [ + KwargInfo( + 'rust_args', + ContainerTypeInfo(list, str), + listify=True, + default=[], + since='1.2.0', + ), + KwargInfo('is_parallel', bool, default=False), +] + + class RustModule(ExtensionModule): """A module that holds helper functions for rust.""" @@ -78,22 +93,7 @@ def __init__(self, interpreter: Interpreter) -> None: 'proc_macro': self.proc_macro, }) - @typed_pos_args('rust.test', str, BuildTarget) - @typed_kwargs( - 'rust.test', - *TEST_KWS, - DEPENDENCIES_KW, - LINK_WITH_KW.evolve(since='1.2.0'), - KwargInfo( - 'rust_args', - ContainerTypeInfo(list, str), - listify=True, - default=[], - since='1.2.0', - ), - KwargInfo('is_parallel', bool, default=False), - ) - def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: FuncTest) -> ModuleReturnValue: + def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: FuncRustTest) -> T.Tuple[Executable, _kwargs.FuncTest]: """Generate a rust test target from a given rust target. Rust puts its unitests inside its main source files, unlike most @@ -141,15 +141,15 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func name = args[0] base_target: BuildTarget = args[1] if not base_target.uses_rust(): - raise InterpreterException('Second positional argument to rustmod.test() must be a rust based target') + raise InterpreterException(f'Second positional argument to rustmod.{funcname}() must be a rust based target') extra_args = kwargs['args'] # Delete any arguments we don't want passed if '--test' in extra_args: - mlog.warning('Do not add --test to rustmod.test arguments') + mlog.warning(f'Do not add --test to rustmod.{funcname}() arguments') extra_args.remove('--test') if '--format' in extra_args: - mlog.warning('Do not add --format to rustmod.test arguments') + mlog.warning(f'Do not add --format to rustmod.{funcname}() arguments') i = extra_args.index('--format') # Also delete the argument to --format del extra_args[i + 1] @@ -189,7 +189,19 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func base_target.objects, base_target.environment, base_target.compilers, new_target_kwargs ) + return new_target, tkwargs + @typed_pos_args('rust.test', str, BuildTarget) + @typed_kwargs( + 'rust.test', + *TEST_KWS, + DEPENDENCIES_KW, + LINK_WITH_KW.evolve(since='1.2.0'), + *RUST_TEST_KWS, + ) + def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: FuncTest) -> ModuleReturnValue: + name, _ = args + new_target, tkwargs = self.test_common('test', state, args, kwargs) test: Test = self.interpreter.make_test( self.interpreter.current_node, (name, new_target), tkwargs) From bf8d4927238a40dc0dca584e91988f456a970bbb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 16:42:09 +0100 Subject: [PATCH 413/624] rust: add rust.doctest Signed-off-by: Paolo Bonzini --- docs/markdown/Rust-module.md | 27 ++++++++ mesonbuild/modules/rust.py | 79 ++++++++++++++++++++++-- test cases/rust/9 unit tests/doctest1.rs | 2 +- test cases/rust/9 unit tests/meson.build | 13 ++-- 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index ee095e9d6da1..6e8202a6fe16 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -24,6 +24,8 @@ like Meson, rather than Meson work more like rust. rustmod.test(name, target, ...) ``` +*Since 1.8.0* + This function creates a new rust unittest target from an existing rust based target, which may be a library or executable. It does this by copying the sources and arguments passed to the original target and @@ -41,6 +43,31 @@ It also takes the following keyword arguments: This function also accepts all of the keyword arguments accepted by the [[test]] function except `protocol`, it will set that automatically. +### doctest() + +```meson +rustmod.doctest(name, target, ...) +``` + +This function creates a new `test()` target from an existing rust +based library target. The test will use `rustdoc` to extract and run +the doctests that are included in `target`'s sources. + +This function takes two positional arguments, the first is the name of the +test and the second is the library or executable that is the rust based target. +It also takes the following keyword arguments: + +- `dependencies`: a list of test-only Dependencies +- `link_with`: a list of additional build Targets to link with +- `rust_args`: a list of extra arguments passed to the Rust compiler + +The target is linked automatically into the doctests. + +This function also accepts all of the keyword arguments accepted by the +[[test]] function except `protocol`, it will set that automatically. +However, arguments are limited to strings that do not contain spaces +due to limitations of `rustdoc`. + ### bindgen() This function wraps bindgen to simplify creating rust bindings around C diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 7e599505dac0..fdb18261ec24 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -4,6 +4,7 @@ from __future__ import annotations import itertools import os +import re import typing as T from mesonbuild.interpreterbase.decorators import FeatureNew @@ -11,15 +12,16 @@ from . import ExtensionModule, ModuleReturnValue, ModuleInfo from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, - CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary) + CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( - DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, OUTPUT_KW, + DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noPosargs, permittedKwargs -from ..mesonlib import File -from ..programs import ExternalProgram +from ..interpreter.interpreterobjects import Doctest +from ..mesonlib import File, MesonException, PerMachine +from ..programs import ExternalProgram, NonExistingExternalProgram if T.TYPE_CHECKING: from . import ModuleState @@ -45,6 +47,7 @@ class FuncRustTest(_kwargs.BaseTest, T.Generic[ArgsType]): rust_args: T.List[str] FuncTest = FuncRustTest[_kwargs.TestArgs] + FuncDoctest = FuncRustTest[str] class FuncBindgen(TypedDict): @@ -70,6 +73,11 @@ class FuncBindgen(TypedDict): KwargInfo('is_parallel', bool, default=False), ] +def no_spaces_validator(arg: T.Optional[T.Union[str, T.List]]) -> T.Optional[str]: + if any(bool(re.search(r'\s', x)) for x in arg): + return 'must not contain spaces due to limitations of rustdoc' + return None + class RustModule(ExtensionModule): @@ -77,6 +85,7 @@ class RustModule(ExtensionModule): INFO = ModuleInfo('rust', '0.57.0', stabilized='1.0.0') _bindgen_rust_target: T.Optional[str] + rustdoc: PerMachine[T.Optional[ExternalProgram]] = PerMachine(None, None) def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) @@ -89,6 +98,7 @@ def __init__(self, interpreter: Interpreter) -> None: self._bindgen_set_std = False self.methods.update({ 'test': self.test, + 'doctest': self.doctest, 'bindgen': self.bindgen, 'proc_macro': self.proc_macro, }) @@ -207,6 +217,67 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func return ModuleReturnValue(None, [new_target, test]) + @FeatureNew('rust.doctest', '1.8.0') + @typed_pos_args('rust.doctest', str, BuildTarget) + @typed_kwargs( + 'rust.doctest', + *TEST_KWS_NO_ARGS, + DEPENDENCIES_KW, + LINK_WITH_KW, + *RUST_TEST_KWS, + KwargInfo( + 'args', + ContainerTypeInfo(list, str), + listify=True, + default=[], + validator=no_spaces_validator, + ), + ) + def doctest(self, state: ModuleState, args: T.Tuple[str, T.Union[SharedLibrary, StaticLibrary]], kwargs: FuncDoctest) -> ModuleReturnValue: + name, base_target = args + + # Link the base target's crate into the tests + kwargs['link_with'].append(base_target) + kwargs['depends'].append(base_target) + workdir = kwargs['workdir'] + kwargs['workdir'] = None + new_target, tkwargs = self.test_common('doctest', state, args, kwargs) + + # added automatically by rustdoc; keep things simple + tkwargs['args'].remove('--test') + + # --test-args= is "parsed" simply via the Rust function split_whitespace(). + # This means no quoting nightmares (pfew) but it also means no spaces. + # Unfortunately it's pretty hard at this point to accept e.g. CustomTarget, + # because their paths may not be known. This is not a big deal because the + # user does not control the test harness, so make things easy and allow + # strings only. + if tkwargs['args']: + tkwargs['args'] = ['--test-args=' + ' '.join(T.cast('T.Sequence[str]', tkwargs['args']))] + if workdir: + tkwargs['args'].append('--test-run-directory=' + workdir) + + if self.rustdoc[base_target.for_machine] is None: + rustc = T.cast('RustCompiler', base_target.compilers['rust']) + rustdoc = rustc.get_rustdoc(state.environment) + if rustdoc: + self.rustdoc[base_target.for_machine] = ExternalProgram(rustdoc.get_exe()) + else: + self.rustdoc[base_target.for_machine] = NonExistingExternalProgram() + + rustdoc_prog = self.rustdoc[base_target.for_machine] + if not rustdoc_prog.found(): + raise MesonException(f'could not find rustdoc for {base_target.for_machine} machine') + + doctests: Doctest = self.interpreter.make_test( + self.interpreter.current_node, (name, rustdoc_prog), tkwargs, Doctest) + + # Note that the new_target is intentionally not returned, as it + # is only reached via the base_target and never built by "ninja" + doctests.target = new_target + base_target.doctests = doctests + return ModuleReturnValue(None, [doctests]) + @noPosargs @typed_kwargs( 'rust.bindgen', diff --git a/test cases/rust/9 unit tests/doctest1.rs b/test cases/rust/9 unit tests/doctest1.rs index d270f7d67047..da42792b8be0 100644 --- a/test cases/rust/9 unit tests/doctest1.rs +++ b/test cases/rust/9 unit tests/doctest1.rs @@ -7,6 +7,6 @@ /// ```ignore /// this one will be skipped /// ``` -fn my_func() +pub fn my_func() { } diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build index 4d04ee892213..0fa2fa80b304 100644 --- a/test cases/rust/9 unit tests/meson.build +++ b/test cases/rust/9 unit tests/meson.build @@ -1,4 +1,4 @@ -project('rust unit tests', 'rust', meson_version: '>=1.2.0') +project('rust unit tests', 'rust', meson_version: '>=1.8.0') t = executable( 'rust_test', @@ -31,14 +31,12 @@ test( suite : ['foo'], ) +rust = import('rust') + rustdoc = find_program('rustdoc', required: false) if rustdoc.found() - # rustdoc is invoked mostly like rustc. This is a simple example - # where it is easy enough to invoke it by hand. - test( - 'rust doctest', - rustdoc, - args : ['--test', '--crate-name', 'doctest1', '--crate-type', 'lib', files('doctest1.rs')], + doclib = static_library('rust_doc_lib', ['doctest1.rs'], build_by_default : false) + rust.doctest('rust doctests', doclib, protocol : 'rust', suite : ['doctests'], ) @@ -46,7 +44,6 @@ endif exe = executable('rust_exe', ['test2.rs', 'test.rs'], build_by_default : false) -rust = import('rust') rust.test('rust_test_from_exe', exe, should_fail : true) lib = static_library('rust_static', ['test.rs'], build_by_default : false, rust_abi: 'c') From 0c8f6400e3f731a5c5091653c0940f6318bdce23 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 23:38:31 +0100 Subject: [PATCH 414/624] rust: add link_whole to rust.test and rust.doctest QEMU needs it in its integration tests (in order to run global constructors), and therefore in rust.doctest too. With this change I could do: # Rust executables do not support objects, so add an intermediate step. rust_qemu_api_objs = static_library( 'rust_qemu_api_objs', objects: [libqom.extract_all_objects(recursive: false), libhwcore.extract_all_objects(recursive: false)]) rust.doctest('rust-qemu-api-doc', _qemu_api_rs, dependencies: [qemu_api, qemu_api_macros], link_with: libqemuutil, link_whole: [rust_qemu_api_objs], suite: ['doc', 'rust']) followed by "meson test --suite doc". For completeness, add it to rust.test as well. Signed-off-by: Paolo Bonzini --- docs/markdown/Rust-module.md | 2 ++ docs/markdown/snippets/rust-test-link-whole.md | 4 ++++ mesonbuild/modules/rust.py | 10 ++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/rust-test-link-whole.md diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 6e8202a6fe16..d2c478c64239 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -38,6 +38,7 @@ It also takes the following keyword arguments: - `dependencies`: a list of test-only Dependencies - `link_with`: a list of additional build Targets to link with (*since 1.2.0*) +- `link_whole`: a list of additional build Targets to link with in their entirety (*since 1.8.0*) - `rust_args`: a list of extra arguments passed to the Rust compiler (*since 1.2.0*) This function also accepts all of the keyword arguments accepted by the @@ -59,6 +60,7 @@ It also takes the following keyword arguments: - `dependencies`: a list of test-only Dependencies - `link_with`: a list of additional build Targets to link with +- `link_whole`: a list of additional build Targets to link with in their entirety - `rust_args`: a list of extra arguments passed to the Rust compiler The target is linked automatically into the doctests. diff --git a/docs/markdown/snippets/rust-test-link-whole.md b/docs/markdown/snippets/rust-test-link-whole.md new file mode 100644 index 000000000000..f3d006d5389d --- /dev/null +++ b/docs/markdown/snippets/rust-test-link-whole.md @@ -0,0 +1,4 @@ +## `rust.test` now supports `link_whole` + +The `test` function in the `rust` module now supports the `link_whole` +keyword argument in addition to `link_with` and `dependencies`. diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index fdb18261ec24..f43a0ede965a 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -15,8 +15,8 @@ CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes from ..interpreter.type_checking import ( - DEPENDENCIES_KW, LINK_WITH_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, OUTPUT_KW, - INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator + DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, + OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noPosargs, permittedKwargs from ..interpreter.interpreterobjects import Doctest @@ -44,6 +44,7 @@ class FuncRustTest(_kwargs.BaseTest, T.Generic[ArgsType]): dependencies: T.List[T.Union[Dependency, ExternalLibrary]] is_parallel: bool link_with: T.List[LibTypes] + link_whole: T.List[LibTypes] rust_args: T.List[str] FuncTest = FuncRustTest[_kwargs.TestArgs] @@ -147,6 +148,8 @@ def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, Buil """ if any(isinstance(t, Jar) for t in kwargs.get('link_with', [])): raise InvalidArguments('Rust tests cannot link with Jar targets') + if any(isinstance(t, Jar) for t in kwargs.get('link_whole', [])): + raise InvalidArguments('Rust tests cannot link with Jar targets') name = args[0] base_target: BuildTarget = args[1] @@ -181,6 +184,7 @@ def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, Buil 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_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: @@ -207,6 +211,7 @@ def test_common(self, funcname: str, state: ModuleState, args: T.Tuple[str, Buil *TEST_KWS, DEPENDENCIES_KW, LINK_WITH_KW.evolve(since='1.2.0'), + LINK_WHOLE_KW.evolve(since='1.8.0'), *RUST_TEST_KWS, ) def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: FuncTest) -> ModuleReturnValue: @@ -224,6 +229,7 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func *TEST_KWS_NO_ARGS, DEPENDENCIES_KW, LINK_WITH_KW, + LINK_WHOLE_KW, *RUST_TEST_KWS, KwargInfo( 'args', From 31114260e575af75d5e8d01c2852092704ef8f24 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 4 Apr 2022 09:20:13 -0400 Subject: [PATCH 415/624] wayland: Mark module stable There is no point in printing warning about unstable module, in the worst case we can just deprecate and add new API. It has been tested in a few projects already, and this warning is a blocker on wider adoption. --- docs/markdown/Wayland-module.md | 11 ++++------- mesonbuild/modules/wayland.py | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/markdown/Wayland-module.md b/docs/markdown/Wayland-module.md index 3ffa58739415..ca7b567178f0 100644 --- a/docs/markdown/Wayland-module.md +++ b/docs/markdown/Wayland-module.md @@ -1,21 +1,18 @@ -# Unstable Wayland Module +# Wayland Module -This module is available since version 0.62.0. +This module is available since version 0.62.0, and has been stable since version +1.8.0. This module provides helper functions to find wayland protocol xmls and to generate .c and .h files using wayland-scanner -**Note**: this module is unstable. It is only provided as a technology -preview. Its API may change in arbitrary ways between releases or it -might be removed from Meson altogether. - ## Quick Usage ```meson project('hello-wayland', 'c') wl_dep = dependency('wayland-client') -wl_mod = import('unstable-wayland') +wl_mod = import('wayland') xml = wl_mod.find_protocol('xdg-shell') xdg_shell = wl_mod.scan_xml(xml) diff --git a/mesonbuild/modules/wayland.py b/mesonbuild/modules/wayland.py index e17cf995ced3..94c6f819db5e 100644 --- a/mesonbuild/modules/wayland.py +++ b/mesonbuild/modules/wayland.py @@ -35,7 +35,7 @@ class FindProtocol(TypedDict): class WaylandModule(ExtensionModule): - INFO = ModuleInfo('wayland', '0.62.0', unstable=True) + INFO = ModuleInfo('wayland', '0.62.0', stabilized='1.8.0') def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) From 8e92f1ee872081417d470e75a32edbadda8972b6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Mar 2025 20:35:36 +0100 Subject: [PATCH 416/624] environment: remove incorrect check for subproject options Because this check is done on the actual key, it will fail even if "subproject:project options" is used. The correct test is already performed in mfilestr2key. Fixes: #14373 --- mesonbuild/environment.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 5640b9de3196..4999be1ec516 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -759,8 +759,6 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme for strk, v in values.items(): # Project options are always for the host machine key = self.mfilestr2key(strk, section_subproject, machine) - if key.subproject: - raise MesonException('Do not set subproject options in [built-in options] section, use [subproject:built-in options] instead.') self.options[key] = v def _set_default_options_from_env(self) -> None: From babd42300e9e05d8e7e1ae0bec05907d143f4f40 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Mar 2025 20:39:16 +0100 Subject: [PATCH 417/624] use correct section when suggesting the placement of subproject options Signed-off-by: Paolo Bonzini --- mesonbuild/environment.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 4999be1ec516..9d0677446ac8 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -702,11 +702,12 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # Store a global state of Cargo dependencies self.cargo: T.Optional[cargo.Interpreter] = None - def mfilestr2key(self, machine_file_string: str, section_subproject: T.Optional[str], machine: MachineChoice) -> OptionKey: + 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: - raise MesonException('Do not set subproject options in [built-in options] section, use [subproject:built-in options] instead.') + 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: @@ -724,7 +725,7 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme if paths: mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.') for strk, v in paths.items(): - k = self.mfilestr2key(strk, None, machine) + k = self.mfilestr2key(strk, 'paths', None, machine) self.options[k] = v # Next look for compiler options in the "properties" section, this is @@ -737,7 +738,7 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme for strk, v in properties.properties.copy().items(): if strk in deprecated_properties: mlog.deprecation(f'{strk} in the [properties] section of the machine file is deprecated, use the [built-in options] section.') - k = self.mfilestr2key(strk, None, machine) + k = self.mfilestr2key(strk, 'properties', None, machine) self.options[k] = v del properties.properties[strk] @@ -748,7 +749,7 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme section_subproject = '' if section == 'built-in options': for strk, v in values.items(): - key = self.mfilestr2key(strk, section_subproject, machine) + key = self.mfilestr2key(strk, section, section_subproject, machine) # If we're in the cross file, and there is a `build.foo` warn about that. Later we'll remove it. if machine is MachineChoice.HOST and key.machine is not machine: mlog.deprecation('Setting build machine options in cross files, please use a native file instead, this will be removed in meson 2.0', once=True) @@ -758,7 +759,7 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme # to read these from the native file for strk, v in values.items(): # Project options are always for the host machine - key = self.mfilestr2key(strk, section_subproject, machine) + key = self.mfilestr2key(strk, section, section_subproject, machine) self.options[key] = v def _set_default_options_from_env(self) -> None: From 5c3bde3eb817d1d8ca41ef97969072d92242d515 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 13:42:51 +0200 Subject: [PATCH 418/624] add testcase for subproject options in machine file --- test cases/common/282 subproj options/crossfile.ini | 2 ++ test cases/common/282 subproj options/meson.build | 3 +++ .../common/282 subproj options/subprojects/sub/meson.build | 2 ++ .../282 subproj options/subprojects/sub/meson_options.txt | 1 + 4 files changed, 8 insertions(+) create mode 100644 test cases/common/282 subproj options/crossfile.ini create mode 100644 test cases/common/282 subproj options/meson.build create mode 100644 test cases/common/282 subproj options/subprojects/sub/meson.build create mode 100644 test cases/common/282 subproj options/subprojects/sub/meson_options.txt diff --git a/test cases/common/282 subproj options/crossfile.ini b/test cases/common/282 subproj options/crossfile.ini new file mode 100644 index 000000000000..01b45a650d1c --- /dev/null +++ b/test cases/common/282 subproj options/crossfile.ini @@ -0,0 +1,2 @@ +[sub:project options] +bar = true diff --git a/test cases/common/282 subproj options/meson.build b/test cases/common/282 subproj options/meson.build new file mode 100644 index 000000000000..b4cf89fa0b6d --- /dev/null +++ b/test cases/common/282 subproj options/meson.build @@ -0,0 +1,3 @@ +project('pkg_opt_test') + +subproject('sub') diff --git a/test cases/common/282 subproj options/subprojects/sub/meson.build b/test cases/common/282 subproj options/subprojects/sub/meson.build new file mode 100644 index 000000000000..9e3bceacfb8d --- /dev/null +++ b/test cases/common/282 subproj options/subprojects/sub/meson.build @@ -0,0 +1,2 @@ +project('subproject') +assert(get_option('bar') == true) diff --git a/test cases/common/282 subproj options/subprojects/sub/meson_options.txt b/test cases/common/282 subproj options/subprojects/sub/meson_options.txt new file mode 100644 index 000000000000..129a7d4a07a4 --- /dev/null +++ b/test cases/common/282 subproj options/subprojects/sub/meson_options.txt @@ -0,0 +1 @@ +option('bar', type: 'boolean', value: false) From 1f4bb37372e39003e1aa0e3ec5739fb04f495c4f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 1 Apr 2025 10:09:03 -0700 Subject: [PATCH 419/624] modules/cmake: Make fully type safe Mostly this was just adding a few asserts for options, and one bug fix from the option refactor --- mesonbuild/modules/cmake.py | 23 ++++++++++++++++------- run_mypy.py | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 1802584cb5b7..e3154b05e135 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -156,13 +156,17 @@ def dependency(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, @typed_pos_args('cmake.subproject.include_directories', str) def include_directories(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> build.IncludeDirs: info = self._args_to_info(args[0]) - return self.get_variable(state, [info['inc']], kwargs) + inc = self.get_variable(state, [info['inc']], kwargs) + assert isinstance(inc, build.IncludeDirs), 'for mypy' + return inc @noKwargs @typed_pos_args('cmake.subproject.target', str) def target(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> build.Target: info = self._args_to_info(args[0]) - return self.get_variable(state, [info['tgt']], kwargs) + tgt = self.get_variable(state, [info['tgt']], kwargs) + assert isinstance(tgt, build.Target), 'for mypy' + return tgt @noKwargs @typed_pos_args('cmake.subproject.target_type', str) @@ -305,7 +309,9 @@ def write_basic_package_version_file(self, state: ModuleState, args: TYPE_var, k pkgroot = pkgroot_name = kwargs['install_dir'] if pkgroot is None: - pkgroot = os.path.join(state.environment.coredata.optstore.get_value_for(OptionKey('libdir')), 'cmake', name) + libdir = state.environment.coredata.optstore.get_value_for(OptionKey('libdir')) + assert isinstance(libdir, str), 'for mypy' + pkgroot = os.path.join(libdir, 'cmake', name) pkgroot_name = os.path.join('{libdir}', 'cmake', name) template_file = os.path.join(self.cmake_root, 'Modules', f'BasicConfigVersion-{compatibility}.cmake.in') @@ -376,7 +382,9 @@ def configure_package_config_file(self, state: ModuleState, args: TYPE_var, kwar install_dir = kwargs['install_dir'] if install_dir is None: - install_dir = os.path.join(state.environment.coredata.optstore.get_value_for(OptionKey('libdir')), 'cmake', name) + libdir = state.environment.coredata.optstore.get_value_for(OptionKey('libdir')) + assert isinstance(libdir, str), 'for mypy' + install_dir = os.path.join(libdir, 'cmake', name) conf = kwargs['configuration'] if isinstance(conf, dict): @@ -384,6 +392,7 @@ def configure_package_config_file(self, state: ModuleState, args: TYPE_var, kwar conf = build.ConfigurationData(conf) prefix = state.environment.coredata.optstore.get_value_for(OptionKey('prefix')) + assert isinstance(prefix, str), 'for mypy' abs_install_dir = install_dir if not os.path.isabs(abs_install_dir): abs_install_dir = os.path.join(prefix, install_dir) @@ -429,7 +438,7 @@ def subproject(self, state: ModuleState, args: T.Tuple[str], kwargs_: Subproject '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') @@ -443,5 +452,5 @@ def subproject(self, state: ModuleState, args: T.Tuple[str], kwargs_: Subproject def subproject_options(self, state: ModuleState, args: TYPE_var, kwargs: TYPE_kwargs) -> CMakeSubprojectOptions: return CMakeSubprojectOptions() -def initialize(*args: T.Any, **kwargs: T.Any) -> CmakeModule: - return CmakeModule(*args, **kwargs) +def initialize(interp: Interpreter) -> CmakeModule: + return CmakeModule(interp) diff --git a/run_mypy.py b/run_mypy.py index afa5531cde24..975232a48624 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -57,6 +57,7 @@ 'mesonbuild/mlog.py', 'mesonbuild/msubprojects.py', 'mesonbuild/modules/__init__.py', + 'mesonbuild/modules/cmake.py', 'mesonbuild/modules/cuda.py', 'mesonbuild/modules/external_project.py', 'mesonbuild/modules/fs.py', From f9c9726ec70fe83a79ac07ee04f077285b666fef Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 11 Mar 2025 11:14:32 -0700 Subject: [PATCH 420/624] unittests: use more subtests --- unittests/optiontests.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 274a74a6e1f7..d02119a064c0 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -140,15 +140,17 @@ def test_augments(self): self.assertEqual(optstore.get_value_for(name, sub2_name), top_value) # First augment a subproject - optstore.set_from_configure_command([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) - - optstore.set_from_configure_command([], [f'{sub_name}:{name}']) - 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) + with self.subTest('set subproject override'): + optstore.set_from_configure_command([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}']) + 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}'], []) From 4001d0261933890a59242beb0a26477f4c452e33 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 15:27:24 -0800 Subject: [PATCH 421/624] options: move BASE_OPTIONS to the options module This makes more sense from a "group all options together" It also allows us to remove a bunch of imports in functions, a clear code smell. --- mesonbuild/compilers/__init__.py | 2 -- mesonbuild/compilers/compilers.py | 31 +------------------------------ mesonbuild/coredata.py | 22 +++++++++------------- mesonbuild/msetup.py | 3 +-- mesonbuild/options.py | 29 ++++++++++++++++++++++++++++- 5 files changed, 39 insertions(+), 48 deletions(-) diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 98ec13faea20..116b3fa7a43e 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -7,7 +7,6 @@ 'RunResult', 'all_languages', - 'BASE_OPTIONS', 'clib_langs', 'clink_langs', 'c_suffixes', @@ -48,7 +47,6 @@ Compiler, RunResult, all_languages, - BASE_OPTIONS, clib_langs, clink_langs, c_suffixes, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 9fbde0e49306..3398aa4afb3c 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -212,35 +212,6 @@ class CompileCheckMode(enum.Enum): } -MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd'] - -BASE_OPTIONS: T.Mapping[OptionKey, options.AnyOptionType] = { - OptionKey(o.name): o for o in T.cast('T.List[options.AnyOptionType]', [ - options.UserBooleanOption('b_pch', 'Use precompiled headers', True), - options.UserBooleanOption('b_lto', 'Use link time optimization', False), - options.UserIntegerOption('b_lto_threads', 'Use multiple threads for Link Time Optimization', 0), - options.UserComboOption('b_lto_mode', 'Select between different LTO modes.', 'default', choices=['default', 'thin']), - options.UserBooleanOption('b_thinlto_cache', 'Use LLVM ThinLTO caching for faster incremental builds', False), - options.UserStringOption('b_thinlto_cache_dir', 'Directory to store ThinLTO cache objects', ''), - options.UserStringArrayOption('b_sanitize', 'Code sanitizer to use', []), - options.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), - options.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), - options.UserComboOption( - 'b_pgo', 'Use profile guided optimization', 'off', choices=['off', 'generate', 'use']), - options.UserBooleanOption('b_coverage', 'Enable coverage tracking.', False), - options.UserComboOption( - 'b_colorout', 'Use colored output', 'always', choices=['auto', 'always', 'never']), - options.UserComboOption( - 'b_ndebug', 'Disable asserts', 'false', choices=['true', 'false', 'if-release']), - options.UserBooleanOption('b_staticpic', 'Build static libraries as position independent', True), - options.UserBooleanOption('b_pie', 'Build executables as position independent', False), - options.UserBooleanOption('b_bitcode', 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), - options.UserComboOption( - 'b_vscrt', 'VS run-time library type to use.', 'from_buildtype', - choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), - ]) -} - def option_enabled(boptions: T.Set[OptionKey], target: 'BuildTarget', env: 'Environment', @@ -1109,7 +1080,7 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: return [] def get_crt_val(self, crt_val: str, buildtype: str) -> str: - if crt_val in MSCRT_VALS: + if crt_val in options.MSCRT_VALS: return crt_val assert crt_val in {'from_buildtype', 'static_from_buildtype'} diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 7cc69a5a71cf..d2dc4bb2e40d 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -603,27 +603,25 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' return dirty def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None: - from .compilers import BASE_OPTIONS - # Main project can set default options on subprojects, but subprojects # can only set default options on themselves. # Preserve order: if env.options has 'buildtype' it must come after # 'optimization' if it is in default_options. - options: T.MutableMapping[OptionKey, T.Any] = OrderedDict() + options_: T.MutableMapping[OptionKey, T.Any] = OrderedDict() for k, v in default_options.items(): if isinstance(k, str): k = OptionKey.from_string(k) if not subproject or k.subproject == subproject: - options[k] = v - options.update(env.options) - env.options = options + options_[k] = v + options_.update(env.options) + env.options = options_ # Create a subset of options, keeping only project and builtin # options for this subproject. # Language and backend specific options will be set later when adding # languages and setting the backend (builtin options must be set first # to know which backend we'll use). - options = OrderedDict() + options_ = OrderedDict() for k, v in env.options.items(): if isinstance(k, str): @@ -642,12 +640,12 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # adding languages and setting backend. if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): continue - if self.optstore.is_base_option(k) and k.evolve(subproject=None) in BASE_OPTIONS: + if self.optstore.is_base_option(k) and k.evolve(subproject=None) in options.BASE_OPTIONS: # set_options will report unknown base options continue - options[k] = v + options_[k] = v - self.set_options(options, subproject=subproject, first_invocation=env.first_invocation) + self.set_options(options_, subproject=subproject, first_invocation=env.first_invocation) def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, env: Environment, subproject: str) -> None: @@ -680,8 +678,6 @@ def add_lang_args(self, lang: str, comp: T.Type['Compiler'], self.optstore.add_compiler_option(lang, gopt_key, gopt_valobj) def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, subproject: str) -> None: - from . import compilers - self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env, subproject) for key in comp.base_options: @@ -690,7 +686,7 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, else: skey = key if skey not in self.optstore: - self.optstore.add_system_option(skey, copy.deepcopy(compilers.BASE_OPTIONS[key])) + self.optstore.add_system_option(skey, copy.deepcopy(options.BASE_OPTIONS[key])) if skey in env.options: self.optstore.set_option(skey, env.options[skey]) elif subproject and key in env.options: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 26e92ee5de34..df5d2398ff51 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -11,7 +11,7 @@ from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog from .mesonlib import MesonException -from .options import OptionKey +from .options import BASE_OPTIONS, OptionKey if T.TYPE_CHECKING: from typing_extensions import Protocol @@ -189,7 +189,6 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - 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: - from mesonbuild.compilers import BASE_OPTIONS pending = coredata.optstore.pending_project_options errlist: T.List[str] = [] for opt in pending: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index f219ebda5b8a..acf7b8c318bb 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -753,6 +753,34 @@ def option_to_argparse(option: AnyOptionType, name: OptionKey, parser: argparse. OptionKey('python.purelibdir'): {}, } +MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd'] + +BASE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in T.cast('T.List[AnyOptionType]', [ + UserBooleanOption('b_pch', 'Use precompiled headers', True), + UserBooleanOption('b_lto', 'Use link time optimization', False), + UserIntegerOption('b_lto_threads', 'Use multiple threads for Link Time Optimization', 0), + UserComboOption('b_lto_mode', 'Select between different LTO modes.', 'default', choices=['default', 'thin']), + UserBooleanOption('b_thinlto_cache', 'Use LLVM ThinLTO caching for faster incremental builds', False), + UserStringOption('b_thinlto_cache_dir', 'Directory to store ThinLTO cache objects', ''), + UserStringArrayOption('b_sanitize', 'Code sanitizer to use', []), + UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), + UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), + UserComboOption( + 'b_pgo', 'Use profile guided optimization', 'off', choices=['off', 'generate', 'use']), + UserBooleanOption('b_coverage', 'Enable coverage tracking.', False), + UserComboOption( + 'b_colorout', 'Use colored output', 'always', choices=['auto', 'always', 'never']), + UserComboOption( + 'b_ndebug', 'Disable asserts', 'false', choices=['true', 'false', 'if-release']), + UserBooleanOption('b_staticpic', 'Build static libraries as position independent', True), + UserBooleanOption('b_pie', 'Build executables as position independent', False), + UserBooleanOption('b_bitcode', 'Generate and embed bitcode (only macOS/iOS/tvOS)', False), + UserComboOption( + 'b_vscrt', 'VS run-time library type to use.', 'from_buildtype', + choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']), + ]) +} class OptionStore: DEFAULT_DEPENDENTS = {'plain': ('plain', False), @@ -1058,7 +1086,6 @@ def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: def get_default_for_b_option(self, key: OptionKey) -> ElementaryOptionValues: assert self.is_base_option(key) - from .compilers.compilers import BASE_OPTIONS try: return T.cast('ElementaryOptionValues', BASE_OPTIONS[key.evolve(subproject=None)].default) except KeyError: From 24d57b5f92439f54241ce4b08d166e26d7e46890 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 10 Mar 2025 13:31:41 -0700 Subject: [PATCH 422/624] options: rename "native_file_*" arguments to "machine_file_*" Since they're not just native files, they're native files and cross files combined. --- mesonbuild/options.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index acf7b8c318bb..ac09d7299e7f 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1224,14 +1224,14 @@ def prefix_split_options(self, coll: T.Union[T.List[str], OptionStringLikeDict]) def first_handle_prefix(self, project_default_options: T.Union[T.List[str], OptionStringLikeDict], cmd_line_options: T.Union[T.List[str], OptionStringLikeDict], - native_file_options: T.Union[T.List[str], OptionStringLikeDict]) \ + machine_file_options: T.Union[T.List[str], OptionStringLikeDict]) \ -> T.Tuple[T.Union[T.List[str], OptionStringLikeDict], T.Union[T.List[str], OptionStringLikeDict], T.Union[T.List[str], OptionStringLikeDict]]: prefix = None (possible_prefix, nopref_project_default_options) = self.prefix_split_options(project_default_options) prefix = prefix if possible_prefix is None else possible_prefix - (possible_prefix, nopref_native_file_options) = self.prefix_split_options(native_file_options) + (possible_prefix, nopref_native_file_options) = self.prefix_split_options(machine_file_options) prefix = prefix if possible_prefix is None else possible_prefix (possible_prefix, nopref_cmd_line_options) = self.prefix_split_options(cmd_line_options) prefix = prefix if possible_prefix is None else possible_prefix @@ -1256,19 +1256,19 @@ def hard_reset_from_prefix(self, prefix: str) -> None: def initialize_from_top_level_project_call(self, project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], cmd_line_options_in: T.Union[T.List[str], OptionStringLikeDict], - native_file_options_in: T.Union[T.List[str], OptionStringLikeDict]) -> None: + machine_file_options_in: T.Union[T.List[str], OptionStringLikeDict]) -> None: first_invocation = True - (project_default_options, cmd_line_options, native_file_options) = self.first_handle_prefix(project_default_options_in, - cmd_line_options_in, - native_file_options_in) + (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(native_file_options, dict) - for keystr, valstr in native_file_options.items(): + 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) From 631486b8f68b152fdbf01c726bf4b0cb6bcbead3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 11 Mar 2025 13:13:23 -0700 Subject: [PATCH 423/624] options: rename OptStore.pending_project_options -> pending_options It's not just project options, it's all options that can end up on here. --- mesonbuild/msetup.py | 2 +- mesonbuild/options.py | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index df5d2398ff51..e6c68a9f9a79 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -189,7 +189,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - 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_project_options + pending = coredata.optstore.pending_options errlist: T.List[str] = [] for opt in pending: # Due to backwards compatibility setting build options in non-cross diff --git a/mesonbuild/options.py b/mesonbuild/options.py index ac09d7299e7f..0107d09f2c8e 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -798,11 +798,15 @@ def __init__(self, is_cross: bool) -> None: self.all_languages = set(all_languages) self.project_options = set() self.augments: T.Dict[str, str] = {} - self.pending_project_options: T.Dict[OptionKey, str] = {} 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_project_options = {} + self.pending_options = {} def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey: if isinstance(key, str): @@ -889,7 +893,7 @@ def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: AnyOp assert isinstance(valobj.name, str) if key not in self.options: self.options[key] = valobj - pval = self.pending_project_options.pop(key, None) + pval = self.pending_options.pop(key, None) if pval is not None: self.set_option(key, pval) @@ -902,7 +906,7 @@ def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valob def add_project_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) assert key.subproject is not None - pval = self.pending_project_options.pop(key, None) + pval = self.pending_options.pop(key, None) if key in self.options: raise MesonException(f'Internal error: tried to add a project option {key} that already exists.') else: @@ -1285,7 +1289,7 @@ def initialize_from_top_level_project_call(self, if proj_key in self.options: self.options[proj_key].set_value(valstr) else: - self.pending_project_options[key] = valstr + 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: @@ -1306,7 +1310,7 @@ def initialize_from_top_level_project_call(self, if not self.is_cross and key.is_for_build(): continue if key.subproject is not None: - self.pending_project_options[key] = valstr + self.pending_options[key] = valstr elif key in self.options: self.set_option(key, valstr, first_invocation) else: @@ -1318,7 +1322,7 @@ def initialize_from_top_level_project_call(self, if self.is_project_option(proj_key): self.set_option(proj_key, valstr) else: - self.pending_project_options[key] = valstr + self.pending_options[key] = valstr assert isinstance(cmd_line_options, dict) for keystr, valstr in cmd_line_options.items(): if isinstance(keystr, str): @@ -1344,9 +1348,9 @@ def initialize_from_top_level_project_call(self, # Permitting them all is not strictly correct. if not self.is_compiler_option(key) and not self.is_base_option(key): raise MesonException(f'Unknown options: "{keystr}"') - self.pending_project_options[key] = valstr + self.pending_options[key] = valstr else: - self.pending_project_options[key] = valstr + self.pending_options[key] = valstr def hacky_mchackface_back_to_list(self, optdict: T.Dict[str, str]) -> T.List[str]: if isinstance(optdict, dict): @@ -1373,7 +1377,7 @@ def initialize_from_subproject_call(self, if key in self.project_options: self.set_option(key, valstr, is_first_invocation) else: - self.pending_project_options.pop(key, None) + self.pending_options.pop(key, None) aug_str = f'{subproject}:{keystr}' self.augments[aug_str] = valstr # Check for pending options @@ -1383,7 +1387,7 @@ def initialize_from_subproject_call(self, key = OptionKey.from_string(key) if key.subproject != subproject: continue - self.pending_project_options.pop(key, None) + self.pending_options.pop(key, None) if key in self.options: self.set_option(key, valstr, is_first_invocation) else: From 3829b6f599c902dbf97130015d44a4daff11eefe Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 Mar 2025 19:59:58 -0800 Subject: [PATCH 424/624] options: Rename BASE_OPTIONS -> COMPILER_BASE_OPTIONS Which better describes their purpose, especially now that this is in the options module rather than the compilers module. --- mesonbuild/coredata.py | 4 ++-- mesonbuild/msetup.py | 4 ++-- mesonbuild/options.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index d2dc4bb2e40d..6e270a4a9a24 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -640,7 +640,7 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], # adding languages and setting backend. if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): continue - if self.optstore.is_base_option(k) and k.evolve(subproject=None) in options.BASE_OPTIONS: + if self.optstore.is_base_option(k) and k.evolve(subproject=None) in options.COMPILER_BASE_OPTIONS: # set_options will report unknown base options continue options_[k] = v @@ -686,7 +686,7 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, else: skey = key if skey not in self.optstore: - self.optstore.add_system_option(skey, copy.deepcopy(options.BASE_OPTIONS[key])) + self.optstore.add_system_option(skey, copy.deepcopy(options.COMPILER_BASE_OPTIONS[key])) if skey in env.options: self.optstore.set_option(skey, env.options[skey]) elif subproject and key in env.options: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index e6c68a9f9a79..bacb79b74269 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -11,7 +11,7 @@ from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog from .mesonlib import MesonException -from .options import BASE_OPTIONS, OptionKey +from .options import COMPILER_BASE_OPTIONS, OptionKey if T.TYPE_CHECKING: from typing_extensions import Protocol @@ -204,7 +204,7 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: if coredata.optstore.is_compiler_option(opt): continue if (coredata.optstore.is_base_option(opt) and - opt.evolve(subproject=None, machine=mesonlib.MachineChoice.HOST) in BASE_OPTIONS): + opt.evolve(subproject=None, machine=mesonlib.MachineChoice.HOST) in COMPILER_BASE_OPTIONS): continue keystr = str(opt) if keystr in cmd_line_options: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 0107d09f2c8e..8f71bc517abb 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -755,7 +755,7 @@ def option_to_argparse(option: AnyOptionType, name: OptionKey, parser: argparse. MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd'] -BASE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { +COMPILER_BASE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { OptionKey(o.name): o for o in T.cast('T.List[AnyOptionType]', [ UserBooleanOption('b_pch', 'Use precompiled headers', True), UserBooleanOption('b_lto', 'Use link time optimization', False), @@ -1091,7 +1091,7 @@ def get_value_object(self, key: T.Union[OptionKey, str]) -> AnyOptionType: def get_default_for_b_option(self, key: OptionKey) -> ElementaryOptionValues: assert self.is_base_option(key) try: - return T.cast('ElementaryOptionValues', BASE_OPTIONS[key.evolve(subproject=None)].default) + 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.') From 7f1614494df8a1d5d11bb7505a95f1db9aa53f10 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 10 Mar 2025 15:51:58 -0700 Subject: [PATCH 425/624] option: move the `is_per_machine_option` to the `OptionStore` --- mesonbuild/coredata.py | 7 ------- mesonbuild/environment.py | 2 +- mesonbuild/mintro.py | 2 +- mesonbuild/options.py | 5 +++++ 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6e270a4a9a24..03d80d940d85 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -489,13 +489,6 @@ def _set_others_from_buildtype(self, value: str) -> bool: return dirty - def is_per_machine_option(self, optname: OptionKey) -> bool: - if isinstance(optname, str): - optname = OptionKey.from_string(optname) - if optname.as_host() in options.BUILTIN_OPTIONS_PER_MACHINE: - return True - return self.optstore.is_compiler_option(optname) - 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) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 9d0677446ac8..12f94bbd4cfe 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -663,7 +663,7 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared # Keep only per machine options from the native file. The cross # file takes precedence over all other options. for key, value in list(self.options.items()): - if self.coredata.is_per_machine_option(key): + if self.coredata.optstore.is_per_machine_option(key): self.options[key.as_build()] = value self._load_machine_file_options(config, properties.host, MachineChoice.HOST) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 87ed66ec8a15..9b2a01f4c9ab 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -307,7 +307,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, options.OptionStore], section: str) -> None: for key, opt in sorted(opts.items()): optdict = {'name': str(key), 'value': opt.value, 'section': section, - 'machine': key.machine.get_lower_case_name() if coredata.is_per_machine_option(key) else 'any'} + 'machine': key.machine.get_lower_case_name() if coredata.optstore.is_per_machine_option(key) else 'any'} if isinstance(opt, options.UserStringOption): typestr = 'string' elif isinstance(opt, options.UserBooleanOption): diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 8f71bc517abb..e854f1119681 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1132,6 +1132,11 @@ def is_project_option(self, key: OptionKey) -> bool: """Convenience method to check if this is a project option.""" return key in self.project_options + def is_per_machine_option(self, optname: OptionKey) -> bool: + if optname.evolve(subproject=None, machine=MachineChoice.HOST) in BUILTIN_OPTIONS_PER_MACHINE: + return True + return self.is_compiler_option(optname) + def is_reserved_name(self, key: OptionKey) -> bool: if key.name in _BUILTIN_NAMES: return True From d6d1daf9cfc717bf000b2b83d776b6d35ab651b7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 10 Mar 2025 15:57:24 -0700 Subject: [PATCH 426/624] environment: filter machine file build options that are invalid Because it makes machine files more generically useful (ie, you can use a file as both a cross and native file) we allow all options to be defined in native files, even when those options are not per machine. It makes more sense to filter invalid options at Environment setup time then to wait and have them filtered when the initializing the project call. --- mesonbuild/environment.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 12f94bbd4cfe..92ce9c511a32 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -690,6 +690,13 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared 'See: https://mesonbuild.com/Builtin-options.html#build-type-options', fatal=False) + # Filter out build machine options that are not valid per-project. + # We allow this in the file because it makes the machine files more + # useful (ie, the same file can be used for host == build configuration + # a host != build configuration) + self.options = {k: v for k, v in self.options.items() + if k.machine is MachineChoice.HOST or self.coredata.optstore.is_per_machine_option(k)} + exe_wrapper = self.lookup_binary_entry(MachineChoice.HOST, 'exe_wrapper') if exe_wrapper is not None: self.exe_wrapper = ExternalProgram.from_bin_list(self, MachineChoice.HOST, 'exe_wrapper') From 05799aec80f3ea2afb19167846992348888123ce Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 10 Mar 2025 13:43:28 -0700 Subject: [PATCH 427/624] options: we need to skip build options for machine files as well When doing a host == build configuration. This allows us to remove the ignore in check_unused_options, which was papering over this bug. --- mesonbuild/msetup.py | 5 ----- mesonbuild/options.py | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index bacb79b74269..81dd1833004e 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -192,11 +192,6 @@ def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: pending = coredata.optstore.pending_options errlist: T.List[str] = [] for opt in pending: - # Due to backwards compatibility setting build options in non-cross - # builds is permitted and is a no-op. This should be made - # a hard error. - if not coredata.is_cross_build() and opt.is_for_build(): - continue # 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: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index e854f1119681..f45c121a8044 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1283,6 +1283,10 @@ def initialize_from_top_level_project_call(self, key = OptionKey.from_string(keystr) else: key = keystr + # Due to backwards compatibility we ignore all cross options when building + # natively. + if not self.is_cross and key.is_for_build(): + continue if key.subproject is not None: #self.pending_project_options[key] = valstr augstr = str(key) From 80116498d076b840162d74541564a29c4a37335f Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 25 Mar 2025 07:09:04 +0000 Subject: [PATCH 428/624] mtest: set VALGRIND_OPTS to fail tests on errors We currently suggest that users run `meson test --wrapper valgrind`, but this doesn't do what one might expect: Valgrind doesn't error out on violations/issues it detects. In the past, we had special handling for Valgrind in tests, see 1f76b76a84cb635f764ecbd2b77aaba1d375d72b but it was later dropped in 951262d7590343ffa9730666c427ad9d708a9fb6. This is similar to what we do for {A,UB,M}SAN_OPTIONS to give sensible behaviour that users expect out-of-the-box. Only do this if we see 'valgrind' in the wrapper command to avoid polluting logs. We may want to do that for the sanitizers variables in future too. Note that we're not adding --exit-on-first-error=yes here, as there may be several issues in an application, or a test may be rather slow, and so on. But --error-exitcode=1 to simply make Valgrind's exit status reflect whether an error was found is uncontroversial. Bug: https://github.com/mesonbuild/meson/issues/4727 Bug: https://github.com/mesonbuild/meson/issues/1105 Bug: https://github.com/mesonbuild/meson/issues/1175 Bug: https://github.com/mesonbuild/meson/issues/13745 --- docs/markdown/snippets/valgrind_test.md | 6 ++++++ mesonbuild/mtest.py | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 docs/markdown/snippets/valgrind_test.md diff --git a/docs/markdown/snippets/valgrind_test.md b/docs/markdown/snippets/valgrind_test.md new file mode 100644 index 000000000000..078730058033 --- /dev/null +++ b/docs/markdown/snippets/valgrind_test.md @@ -0,0 +1,6 @@ +## Valgrind now fails tests if errors are found + +Valgrind does not reflect an error in its exit code by default, meaning +a test may silently pass despite memory errors. Meson now exports +`VALGRIND_OPTS` such that Valgrind will exit with status 1 to indicate +an error if `VALGRIND_OPTS` is not set in the environment. diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 9d16e6358a57..541f8a3175b6 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1485,6 +1485,15 @@ def __init__(self, test: TestSerialisation, env: T.Dict[str, str], name: str, if ('MSAN_OPTIONS' not in env or not env['MSAN_OPTIONS']): env['MSAN_OPTIONS'] = 'halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1' + # Valgrind also doesn't reflect errors in its exit code by default. + if 'VALGRIND_OPTS' not in env or not env['VALGRIND_OPTS']: + try: + wrapper_name = TestHarness.get_wrapper(self.options)[0] + if 'valgrind' in wrapper_name: + env['VALGRIND_OPTS'] = '--error-exitcode=1' + except IndexError: + pass + if self.options.interactive or self.test.timeout is None or self.test.timeout <= 0: timeout = None elif self.options.timeout_multiplier is None: From 6f426e02d1776f77eb3b540dd470613e54a8c785 Mon Sep 17 00:00:00 2001 From: Sam James Date: Thu, 27 Mar 2025 02:08:31 +0000 Subject: [PATCH 429/624] mtest: refactor get_wrapper slightly --wrapper and --gdb are mutually exclusive per run(), so make get_wrapper reflect this (which would've saved me some time earlier with trying to make the two work together for something else, when it turns out that's impossible). As suggested by Eli. --- mesonbuild/mtest.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 541f8a3175b6..4a907d89e9a6 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -2064,15 +2064,16 @@ def open_logfiles(self) -> None: @staticmethod def get_wrapper(options: argparse.Namespace) -> T.List[str]: - wrap: T.List[str] = [] if options.gdb: wrap = [options.gdb_path, '--quiet'] if options.repeat > 1: wrap += ['-ex', 'run', '-ex', 'quit'] # Signal the end of arguments to gdb wrap += ['--args'] - if options.wrapper: - wrap += options.wrapper + elif options.wrapper: + wrap = options.wrapper + else: + wrap = [] return wrap def get_pretty_suite(self, test: TestSerialisation) -> str: From 604f8c0d3a2f376f59a719097aeb49ec208fde1c Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Wed, 26 Mar 2025 00:35:52 -0400 Subject: [PATCH 430/624] docs: fix default for mformat sort_files option sort_files has always defaulted to true. Fixes: 2b37101998c8 ("meson format command") --- docs/markdown/Commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index a99deb4941a9..247f2d74e272 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -473,7 +473,7 @@ The following options are recognized: - tab_width (int): Width of tab stops, used to compute line length when `indent_by` uses tab characters (default is 4). - sort_files (bool): When true, arguments of `files()` function are - sorted alphabetically (default is false). + sorted alphabetically (default is true). - group_arg_value (bool): When true, string argument with `--` prefix followed by string argument without `--` prefix are grouped on the same line, in multiline arguments (default is false). From 95d7fac30a9955fd1f846a650f2bfe0db4b261f8 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 2 Aug 2024 21:20:29 -0400 Subject: [PATCH 431/624] Docs: Cleaner admonishment styling --- docs/refman/templates/notes.mustache | 19 ++++++++++--- docs/theme/extra/css/notes.css | 41 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 docs/theme/extra/css/notes.css diff --git a/docs/refman/templates/notes.mustache b/docs/refman/templates/notes.mustache index de550c53c759..2172aafd5d30 100644 --- a/docs/refman/templates/notes.mustache +++ b/docs/refman/templates/notes.mustache @@ -1,14 +1,25 @@ {{#notes}} -
- Note: + {{/notes}} + {{#warnings}} -
- Warning: + {{/warnings}} diff --git a/docs/theme/extra/css/notes.css b/docs/theme/extra/css/notes.css new file mode 100644 index 000000000000..a9ef4973ae2a --- /dev/null +++ b/docs/theme/extra/css/notes.css @@ -0,0 +1,41 @@ +.note { + border: 0.2rem solid; + border-left-width: 0.5rem; + border-radius: 0.2rem; + margin: 2rem 1rem; +} +.note-topbar { + padding: 0.4rem 1rem; + border-radius: 1rem; + position: relative; + top: -1rem; + left: -1rem; + margin-right: auto; + min-width: min-content; + font-weight: bold; + font-size: 120%; + color: #fff; +} +.note-topbar .glyphicon { + top: 2px; +} + +.note-content { + padding: 0 1rem; + margin-top: -0.5rem; +} + +/* Colors taken from hotdoc_bootstrap_theme */ +.note-info { + border-color: #3dced8; +} +.note-topbar-info { + background-color: #3dced8; +} + +.note-warning { + border-color: #e96506; +} +.note-topbar-warning { + background-color: #e96506; +} From 5d9b9739ddc34a9db966a0034ab5ffb652ec4f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Mon, 17 Mar 2025 00:32:59 +0100 Subject: [PATCH 432/624] compilers/rust: fix sanity_check for Windows targets Windows toolchains append `.exe` to executables. When cross-compiling on Linux, attempting to run `./rusttest` will not execute the generated `rusttest.exe`. Fix this by always appending `.exe`, which is a valid filename on all supported platforms. This is what the `CLikeCompiler` class does too. While reviewing `rust.py`, there are opportunities for improvements and better unification with the rest of the Meson code. However, this commit focuses on fixing cross-compilation with minimal changes. Fixes: #14374 --- mesonbuild/compilers/rust.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index bfc83a57a00d..e35c10231b39 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -109,7 +109,7 @@ def needs_static_linker(self) -> bool: def sanity_check(self, work_dir: str, environment: Environment) -> None: source_name = os.path.join(work_dir, 'sanity.rs') - output_name = os.path.join(work_dir, 'rusttest') + output_name = os.path.join(work_dir, 'rusttest.exe') cmdlist = self.exelist.copy() with open(source_name, 'w', encoding='utf-8') as ofile: From d332a122c86ab64b644c05242aadc3a668e93e7a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 27 Aug 2024 08:53:45 -0700 Subject: [PATCH 433/624] environment: build_dir is allowed to be None in the initializer But we really expect it to be a string once the initializer is done. Therefore, let's set it to `''` just like we do with the scratchdir in the case that it's set to None in the initializer. --- mesonbuild/environment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 92ce9c511a32..a0d98ae581ae 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -566,12 +566,12 @@ class Environment: log_dir = 'meson-logs' info_dir = 'meson-info' - def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.SharedCMDOptions) -> None: + def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: coredata.SharedCMDOptions) -> None: self.source_dir = source_dir - self.build_dir = build_dir # Do not try to create build directories when build_dir is none. # This reduced mode is used by the --buildoptions introspector if build_dir is not None: + self.build_dir = build_dir self.scratch_dir = os.path.join(build_dir, Environment.private_dir) self.log_dir = os.path.join(build_dir, Environment.log_dir) self.info_dir = os.path.join(build_dir, Environment.info_dir) @@ -600,6 +600,7 @@ def __init__(self, source_dir: str, build_dir: str, cmd_options: coredata.Shared raise MesonException(f'{str(e)} Try regenerating using "meson setup --wipe".') else: # Just create a fresh coredata in this case + self.build_dir = '' self.scratch_dir = '' self.create_new_coredata(cmd_options) From 2b2d075b95cf11480113fefc272b625204e9dc11 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jan 2024 15:00:53 -0800 Subject: [PATCH 434/624] tests: demonstrate that our scanner cannot handle cross target modules --- test cases/fortran/8 module names/lib.f90 | 9 ++++++++ test cases/fortran/8 module names/meson.build | 13 +++++++++-- .../fortran/8 module names/meson_options.txt | 1 + test cases/fortran/8 module names/mod2.f90 | 8 +++++++ test cases/fortran/8 module names/test.f90 | 3 +-- unittests/allplatformstests.py | 22 ++++++++++++++++--- unittests/baseplatformtests.py | 1 + 7 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 test cases/fortran/8 module names/lib.f90 create mode 100644 test cases/fortran/8 module names/meson_options.txt diff --git a/test cases/fortran/8 module names/lib.f90 b/test cases/fortran/8 module names/lib.f90 new file mode 100644 index 000000000000..f8a8bfdacbb6 --- /dev/null +++ b/test cases/fortran/8 module names/lib.f90 @@ -0,0 +1,9 @@ +program lib +use MyMod1 +use MyMod2 ! test inline comment + +implicit none + +call showvalues() + +end program diff --git a/test cases/fortran/8 module names/meson.build b/test cases/fortran/8 module names/meson.build index 632c597889bf..9340c79d7789 100644 --- a/test cases/fortran/8 module names/meson.build +++ b/test cases/fortran/8 module names/meson.build @@ -1,6 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + project('mod_name_case', 'fortran') sources = ['test.f90', 'mod1.f90', 'mod2.f90'] -exe = executable('mod_name_case', sources) -test('mod_name_case', exe) +l = static_library('s1', 'mod1.f90') +l2 = static_library('s2', 'mod2.f90', link_whole : l) +if get_option('unittest') + sh = static_library('library', 'lib.f90', link_with : l2) +else + exe = executable('mod_name_case', 'test.f90', link_with : l2) + test('mod_name_case', exe) +endif diff --git a/test cases/fortran/8 module names/meson_options.txt b/test cases/fortran/8 module names/meson_options.txt new file mode 100644 index 000000000000..b5b7ee9a2394 --- /dev/null +++ b/test cases/fortran/8 module names/meson_options.txt @@ -0,0 +1 @@ +option('unittest', type : 'boolean', value : false) diff --git a/test cases/fortran/8 module names/mod2.f90 b/test cases/fortran/8 module names/mod2.f90 index 2087750debfc..3061c211ffab 100644 --- a/test cases/fortran/8 module names/mod2.f90 +++ b/test cases/fortran/8 module names/mod2.f90 @@ -1,6 +1,14 @@ module mymod2 +use mymod1 implicit none integer, parameter :: myModVal2 = 2 +contains + subroutine showvalues() + print*, "myModVal1 = ", myModVal1 + print*, "myModVal2 = ", myModVal2 + end subroutine showvalues + + end module mymod2 diff --git a/test cases/fortran/8 module names/test.f90 b/test cases/fortran/8 module names/test.f90 index 60ff16e9034c..fcfc23f919f8 100644 --- a/test cases/fortran/8 module names/test.f90 +++ b/test cases/fortran/8 module names/test.f90 @@ -1,9 +1,8 @@ program main -use mymod1 use MyMod2 ! test inline comment implicit none -integer, parameter :: testVar = myModVal1 + myModVal2 +call showvalues() end program diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index a189065f6672..34bdf86498b6 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2016-2021 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation import subprocess import re @@ -13,7 +13,7 @@ import pickle import zipfile, tarfile import sys -from unittest import mock, SkipTest, skipIf, skipUnless +from unittest import mock, SkipTest, skipIf, skipUnless, expectedFailure from contextlib import contextmanager from glob import glob from pathlib import (PurePath, Path) @@ -33,7 +33,7 @@ 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, - windows_proof_rm + windows_proof_rm, first ) from mesonbuild.options import OptionKey from mesonbuild.programs import ExternalProgram @@ -5208,3 +5208,19 @@ def test_interactive_tap(self): self.assertRegex(output, r'Ok:\s*0') self.assertRegex(output, r'Fail:\s*0') self.assertRegex(output, r'Ignored:\s*1') + + @expectedFailure + @skip_if_not_language('fortran') + def test_fortran_cross_target_module_dep(self) -> None: + if self.backend is not Backend.ninja: + raise SkipTest('Test is only relavent on the ninja backend') + testdir = os.path.join(self.fortran_test_dir, '8 module names') + self.init(testdir, extra_args=['-Dunittest=true']) + + # Find the correct output to compile, regardless of what compiler is being used + comp = self.get_compdb() + entry = first(comp, lambda e: e['file'].endswith('lib.f90')) + assert entry is not None, 'for mypy' + output = entry['output'] + + self.build(output, extra_args=['-j1']) diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index 5fff212f1440..f9bb58135318 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -78,6 +78,7 @@ def setUpClass(cls) -> None: cls.objc_test_dir = os.path.join(src_root, 'test cases/objc') cls.objcpp_test_dir = os.path.join(src_root, 'test cases/objcpp') cls.darwin_test_dir = os.path.join(src_root, 'test cases/darwin') + cls.fortran_test_dir = os.path.join(src_root, 'test cases/fortran') # Misc stuff if cls.backend is Backend.ninja: From 3996272ca7eabcc9bf6efdabb9abd33727d678f9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 Mar 2024 15:48:51 -0800 Subject: [PATCH 435/624] tests: our fortran order deps are wrong if a new module is introduced Because we don't set the appropriate dependencies --- unittests/allplatformstests.py | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 34bdf86498b6..1feffcec230e 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5224,3 +5224,43 @@ def test_fortran_cross_target_module_dep(self) -> None: output = entry['output'] self.build(output, extra_args=['-j1']) + + @expectedFailure + @skip_if_not_language('fortran') + def test_fortran_new_module_in_dep(self) -> None: + if self.backend is not Backend.ninja: + raise SkipTest('Test is only relavent on the ninja backend') + testdir = self.copy_srcdir(os.path.join(self.fortran_test_dir, '8 module names')) + self.init(testdir, extra_args=['-Dunittest=true']) + self.build() + + with open(os.path.join(testdir, 'mod1.f90'), 'a', encoding='utf-8') as f: + f.write(textwrap.dedent("""\ + module MyMod3 + implicit none + + integer, parameter :: myModVal3 =1 + + end module MyMod3 + """)) + + with open(os.path.join(testdir, 'test.f90'), 'w', encoding='utf-8') as f: + f.write(textwrap.dedent("""\ + program main + use MyMod2 + use MyMod3 + implicit none + + call showvalues() + print*, "MyModValu3 = ", myModVal3 + + end program + """)) + + # Find the correct output to compile, regardless of what compiler is being used + comp = self.get_compdb() + entry = first(comp, lambda e: e['file'].endswith('lib.f90')) + assert entry is not None, 'for mypy' + output = entry['output'] + + self.build(output, extra_args=['-j1']) From aa0a2a03c098de0942319addd737c8a3540b09ba Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 2 Jan 2024 16:40:08 -0800 Subject: [PATCH 436/624] backend/ninja: fortran must fully depend on all linked targets Because we use this dependency to ensure that any binary module files are generated, we must have a full dependency. Otherwise, if a new module is added to a dependent target, and used in our target, we race the generation of the binary module definition. --- mesonbuild/backend/ninjabackend.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2faf8c1d328a..b28bec34b2b3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3144,8 +3144,8 @@ def generate_single_compile(self, target: build.BuildTarget, src, d = os.path.join(self.get_target_private_dir(target), d) element.add_orderdep(d) element.add_dep(pch_dep) - for i in self.get_fortran_orderdeps(target, compiler): - element.add_orderdep(i) + for i in self.get_fortran_module_deps(target, compiler): + element.add_dep(i) if dep_file: element.add_item('DEPFILE', dep_file) if compiler.get_language() == 'cuda': @@ -3212,10 +3212,12 @@ def has_dir_part(self, fname: FileOrString) -> bool: # Fortran is a bit weird (again). When you link against a library, just compiling a source file # requires the mod files that are output when single files are built. To do this right we would need to # scan all inputs and write out explicit deps for each file. That is too slow and too much effort so - # instead just have an ordered dependency on the library. This ensures all required mod files are created. + # instead just have a full dependency on the library. This ensures all required mod files are created. # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that - # produce incorrect dep files but such is life. - def get_fortran_orderdeps(self, target, compiler): + # produce incorrect dep files but such is life. A full dependency is + # required to ensure that if a new module is added to an existing file that + # we correctly rebuild. + def get_fortran_module_deps(self, target, compiler) -> T.List[str]: if compiler.language != 'fortran': return [] return [ From 3c4417f38116954a3c73f0a5d9257e21c9880f19 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 7 Mar 2024 11:54:22 -0800 Subject: [PATCH 437/624] backend/ninja: Fortran targets need to -I transitive deps private dirs Otherwise they won't be able to find their module outputs. This requires a new method to look at dependencies, as the existing ones wont find static libraries that were linked statically into previous targets (this links with A which link_whole's B). We still need to have B in that case because we need it's BMI outputs. --- mesonbuild/backend/ninjabackend.py | 7 ++++++ mesonbuild/build.py | 36 ++++++++++++++++++++++++++++++ unittests/allplatformstests.py | 2 -- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b28bec34b2b3..146c48e8078a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1036,6 +1036,13 @@ def generate_target(self, target) -> None: fortran_inc_args = mesonlib.listify([target.compilers['fortran'].get_include_args( self.get_target_private_dir(t), is_system=False) for t in obj_targets]) + # add the private directories of all transitive dependencies, which + # are needed for their mod files + fc = target.compilers['fortran'] + for t in target.get_all_linked_targets(): + fortran_inc_args.extend(fc.get_include_args( + self.get_target_private_dir(t), False)) + # Generate compilation targets for sources generated by transpilers. # # Do not try to unity-build the generated source files, as these diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2302f17e413b..27d60c854e47 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1090,6 +1090,39 @@ def get_all_link_deps(self) -> ImmutableListProtocol[BuildTargetTypes]: stack.extendleft(t.link_whole_targets) return list(result) + @lru_cache(maxsize=None) + def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: + """Get all targets that have been linked with this one. + + This is useful for cases where we need to analyze these links, such as + for module information. + + This includes static libraries and static libraries linked with static + libraries. This differs from :method:`get_all_link_deps` in that it does + add static libs, and differs from `:method:`get_dependencies`, which + does not look for targets that are not directly linked, such as those + that are added with `link_whole`. + + :returns: An immutable list of BuildTargets + """ + result: OrderedSet[BuildTargetTypes] = OrderedSet() + stack: T.Deque[BuildTargetTypes] = deque() + stack.extendleft(self.link_targets) + stack.extendleft(self.link_whole_targets) + while stack: + t = stack.pop() + if t in result: + continue + if isinstance(t, CustomTargetIndex): + stack.appendleft(t.target) + continue + if isinstance(t, BuildTarget): + result.add(t) + stack.extendleft(t.link_targets) + stack.extendleft(t.link_whole_targets) + assert self not in result, 'should not have self' + return list(result) + def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: return self.get_transitive_link_deps_mapping(prefix) @@ -2607,6 +2640,9 @@ def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None: pass + def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]: + return [] + def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False) -> LibTypes: """Base case used by BothLibraries""" return self diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1feffcec230e..71f8828547dd 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5209,7 +5209,6 @@ def test_interactive_tap(self): self.assertRegex(output, r'Fail:\s*0') self.assertRegex(output, r'Ignored:\s*1') - @expectedFailure @skip_if_not_language('fortran') def test_fortran_cross_target_module_dep(self) -> None: if self.backend is not Backend.ninja: @@ -5225,7 +5224,6 @@ def test_fortran_cross_target_module_dep(self) -> None: self.build(output, extra_args=['-j1']) - @expectedFailure @skip_if_not_language('fortran') def test_fortran_new_module_in_dep(self) -> None: if self.backend is not Backend.ninja: From 11771a8ce6a5767ebf460c5d722a83358d5f83d1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 Mar 2024 12:43:49 -0700 Subject: [PATCH 438/624] backend/ninja: depfile generation needs a full dependency on all scanned sources It is not sufficient to have an order dependency on generated sources. If any of those sources change we need to rescan, otherwise we may not notice changes to modules. --- mesonbuild/backend/ninjabackend.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 146c48e8078a..8971abd2b4b3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1104,7 +1104,7 @@ def generate_target(self, target) -> None: 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, generated_source_files, fortran_order_deps) + self.generate_dependency_scan_target(target, compiled_sources, source2object, fortran_order_deps) 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. @@ -1140,7 +1140,6 @@ def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool: def generate_dependency_scan_target(self, target: build.BuildTarget, compiled_sources: T.List[str], source2object: T.Dict[str, str], - generated_source_files: T.List[mesonlib.File], object_deps: T.List[FileOrString]) -> None: if not self.should_use_dyndeps_for_target(target): return @@ -1166,19 +1165,22 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, pickle.dump(scaninfo, p) elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, pickle_file) - # Add any generated outputs to the order deps of the scan target, so - # that those sources are present - for g in generated_source_files: - elem.orderdeps.add(g.relative_name()) + # A full dependency is required on all scanned sources, if any of them + # are updated we need to rescan, as they may have changed the modules + # they use or export. + for s in scan_sources: + elem.deps.add(s[0]) elem.orderdeps.update(object_deps) self.add_build(elem) - def select_sources_to_scan(self, compiled_sources: T.List[str] + def select_sources_to_scan(self, compiled_sources: T.List[str], ) -> T.Iterable[T.Tuple[str, Literal['cpp', 'fortran']]]: # in practice pick up C++ and Fortran files. If some other language # requires scanning (possibly Java to deal with inner class files) # then add them here. for source in compiled_sources: + if isinstance(source, mesonlib.File): + source = source.rel_to_builddir(self.build_to_src) ext = os.path.splitext(source)[1][1:] if ext.lower() in compilers.lang_suffixes['cpp'] or ext == 'C': yield source, 'cpp' From cc815c4bcac055721ae359cbc757f50c10ed54ed Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 Mar 2024 12:20:00 -0700 Subject: [PATCH 439/624] backend/ninja: fix cross module dependencies This requires that every Fortran target that uses modules have a full dependency on the scan target of it's dependencies. This means that for a three step target `A -> B -> C`, we cannot start compiling any of B until all of A is linked, and cannot start compiling any of C until all of A and B is linked. This fixes various kinds of races, but it serializes the build and makes it slow. This is the best we can do though, since we don't have any sort of portable format for telling C what is in A and B, so C can't know what sources to wait on for it's modules to be fulfilled. --- mesonbuild/backend/ninjabackend.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8971abd2b4b3..a75befd7374c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1170,6 +1170,13 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, # they use or export. for s in scan_sources: elem.deps.add(s[0]) + # We need a full dependency on the output depfiles of other targets. If + # they change we need to completely + for t in target.get_all_linked_targets(): + if self.should_use_dyndeps_for_target(t): + elem.deps.add(os.path.join(self.get_target_dir(t), t.get_filename())) + elem.deps.update({os.path.join(self.get_target_dir(t), t.get_filename()) + for t in self.flatten_object_list(target)[1]}) elem.orderdeps.update(object_deps) self.add_build(elem) From ea344be9b017042fa206cb12e9fee95c1c22fae5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 11 Mar 2024 12:35:25 -0700 Subject: [PATCH 440/624] backend/ninja: use a two step process for dependency scanning This splits the scanner into two discrete steps, one that scans the source files, and one that that reads in the dependency information and produces a dyndep. The scanner uses the JSON format from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html, which is the same format the MSVC and Clang use for C++ modules scanning. This will allow us to more easily move to using MSVC and clang-scan-deps when possible. As an added bonus, this correctly tracks dependencies across TU and Target boundaries, unlike the previous implementation, which assumed that if it couldn't find a provider that everything was good, but could run into issues. Because of that limitation Fortran code had to fully depend on all of it's dependencies, transitive or not. Now, when using the dep scanner, we can remove that restriction, allowing more parallelism. --- mesonbuild/backend/ninjabackend.py | 59 ++++++++---- mesonbuild/scripts/depaccumulate.py | 129 +++++++++++++++++++++++++++ mesonbuild/scripts/depscan.py | 133 ++++++++++++++++++---------- 3 files changed, 254 insertions(+), 67 deletions(-) create mode 100644 mesonbuild/scripts/depaccumulate.py diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a75befd7374c..00cf4ad021d9 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1030,7 +1030,12 @@ def generate_target(self, target) -> None: obj_targets = [t for t in od if t.uses_fortran()] obj_list.extend(o) - fortran_order_deps = [File(True, *os.path.split(self.get_target_filename(t))) for t in obj_targets] + # We don't need this order dep if we're using dyndeps, as the + # depscanner will handle this for us, which produces a better dependency + # graph + fortran_order_deps: T.List[File] = [] + if not self.use_dyndeps_for_fortran(): + fortran_order_deps = [File(True, *os.path.split(self.get_target_filename(t))) for t in obj_targets] fortran_inc_args: T.List[str] = [] if target.uses_fortran(): fortran_inc_args = mesonlib.listify([target.compilers['fortran'].get_include_args( @@ -1144,7 +1149,7 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, if not self.should_use_dyndeps_for_target(target): return self._uses_dyndeps = True - depscan_file = self.get_dep_scan_file_for(target) + json_file, depscan_file = self.get_dep_scan_file_for(target) pickle_base = target.name + '.dat' pickle_file = os.path.join(self.get_target_private_dir(target), pickle_base).replace('\\', '/') pickle_abs = os.path.join(self.get_target_private_dir_abs(target), pickle_base).replace('\\', '/') @@ -1164,20 +1169,25 @@ def generate_dependency_scan_target(self, target: build.BuildTarget, with open(pickle_abs, 'wb') as p: pickle.dump(scaninfo, p) - elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, pickle_file) + elem = NinjaBuildElement(self.all_outputs, json_file, rule_name, pickle_file) # A full dependency is required on all scanned sources, if any of them # are updated we need to rescan, as they may have changed the modules # they use or export. for s in scan_sources: elem.deps.add(s[0]) - # We need a full dependency on the output depfiles of other targets. If - # they change we need to completely + elem.orderdeps.update(object_deps) + elem.add_item('name', target.name) + self.add_build(elem) + + infiles: T.Set[str] = set() for t in target.get_all_linked_targets(): if self.should_use_dyndeps_for_target(t): - elem.deps.add(os.path.join(self.get_target_dir(t), t.get_filename())) - elem.deps.update({os.path.join(self.get_target_dir(t), t.get_filename()) - for t in self.flatten_object_list(target)[1]}) - elem.orderdeps.update(object_deps) + infiles.add(self.get_dep_scan_file_for(t)[0]) + _, od = self.flatten_object_list(target) + infiles.update({self.get_dep_scan_file_for(t)[0] for t in od if t.uses_fortran()}) + + elem = NinjaBuildElement(self.all_outputs, depscan_file, 'depaccumulate', [json_file] + sorted(infiles)) + elem.add_item('name', target.name) self.add_build(elem) def select_sources_to_scan(self, compiled_sources: T.List[str], @@ -2638,10 +2648,19 @@ def generate_scanner_rules(self) -> None: if rulename in self.ruledict: # Scanning command is the same for native and cross compilation. return + command = self.environment.get_build_command() + \ ['--internal', 'depscan'] args = ['$picklefile', '$out', '$in'] - description = 'Scanning modules' + description = 'Scanning target $name for modules' + rule = NinjaRule(rulename, command, args, description) + self.add_rule(rule) + + rulename = 'depaccumulate' + command = self.environment.get_build_command() + \ + ['--internal', 'depaccumulate'] + args = ['$out', '$in'] + description = 'Generating dynamic dependency information for target $name' rule = NinjaRule(rulename, command, args, description) self.add_rule(rule) @@ -3160,8 +3179,9 @@ def generate_single_compile(self, target: build.BuildTarget, src, d = os.path.join(self.get_target_private_dir(target), d) element.add_orderdep(d) element.add_dep(pch_dep) - for i in self.get_fortran_module_deps(target, compiler): - element.add_dep(i) + if not self.use_dyndeps_for_fortran(): + for i in self.get_fortran_module_deps(target, compiler): + element.add_dep(i) if dep_file: element.add_item('DEPFILE', dep_file) if compiler.get_language() == 'cuda': @@ -3204,12 +3224,13 @@ def add_dependency_scanner_entries_to_element(self, target: build.BuildTarget, c extension = extension.lower() if not (extension in compilers.lang_suffixes['fortran'] or extension in compilers.lang_suffixes['cpp']): return - dep_scan_file = self.get_dep_scan_file_for(target) + dep_scan_file = self.get_dep_scan_file_for(target)[1] element.add_item('dyndep', dep_scan_file) element.add_orderdep(dep_scan_file) - def get_dep_scan_file_for(self, target: build.BuildTarget) -> str: - return os.path.join(self.get_target_private_dir(target), 'depscan.dd') + def get_dep_scan_file_for(self, target: build.BuildTarget) -> T.Tuple[str, str]: + 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): for d in header_deps: @@ -3232,9 +3253,11 @@ def has_dir_part(self, fname: FileOrString) -> bool: # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that # produce incorrect dep files but such is life. A full dependency is # required to ensure that if a new module is added to an existing file that - # we correctly rebuild. - def get_fortran_module_deps(self, target, compiler) -> T.List[str]: - if compiler.language != 'fortran': + # we correctly rebuild + def get_fortran_module_deps(self, target: build.BuildTarget, compiler: Compiler) -> T.List[str]: + # If we have dyndeps then we don't need this, since the depscanner will + # do all of things described above. + if compiler.language != 'fortran' or self.use_dyndeps_for_fortran(): return [] return [ os.path.join(self.get_target_dir(lt), lt.get_filename()) diff --git a/mesonbuild/scripts/depaccumulate.py b/mesonbuild/scripts/depaccumulate.py new file mode 100644 index 000000000000..7576390d4380 --- /dev/null +++ b/mesonbuild/scripts/depaccumulate.py @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2021-2024 Intel Corporation + +"""Accumulator for p1689r5 module dependencies. + +See: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html +""" + +from __future__ import annotations +import json +import re +import textwrap +import typing as T + +if T.TYPE_CHECKING: + from .depscan import Description, Rule + +# The quoting logic has been copied from the ninjabackend to avoid having to +# import half of Meson just to quote outputs, which is a performance problem +_QUOTE_PAT = re.compile(r'[$ :\n]') + + +def quote(text: str) -> str: + # Fast path for when no quoting is necessary + if not _QUOTE_PAT.search(text): + return text + if '\n' in text: + errmsg = textwrap.dedent(f'''\ + Ninja does not support newlines in rules. The content was: + + {text} + + Please report this error with a test case to the Meson bug tracker.''') + raise RuntimeError(errmsg) + return _QUOTE_PAT.sub(r'$\g<0>', text) + + +_PROVIDER_CACHE: T.Dict[str, str] = {} + + +def get_provider(rules: T.List[Rule], name: str) -> T.Optional[str]: + """Get the object that a module from another Target provides + + We must rely on the object file here instead of the module itself, because + the object rule is part of the generated build.ninja, while the module is + only declared inside a dyndep. This creates for the dyndep generator to + depend on previous dyndeps as order deps. Since the module + interface file will be generated when the object is generated we can rely on + that in proxy and simplify generation. + + :param rules: The list of rules to check + :param name: The logical-name to look for + :raises RuntimeError: If no provider can be found + :return: The object file of the rule providing the module + """ + # Cache the result for performance reasons + if name in _PROVIDER_CACHE: + return _PROVIDER_CACHE[name] + + for r in rules: + for p in r.get('provides', []): + if p['logical-name'] == name: + obj = r['primary-output'] + _PROVIDER_CACHE[name] = obj + return obj + return None + + +def process_rules(rules: T.List[Rule], + extra_rules: T.List[Rule], + ) -> T.Iterable[T.Tuple[str, T.Optional[T.List[str]], T.List[str]]]: + """Process the rules for this Target + + :param rules: the rules for this target + :param extra_rules: the rules for all of the targets this one links with, to use their provides + :yield: A tuple of the output, the exported modules, and the consumed modules + """ + for rule in rules: + prov: T.Optional[T.List[str]] = None + req: T.List[str] = [] + if 'provides' in rule: + prov = [p['compiled-module-path'] for p in rule['provides']] + if 'requires' in rule: + for p in rule['requires']: + modfile = p.get('compiled-module-path') + if modfile is not None: + req.append(modfile) + else: + # We can't error if this is not found because of compiler + # provided modules + found = get_provider(extra_rules, p['logical-name']) + if found: + req.append(found) + yield rule['primary-output'], prov, req + + +def formatter(files: T.Optional[T.List[str]]) -> str: + if files: + fmt = ' '.join(quote(f) for f in files) + return f'| {fmt}' + return '' + + +def gen(outfile: str, desc: Description, extra_rules: T.List[Rule]) -> int: + with open(outfile, 'w', encoding='utf-8') as f: + f.write('ninja_dyndep_version = 1\n\n') + + for obj, provides, requires in process_rules(desc['rules'], extra_rules): + ins = formatter(requires) + out = formatter(provides) + f.write(f'build {quote(obj)} {out}: dyndep {ins}\n\n') + + return 0 + + +def run(args: T.List[str]) -> int: + assert len(args) >= 2, 'got wrong number of arguments!' + outfile, jsonfile, *jsondeps = args + with open(jsonfile, 'r', encoding='utf-8') as f: + desc: Description = json.load(f) + + # All rules, necessary for fulfilling across TU and target boundaries + rules = desc['rules'].copy() + for dep in jsondeps: + with open(dep, encoding='utf-8') as f: + d: Description = json.load(f) + rules.extend(d['rules']) + + return gen(outfile, desc, rules) diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py index 44e805447713..6bd5cde9aac0 100644 --- a/mesonbuild/scripts/depscan.py +++ b/mesonbuild/scripts/depscan.py @@ -1,22 +1,60 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2020 The Meson development team -# Copyright © 2023 Intel Corporation +# Copyright © 2023-2024 Intel Corporation from __future__ import annotations import collections +import json import os import pathlib import pickle import re import typing as T -from ..backend.ninjabackend import ninja_quote - if T.TYPE_CHECKING: - from typing_extensions import Literal + from typing_extensions import Literal, TypedDict, NotRequired from ..backend.ninjabackend import TargetDependencyScannerInfo + Require = TypedDict( + 'Require', + { + 'logical-name': str, + 'compiled-module-path': NotRequired[str], + 'source-path': NotRequired[str], + 'unique-on-source-path': NotRequired[bool], + 'lookup-method': NotRequired[Literal['by-name', 'include-angle', 'include-quote']] + }, + ) + + Provide = TypedDict( + 'Provide', + { + 'logical-name': str, + 'compiled-module-path': NotRequired[str], + 'source-path': NotRequired[str], + 'unique-on-source-path': NotRequired[bool], + 'is-interface': NotRequired[bool], + }, + ) + + Rule = TypedDict( + 'Rule', + { + 'primary-output': NotRequired[str], + 'outputs': NotRequired[T.List[str]], + 'provides': NotRequired[T.List[Provide]], + 'requires': NotRequired[T.List[Require]], + } + ) + + class Description(TypedDict): + + version: int + revision: int + rules: T.List[Rule] + + CPP_IMPORT_RE = re.compile(r'\w*import ([a-zA-Z0-9]+);') CPP_EXPORT_RE = re.compile(r'\w*export module ([a-zA-Z0-9]+);') @@ -37,7 +75,7 @@ def __init__(self, pickle_file: str, outfile: str): self.sources = self.target_data.sources self.provided_by: T.Dict[str, str] = {} self.exports: T.Dict[str, str] = {} - self.needs: collections.defaultdict[str, T.List[str]] = collections.defaultdict(list) + self.imports: collections.defaultdict[str, T.List[str]] = collections.defaultdict(list) self.sources_with_exports: T.List[str] = [] def scan_file(self, fname: str, lang: Literal['cpp', 'fortran']) -> None: @@ -58,7 +96,7 @@ def scan_fortran_file(self, fname: str) -> None: # In Fortran you have an using declaration also for the module # you define in the same file. Prevent circular dependencies. if needed not in modules_in_this_file: - self.needs[fname].append(needed) + self.imports[fname].append(needed) if export_match: exported_module = export_match.group(1).lower() assert exported_module not in modules_in_this_file @@ -89,7 +127,7 @@ def scan_fortran_file(self, fname: str) -> None: # submodule (a1:a2) a3 <- requires a1@a2.smod # # a3 does not depend on the a1 parent module directly, only transitively. - self.needs[fname].append(parent_module_name_full) + self.imports[fname].append(parent_module_name_full) def scan_cpp_file(self, fname: str) -> None: fpath = pathlib.Path(fname) @@ -98,7 +136,7 @@ def scan_cpp_file(self, fname: str) -> None: export_match = CPP_EXPORT_RE.match(line) if import_match: needed = import_match.group(1) - self.needs[fname].append(needed) + self.imports[fname].append(needed) if export_match: exported_module = export_match.group(1) if exported_module in self.provided_by: @@ -123,47 +161,44 @@ def module_name_for(self, src: str, lang: Literal['cpp', 'fortran']) -> str: def scan(self) -> int: for s, lang in self.sources: self.scan_file(s, lang) - with open(self.outfile, 'w', encoding='utf-8') as ofile: - ofile.write('ninja_dyndep_version = 1\n') - for src, lang in self.sources: - objfilename = self.target_data.source2object[src] - mods_and_submods_needed = [] - module_files_generated = [] - module_files_needed = [] - if src in self.sources_with_exports: - module_files_generated.append(self.module_name_for(src, lang)) - if src in self.needs: - for modname in self.needs[src]: - if modname not in self.provided_by: - # Nothing provides this module, we assume that it - # comes from a dependency library somewhere and is - # already built by the time this compilation starts. - pass - else: - mods_and_submods_needed.append(modname) - - for modname in mods_and_submods_needed: - provider_src = self.provided_by[modname] - provider_modfile = self.module_name_for(provider_src, lang) - # Prune self-dependencies - if provider_src != src: - module_files_needed.append(provider_modfile) - - quoted_objfilename = ninja_quote(objfilename, True) - quoted_module_files_generated = [ninja_quote(x, True) for x in module_files_generated] - quoted_module_files_needed = [ninja_quote(x, True) for x in module_files_needed] - if quoted_module_files_generated: - mod_gen = '| ' + ' '.join(quoted_module_files_generated) - else: - mod_gen = '' - if quoted_module_files_needed: - mod_dep = '| ' + ' '.join(quoted_module_files_needed) - else: - mod_dep = '' - build_line = 'build {} {}: dyndep {}'.format(quoted_objfilename, - mod_gen, - mod_dep) - ofile.write(build_line + '\n') + description: Description = { + 'version': 1, + 'revision': 0, + 'rules': [], + } + for src, lang in self.sources: + rule: Rule = { + 'primary-output': self.target_data.source2object[src], + 'requires': [], + 'provides': [], + } + if src in self.sources_with_exports: + rule['outputs'] = [self.module_name_for(src, lang)] + if src in self.imports: + for modname in self.imports[src]: + provider_src = self.provided_by.get(modname) + if provider_src == src: + continue + rule['requires'].append({ + 'logical-name': modname, + }) + if provider_src: + rule['requires'][-1].update({ + 'source-path': provider_src, + 'compiled-module-path': self.module_name_for(provider_src, lang), + }) + if src in self.exports: + modname = self.exports[src] + rule['provides'].append({ + 'logical-name': modname, + 'source-path': src, + 'compiled-module-path': self.module_name_for(src, lang), + }) + description['rules'].append(rule) + + with open(self.outfile, 'w', encoding='utf-8') as f: + json.dump(description, f) + return 0 def run(args: T.List[str]) -> int: From 60cf59bdaa61adaa14743eb5d6c3d9bdf6791867 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Apr 2024 09:54:18 -0700 Subject: [PATCH 441/624] tests/fortran: also test using a generator() We already test for custom_targets and configure_file, but we should test a generator too --- test cases/fortran/7 generated/gen.py | 45 ++++++++++++++++++++++ test cases/fortran/7 generated/meson.build | 21 +++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100755 test cases/fortran/7 generated/gen.py diff --git a/test cases/fortran/7 generated/gen.py b/test cases/fortran/7 generated/gen.py new file mode 100755 index 000000000000..86d9bf712261 --- /dev/null +++ b/test cases/fortran/7 generated/gen.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation + +from __future__ import annotations +import argparse +import typing as T + +if T.TYPE_CHECKING: + class Arguments(T.Protocol): + + input: str + output: str + replacements: T.List[T.Tuple[str, str]] + + +def process(txt: str, replacements: T.List[T.Tuple[str, str]]) -> str: + for k, v in replacements: + txt = txt.replace(k, v) + return txt + + +def split_arg(arg: str) -> T.Tuple[str, str]: + args = arg.split('=', maxsplit=1) + assert len(args) == 2, 'Did not get the right number of args?' + return T.cast('T.Tuple[str, str]', tuple(args)) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('input') + parser.add_argument('output') + parser.add_argument('--replace', action='append', required=True, dest='replacements', type=split_arg) + args = T.cast('Arguments', parser.parse_args()) + + with open(args.input, 'r', encoding='utf-8') as f: + content = f.read() + + content = process(content, args.replacements) + + with open(args.output, 'w', encoding='utf-8') as f: + f.write(content) + +if __name__ == "__main__": + main() diff --git a/test cases/fortran/7 generated/meson.build b/test cases/fortran/7 generated/meson.build index f021309d1c75..8f3b3d45d4d2 100644 --- a/test cases/fortran/7 generated/meson.build +++ b/test cases/fortran/7 generated/meson.build @@ -35,5 +35,22 @@ foreach template_basename : templates_basenames endforeach sources = ['prog.f90'] + generated_sources -exe = executable('generated', sources, link_with: three) -test('generated', exe) +exe = executable('configure_file', sources, link_with: three) +test('configure_file', exe) + +gen = generator( + find_program('gen.py'), + arguments : [ + '@INPUT@', '@OUTPUT@', + '--replace', '@ONE@=1', + '--replace', '@TWO@=2', + ], + output : ['@BASENAME@.f90'], +) + +exe = executable( + 'generator', + 'prog.f90', gen.process('mod1.fpp', 'mod2.fpp'), + link_with: three, +) +test('generator', exe) From 23a12c3306e54d8a94c8eeabd956842c202c9e0b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 1 Apr 2024 09:55:48 -0700 Subject: [PATCH 442/624] tests/fortran: use fs.copyfile Since the comment saying we need a generic way to do this is a little outdated. --- test cases/fortran/7 generated/meson.build | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/test cases/fortran/7 generated/meson.build b/test cases/fortran/7 generated/meson.build index 8f3b3d45d4d2..257ea1e2207f 100644 --- a/test cases/fortran/7 generated/meson.build +++ b/test cases/fortran/7 generated/meson.build @@ -8,20 +8,7 @@ conf_data = configuration_data() conf_data.set('ONE', 1) conf_data.set('TWO', 2) -mod3_f = custom_target( - 'mod3.f', - input : 'mod3.f90', - output : 'mod3.f90', - # We need a platform agnostic way to do a copy a file, using a custom_target - # and we need to use the @OUTDIR@, not @OUTPUT@ in order to exercise - # https://github.com/mesonbuild/meson/issues/9258 - command : [ - find_program('python', 'python3'), '-c', - 'import sys, shutil; shutil.copy(sys.argv[1], sys.argv[2])', - '@INPUT@', '@OUTDIR@', - ], -) - +mod3_f = import('fs').copyfile('mod3.f90', 'mod3.f90') three = library('mod3', mod3_f) templates_basenames = ['mod2', 'mod1'] From a8321c6f6431d83f96ee0934576f8c9f0dbeddc0 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Thu, 3 Apr 2025 15:00:10 -0400 Subject: [PATCH 443/624] Docs: Set note/warning title color to black text The white text previously used (inadvertently) doesn't show up well against the blue color used as the background for Note boxes. --- docs/theme/extra/css/notes.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theme/extra/css/notes.css b/docs/theme/extra/css/notes.css index a9ef4973ae2a..70f364e0a0af 100644 --- a/docs/theme/extra/css/notes.css +++ b/docs/theme/extra/css/notes.css @@ -14,7 +14,7 @@ min-width: min-content; font-weight: bold; font-size: 120%; - color: #fff; + color: #000; } .note-topbar .glyphicon { top: 2px; From 4ecf608936dbaf70fb1c4e807a82073162b56a45 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 14:11:08 +0200 Subject: [PATCH 444/624] compilers: remove useless get_option_compile_args Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/c.py | 3 --- mesonbuild/compilers/cpp.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 85eea2f1c9c9..de21d48732b1 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -660,9 +660,6 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: - return [] - def get_no_optimization_args(self) -> T.List[str]: return ['-O0'] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index cbb91e439a18..3768556e664a 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -999,9 +999,6 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ def get_always_args(self) -> T.List[str]: return ['-nologo', '-lang=cpp'] - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: - return [] - def get_compile_only_args(self) -> T.List[str]: return [] From 47984f813450bcbcd102ed45d579011deb4572c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 14:15:59 +0200 Subject: [PATCH 445/624] compilers: introduce get_option_std_args Allow CUDA to completely override the -std arguments but not the rest. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 1 + mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/backend/vs2010backend.py | 2 ++ mesonbuild/backend/xcodebackend.py | 1 + mesonbuild/compilers/compilers.py | 3 +++ mesonbuild/interpreter/compiler.py | 1 + 6 files changed, 9 insertions(+) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 8a788783766c..20f7907a1e84 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1024,6 +1024,7 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com # 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) optimization = self.get_target_option(target, 'optimization') assert isinstance(optimization, str), 'for mypy' diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 00cf4ad021d9..8cf459f7ff2a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1836,6 +1836,7 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ 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 += 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') diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 59ddb9c26477..283f9f0a3744 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1000,6 +1000,8 @@ def get_args_defines_and_inc_dirs(self, target, compiler, generated_files_includ target, comp, self.environment) file_args[l] += comp.get_option_compile_args( target, self.environment, target.subproject) + file_args[l] += comp.get_option_std_args( + target, self.environment, 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(): diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index e5c631fff920..587404a01330 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1688,6 +1688,7 @@ def generate_single_build_target(self, objects_dict, target_name, target) -> Non # 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) # 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() diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3398aa4afb3c..89eceab75286 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -611,6 +611,9 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', 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]: + 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) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 303af124043a..8aeac8ac6b2e 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -270,6 +270,7 @@ def _determine_args(self, kwargs: BaseCompileKW, 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) if mode is CompileCheckMode.LINK: args.extend(self.compiler.get_option_link_args(None, self.interpreter.environment, self.subproject)) if kwargs.get('werror', False): From ff0c758b2a8015f7e7ca6fc627c29ef7bb4771b3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 14:20:44 +0200 Subject: [PATCH 446/624] compilers: move -std options to get_option_std_args, special-casing CUDA Move building the -std option to the new get_option_std_args method, special casing CUDA to never include the option from the host compiler. This fixes again #8523, which was broken by the option refactoring (unsurprisingly, since the fix was ripped out unceremoniously without a replacement). Fixes: #14365 Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/c.py | 26 ++++---- mesonbuild/compilers/cpp.py | 91 ++++++++++++++++----------- mesonbuild/compilers/cuda.py | 14 ++++- mesonbuild/compilers/fortran.py | 6 +- mesonbuild/compilers/mixins/elbrus.py | 2 +- mesonbuild/compilers/objc.py | 4 +- mesonbuild/compilers/objcpp.py | 4 +- mesonbuild/compilers/rust.py | 2 +- mesonbuild/compilers/swift.py | 3 +- 9 files changed, 88 insertions(+), 64 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index de21d48732b1..a6769fc96606 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -132,7 +132,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 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_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -220,7 +220,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c90', 'c99', 'c11'], gnu=True) return opts - def get_option_compile_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -264,7 +264,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 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_std_args(self, target: BuildTarget, env: Environment, 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) @@ -388,7 +388,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(stds, gnu=True) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -451,7 +451,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(stds, gnu=True, gnu_deprecated=True) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) @@ -473,7 +473,7 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic full_version=full_version) ClangClCompiler.__init__(self, target) - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + 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) assert isinstance(std, str) if std != "none": @@ -502,7 +502,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99', 'c11']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('winlibs', env, target, subproject) assert isinstance(std, str) @@ -536,7 +536,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c89', 'c99', 'c11']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -569,7 +569,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -617,7 +617,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -698,7 +698,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_no_stdinc_args(self) -> T.List[str]: return [] - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -732,7 +732,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['c99']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -760,7 +760,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['c99']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 3768556e664a..b85751d05043 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -268,17 +268,13 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - std = self.get_compileropt_value('std', env, target, subproject) 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) - assert isinstance(std, str) assert isinstance(rtti, bool) assert isinstance(eh, str) assert isinstance(debugstl, bool) - if std != 'none': - args.append(self._find_best_cpp_std(std)) non_msvc_eh_options(eh, args) @@ -296,6 +292,14 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub return args + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, 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]: if self.info.is_windows() or self.info.is_cygwin(): # without a typedict mypy can't understand this. @@ -368,7 +372,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ info, linker=linker, defines=defines, full_version=full_version) - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -412,7 +416,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -481,19 +485,14 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - std = self.get_compileropt_value('std', env, target, subproject) 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) - assert isinstance(std, str) assert isinstance(rtti, bool) assert isinstance(eh, str) assert isinstance(debugstl, bool) - if std != 'none': - args.append(self._find_best_cpp_std(std)) - non_msvc_eh_options(eh, args) if not rtti: @@ -503,6 +502,14 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub 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]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, 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]: if self.info.is_windows() or self.info.is_cygwin(): # without a typedict mypy can't understand this. @@ -634,11 +641,6 @@ def has_function(self, funcname: str, prefix: str, env: 'Environment', *, # 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]: args: T.List[str] = [] - std = self.get_compileropt_value('std', env, target, subproject) - assert isinstance(std, str) - if std != 'none': - args.append(self._find_best_cpp_std(std)) - eh = self.get_compileropt_value('eh', env, target, subproject) assert isinstance(eh, str) @@ -650,6 +652,14 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub 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]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) + if std != 'none': + args.append(self._find_best_cpp_std(std)) + return args + class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler): def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, @@ -711,22 +721,14 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - std = self.get_compileropt_value('std', env, target, subproject) 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) - assert isinstance(std, str) assert isinstance(rtti, bool) assert isinstance(eh, str) assert isinstance(debugstl, bool) - if std != 'none': - remap_cpp03 = { - 'c++03': 'c++98', - 'gnu++03': 'gnu++98' - } - args.append('-std=' + remap_cpp03.get(std, std)) if eh == 'none': args.append('-fno-exceptions') if rtti: @@ -735,6 +737,19 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub 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]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) + if std != 'none': + remap_cpp03 = { + 'c++03': 'c++98', + 'gnu++03': 'gnu++98' + } + args.append('-std=' + remap_cpp03.get(std, std)) + + return args + def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: return [] @@ -801,11 +816,9 @@ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] - std = self.get_compileropt_value('std', env, target, subproject) eh = self.get_compileropt_value('eh', env, target, subproject) rtti = self.get_compileropt_value('rtti', env, target, subproject) - assert isinstance(std, str) assert isinstance(rtti, bool) assert isinstance(eh, str) @@ -819,14 +832,18 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub if not rtti: args.append('/GR-') - permissive, ver = self.VC_VERSION_MAP[std] + return args + + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: + args: T.List[str] = [] + std = self.get_compileropt_value('std', env, target, subproject) + assert isinstance(std, str) + permissive, ver = self.VC_VERSION_MAP[std] if ver is not None: args.append(f'/std:c++{ver}') - if not permissive: args.append('/permissive-') - return args def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: @@ -840,7 +857,7 @@ class CPP11AsCPP14Mixin(CompilerMixinBase): This is a limitation of Clang and MSVC that ICL doesn't share. """ - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, 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) @@ -854,7 +871,7 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub 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_compile_args(target, env, subproject) + original_args = super().get_option_std_args(target, env, subproject) std_mapping = {'/std:c++11': '/std:c++14', '/std:c++14': '/std:vc++14'} processed_args = [std_mapping.get(x, x) for x in original_args] @@ -891,12 +908,12 @@ def get_options(self) -> 'MutableKeyedOptionDictType': cpp_stds.extend(['c++20', 'vc++20']) return self._get_options_impl(super().get_options(), cpp_stds) - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + 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) 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_compile_args(target, env, subproject) + args = super().get_option_std_args(target, env, subproject) if version_compare(self.version, '<19.11'): try: @@ -969,7 +986,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++03', 'c++11']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -1028,7 +1045,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': std_opt.set_versions(['c++03']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -1068,7 +1085,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, []) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -1096,7 +1113,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, []) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index b67aa8715acf..509044cd444b 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -663,6 +663,14 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 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) + + try: + host_compiler_args = self.host_compiler.get_option_compile_args(target, env, 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]: # 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 @@ -670,13 +678,13 @@ def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', sub std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) if std != 'none': - args.append('--std=' + std) + return ['--std=' + std] try: - host_compiler_args = self.host_compiler.get_option_compile_args(target, env, subproject) + host_compiler_args = self.host_compiler.get_option_std_args(target, env, subproject) except KeyError: host_compiler_args = [] - return args + self._to_host_flags(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) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 088551872259..0bbd7d0ef9dd 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -285,7 +285,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, fortran_stds) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) @@ -419,7 +419,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} @@ -473,7 +473,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']) return opts - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index eac68bd94860..7037db23260e 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -83,7 +83,7 @@ def get_pch_suffix(self) -> str: # Actually it's not supported for now, but probably will be supported in future return 'pch' - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, 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: diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 776f5037d7ea..c36373f434b2 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -76,7 +76,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] key = OptionKey('c_std', subproject=subproject, machine=self.for_machine) if target: @@ -114,7 +114,7 @@ def make_option_name(self, key: OptionKey) -> str: return 'c_std' return super().make_option_name(key) - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, 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) diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index b38fdb60d689..b1cb605e31ca 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -81,7 +81,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ self.supported_warn_args(gnu_common_warning_args) + self.supported_warn_args(gnu_objc_warning_args))} - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] key = OptionKey('cpp_std', subproject=subproject, machine=self.for_machine) if target: @@ -110,7 +110,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ '3': default_warn_args + ['-Wextra', '-Wpedantic'], 'everything': ['-Weverything']} - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, 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) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index e35c10231b39..53b3afbf2a14 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -255,7 +255,7 @@ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: # provided by the linker flags. return [] - def get_option_compile_args(self, target: 'BuildTarget', env: 'Environment', subproject: T.Optional[str] = None) -> T.List[str]: + def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args = [] std = self.get_compileropt_value('std', env, target, subproject) assert isinstance(std, str) diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index b04ccbd0e541..c28e7af25e4f 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -130,8 +130,7 @@ def get_options(self) -> MutableKeyedOptionDictType: return opts - def get_option_compile_args(self, target: build.BuildTarget, env: Environment, subproject: T.Optional[str] = None - ) -> T.List[str]: + def get_option_std_args(self, target: build.BuildTarget, env: Environment, subproject: T.Optional[str] = None) -> T.List[str]: args: T.List[str] = [] std = self.get_compileropt_value('std', env, target, subproject) From a7ea0cd7e4bc0d89d607d5fc0e999a4a6ab5b531 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 06:56:30 +0200 Subject: [PATCH 447/624] add test case for cpp_std/cuda_std This was broken twice, so check that it does not regress. Signed-off-by: Paolo Bonzini --- test cases/cuda/13 cuda compiler setting/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test cases/cuda/13 cuda compiler setting/meson.build b/test cases/cuda/13 cuda compiler setting/meson.build index 4f111d1b9ee1..ba560b1e6f80 100644 --- a/test cases/cuda/13 cuda compiler setting/meson.build +++ b/test cases/cuda/13 cuda compiler setting/meson.build @@ -1,4 +1,5 @@ -project('simple', 'cuda', version : '1.0.0') +project('simple', ['cpp', 'cuda'], version : '1.0.0', + default_options: ['cpp_std=c++2a', 'cuda_std=c++17']) exe = executable('prog', 'prog.cu') test('cudatest', exe) From 31db63e2d8a5d2c3a3db533e86d3676b18bca84b Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev Date: Sun, 6 Oct 2024 13:30:32 +0200 Subject: [PATCH 448/624] cmake: pass clib_compiler to resolve_cmake_trace_targets() This is required to resolve ambiguous LINK_LIBRARIES passed as bare names. --- mesonbuild/cmake/interpreter.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 53f4f193e6da..6fcebb557405 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -251,6 +251,8 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine self.generated_raw: T.List[Path] = [] + self.clib_compiler = None + for i in target.files: languages: T.Set[str] = set() src_suffixes: T.Set[str] = set() @@ -276,6 +278,10 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine # Register the new languages and initialize the compile opts array for lang in languages: self.languages.add(lang) + + if self.clib_compiler is None: + self.clib_compiler = self.env.coredata.compilers[self.for_machine].get(lang) + if lang not in self.compile_opts: self.compile_opts[lang] = [] @@ -346,7 +352,7 @@ def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: Path, su if tgt: self.depends_raw = trace.targets[self.cmake_name].depends - rtgt = resolve_cmake_trace_targets(self.cmake_name, trace, self.env) + 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] self.link_flags += rtgt.link_flags self.public_link_flags += rtgt.public_link_flags From 4a3d60e2020753d1ad891fbb0ac52534784715f2 Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev Date: Wed, 9 Oct 2024 22:31:38 +0200 Subject: [PATCH 449/624] cmake: filter and order clib_compiler languages --- mesonbuild/cmake/interpreter.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 6fcebb557405..9cfb4fac2d6d 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -251,8 +251,6 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine self.generated_raw: T.List[Path] = [] - self.clib_compiler = None - for i in target.files: languages: T.Set[str] = set() src_suffixes: T.Set[str] = set() @@ -278,10 +276,6 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine # Register the new languages and initialize the compile opts array for lang in languages: self.languages.add(lang) - - if self.clib_compiler is None: - self.clib_compiler = self.env.coredata.compilers[self.for_machine].get(lang) - if lang not in self.compile_opts: self.compile_opts[lang] = [] @@ -301,6 +295,17 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine else: self.sources += i.sources + self.clib_compiler = None + compilers = self.env.coredata.compilers[self.for_machine] + + for lang in ['objcpp', 'cpp', 'objc', 'fortran', 'c']: + if lang in self.languages: + try: + self.clib_compiler = compilers[lang] + break + except KeyError: + pass + def __repr__(self) -> str: return f'<{self.__class__.__name__}: {self.name}>' From 9b0a178a27be75f7b53ada4bacde2a7de418281e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 08:52:59 +0200 Subject: [PATCH 450/624] backends: extract get_fortran_order_deps() Allow reusing it for Rust targets. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8cf459f7ff2a..da144016fbde 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1027,19 +1027,13 @@ def generate_target(self, target) -> None: pch_objects = [] o, od = self.flatten_object_list(target) - obj_targets = [t for t in od if t.uses_fortran()] obj_list.extend(o) + fortran_order_deps = self.get_fortran_order_deps(od) - # We don't need this order dep if we're using dyndeps, as the - # depscanner will handle this for us, which produces a better dependency - # graph - fortran_order_deps: T.List[File] = [] - if not self.use_dyndeps_for_fortran(): - fortran_order_deps = [File(True, *os.path.split(self.get_target_filename(t))) for t in obj_targets] fortran_inc_args: T.List[str] = [] if target.uses_fortran(): fortran_inc_args = mesonlib.listify([target.compilers['fortran'].get_include_args( - self.get_target_private_dir(t), is_system=False) for t in obj_targets]) + self.get_target_private_dir(t), is_system=False) for t in od if t.uses_fortran()]) # add the private directories of all transitive dependencies, which # are needed for their mod files @@ -2519,6 +2513,16 @@ def use_dyndeps_for_fortran(self) -> bool: minimum version is bumped to 1.10.''' return self.ninja_has_dyndeps + def get_fortran_order_deps(self, deps: T.List[build.BuildTarget]) -> T.List[File]: + # We don't need this order dep if we're using dyndeps, as the + # depscanner will handle this for us, which produces a better dependency + # graph + if self.use_dyndeps_for_fortran(): + return [] + + return [File(True, *os.path.split(self.get_target_filename(t))) for t in deps + if t.uses_fortran()] + def generate_fortran_dep_hack(self, crstr: str) -> None: if self.use_dyndeps_for_fortran(): return From a53f129586812c932776d201a891a484f73f0bac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 10:37:34 +0100 Subject: [PATCH 451/624] rust: add external objects to the link command line Because rustc does not support extract_objects, QEMU creates a static library with all the C objects. It then passes this static library as link_whole, together with another static library containing general purpose utility functions which is passed as link_with. However, this is brittle because the two have a circular dependency and they cannot be merged because of the link_whole/link_with difference. While lld seems to have the --start-group/--end-group semantics automatically, ld.bfd can fail if these options are needed. This can cause difference between distros depending on how Rust is packaged (e.g. Ubuntu 22.04 and Debian bookworm seem to use ld.bfd). The simplest solution is for Meson to implement "objects:" properly for Rust. Then QEMU can use the same internal dependency objects that it already has in place for C programs. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/rust-objects.md | 4 ++++ mesonbuild/backend/ninjabackend.py | 16 ++++++++++++---- mesonbuild/interpreter/interpreter.py | 2 ++ test cases/rust/27 objects/lib1.c | 11 +++++++++++ test cases/rust/27 objects/lib1.h | 4 ++++ test cases/rust/27 objects/lib2.c | 8 ++++++++ test cases/rust/27 objects/lib2.h | 3 +++ test cases/rust/27 objects/main.rs | 9 +++++++++ test cases/rust/27 objects/meson.build | 18 ++++++++++++++++++ 9 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 docs/markdown/snippets/rust-objects.md create mode 100644 test cases/rust/27 objects/lib1.c create mode 100644 test cases/rust/27 objects/lib1.h create mode 100644 test cases/rust/27 objects/lib2.c create mode 100644 test cases/rust/27 objects/lib2.h create mode 100644 test cases/rust/27 objects/main.rs create mode 100644 test cases/rust/27 objects/meson.build diff --git a/docs/markdown/snippets/rust-objects.md b/docs/markdown/snippets/rust-objects.md new file mode 100644 index 000000000000..575e1f6e38ed --- /dev/null +++ b/docs/markdown/snippets/rust-objects.md @@ -0,0 +1,4 @@ +## `objects` added correctly to Rust executables + +Any objects included in a Rust executable were previously ignored. They +are now added correctly. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index da144016fbde..1ba41bccf17f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2024,7 +2024,7 @@ def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, src 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[RustDep], T.List[str]]: + 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]]: deps: T.List[str] = [] project_deps: T.List[RustDep] = [] args: T.List[str] = [] @@ -2056,6 +2056,12 @@ def _link_library(libname: str, static: bool, bundle: bool = False): type_ += ':' + ','.join(modifiers) args.append(f'-l{type_}={libname}') + objs, od = self.flatten_object_list(target) + for o in objs: + 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() @@ -2141,7 +2147,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): if isinstance(target, build.SharedLibrary) or has_shared_deps: args += self.get_build_rpath_args(target, rustc) - return deps, project_deps, args + return deps, fortran_order_deps, project_deps, args def generate_rust_target(self, target: build.BuildTarget) -> None: rustc = T.cast('RustCompiler', target.compilers['rust']) @@ -2165,7 +2171,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: 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, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) + deps, fortran_order_deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) args += deps_args proc_macro_dylib_path = None @@ -2183,6 +2189,8 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file) if orderdeps: element.add_orderdep(orderdeps) + if fortran_order_deps: + element.add_orderdep(fortran_order_deps) if deps: # dependencies need to cause a relink, they're not just for ordering element.add_dep(deps) @@ -2198,7 +2206,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: rustdoc = rustc.get_rustdoc(self.environment) 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) + _, _, _, deps_args = self.get_rust_compiler_deps_and_args(target.doctests.target, rustdoc) args += deps_args target.doctests.cmd_args = args.to_native() + [main_rust_file] + target.doctests.cmd_args diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 3c542cc4e538..dedd20c8256b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3458,6 +3458,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], kwargs) + if objs and target.uses_rust(): + FeatureNew.single_use('objects in Rust targets', '1.8.0', self.subproject) self.add_target(name, target) self.project_args_frozen = True diff --git a/test cases/rust/27 objects/lib1.c b/test cases/rust/27 objects/lib1.c new file mode 100644 index 000000000000..b463bffa3c6e --- /dev/null +++ b/test cases/rust/27 objects/lib1.c @@ -0,0 +1,11 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib2(void) { + printf("hello world from c\n"); +} + +void c_func(void) { + from_lib1(); +} diff --git a/test cases/rust/27 objects/lib1.h b/test cases/rust/27 objects/lib1.h new file mode 100644 index 000000000000..8bb18d4bbe4e --- /dev/null +++ b/test cases/rust/27 objects/lib1.h @@ -0,0 +1,4 @@ +#pragma once + +void from_lib2(void); +void c_func(void); diff --git a/test cases/rust/27 objects/lib2.c b/test cases/rust/27 objects/lib2.c new file mode 100644 index 000000000000..a61d5349f878 --- /dev/null +++ b/test cases/rust/27 objects/lib2.c @@ -0,0 +1,8 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib1(void) +{ + from_lib2(); +} diff --git a/test cases/rust/27 objects/lib2.h b/test cases/rust/27 objects/lib2.h new file mode 100644 index 000000000000..08c4cd30ad1e --- /dev/null +++ b/test cases/rust/27 objects/lib2.h @@ -0,0 +1,3 @@ +#pragma once + +void from_lib1(void); diff --git a/test cases/rust/27 objects/main.rs b/test cases/rust/27 objects/main.rs new file mode 100644 index 000000000000..538359943271 --- /dev/null +++ b/test cases/rust/27 objects/main.rs @@ -0,0 +1,9 @@ +extern "C" { + fn c_func(); +} + +fn main() { + unsafe { + c_func(); + } +} diff --git a/test cases/rust/27 objects/meson.build b/test cases/rust/27 objects/meson.build new file mode 100644 index 000000000000..738a0998971e --- /dev/null +++ b/test cases/rust/27 objects/meson.build @@ -0,0 +1,18 @@ +project('staticlib group', 'c', 'rust', meson_version: '>=1.8.0') + +lib1 = static_library('lib1', 'lib1.c') +dep1 = declare_dependency(objects: lib1.extract_all_objects(recursive: false)) +lib2 = static_library('lib2', 'lib2.c') +dep2 = declare_dependency(objects: lib2.extract_all_objects(recursive: false)) +executable('lib1objs', 'main.rs', + objects: lib1.extract_all_objects(recursive: false), + link_with: lib2) +executable('lib2objs', 'main.rs', + objects: lib2.extract_all_objects(recursive: false), + link_with: lib1) +executable('lib1objs_as_dep', 'main.rs', + dependencies: dep1, + link_with: lib2) +executable('lib2objs_as_dep', 'main.rs', + dependencies: dep2, + link_with: lib1) From 8bab16792ef6f38687cbd100cad67091e0e2d22c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 10:37:34 +0100 Subject: [PATCH 452/624] rust: add shared library testcase for "objects" Signed-off-by: Paolo Bonzini --- test cases/rust/27 objects/lib1-dylib.rs | 15 +++++++++++++++ test cases/rust/27 objects/meson.build | 10 ++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test cases/rust/27 objects/lib1-dylib.rs diff --git a/test cases/rust/27 objects/lib1-dylib.rs b/test cases/rust/27 objects/lib1-dylib.rs new file mode 100644 index 000000000000..1dbf61422ea4 --- /dev/null +++ b/test cases/rust/27 objects/lib1-dylib.rs @@ -0,0 +1,15 @@ +extern "C" { + fn from_lib1(); +} + +#[no_mangle] +extern "C" fn from_lib2() +{ + println!("hello world from rust"); +} + +#[no_mangle] +pub extern "C" fn c_func() +{ + unsafe { from_lib1(); } +} diff --git a/test cases/rust/27 objects/meson.build b/test cases/rust/27 objects/meson.build index 738a0998971e..78373e4b6535 100644 --- a/test cases/rust/27 objects/meson.build +++ b/test cases/rust/27 objects/meson.build @@ -16,3 +16,13 @@ executable('lib1objs_as_dep', 'main.rs', executable('lib2objs_as_dep', 'main.rs', dependencies: dep2, link_with: lib1) + +lib12 = shared_library('dylib2objs', 'lib1-dylib.rs', + objects: lib2.extract_all_objects(recursive: false), + rust_abi: 'c') +executable('dylib', 'main.rs', link_with: lib12) + +lib12 = shared_library('dylib2objs_as_dep', 'lib1-dylib.rs', + dependencies: dep2, + rust_abi: 'c') +executable('dylib_as_dep', 'main.rs', link_with: lib12) From a02857ca6db697955359e71bc0547ecdc641a812 Mon Sep 17 00:00:00 2001 From: Paul Caprioli Date: Sun, 30 Mar 2025 11:57:22 -0700 Subject: [PATCH 453/624] templates: Fix style by adding space after if --- mesonbuild/templates/cpptemplates.py | 4 ++-- mesonbuild/templates/ctemplates.py | 4 ++-- mesonbuild/templates/cudatemplates.py | 4 ++-- mesonbuild/templates/javatemplates.py | 4 ++-- mesonbuild/templates/objcpptemplates.py | 4 ++-- mesonbuild/templates/objctemplates.py | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index eead3cf05f0b..1bfa2ae4fa25 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -15,7 +15,7 @@ #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << "takes no arguments.\\n"; return 1; }} @@ -97,7 +97,7 @@ class {utoken}_PUBLIC {class_name} {{ #include int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << " takes no arguments.\\n"; return 1; }} diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index 7cf04f6aa9c8..559cef91b2bf 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -48,7 +48,7 @@ #include int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ printf("%s takes no arguments.\\n", argv[0]); return 1; }} @@ -113,7 +113,7 @@ #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ printf("%s takes no arguments.\\n", argv[0]); return 1; }} diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index 416ba376a05f..252f44a276a9 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -16,7 +16,7 @@ #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << " takes no arguments.\\n"; return 1; }} @@ -98,7 +98,7 @@ class {utoken}_PUBLIC {class_name} {{ #include int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << " takes no arguments.\\n"; return 1; }} diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index a2b3ec4dcf0f..c30c7f7b5224 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -13,7 +13,7 @@ final static String PROJECT_NAME = "{project_name}"; public static void main (String args[]) {{ - if(args.length != 0) {{ + if (args.length != 0) {{ System.out.println(args + " takes no arguments."); System.exit(0); }} @@ -62,7 +62,7 @@ public class {class_test} {{ public static void main (String args[]) {{ - if(args.length != 0) {{ + if (args.length != 0) {{ System.out.println(args + " takes no arguments."); System.exit(1); }} diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 45e70c6c93cb..1fdfa06a4fa9 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -48,7 +48,7 @@ #import int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << " takes no arguments." << std::endl; return 1; }} @@ -113,7 +113,7 @@ #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ std::cout << argv[0] << " takes no arguments." << std::endl; return 1; }} diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index 0c7891fdb25e..5603bae3bd76 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -48,7 +48,7 @@ #import int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ printf("%s takes no arguments.\\n", argv[0]); return 1; }} @@ -112,7 +112,7 @@ #define PROJECT_NAME "{project_name}" int main(int argc, char **argv) {{ - if(argc != 1) {{ + if (argc != 1) {{ printf("%s takes no arguments.\\n", argv[0]); return 1; }} From 1dde32681a88e6272b53b62adfb18d12926be3e3 Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Wed, 2 Apr 2025 22:21:09 +0300 Subject: [PATCH 454/624] tests/d: Limit integer debug and version statements Since dmd frontend version 2.111 integer debug and version statements error during parsing: https://dlang.org/changelog/2.111.0.html#dmd.deprecation-version-debug-number --- test cases/d/9 features/app.d | 28 ++++++++++++++++++++++------ test cases/d/9 features/meson.build | 15 +++++++++------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/test cases/d/9 features/app.d b/test cases/d/9 features/app.d index ae59be139d42..e7faec1d648a 100644 --- a/test cases/d/9 features/app.d +++ b/test cases/d/9 features/app.d @@ -1,4 +1,4 @@ - +import std.conv; import std.stdio; import std.array : split; import std.string : strip; @@ -16,6 +16,22 @@ auto getPeople () return import ("people.txt").strip.split ("\n"); } +// Put these in templates to prevent the compiler from failing to parse them +// since frontend version 2.111 +template VersionInt (int v) { + mixin(`version(` ~ v.to!string ~ `) + enum VersionInt = true; + else + enum VersionInt = false;`); +} +template DebugInt (int v) { + import std.conv; + mixin(`debug(` ~ v.to!string ~ `) + enum DebugInt = true; + else + enum DebugInt = false;`); +} + void main (string[] args) { import std.array : join; @@ -43,13 +59,13 @@ void main (string[] args) } version (With_VersionInteger) - version(3) exit(0); + static if (VersionInt!3) exit(0); version (With_Debug) debug exit(0); version (With_DebugInteger) - debug(3) exit(0); + static if (DebugInt!(3)) exit(0); version (With_DebugIdentifier) debug(DebugIdentifier) exit(0); @@ -57,9 +73,9 @@ void main (string[] args) version (With_DebugAll) { int dbg = 0; debug dbg++; - debug(2) dbg++; - debug(3) dbg++; - debug(4) dbg++; + static if (DebugInt!2) dbg++; + static if (DebugInt!3) dbg++; + static if (DebugInt!4) dbg++; debug(DebugIdentifier) dbg++; if (dbg == 5) diff --git a/test cases/d/9 features/meson.build b/test cases/d/9 features/meson.build index 065ef3a6ddef..736ce7535fa3 100644 --- a/test cases/d/9 features/meson.build +++ b/test cases/d/9 features/meson.build @@ -2,13 +2,16 @@ project('D Features', 'd', meson_version: '>=1.6', default_options : ['debug=fal dc = meson.get_compiler('d') -# GDC 13 hard errors if options are given number values. -# https://github.com/mesonbuild/meson/pull/11996 - -if dc.get_id() == 'gcc' and dc.version().version_compare('>=13') - number_options_supported = false +# See: https://dlang.org/changelog/2.111.0.html#dmd.deprecation-version-debug-number +# GDC fails even before that: https://github.com/mesonbuild/meson/pull/11996 +if dc.get_id() == 'gcc' + number_options_supported = dc.version().version_compare('<13') +elif dc.get_id() == 'dmd' + number_options_supported = dc.version().version_compare('<2.111') +elif dc.get_id() == 'llvm' + number_options_supported = dc.version().version_compare('<1.41') else - number_options_supported = true + error(f'Unknown D compiler id') endif # ONLY FOR BACKWARDS COMPATIBILITY. From 4bc6b2701c39f88ba013c37856f1f90d4bbc9dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20L=C3=A4hteenm=C3=A4ki?= Date: Fri, 28 Mar 2025 11:53:14 +0200 Subject: [PATCH 455/624] cmake/interpreter: Don't add __CMake_build to includes Don't add "//__CMake_build" directory to include directories. Fixes #12351 --- mesonbuild/cmake/interpreter.py | 3 +-- test cases/cmake/28 include directories/main.c | 9 +++++++++ .../cmake/28 include directories/meson.build | 17 +++++++++++++++++ .../subprojects/cmTest/CMakeLists.txt | 6 ++++++ .../subprojects/cmTest/include/cmTest.h | 3 +++ .../subprojects/cmTest/src/cmTest.c | 7 +++++++ 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 test cases/cmake/28 include directories/main.c create mode 100644 test cases/cmake/28 include directories/meson.build create mode 100644 test cases/cmake/28 include directories/subprojects/cmTest/CMakeLists.txt create mode 100644 test cases/cmake/28 include directories/subprojects/cmTest/include/cmTest.h create mode 100644 test cases/cmake/28 include directories/subprojects/cmTest/src/cmTest.c diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 9cfb4fac2d6d..929627661e7e 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -432,9 +432,8 @@ def rel_path(x: Path, is_header: bool, is_generated: bool) -> T.Optional[Path]: def non_optional(inputs: T.Iterable[T.Optional[Path]]) -> T.List[Path]: return [p for p in inputs if p is not None] - build_dir_rel = self.build_dir.relative_to(Path(self.env.get_build_dir()) / subdir) self.generated_raw = non_optional(rel_path(x, False, True) for x in self.generated_raw) - self.includes = non_optional(itertools.chain((rel_path(x, True, False) for x in OrderedSet(self.includes)), [build_dir_rel])) + self.includes = non_optional(itertools.chain((rel_path(x, True, False) for x in OrderedSet(self.includes)))) self.sys_includes = non_optional(rel_path(x, True, False) for x in OrderedSet(self.sys_includes)) self.sources = non_optional(rel_path(x, False, False) for x in self.sources) diff --git a/test cases/cmake/28 include directories/main.c b/test cases/cmake/28 include directories/main.c new file mode 100644 index 000000000000..bb7fe2966e18 --- /dev/null +++ b/test cases/cmake/28 include directories/main.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + cmTestFunc(); + return 0; +} + diff --git a/test cases/cmake/28 include directories/meson.build b/test cases/cmake/28 include directories/meson.build new file mode 100644 index 000000000000..e2c92d70f069 --- /dev/null +++ b/test cases/cmake/28 include directories/meson.build @@ -0,0 +1,17 @@ +project('include directories test', 'c') + +cm = import('cmake') + +sub_pro = cm.subproject('cmTest') +sub_dep = sub_pro.dependency('cmTest') + +missing_inc_dir_arg = '-Werror=missing-include-dirs' +has_missing_inc_dir = meson.get_compiler('c').has_argument(missing_inc_dir_arg) +if not has_missing_inc_dir + error('MESON_SKIP_TEST: Compiler does not support ' + missing_inc_dir_arg) +else + add_project_arguments(missing_inc_dir_arg, language: 'c') +endif + +exe1 = executable('exe1', ['main.c'], dependencies: [sub_dep]) +test('test1', exe1) diff --git a/test cases/cmake/28 include directories/subprojects/cmTest/CMakeLists.txt b/test cases/cmake/28 include directories/subprojects/cmTest/CMakeLists.txt new file mode 100644 index 000000000000..66ea3cb309e8 --- /dev/null +++ b/test cases/cmake/28 include directories/subprojects/cmTest/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION ${CMAKE_VERSION}) + +project(cmTest C) + +add_library(cmTest src/cmTest.c) +target_include_directories(cmTest PUBLIC include) diff --git a/test cases/cmake/28 include directories/subprojects/cmTest/include/cmTest.h b/test cases/cmake/28 include directories/subprojects/cmTest/include/cmTest.h new file mode 100644 index 000000000000..3af821cc37c3 --- /dev/null +++ b/test cases/cmake/28 include directories/subprojects/cmTest/include/cmTest.h @@ -0,0 +1,3 @@ +#pragma once + +void cmTestFunc(void); diff --git a/test cases/cmake/28 include directories/subprojects/cmTest/src/cmTest.c b/test cases/cmake/28 include directories/subprojects/cmTest/src/cmTest.c new file mode 100644 index 000000000000..f77b694cc58e --- /dev/null +++ b/test cases/cmake/28 include directories/subprojects/cmTest/src/cmTest.c @@ -0,0 +1,7 @@ +#include "include/cmTest.h" +#include + +void cmTestFunc(void) +{ + printf ("Hello\n"); +} From 1da15c2809798e49ee0b6a839b4f94a9394127a5 Mon Sep 17 00:00:00 2001 From: Sam James Date: Sun, 6 Apr 2025 05:58:13 +0100 Subject: [PATCH 456/624] docs: say '--wrapper', not '--wrap' for tests We document --wrapper and --wrap works only by expanding it automatically to --wrapper as it is unambiguous. --- docs/markdown/Unit-tests.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index e89f393a2cae..ab93b715f54a 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -223,16 +223,16 @@ Meson will set the `MESON_TEST_ITERATION` environment variable to the current iteration of the test *(added 1.5.0)*. Invoking tests via a helper executable such as Valgrind can be done with the -`--wrap` argument +`--wrapper` argument ```console -$ meson test --wrap=valgrind testname +$ meson test --wrapper=valgrind testname ``` Arguments to the wrapper binary can be given like this: ```console -$ meson test --wrap='valgrind --tool=helgrind' testname +$ meson test --wrapper='valgrind --tool=helgrind' testname ``` Meson also supports running the tests under GDB. Just doing this: From 4e1e8762da85870e85d2f2ad6673fc855c234b60 Mon Sep 17 00:00:00 2001 From: Emil Berg Date: Sun, 6 Apr 2025 08:11:42 +0200 Subject: [PATCH 457/624] Add C# to home page supported language list --- docs/markdown/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/index.md b/docs/markdown/index.md index 8827c8df4734..cbb79018fc84 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -17,7 +17,7 @@ code. ## Features * multiplatform support for Linux, macOS, Windows, GCC, Clang, Visual Studio and others -* supported languages include C, C++, D, Fortran, Java, Rust +* supported languages include C, C++, C#, D, Fortran, Java, Rust * build definitions in a very readable and user friendly non-Turing complete DSL * cross compilation for many operating systems as well as bare metal * optimized for extremely fast full and incremental builds without sacrificing correctness From 78213e653ff9240969d673165f89304c805f806f Mon Sep 17 00:00:00 2001 From: Sam James Date: Wed, 2 Apr 2025 01:31:58 +0100 Subject: [PATCH 458/624] ci: opensuse: handle libSDL2-devel going away See https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/thread/BJO756KHSCBPDMXVGFMGPHPUMW4PZK6T/#SLVEVFEFOCT3R5RCSVZPQN3GZSZYAEXL. Use sdl2-compat-devel instead. I did try 'pkgconfig(sdl2)' but it failed with some resolver error I didn't probe further. --- ci/ciimage/opensuse/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 43b0f7c9bfed..7a76071e5d60 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -9,7 +9,7 @@ pkgs=( ninja make git autoconf automake patch libjpeg-devel elfutils gcc gcc-c++ gcc-fortran gcc-objc gcc-obj-c++ vala rust bison flex curl lcov mono-core gtkmm3-devel gtest gmock protobuf-devel wxGTK3-3_2-devel gobject-introspection-devel - itstool gtk3-devel java-17-openjdk-devel gtk-doc llvm-devel clang-devel libSDL2-devel graphviz-devel zlib-devel zlib-devel-static + itstool gtk3-devel java-17-openjdk-devel gtk-doc llvm-devel clang-devel sdl2-compat-devel graphviz-devel zlib-devel zlib-devel-static #hdf5-devel netcdf-devel libscalapack2-openmpi3-devel libscalapack2-gnu-openmpi3-hpc-devel openmpi3-devel doxygen vulkan-devel vulkan-validationlayers openssh mercurial libpcap-devel libgpgme-devel libqt5-qtbase-devel libqt5-qttools-devel libqt5-linguist libqt5-qtbase-private-headers-devel From 66329e6f55e8b6427f7531f6db440573723e1a8e Mon Sep 17 00:00:00 2001 From: Sam James Date: Wed, 2 Apr 2025 19:07:48 +0100 Subject: [PATCH 459/624] ci: arch: workaround cmake-4 vs wxwidgets Workaround for cmake-4.0 vs wxwidgets-gtk2 by setting CMAKE_POLICY_VERSION_MINIMUM=3.5. --- ci/ciimage/arch/install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh index a6ef5e0a918e..6b39a0ee7d2d 100755 --- a/ci/ciimage/arch/install.sh +++ b/ci/ciimage/arch/install.sh @@ -24,6 +24,9 @@ cleanup_pkgs=(go) AUR_USER=docker PACMAN_OPTS='--needed --noprogressbar --noconfirm' +# Workaround for cmake-4.0 vs wxwidgets-gtk2 +export CMAKE_POLICY_VERSION_MINIMUM=3.5 + # Patch config files sed -i 's/#Color/Color/g' /etc/pacman.conf sed -i 's,#MAKEFLAGS="-j2",MAKEFLAGS="-j$(nproc)",g' /etc/makepkg.conf From 9150c2a68a9d432714db04614f416358a48f7e73 Mon Sep 17 00:00:00 2001 From: Sam James Date: Thu, 3 Apr 2025 04:49:17 +0100 Subject: [PATCH 460/624] ci: arch: add vulkan-headers and vulkan-icd-loader The latter is needed for vulkan.pc now, but let's add vulkan-headers explicitly while at it. --- ci/ciimage/arch/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh index 6b39a0ee7d2d..de43cb2ea467 100755 --- a/ci/ciimage/arch/install.sh +++ b/ci/ciimage/arch/install.sh @@ -12,7 +12,7 @@ pkgs=( libelf gcc gcc-fortran gcc-objc vala rust bison 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-validation-layers openssh mercurial gtk-sharp-2 qt5-tools + doxygen vulkan-headers vulkan-icd-loader vulkan-validation-layers openssh mercurial gtk-sharp-2 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 # cuda From 23d60e930555acc58ebe860ba2cd913b770ee128 Mon Sep 17 00:00:00 2001 From: Heikki Rauhala Date: Mon, 7 Apr 2025 09:42:12 +0300 Subject: [PATCH 461/624] Fix link to "Building Fancy DMG Images on Mac OS X" --- docs/markdown/Creating-OSX-packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Creating-OSX-packages.md b/docs/markdown/Creating-OSX-packages.md index 7e0d05bf42ca..f31249f3f7d3 100644 --- a/docs/markdown/Creating-OSX-packages.md +++ b/docs/markdown/Creating-OSX-packages.md @@ -151,7 +151,7 @@ More information is available on the tool's documentation page. A .dmg installer is similarly quite simple, at its core it is basically a fancy compressed archive. A good description can be found -on [this page](https://el-tramo.be/guides/fancy-dmg/). Please read it +on [this page](https://mko.re/blog/fancy-dmg/). Please read it and create a template image file according to its instructions. The actual process of creating the installer is very simple: you mount From 00864ca481c4e29d7f5c648a1e12b934dfa642d2 Mon Sep 17 00:00:00 2001 From: Emil Berg Date: Sun, 6 Apr 2025 10:17:55 +0200 Subject: [PATCH 462/624] Fix documentation typos --- docs/markdown/Build-options.md | 2 +- docs/markdown/Builtin-options.md | 6 +++--- docs/markdown/FAQ.md | 2 +- docs/markdown/IDE-integration.md | 2 +- docs/markdown/Include-directories.md | 2 +- docs/markdown/Qt6-module.md | 2 +- docs/markdown/Unit-tests.md | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md index d615db6891b0..190705d77802 100644 --- a/docs/markdown/Build-options.md +++ b/docs/markdown/Build-options.md @@ -222,7 +222,7 @@ a colon: $ meson configure -Dsubproject:option=newvalue ``` -**NOTE:** If you cannot call `meson configure` you likely have a old +**NOTE:** If you cannot call `meson configure` you likely have an old version of Meson. In that case you can call `mesonconf` instead, but that is deprecated in newer versions diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 56e30885cde5..faf7a6088d06 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -76,7 +76,7 @@ machine](#specifying-options-per-machine) section for details. | -------------------------------------- | ------------- | ----------- | -------------- | ----------------- | | auto_features {enabled, disabled, auto} | auto | Override value of all 'auto' features | no | no | | backend {ninja, vs,
vs2010, vs2012, vs2013, vs2015, vs2017, vs2019, vs2022, xcode, none} | ninja | Backend to use | no | no | -| genvslite {vs2022} | vs2022 | Setup multi-builtype ninja build directories and Visual Studio solution | no | no | +| genvslite {vs2022} | vs2022 | Setup multi-buildtype ninja build directories and Visual Studio solution | no | no | | buildtype {plain, debug,
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 | @@ -115,7 +115,7 @@ for a lighter automated build pipeline. Setup multiple buildtype-suffixed, ninja-backend build directories (e.g. [builddir]_[debug/release/etc.]) and generate [builddir]_vs containing a Visual Studio solution with multiple configurations that invoke a meson compile of the -setup build directories, as appropriate for the current configuration (builtype). +setup build directories, as appropriate for the current configuration (buildtype). This has the effect of a simple setup macro of multiple 'meson setup ...' invocations with a set of different buildtype values. E.g. @@ -152,7 +152,7 @@ All other combinations of `debug` and `optimization` set `buildtype` to `'custom #### Details for `warning_level` -Exact flags per warning level is compiler specific, but there is an approximative +Exact flags per warning level is compiler specific, but there is an approximate table for most common compilers. | Warning level | GCC/Clang | MSVC | diff --git a/docs/markdown/FAQ.md b/docs/markdown/FAQ.md index 9daf9ae98298..faa4becbe130 100644 --- a/docs/markdown/FAQ.md +++ b/docs/markdown/FAQ.md @@ -285,7 +285,7 @@ or they are not called (due to e.g. `if/else`) then nothing is downloaded. If this is not sufficient for you, starting from release 0.40.0 Meson -has a option called `wrap-mode` which can be used to disable wrap +has an option called `wrap-mode` which can be used to disable wrap downloads altogether with `--wrap-mode=nodownload`. You can also disable dependency fallbacks altogether with `--wrap-mode=nofallback`, which also implies the `nodownload` option. diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index ce8d8b42e548..a8e9197f785c 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -10,7 +10,7 @@ this problem, Meson provides an API that makes it easy for any IDE or build tools to integrate Meson builds and provide an experience comparable to a solution native to the IDE. -All the resources required for such a IDE integration can be found in +All the resources required for such an IDE integration can be found in the `meson-info` directory in the build directory. The first thing to do when setting up a Meson project in an IDE is to diff --git a/docs/markdown/Include-directories.md b/docs/markdown/Include-directories.md index f9850ac91451..8b0b420f1dc7 100644 --- a/docs/markdown/Include-directories.md +++ b/docs/markdown/Include-directories.md @@ -7,7 +7,7 @@ short-description: Instructions on handling include directories Most `C`/`C++` projects have headers in different directories than sources. Thus you need to specify include directories. Let's assume that we are at some subdirectory and wish to add its `include` -subdirectory to some target's search path. To create a include +subdirectory to some target's search path. To create an include directory object we do this: ```meson diff --git a/docs/markdown/Qt6-module.md b/docs/markdown/Qt6-module.md index f3b3a32a01f9..ebe42a50cc8d 100644 --- a/docs/markdown/Qt6-module.md +++ b/docs/markdown/Qt6-module.md @@ -69,7 +69,7 @@ It takes no positional arguments, and the following keyword arguments: directory. For instance, when a file called `subdir/one.input` is processed it generates a file `{target private directory}/subdir/one.out` when `true`, and `{target private directory}/one.out` when `false` (default). - - `output_json` bool: *New in 1.7.0*. If `true`, generates additionnaly a + - `output_json` bool: *New in 1.7.0*. If `true`, generates additionally a JSON representation which may be used by external tools such as qmltyperegistrar ## preprocess diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index ab93b715f54a..9654bc4fc099 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -312,7 +312,7 @@ For further information see the command line help of Meson by running ## Legacy notes -If `meson test` does not work for you, you likely have a old version +If `meson test` does not work for you, you likely have an old version of Meson. In that case you should call `mesontest` instead. If `mesontest` doesn't work either you have a very old version prior to 0.37.0 and should upgrade. From 390ea4624c2fbfecf831f1c7f34ec796ff410de7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 12 Mar 2025 11:02:07 -0700 Subject: [PATCH 463/624] coredata: move MutableKeyedOptionDict to options --- mesonbuild/compilers/c.py | 2 +- mesonbuild/compilers/compilers.py | 2 +- mesonbuild/compilers/cpp.py | 2 +- mesonbuild/compilers/cuda.py | 4 ++-- mesonbuild/compilers/cython.py | 4 ++-- mesonbuild/compilers/fortran.py | 2 +- mesonbuild/compilers/mixins/clang.py | 2 +- mesonbuild/compilers/mixins/emscripten.py | 3 +-- mesonbuild/compilers/mixins/gnu.py | 4 ++-- mesonbuild/compilers/objc.py | 4 ++-- mesonbuild/compilers/objcpp.py | 4 ++-- mesonbuild/compilers/rust.py | 4 ++-- mesonbuild/compilers/swift.py | 2 +- mesonbuild/coredata.py | 3 +-- mesonbuild/mconf.py | 17 +++++++++-------- mesonbuild/mintro.py | 8 ++++---- mesonbuild/optinterpreter.py | 3 +-- mesonbuild/options.py | 1 + 18 files changed, 35 insertions(+), 36 deletions(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index a6769fc96606..7a2fec59e6a6 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -37,7 +37,7 @@ ) if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 89eceab75286..a3b243dd9b51 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -26,7 +26,7 @@ if T.TYPE_CHECKING: from .. import coredata from ..build import BuildTarget, DFeatures - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..envconfig import MachineInfo from ..environment import Environment from ..linkers import RSPFileSyntax diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index b85751d05043..01b9bb9fa34f 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -34,7 +34,7 @@ from .mixins.metrowerks import mwccarm_instruction_set_args, mwcceppc_instruction_set_args if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 509044cd444b..6cc6f963b1cf 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2017 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -19,7 +19,7 @@ if T.TYPE_CHECKING: from ..build import BuildTarget - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..dependencies import Dependency from ..environment import Environment # noqa: F401 from ..envconfig import MachineInfo diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index 27cad5502f90..50bb4652b06a 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright © 2021-2024 Intel Corporation +# Copyright © 2021-2025 Intel Corporation from __future__ import annotations """Abstraction for Cython language compilers.""" @@ -11,7 +11,7 @@ from .compilers import Compiler if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..environment import Environment from ..build import BuildTarget diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 0bbd7d0ef9dd..5794db06bb18 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -27,7 +27,7 @@ ) if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 867b58680209..ae5ab631bdca 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -18,7 +18,7 @@ from .gnu import GnuLikeCompiler if T.TYPE_CHECKING: - from ...coredata import MutableKeyedOptionDictType + from ...options import MutableKeyedOptionDictType from ...environment import Environment from ...dependencies import Dependency # noqa: F401 from ..compilers import Compiler diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py index c5b2e6daceef..91b25e8f7971 100644 --- a/mesonbuild/compilers/mixins/emscripten.py +++ b/mesonbuild/compilers/mixins/emscripten.py @@ -15,7 +15,6 @@ from mesonbuild.compilers.compilers import CompileCheckMode if T.TYPE_CHECKING: - from ... import coredata from ...environment import Environment from ...compilers.compilers import Compiler from ...dependencies import Dependency @@ -57,7 +56,7 @@ def thread_link_flags(self, env: 'Environment') -> T.List[str]: args.append(f'-sPTHREAD_POOL_SIZE={count}') return args - def get_options(self) -> coredata.MutableKeyedOptionDictType: + def get_options(self) -> options.MutableKeyedOptionDictType: opts = super().get_options() key = OptionKey(f'{self.language}_thread_count', machine=self.for_machine) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 70fd9ee7d8d2..9ea591e04aad 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2019-2022 The meson development team -# Copyright © 2023 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -21,7 +21,7 @@ if T.TYPE_CHECKING: from ..._typing import ImmutableListProtocol - from ...coredata import MutableKeyedOptionDictType + from ...options import MutableKeyedOptionDictType from ...environment import Environment from ..compilers import Compiler else: diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index c36373f434b2..d013417fccd3 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -15,12 +15,12 @@ from .mixins.gnu import GnuCompiler, GnuCStds, gnu_common_warning_args, gnu_objc_warning_args if T.TYPE_CHECKING: - from .. import coredata from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice from ..build import BuildTarget + from ..options import MutableKeyedOptionDictType class ObjCCompiler(CLikeCompiler, Compiler): @@ -36,7 +36,7 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_ linker=linker) CLikeCompiler.__init__(self) - def get_options(self) -> coredata.MutableKeyedOptionDictType: + def get_options(self) -> MutableKeyedOptionDictType: opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index b1cb605e31ca..441428b2fa5a 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -15,12 +15,12 @@ from .mixins.clike import CLikeCompiler if T.TYPE_CHECKING: - from .. import coredata from ..envconfig import MachineInfo from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice from ..build import BuildTarget + from ..options import MutableKeyedOptionDictType class ObjCPPCompiler(CLikeCompiler, Compiler): @@ -54,7 +54,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: code = '#import\nclass MyClass;int main(void) { return 0; }\n' return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code) - def get_options(self) -> coredata.MutableKeyedOptionDictType: + def get_options(self) -> MutableKeyedOptionDictType: opts = super().get_options() key = self.form_compileropt_key('std') opts.update({ diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 53b3afbf2a14..3e9c016f6529 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2012-2022 The Meson development team -# Copyright © 2023-2024 Intel Corporation +# Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -16,7 +16,7 @@ from .compilers import Compiler, CompileCheckMode, clike_debug_args if T.TYPE_CHECKING: - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..envconfig import MachineInfo from ..environment import Environment # noqa: F401 from ..linkers.linkers import DynamicLinker diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index c28e7af25e4f..8410fbbda77e 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -14,7 +14,7 @@ if T.TYPE_CHECKING: from .. import build - from ..coredata import MutableKeyedOptionDictType + from ..options import MutableKeyedOptionDictType from ..dependencies import Dependency from ..envconfig import MachineInfo from ..environment import Environment diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 03d80d940d85..4791fe1280ca 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -43,7 +43,7 @@ from .mesonlib import FileOrString from .cmake.traceparser import CMakeCacheEntry from .interpreterbase import SubProject - from .options import ElementaryOptionValues + from .options import ElementaryOptionValues, MutableKeyedOptionDictType from .build import BuildTarget class SharedCMDOptions(Protocol): @@ -62,7 +62,6 @@ class SharedCMDOptions(Protocol): native_file: T.List[str] OptionDictType = T.Dict[str, options.AnyOptionType] - MutableKeyedOptionDictType = T.Dict['OptionKey', options.AnyOptionType] CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode] # code, args RunCheckCacheKey = T.Tuple[str, T.Tuple[str, ...]] diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 0811062f371e..cf21622326cf 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -190,9 +190,10 @@ def wrap_text(text: LOGLINE, width: int) -> mlog.TV_LoggableList: items = [l[i] if l[i] else ' ' * four_column[i] for i in range(4)] mlog.log(*items) - def split_options_per_subproject(self, options: T.Union[coredata.MutableKeyedOptionDictType, options.OptionStore]) -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: - result: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = {} - for k, o in options.items(): + def split_options_per_subproject(self, opts: T.Union[options.MutableKeyedOptionDictType, options.OptionStore] + ) -> T.Dict[str, options.MutableKeyedOptionDictType]: + result: T.Dict[str, options.MutableKeyedOptionDictType] = {} + for k, o in opts.items(): if k.subproject: self.all_subprojects.add(k.subproject) result.setdefault(k.subproject, {})[k] = o @@ -228,7 +229,7 @@ def add_section(self, section: str) -> None: self._add_line(mlog.normal_yellow(section + ':'), '', '', '') self.print_margin = 2 - def print_options(self, title: str, opts: T.Union[coredata.MutableKeyedOptionDictType, options.OptionStore]) -> None: + def print_options(self, title: str, opts: T.Union[options.MutableKeyedOptionDictType, options.OptionStore]) -> None: if not opts: return if title: @@ -264,10 +265,10 @@ def print_default_values_warning() -> None: test_option_names = {OptionKey('errorlogs'), OptionKey('stdsplit')} - dir_options: 'coredata.MutableKeyedOptionDictType' = {} - test_options: 'coredata.MutableKeyedOptionDictType' = {} - core_options: 'coredata.MutableKeyedOptionDictType' = {} - module_options: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = collections.defaultdict(dict) + dir_options: options.MutableKeyedOptionDictType = {} + test_options: options.MutableKeyedOptionDictType = {} + core_options: options.MutableKeyedOptionDictType = {} + module_options: T.Dict[str, options.MutableKeyedOptionDictType] = collections.defaultdict(dict) for k, v in self.coredata.optstore.options.items(): if k in dir_option_names: dir_options[k] = v diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 9b2a01f4c9ab..462ee2fb4673 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -290,9 +290,9 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s test_option_names = {OptionKey('errorlogs'), OptionKey('stdsplit')} - dir_options: 'cdata.MutableKeyedOptionDictType' = {} - test_options: 'cdata.MutableKeyedOptionDictType' = {} - core_options: 'cdata.MutableKeyedOptionDictType' = {} + dir_options: options.MutableKeyedOptionDictType = {} + test_options: options.MutableKeyedOptionDictType = {} + core_options: options.MutableKeyedOptionDictType = {} for k, v in coredata.optstore.items(): if k in dir_option_names: dir_options[k] = v @@ -304,7 +304,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s for s in subprojects: core_options[k.evolve(subproject=s)] = v - def add_keys(opts: T.Union[cdata.MutableKeyedOptionDictType, options.OptionStore], section: str) -> None: + def add_keys(opts: T.Union[options.MutableKeyedOptionDictType, options.OptionStore], section: str) -> None: for key, opt in sorted(opts.items()): optdict = {'name': str(key), 'value': opt.value, 'section': section, 'machine': key.machine.get_lower_case_name() if coredata.optstore.is_per_machine_option(key) else 'any'} diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index c33306d5cdec..892d4d5e3786 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -15,7 +15,6 @@ from .interpreter.type_checking import NoneType, in_set_validator if T.TYPE_CHECKING: - from . import coredata from .interpreterbase import TYPE_var, TYPE_kwargs from .interpreterbase import SubProject from typing_extensions import TypedDict, Literal @@ -67,7 +66,7 @@ class OptionException(mesonlib.MesonException): class OptionInterpreter: def __init__(self, optionstore: 'OptionStore', subproject: 'SubProject') -> None: - self.options: 'coredata.MutableKeyedOptionDictType' = {} + self.options: options.MutableKeyedOptionDictType = {} self.subproject = subproject self.option_types: T.Dict[str, T.Callable[..., options.AnyOptionType]] = { 'string': self.string_parser, diff --git a/mesonbuild/options.py b/mesonbuild/options.py index f45c121a8044..3ad4f91556bf 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -41,6 +41,7 @@ 'UserIntegerOption', 'UserStdOption', 'UserStringArrayOption', 'UserStringOption', 'UserUmaskOption'] ElementaryOptionValues: TypeAlias = T.Union[str, int, bool, T.List[str]] + MutableKeyedOptionDictType: TypeAlias = T.Dict['OptionKey', AnyOptionType] _OptionKeyTuple: TypeAlias = T.Tuple[T.Optional[str], MachineChoice, str] From 90d6a3e61100e39ab74ff7b2b4bc456af5a8114c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 12 Mar 2025 11:04:53 -0700 Subject: [PATCH 464/624] coredata: move update_project_options to the optstore --- mesonbuild/coredata.py | 29 ------------------ mesonbuild/interpreterbase/interpreterbase.py | 2 +- mesonbuild/mconf.py | 6 ++-- mesonbuild/options.py | 30 +++++++++++++++++++ 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 4791fe1280ca..ba57821ccb71 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -15,7 +15,6 @@ import textwrap from .mesonlib import ( - MesonBugException, MesonException, MachineChoice, PerMachine, PerMachineDefaultable, default_prefix, @@ -499,34 +498,6 @@ def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.Lis linkkey = OptionKey(f'{lang}_link_args', machine=for_machine) return T.cast('T.List[str]', self.optstore.get_value_for(linkkey)) - def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None: - for key, value in project_options.items(): - if key not in self.optstore: - self.optstore.add_project_option(key, value) - continue - if key.subproject != subproject: - raise MesonBugException(f'Tried to set an option for subproject {key.subproject} from {subproject}!') - - oldval = self.optstore.get_value_object(key) - if type(oldval) is not type(value): - self.optstore.set_option(key, value.value) - elif options.choices_are_different(oldval, value): - # 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.optstore.set_value_object(key, value) - try: - value.set_value(oldval.value) - except MesonException: - mlog.warning(f'Old value(s) of {key} are no longer valid, resetting to default ({value.value}).', - fatal=False) - - # Find any extranious keys for this project and remove them - potential_removed_keys = self.optstore.keys() - project_options.keys() - for key in potential_removed_keys: - if self.optstore.is_project_option(key) and key.subproject == subproject: - self.optstore.remove(key) - def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool: if when_building_for == MachineChoice.BUILD: return False diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 2bdb5ef2e3d8..b13bbae1a5a9 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -704,7 +704,7 @@ def _load_option_file(self) -> None: self.coredata.options_files[self.subproject] = (option_file, hashlib.sha1(f.read()).hexdigest()) oi = optinterpreter.OptionInterpreter(self.environment.coredata.optstore, self.subproject) oi.process(option_file) - self.coredata.update_project_options(oi.options, self.subproject) + self.coredata.optstore.update_project_options(oi.options, self.subproject) self.build_def_files.add(option_file) else: self.coredata.options_files[self.subproject] = None diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index cf21622326cf..416caf1a0e59 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -96,7 +96,7 @@ def __init__(self, build_dir: str): if ophash != conf_options[1]: oi = OptionInterpreter(self.coredata.optstore, sub) oi.process(opfile) - self.coredata.update_project_options(oi.options, sub) + self.coredata.optstore.update_project_options(oi.options, sub) self.coredata.options_files[sub] = (opfile, ophash) else: opfile = os.path.join(self.source_dir, 'meson.options') @@ -105,12 +105,12 @@ def __init__(self, build_dir: str): if os.path.exists(opfile): oi = OptionInterpreter(self.coredata.optstore, sub) oi.process(opfile) - self.coredata.update_project_options(oi.options, sub) + self.coredata.optstore.update_project_options(oi.options, sub) with open(opfile, 'rb') as f: ophash = hashlib.sha1(f.read()).hexdigest() self.coredata.options_files[sub] = (opfile, ophash) else: - self.coredata.update_project_options({}, sub) + self.coredata.optstore.update_project_options({}, sub) elif os.path.isfile(os.path.join(self.build_dir, environment.build_filename)): # Make sure that log entries in other parts of meson don't interfere with the JSON output with mlog.no_logging(): diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 3ad4f91556bf..4339f77734f7 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -35,6 +35,8 @@ if T.TYPE_CHECKING: from typing_extensions import Literal, Final, TypeAlias, TypedDict + from .interpreterbase import SubProject + DeprecatedType: TypeAlias = T.Union[bool, str, T.Dict[str, str], T.List[str]] AnyOptionType: TypeAlias = T.Union[ 'UserBooleanOption', 'UserComboOption', 'UserFeatureOption', @@ -1402,3 +1404,31 @@ def initialize_from_subproject_call(self, self.set_option(key, valstr, is_first_invocation) else: self.augments[str(key)] = valstr + + def update_project_options(self, project_options: MutableKeyedOptionDictType, subproject: SubProject) -> None: + for key, value in project_options.items(): + if key not in self.options: + self.add_project_option(key, value) + continue + if key.subproject != subproject: + raise MesonBugException(f'Tried to set an option for subproject {key.subproject} from {subproject}!') + + oldval = self.get_value_object(key) + if type(oldval) is not type(value): + self.set_option(key, value.value) + elif choices_are_different(oldval, value): + # 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) + try: + value.set_value(oldval.value) + except MesonException: + mlog.warning(f'Old value(s) of {key} are no longer valid, resetting to default ({value.value}).', + fatal=False) + + # Find any extranious keys for this project and remove them + potential_removed_keys = self.options.keys() - project_options.keys() + for key in potential_removed_keys: + if self.is_project_option(key) and key.subproject == subproject: + self.remove(key) From 4c1d1da9b6a1e3f144d26dbc73f9d02f4f7f7e97 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 2 Apr 2025 11:15:26 -0700 Subject: [PATCH 465/624] options: fix typing issues stemming from initialize_from_top_level_project_call Machine files provide a `Mapping[OptionKey, ElementaryOptionValues]`, but the expectation listed was that they provided options in the raw DSL format. --- mesonbuild/options.py | 20 ++++++++++++++------ unittests/optiontests.py | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 4339f77734f7..8a7b7ec3b4e0 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -6,6 +6,7 @@ from collections import OrderedDict from itertools import chain import argparse +import copy import dataclasses import itertools import os @@ -1236,21 +1237,28 @@ def prefix_split_options(self, coll: T.Union[T.List[str], OptionStringLikeDict]) def first_handle_prefix(self, project_default_options: T.Union[T.List[str], OptionStringLikeDict], cmd_line_options: T.Union[T.List[str], OptionStringLikeDict], - machine_file_options: T.Union[T.List[str], OptionStringLikeDict]) \ + machine_file_options: T.Mapping[OptionKey, ElementaryOptionValues]) \ -> T.Tuple[T.Union[T.List[str], OptionStringLikeDict], T.Union[T.List[str], OptionStringLikeDict], - T.Union[T.List[str], OptionStringLikeDict]]: + T.MutableMapping[OptionKey, ElementaryOptionValues]]: + # Copy to avoid later mutation + nopref_machine_file_options = T.cast( + 'T.MutableMapping[OptionKey, ElementaryOptionValues]', copy.copy(machine_file_options)) + prefix = None (possible_prefix, nopref_project_default_options) = self.prefix_split_options(project_default_options) prefix = prefix if possible_prefix is None else possible_prefix - (possible_prefix, nopref_native_file_options) = self.prefix_split_options(machine_file_options) - prefix = prefix if possible_prefix is None else possible_prefix + + possible_prefixv = nopref_machine_file_options.pop(OptionKey('prefix'), None) + assert possible_prefixv is None or isinstance(possible_prefixv, str), 'mypy: prefix from machine file was not a string?' + prefix = prefix if possible_prefixv is None else possible_prefixv + (possible_prefix, nopref_cmd_line_options) = self.prefix_split_options(cmd_line_options) prefix = prefix if possible_prefix is None else possible_prefix if prefix is not None: self.hard_reset_from_prefix(prefix) - return (nopref_project_default_options, nopref_cmd_line_options, nopref_native_file_options) + return (nopref_project_default_options, nopref_cmd_line_options, nopref_machine_file_options) def hard_reset_from_prefix(self, prefix: str) -> None: prefix = self.sanitize_prefix(prefix) @@ -1268,7 +1276,7 @@ def hard_reset_from_prefix(self, prefix: str) -> None: def initialize_from_top_level_project_call(self, project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], cmd_line_options_in: T.Union[T.List[str], OptionStringLikeDict], - machine_file_options_in: T.Union[T.List[str], OptionStringLikeDict]) -> None: + machine_file_options_in: T.Mapping[OptionKey, ElementaryOptionValues]) -> None: first_invocation = True (project_default_options, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in, cmd_line_options_in, diff --git a/unittests/optiontests.py b/unittests/optiontests.py index d02119a064c0..3b3ffc98eff5 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -32,7 +32,7 @@ def test_toplevel_project(self): vo = UserStringOption(k.name, 'An option of some sort', 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([f'someoption={new_value}'], {}, {}) + optstore.initialize_from_top_level_project_call({OptionKey('someoption'): new_value}, {}, {}) self.assertEqual(optstore.get_value_for(k), new_value) def test_parsing(self): From 768712cf32e82ee9e82e6d567f2a39677b48e46c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 4 Apr 2025 10:00:19 -0700 Subject: [PATCH 466/624] options: fix an assertion that is incorrect The key can be an OptionKey, particularly in the next patch. --- mesonbuild/options.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 8a7b7ec3b4e0..2f7663af33cc 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1323,8 +1323,10 @@ def initialize_from_top_level_project_call(self, # # The key parsing function can not handle the difference between the two # and defaults to A. - assert isinstance(keystr, str) - key = OptionKey.from_string(keystr) + if isinstance(keystr, str): + key = OptionKey.from_string(keystr) + else: + key = keystr # Due to backwards compatibility we ignore all cross options when building # natively. if not self.is_cross and key.is_for_build(): From 9d1837e1afc919d18645c8fd6788cf44ac7d078d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 2 Apr 2025 09:19:41 -0700 Subject: [PATCH 467/624] tests: Add a rewriter test that handles prefix "prefix" has a lot of special handling that has to go on because so many other options depend on prefix. It was also regressed by the option refactor changes, so having a test feels appropriate. This was verified against the 1.7 release, so it has the same behavior as pre-option refactor Meson. --- test cases/rewrite/7 prefix/meson.build | 5 +++++ unittests/rewritetests.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test cases/rewrite/7 prefix/meson.build diff --git a/test cases/rewrite/7 prefix/meson.build b/test cases/rewrite/7 prefix/meson.build new file mode 100644 index 000000000000..563d7d7c9547 --- /dev/null +++ b/test cases/rewrite/7 prefix/meson.build @@ -0,0 +1,5 @@ +project('lalala', 'cpp', + default_options : [ + 'prefix=/export/doocs', + ], +) diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index bfb270f04a7a..dfd69c83a30c 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -407,3 +407,18 @@ def test_raw_printer_is_idempotent(self): # Do it line per line because it is easier to debug like that for orig_line, new_line in zip_longest(original_contents.splitlines(), new_contents.splitlines()): self.assertEqual(orig_line, new_line) + + @unittest.expectedFailure + def test_rewrite_prefix(self) -> None: + self.prime('7 prefix') + out = self.rewrite_raw(self.builddir, ['kwargs', 'info', 'project', '/']) + expected = { + 'kwargs': { + 'project#/': { + "default_options": [ + 'prefix=/export/doocs' + ] + } + } + } + self.assertDictEqual(out, expected) From aa8f9229fc76e5d89da04495f3b188f4438de32e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 12 Mar 2025 11:14:28 -0700 Subject: [PATCH 468/624] coredata: delete set_default_options This was only being used by the introspection interpreter, which meant the two interpreters had different behavior. They still do, which is really bad because the IntrospectionInterpreter doesn't have the command line options or project_default_options. I have a plan to fix that later, and I don't want to spend time on it here, as it's not a regression of this patch, it's just the status quo. This also fixes an issue caused by dead code being left, and hit, due to the option refactor branch. Fixes: #14382 --- mesonbuild/ast/introspection.py | 14 +++++++++- mesonbuild/coredata.py | 45 --------------------------------- unittests/rewritetests.py | 1 - 3 files changed, 13 insertions(+), 47 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 0d6470adccfc..4eb3fec3e352 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -119,7 +119,19 @@ def _str_list(node: T.Any) -> T.Optional[T.List[str]]: 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) - self.coredata.set_default_options(self.default_options, self.subproject, self.environment) + 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'] diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index ba57821ccb71..f165fde1065f 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -565,51 +565,6 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' return dirty - def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None: - # Main project can set default options on subprojects, but subprojects - # can only set default options on themselves. - # Preserve order: if env.options has 'buildtype' it must come after - # 'optimization' if it is in default_options. - options_: T.MutableMapping[OptionKey, T.Any] = OrderedDict() - for k, v in default_options.items(): - if isinstance(k, str): - k = OptionKey.from_string(k) - if not subproject or k.subproject == subproject: - options_[k] = v - options_.update(env.options) - env.options = options_ - - # Create a subset of options, keeping only project and builtin - # options for this subproject. - # Language and backend specific options will be set later when adding - # languages and setting the backend (builtin options must be set first - # to know which backend we'll use). - options_ = OrderedDict() - - for k, v in env.options.items(): - if isinstance(k, str): - k = OptionKey.from_string(k) - # If this is a subproject, don't use other subproject options - if k.subproject and k.subproject != subproject: - continue - # If the option is a builtin and is yielding then it's not allowed per subproject. - # - # Always test this using the HOST machine, as many builtin options - # are not valid for the BUILD machine, but the yielding value does - # not differ between them even when they are valid for both. - if subproject and self.optstore.is_builtin_option(k) and self.optstore.get_value_object(k.evolve(subproject=None, machine=MachineChoice.HOST)).yielding: - continue - # Skip base, compiler, and backend options, they are handled when - # adding languages and setting backend. - if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k): - continue - if self.optstore.is_base_option(k) and k.evolve(subproject=None) in options.COMPILER_BASE_OPTIONS: - # set_options will report unknown base options - continue - options_[k] = v - - self.set_options(options_, subproject=subproject, first_invocation=env.first_invocation) - def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, env: Environment, subproject: str) -> None: for k, o in c_options.items(): diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index dfd69c83a30c..169c0350cc3c 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -408,7 +408,6 @@ def test_raw_printer_is_idempotent(self): for orig_line, new_line in zip_longest(original_contents.splitlines(), new_contents.splitlines()): self.assertEqual(orig_line, new_line) - @unittest.expectedFailure def test_rewrite_prefix(self) -> None: self.prime('7 prefix') out = self.rewrite_raw(self.builddir, ['kwargs', 'info', 'project', '/']) From 237513dffd0c946e0a284f484aef553dc75b7572 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Mon, 10 Feb 2025 15:47:39 -0300 Subject: [PATCH 469/624] modules/gnome, modules/Python: Allow injecting RPATH flags through LDFLAGS if needed Fixes communicating the RPATH to g-i-scanner in macOS. See #14169 --- mesonbuild/dependencies/python.py | 1 + mesonbuild/modules/gnome.py | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index ca02d6743090..3dab31c12f8e 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -333,6 +333,7 @@ def __init__(self, name: str, environment: 'Environment', # 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] class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase): diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 926017a334b9..67641339a2ee 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -704,14 +704,14 @@ def _get_dependencies_flags_raw( lib_dir = os.path.dirname(flag) external_ldflags.update([f'-L{lib_dir}']) if include_rpath: - external_ldflags.update([f'-Wl,-rpath {lib_dir}']) + external_ldflags.update([f'-Wl,-rpath,{lib_dir}']) libname = os.path.basename(flag) if libname.startswith("lib"): libname = libname[3:] libname = libname.split(".so")[0] flag = f"-l{libname}" # FIXME: Hack to avoid passing some compiler options in - if flag.startswith("-W"): + if flag.startswith("-W") and not flag.startswith('-Wl,-rpath,'): continue # If it's a framework arg, slurp the framework name too # to preserve the order of arguments @@ -964,6 +964,7 @@ def _make_gir_target( scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], generated_files: T.Sequence[T.Union[str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList]], depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], + env_flags: T.Sequence[str], kwargs: T.Dict[str, T.Any]) -> GirTarget: install = kwargs['install_gir'] if install is None: @@ -984,6 +985,7 @@ def _make_gir_target( # g-ir-scanner uses Python's distutils to find the compiler, which uses 'CC' cc_exelist = state.environment.coredata.compilers.host['c'].get_exelist() run_env.set('CC', [quote_arg(x) for x in cc_exelist], ' ') + run_env.set('CFLAGS', [quote_arg(x) for x in env_flags], ' ') run_env.merge(kwargs['env']) return GirTarget( @@ -1090,11 +1092,12 @@ def _get_scanner_cflags(cflags: T.Iterable[str]) -> T.Iterable[str]: yield f @staticmethod - def _get_scanner_ldflags(ldflags: T.Iterable[str]) -> T.Iterable[str]: + def _get_scanner_ldflags(ldflags: T.Iterable[str]) -> tuple[list[str], list[str]]: 'g-ir-scanner only accepts -L/-l; must ignore -F and other linker flags' - for f in ldflags: - if f.startswith(('-L', '-l', '--extra-library')): - yield f + return ( + [f for f in ldflags if f.startswith(('-L', '-l', '--extra-library'))], + [f for f in ldflags if f.startswith(('-Wl,-rpath,'))], + ) @typed_pos_args('gnome.generate_gir', varargs=(Executable, build.SharedLibrary, build.StaticLibrary), min_varargs=1) @typed_kwargs( @@ -1164,11 +1167,14 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut scan_cflags += list(self._get_scanner_cflags(dep_cflags)) 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_internal_ldflags += list(self._get_scanner_ldflags(internal_ldflags)) - scan_internal_ldflags += list(self._get_scanner_ldflags(dep_internal_ldflags)) scan_external_ldflags = [] - scan_external_ldflags += list(self._get_scanner_ldflags(external_ldflags)) - scan_external_ldflags += list(self._get_scanner_ldflags(dep_external_ldflags)) + scan_env_ldflags = [] + 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 + 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 girtargets_inc_dirs = self._get_gir_targets_inc_dirs(girtargets) inc_dirs = kwargs['include_directories'] @@ -1222,7 +1228,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut generated_files = [f for f in libsources if isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex))] scan_target = self._make_gir_target( - state, girfile, scan_command, generated_files, depends, + state, girfile, scan_command, generated_files, depends, scan_env_ldflags, # We have to cast here because mypy can't figure this out T.cast('T.Dict[str, T.Any]', kwargs)) From f044c6ef69ef6a07a148db4596317c1d297d72bb Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Tue, 11 Feb 2025 14:27:36 -0300 Subject: [PATCH 470/624] gnome: Add a test consuming Python --- .../7 gnome/gir/meson-python-sample.c | 51 +++++++++++++++++++ .../7 gnome/gir/meson-python-sample.def | 4 ++ .../7 gnome/gir/meson-python-sample.h | 17 +++++++ test cases/frameworks/7 gnome/gir/meson.build | 41 +++++++++++++-- test cases/frameworks/7 gnome/meson.build | 7 ++- .../7 gnome/resources-data/meson.build | 4 +- test cases/frameworks/7 gnome/test.json | 1 + 7 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 test cases/frameworks/7 gnome/gir/meson-python-sample.c create mode 100644 test cases/frameworks/7 gnome/gir/meson-python-sample.def create mode 100644 test cases/frameworks/7 gnome/gir/meson-python-sample.h diff --git a/test cases/frameworks/7 gnome/gir/meson-python-sample.c b/test cases/frameworks/7 gnome/gir/meson-python-sample.c new file mode 100644 index 000000000000..0ab7439d023e --- /dev/null +++ b/test cases/frameworks/7 gnome/gir/meson-python-sample.c @@ -0,0 +1,51 @@ +#include "meson-python-sample.h" + +#include + +struct _MesonPythonSample +{ + GObject parent_instance; +}; + +G_DEFINE_TYPE (MesonPythonSample, meson_python_sample, G_TYPE_OBJECT) + +/** + * meson_python_sample_new: + * + * Allocates a new #MesonPythonSample. + * + * Returns: (transfer full): a #MesonPythonSample. + */ +MesonPythonSample * +meson_python_sample_new (void) +{ + return g_object_new (MESON_TYPE_PYTHON_SAMPLE, NULL); +} + +static void +meson_python_sample_class_init (MesonPythonSampleClass *klass) +{ + if (!Py_IsInitialized ()) { + Py_Initialize (); + Py_Finalize (); + } +} + +static void +meson_python_sample_init (MesonPythonSample *self) +{ +} + +/** + * meson_python_sample_print_message: + * @self: a #MesonSample2. + * + * Prints Hello. + * + * Returns: Nothing. + */ +void +meson_python_sample_print_message (MesonPythonSample *self) +{ + g_print ("Message: Hello\n"); +} diff --git a/test cases/frameworks/7 gnome/gir/meson-python-sample.def b/test cases/frameworks/7 gnome/gir/meson-python-sample.def new file mode 100644 index 000000000000..c5542b96bebd --- /dev/null +++ b/test cases/frameworks/7 gnome/gir/meson-python-sample.def @@ -0,0 +1,4 @@ +EXPORTS + meson_python_sample_new + meson_python_sample_print_message + meson_python_sample_get_type diff --git a/test cases/frameworks/7 gnome/gir/meson-python-sample.h b/test cases/frameworks/7 gnome/gir/meson-python-sample.h new file mode 100644 index 000000000000..6dab2f7b8ef4 --- /dev/null +++ b/test cases/frameworks/7 gnome/gir/meson-python-sample.h @@ -0,0 +1,17 @@ +#ifndef MESON_PYTHON_SAMPLE_H +#define MESON_PYTHON_SAMPLE_H + +#include + +G_BEGIN_DECLS + +#define MESON_TYPE_PYTHON_SAMPLE (meson_python_sample_get_type()) + +G_DECLARE_FINAL_TYPE (MesonPythonSample, meson_python_sample, MESON, SAMPLE, GObject) + +MesonPythonSample *meson_python_sample_new (void); +void meson_python_sample_print_message (MesonPythonSample *self); + +G_END_DECLS + +#endif /* MESON_PYTHON_SAMPLE_H */ diff --git a/test cases/frameworks/7 gnome/gir/meson.build b/test cases/frameworks/7 gnome/gir/meson.build index 70db496b8b2b..b02a80618a8a 100644 --- a/test cases/frameworks/7 gnome/gir/meson.build +++ b/test cases/frameworks/7 gnome/gir/meson.build @@ -2,6 +2,7 @@ subdir('dep1') libsources = ['meson-sample.c', 'meson-sample.h'] lib2sources = ['meson-sample2.c', 'meson-sample2.h'] +pythonsources = ['meson-python-sample.c', 'meson-python-sample.h'] gen_source = custom_target( 'meson_sample3.h', @@ -26,6 +27,23 @@ girlib2 = shared_library( install : true ) +if get_option('b_sanitize') == 'none' + py3_dep = py3.dependency(embed: true) +else + warning('Python 3 test not supported with b_sanitize') + py3_dep = disabler() +endif + +if py3_dep.found() + pythongirlib = shared_library( + 'python_gir_lib', + sources: pythonsources, + dependencies: [gobj, py3_dep], + vs_module_defs: 'meson-python-sample.def', + install: true + ) +endif + girexe = executable( 'girprog', sources : 'prog.c', @@ -36,17 +54,30 @@ girexe = executable( fake_dep = dependency('no-way-this-exists', required: false) +# g-ir-scanner ignores CFLAGS for MSVC +flags_dep_for_msvc = declare_dependency( + compile_args: ['-DMESON_TEST_2'] +) + +girs = [girlib, girlib2] +girs_sources = [libsources, lib2sources, gen_source] +# dep1_dep pulls in dep2_dep for us +girs_deps = [fake_dep, dep1_dep, flags_dep_for_msvc] +if py3_dep.found() + girs += [pythongirlib] + girs_sources += [pythonsources] + girs_deps += [py3_dep] +endif + gnome.generate_gir( - girlib, girlib2, - sources : [libsources, lib2sources, gen_source], - env : {'CPPFLAGS': '-DMESON_TEST_2'}, + girs, + sources : girs_sources, nsversion : '1.0', namespace : 'Meson', symbol_prefix : 'meson', identifier_prefix : 'Meson', includes : ['GObject-2.0', 'MesonDep1-1.0'], - # dep1_dep pulls in dep2_dep for us - dependencies : [[fake_dep, dep1_dep]], + dependencies : girs_deps, doc_format: 'gtk-doc-markdown', install : true, build_by_default : true, diff --git a/test cases/frameworks/7 gnome/meson.build b/test cases/frameworks/7 gnome/meson.build index 4d54e774b121..f75ca93a105e 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') +project('gobject-introspection', 'c', meson_version: '>= 1.2.0') copyfile = find_program('copyfile.py') copyfile_gen = generator(copyfile, @@ -15,8 +15,8 @@ if not gir.found() error('MESON_SKIP_TEST gobject-introspection not found.') endif -python3 = import('python3') -py3 = python3.find_python() +python3 = import('python') +py3 = python3.find_installation() if run_command(py3, '-c', 'import gi;', check: false).returncode() != 0 error('MESON_SKIP_TEST python3-gi not found') endif @@ -30,7 +30,6 @@ if cc.get_id() == 'intel' add_global_arguments('-wd2282', language : 'c') endif -py3 = import('python3').find_python() pycode = '''import os, sys if "MESON_UNIT_TEST_PRETEND_GLIB_OLD" in os.environ: sys.exit(0) diff --git a/test cases/frameworks/7 gnome/resources-data/meson.build b/test cases/frameworks/7 gnome/resources-data/meson.build index 31a577b2ed93..bb251b7cad37 100644 --- a/test cases/frameworks/7 gnome/resources-data/meson.build +++ b/test cases/frameworks/7 gnome/resources-data/meson.build @@ -1,7 +1,5 @@ subdir('subdir') -python3 = import('python3').find_python() - fake_generator_script = ''' import os, sys assert os.path.exists(sys.argv[1]), "File %s not found" % sys.argv[1] @@ -13,6 +11,6 @@ print("This is a generated resource.") res3_txt = custom_target('res3', input: 'res3.txt.in', output: 'res3.txt', - command: [python3, '-c', fake_generator_script, '@INPUT@'], + command: [py3, '-c', fake_generator_script, '@INPUT@'], capture: true, ) diff --git a/test cases/frameworks/7 gnome/test.json b/test cases/frameworks/7 gnome/test.json index 77c9bdfb5216..0d3bead150fa 100644 --- a/test cases/frameworks/7 gnome/test.json +++ b/test cases/frameworks/7 gnome/test.json @@ -12,6 +12,7 @@ {"type": "file", "file": "usr/include/subdir-3/marshaller-3.h"}, {"type": "file", "file": "usr/include/subdir-4/marshaller-4.h"}, {"type": "file", "file": "usr/include/subdir-5/marshaller-5.h"}, + {"type": "expr", "file": "usr/lib/?libpython_gir_lib.so"}, {"type": "expr", "file": "usr/lib/?libgir_lib.so"}, {"type": "file", "platform": "cygwin", "file": "usr/lib/libgir_lib.dll.a"}, {"type": "expr", "file": "usr/lib/?libgir_lib2.so"}, From edd746dc4271312c95a7a085d235ec98881b7800 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 12:26:46 +0200 Subject: [PATCH 471/624] options: make cmd_line_options handling more similar to the rest The structure of the "if" statements in the cmd_line_options iteration is a bit different from the others due to the need to raise an error for some kinds of unknown options. Reduce the dissimilarity by checking key.subproject is None just before raising the exception. Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 2f7663af33cc..82fe88b16c38 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1357,10 +1357,10 @@ def initialize_from_top_level_project_call(self, continue if key in self.options: self.set_option(key, valstr, True) - elif key.subproject is None: - projectkey = key.as_root() - if projectkey in self.options: - self.options[projectkey].set_value(valstr) + else: + proj_key = key.as_root() + if proj_key in self.options: + self.options[proj_key].set_value(valstr) else: # Fail on unknown options that we can know must # exist at this point in time. Subproject and compiler @@ -1368,11 +1368,9 @@ def initialize_from_top_level_project_call(self, # # Some base options (sanitizers etc) might get added later. # Permitting them all is not strictly correct. - if not self.is_compiler_option(key) and not self.is_base_option(key): + if key.subproject is None and not self.is_compiler_option(key) and not self.is_base_option(key): raise MesonException(f'Unknown options: "{keystr}"') self.pending_options[key] = valstr - else: - self.pending_options[key] = valstr def hacky_mchackface_back_to_list(self, optdict: T.Dict[str, str]) -> T.List[str]: if isinstance(optdict, dict): From d66c6c126673096e5d6be10332b88f47ea2c41ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 12:27:42 +0200 Subject: [PATCH 472/624] options: fix incorrect comment Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 82fe88b16c38..bb4795e5a481 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1294,8 +1294,8 @@ def initialize_from_top_level_project_call(self, key = OptionKey.from_string(keystr) else: key = keystr - # Due to backwards compatibility we ignore all cross options when building - # natively. + # 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 is not None: @@ -1327,8 +1327,8 @@ def initialize_from_top_level_project_call(self, key = OptionKey.from_string(keystr) else: key = keystr - # Due to backwards compatibility we ignore all cross options when building - # natively. + # 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 is not None: @@ -1351,8 +1351,8 @@ def initialize_from_top_level_project_call(self, key = OptionKey.from_string(keystr) else: key = keystr - # Due to backwards compatibility we ignore all cross options when building - # natively. + # 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 in self.options: From 677c98bb60c1e3d2289fd37a1c43efc09b1d50eb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 12:37:11 +0200 Subject: [PATCH 473/624] options: go through set_option for non-:-prefixed options Allow proper handling of deprecated and read-only options, which is missing from set_value. Fixes: #14433 Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index bb4795e5a481..124a4afdbae2 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1307,7 +1307,7 @@ def initialize_from_top_level_project_call(self, else: proj_key = key.as_root() if proj_key in self.options: - self.options[proj_key].set_value(valstr) + self.set_option(proj_key, valstr, first_invocation) else: self.pending_options[key] = valstr assert isinstance(project_default_options, dict) @@ -1360,7 +1360,7 @@ def initialize_from_top_level_project_call(self, else: proj_key = key.as_root() if proj_key in self.options: - self.options[proj_key].set_value(valstr) + self.set_option(proj_key, valstr, True) else: # Fail on unknown options that we can know must # exist at this point in time. Subproject and compiler From b578f7035e8123be5ba0fc2afdbcacd34b67049f Mon Sep 17 00:00:00 2001 From: Marvin Scholz Date: Wed, 2 Apr 2025 00:13:44 +0200 Subject: [PATCH 474/624] tests: enhance deprecated options test This actually tests the handling of deprecated options when passed to meson instead of just as default_options. Signed-off-by: Paolo Bonzini --- .../common/247 deprecated option/meson.build | 6 +++ .../247 deprecated option/meson_options.txt | 8 +++ .../common/247 deprecated option/test.json | 50 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/test cases/common/247 deprecated option/meson.build b/test cases/common/247 deprecated option/meson.build index 4c734eefbed3..60e6f9650d0f 100644 --- a/test cases/common/247 deprecated option/meson.build +++ b/test cases/common/247 deprecated option/meson.build @@ -11,10 +11,16 @@ project('deprecated options', ) assert(get_option('o1') == false) +assert(get_option('c1') == false) assert(get_option('o2') == ['a', 'b']) +assert(get_option('c2') == ['a', 'b']) assert(get_option('o3') == ['c', 'b']) +assert(get_option('c3') == ['c', 'b']) assert(get_option('o4').enabled()) +assert(get_option('c4').enabled()) assert(get_option('o5') == false) +assert(get_option('c5') == false) assert(get_option('o6') == false) +assert(get_option('c6') == false) assert(get_option('o7').disabled()) assert(get_option('python.platlibdir') == '/foo') diff --git a/test cases/common/247 deprecated option/meson_options.txt b/test cases/common/247 deprecated option/meson_options.txt index 88cd8aa5175e..6e8d14cbb2f6 100644 --- a/test cases/common/247 deprecated option/meson_options.txt +++ b/test cases/common/247 deprecated option/meson_options.txt @@ -1,23 +1,31 @@ # Option fully deprecated, it warns when any value is set. option('o1', type: 'boolean', deprecated: true) +option('c1', type: 'boolean', deprecated: true) # One of the choices is deprecated, it warns only when 'a' is in the list of values. option('o2', type: 'array', choices: ['a', 'b'], deprecated: ['a']) +option('c2', type: 'array', choices: ['a', 'b'], deprecated: ['a']) # One of the choices is deprecated, it warns only when 'a' is in the list of values # and replace it by 'c'. option('o3', type: 'array', choices: ['a', 'b', 'c'], deprecated: {'a': 'c'}) +option('c3', type: 'array', choices: ['a', 'b', 'c'], deprecated: {'a': 'c'}) # A boolean option has been replaced by a feature, old true/false values are remapped. option('o4', type: 'feature', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('c4', type: 'feature', deprecated: {'true': 'enabled', 'false': 'disabled'}) # A feature option has been replaced by a boolean, enabled/disabled/auto values are remapped. option('o5', type: 'boolean', deprecated: {'enabled': 'true', 'disabled': 'false', 'auto': 'false'}) +option('c5', type: 'boolean', deprecated: {'enabled': 'true', 'disabled': 'false', 'auto': 'false'}) # A boolean option has been replaced by a feature with another name, old true/false values # are accepted by the new option for backward compatibility. option('o6', type: 'boolean', value: true, deprecated: 'o7') +option('c6', type: 'boolean', value: true, deprecated: 'o7') + option('o7', type: 'feature', value: 'enabled', deprecated: {'true': 'enabled', 'false': 'disabled'}) # A project option is replaced by a module option option('o8', type: 'string', value: '', deprecated: 'python.platlibdir') +option('c8', type: 'string', value: '', deprecated: 'python.platlibdir') diff --git a/test cases/common/247 deprecated option/test.json b/test cases/common/247 deprecated option/test.json index c2f2ca325f2a..99b596669c24 100644 --- a/test cases/common/247 deprecated option/test.json +++ b/test cases/common/247 deprecated option/test.json @@ -1,4 +1,29 @@ { + "matrix": { + "options": { + "c1": [ + { "val": "false" } + ], + "c2": [ + { "val": "a,b" } + ], + "c3": [ + { "val": "a,b" } + ], + "c4": [ + { "val": "true" } + ], + "c5": [ + { "val": "auto" } + ], + "c6": [ + { "val": "false" } + ], + "c8": [ + { "val": "/foo" } + ] + } + }, "stdout": [ { "line": ".*DEPRECATION: Option 'o1' is deprecated", @@ -24,6 +49,31 @@ "line": ".*DEPRECATION: Option 'o5' value 'auto' is replaced by 'false'", "match": "re", "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'c1' is deprecated", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'c2' value 'a' is deprecated", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'c3' value 'a' is replaced by 'c'", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'c4' value 'true' is replaced by 'enabled'", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'c5' value 'auto' is replaced by 'false'", + "match": "re", + "count": 1 } ] } From a3c41c6a896ed49b157ccc830e1bc665581097b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 12:42:21 +0200 Subject: [PATCH 475/624] options: always look up root-project options in self.options Previously, root-project options (key.subproject == '') would always be stored as an augment or as pending_project_options. But they might actually be included in self.options instead, so exclude them from the initial test. Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 124a4afdbae2..5fc16f5e12c9 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1298,8 +1298,7 @@ def initialize_from_top_level_project_call(self, # when building natively. if not self.is_cross and key.is_for_build(): continue - if key.subproject is not None: - #self.pending_project_options[key] = valstr + if key.subproject: augstr = str(key) self.augments[augstr] = valstr elif key in self.options: @@ -1331,7 +1330,7 @@ def initialize_from_top_level_project_call(self, # when building natively. if not self.is_cross and key.is_for_build(): continue - if key.subproject is not None: + if key.subproject: self.pending_options[key] = valstr elif key in self.options: self.set_option(key, valstr, first_invocation) @@ -1355,7 +1354,9 @@ def initialize_from_top_level_project_call(self, # when building natively. if not self.is_cross and key.is_for_build(): continue - if key in self.options: + 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() From a4be4cd420cd7000aefb6ff52e85f6f873f1b4a5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2025 13:03:29 +0200 Subject: [PATCH 476/624] tests: add test for deprecated options with project-option syntax Cover ":foo" syntax in default_options as well. Signed-off-by: Paolo Bonzini --- .../common/247 deprecated option/meson.build | 13 ++++++++++ .../247 deprecated option/meson_options.txt | 7 ++++++ .../common/247 deprecated option/test.json | 25 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/test cases/common/247 deprecated option/meson.build b/test cases/common/247 deprecated option/meson.build index 60e6f9650d0f..f311421301f5 100644 --- a/test cases/common/247 deprecated option/meson.build +++ b/test cases/common/247 deprecated option/meson.build @@ -7,20 +7,33 @@ project('deprecated options', 'o5=auto', 'o6=false', 'o8=/foo', + ':p1=false', + ':p2=a,b', + ':p3=a,b', + ':p4=true', + ':p5=auto', + ':p6=false', + ':p8=/foo', ] ) assert(get_option('o1') == false) +assert(get_option('p1') == false) assert(get_option('c1') == false) assert(get_option('o2') == ['a', 'b']) +assert(get_option('p2') == ['a', 'b']) assert(get_option('c2') == ['a', 'b']) assert(get_option('o3') == ['c', 'b']) +assert(get_option('p3') == ['c', 'b']) assert(get_option('c3') == ['c', 'b']) assert(get_option('o4').enabled()) +assert(get_option('p4').enabled()) assert(get_option('c4').enabled()) assert(get_option('o5') == false) +assert(get_option('p5') == false) assert(get_option('c5') == false) assert(get_option('o6') == false) +assert(get_option('p6') == false) assert(get_option('c6') == false) assert(get_option('o7').disabled()) assert(get_option('python.platlibdir') == '/foo') diff --git a/test cases/common/247 deprecated option/meson_options.txt b/test cases/common/247 deprecated option/meson_options.txt index 6e8d14cbb2f6..36fcb914c164 100644 --- a/test cases/common/247 deprecated option/meson_options.txt +++ b/test cases/common/247 deprecated option/meson_options.txt @@ -1,31 +1,38 @@ # Option fully deprecated, it warns when any value is set. option('o1', type: 'boolean', deprecated: true) +option('p1', type: 'boolean', deprecated: true) option('c1', type: 'boolean', deprecated: true) # One of the choices is deprecated, it warns only when 'a' is in the list of values. option('o2', type: 'array', choices: ['a', 'b'], deprecated: ['a']) +option('p2', type: 'array', choices: ['a', 'b'], deprecated: ['a']) option('c2', type: 'array', choices: ['a', 'b'], deprecated: ['a']) # One of the choices is deprecated, it warns only when 'a' is in the list of values # and replace it by 'c'. option('o3', type: 'array', choices: ['a', 'b', 'c'], deprecated: {'a': 'c'}) +option('p3', type: 'array', choices: ['a', 'b', 'c'], deprecated: {'a': 'c'}) option('c3', type: 'array', choices: ['a', 'b', 'c'], deprecated: {'a': 'c'}) # A boolean option has been replaced by a feature, old true/false values are remapped. option('o4', type: 'feature', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('p4', type: 'feature', deprecated: {'true': 'enabled', 'false': 'disabled'}) option('c4', type: 'feature', deprecated: {'true': 'enabled', 'false': 'disabled'}) # A feature option has been replaced by a boolean, enabled/disabled/auto values are remapped. option('o5', type: 'boolean', deprecated: {'enabled': 'true', 'disabled': 'false', 'auto': 'false'}) +option('p5', type: 'boolean', deprecated: {'enabled': 'true', 'disabled': 'false', 'auto': 'false'}) option('c5', type: 'boolean', deprecated: {'enabled': 'true', 'disabled': 'false', 'auto': 'false'}) # A boolean option has been replaced by a feature with another name, old true/false values # are accepted by the new option for backward compatibility. option('o6', type: 'boolean', value: true, deprecated: 'o7') +option('p6', type: 'boolean', value: true, deprecated: 'o7') option('c6', type: 'boolean', value: true, deprecated: 'o7') option('o7', type: 'feature', value: 'enabled', deprecated: {'true': 'enabled', 'false': 'disabled'}) # A project option is replaced by a module option option('o8', type: 'string', value: '', deprecated: 'python.platlibdir') +option('p8', type: 'string', value: '', deprecated: 'python.platlibdir') option('c8', type: 'string', value: '', deprecated: 'python.platlibdir') diff --git a/test cases/common/247 deprecated option/test.json b/test cases/common/247 deprecated option/test.json index 99b596669c24..a644b0405a5c 100644 --- a/test cases/common/247 deprecated option/test.json +++ b/test cases/common/247 deprecated option/test.json @@ -50,6 +50,31 @@ "match": "re", "count": 1 }, + { + "line": ".*DEPRECATION: Option 'p1' is deprecated", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'p2' value 'a' is deprecated", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'p3' value 'a' is replaced by 'c'", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'p4' value 'true' is replaced by 'enabled'", + "match": "re", + "count": 1 + }, + { + "line": ".*DEPRECATION: Option 'p5' value 'auto' is replaced by 'false'", + "match": "re", + "count": 1 + }, { "line": ".*DEPRECATION: Option 'c1' is deprecated", "match": "re", From 43816f664395232bfe536c2aa69d1aa3ee0b04f5 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 12 Nov 2024 08:27:33 -0500 Subject: [PATCH 477/624] fixes for mypy --- mesonbuild/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 27d60c854e47..e94f75faa1f3 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1350,7 +1350,7 @@ def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include def get_source_subdir(self): return self.subdir - def get_sources(self): + def get_sources(self) -> T.List[File]: return self.sources def get_objects(self) -> T.List[T.Union[str, 'File', 'ExtractedObjects']]: @@ -2566,7 +2566,7 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_libra def __repr__(self) -> str: return f'' - def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes: + def get(self, lib_type: T.Literal['static', 'shared']) -> T.Union[StaticLibrary, SharedLibrary]: if lib_type == 'static': return self.static if lib_type == 'shared': From 0c9420205cc132743e5b3788b3a6a87502e79415 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 19 Dec 2024 16:21:56 -0500 Subject: [PATCH 478/624] Move get_rsp_threshold function to mesonlib Because we will need to call it from a module in a followup commit. --- mesonbuild/backend/ninjabackend.py | 21 +-------------------- mesonbuild/utils/universal.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 1ba41bccf17f..8627960e8419 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -92,27 +92,8 @@ def gcc_rsp_quote(s: str) -> str: return quote_func(s) -def get_rsp_threshold() -> int: - '''Return a conservative estimate of the commandline size in bytes - above which a response file should be used. May be overridden for - debugging by setting environment variable MESON_RSP_THRESHOLD.''' - - if mesonlib.is_windows(): - # Usually 32k, but some projects might use cmd.exe, - # and that has a limit of 8k. - limit = 8192 - else: - # Unix-like OSes usually have very large command line limits, (On Linux, - # for example, this is limited by the kernel's MAX_ARG_STRLEN). However, - # some programs place much lower limits, notably Wine which enforces a - # 32k limit like Windows. Therefore, we limit the command line to 32k. - limit = 32768 - # Be conservative - limit = limit // 2 - return int(os.environ.get('MESON_RSP_THRESHOLD', limit)) - # a conservative estimate of the command-line length limit -rsp_threshold = get_rsp_threshold() +rsp_threshold = mesonlib.get_rsp_threshold() # ninja variables whose value should remain unquoted. The value of these ninja # variables (or variables we use them in) is interpreted directly by ninja diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 3481aaa88962..5b3f131af9b3 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -106,6 +106,7 @@ class _VerPickleLoadable(Protocol): 'generate_list', 'get_compiler_for_source', 'get_filenames_templates_dict', + 'get_rsp_threshold', 'get_variable_regex', 'get_wine_shortpath', 'git', @@ -2390,6 +2391,27 @@ def first(iter: T.Iterable[_T], predicate: T.Callable[[_T], bool]) -> T.Optional return None +def get_rsp_threshold() -> int: + '''Return a conservative estimate of the commandline size in bytes + above which a response file should be used. May be overridden for + debugging by setting environment variable MESON_RSP_THRESHOLD.''' + + if is_windows(): + # Usually 32k, but some projects might use cmd.exe, + # and that has a limit of 8k. + limit = 8192 + else: + # Unix-like OSes usually have very large command line limits, (On Linux, + # for example, this is limited by the kernel's MAX_ARG_STRLEN). However, + # some programs place much lower limits, notably Wine which enforces a + # 32k limit like Windows. Therefore, we limit the command line to 32k. + limit = 32768 + + # Be conservative + limit = limit // 2 + return int(os.environ.get('MESON_RSP_THRESHOLD', limit)) + + class lazy_property(T.Generic[_T]): """Descriptor that replaces the function it wraps with the value generated. From 1afdac1bc4cbf9816e7109bbedef2825c4fe1155 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 7 Nov 2024 10:58:14 -0500 Subject: [PATCH 479/624] New xgettext method for i18n module This method call xgettext to extract translatable string from source files into a .pot translation template. It differs from a plain CustomTarget in three ways: - It accepts build targets as sources, and automatically resolves source files from those build targets; - It detects command lines that are too long, and writes, at config time, the list of source files into a text file to be consumed by the xgettext command; - It detects dependencies between pot extraction targets, based on the dependencies between source targets. --- docs/markdown/i18n-module.md | 47 ++++++ docs/markdown/snippets/i18n_xgettext.md | 12 ++ mesonbuild/modules/i18n.py | 155 +++++++++++++++++- .../38 gettext extractor/meson.build | 15 ++ .../38 gettext extractor/src/lib1/lib1.c | 10 ++ .../38 gettext extractor/src/lib1/lib1.h | 6 + .../38 gettext extractor/src/lib1/meson.build | 3 + .../38 gettext extractor/src/lib2/lib2.c | 13 ++ .../38 gettext extractor/src/lib2/lib2.h | 6 + .../38 gettext extractor/src/lib2/meson.build | 3 + .../38 gettext extractor/src/main.c | 8 + .../38 gettext extractor/src/meson.build | 6 + .../frameworks/38 gettext extractor/test.json | 6 + 13 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/i18n_xgettext.md create mode 100644 test cases/frameworks/38 gettext extractor/meson.build create mode 100644 test cases/frameworks/38 gettext extractor/src/lib1/lib1.c create mode 100644 test cases/frameworks/38 gettext extractor/src/lib1/lib1.h create mode 100644 test cases/frameworks/38 gettext extractor/src/lib1/meson.build create mode 100644 test cases/frameworks/38 gettext extractor/src/lib2/lib2.c create mode 100644 test cases/frameworks/38 gettext extractor/src/lib2/lib2.h create mode 100644 test cases/frameworks/38 gettext extractor/src/lib2/meson.build create mode 100644 test cases/frameworks/38 gettext extractor/src/main.c create mode 100644 test cases/frameworks/38 gettext extractor/src/meson.build create mode 100644 test cases/frameworks/38 gettext extractor/test.json diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md index a939a34738b5..da6fce74e875 100644 --- a/docs/markdown/i18n-module.md +++ b/docs/markdown/i18n-module.md @@ -74,3 +74,50 @@ for normal keywords. In addition it accepts these keywords: * `mo_targets` *required*: mo file generation targets as returned by `i18n.gettext()`. *Added 0.62.0* + + +### i18n.xgettext() + +``` meson +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 +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]`: + source files or targets. May be a list of `string`, `File`, [[@build_tgt]], + or [[@custom_tgt]] returned from other calls to this function. + +Keyword arguments are the following: + +- recursive `bool`: + if `true`, will merge the resulting pot file with extracted pot files + related to dependencies of the given source targets. For instance, + if you build an executable, then you may want to merge the executable + translations with the translations from the dependent libraries. +- install `bool`: if `true`, will add the resulting pot file to install targets. +- install_tag `str`: install tag to use for the install target. +- install_dir `str`: directory where to install the resulting pot file. + +The `i18n.xgettext()` function returns a [[@custom_tgt]]. + +Usually, you want to pass one build target as sources, and the list of header files +for that target. If the number of source files would result in a command line that +is too long, the list of source files is written to a file at config time, to be +used as input for the `xgettext` program. + +The `recursive: true` argument is to be given to targets that will actually read +the resulting `.mo` file. Each time you call the `i18n.xgettext()` function, +it maps the source targets to the resulting pot file. When `recursive: true` is +given, all generated pot files from dependencies of the source targets are +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. + +*Added 1.8.0* diff --git a/docs/markdown/snippets/i18n_xgettext.md b/docs/markdown/snippets/i18n_xgettext.md new file mode 100644 index 000000000000..0ad0a14b1f13 --- /dev/null +++ b/docs/markdown/snippets/i18n_xgettext.md @@ -0,0 +1,12 @@ +## i18n module xgettext + +There is a new `xgettext` function in `i18n` module that acts as a +wrapper around `xgettext`. It allows to extract strings to translate from +source files. + +This function is convenient, because: +- It can find the sources files from a build target; +- It will use an intermediate file when the number of source files is too + big to be handled directly from the command line; +- It is able to get strings to translate from the dependencies of the given + targets. diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 2e59b25666f4..87baab203089 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -4,6 +4,7 @@ from __future__ import annotations from os import path +from pathlib import Path import shlex import typing as T @@ -13,7 +14,8 @@ from ..options import OptionKey from .. import mlog from ..interpreter.type_checking import CT_BUILD_BY_DEFAULT, CT_INPUT_KW, INSTALL_TAG_KW, OUTPUT_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType, in_set_validator -from ..interpreterbase import FeatureNew, InvalidArguments +from ..interpreterbase import FeatureNew +from ..interpreterbase.exceptions import InvalidArguments from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, noPosargs, typed_kwargs, typed_pos_args from ..programs import ExternalProgram from ..scripts.gettext import read_linguas @@ -65,6 +67,16 @@ class ItsJoinFile(TypedDict): its_files: T.List[str] mo_targets: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] + class XgettextProgramT(TypedDict): + + args: T.List[str] + recursive: bool + install: bool + install_dir: T.Optional[str] + install_tag: T.Optional[str] + + SourcesType = T.Union[str, mesonlib.File, build.BuildTarget, build.BothLibraries, build.CustomTarget] + _ARGS: KwargInfo[T.List[str]] = KwargInfo( 'args', @@ -115,6 +127,125 @@ class ItsJoinFile(TypedDict): } +class XgettextProgram: + + pot_files: T.Dict[str, build.CustomTarget] = {} + + def __init__(self, xgettext: ExternalProgram, interpreter: Interpreter): + self.xgettext = xgettext + self.interpreter = interpreter + + def extract(self, + name: str, + sources: T.List[SourcesType], + args: T.List[str], + recursive: bool, + install: bool, + install_dir: T.Optional[str], + install_tag: T.Optional[str]) -> build.CustomTarget: + + if not name.endswith('.pot'): + name += '.pot' + + source_files = self._get_source_files(sources) + + command = self.xgettext.command + args + command.append(f'--directory={self.interpreter.environment.get_source_dir()}') + command.append(f'--directory={self.interpreter.environment.get_build_dir()}') + command.append('--output=@OUTPUT@') + + depends = list(self._get_depends(sources)) if recursive else [] + rsp_file = self._get_rsp_file(name, source_files, depends, command) + inputs: T.List[T.Union[mesonlib.File, build.CustomTarget]] + if rsp_file: + inputs = [rsp_file] + depend_files = list(source_files) + command.append('--files-from=@INPUT@') + else: + inputs = list(source_files) + depends + depends = None + depend_files = None + command.append('@INPUT@') + + ct = build.CustomTarget( + '', + self.interpreter.subdir, + self.interpreter.subproject, + self.interpreter.environment, + command, + inputs, + [name], + depend_files = depend_files, + extra_depends = depends, + install = install, + install_dir = [install_dir] if install_dir else None, + install_tag = [install_tag] if install_tag else None, + description = 'Extracting translations to {}', + ) + + for source_id in self._get_source_id(sources): + self.pot_files[source_id] = ct + self.pot_files[ct.get_id()] = ct + + self.interpreter.add_target(ct.name, ct) + return ct + + def _get_source_files(self, sources: T.Iterable[SourcesType]) -> T.Set[mesonlib.File]: + source_files = set() + for source in sources: + if isinstance(source, mesonlib.File): + source_files.add(source) + elif isinstance(source, str): + mesonlib.check_direntry_issues(source) + source_files.add(mesonlib.File.from_source_file(self.interpreter.source_root, self.interpreter.subdir, source)) + elif isinstance(source, build.BuildTarget): + source_files.update(source.get_sources()) + elif isinstance(source, build.BothLibraries): + source_files.update(source.get('shared').get_sources()) + return source_files + + def _get_depends(self, sources: T.Iterable[SourcesType]) -> T.Set[build.CustomTarget]: + depends = set() + for source in sources: + if isinstance(source, build.BuildTarget): + for source_id in self._get_source_id(source.get_dependencies()): + if source_id in self.pot_files: + depends.add(self.pot_files[source_id]) + elif isinstance(source, build.CustomTarget): + # Dependency on another extracted pot file + source_id = source.get_id() + if source_id in self.pot_files: + depends.add(self.pot_files[source_id]) + return depends + + def _get_rsp_file(self, + name: str, + source_files: T.Iterable[mesonlib.File], + depends: T.Iterable[build.CustomTarget], + arguments: T.List[str]) -> T.Optional[mesonlib.File]: + source_list = '\n'.join(source.relative_name() for source in source_files) + for dep in depends: + source_list += '\n' + path.join(dep.subdir, dep.get_filename()) + + estimated_cmdline_length = len(source_list) + sum(len(arg) + 1 for arg in arguments) + 1 + if estimated_cmdline_length < mesonlib.get_rsp_threshold(): + return None + + rsp_file = Path(self.interpreter.environment.build_dir, self.interpreter.subdir, name+'.rsp') + rsp_file.write_text(source_list, encoding='utf-8') + + 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]: + for source in sources: + if isinstance(source, build.Target): + yield source.get_id() + elif isinstance(source, build.BothLibraries): + yield source.get('static').get_id() + yield source.get('shared').get_id() + + class I18nModule(ExtensionModule): INFO = ModuleInfo('i18n') @@ -125,6 +256,7 @@ def __init__(self, interpreter: 'Interpreter'): 'merge_file': self.merge_file, 'gettext': self.gettext, 'itstool_join': self.itstool_join, + 'xgettext': self.xgettext, }) self.tools: T.Dict[str, T.Optional[T.Union[ExternalProgram, build.Executable]]] = { 'itstool': None, @@ -398,6 +530,27 @@ def itstool_join(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' 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_kwargs( + 'i18n.xgettext', + _ARGS, + KwargInfo('recursive', bool, default=False), + INSTALL_KW, + INSTALL_DIR_KW, + INSTALL_TAG_KW, + ) + def xgettext(self, state: ModuleState, args: T.Tuple[str, T.List[SourcesType]], kwargs: XgettextProgramT) -> build.CustomTarget: + 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) + + if kwargs['install'] and not kwargs['install_dir']: + raise InvalidArguments('i18n.xgettext: "install_dir" keyword argument must be set when "install" is true.') + + xgettext_program = XgettextProgram(T.cast('ExternalProgram', self.tools[toolname]), self.interpreter) + return xgettext_program.extract(*args, **kwargs) + def initialize(interp: 'Interpreter') -> I18nModule: return I18nModule(interp) diff --git a/test cases/frameworks/38 gettext extractor/meson.build b/test cases/frameworks/38 gettext extractor/meson.build new file mode 100644 index 000000000000..962905a4e63c --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/meson.build @@ -0,0 +1,15 @@ +project( + 'gettext extractor', + 'c', + default_options: {'default_library': 'static'}, + meson_version: '1.8.0', +) + +if not find_program('xgettext', required: false).found() + error('MESON_SKIP_TEST xgettext command not found') +endif + +i18n = import('i18n') +xgettext_args = ['-ktr', '--add-comments=TRANSLATOR:', '--from-code=UTF-8'] + +subdir('src') diff --git a/test cases/frameworks/38 gettext extractor/src/lib1/lib1.c b/test cases/frameworks/38 gettext extractor/src/lib1/lib1.c new file mode 100644 index 000000000000..723edda00637 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib1/lib1.c @@ -0,0 +1,10 @@ +#include "lib1.h" + +#include + +#define tr(STRING) (STRING) + +void say_something(void) +{ + printf("%s\n", tr("Something!")); +} diff --git a/test cases/frameworks/38 gettext extractor/src/lib1/lib1.h b/test cases/frameworks/38 gettext extractor/src/lib1/lib1.h new file mode 100644 index 000000000000..6199d29c4ec6 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib1/lib1.h @@ -0,0 +1,6 @@ +#ifndef LIB1_H +#define LIB1_H + +void say_something(void); + +#endif diff --git a/test cases/frameworks/38 gettext extractor/src/lib1/meson.build b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build new file mode 100644 index 000000000000..3ec7fa987d6d --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib1/meson.build @@ -0,0 +1,3 @@ +lib1 = library('mylib1', 'lib1.c') +lib1_pot = i18n.xgettext('lib1', lib1, args: xgettext_args) +lib1_includes = include_directories('.') \ No newline at end of file diff --git a/test cases/frameworks/38 gettext extractor/src/lib2/lib2.c b/test cases/frameworks/38 gettext extractor/src/lib2/lib2.c new file mode 100644 index 000000000000..051271ec703d --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib2/lib2.c @@ -0,0 +1,13 @@ +#include "lib2.h" + +#include + +#include + +#define tr(STRING) (STRING) + +void say_something_else(void) +{ + say_something(); + printf("%s\n", tr("Something else!")); +} diff --git a/test cases/frameworks/38 gettext extractor/src/lib2/lib2.h b/test cases/frameworks/38 gettext extractor/src/lib2/lib2.h new file mode 100644 index 000000000000..faf693f7ceb3 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib2/lib2.h @@ -0,0 +1,6 @@ +#ifndef LIB2_H +#define LIB2_H + +void say_something_else(void); + +#endif diff --git a/test cases/frameworks/38 gettext extractor/src/lib2/meson.build b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build new file mode 100644 index 000000000000..ac5e7fe4b050 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/lib2/meson.build @@ -0,0 +1,3 @@ +lib2 = library('mylib2', 'lib2.c', include_directories: lib1_includes, link_with: lib1) +lib2_pot = i18n.xgettext('lib2', lib2, args: xgettext_args) +lib2_includes = include_directories('.') diff --git a/test cases/frameworks/38 gettext extractor/src/main.c b/test cases/frameworks/38 gettext extractor/src/main.c new file mode 100644 index 000000000000..807096bd7925 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/main.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + say_something_else(); + + return 0; +} diff --git a/test cases/frameworks/38 gettext extractor/src/meson.build b/test cases/frameworks/38 gettext extractor/src/meson.build new file mode 100644 index 000000000000..27fc81326450 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/src/meson.build @@ -0,0 +1,6 @@ +subdir('lib1') +subdir('lib2') + +main = executable('say', 'main.c', link_with: [lib2], include_directories: lib2_includes) + +main_pot = i18n.xgettext('main', main, args: xgettext_args, install: true, install_dir: 'intl', install_tag: 'intl', recursive: true) diff --git a/test cases/frameworks/38 gettext extractor/test.json b/test cases/frameworks/38 gettext extractor/test.json new file mode 100644 index 000000000000..c5952ffd59f2 --- /dev/null +++ b/test cases/frameworks/38 gettext extractor/test.json @@ -0,0 +1,6 @@ +{ + "installed": [ + { "type": "file", "file": "usr/intl/main.pot" } + ], + "expect_skip_on_jobname": ["azure", "cygwin"] +} From f23b0e7f35661645b7fec82025e72dffa189ab59 Mon Sep 17 00:00:00 2001 From: Andrew McNulty Date: Wed, 9 Apr 2025 18:13:39 +0200 Subject: [PATCH 480/624] interpreter: Error if java sources used with non-jar target (#14424) If the user specifies java sources as input to a non-jar build target, raise an error with a message directing them to use the jar target instead. Fixes: https://github.com/mesonbuild/meson/issues/13870 --- mesonbuild/compilers/__init__.py | 2 ++ mesonbuild/compilers/compilers.py | 6 ++++++ mesonbuild/interpreter/interpreter.py | 8 ++++++++ .../failing/134 java sources in non jar target/Test.java | 0 .../134 java sources in non jar target/meson.build | 3 +++ .../failing/134 java sources in non jar target/test.json | 7 +++++++ 6 files changed, 26 insertions(+) create mode 100644 test cases/failing/134 java sources in non jar target/Test.java create mode 100644 test cases/failing/134 java sources in non jar target/meson.build create mode 100644 test cases/failing/134 java sources in non jar target/test.json diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 116b3fa7a43e..aab761af426d 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -19,6 +19,7 @@ 'is_llvm_ir', 'is_object', 'is_source', + 'is_java', 'is_known_suffix', 'lang_suffixes', 'LANGUAGES_USING_LDFLAGS', @@ -55,6 +56,7 @@ get_base_link_args, is_header, is_source, + is_java, is_assembly, is_llvm_ir, is_object, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index a3b243dd9b51..3c1d58b4ed18 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -148,6 +148,12 @@ def is_assembly(fname: 'mesonlib.FileOrString') -> bool: suffix = fname.split('.')[-1] return suffix in assembler_suffixes +def is_java(fname: mesonlib.FileOrString) -> bool: + if isinstance(fname, mesonlib.File): + fname = fname.fname + suffix = fname.split('.')[-1] + return suffix in lang_suffixes['java'] + def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool: if isinstance(fname, mesonlib.File): fname = fname.fname diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index dedd20c8256b..2cd272db39ff 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3390,6 +3390,7 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs kwargs['language_args'][lang].extend(args) kwargs['depend_files'].extend(deps) if targetclass is not build.Jar: + self.check_for_jar_sources(sources, targetclass) kwargs['d_import_dirs'] = self.extract_incdirs(kwargs, 'd_import_dirs') # Filter out kwargs from other target types. For example 'soversion' @@ -3479,6 +3480,13 @@ def check_sources_exist(self, subdir, sources): if not os.path.isfile(fname): raise InterpreterException(f'Tried to add non-existing source file {s}.') + def check_for_jar_sources(self, sources, targetclass): + for s in sources: + if isinstance(s, (str, mesonlib.File)) and compilers.is_java(s): + raise InvalidArguments(f'Build target of type "{targetclass.typename}" cannot build java source: "{s}". Use "{build.Jar.typename}" instead.') + 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: diff --git a/test cases/failing/134 java sources in non jar target/Test.java b/test cases/failing/134 java sources in non jar target/Test.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/failing/134 java sources in non jar target/meson.build b/test cases/failing/134 java sources in non jar target/meson.build new file mode 100644 index 000000000000..0aa802a48304 --- /dev/null +++ b/test cases/failing/134 java sources in non jar target/meson.build @@ -0,0 +1,3 @@ +# https://github.com/mesonbuild/meson/issues/13870 +project('134 java sources in non jar target') +executable('Test.jar', 'Test.java') diff --git a/test cases/failing/134 java sources in non jar target/test.json b/test cases/failing/134 java sources in non jar target/test.json new file mode 100644 index 000000000000..271ac36affdf --- /dev/null +++ b/test cases/failing/134 java sources in non jar target/test.json @@ -0,0 +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." + } + ] +} From a717554cdb726cf963556ecf6516f4386d5a879b Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Wed, 9 Apr 2025 16:39:54 -0400 Subject: [PATCH 481/624] Add documentation for compiler.has_define to user guide --- docs/markdown/Compiler-properties.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md index 6d04c8bd79aa..7eea36891db8 100644 --- a/docs/markdown/Compiler-properties.md +++ b/docs/markdown/Compiler-properties.md @@ -194,6 +194,18 @@ correctly report the function as missing. Without the header however, it would lack the necessary availability information and incorrectly report the function as available. +## Is a macro defined? + +Macro detection can often be useful to determine if non-standard features +are supported on your target platform. Fortunately, Meson makes it +easy to check if a macro is defined: + +```meson +if [[#compiler.has_define]]('__SIZEOF_INT128__') + # macro is defined, do whatever is required +endif +``` + ## Does a structure contain a member? Some platforms have different standard structures. Here's how one From eca9d81eb461b4c9c9f02e064419b207bf417d27 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 11 Apr 2025 10:53:02 +0200 Subject: [PATCH 482/624] linkers: fix Apple ld -install_name with custom suffix Use the custom suffix instead of .dylib, so that executables that are linked to a custom-suffix library can be ran from the build tree. Fixes: #14470 --- mesonbuild/linkers/linkers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 58753e63b56b..0302ed7c6698 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -857,7 +857,7 @@ def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, install_name = ['@rpath/', prefix, shlib_name] if soversion is not None: install_name.append('.' + soversion) - install_name.append('.dylib') + install_name.append('.' + suffix) args = ['-install_name', ''.join(install_name)] if darwin_versions: args.extend(['-compatibility_version', darwin_versions[0], From a2d2d311a16207a1cd65fa18cd2c88ae749db9d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 10 Mar 2025 14:10:51 +0100 Subject: [PATCH 483/624] add exe_wrapper to the wasm cross file This makes it possible to run tests ("1 trivial" passes). Signed-off-by: Paolo Bonzini --- cross/wasm.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cross/wasm.txt b/cross/wasm.txt index 2a64319789a5..1b589d61ba33 100644 --- a/cross/wasm.txt +++ b/cross/wasm.txt @@ -2,6 +2,7 @@ c = 'emcc' cpp = 'em++' ar = 'emar' +exe_wrapper = 'node' [built-in options] c_args = [] From bb776a5fe13d60166488712a40b6f8bde7738e0e Mon Sep 17 00:00:00 2001 From: Aditya Vidyadhar Kamath Date: Mon, 14 Apr 2025 07:24:26 -0500 Subject: [PATCH 484/624] Fix two shared objects in archive for AIX. In AIX, when we use ar -q to archive then if there is a shared object already existing with the same name a duplicate one is created if one tries to archive again. Meson archives shared library during build time and relinks again during install time. At this stage when shared object is again made and archived again, creating two shared objects in the archive. When we use "ar -rs" then we ensure it is only one. Reference: https://www.ibm.com/docs/en/aix/7.1?topic=ar-command This patch is a fix to the same. Before patch output: rwxr-xr-x 0/0 5018 Apr 14 05:57 2025 libmylib.so rwxr-xr-x 0/0 5018 Apr 14 05:58 2025 libmylib.so After patch output: rwxr-xr-x 0/0 5018 Apr 14 07:23 2025 libmylib.so --- mesonbuild/linkers/linkers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 0302ed7c6698..e74ff708d045 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1556,7 +1556,7 @@ def get_archive_name(self, filename: str) -> str: def get_command_to_archive_shlib(self) -> T.List[str]: # Archive shared library object and remove the shared library object, # since it already exists in the archive. - command = ['ar', '-q', '-v', '$out', '$in', '&&', 'rm', '-f', '$in'] + command = ['ar', '-r', '-s', '-v', '$out', '$in', '&&', 'rm', '-f', '$in'] return command def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: From 028abfe87c5d3b4dfe8a29472119aa1581beb215 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 11 Apr 2025 11:57:12 +0200 Subject: [PATCH 485/624] minstall: Don't treat symlinks to directories as directories in do_copydir() As documented in https://docs.python.org/3/library/os.html#os.walk: > dirnames is a list of the names of the subdirectories in dirpath > (including symlinks to directories, and excluding '.' and '..') So currently, we treat any symlink to a directory as a directory instead of as a symlink. In practice, this means symlinks to directories are installed as empty directories instead of as symlinks. Let's make sure the symlinks are kept intact by checking for symlinks when we iterate over the directory names and treating any symlinks we find as files instead of as symlinks. --- mesonbuild/minstall.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index b4d59af7533c..f65087c66194 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -507,6 +507,9 @@ def do_copydir(self, data: InstallData, src_dir: str, dst_dir: str, abs_src = os.path.join(root, d) filepart = os.path.relpath(abs_src, start=src_dir) abs_dst = os.path.join(dst_dir, filepart) + if os.path.islink(abs_src): + files.append(d) + continue # Remove these so they aren't visited by os.walk at all. if filepart in exclude_dirs: dirs.remove(d) From cdf4b39e6f0a94ae14fc923bb54a04e846bcc2d7 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 14 Apr 2025 22:14:50 +0300 Subject: [PATCH 486/624] Condense directory names for 1.8 rc1. --- run_format_tests.py | 2 +- .../crossfile.ini | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../subprojects/sub/meson_options.txt | 0 .../bar.c | 0 .../bar.h | 0 .../foo.c | 0 .../foo.h | 0 .../meson.build | 0 .../test.json | 0 .../{39 qt qml => 40 qt qml}/Basic.qml | 0 .../{39 qt qml => 40 qt qml}/Internal.qml | 0 .../{39 qt qml => 40 qt qml}/Main.qml | 0 .../QmlCppExposed.hpp | 0 .../QmlCppOtherExposed.hpp | 0 .../{39 qt qml => 40 qt qml}/QmlMain.cpp | 0 .../{39 qt qml => 40 qt qml}/QmlSingleton.qml | 0 .../{39 qt qml => 40 qt qml}/custom_qmldir | 0 .../custom_qmldir.qrc | 0 .../{39 qt qml => 40 qt qml}/meson.build | 0 .../meson_options.txt | 0 .../subdir/SubdirHeader.hpp | 0 .../{39 qt qml => 40 qt qml}/subdir/Thing.qml | 0 .../{39 qt qml => 40 qt qml}/test.json | 0 .../Library.swift | 0 .../Library2.swift | 0 .../main.swift | 0 .../meson.build | 0 .../foo.py | 0 .../meson.build | 0 .../subdir/meson.build | 0 .../lib2.rs | 0 .../main.rs | 0 .../meson.build | 0 .../meson.build | 0 .../unit/{104 strip => 103 strip}/lib.c | 0 .../unit/{104 strip => 103 strip}/meson.build | 0 .../meson.build | 0 .../meson.build | 0 .../main.c | 0 .../meson.build | 0 .../runner.py | 0 .../cp.py | 0 .../main.c | 0 .../meson.build | 0 .../symlinked_subproject/datadir/datafile | 0 .../symlinked_subproject/datadir/meson.build | 0 .../symlinked_subproject/meson.build | 0 .../symlinked_subproject/src.c | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/foo/foo.c | 0 .../subprojects/foo/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../unit/{111 freeze => 110 freeze}/freeze.c | 0 .../{111 freeze => 110 freeze}/meson.build | 0 .../meson.build | 0 .../script.py | 0 .../com/mesonbuild/Simple.java | 0 .../meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../main.c | 0 .../meson.build | 0 .../s1.c | 0 .../s2.c | 0 .../s3.c | 0 .../meson.build | 0 .../meson.options | 0 .../expected_mods.json | 0 .../meson.build | 0 .../{118 genvslite => 117 genvslite}/main.cpp | 0 .../meson.build | 0 .../cache_dir/bar/meson.build | 0 .../cache_dir/foo.zip | Bin .../meson.build | 0 .../subprojects/bar.wrap | 0 .../subprojects/foo.wrap | 0 .../meson.build | 0 .../nativefile.ini | 0 .../{121 rewrite => 120 rewrite}/meson.build | 0 .../main.c | 0 .../meson.build | 0 .../meson.build | 0 .../meson.options | 0 .../subprojects/sub1/meson.build | 0 .../subprojects/sub1/meson.options | 0 .../subprojects/sub1/sub1.c | 0 .../subprojects/sub2/meson.build | 0 .../subprojects/sub2/meson.options | 0 .../subprojects/sub2/sub2.c | 0 .../toplevel.c | 0 .../meson.build | 0 .../subprojects/sub/meson.build | 0 .../meson.build | 0 .../test.py | 0 .../meson.build | 0 .../foo.c | 0 .../foo.dat | 0 .../foo.h | 0 .../foo/foofile | 0 .../meson.build | 0 .../subprojects/bar/bar.c | 0 .../subprojects/bar/bar.dat | 0 .../subprojects/bar/bar.h | 0 .../subprojects/bar/bar/barfile | 0 .../subprojects/bar/meson.build | 0 .../meson.build | 0 .../meson_options.txt | 0 .../subprojects/sub/foo.c | 0 .../subprojects/sub/meson.build | 0 .../.clang-format | 0 .../.clang-format-ignore | 0 .../.clang-format-include | 0 .../meson.build | 0 .../not-included/badformat.cpp | 0 .../src/badformat.c | 0 .../src/badformat.cpp | 0 .../easytogrepfor/genh.py | 0 .../easytogrepfor/meson.build | 0 .../{95 custominc => 94 custominc}/helper.c | 0 .../meson.build | 0 .../{95 custominc => 94 custominc}/prog.c | 0 .../{95 custominc => 94 custominc}/prog2.c | 0 .../meson.build | 0 .../subprojects/something/meson.build | 0 .../meson.build | 0 .../test.c | 0 .../.gitignore | 0 .../libtestprovider/meson.build | 0 .../libtestprovider/provider.c | 0 .../proguser/meson.build | 0 .../proguser/receiver.c | 0 .../bar-custom.txt | 0 .../bar-devel.h | 0 .../bar-notag.txt | 0 .../custom_files/data.txt | 0 .../excludes/excluded.txt | 0 .../excludes/excluded/placeholder.txt | 0 .../excludes/installed.txt | 0 .../foo.in | 0 .../foo1-devel.h | 0 .../lib.c | 0 .../main.c | 0 .../meson.build | 0 .../script.py | 0 .../subdir/bar2-devel.h | 0 .../subdir/foo2.in | 0 .../subdir/foo3-devel.h | 0 .../subdir/lib.c | 0 .../subdir/main.c | 0 .../subdir/meson.build | 0 .../subdir/script.py | 0 .../subprojects/subproject/aaa.txt | 0 .../subprojects/subproject/bbb.txt | 0 .../subprojects/subproject/meson.build | 0 .../file.txt.in | 0 .../meson.build | 0 .../subdir/meson.build | 0 unittests/allplatformstests.py | 38 +++++++++--------- unittests/linuxliketests.py | 16 ++++---- unittests/machinefiletests.py | 2 +- unittests/platformagnostictests.py | 16 ++++---- unittests/rewritetests.py | 2 +- unittests/windowstests.py | 2 +- 167 files changed, 39 insertions(+), 39 deletions(-) rename test cases/common/{282 subproj options => 281 subproj options}/crossfile.ini (100%) rename test cases/common/{282 subproj options => 281 subproj options}/meson.build (100%) rename test cases/common/{282 subproj options => 281 subproj options}/subprojects/sub/meson.build (100%) rename test cases/common/{282 subproj options => 281 subproj options}/subprojects/sub/meson_options.txt (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/bar.c (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/bar.h (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/foo.c (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/foo.h (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/meson.build (100%) rename test cases/frameworks/{38 gir both_libraries => 39 gir both_libraries}/test.json (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/Basic.qml (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/Internal.qml (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/Main.qml (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/QmlCppExposed.hpp (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/QmlCppOtherExposed.hpp (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/QmlMain.cpp (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/QmlSingleton.qml (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/custom_qmldir (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/custom_qmldir.qrc (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/meson.build (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/meson_options.txt (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/subdir/SubdirHeader.hpp (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/subdir/Thing.qml (100%) rename test cases/frameworks/{39 qt qml => 40 qt qml}/test.json (100%) rename test cases/swift/{13 file name matches module name => 10 file name matches module name}/Library.swift (100%) rename test cases/swift/{13 file name matches module name => 10 file name matches module name}/Library2.swift (100%) rename test cases/swift/{13 file name matches module name => 10 file name matches module name}/main.swift (100%) rename test cases/swift/{13 file name matches module name => 10 file name matches module name}/meson.build (100%) rename test cases/unit/{101 relative find program => 100 relative find program}/foo.py (100%) rename test cases/unit/{101 relative find program => 100 relative find program}/meson.build (100%) rename test cases/unit/{101 relative find program => 100 relative find program}/subdir/meson.build (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/lib2.rs (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/main.rs (100%) rename test cases/unit/{102 rlib linkage => 101 rlib linkage}/meson.build (100%) rename test cases/unit/{103 python without pkgconfig => 102 python without pkgconfig}/meson.build (100%) rename test cases/unit/{104 strip => 103 strip}/lib.c (100%) rename test cases/unit/{104 strip => 103 strip}/meson.build (100%) rename test cases/unit/{105 debug function => 104 debug function}/meson.build (100%) rename test cases/unit/{106 pkgconfig relocatable with absolute path => 105 pkgconfig relocatable with absolute path}/meson.build (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/main.c (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/meson.build (100%) rename test cases/unit/{107 underspecified mtest => 106 underspecified mtest}/runner.py (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/cp.py (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/main.c (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/datadir/datafile (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/datadir/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/meson.build (100%) rename test cases/unit/{108 subproject symlink => 107 subproject symlink}/symlinked_subproject/src.c (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/meson.build (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/meson_options.txt (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/subprojects/foo/foo.c (100%) rename test cases/unit/{109 new subproject on reconfigure => 108 new subproject on reconfigure}/subprojects/foo/meson.build (100%) rename test cases/unit/{110 configure same noop => 109 configure same noop}/meson.build (100%) rename test cases/unit/{110 configure same noop => 109 configure same noop}/meson_options.txt (100%) rename test cases/unit/{111 freeze => 110 freeze}/freeze.c (100%) rename test cases/unit/{111 freeze => 110 freeze}/meson.build (100%) rename test cases/unit/{112 replace unencodable xml chars => 111 replace unencodable xml chars}/meson.build (100%) rename test cases/unit/{112 replace unencodable xml chars => 111 replace unencodable xml chars}/script.py (100%) rename test cases/unit/{113 classpath => 112 classpath}/com/mesonbuild/Simple.java (100%) rename test cases/unit/{113 classpath => 112 classpath}/meson.build (100%) rename test cases/unit/{114 list build options => 113 list build options}/meson.build (100%) rename test cases/unit/{114 list build options => 113 list build options}/meson_options.txt (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/main.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/meson.build (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s1.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s2.c (100%) rename test cases/unit/{115 complex link cases => 114 complex link cases}/s3.c (100%) rename test cases/unit/{116 c cpp stds => 115 c cpp stds}/meson.build (100%) rename test cases/unit/{116 c cpp stds => 115 c cpp stds}/meson.options (100%) rename test cases/unit/{117 empty project => 116 empty project}/expected_mods.json (100%) rename test cases/unit/{117 empty project => 116 empty project}/meson.build (100%) rename test cases/unit/{118 genvslite => 117 genvslite}/main.cpp (100%) rename test cases/unit/{118 genvslite => 117 genvslite}/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/cache_dir/bar/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/cache_dir/foo.zip (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/meson.build (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/subprojects/bar.wrap (100%) rename test cases/unit/{119 meson package cache dir => 118 meson package cache dir}/subprojects/foo.wrap (100%) rename test cases/unit/{120 openssl cmake bug => 119 openssl cmake bug}/meson.build (100%) rename test cases/unit/{120 openssl cmake bug => 119 openssl cmake bug}/nativefile.ini (100%) rename test cases/unit/{121 rewrite => 120 rewrite}/meson.build (100%) rename test cases/unit/{122 executable suffix => 121 executable suffix}/main.c (100%) rename test cases/unit/{122 executable suffix => 121 executable suffix}/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub1/sub1.c (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/meson.build (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/meson.options (100%) rename test cases/unit/{123 persp options => 122 persp options}/subprojects/sub2/sub2.c (100%) rename test cases/unit/{123 persp options => 122 persp options}/toplevel.c (100%) rename test cases/unit/{124 pkgsubproj => 125 pkgsubproj}/meson.build (100%) rename test cases/unit/{124 pkgsubproj => 125 pkgsubproj}/subprojects/sub/meson.build (100%) rename test cases/unit/{124 test slice => 126 test slice}/meson.build (100%) rename test cases/unit/{124 test slice => 126 test slice}/test.py (100%) rename test cases/unit/{125 sanitizers => 127 sanitizers}/meson.build (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.c (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.dat (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo.h (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/foo/foofile (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/meson.build (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.c (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.dat (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar.h (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/bar/barfile (100%) rename test cases/unit/{92 install skip subprojects => 91 install skip subprojects}/subprojects/bar/meson.build (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/meson.build (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/meson_options.txt (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/subprojects/sub/foo.c (100%) rename test cases/unit/{93 new subproject in configured project => 92 new subproject in configured project}/subprojects/sub/meson.build (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format-ignore (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/.clang-format-include (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/meson.build (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/not-included/badformat.cpp (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/src/badformat.c (100%) rename test cases/unit/{94 clangformat => 93 clangformat}/src/badformat.cpp (100%) rename test cases/unit/{95 custominc => 94 custominc}/easytogrepfor/genh.py (100%) rename test cases/unit/{95 custominc => 94 custominc}/easytogrepfor/meson.build (100%) rename test cases/unit/{95 custominc => 94 custominc}/helper.c (100%) rename test cases/unit/{95 custominc => 94 custominc}/meson.build (100%) rename test cases/unit/{95 custominc => 94 custominc}/prog.c (100%) rename test cases/unit/{95 custominc => 94 custominc}/prog2.c (100%) rename test cases/unit/{96 implicit force fallback => 95 implicit force fallback}/meson.build (100%) rename test cases/unit/{96 implicit force fallback => 95 implicit force fallback}/subprojects/something/meson.build (100%) rename test cases/unit/{97 compiler.links file arg => 96 compiler.links file arg}/meson.build (100%) rename test cases/unit/{97 compiler.links file arg => 96 compiler.links file arg}/test.c (100%) rename test cases/unit/{98 link full name => 97 link full name}/.gitignore (100%) rename test cases/unit/{98 link full name => 97 link full name}/libtestprovider/meson.build (100%) rename test cases/unit/{98 link full name => 97 link full name}/libtestprovider/provider.c (100%) rename test cases/unit/{98 link full name => 97 link full name}/proguser/meson.build (100%) rename test cases/unit/{98 link full name => 97 link full name}/proguser/receiver.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-custom.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/bar-notag.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/custom_files/data.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/excluded.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/excluded/placeholder.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/excludes/installed.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/foo.in (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/foo1-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/lib.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/main.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/meson.build (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/script.py (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/bar2-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/foo2.in (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/foo3-devel.h (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/lib.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/main.c (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/meson.build (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subdir/script.py (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/aaa.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/bbb.txt (100%) rename test cases/unit/{99 install all targets => 98 install all targets}/subprojects/subproject/meson.build (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/file.txt.in (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/meson.build (100%) rename test cases/unit/{100 custom target name => 99 custom target name}/subdir/meson.build (100%) diff --git a/run_format_tests.py b/run_format_tests.py index dee787b332ae..30c975882d9b 100755 --- a/run_format_tests.py +++ b/run_format_tests.py @@ -51,7 +51,7 @@ def check_format() -> None: 'work area', '.eggs', '_cache', # e.g. .mypy_cache 'venv', # virtualenvs have DOS line endings - '121 rewrite', # we explicitly test for tab in meson.build file + '120 rewrite', # we explicitly test for tab in meson.build file '3 editorconfig', } for (root, _, filenames) in os.walk('.'): diff --git a/test cases/common/282 subproj options/crossfile.ini b/test cases/common/281 subproj options/crossfile.ini similarity index 100% rename from test cases/common/282 subproj options/crossfile.ini rename to test cases/common/281 subproj options/crossfile.ini diff --git a/test cases/common/282 subproj options/meson.build b/test cases/common/281 subproj options/meson.build similarity index 100% rename from test cases/common/282 subproj options/meson.build rename to test cases/common/281 subproj options/meson.build diff --git a/test cases/common/282 subproj options/subprojects/sub/meson.build b/test cases/common/281 subproj options/subprojects/sub/meson.build similarity index 100% rename from test cases/common/282 subproj options/subprojects/sub/meson.build rename to test cases/common/281 subproj options/subprojects/sub/meson.build diff --git a/test cases/common/282 subproj options/subprojects/sub/meson_options.txt b/test cases/common/281 subproj options/subprojects/sub/meson_options.txt similarity index 100% rename from test cases/common/282 subproj options/subprojects/sub/meson_options.txt rename to test cases/common/281 subproj options/subprojects/sub/meson_options.txt diff --git a/test cases/frameworks/38 gir both_libraries/bar.c b/test cases/frameworks/39 gir both_libraries/bar.c similarity index 100% rename from test cases/frameworks/38 gir both_libraries/bar.c rename to test cases/frameworks/39 gir both_libraries/bar.c diff --git a/test cases/frameworks/38 gir both_libraries/bar.h b/test cases/frameworks/39 gir both_libraries/bar.h similarity index 100% rename from test cases/frameworks/38 gir both_libraries/bar.h rename to test cases/frameworks/39 gir both_libraries/bar.h diff --git a/test cases/frameworks/38 gir both_libraries/foo.c b/test cases/frameworks/39 gir both_libraries/foo.c similarity index 100% rename from test cases/frameworks/38 gir both_libraries/foo.c rename to test cases/frameworks/39 gir both_libraries/foo.c diff --git a/test cases/frameworks/38 gir both_libraries/foo.h b/test cases/frameworks/39 gir both_libraries/foo.h similarity index 100% rename from test cases/frameworks/38 gir both_libraries/foo.h rename to test cases/frameworks/39 gir both_libraries/foo.h diff --git a/test cases/frameworks/38 gir both_libraries/meson.build b/test cases/frameworks/39 gir both_libraries/meson.build similarity index 100% rename from test cases/frameworks/38 gir both_libraries/meson.build rename to test cases/frameworks/39 gir both_libraries/meson.build diff --git a/test cases/frameworks/38 gir both_libraries/test.json b/test cases/frameworks/39 gir both_libraries/test.json similarity index 100% rename from test cases/frameworks/38 gir both_libraries/test.json rename to test cases/frameworks/39 gir both_libraries/test.json diff --git a/test cases/frameworks/39 qt qml/Basic.qml b/test cases/frameworks/40 qt qml/Basic.qml similarity index 100% rename from test cases/frameworks/39 qt qml/Basic.qml rename to test cases/frameworks/40 qt qml/Basic.qml diff --git a/test cases/frameworks/39 qt qml/Internal.qml b/test cases/frameworks/40 qt qml/Internal.qml similarity index 100% rename from test cases/frameworks/39 qt qml/Internal.qml rename to test cases/frameworks/40 qt qml/Internal.qml diff --git a/test cases/frameworks/39 qt qml/Main.qml b/test cases/frameworks/40 qt qml/Main.qml similarity index 100% rename from test cases/frameworks/39 qt qml/Main.qml rename to test cases/frameworks/40 qt qml/Main.qml diff --git a/test cases/frameworks/39 qt qml/QmlCppExposed.hpp b/test cases/frameworks/40 qt qml/QmlCppExposed.hpp similarity index 100% rename from test cases/frameworks/39 qt qml/QmlCppExposed.hpp rename to test cases/frameworks/40 qt qml/QmlCppExposed.hpp diff --git a/test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp b/test cases/frameworks/40 qt qml/QmlCppOtherExposed.hpp similarity index 100% rename from test cases/frameworks/39 qt qml/QmlCppOtherExposed.hpp rename to test cases/frameworks/40 qt qml/QmlCppOtherExposed.hpp diff --git a/test cases/frameworks/39 qt qml/QmlMain.cpp b/test cases/frameworks/40 qt qml/QmlMain.cpp similarity index 100% rename from test cases/frameworks/39 qt qml/QmlMain.cpp rename to test cases/frameworks/40 qt qml/QmlMain.cpp diff --git a/test cases/frameworks/39 qt qml/QmlSingleton.qml b/test cases/frameworks/40 qt qml/QmlSingleton.qml similarity index 100% rename from test cases/frameworks/39 qt qml/QmlSingleton.qml rename to test cases/frameworks/40 qt qml/QmlSingleton.qml diff --git a/test cases/frameworks/39 qt qml/custom_qmldir b/test cases/frameworks/40 qt qml/custom_qmldir similarity index 100% rename from test cases/frameworks/39 qt qml/custom_qmldir rename to test cases/frameworks/40 qt qml/custom_qmldir diff --git a/test cases/frameworks/39 qt qml/custom_qmldir.qrc b/test cases/frameworks/40 qt qml/custom_qmldir.qrc similarity index 100% rename from test cases/frameworks/39 qt qml/custom_qmldir.qrc rename to test cases/frameworks/40 qt qml/custom_qmldir.qrc diff --git a/test cases/frameworks/39 qt qml/meson.build b/test cases/frameworks/40 qt qml/meson.build similarity index 100% rename from test cases/frameworks/39 qt qml/meson.build rename to test cases/frameworks/40 qt qml/meson.build diff --git a/test cases/frameworks/39 qt qml/meson_options.txt b/test cases/frameworks/40 qt qml/meson_options.txt similarity index 100% rename from test cases/frameworks/39 qt qml/meson_options.txt rename to test cases/frameworks/40 qt qml/meson_options.txt diff --git a/test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp b/test cases/frameworks/40 qt qml/subdir/SubdirHeader.hpp similarity index 100% rename from test cases/frameworks/39 qt qml/subdir/SubdirHeader.hpp rename to test cases/frameworks/40 qt qml/subdir/SubdirHeader.hpp diff --git a/test cases/frameworks/39 qt qml/subdir/Thing.qml b/test cases/frameworks/40 qt qml/subdir/Thing.qml similarity index 100% rename from test cases/frameworks/39 qt qml/subdir/Thing.qml rename to test cases/frameworks/40 qt qml/subdir/Thing.qml diff --git a/test cases/frameworks/39 qt qml/test.json b/test cases/frameworks/40 qt qml/test.json similarity index 100% rename from test cases/frameworks/39 qt qml/test.json rename to test cases/frameworks/40 qt qml/test.json diff --git a/test cases/swift/13 file name matches module name/Library.swift b/test cases/swift/10 file name matches module name/Library.swift similarity index 100% rename from test cases/swift/13 file name matches module name/Library.swift rename to test cases/swift/10 file name matches module name/Library.swift diff --git a/test cases/swift/13 file name matches module name/Library2.swift b/test cases/swift/10 file name matches module name/Library2.swift similarity index 100% rename from test cases/swift/13 file name matches module name/Library2.swift rename to test cases/swift/10 file name matches module name/Library2.swift diff --git a/test cases/swift/13 file name matches module name/main.swift b/test cases/swift/10 file name matches module name/main.swift similarity index 100% rename from test cases/swift/13 file name matches module name/main.swift rename to test cases/swift/10 file name matches module name/main.swift diff --git a/test cases/swift/13 file name matches module name/meson.build b/test cases/swift/10 file name matches module name/meson.build similarity index 100% rename from test cases/swift/13 file name matches module name/meson.build rename to test cases/swift/10 file name matches module name/meson.build diff --git a/test cases/unit/101 relative find program/foo.py b/test cases/unit/100 relative find program/foo.py similarity index 100% rename from test cases/unit/101 relative find program/foo.py rename to test cases/unit/100 relative find program/foo.py diff --git a/test cases/unit/101 relative find program/meson.build b/test cases/unit/100 relative find program/meson.build similarity index 100% rename from test cases/unit/101 relative find program/meson.build rename to test cases/unit/100 relative find program/meson.build diff --git a/test cases/unit/101 relative find program/subdir/meson.build b/test cases/unit/100 relative find program/subdir/meson.build similarity index 100% rename from test cases/unit/101 relative find program/subdir/meson.build rename to test cases/unit/100 relative find program/subdir/meson.build diff --git a/test cases/unit/102 rlib linkage/lib2.rs b/test cases/unit/101 rlib linkage/lib2.rs similarity index 100% rename from test cases/unit/102 rlib linkage/lib2.rs rename to test cases/unit/101 rlib linkage/lib2.rs diff --git a/test cases/unit/102 rlib linkage/main.rs b/test cases/unit/101 rlib linkage/main.rs similarity index 100% rename from test cases/unit/102 rlib linkage/main.rs rename to test cases/unit/101 rlib linkage/main.rs diff --git a/test cases/unit/102 rlib linkage/meson.build b/test cases/unit/101 rlib linkage/meson.build similarity index 100% rename from test cases/unit/102 rlib linkage/meson.build rename to test cases/unit/101 rlib linkage/meson.build diff --git a/test cases/unit/103 python without pkgconfig/meson.build b/test cases/unit/102 python without pkgconfig/meson.build similarity index 100% rename from test cases/unit/103 python without pkgconfig/meson.build rename to test cases/unit/102 python without pkgconfig/meson.build diff --git a/test cases/unit/104 strip/lib.c b/test cases/unit/103 strip/lib.c similarity index 100% rename from test cases/unit/104 strip/lib.c rename to test cases/unit/103 strip/lib.c diff --git a/test cases/unit/104 strip/meson.build b/test cases/unit/103 strip/meson.build similarity index 100% rename from test cases/unit/104 strip/meson.build rename to test cases/unit/103 strip/meson.build diff --git a/test cases/unit/105 debug function/meson.build b/test cases/unit/104 debug function/meson.build similarity index 100% rename from test cases/unit/105 debug function/meson.build rename to test cases/unit/104 debug function/meson.build diff --git a/test cases/unit/106 pkgconfig relocatable with absolute path/meson.build b/test cases/unit/105 pkgconfig relocatable with absolute path/meson.build similarity index 100% rename from test cases/unit/106 pkgconfig relocatable with absolute path/meson.build rename to test cases/unit/105 pkgconfig relocatable with absolute path/meson.build diff --git a/test cases/unit/107 underspecified mtest/main.c b/test cases/unit/106 underspecified mtest/main.c similarity index 100% rename from test cases/unit/107 underspecified mtest/main.c rename to test cases/unit/106 underspecified mtest/main.c diff --git a/test cases/unit/107 underspecified mtest/meson.build b/test cases/unit/106 underspecified mtest/meson.build similarity index 100% rename from test cases/unit/107 underspecified mtest/meson.build rename to test cases/unit/106 underspecified mtest/meson.build diff --git a/test cases/unit/107 underspecified mtest/runner.py b/test cases/unit/106 underspecified mtest/runner.py similarity index 100% rename from test cases/unit/107 underspecified mtest/runner.py rename to test cases/unit/106 underspecified mtest/runner.py diff --git a/test cases/unit/108 subproject symlink/cp.py b/test cases/unit/107 subproject symlink/cp.py similarity index 100% rename from test cases/unit/108 subproject symlink/cp.py rename to test cases/unit/107 subproject symlink/cp.py diff --git a/test cases/unit/108 subproject symlink/main.c b/test cases/unit/107 subproject symlink/main.c similarity index 100% rename from test cases/unit/108 subproject symlink/main.c rename to test cases/unit/107 subproject symlink/main.c diff --git a/test cases/unit/108 subproject symlink/meson.build b/test cases/unit/107 subproject symlink/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/meson.build rename to test cases/unit/107 subproject symlink/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile b/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/datadir/datafile rename to test cases/unit/107 subproject symlink/symlinked_subproject/datadir/datafile diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build b/test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/datadir/meson.build rename to test cases/unit/107 subproject symlink/symlinked_subproject/datadir/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/meson.build b/test cases/unit/107 subproject symlink/symlinked_subproject/meson.build similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/meson.build rename to test cases/unit/107 subproject symlink/symlinked_subproject/meson.build diff --git a/test cases/unit/108 subproject symlink/symlinked_subproject/src.c b/test cases/unit/107 subproject symlink/symlinked_subproject/src.c similarity index 100% rename from test cases/unit/108 subproject symlink/symlinked_subproject/src.c rename to test cases/unit/107 subproject symlink/symlinked_subproject/src.c diff --git a/test cases/unit/109 new subproject on reconfigure/meson.build b/test cases/unit/108 new subproject on reconfigure/meson.build similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/meson.build rename to test cases/unit/108 new subproject on reconfigure/meson.build diff --git a/test cases/unit/109 new subproject on reconfigure/meson_options.txt b/test cases/unit/108 new subproject on reconfigure/meson_options.txt similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/meson_options.txt rename to test cases/unit/108 new subproject on reconfigure/meson_options.txt diff --git a/test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c b/test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/subprojects/foo/foo.c rename to test cases/unit/108 new subproject on reconfigure/subprojects/foo/foo.c diff --git a/test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build b/test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build similarity index 100% rename from test cases/unit/109 new subproject on reconfigure/subprojects/foo/meson.build rename to test cases/unit/108 new subproject on reconfigure/subprojects/foo/meson.build diff --git a/test cases/unit/110 configure same noop/meson.build b/test cases/unit/109 configure same noop/meson.build similarity index 100% rename from test cases/unit/110 configure same noop/meson.build rename to test cases/unit/109 configure same noop/meson.build diff --git a/test cases/unit/110 configure same noop/meson_options.txt b/test cases/unit/109 configure same noop/meson_options.txt similarity index 100% rename from test cases/unit/110 configure same noop/meson_options.txt rename to test cases/unit/109 configure same noop/meson_options.txt diff --git a/test cases/unit/111 freeze/freeze.c b/test cases/unit/110 freeze/freeze.c similarity index 100% rename from test cases/unit/111 freeze/freeze.c rename to test cases/unit/110 freeze/freeze.c diff --git a/test cases/unit/111 freeze/meson.build b/test cases/unit/110 freeze/meson.build similarity index 100% rename from test cases/unit/111 freeze/meson.build rename to test cases/unit/110 freeze/meson.build diff --git a/test cases/unit/112 replace unencodable xml chars/meson.build b/test cases/unit/111 replace unencodable xml chars/meson.build similarity index 100% rename from test cases/unit/112 replace unencodable xml chars/meson.build rename to test cases/unit/111 replace unencodable xml chars/meson.build diff --git a/test cases/unit/112 replace unencodable xml chars/script.py b/test cases/unit/111 replace unencodable xml chars/script.py similarity index 100% rename from test cases/unit/112 replace unencodable xml chars/script.py rename to test cases/unit/111 replace unencodable xml chars/script.py diff --git a/test cases/unit/113 classpath/com/mesonbuild/Simple.java b/test cases/unit/112 classpath/com/mesonbuild/Simple.java similarity index 100% rename from test cases/unit/113 classpath/com/mesonbuild/Simple.java rename to test cases/unit/112 classpath/com/mesonbuild/Simple.java diff --git a/test cases/unit/113 classpath/meson.build b/test cases/unit/112 classpath/meson.build similarity index 100% rename from test cases/unit/113 classpath/meson.build rename to test cases/unit/112 classpath/meson.build diff --git a/test cases/unit/114 list build options/meson.build b/test cases/unit/113 list build options/meson.build similarity index 100% rename from test cases/unit/114 list build options/meson.build rename to test cases/unit/113 list build options/meson.build diff --git a/test cases/unit/114 list build options/meson_options.txt b/test cases/unit/113 list build options/meson_options.txt similarity index 100% rename from test cases/unit/114 list build options/meson_options.txt rename to test cases/unit/113 list build options/meson_options.txt diff --git a/test cases/unit/115 complex link cases/main.c b/test cases/unit/114 complex link cases/main.c similarity index 100% rename from test cases/unit/115 complex link cases/main.c rename to test cases/unit/114 complex link cases/main.c diff --git a/test cases/unit/115 complex link cases/meson.build b/test cases/unit/114 complex link cases/meson.build similarity index 100% rename from test cases/unit/115 complex link cases/meson.build rename to test cases/unit/114 complex link cases/meson.build diff --git a/test cases/unit/115 complex link cases/s1.c b/test cases/unit/114 complex link cases/s1.c similarity index 100% rename from test cases/unit/115 complex link cases/s1.c rename to test cases/unit/114 complex link cases/s1.c diff --git a/test cases/unit/115 complex link cases/s2.c b/test cases/unit/114 complex link cases/s2.c similarity index 100% rename from test cases/unit/115 complex link cases/s2.c rename to test cases/unit/114 complex link cases/s2.c diff --git a/test cases/unit/115 complex link cases/s3.c b/test cases/unit/114 complex link cases/s3.c similarity index 100% rename from test cases/unit/115 complex link cases/s3.c rename to test cases/unit/114 complex link cases/s3.c diff --git a/test cases/unit/116 c cpp stds/meson.build b/test cases/unit/115 c cpp stds/meson.build similarity index 100% rename from test cases/unit/116 c cpp stds/meson.build rename to test cases/unit/115 c cpp stds/meson.build diff --git a/test cases/unit/116 c cpp stds/meson.options b/test cases/unit/115 c cpp stds/meson.options similarity index 100% rename from test cases/unit/116 c cpp stds/meson.options rename to test cases/unit/115 c cpp stds/meson.options diff --git a/test cases/unit/117 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json similarity index 100% rename from test cases/unit/117 empty project/expected_mods.json rename to test cases/unit/116 empty project/expected_mods.json diff --git a/test cases/unit/117 empty project/meson.build b/test cases/unit/116 empty project/meson.build similarity index 100% rename from test cases/unit/117 empty project/meson.build rename to test cases/unit/116 empty project/meson.build diff --git a/test cases/unit/118 genvslite/main.cpp b/test cases/unit/117 genvslite/main.cpp similarity index 100% rename from test cases/unit/118 genvslite/main.cpp rename to test cases/unit/117 genvslite/main.cpp diff --git a/test cases/unit/118 genvslite/meson.build b/test cases/unit/117 genvslite/meson.build similarity index 100% rename from test cases/unit/118 genvslite/meson.build rename to test cases/unit/117 genvslite/meson.build diff --git a/test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build b/test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build similarity index 100% rename from test cases/unit/119 meson package cache dir/cache_dir/bar/meson.build rename to test cases/unit/118 meson package cache dir/cache_dir/bar/meson.build diff --git a/test cases/unit/119 meson package cache dir/cache_dir/foo.zip b/test cases/unit/118 meson package cache dir/cache_dir/foo.zip similarity index 100% rename from test cases/unit/119 meson package cache dir/cache_dir/foo.zip rename to test cases/unit/118 meson package cache dir/cache_dir/foo.zip diff --git a/test cases/unit/119 meson package cache dir/meson.build b/test cases/unit/118 meson package cache dir/meson.build similarity index 100% rename from test cases/unit/119 meson package cache dir/meson.build rename to test cases/unit/118 meson package cache dir/meson.build diff --git a/test cases/unit/119 meson package cache dir/subprojects/bar.wrap b/test cases/unit/118 meson package cache dir/subprojects/bar.wrap similarity index 100% rename from test cases/unit/119 meson package cache dir/subprojects/bar.wrap rename to test cases/unit/118 meson package cache dir/subprojects/bar.wrap diff --git a/test cases/unit/119 meson package cache dir/subprojects/foo.wrap b/test cases/unit/118 meson package cache dir/subprojects/foo.wrap similarity index 100% rename from test cases/unit/119 meson package cache dir/subprojects/foo.wrap rename to test cases/unit/118 meson package cache dir/subprojects/foo.wrap diff --git a/test cases/unit/120 openssl cmake bug/meson.build b/test cases/unit/119 openssl cmake bug/meson.build similarity index 100% rename from test cases/unit/120 openssl cmake bug/meson.build rename to test cases/unit/119 openssl cmake bug/meson.build diff --git a/test cases/unit/120 openssl cmake bug/nativefile.ini b/test cases/unit/119 openssl cmake bug/nativefile.ini similarity index 100% rename from test cases/unit/120 openssl cmake bug/nativefile.ini rename to test cases/unit/119 openssl cmake bug/nativefile.ini diff --git a/test cases/unit/121 rewrite/meson.build b/test cases/unit/120 rewrite/meson.build similarity index 100% rename from test cases/unit/121 rewrite/meson.build rename to test cases/unit/120 rewrite/meson.build diff --git a/test cases/unit/122 executable suffix/main.c b/test cases/unit/121 executable suffix/main.c similarity index 100% rename from test cases/unit/122 executable suffix/main.c rename to test cases/unit/121 executable suffix/main.c diff --git a/test cases/unit/122 executable suffix/meson.build b/test cases/unit/121 executable suffix/meson.build similarity index 100% rename from test cases/unit/122 executable suffix/meson.build rename to test cases/unit/121 executable suffix/meson.build diff --git a/test cases/unit/123 persp options/meson.build b/test cases/unit/122 persp options/meson.build similarity index 100% rename from test cases/unit/123 persp options/meson.build rename to test cases/unit/122 persp options/meson.build diff --git a/test cases/unit/123 persp options/meson.options b/test cases/unit/122 persp options/meson.options similarity index 100% rename from test cases/unit/123 persp options/meson.options rename to test cases/unit/122 persp options/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.build b/test cases/unit/122 persp options/subprojects/sub1/meson.build similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/meson.build rename to test cases/unit/122 persp options/subprojects/sub1/meson.build diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.options b/test cases/unit/122 persp options/subprojects/sub1/meson.options similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/meson.options rename to test cases/unit/122 persp options/subprojects/sub1/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub1/sub1.c b/test cases/unit/122 persp options/subprojects/sub1/sub1.c similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub1/sub1.c rename to test cases/unit/122 persp options/subprojects/sub1/sub1.c diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.build b/test cases/unit/122 persp options/subprojects/sub2/meson.build similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/meson.build rename to test cases/unit/122 persp options/subprojects/sub2/meson.build diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.options b/test cases/unit/122 persp options/subprojects/sub2/meson.options similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/meson.options rename to test cases/unit/122 persp options/subprojects/sub2/meson.options diff --git a/test cases/unit/123 persp options/subprojects/sub2/sub2.c b/test cases/unit/122 persp options/subprojects/sub2/sub2.c similarity index 100% rename from test cases/unit/123 persp options/subprojects/sub2/sub2.c rename to test cases/unit/122 persp options/subprojects/sub2/sub2.c diff --git a/test cases/unit/123 persp options/toplevel.c b/test cases/unit/122 persp options/toplevel.c similarity index 100% rename from test cases/unit/123 persp options/toplevel.c rename to test cases/unit/122 persp options/toplevel.c diff --git a/test cases/unit/124 pkgsubproj/meson.build b/test cases/unit/125 pkgsubproj/meson.build similarity index 100% rename from test cases/unit/124 pkgsubproj/meson.build rename to test cases/unit/125 pkgsubproj/meson.build diff --git a/test cases/unit/124 pkgsubproj/subprojects/sub/meson.build b/test cases/unit/125 pkgsubproj/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/124 pkgsubproj/subprojects/sub/meson.build rename to test cases/unit/125 pkgsubproj/subprojects/sub/meson.build diff --git a/test cases/unit/124 test slice/meson.build b/test cases/unit/126 test slice/meson.build similarity index 100% rename from test cases/unit/124 test slice/meson.build rename to test cases/unit/126 test slice/meson.build diff --git a/test cases/unit/124 test slice/test.py b/test cases/unit/126 test slice/test.py similarity index 100% rename from test cases/unit/124 test slice/test.py rename to test cases/unit/126 test slice/test.py diff --git a/test cases/unit/125 sanitizers/meson.build b/test cases/unit/127 sanitizers/meson.build similarity index 100% rename from test cases/unit/125 sanitizers/meson.build rename to test cases/unit/127 sanitizers/meson.build diff --git a/test cases/unit/92 install skip subprojects/foo.c b/test cases/unit/91 install skip subprojects/foo.c similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.c rename to test cases/unit/91 install skip subprojects/foo.c diff --git a/test cases/unit/92 install skip subprojects/foo.dat b/test cases/unit/91 install skip subprojects/foo.dat similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.dat rename to test cases/unit/91 install skip subprojects/foo.dat diff --git a/test cases/unit/92 install skip subprojects/foo.h b/test cases/unit/91 install skip subprojects/foo.h similarity index 100% rename from test cases/unit/92 install skip subprojects/foo.h rename to test cases/unit/91 install skip subprojects/foo.h diff --git a/test cases/unit/92 install skip subprojects/foo/foofile b/test cases/unit/91 install skip subprojects/foo/foofile similarity index 100% rename from test cases/unit/92 install skip subprojects/foo/foofile rename to test cases/unit/91 install skip subprojects/foo/foofile diff --git a/test cases/unit/92 install skip subprojects/meson.build b/test cases/unit/91 install skip subprojects/meson.build similarity index 100% rename from test cases/unit/92 install skip subprojects/meson.build rename to test cases/unit/91 install skip subprojects/meson.build diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.c b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.c similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.c rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.c diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.dat rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.dat diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar.h b/test cases/unit/91 install skip subprojects/subprojects/bar/bar.h similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar.h rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar.h diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile b/test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/bar/barfile rename to test cases/unit/91 install skip subprojects/subprojects/bar/bar/barfile diff --git a/test cases/unit/92 install skip subprojects/subprojects/bar/meson.build b/test cases/unit/91 install skip subprojects/subprojects/bar/meson.build similarity index 100% rename from test cases/unit/92 install skip subprojects/subprojects/bar/meson.build rename to test cases/unit/91 install skip subprojects/subprojects/bar/meson.build diff --git a/test cases/unit/93 new subproject in configured project/meson.build b/test cases/unit/92 new subproject in configured project/meson.build similarity index 100% rename from test cases/unit/93 new subproject in configured project/meson.build rename to test cases/unit/92 new subproject in configured project/meson.build diff --git a/test cases/unit/93 new subproject in configured project/meson_options.txt b/test cases/unit/92 new subproject in configured project/meson_options.txt similarity index 100% rename from test cases/unit/93 new subproject in configured project/meson_options.txt rename to test cases/unit/92 new subproject in configured project/meson_options.txt diff --git a/test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c b/test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c similarity index 100% rename from test cases/unit/93 new subproject in configured project/subprojects/sub/foo.c rename to test cases/unit/92 new subproject in configured project/subprojects/sub/foo.c diff --git a/test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build b/test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build similarity index 100% rename from test cases/unit/93 new subproject in configured project/subprojects/sub/meson.build rename to test cases/unit/92 new subproject in configured project/subprojects/sub/meson.build diff --git a/test cases/unit/94 clangformat/.clang-format b/test cases/unit/93 clangformat/.clang-format similarity index 100% rename from test cases/unit/94 clangformat/.clang-format rename to test cases/unit/93 clangformat/.clang-format diff --git a/test cases/unit/94 clangformat/.clang-format-ignore b/test cases/unit/93 clangformat/.clang-format-ignore similarity index 100% rename from test cases/unit/94 clangformat/.clang-format-ignore rename to test cases/unit/93 clangformat/.clang-format-ignore diff --git a/test cases/unit/94 clangformat/.clang-format-include b/test cases/unit/93 clangformat/.clang-format-include similarity index 100% rename from test cases/unit/94 clangformat/.clang-format-include rename to test cases/unit/93 clangformat/.clang-format-include diff --git a/test cases/unit/94 clangformat/meson.build b/test cases/unit/93 clangformat/meson.build similarity index 100% rename from test cases/unit/94 clangformat/meson.build rename to test cases/unit/93 clangformat/meson.build diff --git a/test cases/unit/94 clangformat/not-included/badformat.cpp b/test cases/unit/93 clangformat/not-included/badformat.cpp similarity index 100% rename from test cases/unit/94 clangformat/not-included/badformat.cpp rename to test cases/unit/93 clangformat/not-included/badformat.cpp diff --git a/test cases/unit/94 clangformat/src/badformat.c b/test cases/unit/93 clangformat/src/badformat.c similarity index 100% rename from test cases/unit/94 clangformat/src/badformat.c rename to test cases/unit/93 clangformat/src/badformat.c diff --git a/test cases/unit/94 clangformat/src/badformat.cpp b/test cases/unit/93 clangformat/src/badformat.cpp similarity index 100% rename from test cases/unit/94 clangformat/src/badformat.cpp rename to test cases/unit/93 clangformat/src/badformat.cpp diff --git a/test cases/unit/95 custominc/easytogrepfor/genh.py b/test cases/unit/94 custominc/easytogrepfor/genh.py similarity index 100% rename from test cases/unit/95 custominc/easytogrepfor/genh.py rename to test cases/unit/94 custominc/easytogrepfor/genh.py diff --git a/test cases/unit/95 custominc/easytogrepfor/meson.build b/test cases/unit/94 custominc/easytogrepfor/meson.build similarity index 100% rename from test cases/unit/95 custominc/easytogrepfor/meson.build rename to test cases/unit/94 custominc/easytogrepfor/meson.build diff --git a/test cases/unit/95 custominc/helper.c b/test cases/unit/94 custominc/helper.c similarity index 100% rename from test cases/unit/95 custominc/helper.c rename to test cases/unit/94 custominc/helper.c diff --git a/test cases/unit/95 custominc/meson.build b/test cases/unit/94 custominc/meson.build similarity index 100% rename from test cases/unit/95 custominc/meson.build rename to test cases/unit/94 custominc/meson.build diff --git a/test cases/unit/95 custominc/prog.c b/test cases/unit/94 custominc/prog.c similarity index 100% rename from test cases/unit/95 custominc/prog.c rename to test cases/unit/94 custominc/prog.c diff --git a/test cases/unit/95 custominc/prog2.c b/test cases/unit/94 custominc/prog2.c similarity index 100% rename from test cases/unit/95 custominc/prog2.c rename to test cases/unit/94 custominc/prog2.c diff --git a/test cases/unit/96 implicit force fallback/meson.build b/test cases/unit/95 implicit force fallback/meson.build similarity index 100% rename from test cases/unit/96 implicit force fallback/meson.build rename to test cases/unit/95 implicit force fallback/meson.build diff --git a/test cases/unit/96 implicit force fallback/subprojects/something/meson.build b/test cases/unit/95 implicit force fallback/subprojects/something/meson.build similarity index 100% rename from test cases/unit/96 implicit force fallback/subprojects/something/meson.build rename to test cases/unit/95 implicit force fallback/subprojects/something/meson.build diff --git a/test cases/unit/97 compiler.links file arg/meson.build b/test cases/unit/96 compiler.links file arg/meson.build similarity index 100% rename from test cases/unit/97 compiler.links file arg/meson.build rename to test cases/unit/96 compiler.links file arg/meson.build diff --git a/test cases/unit/97 compiler.links file arg/test.c b/test cases/unit/96 compiler.links file arg/test.c similarity index 100% rename from test cases/unit/97 compiler.links file arg/test.c rename to test cases/unit/96 compiler.links file arg/test.c diff --git a/test cases/unit/98 link full name/.gitignore b/test cases/unit/97 link full name/.gitignore similarity index 100% rename from test cases/unit/98 link full name/.gitignore rename to test cases/unit/97 link full name/.gitignore diff --git a/test cases/unit/98 link full name/libtestprovider/meson.build b/test cases/unit/97 link full name/libtestprovider/meson.build similarity index 100% rename from test cases/unit/98 link full name/libtestprovider/meson.build rename to test cases/unit/97 link full name/libtestprovider/meson.build diff --git a/test cases/unit/98 link full name/libtestprovider/provider.c b/test cases/unit/97 link full name/libtestprovider/provider.c similarity index 100% rename from test cases/unit/98 link full name/libtestprovider/provider.c rename to test cases/unit/97 link full name/libtestprovider/provider.c diff --git a/test cases/unit/98 link full name/proguser/meson.build b/test cases/unit/97 link full name/proguser/meson.build similarity index 100% rename from test cases/unit/98 link full name/proguser/meson.build rename to test cases/unit/97 link full name/proguser/meson.build diff --git a/test cases/unit/98 link full name/proguser/receiver.c b/test cases/unit/97 link full name/proguser/receiver.c similarity index 100% rename from test cases/unit/98 link full name/proguser/receiver.c rename to test cases/unit/97 link full name/proguser/receiver.c diff --git a/test cases/unit/99 install all targets/bar-custom.txt b/test cases/unit/98 install all targets/bar-custom.txt similarity index 100% rename from test cases/unit/99 install all targets/bar-custom.txt rename to test cases/unit/98 install all targets/bar-custom.txt diff --git a/test cases/unit/99 install all targets/bar-devel.h b/test cases/unit/98 install all targets/bar-devel.h similarity index 100% rename from test cases/unit/99 install all targets/bar-devel.h rename to test cases/unit/98 install all targets/bar-devel.h diff --git a/test cases/unit/99 install all targets/bar-notag.txt b/test cases/unit/98 install all targets/bar-notag.txt similarity index 100% rename from test cases/unit/99 install all targets/bar-notag.txt rename to test cases/unit/98 install all targets/bar-notag.txt diff --git a/test cases/unit/99 install all targets/custom_files/data.txt b/test cases/unit/98 install all targets/custom_files/data.txt similarity index 100% rename from test cases/unit/99 install all targets/custom_files/data.txt rename to test cases/unit/98 install all targets/custom_files/data.txt diff --git a/test cases/unit/99 install all targets/excludes/excluded.txt b/test cases/unit/98 install all targets/excludes/excluded.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/excluded.txt rename to test cases/unit/98 install all targets/excludes/excluded.txt diff --git a/test cases/unit/99 install all targets/excludes/excluded/placeholder.txt b/test cases/unit/98 install all targets/excludes/excluded/placeholder.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/excluded/placeholder.txt rename to test cases/unit/98 install all targets/excludes/excluded/placeholder.txt diff --git a/test cases/unit/99 install all targets/excludes/installed.txt b/test cases/unit/98 install all targets/excludes/installed.txt similarity index 100% rename from test cases/unit/99 install all targets/excludes/installed.txt rename to test cases/unit/98 install all targets/excludes/installed.txt diff --git a/test cases/unit/99 install all targets/foo.in b/test cases/unit/98 install all targets/foo.in similarity index 100% rename from test cases/unit/99 install all targets/foo.in rename to test cases/unit/98 install all targets/foo.in diff --git a/test cases/unit/99 install all targets/foo1-devel.h b/test cases/unit/98 install all targets/foo1-devel.h similarity index 100% rename from test cases/unit/99 install all targets/foo1-devel.h rename to test cases/unit/98 install all targets/foo1-devel.h diff --git a/test cases/unit/99 install all targets/lib.c b/test cases/unit/98 install all targets/lib.c similarity index 100% rename from test cases/unit/99 install all targets/lib.c rename to test cases/unit/98 install all targets/lib.c diff --git a/test cases/unit/99 install all targets/main.c b/test cases/unit/98 install all targets/main.c similarity index 100% rename from test cases/unit/99 install all targets/main.c rename to test cases/unit/98 install all targets/main.c diff --git a/test cases/unit/99 install all targets/meson.build b/test cases/unit/98 install all targets/meson.build similarity index 100% rename from test cases/unit/99 install all targets/meson.build rename to test cases/unit/98 install all targets/meson.build diff --git a/test cases/unit/99 install all targets/script.py b/test cases/unit/98 install all targets/script.py similarity index 100% rename from test cases/unit/99 install all targets/script.py rename to test cases/unit/98 install all targets/script.py diff --git a/test cases/unit/99 install all targets/subdir/bar2-devel.h b/test cases/unit/98 install all targets/subdir/bar2-devel.h similarity index 100% rename from test cases/unit/99 install all targets/subdir/bar2-devel.h rename to test cases/unit/98 install all targets/subdir/bar2-devel.h diff --git a/test cases/unit/99 install all targets/subdir/foo2.in b/test cases/unit/98 install all targets/subdir/foo2.in similarity index 100% rename from test cases/unit/99 install all targets/subdir/foo2.in rename to test cases/unit/98 install all targets/subdir/foo2.in diff --git a/test cases/unit/99 install all targets/subdir/foo3-devel.h b/test cases/unit/98 install all targets/subdir/foo3-devel.h similarity index 100% rename from test cases/unit/99 install all targets/subdir/foo3-devel.h rename to test cases/unit/98 install all targets/subdir/foo3-devel.h diff --git a/test cases/unit/99 install all targets/subdir/lib.c b/test cases/unit/98 install all targets/subdir/lib.c similarity index 100% rename from test cases/unit/99 install all targets/subdir/lib.c rename to test cases/unit/98 install all targets/subdir/lib.c diff --git a/test cases/unit/99 install all targets/subdir/main.c b/test cases/unit/98 install all targets/subdir/main.c similarity index 100% rename from test cases/unit/99 install all targets/subdir/main.c rename to test cases/unit/98 install all targets/subdir/main.c diff --git a/test cases/unit/99 install all targets/subdir/meson.build b/test cases/unit/98 install all targets/subdir/meson.build similarity index 100% rename from test cases/unit/99 install all targets/subdir/meson.build rename to test cases/unit/98 install all targets/subdir/meson.build diff --git a/test cases/unit/99 install all targets/subdir/script.py b/test cases/unit/98 install all targets/subdir/script.py similarity index 100% rename from test cases/unit/99 install all targets/subdir/script.py rename to test cases/unit/98 install all targets/subdir/script.py diff --git a/test cases/unit/99 install all targets/subprojects/subproject/aaa.txt b/test cases/unit/98 install all targets/subprojects/subproject/aaa.txt similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/aaa.txt rename to test cases/unit/98 install all targets/subprojects/subproject/aaa.txt diff --git a/test cases/unit/99 install all targets/subprojects/subproject/bbb.txt b/test cases/unit/98 install all targets/subprojects/subproject/bbb.txt similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/bbb.txt rename to test cases/unit/98 install all targets/subprojects/subproject/bbb.txt diff --git a/test cases/unit/99 install all targets/subprojects/subproject/meson.build b/test cases/unit/98 install all targets/subprojects/subproject/meson.build similarity index 100% rename from test cases/unit/99 install all targets/subprojects/subproject/meson.build rename to test cases/unit/98 install all targets/subprojects/subproject/meson.build diff --git a/test cases/unit/100 custom target name/file.txt.in b/test cases/unit/99 custom target name/file.txt.in similarity index 100% rename from test cases/unit/100 custom target name/file.txt.in rename to test cases/unit/99 custom target name/file.txt.in diff --git a/test cases/unit/100 custom target name/meson.build b/test cases/unit/99 custom target name/meson.build similarity index 100% rename from test cases/unit/100 custom target name/meson.build rename to test cases/unit/99 custom target name/meson.build diff --git a/test cases/unit/100 custom target name/subdir/meson.build b/test cases/unit/99 custom target name/subdir/meson.build similarity index 100% rename from test cases/unit/100 custom target name/subdir/meson.build rename to test cases/unit/99 custom target name/subdir/meson.build diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 71f8828547dd..412723cc53a2 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -488,7 +488,7 @@ def test_replace_unencodable_xml_chars_unit(self): ''' if not shutil.which('xmllint'): raise SkipTest('xmllint not installed') - testdir = os.path.join(self.unit_test_dir, '112 replace unencodable xml chars') + testdir = os.path.join(self.unit_test_dir, '111 replace unencodable xml chars') self.init(testdir) tests_command_output = self.run_tests() junit_xml_logs = Path(self.logdir, 'testlog.junit.xml') @@ -648,7 +648,7 @@ def test_forcefallback(self): self.run_tests() def test_implicit_forcefallback(self): - testdir = os.path.join(self.unit_test_dir, '96 implicit force fallback') + testdir = os.path.join(self.unit_test_dir, '95 implicit force fallback') with self.assertRaises(subprocess.CalledProcessError): self.init(testdir) self.init(testdir, extra_args=['--wrap-mode=forcefallback']) @@ -2082,7 +2082,7 @@ def check_pcfile(name, *, relocatable, levels=2): check_pcfile('libvartest2.pc', relocatable=False) self.wipe() - testdir_abs = os.path.join(self.unit_test_dir, '106 pkgconfig relocatable with absolute path') + testdir_abs = os.path.join(self.unit_test_dir, '105 pkgconfig relocatable with absolute path') self.init(testdir_abs) check_pcfile('libsimple.pc', relocatable=True, levels=3) @@ -2164,7 +2164,7 @@ def get_opt(): self.assertDictEqual(original, expected) def test_executable_names(self): - testdir = os.path.join(self.unit_test_dir, '122 executable suffix') + testdir = os.path.join(self.unit_test_dir, '121 executable suffix') self.init(testdir) self.build() exe1 = os.path.join(self.builddir, 'foo' + exe_suffix) @@ -2255,7 +2255,7 @@ def test_options_with_choices_changing(self) -> None: def test_options_listed_in_build_options(self) -> None: """Detect when changed options become listed in build options.""" - testdir = os.path.join(self.unit_test_dir, '114 list build options') + testdir = os.path.join(self.unit_test_dir, '113 list build options') out = self.init(testdir) for line in out.splitlines(): @@ -2493,7 +2493,7 @@ def test_check_module_linking(self): self.assertIn(msg, out) def test_mixed_language_linker_check(self): - testdir = os.path.join(self.unit_test_dir, '97 compiler.links file arg') + testdir = os.path.join(self.unit_test_dir, '96 compiler.links file arg') self.init(testdir) cmds = self.get_meson_log_compiler_checks() self.assertEqual(len(cmds), 5) @@ -4331,7 +4331,7 @@ def test_build_b_options(self) -> None: self.init(srcdir, extra_args=['-Dbuild.b_lto=true']) def test_install_skip_subprojects(self): - testdir = os.path.join(self.unit_test_dir, '92 install skip subprojects') + testdir = os.path.join(self.unit_test_dir, '91 install skip subprojects') self.init(testdir) self.build() @@ -4378,7 +4378,7 @@ def check_installed_files(extra_args, expected): check_installed_files(['--skip-subprojects', 'another'], all_expected) def test_adding_subproject_to_configure_project(self) -> None: - srcdir = os.path.join(self.unit_test_dir, '93 new subproject in configured project') + srcdir = os.path.join(self.unit_test_dir, '92 new subproject in configured project') self.init(srcdir) self.build() self.setconf('-Duse-sub=true') @@ -4428,7 +4428,7 @@ def test_clang_format_check(self): if not shutil.which('clang-format'): raise SkipTest('clang-format not found') - testdir = os.path.join(self.unit_test_dir, '94 clangformat') + testdir = os.path.join(self.unit_test_dir, '93 clangformat') newdir = os.path.join(self.builddir, 'testdir') shutil.copytree(testdir, newdir) self.new_builddir() @@ -4453,7 +4453,7 @@ def test_clang_format_check(self): self.build('clang-format-check') def test_custom_target_implicit_include(self): - testdir = os.path.join(self.unit_test_dir, '95 custominc') + testdir = os.path.join(self.unit_test_dir, '94 custominc') self.init(testdir) self.build() compdb = self.get_compdb() @@ -4499,7 +4499,7 @@ def DISABLED_test_env_flags_to_linker(self) -> None: # self.assertEqual(sorted(link_args), sorted(['-flto'])) def test_install_tag(self) -> None: - testdir = os.path.join(self.unit_test_dir, '99 install all targets') + testdir = os.path.join(self.unit_test_dir, '98 install all targets') self.init(testdir) self.build() @@ -4670,7 +4670,7 @@ def test_install_script_dry_run(self): def test_introspect_install_plan(self): - testdir = os.path.join(self.unit_test_dir, '99 install all targets') + testdir = os.path.join(self.unit_test_dir, '98 install all targets') introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json') self.init(testdir) self.assertPathExists(introfile) @@ -4985,7 +4985,7 @@ def test_rust_rlib_linkage(self) -> None: }} ''') - testdir = os.path.join(self.unit_test_dir, '102 rlib linkage') + testdir = os.path.join(self.unit_test_dir, '101 rlib linkage') gen_file = os.path.join(testdir, 'lib.rs') with open(gen_file, 'w', encoding='utf-8') as f: f.write(template.format(0)) @@ -5033,7 +5033,7 @@ def test_bindgen_drops_invalid(self) -> None: return def test_custom_target_name(self): - testdir = os.path.join(self.unit_test_dir, '100 custom target name') + testdir = os.path.join(self.unit_test_dir, '99 custom target name') self.init(testdir) out = self.build() if self.backend is Backend.ninja: @@ -5041,7 +5041,7 @@ def test_custom_target_name(self): self.assertIn('Generating subdir/file.txt with a custom command', out) def test_symlinked_subproject(self): - testdir = os.path.join(self.unit_test_dir, '108 subproject symlink') + testdir = os.path.join(self.unit_test_dir, '107 subproject symlink') subproject_dir = os.path.join(testdir, 'subprojects') subproject = os.path.join(testdir, 'symlinked_subproject') symlinked_subproject = os.path.join(testdir, 'subprojects', 'symlinked_subproject') @@ -5057,7 +5057,7 @@ def test_symlinked_subproject(self): self.build() def test_configure_same_noop(self): - testdir = os.path.join(self.unit_test_dir, '110 configure same noop') + testdir = os.path.join(self.unit_test_dir, '109 configure same noop') args = [ '-Dstring=val', '-Dboolean=true', @@ -5095,7 +5095,7 @@ def test_configure_same_noop(self): def __test_multi_stds(self, test_c: bool = True, test_objc: bool = False) -> None: assert test_c or test_objc, 'must test something' - testdir = os.path.join(self.unit_test_dir, '116 c cpp stds') + testdir = os.path.join(self.unit_test_dir, '115 c cpp stds') self.init(testdir, extra_args=[f'-Dwith-c={str(test_c).lower()}', f'-Dwith-objc={str(test_objc).lower()}']) # Invalid values should fail whatever compiler we have @@ -5154,7 +5154,7 @@ def test_c_cpp_objc_objcpp_stds(self) -> None: self.__test_multi_stds(test_objc=True) def test_slice(self): - testdir = os.path.join(self.unit_test_dir, '124 test slice') + testdir = os.path.join(self.unit_test_dir, '126 test slice') self.init(testdir) self.build() @@ -5192,7 +5192,7 @@ def test_rsp_support(self): self.assertEqual(cc.linker.get_accepts_rsp(), has_rsp) def test_nonexisting_bargs(self): - testdir = os.path.join(self.unit_test_dir, '117 empty project') + testdir = os.path.join(self.unit_test_dir, '116 empty project') args = ['-Db_ndebug=if_release'] self.init(testdir, extra_args=args) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 03b57b9f8bec..db7068d5b03f 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1363,7 +1363,7 @@ def test_link_arg_fullname(self): see: https://github.com/mesonbuild/meson/issues/9000 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a ''' - testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider') + testdir = os.path.join(self.unit_test_dir, '97 link full name','libtestprovider') oldprefix = self.prefix # install into installdir without using DESTDIR installdir = self.installdir @@ -1376,7 +1376,7 @@ def test_link_arg_fullname(self): self.new_builddir() env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir), 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))} - testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser') + testdir = os.path.join(self.unit_test_dir, '97 link full name','proguser') self.init(testdir,override_envvars=env) # test for link with full path @@ -1782,7 +1782,7 @@ def test_cmake_multilib(self): @skipUnless(is_linux() or is_osx(), 'Test only applicable to Linux and macOS') def test_install_strip(self): - testdir = os.path.join(self.unit_test_dir, '104 strip') + testdir = os.path.join(self.unit_test_dir, '103 strip') self.init(testdir) self.build() @@ -1829,7 +1829,7 @@ def test_isystem_default_removal_with_symlink(self): self.assertFalse(cpp.compiler_args([f'-isystem{symlink}' for symlink in default_symlinks]).to_native()) def test_freezing(self): - testdir = os.path.join(self.unit_test_dir, '111 freeze') + testdir = os.path.join(self.unit_test_dir, '110 freeze') self.init(testdir) self.build() with self.assertRaises(subprocess.CalledProcessError) as e: @@ -1838,7 +1838,7 @@ def test_freezing(self): @skipUnless(is_linux(), "Ninja file differs on different platforms") def test_complex_link_cases(self): - testdir = os.path.join(self.unit_test_dir, '115 complex link cases') + testdir = os.path.join(self.unit_test_dir, '114 complex link cases') self.init(testdir) self.build() with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as f: @@ -1859,7 +1859,7 @@ def test_complex_link_cases(self): 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, '124 pkgsubproj') + testdir = os.path.join(self.unit_test_dir, '125 pkgsubproj') self.init(testdir) def check_has_flag(self, compdb, src, argument): @@ -1873,7 +1873,7 @@ def test_persp_options(self): if self.backend is not Backend.ninja: raise SkipTest(f'{self.backend.name!r} backend can\'t install files') - testdir = os.path.join(self.unit_test_dir, '123 persp options') + testdir = os.path.join(self.unit_test_dir, '122 persp options') with self.subTest('init'): self.init(testdir, extra_args='-Doptimization=1') @@ -1926,7 +1926,7 @@ def test_persp_options(self): self.check_has_flag(compdb, sub2src, '-O2') def test_sanitizers(self): - testdir = os.path.join(self.unit_test_dir, '125 sanitizers') + testdir = os.path.join(self.unit_test_dir, '127 sanitizers') with self.subTest('no b_sanitize value'): try: diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 9aa1eb4d1a80..b2839e6b289a 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -388,7 +388,7 @@ def test_java_compiler(self): def test_java_classpath(self): if self.backend is not Backend.ninja: raise SkipTest('Jar is only supported with Ninja') - testdir = os.path.join(self.unit_test_dir, '113 classpath') + testdir = os.path.join(self.unit_test_dir, '112 classpath') self.init(testdir) self.build() one_build_path = get_classpath(os.path.join(self.builddir, 'one.jar')) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 00106089cdac..d6c00787ccbd 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -33,7 +33,7 @@ def test_relative_find_program(self): Tests that find_program() with a relative path does not find the program in current workdir. ''' - testdir = os.path.join(self.unit_test_dir, '101 relative find program') + testdir = os.path.join(self.unit_test_dir, '100 relative find program') self.init(testdir, workdir=testdir) def test_invalid_option_names(self): @@ -92,11 +92,11 @@ def write_file(code: str): interp.process, fname) def test_python_dependency_without_pkgconfig(self): - testdir = os.path.join(self.unit_test_dir, '103 python without pkgconfig') + testdir = os.path.join(self.unit_test_dir, '102 python without pkgconfig') self.init(testdir, override_envvars={'PKG_CONFIG': 'notfound'}) def test_debug_function_outputs_to_meson_log(self): - testdir = os.path.join(self.unit_test_dir, '105 debug function') + 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' output = self.init(testdir) @@ -108,7 +108,7 @@ def test_debug_function_outputs_to_meson_log(self): self.assertIn(log_msg, mesonlog) def test_new_subproject_reconfigure(self): - testdir = os.path.join(self.unit_test_dir, '109 new subproject on reconfigure') + testdir = os.path.join(self.unit_test_dir, '108 new subproject on reconfigure') self.init(testdir) self.build() @@ -271,7 +271,7 @@ def test_setup_loaded_modules(self): thing to do as new features are added, but keeping track of them is good. ''' - testdir = os.path.join(self.unit_test_dir, '117 empty project') + testdir = os.path.join(self.unit_test_dir, '116 empty project') self.init(testdir) self._run(self.meson_command + ['--internal', 'regenerate', '--profile-self', testdir, self.builddir]) @@ -286,7 +286,7 @@ def test_setup_loaded_modules(self): def test_meson_package_cache_dir(self): # Copy testdir into temporary directory to not pollute meson source tree. - testdir = os.path.join(self.unit_test_dir, '119 meson package cache dir') + testdir = os.path.join(self.unit_test_dir, '118 meson package cache dir') srcdir = os.path.join(self.builddir, 'srctree') shutil.copytree(testdir, srcdir) builddir = os.path.join(srcdir, '_build') @@ -295,7 +295,7 @@ def test_meson_package_cache_dir(self): def test_cmake_openssl_not_found_bug(self): """Issue #12098""" - testdir = os.path.join(self.unit_test_dir, '120 openssl cmake bug') + testdir = os.path.join(self.unit_test_dir, '119 openssl cmake bug') self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini')) out = self.init(testdir, allow_fail=True) self.assertNotIn('Unhandled python exception', out) @@ -521,7 +521,7 @@ def test_configure_new_option_subproject(self) -> None: self.assertEqual(self.getconf('subproject:new_option'), True) def test_mtest_rebuild_deps(self): - testdir = os.path.join(self.unit_test_dir, '107 underspecified mtest') + testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') self.init(testdir) with self.assertRaises(subprocess.CalledProcessError): diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 169c0350cc3c..57a6782dd383 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -391,7 +391,7 @@ def test_target_remove_extra_files(self): self.assertDictEqual(out, expected) def test_raw_printer_is_idempotent(self): - test_path = Path(self.unit_test_dir, '121 rewrite') + test_path = Path(self.unit_test_dir, '120 rewrite') meson_build_file = test_path / 'meson.build' # original_contents = meson_build_file.read_bytes() original_contents = meson_build_file.read_text(encoding='utf-8') diff --git a/unittests/windowstests.py b/unittests/windowstests.py index 9506a75efc98..7fa4ab286db9 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -185,7 +185,7 @@ def test_genvslite(self): if self.backend is not Backend.ninja: raise SkipTest('Test only applies when using the Ninja backend') - testdir = os.path.join(self.unit_test_dir, '118 genvslite') + testdir = os.path.join(self.unit_test_dir, '117 genvslite') env = get_fake_env(testdir, self.builddir, self.prefix) cc = detect_c_compiler(env, MachineChoice.HOST) From 59d3c427f53e87df2d10e8e314f1789fc9d6ddfb Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 15 Apr 2025 00:42:50 +0300 Subject: [PATCH 487/624] Fix version requirement. --- test cases/frameworks/38 gettext extractor/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test cases/frameworks/38 gettext extractor/meson.build b/test cases/frameworks/38 gettext extractor/meson.build index 962905a4e63c..9a54df542e18 100644 --- a/test cases/frameworks/38 gettext extractor/meson.build +++ b/test cases/frameworks/38 gettext extractor/meson.build @@ -2,7 +2,7 @@ project( 'gettext extractor', 'c', default_options: {'default_library': 'static'}, - meson_version: '1.8.0', + meson_version: '>=1.8.0', ) if not find_program('xgettext', required: false).found() From 48fc3ec8fa25c0cb001603f025ab516e8f0df1e2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 Apr 2025 10:18:42 +0200 Subject: [PATCH 488/624] environment: handle all iOS variants as xnu All of iOS, tvOS, visionOS, watchOS use the XNU kernel. Report that and also make them return true for is_darwin() which is really more like "is_xnu()". Co-authored-by: Russell Keith-Magee Signed-off-by: Paolo Bonzini --- docs/markdown/Reference-tables.md | 2 ++ mesonbuild/envconfig.py | 4 ++-- mesonbuild/environment.py | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 1c5f9a33a4f3..a5d27858e473 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -208,6 +208,8 @@ Meson natively. | ios-simulator | | | tvos | Apple tvOS | | tvos-simulator | | +| visionos | Apple visionOS | +| visionos-simulator | | | watchos | Apple watchOS | | watchos-simulator | | diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index c877a7c5a78e..43fad0cd2ac4 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -320,9 +320,9 @@ def is_linux(self) -> bool: def is_darwin(self) -> bool: """ - Machine is Darwin (iOS/tvOS/OS X)? + Machine is Darwin (macOS/iOS/tvOS/visionOS/watchOS)? """ - return self.system in {'darwin', 'ios', 'tvos'} + return self.system in {'darwin', 'ios', 'tvos', 'visionos', 'watchos'} def is_android(self) -> bool: """ diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index a0d98ae581ae..f322cda95cc9 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -487,6 +487,10 @@ def detect_cpu(compilers: CompilersDict) -> str: 'linux': 'linux', 'cygwin': 'nt', 'darwin': 'xnu', + 'ios': 'xnu', + 'tvos': 'xnu', + 'visionos': 'xnu', + 'watchos': 'xnu', 'dragonfly': 'dragonfly', 'haiku': 'haiku', 'gnu': 'gnu', From cece1a7e9992a3bbcc35f90777ba22ba9d62b538 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 Apr 2025 10:27:20 +0200 Subject: [PATCH 489/624] linkers: pass system to DynamicLinker.__init__ for Darwin linkers Apple linkers need to use different arguments on macOS and iOS-like platforms. Pass the system to the constructor so that it can be examined. Co-authored-by: Russell Keith-Magee Signed-off-by: Paolo Bonzini --- mesonbuild/compilers/detect.py | 4 ++-- mesonbuild/linkers/detect.py | 13 ++++++++++--- mesonbuild/linkers/linkers.py | 4 +++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 9dab06a85a63..53bdd85131d9 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1122,8 +1122,8 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust version=cc.linker.version, **extra_args) # type: ignore else: linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX, - always_args=always_args, version=cc.linker.version, - **extra_args) + always_args=always_args, system=cc.linker.system, + version=cc.linker.version, **extra_args) elif 'link' in override[0]: linker = guess_win_linker(env, override, cls, version, for_machine, use_linker_prefix=False) diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index 493430a87cee..ee9bb0861e4f 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -131,6 +131,7 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env) extra_args = extra_args or [] + system = env.machines[for_machine].system ldflags = env.coredata.get_external_link_args(for_machine, comp_class.language) extra_args += comp_class._unix_args_to_native(ldflags, env.machines[for_machine]) @@ -164,7 +165,7 @@ 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, version=v) + compiler, for_machine, comp_class.LINKER_PREFIX, override, system=system, version=v) elif 'Snapdragon' in e and 'LLVM' in e: linker = linkers.QualcommLLVMDynamicLinker( compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v) @@ -222,7 +223,10 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty 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, version=v) + linker = linkers.AppleDynamicLinker( + compiler, for_machine, comp_class.LINKER_PREFIX, override, + system=system, version=v + ) # detect linker on MacOS - must be after other platforms because the # "(use -v to see invocation)" will match clang on other platforms, # but the rest of the checks will fail and call __failed_to_detect_linker. @@ -241,7 +245,10 @@ def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Ty break else: __failed_to_detect_linker(compiler, check_args, o, e) - linker = linkers.AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v) + linker = linkers.AppleDynamicLinker( + compiler, for_machine, comp_class.LINKER_PREFIX, override, + system=system, version=v + ) 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 e74ff708d045..47a76198e159 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -141,9 +141,11 @@ def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: 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'): + always_args: T.List[str], *, system: str = 'unknown system', + version: str = 'unknown version'): self.exelist = exelist self.for_machine = for_machine + self.system = system self.version = version self.prefix_arg = prefix_arg self.always_args = always_args From 7a43b6e318d7a35f0ce250e80a9a04b6b2983f05 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 Apr 2025 10:32:37 +0200 Subject: [PATCH 490/624] linkers: apple: fix shared module args iOS should not use -undefined,dynamic_lookup; whereas macOS should revert from -dynamiclib to -bundle, as was the case before commit cfb5a48e0 ("linkers: darwin: do not use -bundle for shared_modules", 2025-03-24), because Postgres wants to use -bundle_loader and that is only possible together with -bundle. Co-authored-by: Russell Keith-Magee Signed-off-by: Paolo Bonzini --- mesonbuild/linkers/linkers.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 47a76198e159..617d4ad3cf87 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -811,10 +811,17 @@ def get_asneeded_args(self) -> T.List[str]: return self._apply_prefix('-dead_strip_dylibs') def get_allow_undefined_args(self) -> T.List[str]: - return self._apply_prefix('-undefined,dynamic_lookup') + # iOS doesn't allow undefined symbols when linking + if self.system == 'ios': + return [] + else: + return self._apply_prefix('-undefined,dynamic_lookup') def get_std_shared_module_args(self, target: 'BuildTarget') -> T.List[str]: - return ['-dynamiclib'] + self._apply_prefix('-undefined,dynamic_lookup') + if self.system == 'ios': + return ['-dynamiclib'] + else: + return ['-bundle'] + self.get_allow_undefined_args() def get_pie_args(self) -> T.List[str]: return [] From ce7e1876303071672b5e4b3f1182668bb43d68bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Mon, 17 Mar 2025 00:23:48 +0100 Subject: [PATCH 491/624] compilers/rust: remove CRT selection from native_static_libs args This will be handled by target binary link. And if it's not compatible with what Rust uses, it wouldn't work anyway. --- mesonbuild/compilers/rust.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 3e9c016f6529..d0d2e69acb52 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -170,7 +170,7 @@ def _native_static_libs(self, work_dir: str, source_name: str) -> None: # are always part of C/C++ linkers. Rustc probably should not print # them, pkg-config for example never specify them. # FIXME: https://github.com/rust-lang/rust/issues/55120 - exclude = {'-lc', '-lgcc_s', '-lkernel32', '-ladvapi32'} + exclude = {'-lc', '-lgcc_s', '-lkernel32', '-ladvapi32', '/defaultlib:msvcrt'} self.native_static_libs = [i for i in match.group(1).split() if i not in exclude] def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: From e574889f68c5a9c6d94344db731405809843ed37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Mon, 17 Mar 2025 00:20:11 +0100 Subject: [PATCH 492/624] ninjabackend: ensure that native static libraries use Unix-style naming Depending on the target/linker, rustc --print native-static-libs may output MSVC-style names. Converting these to Unix-style is safe, as the list contains only native static libraries. Fixes linking with C targets built with clang on x86_64-pc-windows-msvc target. Fixes: #14366 --- mesonbuild/backend/ninjabackend.py | 10 +++++++++- mesonbuild/build.py | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8627960e8419..d7de987990c8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3647,7 +3647,15 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. for d in target.get_dependencies(): if isinstance(d, build.StaticLibrary): for dep in d.get_external_deps(): - commands.extend_preserving_lflags(linker.get_dependency_link_args(dep)) + link_args = linker.get_dependency_link_args(dep) + # Ensure that native static libraries use Unix-style naming if necessary. + # Depending on the target/linker, rustc --print native-static-libs may + # output MSVC-style names. Converting these to Unix-style is safe, as the + # list contains only native static libraries. + if dep.name == '_rust_native_static_libs' and linker.get_argument_syntax() != 'msvc': + from ..linkers.linkers import VisualStudioLikeLinker + link_args = VisualStudioLikeLinker.native_args_to_unix(link_args) + commands.extend_preserving_lflags(link_args) # Add link args specific to this BuildTarget type that must not be overridden by dependencies commands += self.get_target_type_link_args_post_dependencies(target, linker) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e94f75faa1f3..0e8fb59e4334 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2173,7 +2173,8 @@ def post_init(self) -> None: rustc = self.compilers['rust'] d = dependencies.InternalDependency('undefined', [], [], rustc.native_static_libs, - [], [], [], [], [], {}, [], [], []) + [], [], [], [], [], {}, [], [], [], + '_rust_native_static_libs') self.external_deps.append(d) # By default a static library is named libfoo.a even on Windows because # MSVC does not have a consistent convention for what static libraries From 80f26fea5ef2ff1dbd57a16f5a04d45bd2908aba Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 16 Apr 2025 00:07:51 -0400 Subject: [PATCH 493/624] setup.py: crudely label wheels using setuptools_scm if building from git --- setup.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 305005416c60..425d51d33b70 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ # Copyright 2016 The Meson development team -import sys +import os, sys if sys.version_info < (3, 7): raise SystemExit('ERROR: Tried to install Meson with an unsupported Python version: \n{}' @@ -11,10 +11,24 @@ from setuptools import setup +scm_args = {} +HERE = os.path.dirname(__file__) +if os.path.exists(os.path.join(HERE, '.git')): + try: + import setuptools_scm + except ModuleNotFoundError: + pass + else: + sys.path.insert(0, HERE) + from mesonbuild import coredata + + scheme = 'guess-next-dev' if 'rc' in coredata.version else 'release-branch-semver' + scm_args = {'use_scm_version': {'version_scheme': scheme}} + data_files = [] if sys.platform != 'win32': # Only useful on UNIX-like systems data_files = [('share/man/man1', ['man/meson.1']), ('share/polkit-1/actions', ['data/com.mesonbuild.install.policy'])] -setup(data_files=data_files,) +setup(data_files=data_files,**scm_args) From db8938791fe7e8d22e964c78e04b2d4e61638667 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 16 Apr 2025 00:20:56 -0400 Subject: [PATCH 494/624] build nightly wheels whenever a PR is merged Closes https://github.com/mesonbuild/meson/issues/14419 --- .github/workflows/nightly.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 000000000000..ab773f9e03b3 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,32 @@ +name: Nightly Wheels + +concurrency: + group: wheels + cancel-in-progress: true + +on: + push: + branches: + - master + paths: + - "mesonbuild/*" + +permissions: + contents: read + +jobs: + wheel: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Build wheel + run: | + python -m pip install build setuptools_scm + python -m build -nwx + - name: Upload wheel + uses: scientific-python/upload-nightly-action@main + with: + artifacts_path: dist + anaconda_nightly_upload_token: ${{secrets.ANACONDA_ORG_UPLOAD_TOKEN}} From 84b7870caca35d063e35cf407ec804ff91d8f8c7 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 14 Apr 2025 23:43:49 +0300 Subject: [PATCH 495/624] Bump version number for rc1. --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index af8233cde04a..2c8da27a983c 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "January 2025" "meson 1.7.0" "User Commands" +.TH MESON "1" "April 2025" "meson 1.8.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f165fde1065f..36120ff7e7e4 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.7.99' +version = '1.8.0.rc1' # 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 From 944456b5f9b4428853f231368e2baea979042c46 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 16 Apr 2025 16:50:46 -0400 Subject: [PATCH 496/624] linkers: fix regression when using lld after iOS changes ``` $ LDFLAGS="-fuse-ld=lld" meson setup builddir/ [...] linker = lld_cls( compiler, for_machine, comp_class.LINKER_PREFIX, override, system=system, version=v) TypeError: LLVMDynamicLinker.__init__() got an unexpected keyword argument 'system' ``` Fixes regression in commit cece1a7e9992a3bbcc35f90777ba22ba9d62b538. We pass system=system to the linker construction, which worked fine for Apple LLVM LD64 as that inherited `__init__` from DynamicLinker. But non-Apple LLD had an overridden `__init__` which wasn't adapted. Even if it is thrown away and never used, since LLVM isn't an Apple linker, we still need to obey the argument contract. --- mesonbuild/linkers/linkers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 617d4ad3cf87..59f60e03a19c 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -945,8 +945,9 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna 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'): - super().__init__(exelist, for_machine, prefix_arg, always_args, version=version) + 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) # Some targets don't seem to support this argument (windows, wasm, ...) self.has_allow_shlib_undefined = self._supports_flag('--allow-shlib-undefined', always_args) From 02792fa77878b50c10bebeb52b1a3ba2f7bf5f78 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 16 Apr 2025 22:17:01 -0400 Subject: [PATCH 497/624] nightly wheels: fix glob to match across directory separator --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ab773f9e03b3..79332196c173 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -9,7 +9,7 @@ on: branches: - master paths: - - "mesonbuild/*" + - "mesonbuild/**" permissions: contents: read From da28caa63dc855ad897b259e6dbc1615c847fbf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Wed, 16 Apr 2025 22:11:01 +0200 Subject: [PATCH 498/624] build/executable: Respect name_prefix executable did not respect the name_prefix kwarg --- mesonbuild/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0e8fb59e4334..4f40a62d0f09 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2043,6 +2043,8 @@ def post_init(self) -> None: else: self.suffix = machine.get_exe_suffix() self.filename = self.name + if self.prefix: + self.filename = self.prefix + self.filename if self.suffix: self.filename += '.' + self.suffix self.outputs[0] = self.filename From 855cf199fc950de3e764a74e7b545c2213aa601c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22sp1rit=22=E2=80=8B?= Date: Wed, 16 Apr 2025 22:11:38 +0200 Subject: [PATCH 499/624] android: Added android_exe_type kwargs to executable By setting android_exe_type to `application`, the executable gets actually built as a shared library instead of an executable. This makes it possible to use an application within an android application process. mesonbuild#13758 https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/7555/ --- docs/yaml/functions/executable.yaml | 11 +++++++++++ mesonbuild/build.py | 3 ++- mesonbuild/interpreter/interpreter.py | 13 +++++++++++++ mesonbuild/interpreter/kwargs.py | 1 + mesonbuild/interpreter/type_checking.py | 6 ++++++ run_project_tests.py | 4 +++- test cases/android/1 exe_type/exe_type.c | 5 +++++ test cases/android/1 exe_type/meson.build | 15 +++++++++++++++ 8 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 test cases/android/1 exe_type/exe_type.c create mode 100644 test cases/android/1 exe_type/meson.build diff --git a/docs/yaml/functions/executable.yaml b/docs/yaml/functions/executable.yaml index abbc5feee909..df71b79fadc5 100644 --- a/docs/yaml/functions/executable.yaml +++ b/docs/yaml/functions/executable.yaml @@ -21,6 +21,17 @@ varargs_inherit: _build_target_base kwargs_inherit: _build_target_base kwargs: + android_exe_type: + type: str + default: "'executable'" + since: 1.8.0 + description: | + Specifies the intended target of the executable. This can either be + `executable`, if the intended usecase is to run the executable using + fork + exec, or `application` if the executable is supposed to be + loaded as shared object by the android runtime. + + export_dynamic: type: bool since: 0.45.0 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4f40a62d0f09..7320b88fdd4b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -112,7 +112,7 @@ class DFeatures(TypedDict): rust_kwargs | cs_kwargs) -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs'} +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_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'} known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'} @@ -1996,6 +1996,7 @@ def __init__( 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'} # Check for export_dynamic self.export_dynamic = kwargs.get('export_dynamic', False) if not isinstance(self.export_dynamic, bool): diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2cd272db39ff..d3bf86df3e4a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -54,6 +54,7 @@ CT_BUILD_BY_DEFAULT, CT_INPUT_KW, CT_INSTALL_DIR_KW, + _EXCLUSIVE_EXECUTABLE_KWS, EXECUTABLE_KWS, JAR_KWS, LIBRARY_KWS, @@ -1816,12 +1817,24 @@ def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kw 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 + @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: + 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.shared_library_only = True + return holder return self.build_target(node, args, kwargs, build.Executable) @permittedKwargs(build.known_stlib_kwargs) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index c0b74405f6f1..fb34bbbce9c8 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -393,6 +393,7 @@ class Executable(_BuildTarget): pie: T.Optional[bool] vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] win_subsystem: T.Optional[str] + android_exe_type: T.Optional[Literal['application', 'executable']] class _StaticLibMixin(TypedDict): diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 1c9953ee4153..78938ba9cd0c 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -701,6 +701,12 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup convertor=lambda x: x.lower() if isinstance(x, str) else None, validator=_validate_win_subsystem, ), + KwargInfo( + 'android_exe_type', + (str, NoneType), + validator=in_set_validator({'application', 'executable'}), + since='1.8.0' + ), ] # The total list of arguments used by Executable diff --git a/run_project_tests.py b/run_project_tests.py index 0dc287191f21..fa7c8a6eb3dd 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -76,7 +76,7 @@ class ArgumentType(CompilerArgumentType): v: bool ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'failing-build', 'failing-test', - 'keyval', 'platform-osx', 'platform-windows', 'platform-linux', + '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', @@ -1123,6 +1123,8 @@ def __init__(self, category: str, subdir: str, skip: bool = False, stdout_mandat TestCategory('platform-osx', 'osx', not mesonlib.is_osx()), TestCategory('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()), TestCategory('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), + # FIXME, does not actually run in CI, change to run the test if an Android cross toolchain is detected. + TestCategory('platform-android', 'android', not mesonlib.is_android()), TestCategory('java', 'java', backend is not Backend.ninja or not have_java()), TestCategory('C#', 'csharp', skip_csharp(backend)), TestCategory('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), diff --git a/test cases/android/1 exe_type/exe_type.c b/test cases/android/1 exe_type/exe_type.c new file mode 100644 index 000000000000..cd9ca7db9479 --- /dev/null +++ b/test cases/android/1 exe_type/exe_type.c @@ -0,0 +1,5 @@ +#include + +int main(void) { + return 0; +} diff --git a/test cases/android/1 exe_type/meson.build b/test cases/android/1 exe_type/meson.build new file mode 100644 index 000000000000..5b0e64a5182d --- /dev/null +++ b/test cases/android/1 exe_type/meson.build @@ -0,0 +1,15 @@ +project('android exe type', 'c') +fs = import('fs') + +e = executable('executable', 'exe_type.c', + android_exe_type : 'executable') +a = executable('application', 'exe_type.c', + android_exe_type : 'application') + +if fs.name(e.full_path()).contains('.') + error('Executable with exe_type `executable` did have expected filename') +endif + +if not fs.name(a.full_path()).startswith('lib') or not fs.name(a.full_path()).endswith('.so') + error('Executable with exe_type `application` did not have expected filename') +endif From b9db06b2b12a50b95185082910cfef01341fd7db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Apr 2025 17:57:58 +0200 Subject: [PATCH 500/624] fix prefix computation in validate_original_args validate_original_args only checks whether an option is prefixed with the name of a built-in option that was also set. It does not check whether the prefix is the full name of the option specified with -D. Adding an "=" at the end fixes the test. Fixes: #14487 Reported-by: Alyssa Ross Signed-off-by: Paolo Bonzini --- mesonbuild/mesonmain.py | 2 +- test cases/unit/128 long opt vs D/meson.build | 1 + .../unit/128 long opt vs D/meson_options.txt | 1 + unittests/allplatformstests.py | 30 +++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test cases/unit/128 long opt vs D/meson.build create mode 100644 test cases/unit/128 long opt vs D/meson_options.txt diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 6a88501d4564..dd265c41b74c 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -247,7 +247,7 @@ def has_startswith(coll, target): #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/test cases/unit/128 long opt vs D/meson.build b/test cases/unit/128 long opt vs D/meson.build new file mode 100644 index 000000000000..e05d88d200d3 --- /dev/null +++ b/test cases/unit/128 long opt vs D/meson.build @@ -0,0 +1 @@ +project('empty test') diff --git a/test cases/unit/128 long opt vs D/meson_options.txt b/test cases/unit/128 long opt vs D/meson_options.txt new file mode 100644 index 000000000000..255bf1576e02 --- /dev/null +++ b/test cases/unit/128 long opt vs D/meson_options.txt @@ -0,0 +1 @@ +option('sysconfdir2', type: 'string', value: '') diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 412723cc53a2..2fee06c690fa 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1361,6 +1361,36 @@ def test_source_generator_program_cause_rebuild(self): self.utime(os.path.join(testdir, 'srcgen.py')) self.assertRebuiltTarget('basic') + def test_long_opt_vs_D(self): + ''' + 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') + + for opt in ['-Dsysconfdir=/etc', '-Dsysconfdir2=/etc']: + exception_raised = False + try: + self.init(testdir, extra_args=[opt, '--sysconfdir=/etc']) + except subprocess.CalledProcessError: + exception_raised = True + if 'sysconfdir2' in opt: + self.assertFalse(exception_raised, f'{opt} --sysconfdir raised an exception') + else: + self.assertTrue(exception_raised, f'{opt} --sysconfdir did not raise an exception') + + exception_raised = False + try: + self.init(testdir, extra_args=['--sysconfdir=/etc', opt]) + except subprocess.CalledProcessError: + exception_raised = True + if 'sysconfdir2' in opt: + self.assertFalse(exception_raised, f'--sysconfdir {opt} raised an exception') + else: + self.assertTrue(exception_raised, f'--sysconfdir {opt} did not raise an exception') + + self.wipe() + def test_static_library_lto(self): ''' Test that static libraries can be built with LTO and linked to From 95895492f5b25824d80471ab7a850e9cde52bab8 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 18 Apr 2025 16:17:59 -0700 Subject: [PATCH 501/624] Make run_mypy.py work in mypy_primer See also https://github.com/hauntsaninja/mypy_primer/pull/77 --- run_mypy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index 975232a48624..d7d3aaade1ac 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -111,8 +111,6 @@ def check_mypy() -> None: sys.exit(1) def main() -> int: - check_mypy() - root = Path(__file__).absolute().parent parser = argparse.ArgumentParser(description='Process some integers.') @@ -124,6 +122,9 @@ def main() -> int: parser.add_argument('--allver', action='store_true', help='Check all supported versions of python') opts, args = parser.parse_known_args() + if not opts.mypy: + check_mypy() + if opts.pretty: args.append('--pretty') From 7889cac7d5c976a8f11c47bfcb8978ac27b15a61 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sat, 19 Apr 2025 00:21:46 -0500 Subject: [PATCH 502/624] Log non-fatally if CMake doesn't find a package If CMake fails to find a package, we should log it in meson-log.txt, but shouldn't bother the user unless there are details to report. In addition, we were calling mlog.warning() without fatal=False, so if msetup was called with --fatal-meson-warnings we'd fail the entire setup in this case, even for optional dependencies. Log a notice with fatal=False if CMake reported an error, and a debug message otherwise. Notably, the "even though Meson's preliminary check succeeded" case can occur when a dependency is missing but its Find*.cmake is shipped with CMake itself. Fixes: 92c517ea69 ("output PACKAGE_NOT_FOUND_MESSAGE as warning when CMake package is not found") --- mesonbuild/dependencies/cmake.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py index 0c350214ba54..4e449816d344 100644 --- a/mesonbuild/dependencies/cmake.py +++ b/mesonbuild/dependencies/cmake.py @@ -416,11 +416,11 @@ def _detect_dep(self, name: str, package_version: str, modules: T.List[T.Tuple[s if not self.is_found: not_found_message = self.traceparser.get_cmake_var('PACKAGE_NOT_FOUND_MESSAGE') if len(not_found_message) > 0: - mlog.warning( + mlog.notice( 'CMake reported that the package {} was not found with the following reason:\n' - '{}'.format(name, not_found_message[0])) + '{}'.format(name, not_found_message[0]), fatal=False) else: - mlog.warning( + mlog.debug( 'CMake reported that the package {} was not found, ' 'even though Meson\'s preliminary check succeeded.'.format(name)) raise self._gen_exception('PACKAGE_FOUND is false') From 1e59db30be74038c999643c7184a6416fc002c2f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 12:53:19 +0200 Subject: [PATCH 503/624] move "since 1.8.0" for rust.doctest to the right place --- docs/markdown/Rust-module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index d2c478c64239..35eaf39c3ac9 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -24,8 +24,6 @@ like Meson, rather than Meson work more like rust. rustmod.test(name, target, ...) ``` -*Since 1.8.0* - This function creates a new rust unittest target from an existing rust based target, which may be a library or executable. It does this by copying the sources and arguments passed to the original target and @@ -50,6 +48,8 @@ This function also accepts all of the keyword arguments accepted by the rustmod.doctest(name, target, ...) ``` +*Since 1.8.0* + This function creates a new `test()` target from an existing rust based library target. The test will use `rustdoc` to extract and run the doctests that are included in `target`'s sources. From d9cb8836c51aeb9f206466f3f43612ab7c9966cf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 12:57:20 +0200 Subject: [PATCH 504/624] fix typo --- docs/markdown/Releasing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/markdown/Releasing.md b/docs/markdown/Releasing.md index 488ca91a12c5..5fec13c6e6f7 100644 --- a/docs/markdown/Releasing.md +++ b/docs/markdown/Releasing.md @@ -21,4 +21,4 @@ For each new meson release, several different artifacts are created: - Eli Schwartz. PGP key: [BD27B07A5EF45C2ADAF70E0484818A6819AF4A9B](https://keyserver.ubuntu.com/pks/lookup?search=0xBD27B07A5EF45C2ADAF70E0484818A6819AF4A9B&op=index) - Dylan Baker. PGP key: [71C4B75620BC75708B4BDB254C95FAAB3EB073EC](https://keyserver.ubuntu.com/pks/lookup?search=0x71C4B75620BC75708B4BDB254C95FAAB3EB073EC&op=index) -The default release manager for new versions of Meson is Jussi Pakkanen. Starting with meson 1.8.0, the release team has been expanded with fallback options to reduce the bus factor, but but will continue to be done by Jussi when possible. +The default release manager for new versions of Meson is Jussi Pakkanen. Starting with meson 1.8.0, the release team has been expanded with fallback options to reduce the bus factor, but will continue to be done by Jussi when possible. From 514fc7bd5801543f97574da159385a7490b386b7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 12:52:34 +0200 Subject: [PATCH 505/624] add wayland stabilisation to release notes --- docs/markdown/snippets/stabilized-wayland.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/markdown/snippets/stabilized-wayland.md diff --git a/docs/markdown/snippets/stabilized-wayland.md b/docs/markdown/snippets/stabilized-wayland.md new file mode 100644 index 000000000000..3b132e68d542 --- /dev/null +++ b/docs/markdown/snippets/stabilized-wayland.md @@ -0,0 +1,4 @@ +## The Wayland module is stable + +The Wayland module has been tested in several projects and had the +last breaking change in Meson 0.64.0; it is now marked as stable. From 76323ad829e56269ced544eacc434b5e8cf9ef87 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 12:56:33 +0200 Subject: [PATCH 506/624] document -Dswift_std in release notes --- docs/markdown/snippets/swift-std.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/markdown/snippets/swift-std.md diff --git a/docs/markdown/snippets/swift-std.md b/docs/markdown/snippets/swift-std.md new file mode 100644 index 000000000000..64fe70c174ee --- /dev/null +++ b/docs/markdown/snippets/swift-std.md @@ -0,0 +1,4 @@ +## New `swift_std` compiler option + +A new compiler option allows to set the language version that is passed +to swiftc (`none`, `4`, `4.2`, `5` or `6`). From 83c4d8f1fedba1661ae7de56b706cb41eb22d90f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 13:05:03 +0200 Subject: [PATCH 507/624] document improvements to Objective-C and Objective-C++ --- docs/markdown/snippets/objc-cpp.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/markdown/snippets/objc-cpp.md diff --git a/docs/markdown/snippets/objc-cpp.md b/docs/markdown/snippets/objc-cpp.md new file mode 100644 index 000000000000..3d22ccb7ae7e --- /dev/null +++ b/docs/markdown/snippets/objc-cpp.md @@ -0,0 +1,8 @@ +## Improvements to Objective-C and Objective-C++ + +Meson does not assume anymore that gcc/g++ always support +Objective-C and Objective-C++, and instead checks that they +can actually do a basic compile. + +Furthermore, Objective-C and Objective-C++ now support the +same language standards as C and C++ respectively. From 0f5307a4a67d79fcd920ce2f7d1e40da65dcb846 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 13:07:01 +0200 Subject: [PATCH 508/624] document c_std=c2y in release notes --- docs/markdown/snippets/c2y.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/markdown/snippets/c2y.md diff --git a/docs/markdown/snippets/c2y.md b/docs/markdown/snippets/c2y.md new file mode 100644 index 000000000000..4b647f8cac23 --- /dev/null +++ b/docs/markdown/snippets/c2y.md @@ -0,0 +1,4 @@ +## New C standard `c2y` (and `gnu2y`) + +The `c2y`` standard and its companion `gnu2y` are now supported +when using either Clang 19.0.0 or newer, or GCC 15.0.0 or newer. From 86ad0097484080f2da5d09484f8a6f6a2d5c535e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Apr 2025 15:49:00 +0200 Subject: [PATCH 509/624] man: document "meson setup -D" Fixes: #810 --- man/meson.1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/meson.1 b/man/meson.1 index 2c8da27a983c..b78cf968fcdf 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -40,6 +40,14 @@ your build dir. After that you just run the build command. Meson will autodetect changes in your source tree and regenerate all files needed to build the project. +Meson includes many built-in options that can be used to tweak the +configuration of a build directory; projects can also add their +own options. To set values for the options, use the \-D command +line argument like this: + +.B meson setup \-Dopt1=value1 \-Dopt2=value2 +.I rest of the command line... + .SS "options:" .TP \fB\-\-version\fR From 872f1a211b5b89cdebefc0f501bde38f6a17b20c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 15:51:16 +0200 Subject: [PATCH 510/624] document android_exe_type in release notes Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/android-exe-type.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/markdown/snippets/android-exe-type.md diff --git a/docs/markdown/snippets/android-exe-type.md b/docs/markdown/snippets/android-exe-type.md new file mode 100644 index 000000000000..ce4d94647a84 --- /dev/null +++ b/docs/markdown/snippets/android-exe-type.md @@ -0,0 +1,10 @@ +## New argument `android_exe_type` for executables + +Android application executables actually need to be linked +as a shared object, which is loaded from a pre-warmed JVM. +Meson projects can now specify a new argument `android_exe_type` +and set it to `application`, in order produce such a shared library +only on Android targets. + +This makes it possible to use the same `meson.build` file +for both Android and non-Android systems. From cf2cda0f3ecc9a4f63de082c3e1ecb2f2a18cbc2 Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 22 Apr 2025 04:44:18 +0100 Subject: [PATCH 511/624] docs: users: list is for notable meson adoptees We don't want churn with thie list for every project someone creates that uses Meson. Change the intro to say we want notable projects and link to a GitHub search for 'meson.build' files for people who want further examples to consider. --- docs/markdown/Users.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 9014510f9784..a515b24af7bb 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -2,14 +2,13 @@ title: Users ... -# List of projects using Meson +# Notable projects using Meson -If you have a project that uses Meson that you want to add to this -list, please [file a -pull-request](https://github.com/mesonbuild/meson/edit/master/docs/markdown/Users.md) -for it. All the software on this list is tested for regressions before -release, so it's highly recommended that projects add themselves -here. Some additional projects are listed in the [`meson` GitHub +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) +for it. For other projects using Meson, you may be interested in this +[GitHub search](https://github.com/search?q=path%3A%2F%28%5E%7C%5C%2F%29meson%5C.build%24%2F&type=code). +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++ From 7e5ac33bb47633d4abb19fe65047c90847f2d15f Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Wed, 14 Feb 2024 19:18:15 +0100 Subject: [PATCH 512/624] determine_windows_extra_paths: sort internal dependencies first Fixes #12330 --- mesonbuild/backend/backends.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 20f7907a1e84..3dfa2fba604a 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1196,25 +1196,31 @@ def determine_windows_extra_paths( links to and return them so they can be used in unit tests. """ - result: T.Set[str] = set() prospectives: T.Set[build.BuildTargetTypes] = set() + internal_deps: T.Set[str] = set() + external_deps: T.Set[str] = set() + if isinstance(target, build.BuildTarget): prospectives.update(target.get_all_link_deps()) - # External deps - result.update(self.extract_dll_paths(target)) for bdep in extra_bdeps: prospectives.add(bdep) if isinstance(bdep, build.BuildTarget): prospectives.update(bdep.get_all_link_deps()) + # Internal deps for ld in prospectives: dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld)) - result.add(dirseg) - if (isinstance(target, build.BuildTarget) and - not self.environment.machines.matches_build_machine(target.for_machine)): - result.update(self.get_mingw_extra_paths(target)) - return list(result) + internal_deps.add(dirseg) + + if isinstance(target, build.BuildTarget): + # External deps + external_deps.update(self.extract_dll_paths(target)) + + if not self.environment.machines.matches_build_machine(target.for_machine): + external_deps.update(self.get_mingw_extra_paths(target)) + + return list(internal_deps) + list(external_deps) def write_benchmark_file(self, datafile: T.BinaryIO) -> None: self.write_test_serialisation(self.build.get_benchmarks(), datafile) From b4266eec85b069dc636ff627e38301ffe85a0ca5 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 21 Apr 2025 08:30:40 -0700 Subject: [PATCH 513/624] interpreter: Remove accidental annotations changes for func_get_option These changes were correct in the original PR, but the changes to the keyword arguments were dropped in the rebase that landed, so this is now incorrect. --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d3bf86df3e4a..ac298d564b58 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1059,7 +1059,7 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, @typed_pos_args('get_option', str) @noKwargs def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], - kwargs: kwtypes.FuncGetOption) -> T.Union[options.UserOption, 'TYPE_var']: + kwargs: TYPE_kwargs) -> T.Union[options.UserOption, 'TYPE_var']: optname = args[0] if ':' in optname: From 84a2bc483a1e276249e285e95a2884c8b1245c2f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 21 Apr 2025 13:41:38 -0700 Subject: [PATCH 514/624] interpreter: remove dead code from func_get_option This code cannot be reached, as the guard checking that `value_object` is a UserOption will always be true. --- mesonbuild/interpreter/interpreter.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index ac298d564b58..04de6fc6bc98 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1095,13 +1095,10 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], if not value: return 'none' return ','.join(sorted(value)) - elif isinstance(value_object, options.UserOption): - if isinstance(value_object.value, str): - return P_OBJ.OptionString(value, f'{{{optname}}}') - return value - ocopy = copy.copy(value_object) - ocopy.value = value - return ocopy + + if isinstance(value_object.value, str): + return P_OBJ.OptionString(value, f'{{{optname}}}') + return value @typed_pos_args('configuration_data', optargs=[dict]) @noKwargs From 2e8a804a009d5ee0c4bfba8bf629ace3a28c6e14 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 21 Apr 2025 08:40:23 -0700 Subject: [PATCH 515/624] interpreter: fix interaction between option refactor and b_sanitize If we return the default value of the `b_sanitize` option, it won't go through the conversion back to string. This shows up in the ClangCL implementation. Fixes: #14501 --- mesonbuild/interpreter/interpreter.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 04de6fc6bc98..185bb07ee667 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1069,6 +1069,9 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], 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] + try: optkey = options.OptionKey(optname, self.subproject) value_object, value = self.coredata.optstore.get_value_object_and_value_for(optkey) @@ -1076,7 +1079,14 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], if self.coredata.optstore.is_base_option(optkey): # Due to backwards compatibility return the default # option for base options instead of erroring out. - return self.coredata.optstore.get_default_for_b_option(optkey) + # + # 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 else: if self.subproject: raise MesonException(f'Option {optname} does not exist for subproject {self.subproject}.') @@ -1087,7 +1097,7 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], ocopy.value = value return ocopy elif optname == 'b_sanitize': - assert isinstance(value_object, options.UserStringArrayOption) + assert value_object is None or isinstance(value_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 @@ -1096,7 +1106,7 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], return 'none' return ','.join(sorted(value)) - if isinstance(value_object.value, str): + if isinstance(value, str): return P_OBJ.OptionString(value, f'{{{optname}}}') return value From 66420a4a7fc43e5e6076afbdaa709cf4581e398c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 14:52:55 +0200 Subject: [PATCH 516/624] options: use early return Prepare for adding more complex logic to add_system_option_internal, in order to handle inheritance of global options to subprojects. Do the same in add_project_option to make the similarities and differences evident. Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 5fc16f5e12c9..75eb1971e0d6 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -895,11 +895,13 @@ def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: AnyOp assert isinstance(valobj, UserOption) if not isinstance(valobj.name, str): assert isinstance(valobj.name, str) - if key not in self.options: - self.options[key] = valobj - pval = self.pending_options.pop(key, None) - if pval is not None: - self.set_option(key, pval) + if key in self.options: + return + + self.options[key] = valobj + pval = self.pending_options.pop(key, None) + if pval is not None: + self.set_option(key, pval) def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) @@ -910,14 +912,14 @@ def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valob def add_project_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) assert key.subproject is not None - pval = self.pending_options.pop(key, None) if key in self.options: raise MesonException(f'Internal error: tried to add a project option {key} that already exists.') - else: - self.options[key] = valobj - self.project_options.add(key) - if pval is not None: - self.set_option(key, pval) + + 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) def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) From 705a63fc3a95c3a6fdb9506e07b1fd5e5c0522e4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 23 Apr 2025 11:10:52 +0200 Subject: [PATCH 517/624] options: strengthen typing for add_system_option_internal Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 75eb1971e0d6..2226e6bf0174 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -890,8 +890,7 @@ def add_system_option(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) raise MesonException(f'Internal error: non-module option has a period in its name {key.name}.') self.add_system_option_internal(key, valobj) - def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: - key = self.ensure_and_validate_key(key) + def add_system_option_internal(self, key: OptionKey, valobj: AnyOptionType) -> None: assert isinstance(valobj, UserOption) if not isinstance(valobj.name, str): assert isinstance(valobj.name, str) From 8fe6edfa4f8b6e64a19bbae471f37a2750023af6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 14:39:14 +0200 Subject: [PATCH 518/624] options: subproject system options require the global ones Because system options apply to all subprojects, they need to be present globally whenever a subproject needs them. Signed-off-by: Paolo Bonzini --- mesonbuild/options.py | 6 ++++++ unittests/optiontests.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 2226e6bf0174..62413b1a7adf 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -899,6 +899,12 @@ def add_system_option_internal(self, key: OptionKey, valobj: AnyOptionType) -> N 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 + if pval is not None: self.set_option(key, pval) diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 3b3ffc98eff5..5ed601fed7cd 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -35,6 +35,20 @@ def test_toplevel_project(self): optstore.initialize_from_top_level_project_call({OptionKey('someoption'): new_value}, {}, {}) self.assertEqual(optstore.get_value_for(k), new_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' + k = OptionKey(name) + subk = k.evolve(subproject='sub') + optstore.initialize_from_top_level_project_call({}, {}, {OptionKey(name): new_value}) + vo = UserStringOption(k.name, 'An option of some sort', default_value) + optstore.add_system_option(subk, vo) + self.assertEqual(optstore.get_value_for(subk), new_value) + def test_parsing(self): with self.subTest('subproject'): s1 = OptionKey.from_string('sub:optname') From b324a084aabb77297b02765c56b8c3b973e6fb39 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 14:42:47 +0200 Subject: [PATCH 519/624] coredata: remove unnecessary use of env.options The data is already available in the OptionStore's pending_options and add_compiler_option() will pick it from there. Signed-off-by: Paolo Bonzini --- mesonbuild/coredata.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 36120ff7e7e4..a94fdbdacedc 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -568,14 +568,6 @@ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = ' def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, env: Environment, subproject: str) -> None: for k, o in c_options.items(): - value = env.options.get(k) - if value is not None: - o.set_value(value) - if not subproject: - # FIXME, add augment - #self.optstore[k] = o # override compiler option on reconfigure - pass - comp_key = OptionKey(f'{k.name}', None, for_machine) if lang == 'objc' and k.name == 'c_std': # For objective C, always fall back to c_std. @@ -605,17 +597,7 @@ def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, skey = key if skey not in self.optstore: self.optstore.add_system_option(skey, copy.deepcopy(options.COMPILER_BASE_OPTIONS[key])) - if skey in env.options: - self.optstore.set_option(skey, env.options[skey]) - elif subproject and key in env.options: - self.optstore.set_option(skey, env.options[key]) - # FIXME - #if subproject and not self.optstore.has_option(key): - # self.optstore[key] = copy.deepcopy(self.optstore[skey]) - elif skey in env.options: - self.optstore.set_option(skey, env.options[skey]) - elif subproject and key in env.options: - self.optstore.set_option(skey, env.options[key]) + self.emit_base_options_warnings() def emit_base_options_warnings(self) -> None: From 05fc298c61fc5921a28f0f957726b5fa652ecbcf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 09:47:20 +0200 Subject: [PATCH 520/624] add testcase for propagating options to subprojects --- test cases/common/281 subproj options/meson.build | 1 + .../281 subproj options/subprojects/sub/meson.build | 8 +++++++- .../common/281 subproj options/subprojects/sub2/f.c | 3 +++ .../281 subproj options/subprojects/sub2/meson.build | 7 +++++++ test cases/common/281 subproj options/test.json | 7 +++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test cases/common/281 subproj options/subprojects/sub2/f.c create mode 100644 test cases/common/281 subproj options/subprojects/sub2/meson.build create mode 100644 test cases/common/281 subproj options/test.json diff --git a/test cases/common/281 subproj options/meson.build b/test cases/common/281 subproj options/meson.build index b4cf89fa0b6d..55fb109a3eec 100644 --- a/test cases/common/281 subproj options/meson.build +++ b/test cases/common/281 subproj options/meson.build @@ -1,3 +1,4 @@ project('pkg_opt_test') subproject('sub') +subproject('sub2') 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 9e3bceacfb8d..82cd38665e38 100644 --- a/test cases/common/281 subproj options/subprojects/sub/meson.build +++ b/test cases/common/281 subproj options/subprojects/sub/meson.build @@ -1,2 +1,8 @@ -project('subproject') +project('subproject', 'c') assert(get_option('bar') == 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 diff --git a/test cases/common/281 subproj options/subprojects/sub2/f.c b/test cases/common/281 subproj options/subprojects/sub2/f.c new file mode 100644 index 000000000000..0aae46148dc8 --- /dev/null +++ b/test cases/common/281 subproj options/subprojects/sub2/f.c @@ -0,0 +1,3 @@ +int f(void) +{ +} diff --git a/test cases/common/281 subproj options/subprojects/sub2/meson.build b/test cases/common/281 subproj options/subprojects/sub2/meson.build new file mode 100644 index 000000000000..3b0ed92c3f94 --- /dev/null +++ b/test cases/common/281 subproj options/subprojects/sub2/meson.build @@ -0,0 +1,7 @@ +project('subproject', 'c') + +# 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 diff --git a/test cases/common/281 subproj options/test.json b/test cases/common/281 subproj options/test.json new file mode 100644 index 000000000000..fcdf4ddd8257 --- /dev/null +++ b/test cases/common/281 subproj options/test.json @@ -0,0 +1,7 @@ +{ + "matrix": { + "options": { + "b_lto": [{ "val": "true" }] + } + } +} From 475bfba79a1a65d85e880a44fd995fc4aaf0c8c4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 19 Apr 2025 15:29:27 +0200 Subject: [PATCH 521/624] ci: bump Ubuntu version for UnusedMissingReturn jobs --- .github/workflows/unusedargs_missingreturn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unusedargs_missingreturn.yml b/.github/workflows/unusedargs_missingreturn.yml index 72f39b511f45..d6f1246d1ae6 100644 --- a/.github/workflows/unusedargs_missingreturn.yml +++ b/.github/workflows/unusedargs_missingreturn.yml @@ -42,7 +42,7 @@ permissions: jobs: linux: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 From fc9fd42899e1e2160a69ec245931c3aa79b0d267 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Mar 2025 10:18:24 +0100 Subject: [PATCH 522/624] interpreter: do not use pathlib for DependencyVariableString creation Path.is_dir() can raise a PermissionError if a parent does not have the executable permission set; plus the "in p.parents" tests are very expensive. Do not use Path at all. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 18 ++++++++---------- .../unit/125 declare_dep var/meson.build | 7 +++++++ .../unit/125 declare_dep var/meson_options.txt | 1 + unittests/linuxliketests.py | 12 ++++++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 test cases/unit/125 declare_dep var/meson.build create mode 100644 test cases/unit/125 declare_dep var/meson_options.txt diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 185bb07ee667..bf41bfb55171 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -704,20 +704,18 @@ def func_declare_dependency(self, node: mparser.BaseNode, args: T.List[TYPE_var] version = self.project_version d_module_versions = kwargs['d_module_versions'] d_import_dirs = self.extract_incdirs(kwargs, 'd_import_dirs') - srcdir = Path(self.environment.source_dir) + srcdir = self.environment.source_dir + subproject_dir = os.path.abspath(os.path.join(srcdir, self.subproject_dir)) + project_root = os.path.abspath(os.path.join(srcdir, self.root_subdir)) # convert variables which refer to an -uninstalled.pc style datadir for k, v in variables.items(): if not v: FeatureNew.single_use('empty variable value in declare_dependency', '1.4.0', self.subproject, location=node) - try: - p = Path(v) - except ValueError: - continue - else: - if not self.is_subproject() and srcdir / self.subproject_dir in p.parents: - continue - if p.is_absolute() and p.is_dir() and srcdir / self.root_subdir in [p] + list(Path(os.path.abspath(p)).parents): - variables[k] = P_OBJ.DependencyVariableString(v) + if os.path.isabs(v) \ + and (self.is_subproject() or not is_parent_path(subproject_dir, v)) \ + and is_parent_path(project_root, v) \ + and os.path.isdir(v): + variables[k] = P_OBJ.DependencyVariableString(v) dep = dependencies.InternalDependency(version, incs, compile_args, link_args, libs, libs_whole, sources, extra_files, diff --git a/test cases/unit/125 declare_dep var/meson.build b/test cases/unit/125 declare_dep var/meson.build new file mode 100644 index 000000000000..4909b590eff3 --- /dev/null +++ b/test cases/unit/125 declare_dep var/meson.build @@ -0,0 +1,7 @@ +project('foo') + +declare_dependency( + variables: { + 'dir': get_option('dir') + } +) diff --git a/test cases/unit/125 declare_dep var/meson_options.txt b/test cases/unit/125 declare_dep var/meson_options.txt new file mode 100644 index 000000000000..eb15ffcdebbb --- /dev/null +++ b/test cases/unit/125 declare_dep var/meson_options.txt @@ -0,0 +1 @@ +option('dir', type: 'string') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index db7068d5b03f..6b896d73f942 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1862,6 +1862,18 @@ def test_top_options_in_sp(self): testdir = os.path.join(self.unit_test_dir, '125 pkgsubproj') self.init(testdir) + def test_unreadable_dir_in_declare_dep(self): + testdir = os.path.join(self.unit_test_dir, '125 declare_dep var') + tmpdir = Path(tempfile.mkdtemp()) + self.addCleanup(windows_proof_rmtree, tmpdir) + declaredepdir = tmpdir / 'test' + declaredepdir.mkdir() + try: + tmpdir.chmod(0o444) + self.init(testdir, extra_args=f'-Ddir={declaredepdir}') + finally: + tmpdir.chmod(0o755) + def check_has_flag(self, compdb, src, argument): for i in compdb: if src in i['file']: From 17fafda1f98f7ce9b4ac43a511e00f70efbee227 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Thu, 24 Apr 2025 15:17:30 +0300 Subject: [PATCH 523/624] Bump version number for rc2. --- mesonbuild/coredata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index a94fdbdacedc..98ea65c9b48a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.8.0.rc1' +version = '1.8.0.rc2' # 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 From 39be44fed2938ad03f8e2bc183401a121387bf50 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Mon, 28 Apr 2025 18:36:57 +0300 Subject: [PATCH 524/624] Bump version number for release. --- mesonbuild/coredata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 98ea65c9b48a..9a265ee27d21 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright 2013-2024 The Meson development team +# Copyright 2013-2025 The Meson development team # Copyright © 2023-2025 Intel Corporation from __future__ import annotations @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.8.0.rc2' +version = '1.8.0' # 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 From 0c8216f01d89f10c6067167ce09d458512f8b959 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 May 2025 14:10:51 +0200 Subject: [PATCH 525/624] options: fix "deprecated" with dictionary argument and non-string types Since opt.deprecated is a dictionary with string keys, the lookup must use str() around the user-provided value; with some care because booleans will be the meson-ic 'true' and 'false' instead of Python's 'True' and 'False'. Signed-off-by: Paolo Bonzini (cherry picked from commit c1b7ef4218f12905641f1cb1503c8b9b3542e983) --- mesonbuild/options.py | 12 +++++++++--- mesonbuild/utils/universal.py | 2 +- unittests/optiontests.py | 11 +++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 62413b1a7adf..7d6df1b4d626 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -327,7 +327,13 @@ def __post_init__(self, value_: _T) -> None: # 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: @@ -503,7 +509,7 @@ def printable_choices(self) -> T.Optional[T.List[str]]: @dataclasses.dataclass class UserStringArrayOption(UserArrayOption[str]): - def listify(self, value: T.Any) -> T.List[T.Any]: + def listify(self, value: ElementaryOptionValues) -> T.List[str]: try: return listify_array_value(value, self.split_args) except MesonException as e: @@ -1005,7 +1011,7 @@ def set_option(self, key: OptionKey, new_value: ElementaryOptionValues, first_in if v in opt.deprecated: mlog.deprecation(f'Option {key.name!r} 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: diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 5b3f131af9b3..d165bf54d868 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -1578,7 +1578,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: diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 5ed601fed7cd..aad8eadfc405 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -202,3 +202,14 @@ def test_b_default(self): optstore = OptionStore(False) value = optstore.get_default_for_b_option(OptionKey('b_vscrt')) self.assertEqual(value, 'from_buildtype') + + 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(name) + self.assertEqual(value, '1') From 21302a5e60f04401fdc54e5bfdbe4478903d1d7d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 29 Apr 2025 14:50:10 +0200 Subject: [PATCH 526/624] unittests: fix overly loose regex in tests for `--slice=` option The unit tests for the `meson test --slice=` option check that the option is working by extracting all tests that have been run from the command output. This is done with a rather loose regular expression "test-[0-9]*", which can easily match other parts of the output, as well. One user for example reported that the test broke because they were executing tests in a directory called "meson-test-1.8.0-build", and given that the "test-1" part of that directory matches the regular expression we have too many matches. Fix the issue by tightening the regex so that is way less likely to match anything from the host's build environment. Reported-by: Dominique Leuenberger Signed-off-by: Patrick Steinhardt (cherry picked from commit 53f9d60756ee750e93464f8e1b0944ee2b077976) --- unittests/allplatformstests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 2fee06c690fa..ea220a065f10 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -5196,7 +5196,7 @@ def test_slice(self): '10/10': [10], }.items(): output = self._run(self.mtest_command + ['--slice=' + arg]) - tests = sorted([ int(x[5:]) for x in re.findall(r'test-[0-9]*', output) ]) + tests = sorted([ int(x) for x in re.findall(r'\n[ 0-9]+/[0-9]+ test-([0-9]*)', output) ]) self.assertEqual(tests, expectation) for arg, expectation in {'': 'error: argument --slice: value does not conform to format \'SLICE/NUM_SLICES\'', From cb8da2d4de383b3ffbcf75b115dea6425d7dad90 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Apr 2025 19:43:51 +0200 Subject: [PATCH 527/624] options: tighten type of cmd_line_options Based on the SharedCMDOptions protocol it is guaranteed to be a dictionary. Signed-off-by: Paolo Bonzini (cherry picked from commit d241394f8b8c5f24b5ffde56a9956eee93e611e4) --- mesonbuild/options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 7d6df1b4d626..ce4606398bb5 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1249,7 +1249,7 @@ def prefix_split_options(self, coll: T.Union[T.List[str], OptionStringLikeDict]) def first_handle_prefix(self, project_default_options: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_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], @@ -1288,7 +1288,7 @@ def hard_reset_from_prefix(self, prefix: str) -> None: def initialize_from_top_level_project_call(self, project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_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, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in, From 5a3f188e1ac2bfa669ac9d5d3cc9dba4fae81525 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Apr 2025 19:26:05 +0200 Subject: [PATCH 528/624] options: extract validation of command line options Which command line options are valid is not entirely known until the backend option is processed. Split the validation to a separate function so that it can be done later, and while at it mention all unknown options instead of just the first. Signed-off-by: Paolo Bonzini (cherry picked from commit e22fba20dbd9010333b150be2564e70866aa3676) --- mesonbuild/interpreter/interpreter.py | 1 + mesonbuild/options.py | 28 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bf41bfb55171..b32b8c6e78ec 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1201,6 +1201,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str self.coredata.initialized_subprojects.add(self.subproject) 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 diff --git a/mesonbuild/options.py b/mesonbuild/options.py index ce4606398bb5..cb557397bc29 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1376,16 +1376,28 @@ def initialize_from_top_level_project_call(self, if proj_key in self.options: self.set_option(proj_key, valstr, True) else: - # 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): - raise MesonException(f'Unknown options: "{keystr}"') self.pending_options[key] = valstr + 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()] From 4e9fac15de50816e246c82b696f6843a045d6f84 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Apr 2025 19:26:54 +0200 Subject: [PATCH 529/624] interpreter: add backend options before validating the command line options Allow specifying e.g. -Dbackend_max_links on the command line. Fixes: #14524 Signed-off-by: Paolo Bonzini (cherry picked from commit 6d134ef999683ce49b933b6c927c837a585855b1) --- mesonbuild/interpreter/interpreter.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b32b8c6e78ec..abdc8899d8f6 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1200,6 +1200,19 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str self.user_defined_options.cmd_line_options) self.coredata.initialized_subprojects.add(self.subproject) + if not self.is_subproject(): + # We have to activate VS before adding languages and before calling + # self.set_backend() otherwise it wouldn't be able to detect which + # vs backend version we need. But after setting default_options in case + # the project sets vs backend by default. + backend = self.coredata.optstore.get_value_for(OptionKey('backend')) + assert backend is None or isinstance(backend, str), 'for mypy' + vsenv = self.coredata.optstore.get_value_for(OptionKey('vsenv')) + assert isinstance(vsenv, bool), 'for mypy' + force_vsenv = vsenv or backend.startswith('vs') + mesonlib.setup_vsenv(force_vsenv) + 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 @@ -1271,22 +1284,9 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) - if not self.is_subproject(): - # We have to activate VS before adding languages and before calling - # self.set_backend() otherwise it wouldn't be able to detect which - # vs backend version we need. But after setting default_options in case - # the project sets vs backend by default. - backend = self.coredata.optstore.get_value_for(OptionKey('backend')) - assert backend is None or isinstance(backend, str), 'for mypy' - vsenv = self.coredata.optstore.get_value_for(OptionKey('vsenv')) - assert isinstance(vsenv, bool), 'for mypy' - force_vsenv = vsenv or backend.startswith('vs') - mesonlib.setup_vsenv(force_vsenv) - self.add_languages(proj_langs, True, MachineChoice.HOST) self.add_languages(proj_langs, False, MachineChoice.BUILD) - self.set_backend() if not self.is_subproject(): self.check_stdlibs() From ea45ee0fc3f367cbe89d8facbfe4675eb4b52ffc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Apr 2025 19:51:43 +0200 Subject: [PATCH 530/624] unittests: smoke test the backend options Signed-off-by: Paolo Bonzini (cherry picked from commit 2ba3c2d0da8a324ff3e38fde9003ca1aecffe84a) --- unittests/allplatformstests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index ea220a065f10..0618b20ee570 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -529,7 +529,8 @@ def test_install_introspection(self): if self.backend is not Backend.ninja: raise SkipTest(f'{self.backend.name!r} backend can\'t install files') testdir = os.path.join(self.common_test_dir, '8 install') - self.init(testdir) + # sneak in a test that covers backend options... + self.init(testdir, extra_args=['-Dbackend_max_links=4']) intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] From 93ec53752565e8a7fc8718ca59bce020af1f5823 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 16:14:15 +0200 Subject: [PATCH 531/624] cmake: include_directories() returns an array See the way that it is created: dir_node = assign(dir_var, function(include_directories, tgt.includes)) sys_node = assign(sys_var, function(include_directories, tgt.sys_includes, {is_system: True})) inc_node = assign(inc_var, array([id_node(dir_var), id_node(sys_var)])) Due to incorrect documentation, commit 1f4bb3737 ("modules/cmake: Make fully type safe", 2025-04-02) added an incorrect assertion. Fix both. Fixes: #14530 Signed-off-by: Paolo Bonzini (cherry picked from commit 1b3263c893a67810119495cae27a77a03b569705) --- docs/markdown/CMake-module.md | 4 ++-- mesonbuild/modules/cmake.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md index f8275c981733..982fa35d086e 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/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index e3154b05e135..f12cc51a623c 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -154,10 +154,11 @@ def dependency(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, @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 From fc3509d69e3a882573f98e597cd7826157e01410 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 5 May 2025 10:30:06 +0200 Subject: [PATCH 532/624] test casees: add test for cmake_proj.include_directories Signed-off-by: Paolo Bonzini (cherry picked from commit 6d0b01638650616e5ef7451277590fd0e69e6bb7) --- test cases/cmake/13 system includes/main2.cpp | 5 +++++ test cases/cmake/13 system includes/meson.build | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 test cases/cmake/13 system includes/main2.cpp 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 000000000000..a94a11679dd3 --- /dev/null +++ b/test cases/cmake/13 system includes/main2.cpp @@ -0,0 +1,5 @@ +#include + +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 1265d4607421..fe7158070da5 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) From 43c86bb0791a6fe24072339453605a8cb107c2df Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Thu, 1 May 2025 14:41:43 -0400 Subject: [PATCH 533/624] more explicit error message for unrecognized lexer token Fixes #14415 (cherry picked from commit 2d22385aa40c63927d947e3c1eadc0a6e0278d46) --- mesonbuild/mparser.py | 2 +- test cases/failing/95 invalid option file/test.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index f1c60712c92c..d0b32e25aea5 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -221,7 +221,7 @@ def lex(self, filename: str) -> T.Generator[Token, None, None]: yield Token(tid, filename, curline_start, curline, col, bytespan, value) break if not matched: - raise ParseException('lexer', self.getline(line_start), lineno, col) + raise ParseException(f'lexer: unrecognized token {self.code[loc]!r}', self.getline(line_start), lineno, col) @dataclass class BaseNode: diff --git a/test cases/failing/95 invalid option file/test.json b/test cases/failing/95 invalid option file/test.json index 073ac67177af..debb4a1440e8 100644 --- a/test cases/failing/95 invalid option file/test.json +++ b/test cases/failing/95 invalid option file/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/95 invalid option file/meson_options.txt:1:0: ERROR: lexer" + "line": "test cases/failing/95 invalid option file/meson_options.txt:1:0: ERROR: lexer: unrecognized token \"'\"" } ] } From 1356bd996847244aedb0894b0c64183f0fb2cf55 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 1 May 2025 16:00:57 -0400 Subject: [PATCH 534/624] parser: update position when reporting lexer errors for unrecognized token By default we point to the start of the most recent token we parsed. This is used when erroring out on parser issues, to print the line that caused the error, with a pointer to where we were when we got the error. In this particular case, the pointer pointed to the start of the last token we successfully parsed (col), but was not updated if we hit a token we didn't understand at all. Instead use a pointer to the unrecognized token itself. Fixes: https://github.com/mesonbuild/meson/issues/14415 (cherry picked from commit f36d680b0093e071ca54c9d20b84f9a62f88b94b) --- mesonbuild/mparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index d0b32e25aea5..116e88fdb31d 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -221,7 +221,7 @@ def lex(self, filename: str) -> T.Generator[Token, None, None]: yield Token(tid, filename, curline_start, curline, col, bytespan, value) break if not matched: - raise ParseException(f'lexer: unrecognized token {self.code[loc]!r}', self.getline(line_start), lineno, col) + raise ParseException(f'lexer: unrecognized token {self.code[loc]!r}', self.getline(line_start), lineno, loc - line_start) @dataclass class BaseNode: From 7cf72578e1238f052555b0112832d64e85d93bbc Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Wed, 30 Apr 2025 11:05:00 +0200 Subject: [PATCH 535/624] dependencies/python: Replace non-breakable space with space (cherry picked from commit a343b01d1b3d5ba159becebf11974f4ee7189323) --- mesonbuild/dependencies/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index 3dab31c12f8e..bed5690d0614 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -330,7 +330,7 @@ def __init__(self, name: str, environment: 'Environment', # 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] From 081dbd0e0bebf7db45f319140128713b24ecf60f Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Wed, 30 Apr 2025 11:59:55 +0200 Subject: [PATCH 536/624] dependencies/python: Fix Framework Python when pkg-config is installed In this case, self.raw_link_args is None. Fixes #14534. (cherry picked from commit 96e0c4bf4a956956483d640fc4aadf5cde43455e) --- mesonbuild/dependencies/python.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index bed5690d0614..ab040b50b384 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -333,7 +333,9 @@ def __init__(self, name: str, environment: 'Environment', # 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): From de70eb28d9c5e593bf1424c1d1f42eb95a1b162c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 15:10:51 +0200 Subject: [PATCH 537/624] options: set subproject options as augments Fixes: #14528 Signed-off-by: Paolo Bonzini (cherry picked from commit 1a6a9d603248a2f8b659ab80b0bdbe4f0e74cb64) --- mesonbuild/options.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index cb557397bc29..1ce2b5877a67 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1344,7 +1344,8 @@ def initialize_from_top_level_project_call(self, if not self.is_cross and key.is_for_build(): continue if key.subproject: - self.pending_options[key] = valstr + augstr = str(key) + self.augments[augstr] = valstr elif key in self.options: self.set_option(key, valstr, first_invocation) else: @@ -1368,7 +1369,8 @@ def initialize_from_top_level_project_call(self, if not self.is_cross and key.is_for_build(): continue if key.subproject: - self.pending_options[key] = valstr + augstr = str(key) + self.augments[augstr] = valstr elif key in self.options: self.set_option(key, valstr, True) else: From 4bd7d63831e77256caaa8169881c4378afea2e2e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 May 2025 18:21:55 +0200 Subject: [PATCH 538/624] options: allow setting subproject options in subproject() call Something like subproject('sub', default_options: ['sub2:from_subp=true']) will cause an assertion failure due to "key.subproject is None" obviously being false. Just support this, since it's easy to do so. (cherry picked from commit 8e2e5c52d00c014ffb90464fa09633fb2a0cc6a4) --- mesonbuild/options.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 1ce2b5877a67..508bb74abcfd 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1418,15 +1418,18 @@ def initialize_from_subproject_call(self, 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 key.subproject is None: + key = key.evolve(subproject=subproject) + elif 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 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}' + aug_str = str(key) self.augments[aug_str] = valstr # Check for pending options assert isinstance(cmd_line_options, dict) From 93e83b3f36bfed5134eb1c1756b93e06c00c7e54 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 15:45:33 +0200 Subject: [PATCH 539/624] options: fix types for initialize_from_subproject_call No code changes, just making mypy annotations truthful. Signed-off-by: Paolo Bonzini (cherry picked from commit d55364fba7c61dbc103e758b2ac27bc172de9b3d) --- mesonbuild/options.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 508bb74abcfd..3e8f16a1a39a 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1400,21 +1400,19 @@ def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> N 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]: + def hacky_mchackface_back_to_list(self, optdict: T.Union[T.List[str], OptionStringLikeDict]) -> T.List[str]: if isinstance(optdict, dict): return [f'{k}={v}' for k, v in optdict.items()] return optdict 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: + spcall_default_options_in: T.Union[T.List[str], OptionStringLikeDict], + project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], + cmd_line_options: 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] + spcall_default_options = self.hacky_mchackface_back_to_list(spcall_default_options_in) + project_default_options = self.hacky_mchackface_back_to_list(project_default_options_in) for o in itertools.chain(project_default_options, spcall_default_options): keystr, valstr = o.split('=', 1) key = OptionKey.from_string(keystr) @@ -1432,7 +1430,6 @@ def initialize_from_subproject_call(self, aug_str = str(key) 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) From 3abb3ca1be58385653c7f347f415229974b6d4a0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 15:46:18 +0200 Subject: [PATCH 540/624] interpreter: convert subproject default options to dictionary Always use a dictionary (even though later OptionStore will convert it back to list for hackish historical reasons) to make it easy to apply overrides. Long term we probably want OptionStore to not know about T.List[str] at all, anyway. Signed-off-by: Paolo Bonzini (cherry picked from commit bc825b805daf7f48cade7bc8d6defd6e2fa75b87) --- mesonbuild/interpreter/interpreter.py | 15 +++++++++------ mesonbuild/interpreter/kwargs.py | 2 +- mesonbuild/options.py | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index abdc8899d8f6..27e682edb686 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -296,9 +296,8 @@ def __init__( 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 + assert isinstance(default_project_options, dict) + self.default_project_options = default_project_options else: self.default_project_options = {} self.project_default_options: T.List[str] = [] @@ -878,6 +877,10 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth return self.disabled_subproject(subp_name, disabled_feature=feature) default_options = kwargs['default_options'] + if isinstance(default_options, str): + default_options = [default_options] + if isinstance(default_options, list): + default_options = dict((x.split('=', 1) for x in default_options)) if subp_name == '': raise InterpreterException('Subproject name must not be empty.') @@ -952,7 +955,7 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth raise e def _do_subproject_meson(self, subp_name: str, subdir: str, - default_options: T.List[str], + default_options: T.Dict[str, options.ElementaryOptionValues], kwargs: kwtypes.DoSubproject, ast: T.Optional[mparser.CodeBlockNode] = None, build_def_files: T.Optional[T.List[str]] = None, @@ -1012,7 +1015,7 @@ def _do_subproject_meson(self, subp_name: str, subdir: str, return self.subprojects[subp_name] def _do_subproject_cmake(self, subp_name: str, subdir: str, - default_options: T.List[str], + default_options: T.Dict[str, options.ElementaryOptionValues], kwargs: kwtypes.DoSubproject) -> SubprojectHolder: from ..cmake import CMakeInterpreter with mlog.nested(subp_name): @@ -1039,7 +1042,7 @@ def _do_subproject_cmake(self, subp_name: str, subdir: str, return result def _do_subproject_cargo(self, subp_name: str, subdir: str, - default_options: T.List[str], + default_options: T.Dict[str, options.ElementaryOptionValues], kwargs: kwtypes.DoSubproject) -> SubprojectHolder: from .. import cargo FeatureNew.single_use('Cargo subproject', '1.3.0', self.subproject, location=self.current_node) diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index fb34bbbce9c8..d741aabc5583 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -321,7 +321,7 @@ class Subproject(ExtractRequired): class DoSubproject(ExtractRequired): - default_options: T.List[str] + default_options: T.Union[T.List[str], T.Dict[str, options.ElementaryOptionValues], str] version: T.List[str] cmake_options: T.List[str] options: T.Optional[CMakeSubprojectOptions] diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 3e8f16a1a39a..2d96380c4403 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1407,7 +1407,7 @@ def hacky_mchackface_back_to_list(self, optdict: T.Union[T.List[str], OptionStri def initialize_from_subproject_call(self, subproject: str, - spcall_default_options_in: T.Union[T.List[str], OptionStringLikeDict], + spcall_default_options_in: OptionStringLikeDict, project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], cmd_line_options: OptionStringLikeDict) -> None: is_first_invocation = True From e56dc17317f496dfa605069b4797a3a1dde734bc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 15:40:21 +0200 Subject: [PATCH 541/624] interpreter: allow passing default default_options to do_subproject Apply the default_library=... default after the default options have been converted to a dictionary, to avoid having to deal with all the possible types of the default_options keyword argument. Fixes: #14532 Signed-off-by: Paolo Bonzini (cherry picked from commit 42d531da65f7d51803ad05dcdd18a650827a062c) --- mesonbuild/interpreter/dependencyfallbacks.py | 16 +++++----------- mesonbuild/interpreter/interpreter.py | 5 ++++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index 0ebfe3bd578f..53eeb2911cba 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -4,14 +4,12 @@ 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 extract_as_list, stringlistify, version_compare_many from ..options import OptionKey from ..dependencies import Dependency, DependencyException, NotFoundDependency from ..interpreterbase import (MesonInterpreterObject, FeatureNew, @@ -124,21 +122,17 @@ def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs # 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: + extra_default_options = {} + 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 + extra_default_options['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, extra_default_options=extra_default_options) return self._get_subproject_dep(subp_name, varname, kwargs) def _get_subproject(self, subp_name: str) -> T.Optional[SubprojectHolder]: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 27e682edb686..8fb660d1f078 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -867,7 +867,8 @@ def disabled_subproject(self, subp_name: str, disabled_feature: T.Optional[str] 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, + extra_default_options: T.Optional[T.Dict[str, options.ElementaryOptionValues]] = None) -> SubprojectHolder: if subp_name == 'sub_static': pass disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) @@ -881,6 +882,8 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth default_options = [default_options] if isinstance(default_options, list): default_options = dict((x.split('=', 1) for x in default_options)) + if extra_default_options: + default_options = {**extra_default_options, **default_options} if subp_name == '': raise InterpreterException('Subproject name must not be empty.') From 474ddd78d3f091a16dac150abc389545cdc7a064 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 May 2025 18:28:28 +0200 Subject: [PATCH 542/624] test cases: cover passing default_options together with static: true Signed-off-by: Paolo Bonzini (cherry picked from commit b244b4067ccce234c71fb76192e1656073290d56) --- test cases/common/98 subproject subdir/meson.build | 2 +- .../98 subproject subdir/subprojects/sub_static/meson.build | 1 + .../subprojects/sub_static/meson_options.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt diff --git a/test cases/common/98 subproject subdir/meson.build b/test cases/common/98 subproject subdir/meson.build index d2bafedf5119..5d92772c1354 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 6c00623a1d22..8de7cb40674a 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 000000000000..129a7d4a07a4 --- /dev/null +++ b/test cases/common/98 subproject subdir/subprojects/sub_static/meson_options.txt @@ -0,0 +1 @@ +option('bar', type: 'boolean', value: false) From ccec3b37020e3e33ef88b8d30d73babe11dc6308 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 May 2025 18:05:11 +0200 Subject: [PATCH 543/624] test cases: add testcase for setting options from superproject Signed-off-by: Paolo Bonzini (cherry picked from commit a46371f6d8c01953d8d8bd3d8e86594cc3c8e37f) --- test cases/common/281 subproj options/meson.build | 5 ++--- .../common/281 subproj options/subprojects/sub/meson.build | 4 ++++ .../281 subproj options/subprojects/sub/meson_options.txt | 1 + .../common/281 subproj options/subprojects/sub2/meson.build | 2 ++ .../281 subproj options/subprojects/sub2/meson_options.txt | 1 + 5 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 test cases/common/281 subproj options/subprojects/sub2/meson_options.txt diff --git a/test cases/common/281 subproj options/meson.build b/test cases/common/281 subproj options/meson.build index 55fb109a3eec..d450004900a8 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 82cd38665e38..6cc4906cc71c 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 129a7d4a07a4..7f94d02cc60a 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 3b0ed92c3f94..65f3e5af0501 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 000000000000..d645182a2c45 --- /dev/null +++ b/test cases/common/281 subproj options/subprojects/sub2/meson_options.txt @@ -0,0 +1 @@ +option('from_subp', type: 'boolean', value: false) From 71472371bc584979e114459aab8ade749d9b2949 Mon Sep 17 00:00:00 2001 From: Daniel Foster Date: Sun, 11 May 2025 11:49:31 +1000 Subject: [PATCH 544/624] compilers/rust: fix syntax of has_argument checks (cherry picked from commit 24d7c347f5650b14f991da8f8c92b8ef6e4ff110) --- mesonbuild/compilers/rust.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index d0d2e69acb52..6f9d642bc16c 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -327,11 +327,11 @@ def get_rust_tool(self, name: str, env: Environment) -> T.List[str]: 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) + return self.compiles('fn main() { std::process::exit(0) }\n', env, 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) + return self.compiles('fn main() { std::process::exit(0) }\n', env, extra_args=args, mode=CompileCheckMode.LINK) @functools.lru_cache(maxsize=None) def get_rustdoc(self, env: 'Environment') -> T.Optional[RustdocTestCompiler]: From 451dc96eef1229ae94260ae34e44f8be77ed24bb Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Mon, 12 May 2025 14:51:07 -0400 Subject: [PATCH 545/624] cpp: Fix cpp_std=vc++14 Fixes a regression introduced in commit d37d649b08b8 "Make all Meson level options overridable per subproject." This change results in every file printing the warning "cl : Command line warning D9002 : ignoring unknown option '/std:vc++14'" Now that "get_option_..." is called before overwriting the option (instead of after), we have to operate on compiler options, not meson options. There is no such compiler option as /std:vc++14 (the meson option vc++xx is split into /std:c++xx for the C++ standard version, and a separate flag that enables Microsoft extensions). Remove the mapping from c++14 to vc++14. (cherry picked from commit 043ff22fc732ac25fab9b1d59e66cc59c493d07f) --- mesonbuild/compilers/cpp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 01b9bb9fa34f..1755db2d7429 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -872,8 +872,7 @@ def get_option_std_args(self, target: BuildTarget, env: Environment, subproject: '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'} + std_mapping = {'/std:c++11': '/std:c++14'} processed_args = [std_mapping.get(x, x) for x in original_args] return processed_args From 65977c977a9f07b1b0878db4d2514f3b6b8b21e7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 12 May 2025 09:29:19 +0200 Subject: [PATCH 546/624] cross: add rustdoc to ubuntu-armhf toolchain Document that "rustdoc" is a useful key for the `[binaries]` section. Signed-off-by: Paolo Bonzini (cherry picked from commit c3beaa9a35622987df522d10f8584b4e8cbfe05d) --- cross/ubuntu-armhf.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt index 6409e396b577..97a1c21e8682 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' From 6b3ecc66e7c08fc5c56a01775268f3c1b4561429 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 12 May 2025 09:11:36 +0200 Subject: [PATCH 547/624] rust: skip doctests when build machine cannot run host binaries "rustdoc --test" relies on running host binaries, and has no way of wrapping them with Meson's exe_wrapper. Just skip the doctests in that case. Fixes: #14583 Signed-off-by: Paolo Bonzini (cherry picked from commit 14010f4dfdb9847944592149b189184ab59b6de0) --- mesonbuild/modules/rust.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index f43a0ede965a..c5f18e8e5705 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -242,6 +242,10 @@ def test(self, state: ModuleState, args: T.Tuple[str, BuildTarget], kwargs: Func def doctest(self, state: ModuleState, args: T.Tuple[str, T.Union[SharedLibrary, StaticLibrary]], kwargs: FuncDoctest) -> ModuleReturnValue: name, base_target = args + 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) From 8d3abc7ca1541d46c99c71642a7cc9bc2515c138 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Fri, 9 May 2025 16:42:59 -0400 Subject: [PATCH 548/624] Skip gettext extractor on macos because it is unstable (cherry picked from commit 0dc65117077b39cf85410f1561e7915a8bc1cbae) --- test cases/frameworks/38 gettext extractor/meson.build | 4 ++++ test cases/frameworks/38 gettext extractor/test.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test cases/frameworks/38 gettext extractor/meson.build b/test cases/frameworks/38 gettext extractor/meson.build index 9a54df542e18..a31c87d0aa69 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/frameworks/38 gettext extractor/test.json b/test cases/frameworks/38 gettext extractor/test.json index c5952ffd59f2..032698e20a34 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"] } From 605355b92c47dc1c05207b4d1c4bb2b5bfacba23 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 May 2025 08:18:13 +0200 Subject: [PATCH 549/624] environment: split list of important environment variables to a constant Signed-off-by: Paolo Bonzini (cherry picked from commit 938690eef76d036a1682150944e65d956ec9db8d) --- mesonbuild/environment.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index f322cda95cc9..5a687b8310ce 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -50,6 +50,13 @@ 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' @@ -777,12 +784,7 @@ def _load_machine_file_options(self, config: T.Mapping[str, T.Mapping[str, Eleme 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) From 83e172d122ced6f41e7df791f798670223b431cd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 May 2025 08:20:52 +0200 Subject: [PATCH 550/624] environment: move a comment around Make space for moving the larger comment about *_env_args, which will be before the for loop once *_env_args is removed. Signed-off-by: Paolo Bonzini (cherry picked from commit d11fa36196b6f037d95304772efd6e51b39b8379) --- mesonbuild/environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 5a687b8310ce..a3b8cd5f2970 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -843,9 +843,9 @@ def _set_default_options_from_env(self) -> None: 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(): + # Only store options that are not already in self.options, + # otherwise we'd override the machine files if k not in self.options: self.options[k] = v From f2c0bdcda9b1ce571ed802c722280f77eb92d9e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 6 May 2025 18:35:40 +0200 Subject: [PATCH 551/624] options: restore special behavior of CFLAGS vs. c_args For compatibility with Autotools, CFLAGS is added to the linker command line if the compiler acts as a linker driver. However, this behavior was lost in commit d37d649b0 ("Make all Meson level options overridable per subproject.", 2025-02-13). The issue is that (for example) c_link_args is stored in env.options, and from that point on it is treated as a machine-file option. This includes not being able to override it in compilers.get_global_options: - initialize_from_top_level_project_call places it in pending_options - add_lang_args passes the right value to add_compiler_option - add_compiler_option calls add_system_option_internal - add_system_option_internal fishes the value out of pending_options and ignores what get_global_options provided. Instead, store the putative values of the compiler options coming from the environment in a separate dictionary, that is only accessed by get_global_options. This way it never appears in pending_options, and also there is no internal *_env_args variable anymore. Fixes: #14533 Signed-off-by: Paolo Bonzini (cherry picked from commit 2d1c67f095e5bf709a1fa2a21a99e8c1d543961c) --- mesonbuild/compilers/compilers.py | 17 ++++++++---- mesonbuild/environment.py | 45 +++++++++++++++++-------------- mesonbuild/options.py | 6 +++++ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3c1d58b4ed18..4db4ad2cf078 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1407,12 +1407,19 @@ def get_global_options(lang: str, 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_args_from_envvar = False + comp_options = env.coredata.optstore.get_pending_value(argkey) + if comp_options is None: + comp_args_from_envvar = True + comp_options = env.env_opts.get(argkey, []) + + link_args_from_envvar = False + link_options = env.coredata.optstore.get_pending_value(largkey) + if link_options is None: + link_args_from_envvar = True + link_options = env.env_opts.get(largkey, []) - 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' @@ -1426,7 +1433,7 @@ def get_global_options(lang: str, description + ' linker', link_options, split_args=True, allow_dups=True) - if comp.INVOKES_LINKER and comp_key == envkey: + if comp.INVOKES_LINKER and comp_args_from_envvar and link_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. diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index a3b8cd5f2970..2a9cf165b191 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -648,6 +648,11 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cor # 'optimization' and 'debug' keys, it override them. self.options: T.MutableMapping[OptionKey, ElementaryOptionValues] = 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: T.MutableMapping[OptionKey, ElementaryOptionValues] = {} + self.machinestore = machinefile.MachineFileStore(self.coredata.config_files, self.coredata.cross_files, self.source_dir) ## Read in native file(s) to override build machine configuration @@ -819,35 +824,35 @@ def _set_default_options_from_env(self) -> None: 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') + key = key.evolve(f'{lang}_args') env_opts[key].extend(p_list) - for k, v in env_opts.items(): + # 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 k not in self.options: - self.options[k] = v + 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. diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 2d96380c4403..b31b88e09eee 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -835,6 +835,12 @@ def ensure_and_validate_key(self, key: T.Union[OptionKey, str]) -> OptionKey: key = key.as_host() return key + 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 get_value(self, key: T.Union[OptionKey, str]) -> ElementaryOptionValues: return self.get_value_object(key).value From c3ae3292664663d54a86c966272202e604c29cce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 May 2025 08:26:01 +0200 Subject: [PATCH 552/624] unittests: add test for CFLAGS in linker command line Signed-off-by: Paolo Bonzini (cherry picked from commit e71d1dc6c3d500b8d7efe45ec77b4b22b1efa1a6) --- unittests/linuxliketests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 6b896d73f942..376c3959ec45 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -985,6 +985,22 @@ def test_global_rpath(self): got_rpath = get_rpath(os.path.join(yonder_prefix, 'bin/rpathified')) 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(): From c150eb63ebc7159859c248205cb3913ee995e2c3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 May 2025 11:50:02 +0200 Subject: [PATCH 553/624] mconf: remove dead function This was the pre-refactoring implementation of `-D`, which is now unused. Signed-off-by: Paolo Bonzini (cherry picked from commit 625184335f6898a573d7e24040d3e94bc9ab0c8e) --- mesonbuild/mconf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 416caf1a0e59..fec6c0917237 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -125,9 +125,6 @@ def __init__(self, build_dir: str): 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: From 93a900b6c4ebf762b356372b2b6a38637fd4c5e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 May 2025 11:58:53 +0200 Subject: [PATCH 554/624] mconf: extract option processing to CoreData.set_from_configure_command Prepare to reuse the logic in "meson setup --reconfigure". Signed-off-by: Paolo Bonzini (cherry picked from commit d8defe8ecdc5bca03acc269c11afe9db7ed6d454) --- mesonbuild/coredata.py | 7 +++++++ mesonbuild/mconf.py | 6 +----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 9a265ee27d21..6d39ec9cc3ee 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -412,6 +412,13 @@ def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionK return option_object.validate_value(override) return value + def set_from_configure_command(self, options: SharedCMDOptions) -> bool: + 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}') + return self.optstore.set_from_configure_command(all_D, unset_opts) + def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: dirty = False try: diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index fec6c0917237..ec6e58601842 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -370,11 +370,7 @@ 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) + save |= c.coredata.set_from_configure_command(options) coredata.update_cmd_line_file(builddir, options) if options.clearcache: c.clear_cache() From 4b3185fba962e814d176c17cc3372522542764bc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 May 2025 11:54:09 +0200 Subject: [PATCH 555/624] msetup: update coredata if options are passed together with --reconfigure This makes "meson setup --reconfigure" behave quite literally the same as "meson configure" + "meson setup"; except that saving coredata and cmdline file is delayed until the setup succeeds. Fixes: #14575 Signed-off-by: Paolo Bonzini (cherry picked from commit 2f6fc30df10583c77dc1318f2fb62111de3e121c) --- mesonbuild/msetup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 81dd1833004e..c8770fd93de3 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -180,6 +180,9 @@ def validate_dirs(self) -> T.Tuple[str, str]: # 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()) From 91bea51b388901b24272bf317e74d6763cc36429 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 May 2025 11:44:36 +0200 Subject: [PATCH 556/624] add test case for setting options from reconfigure Signed-off-by: Paolo Bonzini (cherry picked from commit 5081536f5764cfa8f6ce9cd7a5e43595b8b0f50d) --- test cases/common/40 options/meson.build | 3 +-- unittests/platformagnostictests.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/test cases/common/40 options/meson.build b/test cases/common/40 options/meson.build index 3849d54eaeb4..f41265af7cb3 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/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index d6c00787ccbd..c1475c51e491 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -453,6 +453,16 @@ def test_configure_removed_option(self) -> None: self.setconf('-Dneg_int_opt=0') self.assertIn('Unknown options: ":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.""" testdir = self.copy_srcdir(os.path.join(self.common_test_dir, '40 options')) From a832cb52897ea1fc2906e6dc2efd29dde4ebd5b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 May 2025 00:36:50 +0200 Subject: [PATCH 557/624] options: "custom" buildtype does not trigger changes in debug or optimization options Fixes: #14603 Signed-off-by: Paolo Bonzini (cherry picked from commit 203078f396656ad3694689549203662a4913b0dd) --- mesonbuild/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index b31b88e09eee..91f25e9b7865 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1044,7 +1044,7 @@ def replace(v: str) -> str: 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') From faabf0a658893ca9daff36ebe8a110b5ed8a552e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 May 2025 00:36:50 +0200 Subject: [PATCH 558/624] unittests: add minimal coverage of --buildtype=custom Signed-off-by: Paolo Bonzini (cherry picked from commit 68c55b86110dd333206ed974fcd1f3780a83a86c) --- unittests/platformagnostictests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index c1475c51e491..75071d9da55d 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -203,10 +203,10 @@ def test_validate_dirs(self): # 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) From 1ee05a62790bf24682f1cc399d3a5a0311e39b77 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 May 2025 10:53:48 +0200 Subject: [PATCH 559/624] options: remove unnecessary Union[OptionKey, str] classify_D_arguments returns a list with OptionKeys in it. Rename the function so that the difference with set_option is clear. Signed-off-by: Paolo Bonzini (cherry picked from commit 8f4f16521751fe48ba20f0d0b8c7a7d4097815d6) # Conflicts: # mesonbuild/options.py --- mesonbuild/options.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 91f25e9b7865..b1dd88eb3328 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1054,11 +1054,7 @@ def replace(v: str) -> str: 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_option_maybe_root(self, o: OptionKey, new_value: str) -> bool: if o in self.options: return self.set_option(o, new_value) o = o.as_root() @@ -1070,9 +1066,9 @@ def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) - (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) + dirty |= self.set_option_maybe_root(key, valstr) for key, valstr in project_options: - dirty |= self.set_option_from_string(key, valstr) + dirty |= self.set_option_maybe_root(key, valstr) for keystr, valstr in perproject_global_options: if keystr in self.augments: if self.augments[keystr] != valstr: From ac06686a43b9c8760f2dfb7eacbb0b96b3d3c7a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 May 2025 11:04:11 +0200 Subject: [PATCH 560/624] options: commonize code to accept unknown options The check for unknown options is duplicated in OptionStore and MesonApp. Place the better version of the two as a new method of OptionStore, and use it in OptionStore.validate_cmd_line_options. Signed-off-by: Paolo Bonzini (cherry picked from commit cc382b237ac88b14210e0292940394385cab31f6) # Conflicts: # mesonbuild/options.py --- mesonbuild/msetup.py | 16 ++++++---------- mesonbuild/options.py | 23 ++++++++++++++++------- unittests/optiontests.py | 11 +++++++++++ 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index c8770fd93de3..d4e745468379 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -11,7 +11,7 @@ from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog from .mesonlib import MesonException -from .options import COMPILER_BASE_OPTIONS, OptionKey +from .options import OptionKey if T.TYPE_CHECKING: from typing_extensions import Protocol @@ -191,18 +191,14 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) - with mesonlib.BuildDirLock(self.build_dir): 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: + def check_unused_options(self, coredata: 'coredata.CoreData', cmd_line_options: T.Any, all_subprojects: T.Mapping[str, object]) -> None: pending = coredata.optstore.pending_options errlist: T.List[str] = [] + known_subprojects = all_subprojects.keys() 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: - continue - if coredata.optstore.is_compiler_option(opt): - continue - if (coredata.optstore.is_base_option(opt) and - opt.evolve(subproject=None, machine=mesonlib.MachineChoice.HOST) in COMPILER_BASE_OPTIONS): + # It is not an error to set wrong option for unknown subprojects + # because they might be used in future reconfigurations + if coredata.optstore.accept_as_pending_option(opt, known_subprojects): continue keystr = str(opt) if keystr in cmd_line_options: diff --git a/mesonbuild/options.py b/mesonbuild/options.py index b1dd88eb3328..6690c8b431ce 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1382,6 +1382,20 @@ def initialize_from_top_level_project_call(self, else: self.pending_options[key] = valstr + def accept_as_pending_option(self, key: OptionKey, known_subprojects: T.Optional[T.Union[T.Set[str], T.KeysView[str]]] = None) -> bool: + # 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: + if known_subprojects is None or key.subproject not in known_subprojects: + return True + if self.is_compiler_option(key): + return True + return (self.is_base_option(key) and + key.evolve(subproject=None, machine=MachineChoice.HOST) in COMPILER_BASE_OPTIONS) + def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> None: unknown_options = [] for keystr, valstr in cmd_line_options.items(): @@ -1389,13 +1403,8 @@ def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> N 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: + + if key in self.pending_options and not self.accept_as_pending_option(key): unknown_options.append(f'"{key}"') if unknown_options: diff --git a/unittests/optiontests.py b/unittests/optiontests.py index aad8eadfc405..416da8f47c17 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -203,6 +203,17 @@ def test_b_default(self): value = optstore.get_default_for_b_option(OptionKey('b_vscrt')) self.assertEqual(value, 'from_buildtype') + def test_b_nonexistent(self): + optstore = OptionStore(False) + assert optstore.accept_as_pending_option(OptionKey('b_ndebug')) + assert not optstore.accept_as_pending_option(OptionKey('b_whatever')) + + def test_subproject_nonexistent(self): + optstore = OptionStore(False) + subprojects = {'found'} + assert not optstore.accept_as_pending_option(OptionKey('foo', subproject='found'), subprojects) + assert optstore.accept_as_pending_option(OptionKey('foo', subproject='whatisthis'), subprojects) + def test_deprecated_nonstring_value(self): # TODO: add a lot more deprecated option tests optstore = OptionStore(False) From f372a562fd48b63c3738303bff8457517159d99f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 May 2025 11:08:07 +0200 Subject: [PATCH 561/624] options: accept compiler and built-in options in --reconfigure and "meson configure" Follow the same logic that is used at the end of the first invocation. This fixes meson setup --reconfigure -Db_ndebug=true on a project that has no language that defines b_ndebug. Signed-off-by: Paolo Bonzini (cherry picked from commit 8dcc9d3423762f9cf996af87b8a2c71c7111aa32) --- mesonbuild/options.py | 9 +++++++-- unittests/optiontests.py | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 6690c8b431ce..571453848ce9 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1057,8 +1057,13 @@ def replace(v: str) -> str: def set_option_maybe_root(self, o: OptionKey, new_value: str) -> bool: if o in self.options: return self.set_option(o, new_value) - o = o.as_root() - return self.set_option(o, new_value) + if self.accept_as_pending_option(o): + 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 + else: + o = o.as_root() + return self.set_option(o, new_value) def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool: dirty = False diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 416da8f47c17..0bdd7dc4550a 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -208,6 +208,10 @@ def test_b_nonexistent(self): assert optstore.accept_as_pending_option(OptionKey('b_ndebug')) assert not optstore.accept_as_pending_option(OptionKey('b_whatever')) + def test_reconfigure_b_nonexistent(self): + optstore = OptionStore(False) + optstore.set_from_configure_command(['b_ndebug=true'], []) + def test_subproject_nonexistent(self): optstore = OptionStore(False) subprojects = {'found'} From 697dbd094013ae94de38f1372970418967e263d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 May 2025 11:12:21 +0200 Subject: [PATCH 562/624] options: accept build options in --reconfigure or "meson configure" Make more of the first-invocation logic apply to subsequent configuration of the build tree. This also opens the door for using set_option_maybe_root for the first invocation. This is a huge cleanup but also a larger change, and therefore not something for stable branches. Leave it for later. Signed-off-by: Paolo Bonzini (cherry picked from commit 1ac86c1df8dd3321f81fa5c4eb861c6e86d891ab) --- mesonbuild/options.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 571453848ce9..1ecec77e8487 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1055,6 +1055,9 @@ def replace(v: str) -> str: return changed def set_option_maybe_root(self, o: OptionKey, new_value: str) -> bool: + if not self.is_cross and o.is_for_build(): + return False + if o in self.options: return self.set_option(o, new_value) if self.accept_as_pending_option(o): From e6e8a5e30e9cd86c0b29ce54363e060d0be1d018 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 16:26:24 +0200 Subject: [PATCH 563/624] gnome: fix typo in creating gir flags Append to scan_env_ldflags instead of overwriting it. Fixes: #14631 Signed-off-by: Paolo Bonzini (cherry picked from commit ecf29efef43064e9edb54ddc1efcc3da8281cc47) --- mesonbuild/modules/gnome.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 67641339a2ee..68b44d13ec53 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1171,10 +1171,10 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut scan_env_ldflags = [] 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'] From 860033fd51d97dc518c708cd01c4ec99d488718c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 16:26:44 +0200 Subject: [PATCH 564/624] gnome: initialize CFLAGS environment variable to linker arguments The CFLAGS environment variable is used for g-ir-scanner's linking pass, It is emptied since commit 237513dff ("modules/gnome, modules/Python: Allow injecting RPATH flags through LDFLAGS if needed", 2025-04-09); which could even be considered a bugfix if it didn't break Fedora quite badly. I could not write a testcase, but the culprit seems to be the -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 that Fedora places in CFLAGS. The file contains *cc1_options: + %{!r:%{!fpie:%{!fPIE:%{!fpic:%{!fPIC:%{!fno-pic:-fPIE}}}}}} and the lack of -fPIE option upsets the linker. Fix by priming the contents of the CFLAGS variable with the c_link_args being used for the build. Fixes: #14631 Signed-off-by: Paolo Bonzini (cherry picked from commit 6cf312022f431008d2519e347b630b4ab2ce227c) --- mesonbuild/modules/gnome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 68b44d13ec53..d8a2376cd291 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1168,7 +1168,7 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut 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 From 15d3bdb2fe2d29e50ac2553e539b28c08b885d28 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 17:36:14 +0200 Subject: [PATCH 565/624] ci: do not update pip on macos The workaround in commit 2f146775e ("Work around the mising RECORD file with homebrew pip.", 2025-05-04) is brittle and has broken again. Do not bother updating setuptool and pip at all, they should be relatively up to date on homebrew. Suggested-by: Eli Schwartz Signed-off-by: Paolo Bonzini (cherry picked from commit cf3e5fe4c389d5ea7b8448b94f009157404c192c) --- .github/workflows/macos.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 2d2ea39c096a..675807bd8653 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -98,8 +98,6 @@ 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 - env: CPPFLAGS: "-I/opt/homebrew/include" From f5aa3c7c346b2298f2e8c3daaf89985feadb2388 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 May 2025 19:14:44 +0200 Subject: [PATCH 566/624] options: process project options before machine options Restore the behavior from before commit d37d649b0 ("Make all Meson level options overridable per subproject.", 2025-02-13). The old code was: options: T.MutableMapping[OptionKey, T.Any] = OrderedDict() # process project default options for k, v in default_options.items(): if not subproject or k.subproject == subproject: options[k] = v # override them with machine default and command line options options.update(env.options) env.options = options Fixes: #14608 Signed-off-by: Paolo Bonzini (cherry picked from commit 82fca502e131007f21d849f6a51064a92ac5b9ba) # Conflicts: # mesonbuild/options.py --- mesonbuild/options.py | 44 ++++++++++++++++++++-------------------- unittests/optiontests.py | 16 +++++++++++++++ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 1ecec77e8487..78dafd8bca53 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1310,28 +1310,6 @@ def initialize_from_top_level_project_call(self, 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: @@ -1368,6 +1346,28 @@ def initialize_from_top_level_project_call(self, self.set_option(proj_key, valstr) else: self.pending_options[key] = valstr + 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(cmd_line_options, dict) for keystr, valstr in cmd_line_options.items(): if isinstance(keystr, str): diff --git a/unittests/optiontests.py b/unittests/optiontests.py index 0bdd7dc4550a..5758a2d5c8ac 100644 --- a/unittests/optiontests.py +++ b/unittests/optiontests.py @@ -35,6 +35,22 @@ def test_toplevel_project(self): 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").""" From fe87af6dfe215698641ddde980b4932004ed37cc Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Sun, 18 May 2025 14:35:00 -0300 Subject: [PATCH 567/624] cmake: Fix toolchain including unsupported languages The most egregious cases are Nasm (which needs to be transformed to `ASM_NASM`) and Rust (which is not yet supported by CMake). See https://cmake.org/cmake/help/v4.0/command/project.html (cherry picked from commit 315140fd07625ea9f692ac01fc19da27ae1754c5) --- mesonbuild/cmake/common.py | 1 + mesonbuild/cmake/toolchain.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py index 7644c0b966f6..b7ab1ba2288f 100644 --- a/mesonbuild/cmake/common.py +++ b/mesonbuild/cmake/common.py @@ -19,6 +19,7 @@ 'cuda': 'CUDA', 'objc': 'OBJC', 'objcpp': 'OBJCXX', + 'nasm': 'ASM_NASM', 'cs': 'CSharp', 'java': 'Java', 'fortran': 'Fortran', diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index d410886ecf25..11a00be5d5f5 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -175,7 +175,12 @@ def make_abs(exe: str) -> str: # Set the compiler variables 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 +216,7 @@ def update_cmake_compiler_state(self) -> None: # 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)}) From bf17c66f63ed97566516057c2bf35131d1ef240d Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Fri, 23 May 2025 15:11:19 -0400 Subject: [PATCH 568/624] Bump versions to 1.8.1 for release --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index b78cf968fcdf..20a54e3896e8 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" "May 2025" "meson 1.8.1" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6d39ec9cc3ee..daa4e0299b15 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.8.0' +version = '1.8.1' # 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 From 9062126a1a2d60d45ab64dd6126ba3e8e1c8de9b Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sat, 24 May 2025 22:38:44 -0400 Subject: [PATCH 569/624] Unbreak "Fix setuptools 49 test. (fixes #7452)" This unbreaks commit 59910c437a81b94c72e3cbdfc2c3612fae576d6e. It kind of maybe appears to fix something but does break it all quite terribly too. Totally random subdirectories of site-packages/ should certainly not be added to PYTHONPATH regardless of anything else as that may include mesonbuild/, leading to `import ast` finding mesonbuild.ast instead... The underlying issue here is that egg .pth is not loaded from PYTHONPATH at all, which means depending on versions of e.g. setuptools this test may end up solely testing system-installed meson, or fail entirely. So we can fix this by manually adding eggs specifically. (cherry picked from commit 23e4e2f20ca698f4070f7da79f5e06249d34b622) --- run_meson_command_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py index f9faca9af6fe..7265d3e031e2 100755 --- a/run_meson_command_tests.py +++ b/run_meson_command_tests.py @@ -143,7 +143,7 @@ def test_meson_installed(self): 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 = [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 From 8b2e8dddd4a2bbe14740fbdc871b23495e6dbe06 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Thu, 15 May 2025 17:14:35 -0400 Subject: [PATCH 570/624] vs2010backend: Escape linker arguments This allows linker arguments containing a space (or other special character) to pass through to the linker correctly. For example, the spaces in "test cases/windows/25 embed manifest" when using meson.project_source_root(). (cherry picked from commit 32068a59f5e30862c61ebc2bcd4d2373694e1c96) --- mesonbuild/backend/vs2010backend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 283f9f0a3744..1a673217ac13 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1493,8 +1493,9 @@ def add_non_makefile_vcxproj_elements( 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) From dcf41df756a98d5a02ea510fc37f04ae21db70d5 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Mon, 12 May 2025 16:52:33 -0400 Subject: [PATCH 571/624] vs2010backend: fix EmbedManifest state when /MANIFEST:EMBED is used With introduction of dfd8cfbd8d9c VS compile is broken for cases where /MANIFEST:EMBED linker flag is actually used. The fix keeps the default creation of but adopts the same strategy as e3db7af0ea41, that is to scan the link flags for the embed case to decide state is emit 'true' or 'false' for EmbedManifest. (cherry picked from commit 6447eb00d87f439147249dd593635ae14e56d0dd) # Conflicts: # mesonbuild/backend/vs2010backend.py --- mesonbuild/backend/vs2010backend.py | 8 +++++--- .../windows/25 embed manifest/DPIAware.manifest | 9 +++++++++ test cases/windows/25 embed manifest/meson.build | 11 +++++++++++ test cases/windows/25 embed manifest/prog.c | 3 +++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 test cases/windows/25 embed manifest/DPIAware.manifest create mode 100644 test cases/windows/25 embed manifest/meson.build create mode 100644 test cases/windows/25 embed manifest/prog.c diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 1a673217ac13..503d5ccfa438 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -692,9 +692,8 @@ def create_basic_project(self, target_name, *, 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' + ET.SubElement(direlem, 'EmbedManifest').text = 'true' if gen_manifest == 'embed' else 'false' + ET.SubElement(direlem, 'GenerateManifest').text = 'true' if gen_manifest else 'false' return (root, type_config) @@ -2097,6 +2096,7 @@ def generate_lang_standard_info(self, file_args: T.Dict[str, CompilerArgs], clco pass # Returns if a target generates a manifest or not. + # Returns 'embed' if the generated manifest is embedded. def get_gen_manifest(self, target): if not isinstance(target, build.BuildTarget): return True @@ -2114,6 +2114,8 @@ def get_gen_manifest(self, target): 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 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 000000000000..f2708ecb1315 --- /dev/null +++ b/test cases/windows/25 embed manifest/DPIAware.manifest @@ -0,0 +1,9 @@ + + + + + true + PerMonitorV2 + + + 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 000000000000..0f4c9b43f072 --- /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 000000000000..b1d9c2ce482b --- /dev/null +++ b/test cases/windows/25 embed manifest/prog.c @@ -0,0 +1,3 @@ +int main(int argc, char *argv[]) { + return 0; +} From 30834d8e343094559a8bec60fe5e789964a6604b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 May 2025 15:46:23 +0200 Subject: [PATCH 572/624] cargo: set edition for build machine as well This fixes compilation of glib-macros-0.20.4: error[E0405]: cannot find trait `TryFrom` in this scope --> ../subprojects/glib-macros-0.20.4/src/clone.rs:22:10 | 22 | impl<'a> TryFrom<&'a Ident> for CaptureKind { | ^^^^^^^ not found in this scope | = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 help: consider importing this trait | 3 + use std::convert::TryFrom; | Signed-off-by: Paolo Bonzini (cherry picked from commit 7c2f22feefe2d8014138b02a2119026a01f52dea) --- mesonbuild/cargo/interpreter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index af272a86fb04..78bce95d227e 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -608,6 +608,7 @@ def _create_project(self, pkg: PackageState, build: builder.Builder) -> T.List[m """ default_options: T.List[mparser.BaseNode] = [] default_options.append(build.string(f'rust_std={pkg.manifest.package.edition}')) + default_options.append(build.string(f'build.rust_std={pkg.manifest.package.edition}')) if pkg.downloaded: default_options.append(build.string('warning_level=0')) From 2449399a4c51fafc41acf8aaa0cc70ab58360a9d Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 26 May 2025 19:37:56 -0500 Subject: [PATCH 573/624] Vala: Fix GResource source directories The code that adds `--gresourcesdir=` arguments to valac based on the source directories of GResource dependencies was incorrect. It added the current target directory to the source path, but the GResource source directories are already relative to the build directory. (cherry picked from commit 9daf73008f7b97858dbd34f38a577b531e412570) --- mesonbuild/backend/ninjabackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index d7de987990c8..000e072bf26b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1775,7 +1775,7 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ 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 From 57f55b1942b93e1e0b1751c57b1f340229f701a1 Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 26 May 2025 19:37:56 -0500 Subject: [PATCH 574/624] Vala: Add test for generated UI in subdirectories (cherry picked from commit f3adf71b85960da3ff22914cd1a5ce885d3b6a67) --- .../meson.build | 22 +++++++++++++++++++ .../subdir/TestBox.ui.in | 6 +++++ .../subdir/meson.build | 13 +++++++++++ .../subdir/test.gresource.xml | 6 +++++ .../test.vala | 7 ++++++ 5 files changed, 54 insertions(+) create mode 100644 test cases/vala/31 generated ui file subdirectory/meson.build create mode 100644 test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in create mode 100644 test cases/vala/31 generated ui file subdirectory/subdir/meson.build create mode 100644 test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml create mode 100644 test cases/vala/31 generated ui file subdirectory/test.vala 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 000000000000..421058151828 --- /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 000000000000..bf5c83178b65 --- /dev/null +++ b/test cases/vala/31 generated ui file subdirectory/subdir/TestBox.ui.in @@ -0,0 +1,6 @@ + + + + + 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 000000000000..dbe9344a5fe8 --- /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 000000000000..382b95193a7c --- /dev/null +++ b/test cases/vala/31 generated ui file subdirectory/subdir/test.gresource.xml @@ -0,0 +1,6 @@ + + + + TestBox.ui + + 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 000000000000..36f565b63451 --- /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; +} From 0961b3c2e228d5a828b9f49a12ff321891615aac Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Mon, 12 May 2025 22:13:34 -0300 Subject: [PATCH 575/624] cmake: Fix target_link_libraries against project targets These were supported for shared libraries, but for static libraries the link_with property was never populated with `LINK_LIBRARIES` or `INTERFACE_LINK_LIBRARIES`. Fixes #13101 (cherry picked from commit e6332b8ed4c0ff8cb3d1774c27e09943bd280230) --- mesonbuild/cmake/interpreter.py | 19 ++++++++++++++++--- mesonbuild/cmake/tracetargets.py | 9 +++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 929627661e7e..609038ddf930 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -223,6 +223,7 @@ def __init__(self, target: CMakeTarget, env: 'Environment', for_machine: Machine 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] = [] @@ -363,6 +364,8 @@ def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: Path, su 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') @@ -957,17 +960,27 @@ def analyse(self) -> None: 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() diff --git a/mesonbuild/cmake/tracetargets.py b/mesonbuild/cmake/tracetargets.py index 2cc0c1722c3a..9873845f849b 100644 --- a/mesonbuild/cmake/tracetargets.py +++ b/mesonbuild/cmake/tracetargets.py @@ -45,6 +45,7 @@ def __init__(self) -> None: 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', @@ -144,9 +145,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] From 00245a55864d608ac0b779f3539eac72edc44aba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Jun 2025 16:57:55 +0200 Subject: [PATCH 576/624] ninjabackend: clear allow_thin_archives[] at generate() time Compilers are not ready at the time the backend is created. Do not look at them until generate() runs. Fixes: 4e9fac15d ("interpreter: add backend options before validating the command line options", 2025-05-16) Signed-off-by: Paolo Bonzini (cherry picked from commit dd12351c0d9d8d15d3945151c72e491a2bc2dc03) --- mesonbuild/backend/ninjabackend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 000e072bf26b..957dfb2d2969 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -500,11 +500,6 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter # - 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 def create_phony_target(self, dummy_outfile: str, rulename: str, phony_infilename: str) -> NinjaBuildElement: ''' @@ -595,6 +590,12 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[T.Dict] = None) # 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\'') + 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 = environment.detect_ninja_command_and_version(log=True) if self.environment.coredata.optstore.get_value_for(OptionKey('vsenv')): builddir = Path(self.environment.get_build_dir()) From bba726b04680376c61d98bc05024ab687b0de0b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Jun 2025 17:01:12 +0200 Subject: [PATCH 577/624] vsbackend: detect toolset and SDK version at generate() time Compilers are not ready at the time the backend is created. Do not look at them until generate() runs. Fixes: 4e9fac15d ("interpreter: add backend options before validating the command line options", 2025-05-16) Signed-off-by: Paolo Bonzini (cherry picked from commit cbb259990f9509593a2f5765d0d625bcc1157463) --- mesonbuild/backend/vs2010backend.py | 4 ++++ mesonbuild/backend/vs2012backend.py | 2 ++ mesonbuild/backend/vs2013backend.py | 2 ++ mesonbuild/backend/vs2015backend.py | 2 ++ mesonbuild/backend/vs2017backend.py | 2 ++ mesonbuild/backend/vs2019backend.py | 2 ++ mesonbuild/backend/vs2022backend.py | 2 ++ 7 files changed, 16 insertions(+) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 503d5ccfa438..fbb113faf998 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -147,6 +147,9 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter 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()) @@ -227,6 +230,7 @@ def generate(self, # 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 diff --git a/mesonbuild/backend/vs2012backend.py b/mesonbuild/backend/vs2012backend.py index 307964bdd1c2..922cd60d49d9 100644 --- a/mesonbuild/backend/vs2012backend.py +++ b/mesonbuild/backend/vs2012backend.py @@ -21,6 +21,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter 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 ae0b68bbef2f..cf5d5980e8bb 100644 --- a/mesonbuild/backend/vs2013backend.py +++ b/mesonbuild/backend/vs2013backend.py @@ -20,6 +20,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter 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 4c515cca6f1e..1862def1abba 100644 --- a/mesonbuild/backend/vs2015backend.py +++ b/mesonbuild/backend/vs2015backend.py @@ -21,6 +21,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter 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 393544febb60..372e1ce0d097 100644 --- a/mesonbuild/backend/vs2017backend.py +++ b/mesonbuild/backend/vs2017backend.py @@ -24,6 +24,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter 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 4d6e226d16ff..61ad75d5ea7e 100644 --- a/mesonbuild/backend/vs2019backend.py +++ b/mesonbuild/backend/vs2019backend.py @@ -22,6 +22,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter super().__init__(build, interpreter) 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 27e0438c7075..ca449a4e57f8 100644 --- a/mesonbuild/backend/vs2022backend.py +++ b/mesonbuild/backend/vs2022backend.py @@ -22,6 +22,8 @@ def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter super().__init__(build, interpreter, 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()): From 83d16072662a09b14d7813c03063513ac43e7e2b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 May 2025 09:06:04 -0700 Subject: [PATCH 578/624] unittests: use subtests to break up test_compiler_detection Which is a very large and complicated test function (cherry picked from commit 49b0a2afa3cf1df40eb4b9554f22bdca52e81292) --- unittests/allplatformstests.py | 205 +++++++++++++++++---------------- 1 file changed, 106 insertions(+), 99 deletions(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 0618b20ee570..17180f8a09a0 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1099,110 +1099,117 @@ def test_compiler_detection(self): 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): + 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: + 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): + 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.assertFalse(cc.is_64) + 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) + + wcc = compiler_from_language(env, lang, MachineChoice.HOST) + 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') From d719bc53e6ab0847fbd7d8569a5a1203610ea3d2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 May 2025 09:09:29 -0700 Subject: [PATCH 579/624] unittests: add asserts to test_compiler_detection Add a few asserts for functions that could in theory return None, but if they do something has gone *really* wrong with the test. (cherry picked from commit f4918374d972e4680ea46ee00796d8fec21a6adf) --- unittests/allplatformstests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 17180f8a09a0..1ef403c4fb20 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1101,6 +1101,7 @@ def test_compiler_detection(self): if evar in os.environ: with self.subTest(lang=lang, evar=evar): ecc = compiler_from_language(env, lang, MachineChoice.HOST) + assert ecc is not None, "Something went really wrong" self.assertTrue(ecc.version) elinker = detect_static_linker(env, ecc) # Pop it so we don't use it for the next detection @@ -1131,6 +1132,7 @@ def test_compiler_detection(self): # Do auto-detection of compiler based on platform, PATH, etc. with self.subTest(lang=lang): cc = compiler_from_language(env, lang, MachineChoice.HOST) + assert cc is not None, "Something went really wrong" self.assertTrue(cc.version) linker = detect_static_linker(env, cc) # Check compiler type From 1c7b6b5fcdc824628a4ca0dab8e6dde4c74c30c2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 29 May 2025 09:28:37 -0700 Subject: [PATCH 580/624] unittests: Handle missing compiler support in test_compiler_detection This wraps all of the compiler detections in this test case in try/except blocks. These blocks will return a skipTest for Python >= 3.11 (where subTest and skipTest interact correctly), and continue if they do not. For Meson CI runs they will fail the specific subtest, which is also an improvement as it can help pinpoint exactly which subtest failed. Fixes: #14579 (cherry picked from commit 75f79fc234437ee5f95686432619397e936bcd92) --- unittests/allplatformstests.py | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1ef403c4fb20..1678ba446de7 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1100,12 +1100,21 @@ def test_compiler_detection(self): # Detect with evar and do sanity checks on that if evar in os.environ: with self.subTest(lang=lang, evar=evar): - ecc = compiler_from_language(env, lang, MachineChoice.HOST) + 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) - # 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) @@ -1131,7 +1140,15 @@ def test_compiler_detection(self): # Do auto-detection of compiler based on platform, PATH, etc. with self.subTest(lang=lang): - cc = compiler_from_language(env, lang, MachineChoice.HOST) + 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) @@ -1196,7 +1213,15 @@ def test_compiler_detection(self): # 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) + 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'] From 12103c5e9438d4448fe97282cb63cfb81a45ca26 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 19 May 2025 11:34:37 -0700 Subject: [PATCH 581/624] build: fix tasking compiler b_lto support after option refactor The weird format is to minimize the diff in the next fixup patch. (cherry picked from commit d01cce9b1e47feb131af0c644d02e58b62a7a6e7) --- mesonbuild/build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7320b88fdd4b..ff402dee01bd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2199,10 +2199,10 @@ def post_init(self) -> None: elif self.rust_crate_type == 'staticlib': self.suffix = 'a' 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.suffix = 'a' + if 'c' in self.compilers and self.compilers['c'].get_id() == 'tasking' and not self.prelink: + if self.environment.coredata.optstore.get_value_for(OptionKey('b_lto'), self.subproject): + self.suffix = 'ma' self.filename = self.prefix + self.name + '.' + self.suffix self.outputs[0] = self.filename From 522fd0cdb1e59fd80963b2d72864875f65e80473 Mon Sep 17 00:00:00 2001 From: gerioldman Date: Tue, 20 May 2025 21:06:23 +0200 Subject: [PATCH 582/624] backend: fix tasking compiler support after option refactor (cherry picked from commit 78efb1c2d0d1221eada5aa9966f5a8aa95b56134) --- mesonbuild/backend/ninjabackend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 957dfb2d2969..937b1bd82b9d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3128,9 +3128,9 @@ def generate_single_compile(self, target: build.BuildTarget, src, # 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) @@ -3689,7 +3689,7 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. 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. From c2ccd4ba611544dd7481d83cffbc12342ef7ab8e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 19 May 2025 11:39:15 -0700 Subject: [PATCH 583/624] build: Fix tasking compiler b_lto detection for overrides Actually take an override into account, ie: ```meson library( ... override_options : {'b_lto' : false}, ) ``` (cherry picked from commit a7a228fa74d27328575863b295fff1773e96abdb) --- mesonbuild/build.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ff402dee01bd..867775f7eabb 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2201,7 +2201,13 @@ def post_init(self) -> None: else: self.suffix = 'a' if 'c' in self.compilers and self.compilers['c'].get_id() == 'tasking' and not self.prelink: - if self.environment.coredata.optstore.get_value_for(OptionKey('b_lto'), self.subproject): + key = OptionKey('b_lto', self.subproject, self.for_machine) + try: + v = self.environment.coredata.get_option_for_target(self, key) + except KeyError: + v = self.environment.coredata.optstore.get_value_for(key) + assert isinstance(v, bool), 'for mypy' + if v: self.suffix = 'ma' self.filename = self.prefix + self.name + '.' + self.suffix self.outputs[0] = self.filename From 038335658695588634adbbae0958c1f8812f1eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corentin=20No=C3=ABl?= Date: Tue, 6 May 2025 12:09:56 +0200 Subject: [PATCH 584/624] vala: Pass --shared-library to valac when generating a .gir file This is required to make sure that the generated .gir file actually contains all the information to be used dynamically. Valac supports this argument since 0.29.3 released in 2015. (cherry picked from commit f4f64280c36f738b2bfde82d33c375cf9263dec0) --- mesonbuild/backend/ninjabackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 937b1bd82b9d..cd7b7610832f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1766,6 +1766,9 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ 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', self.get_target_filename_for_linking(shared_target)] # 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') From c7ebee5cddbfe7e3b0ec1bf7d38195107ebf1966 Mon Sep 17 00:00:00 2001 From: Mugundanmcw Date: Fri, 6 Jun 2025 15:50:22 +0530 Subject: [PATCH 585/624] Fix Flang stdlib linking for LLVM toolchain versions >= 19 (cherry picked from commit b474a8f715f51c22698caa999c40d86d9ad6ab86) --- mesonbuild/compilers/fortran.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 5794db06bb18..a25778cf931d 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -643,7 +643,11 @@ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: # 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): From d0e29f501c83060dc0f61bc7bc65dc774fd9065e Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Mon, 19 May 2025 22:20:59 +0200 Subject: [PATCH 586/624] CI: always install llvm for MSYS2 Due to some recent package splits llvm is no longer installed when clang is installed and the meson test suite was depending on the transitive dependency. Instead explicitly install llvm in all cases. (cherry picked from commit 8f767cdab38085bc27d79075548a3b7480c5634c) --- .github/workflows/msys2.yml | 1 + test cases/frameworks/15 llvm/test.json | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index 9101e6b9d1f5..8c7b2e5e29d7 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -85,6 +85,7 @@ 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 diff --git a/test cases/frameworks/15 llvm/test.json b/test cases/frameworks/15 llvm/test.json index fa883b1f43c6..b9cdc20a3970 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"] }, From ba54a3e19a626e63e352abe928cb8905d98b8871 Mon Sep 17 00:00:00 2001 From: Sam James Date: Thu, 22 May 2025 20:08:12 +0100 Subject: [PATCH 587/624] tests: don't skip frameworks/17 mpi for auto/pkgconfig This is fixed in Ubuntu rolling now and Bionic wasn't affected to begin with. Bug: https://bugs.debian.org/1078026 (cherry picked from commit 858905ec12a56b2aab1b2940d15847541325855e) --- test cases/frameworks/17 mpi/test.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test cases/frameworks/17 mpi/test.json b/test cases/frameworks/17 mpi/test.json index 3a46657ef112..cbd1686121c5 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"] }, { From 5a82ea0501736a666ca9cc003ea0774f8219fd65 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Mon, 9 Jun 2025 13:13:29 -0400 Subject: [PATCH 588/624] Bump versions to 1.8.2 for release --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index 20a54e3896e8..02df49cf5043 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "May 2025" "meson 1.8.1" "User Commands" +.TH MESON "1" "June 2025" "meson 1.8.2" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index daa4e0299b15..6d387339931e 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -72,7 +72,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.8.1' +version = '1.8.2' # The next stable version when we are in dev. This is used to allow projects to # require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when From 7edd7394cb75d929f56cfc722ce3c5f31e8d0c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20D=C3=B8rum?= Date: Fri, 2 May 2025 10:56:28 +0200 Subject: [PATCH 589/624] cpp: fix _LIBCPP_ENABLE_ASSERTIONS warning libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS from version 18. However, the libc++ shipped with Apple Clang backported that deprecation in version 17 already, which is the version which Apple currently ships for macOS. This PR changes the _LIBCPP_ENABLE_ASSERTIONS deprecation check to use version ">=17" on Apple Clang. (cherry picked from commit a16ec8b0fb6d7035b669a13edd4d97ff0c307a0b) --- mesonbuild/compilers/cpp.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 1755db2d7429..ed8d1cf05a41 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -311,6 +311,9 @@ def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subpro return libs return [] + def is_libcpp_enable_assertions_deprecated(self) -> bool: + return version_compare(self.version, ">=18") + def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: if disable: return ['-DNDEBUG'] @@ -323,7 +326,7 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: if self.language_stdlib_provider(env) == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] else: - if version_compare(self.version, '>=18'): + if self.is_libcpp_enable_assertions_deprecated(): return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST'] elif version_compare(self.version, '>=15'): return ['-D_LIBCPP_ENABLE_ASSERTIONS=1'] @@ -343,7 +346,12 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(AppleCompilerMixin, AppleCPPStdsMixin, ClangCPPCompiler): - pass + def is_libcpp_enable_assertions_deprecated(self) -> bool: + # Upstream libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS + # in favor of _LIBCPP_HARDENING_MODE from version 18 onwards, + # but Apple Clang 17's libc++ has back-ported that change. + # See: https://github.com/mesonbuild/meson/issues/14440 + return version_compare(self.version, ">=17") class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): From b13b2be888e624988711aad983f546289c3ff837 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Tue, 27 May 2025 14:32:54 +0200 Subject: [PATCH 590/624] Add project to check that test target's args and depends are in path (cherry picked from commit 546a7796de07922048bdfde336d98c89059c76c5) --- .../libs/a/lib_a.c | 5 ++ .../libs/a/lib_a.def | 3 + .../libs/a/meson.build | 5 ++ .../libs/b/lib_b.c | 5 ++ .../libs/b/lib_b.def | 3 + .../libs/b/meson.build | 5 ++ .../libs/meson.build | 2 + .../meson.build | 19 ++++++ .../282 test args and depends in path/test.c | 67 +++++++++++++++++++ 9 files changed, 114 insertions(+) create mode 100644 test cases/common/282 test args and depends in path/libs/a/lib_a.c create mode 100644 test cases/common/282 test args and depends in path/libs/a/lib_a.def create mode 100644 test cases/common/282 test args and depends in path/libs/a/meson.build create mode 100644 test cases/common/282 test args and depends in path/libs/b/lib_b.c create mode 100644 test cases/common/282 test args and depends in path/libs/b/lib_b.def create mode 100644 test cases/common/282 test args and depends in path/libs/b/meson.build create mode 100644 test cases/common/282 test args and depends in path/libs/meson.build create mode 100644 test cases/common/282 test args and depends in path/meson.build create mode 100644 test cases/common/282 test args and depends in path/test.c 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 000000000000..7191a69c316d --- /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 000000000000..4af3bdb165e6 --- /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 000000000000..0b4b6a4addf8 --- /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 000000000000..17e5730f65b8 --- /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 000000000000..403a731aa771 --- /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 000000000000..766125d5901c --- /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 000000000000..b00ea8a2ecb6 --- /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 000000000000..d9dd9ad5da6d --- /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 000000000000..82452bab319b --- /dev/null +++ b/test cases/common/282 test args and depends in path/test.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#else +#include +#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; +} From 0edf3ac14da988a7424a7ffc0c82871348483b01 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Tue, 1 Apr 2025 15:41:59 +0200 Subject: [PATCH 591/624] Test serialization: set LD_LIBRARY_PATH also on Darwin It's needed on Darwin for the same reason it's needed on generic UNIX. Darwin supports both LD_LIBRARY_PATH and DYLD_LIBRARY_PATH, but the two are not quite equivalent [1], so we set both. [1] https://github.com/ffi/ffi/blob/29ad900a/lib/ffi/dynamic_library.rb#L40 (cherry picked from commit 287d0e87a932427dd06b4b18f6ba4da649583697) --- mesonbuild/backend/backends.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 3dfa2fba604a..19876c187f43 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1289,8 +1289,12 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali 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): @@ -1303,6 +1307,8 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali 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(), From 27075bb04d27f0e964b47fe1d508b637e60f8417 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Wed, 28 May 2025 14:03:47 +0200 Subject: [PATCH 592/624] Test serialization: Also look for depends when setting PATH on Windows Fixes https://github.com/mesonbuild/meson/issues/4668 (cherry picked from commit a6e540d18a46393cbf8c153a76388af1b8b573e5) --- mesonbuild/backend/backends.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 19876c187f43..b9aafce4bded 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1258,6 +1258,7 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = [] if isinstance(exe, build.CustomTarget): extra_bdeps = list(exe.get_transitive_build_target_deps()) + extra_bdeps.extend(t.depends) extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps) for a in t.cmd_args: if isinstance(a, build.BuildTarget): From c9e10abb949b0c550b17da73a20d7a3464217349 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Thu, 29 May 2025 12:04:34 +0200 Subject: [PATCH 593/624] Test serialization: Simplify determination of Windows extra paths Pointed out by Charles Brunet (cherry picked from commit 3878f8db71e35c7a57697c97debb5a5fd8b2fc47) --- mesonbuild/backend/backends.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b9aafce4bded..763dde33a829 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1259,12 +1259,8 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali 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 = [] From bf8038dee2de1b8cbf963d8e729ef9f56cc2218d Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 17 Jun 2025 14:14:43 +0300 Subject: [PATCH 594/624] Update Windows version in CI. Windows 2019 in GH Actions goes away on the 30th. (cherry picked from commit c3ea9753e3941789bdd4b8458a9912f407a5f9de) --- .github/workflows/msys2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index 8c7b2e5e29d7..d1c2fa2b698d 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 From 418af9e01d894e3a416d312e74dca3e8b18bdd7a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Tue, 17 Jun 2025 19:27:19 +0300 Subject: [PATCH 595/624] Remove coverage measurement. (cherry picked from commit 7f908d909ea6874af0512e2258777f5cd42be790) --- .github/codecov.yml | 11 ----- .github/workflows/cygwin.yml | 18 +------- .github/workflows/lint.yml | 2 +- .github/workflows/macos.yml | 32 ++------------ .github/workflows/msys2.yml | 15 +------ .github/workflows/nonnative.yml | 14 +------ .github/workflows/os_comp.yml | 26 +----------- .../workflows/unusedargs_missingreturn.yml | 30 ++----------- azure-pipelines.yml | 5 --- ci/combine_cov.sh | 10 ----- ci/coverage.ps1 | 14 ------- ci/run.ps1 | 4 +- ci/usercustomize.py | 5 --- tools/run_with_cov.py | 42 ------------------- 14 files changed, 17 insertions(+), 211 deletions(-) delete mode 100644 .github/codecov.yml delete mode 100755 ci/combine_cov.sh delete mode 100644 ci/coverage.ps1 delete mode 100644 ci/usercustomize.py delete mode 100755 tools/run_with_cov.py diff --git a/.github/codecov.yml b/.github/codecov.yml deleted file mode 100644 index fa7b82a052b8..000000000000 --- 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 d641b185050b..2ba1ff2071ef 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/lint.yml b/.github/workflows/lint.yml index 5588034723b8..ef5889579009 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -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 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 675807bd8653..3afb4baca353 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,7 +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 cython coverage + python3 -m pip install cython - env: CPPFLAGS: "-I/opt/homebrew/include" LDFLAGS: "-L/opt/homebrew/lib" @@ -112,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 d1c2fa2b698d..b926d189847d 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -90,7 +90,7 @@ jobs: - 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: | @@ -125,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 2712d1032935..1714edda463c 100644 --- a/.github/workflows/nonnative.yml +++ b/.github/workflows/nonnative.yml @@ -36,18 +36,6 @@ 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' - - - 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 + run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS --cross ubuntu-armhf.json --cross-only' diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 0912a750a76b..4b9b7a4a6eae 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 d6f1246d1ae6..4367ce556caf 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/azure-pipelines.yml b/azure-pipelines.yml index ea511f33f94e..86c6b3aa23f9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -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/combine_cov.sh b/ci/combine_cov.sh deleted file mode 100755 index 99a503bb8db4..000000000000 --- 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 ebd7cd4c3fd4..000000000000 --- 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 d781cf725fce..5b754bd3647b 100644 --- a/ci/run.ps1 +++ b/ci/run.ps1 @@ -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 d72c6ad2d4c3..000000000000 --- 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/tools/run_with_cov.py b/tools/run_with_cov.py deleted file mode 100755 index 0d3fba654f9f..000000000000 --- 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()) From a1b40843d863260d01f26b210cbf31002bbc38d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Jun 2025 13:52:52 +0200 Subject: [PATCH 596/624] compilers: pgi: fix preprocessing arguments Based on reports from the users, PGI compilers need an explicit "-o -" on the command line to emit preprocessed output on stdout. Override the methods in the PGICompiler mixin to add it when output goes to stdout but not when output goes elsewhere. Fixes: #13216 Signed-off-by: Paolo Bonzini (cherry picked from commit fc2c68aa1b16a1895f4fdc8e7000d50952a47ecb) --- mesonbuild/compilers/mixins/pgi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 50335c895cc5..fddc8378f636 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -54,6 +54,12 @@ def get_pic_args(self) -> T.List[str]: def openmp_flags(self, env: Environment) -> 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] From 425f473e62f1ff0bfc30c1b1322ece9d46ef8f04 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Jun 2025 13:27:56 +0200 Subject: [PATCH 597/624] options: do not store duplicate UserOptions These options are never looked up except by "meson configure" and introspection, because option values are taken from self.augments[] instead. Fixes: #14558 Signed-off-by: Paolo Bonzini (cherry picked from commit aa735fb7342c864b5e315c7e22d0d57a6a4337fa) --- mesonbuild/options.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 78dafd8bca53..2b2f9d578725 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -909,16 +909,16 @@ def add_system_option_internal(self, key: OptionKey, valobj: AnyOptionType) -> N 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 - - if pval is not None: - self.set_option(key, pval) + if pval is not None: + self.augments[key] = pval + else: + self.options[key] = valobj + if pval is not None: + self.set_option(key, pval) def add_compiler_option(self, language: str, key: T.Union[OptionKey, str], valobj: AnyOptionType) -> None: key = self.ensure_and_validate_key(key) From 9befcecf6a30d6fed6b61a63c3fd6bbcd3bbffba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Jun 2025 13:39:51 +0200 Subject: [PATCH 598/624] mconf: print global compiler options Fixes: #14476 Signed-off-by: Paolo Bonzini (cherry picked from commit 3d4c9154b21b4414efaa7b3f15b75c7e49596764) --- mesonbuild/mconf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index ec6e58601842..f74b14d668e0 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -294,9 +294,9 @@ def print_default_values_warning() -> None: 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) From 203abf60e3a96bd852b0b6ac252d9b810256cf10 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Jun 2025 13:38:37 +0200 Subject: [PATCH 599/624] mconf: print sections lazily To the user, toplevel project options are the same as global options because they are not prefixed by ":". So, even if we starting printing toplevel project overrides, we want to keep project options outside of that section. Then one would end up with: ... Project options --------------- Main project: Subproject foo: The "Main project" line is printed because '' is in self.all_subprojects, but there is nothing below because project options have been printed before. To fix this, print section names lazily, together with their first content item. Signed-off-by: Paolo Bonzini (cherry picked from commit 3b52f339ba23aa6aa03130cb350d8318ec9f27f0) --- mesonbuild/mconf.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index f74b14d668e0..9ef0a848274f 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -73,6 +73,7 @@ def __init__(self, build_dir: str): 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] = [] @@ -207,11 +208,13 @@ def _add_line(self, name: LOGLINE, value: LOGLINE, choices: LOGLINE, descr: LOGL self.descr_col.append(descr) def add_option(self, name: str, 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) 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') @@ -220,11 +223,17 @@ def add_title(self, title: str) -> None: 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: From 73bdfe6aa3c163a916e56fb0b7f1d1c7768b22b8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Jun 2025 13:39:18 +0200 Subject: [PATCH 600/624] mconf: print overrides specific to the main project Those were hidden, because the global options look at subproject `None` rather than `''`. Signed-off-by: Paolo Bonzini (cherry picked from commit 50ae7363ca65fae157d71bf554e930f96a0f8b0e) --- mesonbuild/mconf.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 9ef0a848274f..3a0b0cc474f4 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -192,7 +192,7 @@ def split_options_per_subproject(self, opts: T.Union[options.MutableKeyedOptionD ) -> 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 @@ -297,7 +297,7 @@ def print_default_values_warning() -> None: 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]) @@ -313,8 +313,9 @@ def print_default_values_warning() -> None: 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: @@ -323,7 +324,7 @@ def print_default_values_warning() -> None: 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() From 2e5d1b4e00ee3e8050dffc8467d31e4ac962b543 Mon Sep 17 00:00:00 2001 From: Michael J Steel Date: Fri, 4 Jul 2025 17:17:03 +1000 Subject: [PATCH 601/624] Correct get_option_std_args for IntelClCCompiler In get_option_std_args for the Intel C compiler, the requested command line flag is 'winlibs' which returns a list of strings of libs. It should be 'std' as in other adjacent classes, to return the particular value of the C standard desired. (cherry picked from commit d990d0694483aac96bf8b588a83fcdf93067d71a) --- mesonbuild/compilers/c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 7a2fec59e6a6..424b61251618 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -504,7 +504,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': def get_option_std_args(self, target: BuildTarget, env: Environment, 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', env, 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) From 0d07601f490e26d104dfaa2160865a0e99481d3d Mon Sep 17 00:00:00 2001 From: mattbsage Date: Thu, 19 Jun 2025 13:26:17 +1000 Subject: [PATCH 602/624] Add -F compiler flag for included MacOS frameworks Currently, when using frameworks on MacOS systems, Meson will send the appropriate flags to the linker but fails to pass flags to the compiler, resulting in the headers not being found for the included frameworks. This patch adds the required "-F{framework}" flag to the compiler options, so that the compiler can find the headers of included frameworks. See: https://github.com/mesonbuild/meson/issues/14641 (cherry picked from commit ed6cbaa92aeb015fe05a068303a248fad1c144f4) --- mesonbuild/cmake/tracetargets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/cmake/tracetargets.py b/mesonbuild/cmake/tracetargets.py index 9873845f849b..2b2b93de7eb5 100644 --- a/mesonbuild/cmake/tracetargets.py +++ b/mesonbuild/cmake/tracetargets.py @@ -87,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] From b293ca7516de7ce6e40d8fca4b4727414f9d7e84 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Jul 2025 11:07:09 +0200 Subject: [PATCH 603/624] options: ignore c(pp)_link_args when deciding whether to apply C(XX)FLAGS Commit 2d1c67f09 ("options: restore special behavior of CFLAGS vs. c_args", 2025-05-15) incorrectly checked the presence of c_link_args, and did not apply CFLAGS if c_link_args was set. This was not the behavior of 1.7, so remove the check. Signed-off-by: Paolo Bonzini (cherry picked from commit 5736120edc2322476a01622b1e5106877f08d327) --- mesonbuild/compilers/compilers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 4db4ad2cf078..cedb9ac9b4c6 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1414,10 +1414,8 @@ def get_global_options(lang: str, comp_args_from_envvar = True comp_options = env.env_opts.get(argkey, []) - link_args_from_envvar = False link_options = env.coredata.optstore.get_pending_value(largkey) if link_options is None: - link_args_from_envvar = True link_options = env.env_opts.get(largkey, []) assert isinstance(comp_options, (str, list)), 'for mypy' @@ -1433,7 +1431,7 @@ def get_global_options(lang: str, description + ' linker', link_options, split_args=True, allow_dups=True) - if comp.INVOKES_LINKER and comp_args_from_envvar and link_args_from_envvar: + 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. From 524e547aee314f27b32a405cdf63849e2de366fc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Jul 2025 11:07:35 +0200 Subject: [PATCH 604/624] options: apply CFLAGS even if c_link_args exists This restores the behavior before 1.8's option store refactoring. The bug arises because c_link_args has been stored in pending_options, and therefore the extended value (which get_global_options correctly computes) is overwritten by the value passed on the command line. In fact, this bug is the reason why I added the "link_args_from_envvar" check: the CFLAGS would be ignored anyway, so I put that logic in code instead of relying on the option store's behavior. The fix is to extend the value *after* the option has been added and the pending_options resolved. This requires a tiny refactoring of the split between CoreData.add_lang_args and compilers.get_global_options. Signed-off-by: Paolo Bonzini (cherry picked from commit 1c14c08aabfd4852451d194a6f5aee9568fe4455) --- mesonbuild/compilers/compilers.py | 11 +++++------ mesonbuild/coredata.py | 6 +----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index cedb9ac9b4c6..00be201de5d2 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1399,10 +1399,10 @@ def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[ std.choices = value -def get_global_options(lang: str, +def add_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, - env: 'Environment') -> dict[OptionKey, options.AnyOptionType]: + env: 'Environment'): """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) @@ -1431,6 +1431,9 @@ def get_global_options(lang: str, description + ' linker', link_options, split_args=True, allow_dups=True) + env.coredata.optstore.add_compiler_option(lang, argkey, cargs) + env.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 @@ -1438,7 +1441,3 @@ def get_global_options(lang: str, # 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/coredata.py b/mesonbuild/coredata.py index 6d387339931e..4f9d4438dffd 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -588,11 +588,7 @@ 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) + compilers.add_global_options(lang, comp, for_machine, env) 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) From 8c973486453805f3224f62ebe780d73b724bba7e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Jul 2025 11:15:06 +0200 Subject: [PATCH 605/624] unittests: add test for c_link_args and CFLAGS interaction Signed-off-by: Paolo Bonzini (cherry picked from commit 2dc4ddeccbb01b8e35bf378b1c447799124d188a) --- unittests/linuxliketests.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 376c3959ec45..c25449c98a4b 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -446,6 +446,24 @@ def test_installed_soname(self): 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 From d9b98854b6b784db4be79c0361f388230badbceb Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 3 Jul 2025 18:08:09 +0100 Subject: [PATCH 606/624] qt dependency: Don't insert backslashes into cflags on windows The use of os.path.join is inserting `\` into the compile_args, which is wrong since qmake outputs `/` even on Windows, and in fact it causes pkgconfig files that depend on qmake dependencies to have `\` path separators, which get resolved as escape sequences. Use Path.as_posix() to avoid this. (cherry picked from commit 9ca276442eaaa6cb0ef3b595f3c679aabad15a39) --- mesonbuild/dependencies/qt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index a3a938828f17..8bb269e83d3f 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -9,6 +9,7 @@ import abc import re import os +from pathlib import Path import typing as T from .base import DependencyException, DependencyMethods @@ -50,7 +51,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: @@ -303,7 +304,7 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]): 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': From c0dade7a318694af695f509e6ce6251768e804ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 08:42:53 +0200 Subject: [PATCH 607/624] interpreter: allow retrieving build options with get_option() Fixes: #14788 Signed-off-by: Paolo Bonzini (cherry picked from commit bdabc2e5de8764521ded7e2e919a13e9e214757d) --- docs/yaml/functions/get_option.yaml | 5 +++++ mesonbuild/interpreter/interpreter.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/yaml/functions/get_option.yaml b/docs/yaml/functions/get_option.yaml index 0934758bc868..b8b1fc4cdddf 100644 --- a/docs/yaml/functions/get_option.yaml +++ b/docs/yaml/functions/get_option.yaml @@ -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/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 8fb660d1f078..7da5436c61bd 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1077,7 +1077,7 @@ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str], value_object: T.Optional[options.AnyOptionType] try: - optkey = options.OptionKey(optname, self.subproject) + optkey = options.OptionKey.from_string(optname).evolve(subproject=self.subproject) value_object, value = self.coredata.optstore.get_value_object_and_value_for(optkey) except KeyError: if self.coredata.optstore.is_base_option(optkey): From 4fe5593bf42e5156e114aef46ea64adf6472c434 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 12 Jul 2025 11:23:50 +0200 Subject: [PATCH 608/624] unittests: add test case for setting and retrieving build options Check that build options fall back to host options, and that they can be retrieved with get_option. Signed-off-by: Paolo Bonzini (cherry picked from commit 1537a13d468aa7b49e7e0917be258e2e1a8b515f) --- test cases/unit/69 cross/crossfile.in | 3 +++ test cases/unit/69 cross/meson.build | 11 ++++++++++- test cases/unit/69 cross/nativefile.in | 2 ++ unittests/allplatformstests.py | 9 +++++++-- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test cases/unit/69 cross/nativefile.in diff --git a/test cases/unit/69 cross/crossfile.in b/test cases/unit/69 cross/crossfile.in index 678e8d3a3abd..beab9bc45edc 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 acf4f0f177b2..645d453c5ba4 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 000000000000..054cf108d3cc --- /dev/null +++ b/test cases/unit/69 cross/nativefile.in @@ -0,0 +1,2 @@ +[built-in options] +c_args = ['@c_args@'] diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 1678ba446de7..ae6d231b2b8d 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3276,10 +3276,15 @@ def test_clang_tidy_fix(self): 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) From 11ed299e96e6bce8ba483195f8947e63dd85e0d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 12 Jul 2025 11:28:45 +0200 Subject: [PATCH 609/624] mconf: print build option names including "build." Signed-off-by: Paolo Bonzini (cherry picked from commit b9ddea245ef39fc29bd30a446c937058460c2196) --- mesonbuild/mconf.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 3a0b0cc474f4..933fa6554fa3 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -147,7 +147,7 @@ def print_aligned(self) -> None: 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) @@ -207,11 +207,12 @@ def _add_line(self, name: LOGLINE, value: LOGLINE, choices: LOGLINE, descr: LOGL 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() @@ -248,7 +249,7 @@ def print_options(self, title: str, opts: T.Union[options.MutableKeyedOptionDict # printable_value = '' #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: From 38162e79ffdfaad7be963704ef1a0a1610930f5c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 12 Jul 2025 11:34:19 +0200 Subject: [PATCH 610/624] environment: allow setting build options with "build." prefix This is allowed -- it is already deprecated for the cross file, but it should not assert. Add a deprecation for the native file too, and remove the assert. Fixes: d37d649b08b832d52fa684bc0506829fb40d5261 Fixes: #14789 Signed-off-by: Paolo Bonzini (cherry picked from commit 6eb1f2de031be04e2757b0d09784d5ada53d87f7) --- mesonbuild/environment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 2a9cf165b191..0ba64f091666 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -735,6 +735,8 @@ def mfilestr2key(self, machine_file_string: str, section: T.Optional[str], secti 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 From 4443094c29ad619a000baa00f55441d67966debc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 14 Jul 2025 19:50:05 +0200 Subject: [PATCH 611/624] environment: really remove assertion Fixes: #14789 Signed-off-by: Paolo Bonzini (cherry picked from commit 0376d655694747ba36aa2bfd94e5bce2d2f1efa8) --- mesonbuild/environment.py | 1 - test cases/unit/69 cross/nativefile.in | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 0ba64f091666..c20390fccb92 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -728,7 +728,6 @@ def __init__(self, source_dir: str, build_dir: T.Optional[str], cmd_options: cor 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.') diff --git a/test cases/unit/69 cross/nativefile.in b/test cases/unit/69 cross/nativefile.in index 054cf108d3cc..9a639999d355 100644 --- a/test cases/unit/69 cross/nativefile.in +++ b/test cases/unit/69 cross/nativefile.in @@ -1,2 +1,2 @@ [built-in options] -c_args = ['@c_args@'] +build.c_args = ['@c_args@'] From 2ad03a03fe61dbeb703b7a8139736882e17a67e3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 16 Jul 2025 13:46:25 -0700 Subject: [PATCH 612/624] coredata: actually use the correct machine for the dependency cache key (cherry picked from commit 8f9eea89b4c104fee3469ac3f9b93e0c8169967d) --- mesonbuild/coredata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 4f9d4438dffd..e12fb6495a0b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -147,8 +147,8 @@ 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]] = { From 4a622cf5f243c0c051fb354122bb70e2abd29879 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 16 Jul 2025 13:48:50 -0700 Subject: [PATCH 613/624] coredata: remove use of `stringlistify` to do a cast This is a heavyweight and expensive function to use to verify that we have a string list. Just cast to make mypy happy, we know what we have. (cherry picked from commit cec99fbb3ea770f5d88f3acccb50ebde55ac2e56) --- mesonbuild/coredata.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index e12fb6495a0b..5ef5e28a5adc 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -18,7 +18,6 @@ MesonException, MachineChoice, PerMachine, PerMachineDefaultable, default_prefix, - stringlistify, pickle_load ) @@ -152,8 +151,8 @@ def __init__(self, builtins: options.OptionStore, for_machine: MachineChoice): 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' From 19d99476242f5a4d4123ed8b2ef6cb4665ded92a Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 13 Jul 2025 14:41:28 +0100 Subject: [PATCH 614/624] wrap: Don't use old patch.exe from Strawberry Perl It is too old and barfs on patches from git-format-patch: ``` Applying diff file "orc-0.4.38\0001-meson-fix-symbols-leaking-from-static-library-on-Win.patch" patching file meson.build Assertation failed! Program: C:\Strawberry\c\bin\patch.EXE File: .\src\patch\2.5.9\patch-2.5.9-src\patch.c, Line 354 Expression: hunk ``` 2.6.1 is the oldest known version that works correctly. (cherry picked from commit ab261f9951f235c776b8664fbdbf0365a8cf535d) --- mesonbuild/wrap/wrap.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 9af1f39efaae..e22dabfec45b 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -53,7 +53,21 @@ ALL_TYPES = ['file', 'git', 'hg', 'svn', 'redirect'] -PATCH = shutil.which('patch') +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') + def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult: """ raises WrapException if not whitelisted subdomain """ From 29fc4b03268771e65468c286ae54d0f81fb1d85f Mon Sep 17 00:00:00 2001 From: Henrik Lehtonen Date: Sun, 27 Jul 2025 09:17:56 +0200 Subject: [PATCH 615/624] c: add more exceptions to -Wno-* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit eca1ac18dc1978b15b500c9f1710c05cb1ccc0ec (#14252) added support for properly detecting the -Wno-vla-larger-than flag: a value must be specified for its positive form (-Wvla-larger-than=N), or GCC will exit with the error "missing argument to ‘-Walloc-size-larger-than=’". There is a handful of other -Wno-* flags whose positive form act in the same manner, but are not covered here: * -Wno-alloc-size-larger-than (GCC >=7.1.0) * -Wno-alloca-larger-than (GCC >=7.1.0) * -Wno-frame-larger-than (GCC >=4.4.0) * -Wno-stack-usage (GCC >=4.7.0) Add logic to treat these in the same way. Signed-off-by: Henrik Lehtonen (cherry picked from commit e501a226e91df77d60d058c5d9efd33bd2b38f99) --- mesonbuild/compilers/mixins/clike.py | 17 +++++++++++++---- test cases/common/104 has arg/meson.build | 19 ++++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index b163407f7aa8..e9125d548325 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1287,12 +1287,21 @@ def _has_multi_arguments(self, args: T.List[str], env: 'Environment', code: str) # 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,'): diff --git a/test cases/common/104 has arg/meson.build b/test cases/common/104 has arg/meson.build index 466bed9dfe13..500b8a9ad097 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') From 77eaf12c755eb5ead3060bd02468db939aba72d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 10 Jul 2025 20:10:23 +0200 Subject: [PATCH 616/624] scalapack: Fix exception when MKLROOT is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix `scalapack.MKLPkgConfigDependency` not to crash when `MKLROOT` is unset: ``` File "/meson/mesonbuild/dependencies/scalapack.py", line 65, in __init__ super().__init__(name, env, kwargs, language=language) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/meson/mesonbuild/dependencies/pkgconfig.py", line 322, in __init__ self._set_cargs() ~~~~~~~~~~~~~~~^^ File "/meson/mesonbuild/dependencies/scalapack.py", line 141, in _set_cargs cflags = self.pkgconfig.cflags(self.name, allow_system, define_variable=(('prefix', self.__mklroot.as_posix()),)) ^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'as_posix' ``` The code is crashing because the `_set_cargs()` method assumes that `self.__mklroot` will always be set, presumably because it assumes it will only be called only when `MKLPkgConfigDependency.__init__()` finishes with `is_found = True`. However, both `_set_cargs()` and `_set_libs()` are called during `PkgConfigDependency.__init__()`, and therefore they will be called if pkg-config lookup succeeds even without `MKL_ROOT` set. To avoid the issue, check for `self.__mklroot is None` in both functions, and raise a `DependencyException` — effectively causing the pkg-config lookup to fail in a natural way. Fixes #11172 Signed-off-by: Michał Górny (cherry picked from commit 27c5ac36c841b047391fae9378684e06dcffea93) --- mesonbuild/dependencies/scalapack.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mesonbuild/dependencies/scalapack.py b/mesonbuild/dependencies/scalapack.py index c04d1f51fb47..f34692c25615 100644 --- a/mesonbuild/dependencies/scalapack.py +++ b/mesonbuild/dependencies/scalapack.py @@ -9,7 +9,7 @@ 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 @@ -65,8 +65,7 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], 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 +95,9 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], 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 +135,9 @@ def _set_libs(self) -> None: 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, From 90c1a71ea477570279bd796e75344a4cdcbd4bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Fri, 11 Jul 2025 19:20:17 +0200 Subject: [PATCH 617/624] hdf5: Skip failing Fortran config-tool test when h5fc is broken MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip the hdf5 Fortran config-tool test when running on CI and h5fc looks broken. This is needed due to upstream bug that h5fc does not include include paths when built via CMake, that currently affects at least Arch Linux and Gentoo. Bug: https://github.com/HDFGroup/hdf5/issues/5660 Signed-off-by: Michał Górny (cherry picked from commit 23e4901088cc4690d8ffbef9796421e42d77f5bd) --- test cases/frameworks/25 hdf5/meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test cases/frameworks/25 hdf5/meson.build b/test cases/frameworks/25 hdf5/meson.build index 38e001202bfc..095c63f66760 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 From f02eeb31ab0db5d2ec89affe6c557c1887dda357 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 7 May 2025 08:06:26 -0400 Subject: [PATCH 618/624] Replace deprecated setup.py install command In `run_meson_command_tests.py`. Replace it with `pip install .` if `pip` is available. Replace it with 'gpep517 install-from-source` if available. Else keep the old behaviour. Fixes #14522. (cherry picked from commit b4d32763940ae67d1ed36952112b3d94ba9f03a7) --- run_meson_command_tests.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py index 7265d3e031e2..2877b9873e1f 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 @@ -141,11 +146,17 @@ def test_meson_installed(self): # 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() if x.name.endswith('.egg')] - 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()) From 8f66a5937505ac2bf50ef34dd15b461655e71e68 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 21 Mar 2025 14:09:55 -0300 Subject: [PATCH 619/624] gnome.mkenums: Use rspfiles on Windows when possible Fixes #6710 (cherry picked from commit 49c462ba7a85063ac03b8cd67007c3607e2bc4de) --- mesonbuild/backend/backends.py | 24 ++++++++++++++++++++++-- mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/build.py | 4 ++++ mesonbuild/modules/gnome.py | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 763dde33a829..d4dfc66843b5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -29,7 +29,7 @@ File, MachineChoice, MesonException, MesonBugException, OrderedSet, ExecutableSerialisation, EnvironmentException, classify_unity_sources, get_compiler_for_source, - is_parent_path, + is_parent_path, get_rsp_threshold, ) from ..options import OptionKey @@ -533,6 +533,7 @@ def get_executable_serialisation( 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': @@ -594,6 +595,21 @@ def get_executable_serialisation( 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 +622,7 @@ def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTarge 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 +630,7 @@ def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTarge 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 +670,9 @@ def as_meson_exe_cmdline(self, exe: T.Union[str, mesonlib.File, build.BuildTarge 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, '' diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cd7b7610832f..b783417cad2d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1224,6 +1224,7 @@ def generate_custom_target(self, target: build.CustomTarget) -> None: 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})' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 867775f7eabb..9373618ebddf 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2687,6 +2687,7 @@ def __init__(self, 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', @@ -2719,6 +2720,9 @@ def __init__(self, # 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 diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index d8a2376cd291..3edf07062df4 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1991,6 +1991,7 @@ def _make_mkenum_impl( 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 {}', ) From 2ddec9e14f0669c84d749d9089cc3f109ffcea69 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 21 Mar 2025 22:02:36 -0300 Subject: [PATCH 620/624] gnome.mkenums: Allow passthrough of ExternalPrograms to enable converting only the real arguments to response file (cherry picked from commit e8c715786d85dcdbc367f3e379acae25a899c235) --- mesonbuild/backend/backends.py | 11 ++++++++--- mesonbuild/build.py | 5 +++-- mesonbuild/utils/universal.py | 9 +++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d4dfc66843b5..9eac7daa4121 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1585,7 +1585,7 @@ def get_custom_target_dir_include_args( 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 @@ -1598,7 +1598,7 @@ def eval_custom_target_command( 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) @@ -1634,6 +1634,9 @@ def eval_custom_target_command( 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) @@ -1658,7 +1661,7 @@ def eval_custom_target_command( # 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 get_introspect_command(self) -> str: @@ -2019,6 +2022,8 @@ def get_introspection_data(self, target_id: str, target: build.Target) -> T.List 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') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 9373618ebddf..7cf85de97ddb 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2600,7 +2600,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: @@ -2617,7 +2617,8 @@ def flatten_command(self, cmd: T.Sequence[T.Union[str, File, programs.ExternalPr # 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) diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index d165bf54d868..f3b4355bf6db 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -38,6 +38,7 @@ from ..environment import Environment from ..compilers.compilers import Compiler from ..interpreterbase.baseobjects import SubProject + from .. import programs class _EnvPickleLoadable(Protocol): @@ -1738,7 +1739,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 +1755,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 +1795,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 +1822,7 @@ def replace(m: T.Match[str]) -> str: _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: From 6b5a2f69951ef779ffe3f94b7aa1f2431f2783c1 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Thu, 5 Jun 2025 12:35:24 -0300 Subject: [PATCH 621/624] gnome.generate_gir: Use rspfiles on Windows when possible I ran into GStreamer's CI being overwhelmed by a 5k long command line to g-ir-scanner. This will help bypass the limitation. See https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/532 See #6710 (cherry picked from commit 98f58024020b3300853dd1d02a1025b82d16b3cf) --- mesonbuild/modules/gnome.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 3edf07062df4..331cc37c7838 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -957,8 +957,8 @@ def _make_gir_filelist(state: 'ModuleState', srcdir: str, ns: str, return gir_filelist_filename - @staticmethod def _make_gir_target( + self, state: 'ModuleState', girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], @@ -988,6 +988,11 @@ def _make_gir_target( run_env.set('CFLAGS', [quote_arg(x) for x in env_flags], ' ') run_env.merge(kwargs['env']) + gir_dep, _, _ = self._get_gir_dep(state) + + # response file supported? + rspable = mesonlib.version_compare(gir_dep.get_version(), '>= 1.85.0') + return GirTarget( girfile, state.subdir, @@ -1002,6 +1007,7 @@ def _make_gir_target( install_dir=[install_dir], install_tag=['devel'], env=run_env, + rspable=rspable, ) @staticmethod From 872ae45f7f088524676eccf26959f0fd6dfa1b13 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 29 Jul 2025 23:15:59 -0400 Subject: [PATCH 622/624] Bump versions to 1.8.3 for release --- man/meson.1 | 2 +- mesonbuild/coredata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/meson.1 b/man/meson.1 index 02df49cf5043..caf0f5b4baf9 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "June 2025" "meson 1.8.2" "User Commands" +.TH MESON "1" "July 2025" "meson 1.8.3" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 5ef5e28a5adc..3b17e74cee62 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -71,7 +71,7 @@ class SharedCMDOptions(Protocol): # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '1.8.2' +version = '1.8.3' # 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 From cb85ff2021ca3b67927d4e4afa186c3b9288138e Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Fri, 1 Aug 2025 09:46:41 +1000 Subject: [PATCH 623/624] fix long-standing linting problems --- mesonbuild/build.py | 23 ++------------------- mesonbuild/dependencies/__init__.py | 1 - mesonbuild/dependencies/blas_lapack.py | 24 +++++++++------------- mesonbuild/linkers/linkers.py | 3 --- mesonbuild/modules/features/feature.py | 14 +++++++------ mesonbuild/modules/features/module.py | 28 +++++++++++++++----------- mesonbuild/modules/features/utils.py | 1 - 7 files changed, 36 insertions(+), 58 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 27a8d2f85f1d..7cf85de97ddb 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1473,7 +1473,6 @@ def link_whole(self, targets: T.List[BuildTargetTypes], promoted: bool = False) msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. " msg += "Use the 'pic' option to static_library to build with PIC." raise InvalidArguments(msg) - self.check_can_link_together(t) if isinstance(self, StaticLibrary): # When we're a static library and we link_whole: to another static @@ -1495,29 +1494,11 @@ def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None: for t in self.link_targets: if t.is_internal() and t not in result: - self.check_can_extract_objects(t, origin, promoted=True) result.add(t) - t.get_internal_static_libraries_recurse(result, origin) + t.get_internal_static_libraries_recurse(result) for t in self.link_whole_targets: if t.is_internal(): - t.get_internal_static_libraries_recurse(result, origin) - - def check_can_extract_objects(self, t: T.Union[Target, CustomTargetIndex], origin: StaticLibrary, promoted: bool = False) -> None: - if isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust(): - # To extract objects from a custom target we would have to extract - # the archive, WIP implementation can be found in - # https://github.com/mesonbuild/meson/pull/9218. - # For Rust C ABI we could in theory have access to objects, but there - # are several meson issues that need to be fixed: - # https://github.com/mesonbuild/meson/issues/10722 - # https://github.com/mesonbuild/meson/issues/10723 - # https://github.com/mesonbuild/meson/issues/10724 - m = (f'Cannot link_whole a custom or Rust target {t.name!r} into a static library {origin.name!r}. ' - 'Instead, pass individual object files with the "objects:" keyword argument if possible.') - if promoted: - m += (f' Meson had to promote link to link_whole because {origin.name!r} is installed but not {t.name!r},' - f' and thus has to include objects from {t.name!r} to be usable.') - raise InvalidArguments(m) + t.get_internal_static_libraries_recurse(result) def _bundle_static_library(self, t: T.Union[BuildTargetTypes], promoted: bool = False) -> None: if self.uses_rust(): diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 3ce1071773b8..7262bc1bdaec 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -7,7 +7,6 @@ ExternalLibrary, DependencyException, DependencyMethods, BuiltinDependency, SystemDependency, get_leaf_external_dependencies) from .detect import find_external_dependency, get_dep_identifier, packages, _packages_accept_language -from .blas_lapack import openblas_factory __all__ = [ diff --git a/mesonbuild/dependencies/blas_lapack.py b/mesonbuild/dependencies/blas_lapack.py index d86f4d6ffa63..7cc4e99c3008 100644 --- a/mesonbuild/dependencies/blas_lapack.py +++ b/mesonbuild/dependencies/blas_lapack.py @@ -26,7 +26,7 @@ from ..mesonlib import MachineChoice from ..options import OptionKey -from .base import DependencyMethods, SystemDependency +from .base import DependencyMethods, SystemDependency, DependencyException from .cmake import CMakeDependency from .detect import packages from .factory import DependencyFactory, factory_methods @@ -34,6 +34,7 @@ if T.TYPE_CHECKING: from ..environment import Environment + from . factory import DependencyGenerator """ TODO: how to select BLAS interface layer (LP64, ILP64)? @@ -303,7 +304,7 @@ """ -def check_blas_machine_file(self, name: str, props: dict) -> T.Tuple[bool, T.List[str]]: +def check_blas_machine_file(name: str, props: dict) -> T.Tuple[bool, T.List[str]]: # TBD: do we need to support multiple extra dirs? incdir = props.get(f'{name}_includedir') assert incdir is None or isinstance(incdir, str) @@ -364,11 +365,11 @@ def check_symbols(self, compile_args, suffix=None, check_cblas=True, prototypes = "".join(f"void {symbol}{suffix}();\n" for symbol in symbols) calls = " ".join(f"{symbol}{suffix}();\n" for symbol in symbols) code = (f"{prototypes}" - "int main(int argc, const char *argv[])\n" - "{\n" + "int main(int argc, const char *argv[])\n" + "{\n" f" {calls}" - " return 0;\n" - "}" + " return 0;\n" + "}" ) code = '''#ifdef __cplusplus extern "C" { @@ -720,7 +721,6 @@ def detect_lapack_machine_file(self, props: dict) -> None: self.detect([libdir], [incdir]) - class AccelerateSystemDependency(BLASLAPACKMixin, SystemDependency): """ Accelerate is always installed on macOS, and not available on other OSes. @@ -781,7 +781,6 @@ def detect(self, kwargs: T.Dict[str, T.Any]) -> None: # We won't check symbols here, because Accelerate is built in a consistent fashion # with known symbol mangling, unlike OpenBLAS or Netlib BLAS/LAPACK. - return None def get_symbol_suffix(self) -> str: return '$NEWLAPACK' if self.interface == 'lp64' else '$NEWLAPACK$ILP64' @@ -804,7 +803,7 @@ def parse_mkl_options(self, kwargs: T.Dict[str, T.Any]) -> None: if not threading_module: self.threading = 'iomp' elif len(threading_module) > 1: - raise mesonlib.MesonException(f'Multiple threading arguments: {threading_modules}') + raise mesonlib.MesonException(f'Multiple threading arguments: {threading_module}') else: # We have a single threading option specified - validate and process it opt = threading_module[0] @@ -818,7 +817,7 @@ def parse_mkl_options(self, kwargs: T.Dict[str, T.Any]) -> None: if not sdl_module: self.use_sdl = 'auto' elif len(sdl_module) > 1: - raise mesonlib.MesonException(f'Multiple sdl arguments: {threading_modules}') + raise mesonlib.MesonException(f'Multiple sdl arguments: {threading_module}') else: # We have a single sdl option specified - validate and process it opt = sdl_module[0] @@ -845,8 +844,6 @@ def parse_mkl_options(self, kwargs: T.Dict[str, T.Any]) -> None: raise mesonlib.MesonException(f'Linking SDL implies using LP64 and Intel OpenMP, found ' f'conflicting options: {self.interface}, {self.threading}') - return None - class MKLPkgConfigDependency(BLASLAPACKMixin, MKLMixin, PkgConfigDependency): """ @@ -896,7 +893,6 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. if self.use_sdl: self.detect_sdl() - return None def detect_sdl(self) -> None: # Use MKLROOT in addition to standard libdir(s) @@ -926,7 +922,7 @@ def detect_sdl(self) -> None: self.is_found = True self.compile_args += incdir_args self.link_args += link_arg - if not sys.platform == 'win32': + if sys.platform != 'win32': self.link_args += ['-lpthread', '-lm', '-ldl'] # Determine MKL version diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index b57ae02ceea1..59f60e03a19c 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1396,9 +1396,6 @@ def rsp_file_syntax(self) -> RSPFileSyntax: def get_pie_args(self) -> T.List[str]: return [] - def get_pie_args(self) -> T.List[str]: - return [] - class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): diff --git a/mesonbuild/modules/features/feature.py b/mesonbuild/modules/features/feature.py index 7e0f621e543f..1bc603773301 100644 --- a/mesonbuild/modules/features/feature.py +++ b/mesonbuild/modules/features/feature.py @@ -3,7 +3,7 @@ import re from typing import ( Dict, Set, Tuple, List, Callable, Optional, - Union, Any, Iterable, cast, TYPE_CHECKING + Union, Any, Iterable, TYPE_CHECKING ) from dataclasses import dataclass, field from ...mesonlib import File, MesonException @@ -18,7 +18,6 @@ from typing import TypedDict from typing_extensions import NotRequired from ...interpreterbase import TYPE_var, TYPE_kwargs - from ...compilers import Compiler from .. import ModuleState @dataclass(unsafe_hash=True, order=True) @@ -80,7 +79,7 @@ def __init__(self, func_name: str, opt_name: str, default: Any = None): ) @staticmethod - def convert(func_name:str, opt_name: str, values: 'IMPLIED_ATTR', + def convert(func_name: str, opt_name: str, values: 'IMPLIED_ATTR', ) -> Union[None, List[ConflictAttr]]: if values is None: return None @@ -131,6 +130,7 @@ def convert(func_name:str, opt_name: str, values: 'IMPLIED_ATTR', Union[str, Dict[str, str]] ] ] + class FeatureKwArgs(TypedDict): #implies: Optional[List['FeatureObject']] implies: NotRequired[List[Any]] @@ -163,7 +163,8 @@ def __init__(self, state: 'ModuleState', super().__init__() @typed_pos_args('features.new', str, int) - @typed_kwargs('features.new', + @typed_kwargs( + 'features.new', KwargInfo( 'implies', (FeatureObject, ContainerTypeInfo(list, FeatureObject)), @@ -209,7 +210,8 @@ def init_attrs(state: 'ModuleState', def update_method(self, state: 'ModuleState', args: List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'FeatureObject': @noPosargs - @typed_kwargs('features.FeatureObject.update', + @typed_kwargs( + 'features.FeatureObject.update', KwargInfo('name', (NoneType, str)), KwargInfo('interest', (NoneType, int)), KwargInfo( @@ -306,7 +308,7 @@ def sort_cb(k: Union[FeatureObject, Iterable[FeatureObject]]) -> int: # FIXME: that's not a safe way to increase the rank for # multi features this why this function isn't considerd # accurate. - rank += len(prevalent_features) -1 + rank += len(prevalent_features) - 1 return rank return sorted(features, reverse=reverse, key=sort_cb) diff --git a/mesonbuild/modules/features/module.py b/mesonbuild/modules/features/module.py index a6f357b3f65b..c413c15e81b0 100644 --- a/mesonbuild/modules/features/module.py +++ b/mesonbuild/modules/features/module.py @@ -20,7 +20,6 @@ from typing import TypedDict from ...interpreterbase import TYPE_var, TYPE_kwargs from .. import ModuleState - from .feature import FeatureKwArgs class TestKwArgs(TypedDict): compiler: Optional[Compiler] @@ -109,6 +108,7 @@ def add_target(self, features: Union[FeatureObject, List[FeatureObject]], class Module(NewExtensionModule): INFO = ModuleInfo('features', '0.1.0') + def __init__(self) -> None: super().__init__() self.methods.update({ @@ -142,7 +142,8 @@ def _set_cache(self, state: 'ModuleState', key: str, self._cache_dict(state)[key] = val @typed_pos_args('features.test', varargs=FeatureObject, min_varargs=1) - @typed_kwargs('features.test', + @typed_kwargs( + 'features.test', KwargInfo('compiler', (NoneType, Compiler)), KwargInfo('anyfet', bool, default = False), KwargInfo('cached', bool, default = True), @@ -255,7 +256,7 @@ def test_any(self, state: 'ModuleState', features: Set[FeatureObject], features_any = set() for fet in all_features: _, test_any_result = self.cached_test( - state, features={fet,}, + state, features={fet, }, compiler=compiler, cached=cached, anyfet=False, @@ -293,7 +294,7 @@ def test(self, state: 'ModuleState', features: Set[FeatureObject], # Set the highest interested feature prevalent_features = sorted(features)[-1:] - prevalent_names = [fet.name for fet in prevalent_features] + prevalent_names = [fet.name for fet in prevalent_features] # prepare the result dict test_result: 'TestResultKwArgs' = { 'target_name': '__'.join(prevalent_names), @@ -307,6 +308,7 @@ def test(self, state: 'ModuleState', features: Set[FeatureObject], 'is_disabled': False, 'fail_reason': '', } + def fail_result(fail_reason: str, is_disabled: bool = False ) -> 'TestResultKwArgs': test_result.update({ @@ -337,7 +339,7 @@ def fail_result(fail_reason: str, is_disabled: bool = False predecessor_features = implied_features.difference(_caller) for fet in sorted(predecessor_features): _, pred_result = self.cached_test( - state, features={fet,}, + state, features={fet, }, compiler=compiler, cached=cached, anyfet=False, @@ -431,7 +433,8 @@ def fail_result(fail_reason: str, is_disabled: bool = False build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget )) - @typed_kwargs('features.multi_targets', + @typed_kwargs( + 'features.multi_targets', KwargInfo( 'dispatch', ( ContainerTypeInfo(list, (FeatureObject, list)), @@ -451,8 +454,8 @@ def fail_result(fail_reason: str, is_disabled: bool = False allow_unknown=True ) def multi_targets_method(self, state: 'ModuleState', - args: Tuple[str], kwargs: 'TYPE_kwargs' - ) -> TargetsObject: + args: Tuple[str], kwargs: 'TYPE_kwargs' + ) -> TargetsObject: config_name = args[0] sources = args[1] # type: ignore dispatch: List[Union[FeatureObject, List[FeatureObject]]] = ( @@ -467,7 +470,7 @@ def multi_targets_method(self, state: 'ModuleState', if not compiler: compiler = get_compiler(state) - baseline_features : Set[FeatureObject] = set() + baseline_features: Set[FeatureObject] = set() has_baseline = baseline is not None if has_baseline: baseline_features = FeatureObject.get_implicit_combine_multi(baseline) @@ -488,7 +491,7 @@ def multi_targets_method(self, state: 'ModuleState', ]] = [] for d in dispatch: if isinstance(d, FeatureObject): - target = {d,} + target = {d, } is_base_part = d in baseline_features else: target = set(d) @@ -647,7 +650,7 @@ def gen_config(self, state: 'ModuleState', config_name: str, c_detect = '1' dispatch_calls.append( f'{prefix}_MTARGETS_EXPAND(' - f'EXEC_CB({c_detect}, {test["target_name"]}, __VA_ARGS__)' + f'EXEC_CB({c_detect}, {test["target_name"]}, __VA_ARGS__)' ')' ) @@ -683,7 +686,8 @@ def gen_config(self, state: 'ModuleState', config_name: str, return config_path @typed_pos_args('features.sort', varargs=FeatureObject, min_varargs=1) - @typed_kwargs('features.sort', + @typed_kwargs( + 'features.sort', KwargInfo('reverse', bool, default = False), ) def sort_method(self, state: 'ModuleState', diff --git a/mesonbuild/modules/features/utils.py b/mesonbuild/modules/features/utils.py index 88eb19d82d92..21136e3cdc98 100644 --- a/mesonbuild/modules/features/utils.py +++ b/mesonbuild/modules/features/utils.py @@ -33,7 +33,6 @@ def test_code(state: 'ModuleState', compiler: 'Compiler', def generate_hash(*args: Any) -> str: hasher = hashlib.sha1() - test: List[bytes] = [] for a in args: hasher.update(bytes(str(a), encoding='utf-8')) return hasher.hexdigest() From 99d408fbafecf92e58b2fcefebba920705396bf4 Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Fri, 1 Aug 2025 10:43:44 +1000 Subject: [PATCH 624/624] update syntax for MKL get_option() --- mesonbuild/dependencies/blas_lapack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/blas_lapack.py b/mesonbuild/dependencies/blas_lapack.py index 7cc4e99c3008..efdedd2b5147 100644 --- a/mesonbuild/dependencies/blas_lapack.py +++ b/mesonbuild/dependencies/blas_lapack.py @@ -871,7 +871,7 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]) -> # available before the .pc file for SDL self.use_sdl = False - static_opt = kwargs.get('static', env.coredata.get_option(OptionKey('prefer_static'))) + static_opt = kwargs.get('static', env.coredata.optstore.get_value_for(OptionKey('prefer_static'))) libtype = 'static' if static_opt else 'dynamic' if self.use_sdl: