Skip to content

determine_windows_extra_paths() can add too many paths #16010

Description

@adunstan

*Describe the bug

On Windows, determine_windows_extra_paths()
(mesonbuild/backend/backends.py) is used to build the PATH a test()
runs with, so DLLs can be found at runtime (there's no rpath equivalent).
For the test's own executable, it correctly walks only the targets
relevant to running it, via get_all_link_deps()
(now get_runtime_dependencies() per #12059).

However, anything passed through a test's depends: kwarg is added
unconditionally, with no equivalent filtering:

# create_test_serialisation(), backends.py
extra_bdeps: T.List[build.BuildTargetTypes] = []
if isinstance(exe, build.CustomTarget):
    extra_bdeps = list(exe.get_transitive_build_target_deps())
extra_bdeps.extend(t.depends)
extra_bdeps.extend(a for a in t.cmd_args if isinstance(a, build.BuildTarget))
extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps)
# determine_windows_extra_paths(), backends.py
for bdep in extra_bdeps:
    prospectives.add(bdep)  # <- unconditional, regardless of target type
    if isinstance(bdep, build.BuildTarget):
        prospectives.update(bdep.get_all_link_deps())
...
for ld in prospectives:
    dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld))
    internal_deps.add(dirseg)

Every target reachable through depends: gets its build directory added
to PATH, whether or not it's a shared library, a static library, or an
arbitrary custom_target with nothing to do with DLL loading at all.

To Reproduce

Minimal project, verified against meson 1.11.1 (Windows, MSVC, ninja
backend). Each dummy target needs to live in its own subdirectory - one
meson.build per target, all in a single directory, collapses to a
single (shared) build directory and won't reproduce the bug:

meson.build:

project('extra-bdeps-path-repro', 'c')

py = import('python').find_installation()

# stand-ins for "a great many custom_targets that are dependencies for
# ordering purposes only, and are not DLLs" - e.g. postgres's nls_mo_targets
# (one custom_target per locale/domain of compiled gettext .mo catalogs,
# each in its own po/<locale>/ subdirectory)
irrelevant_targets = []
foreach i : range(50)
  subdir('sub' + i.to_string())
endforeach

exe = executable('repro', 'main.c')

test('repro-test', exe, depends: irrelevant_targets)

sub<N>/meson.build (one such subdirectory per N in 0..49):

irrelevant_targets += custom_target('irrelevant-@0@'.format(N),
  output: 'irrelevant-@0@.stamp'.format(N),
  command: [py, '-c', 'pass'],
  build_always_stale: true,
)

main.c:

int main(void) { return 0; }

Run meson setup build . && ninja -C build && meson test -C build -v,
or inspect build/meson-logs/testlog.txt for the repro-test entry's
PATH. Confirmed result: 50 of the 87 total PATH entries are exactly
the 50 sub<N> custom_target build directories, despite none of them
producing anything DLL-related.

Expected behavior

Targets passed via a test's depends: that don't produce a shared
library (or otherwise link dynamically) shouldn't contribute a PATH
entry, matching the filtering determine_windows_extra_paths() already
applies to the test executable's own dependencies.

Real-world impact

This isn't just theoretical - it's currently breaking PostgreSQL's own
meson.build on Windows. Its tmp_install test setup depends on
installed_targets, which includes one custom_target per
locale/domain of compiled gettext .mo catalogs
(nls_mo_targets). With NLS enabled, that's several hundred targets. In
local testing this inflated the test's PATH to ~39,000 characters
across 584 entries (448 of them po/*/LC_MESSAGES directories),
comfortably past practical Windows environment-variable/command-line
length limits - the test then fails immediately with exit status 1 and
no stdout/stderr at all, since the failure happens before the child
process can produce any output.

PostgreSQL can work around this on its own end (depend on a stamp
custom_target instead of the raw list, so determine_windows_extra_paths()
doesn't recurse into it), but the underlying gap in extra_bdeps handling
would presumably bite any project with a similar "many non-DLL
custom_targets as an ordering dependency" shape.

Additional context

Related: #12059 (get_all_link_deps() -> get_runtime_dependencies())
tightened the filtering for the primary test target's own dependencies
(excluding static libraries, following link_whole, etc.), but that work
only touches the target argument to determine_windows_extra_paths(),
not the extra_bdeps path fed from t.depends - the gap described here
appears to remain.

system parameters

  • meson version: 1.11.1
  • OS: Windows 11
  • compiler: MSVC (Visual Studio 2026/18 Build Tools)
  • backend: ninja

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions