Hi
While setting up a Windows/MSVC buildfarm animal with NLS enabled, the
meson 'tmp_install' and 'initdb_cache' setup tests failed immediately
with exit status 1 and no stdout/stderr at all.
With a little help from Claude, I (eventually) found this diagnosis and
solution:
The root cause turned out to be in meson itself, not in our meson.build.
On Windows, determine_windows_extra_paths() in
mesonbuild/backend/backends.py builds a test's PATH by walking every
target passed via that test's 'depends:' kwarg (extra_bdeps) and adding
each one's build directory unconditionally, with no check for whether
the target actually produces a DLL. That's fine when 'depends:' lists a
handful of real link dependencies, but the 'tmp_install' test depends on
installed_targets, which includes nls_mo_targets - one custom_target per
locale/domain of compiled .mo catalogs. With NLS enabled that's several
hundred targets, none of them DLLs, none of them ever looked up via
PATH, and each one still gets its own entry.
In our case this inflated PATH to ~39000 characters across 584 entries
(448 of them po/*/LC_MESSAGES directories), comfortably past practical
Windows environment-variable/command-line length limits, which is why
the test failed silently - the failure happens before the child process
gets a chance to produce any output.
I think this is arguably a meson bug (determine_windows_extra_paths()
should filter extra_bdeps the same way it already filters a test
executable's own link dependencies), but regardless of whether that
gets fixed upstream, we can sidestep it on our end cheaply: depend on a
trivial stamp custom_target instead of installed_targets directly. Its
own 'depends:' still forces installed_targets to build first, so build
ordering is unaffected, but since a custom_target is not a
build.BuildTarget, meson doesn't recurse into its dependencies when
computing the test PATH - it contributes at most one harmless directory
instead of hundreds.
I initially tried wrapping installed_targets in an alias_target()
instead, which would avoid the recursion the same way, but test()'s
'depends:' kwarg is typechecked to only accept
BuildTarget | CustomTarget | CustomTargetIndex and rejects AliasTarget
outright:
meson.build:NNNN:0: ERROR: test keyword argument 'depends' was of
type array[AliasTarget] but should have been type
array[BuildTarget | CustomTarget | CustomTargetIndex]
The attached patch uses a custom_target instead, which satisfies that
type check.
Tested on Windows/MSVC (meson 1.11.1), building with -Dnls=enabled:
before: tmp_install test PATH = 39193 chars, 584 entries, 448
LC_MESSAGES - tmp_install and initdb_cache setup tests FAIL
(exit status 1, no output)
after: tmp_install test PATH = 2189 chars, 43 entries, 0
LC_MESSAGES - all three setup tests (tmp_install,
install_test_files, initdb_cache) OK
I did not attempt to fix the same class of problem for any other test
in the tree - this patch only touches the one setup test that was
actually failing for us. If there's interest, the same technique could
presumably be applied wherever else a test's 'depends:' pulls in a
large target list.
From cdc4f8a5f5d880dfd1cefd639ce35a353e908e80 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email protected]>
Date: Mon, 13 Jul 2026 10:50:54 -0400
Subject: [PATCH] meson: Avoid PATH bloat from NLS .mo targets in tmp_install
test setup
On Windows, meson's test() 'depends:' kwarg feeds every listed target
unconditionally into determine_windows_extra_paths()
(mesonbuild/backend/backends.py), which adds each one's build directory
to the test's PATH so that any DLL it needs can be found at runtime
(Windows has no rpath equivalent). This isn't limited to targets that
actually produce a DLL - any target reachable through 'depends:' gets a
PATH entry, whether or not it's relevant to library loading.
The 'tmp_install' test setup depends on installed_targets, which
includes nls_mo_targets: one custom_target per locale/domain of compiled
.mo message catalogs. With NLS enabled, that's several hundred targets,
none of which are DLLs and none of which are ever looked up via PATH.
Each one still gets its own PATH entry, and the aggregate is large
enough (in local testing, around 39000 characters across 584 entries,
448 of them po/*/LC_MESSAGES directories) to exceed practical Windows
environment-variable/command-line length limits. The result is that the
'tmp_install' and 'initdb_cache' setup tests fail immediately with exit
status 1 and no diagnostic output at all, since the failure happens
before the child process can produce anything on stdout/stderr.
Fix this by depending on a trivial stamp custom_target instead of
installed_targets directly. Its own 'depends:' still forces
installed_targets to build before it runs, preserving correct ordering,
but because a custom_target is not a build.BuildTarget,
determine_windows_extra_paths() does not recurse into its dependencies
- it contributes at most its own single, harmless output directory to
PATH instead of hundreds of real ones.
alias_target() would achieve the same non-recursion (the function only
expands prospectives that are build.BuildTarget instances, and
AliasTarget isn't one), but test()'s 'depends:' kwarg is typechecked to
accept only BuildTarget | CustomTarget | CustomTargetIndex, and rejects
AliasTarget outright.
---
meson.build | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index d88a7a7..659ee2a 100644
--- a/meson.build
+++ b/meson.build
@@ -3865,13 +3865,26 @@ meson_install_args = meson_args + ['install'] + {
# setup tests should be run first,
# so define priority for these
setup_tests_priority = 100
+
+# Depend on a single stamp custom_target rather than the raw
+# installed_targets list directly, so that on Windows this test's PATH
+# doesn't get one entry per target in installed_targets (including the
+# hundreds of per-locale NLS .mo directories). This still forces
+# installed_targets to build first.
+installed_targets_stamp = custom_target('installed-targets-stamp',
+ output: 'installed-targets-stamp',
+ command: [python, '-c', 'pass'],
+ depends: installed_targets,
+ build_always_stale: true,
+)
+
test('tmp_install',
meson_bin, args: meson_install_args ,
env: {'DESTDIR':test_install_destdir},
priority: setup_tests_priority,
timeout: 300,
is_parallel: false,
- depends: installed_targets,
+ depends: installed_targets_stamp,
suite: ['setup'])
test('install_test_files',
--
2.54.0.windows.1