From 17ddc93f8f32c7b3f0fccf3db10864a653d7bcdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Andr=C3=A9=20Reuter?= Date: Sat, 11 Apr 2026 12:19:54 +0200 Subject: [PATCH 1/6] enable 'runtest' and 'test_cmd' customization in MesonNinja easyblock --- easybuild/easyblocks/generic/mesonninja.py | 32 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index e31e4cc4397..34f49701fae 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -30,7 +30,7 @@ from easybuild.tools import LooseVersion from easybuild.framework.easyblock import EasyBlock -from easybuild.framework.easyconfig import CUSTOM +from easybuild.framework.easyconfig import CUSTOM, BUILD from easybuild.tools.build_log import EasyBuildError from easybuild.tools.filetools import change_dir, create_unused_dir, which from easybuild.tools.modules import get_software_version @@ -39,6 +39,7 @@ DEFAULT_CONFIGURE_CMD = 'meson' DEFAULT_BUILD_CMD = 'ninja' DEFAULT_INSTALL_CMD = 'ninja' +DEFAULT_TEST_CMD = 'meson' class MesonNinja(EasyBlock): @@ -60,6 +61,8 @@ def extra_options(extra_vars=None): 'configure_cmd': [DEFAULT_CONFIGURE_CMD, "Configure command to use", CUSTOM], 'install_cmd': [DEFAULT_INSTALL_CMD, "Install command to use", CUSTOM], 'separate_build_dir': [True, "Perform build in a separate directory", CUSTOM], + 'test_cmd': [DEFAULT_TEST_CMD, "Test command to use ('runtest' value is appended)", CUSTOM], + 'runtest': [None, "Meson target to test build or True to use 'meson test'", BUILD], }) return extra_vars @@ -149,11 +152,32 @@ def build_step(self, *args, **kwargs): def test_step(self): """ - Run tests using Ninja. + Run tests using Meson. """ - if self.cfg['runtest']: - cmd = "%s %s %s" % (self.cfg['pretestopts'], self.cfg['runtest'], self.cfg['testopts']) + test_cmd = self.cfg.get('test_cmd') or DEFAULT_TEST_CMD + runtest = self.cfg['runtest'] + if runtest or test_cmd != DEFAULT_TEST_CMD: + # Make run_test a string (empty if it is e.g. a boolean) + if not isinstance(runtest, str): + runtest = '' + # Run tests as recommended in https://mesonbuild.com/Unit-tests.html#testing-tool + if test_cmd == DEFAULT_TEST_CMD: + runtest = 'test' + + # Make sure Meson does not use more resources than we want. + # From the documentation: + # By default Meson uses as many concurrent processes as there are cores on the test machine. + if self.cfg.parallel >= 1 and 'meson' in test_cmd: + if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: + self.cfg['pretestopts'] += f'export MESON_TESTTHREADS={self.cfg.parallel} && ' + # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. + if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: + self.cfg['pretestopts'] += f'export MESON_NUM_PROCESSES={self.cfg.parallel} && ' + + # Compose command filtering out empty values + cmd = ' '.join([x for x in (self.cfg['pretestopts'], test_cmd, runtest, self.cfg['testopts']) if x]) res = run_shell_cmd(cmd) + return res.output def install_step(self): From f23608daeda6ccf22bf748831003ded626469c76 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 20 Jun 2026 14:22:42 +0200 Subject: [PATCH 2/6] minor code style fix in MesonNinja easyblock: fix order Co-authored-by: Kenneth Hoste --- easybuild/easyblocks/generic/mesonninja.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index 34f49701fae..5b6d0ac9c3c 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -30,7 +30,7 @@ from easybuild.tools import LooseVersion from easybuild.framework.easyblock import EasyBlock -from easybuild.framework.easyconfig import CUSTOM, BUILD +from easybuild.framework.easyconfig import BUILD, CUSTOM from easybuild.tools.build_log import EasyBuildError from easybuild.tools.filetools import change_dir, create_unused_dir, which from easybuild.tools.modules import get_software_version @@ -38,8 +38,8 @@ DEFAULT_CONFIGURE_CMD = 'meson' DEFAULT_BUILD_CMD = 'ninja' -DEFAULT_INSTALL_CMD = 'ninja' DEFAULT_TEST_CMD = 'meson' +DEFAULT_INSTALL_CMD = 'ninja' class MesonNinja(EasyBlock): @@ -60,9 +60,9 @@ def extra_options(extra_vars=None): "This disabled costly asserts in code, typical for production.", CUSTOM], 'configure_cmd': [DEFAULT_CONFIGURE_CMD, "Configure command to use", CUSTOM], 'install_cmd': [DEFAULT_INSTALL_CMD, "Install command to use", CUSTOM], + 'runtest': [None, "Meson target to test build, or True to use 'meson test'", BUILD], 'separate_build_dir': [True, "Perform build in a separate directory", CUSTOM], 'test_cmd': [DEFAULT_TEST_CMD, "Test command to use ('runtest' value is appended)", CUSTOM], - 'runtest': [None, "Meson target to test build or True to use 'meson test'", BUILD], }) return extra_vars From 4b354241e8a5939dfaf3fb2b63d58140909148c9 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 20 Jun 2026 14:23:21 +0200 Subject: [PATCH 3/6] add leading space when appending to pretestopts Co-authored-by: Kenneth Hoste --- easybuild/easyblocks/generic/mesonninja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index 5b6d0ac9c3c..49eef44b4cb 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -169,10 +169,10 @@ def test_step(self): # By default Meson uses as many concurrent processes as there are cores on the test machine. if self.cfg.parallel >= 1 and 'meson' in test_cmd: if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: - self.cfg['pretestopts'] += f'export MESON_TESTTHREADS={self.cfg.parallel} && ' + self.cfg['pretestopts'] += f" export MESON_TESTTHREADS={self.cfg.parallel} && " # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: - self.cfg['pretestopts'] += f'export MESON_NUM_PROCESSES={self.cfg.parallel} && ' + self.cfg['pretestopts'] += f" export MESON_NUM_PROCESSES={self.cfg.parallel} && " # Compose command filtering out empty values cmd = ' '.join([x for x in (self.cfg['pretestopts'], test_cmd, runtest, self.cfg['testopts']) if x]) From 7e3d185888226f60e29b2d6c530a8ceadac85bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Andr=C3=A9=20Reuter?= Date: Sat, 20 Jun 2026 15:14:16 +0200 Subject: [PATCH 4/6] Use `update` instead of `+=` to append options Co-authored-by: Kenneth Hoste --- easybuild/easyblocks/generic/mesonninja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index 49eef44b4cb..1ddde5ba262 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -169,10 +169,10 @@ def test_step(self): # By default Meson uses as many concurrent processes as there are cores on the test machine. if self.cfg.parallel >= 1 and 'meson' in test_cmd: if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: - self.cfg['pretestopts'] += f" export MESON_TESTTHREADS={self.cfg.parallel} && " + self.cfg.update('pretestopts', f" export MESON_TESTTHREADS={self.cfg.parallel} && ") # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: - self.cfg['pretestopts'] += f" export MESON_NUM_PROCESSES={self.cfg.parallel} && " + self.cfg.update('pretestopts', f" export MESON_NUM_PROCESSES={self.cfg.parallel} && ") # Compose command filtering out empty values cmd = ' '.join([x for x in (self.cfg['pretestopts'], test_cmd, runtest, self.cfg['testopts']) if x]) From 0abc2da0f3524b6a625be42702530b9377cf9031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Andr=C3=A9=20Reuter?= Date: Sat, 20 Jun 2026 15:33:00 +0200 Subject: [PATCH 5/6] ensure that old easyconfigs can still run with mesonninja changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan André Reuter --- easybuild/easyblocks/generic/mesonninja.py | 40 ++++++++++++++-------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index 1ddde5ba262..547488f0631 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -154,25 +154,32 @@ def test_step(self): """ Run tests using Meson. """ - test_cmd = self.cfg.get('test_cmd') or DEFAULT_TEST_CMD runtest = self.cfg['runtest'] - if runtest or test_cmd != DEFAULT_TEST_CMD: - # Make run_test a string (empty if it is e.g. a boolean) - if not isinstance(runtest, str): - runtest = '' + + if runtest: + # Essentially runtest = True. Use the default behavior. + if isinstance(runtest, bool): + test_cmd = self.cfg.get('test_cmd') or DEFAULT_TEST_CMD + # Run tests as recommended in https://mesonbuild.com/Unit-tests.html#testing-tool if test_cmd == DEFAULT_TEST_CMD: runtest = 'test' - - # Make sure Meson does not use more resources than we want. - # From the documentation: - # By default Meson uses as many concurrent processes as there are cores on the test machine. - if self.cfg.parallel >= 1 and 'meson' in test_cmd: - if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: - self.cfg.update('pretestopts', f" export MESON_TESTTHREADS={self.cfg.parallel} && ") - # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. - if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: - self.cfg.update('pretestopts', f" export MESON_NUM_PROCESSES={self.cfg.parallel} && ") + # User has defined the test_cmd. Adding test by default here might cause trouble, hence omit. + else: + runtest = '' + + # Make sure Meson does not use more resources than we want. + # From the documentation: + # By default Meson uses as many concurrent processes as there are cores on the test machine. + if self.cfg.parallel >= 1 and 'meson' in test_cmd: + if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: + self.cfg.update('pretestopts', f" export MESON_TESTTHREADS={self.cfg.parallel} && ") + # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. + if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: + self.cfg.update('pretestopts', f" export MESON_NUM_PROCESSES={self.cfg.parallel} && ") + # EasyConfig defined the test command to be executed via runtest + else: + test_cmd = '' # Compose command filtering out empty values cmd = ' '.join([x for x in (self.cfg['pretestopts'], test_cmd, runtest, self.cfg['testopts']) if x]) @@ -180,6 +187,9 @@ def test_step(self): return res.output + # No test executed, hence no output to return + return '' + def install_step(self): """ Install with 'ninja install'. From a2e6975a0078ecc5146a24e22eb5aa604dfd0dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Andr=C3=A9=20Reuter?= Date: Sat, 20 Jun 2026 18:34:25 +0200 Subject: [PATCH 6/6] always apply env vars for limiting parallelism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan André Reuter --- easybuild/easyblocks/generic/mesonninja.py | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/easybuild/easyblocks/generic/mesonninja.py b/easybuild/easyblocks/generic/mesonninja.py index 547488f0631..0c178139c65 100644 --- a/easybuild/easyblocks/generic/mesonninja.py +++ b/easybuild/easyblocks/generic/mesonninja.py @@ -167,22 +167,23 @@ def test_step(self): # User has defined the test_cmd. Adding test by default here might cause trouble, hence omit. else: runtest = '' - - # Make sure Meson does not use more resources than we want. - # From the documentation: - # By default Meson uses as many concurrent processes as there are cores on the test machine. - if self.cfg.parallel >= 1 and 'meson' in test_cmd: - if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: - self.cfg.update('pretestopts', f" export MESON_TESTTHREADS={self.cfg.parallel} && ") - # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. - if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: - self.cfg.update('pretestopts', f" export MESON_NUM_PROCESSES={self.cfg.parallel} && ") # EasyConfig defined the test command to be executed via runtest else: test_cmd = '' + full_test_cmd = ' '.join([x for x in (test_cmd, runtest, self.cfg['testopts']) if x]) + # Make sure Meson does not use more resources than we want. + # From the documentation: + # By default Meson uses as many concurrent processes as there are cores on the test machine. + if self.cfg.parallel >= 1 and 'meson' in full_test_cmd: + if 'MESON_TESTTHREADS' not in self.cfg['pretestopts']: + self.cfg.update('pretestopts', f" export MESON_TESTTHREADS={self.cfg.parallel} && ") + # Preferred way to set parallelism since Meson v1.7.0, but does not hurt to set both. + if 'MESON_NUM_PROCESSES' not in self.cfg['pretestopts']: + self.cfg.update('pretestopts', f" export MESON_NUM_PROCESSES={self.cfg.parallel} && ") + # Compose command filtering out empty values - cmd = ' '.join([x for x in (self.cfg['pretestopts'], test_cmd, runtest, self.cfg['testopts']) if x]) + cmd = ' '.join([x for x in (self.cfg['pretestopts'], full_test_cmd) if x]) res = run_shell_cmd(cmd) return res.output