From 183b6ab6d62c7ba1c5bbea33261ace24d1c297d4 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Mon, 24 Aug 2026 20:48:09 +0000 Subject: [PATCH] Stop passing link arguments to compile-only sanity checks There is no need to pass link-only arguments where we are only compiling. This has led to real world issues: Clang will emit a warning when unused arguments are passed on the command line: clang: warning: argument unused during compilation: '-L/tmp/opencode' [-Wunused-command-line-argument] This is a bit of a hard warning to trigger. The following is required: 1. Clang or Clang-derived compiler (clang-cl) 2. A cross build 3. No exe wrapper configured 4. c_link_args is non-empty When (2) and (3) hold, the compile mode becomes CompileCheckMode.COMPILE, which means we pass -c to clang's sanity check command. When -c is passed to clang, it ignores linker arguments like -L. Signed-off-by: Tristan Partin --- mesonbuild/compilers/mixins/clike.py | 11 ++++++++++- unittests/internaltests.py | 21 ++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index c7b37b667e6b..a60a8dfeea5c 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -282,8 +282,17 @@ def _sanity_check_compile_args(self, sourcename: str, binname: str # Cross-compiling is hard. For example, you might need -nostdlib, or to pass --target, etc. mode = CompileCheckMode.COMPILE if self.is_cross and not self.environment.has_exe_wrapper() else CompileCheckMode.LINK cargs, b_largs = self._get_basic_compiler_args(mode) - largs = self.linker_to_compiler_args(b_largs) s_args, s_largs = super()._sanity_check_compile_args(sourcename, binname) + if mode is CompileCheckMode.COMPILE: + # We aren't linking in this invocation (and can't run the result + # without an exe wrapper anyway), so don't pass any link-only + # arguments to a compile-only command. Some compilers add fixed + # flags (e.g. MSVC-style compilers always prepend /link) even + # when there is nothing to link, which would otherwise end up + # unused/misplaced in a compile-only invocation. + return s_args + cargs, [] + + largs = self.linker_to_compiler_args(b_largs) return s_args + cargs, s_largs + largs def check_header(self, hname: str, prefix: str, *, diff --git a/unittests/internaltests.py b/unittests/internaltests.py index 1237885fe279..86eb5ee22529 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -30,7 +30,7 @@ import mesonbuild.scripts.env2mfile from mesonbuild import coredata from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler -from mesonbuild.compilers.compilers import ManyInOneLinkerOptionStyle +from mesonbuild.compilers.compilers import Compiler, ManyInOneLinkerOptionStyle from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.d import DmdDCompiler from mesonbuild.compilers.detect import detect_c_compiler @@ -416,6 +416,25 @@ def test_compiler_args_class_visualstudio(self): self.assertEqual(a.to_native(copy=True), ['/showIncludes']) + def test_clike_sanity_check_drops_link_only_args_when_compile_only(self): + # When cross-compiling without an exe wrapper, CLikeCompiler's sanity + # check only compiles and never links because we can't run the + # executable. Therefore, it doesn't make sense for link-only arguments + # to be added on the command line. Some compilers warn about unused + # command line arguments. + env = get_fake_env() + linker = linkers.MoldDynamicLinker([], env, MachineChoice.HOST, '-Wl,', []) + cc = ClangCCompiler([], [], '14.0.0', MachineChoice.HOST, env, linker=linker) + cc.is_cross = True + + with mock.patch.object(env, 'has_exe_wrapper', return_value=False), \ + mock.patch.object(cc, '_get_basic_compiler_args', return_value=([], ['-fake-cross-link-arg'])), \ + mock.patch.object(Compiler, '_sanity_check_compile_args', return_value=([], ['-Lfake-ldflags-arg'])): + _, largs = cc._sanity_check_compile_args('foo.c', 'foo.exe') + + self.assertEqual(largs, []) + + def test_msvc_unix_args_to_native(self): # joined self.assertEqual(MSVCCompiler.unix_args_to_native(['-isystemfoo']), ['/Ifoo'])