Repository: mesonbuild/meson

Issue:
cannot use precompiled headers in static libraries when using clang-cl
**Describe the bug**
When using precompiled headers, clang-cl, and llvm-ar. builds fail with the error `llvm-ar.exe: error: unable to load '..\pch/stdafx.obj': file too small to be an archive`

**To Reproduce**
```py
# meson.build
project('temp', 'cpp',
  version : '0.1',
  default_options : [
    'warning_level=3',
    'cpp_std=c++latest',
  ]
)

cpp = meson.get_compiler('cpp')

static_library('example', 'main.cpp',
  include_directories : ['pch'],
  cpp_pch : ['pch/stdafx.hpp', 'pch/stdafx.cpp']
)
```
clang-cl.ini
```ini
[binaries]
c = 'clang-cl'
cpp = 'clang-cl'
ar = 'llvm-ar'
ld = 'lld-link'
strip = 'llvm-strip'

[constants]
common_args = [ '-fcolor-diagnostics' ]

[built-in options]
c_args = common_args
cpp_args = common_args
c_ld_args = common_args
cpp_ld_args = common_args
```
```cpp
// main.cpp
#include "stdafx.hpp"

int do_thing() {
    std::cout << "Hello, World!" << std::endl;
}
```
```cpp
// pch/stdafx.hpp
#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
```
```cpp
// pch/stdafx.cpp
#include "stdafx.hpp"
```
`meson setup build --native-file clang-cl.ini`
`ninja -C build`

**Expected behavior**
The build is successful

**system parameters**
* Windows 11 build 22621
* meson 1.3.2
* ninja 1.11.1
* python 3.11.8
* clang-cl 19.0.0git
* llvm-ar 19.0.0git




Discussion:
Your main.cpp looks off. That should be:
```cpp
#include <string>
#include <iostream>

int do_thing() {
    std::cout << "Hello, World!" << std::endl;
}
```
Does that fix the compilcation errors?

Also noe that modern versions of Meson don't need the precompiled .cpp file, it will generate one automatically if it's needed
neither removing the `stdafx.hpp` include, nor removing `stdafx.cpp` and using the autogenerated one fixed the issue. this appears to be because llvm-ar only accepts .a archives, and meson attempts to give it the pch .obj file without putting it into an archive first.

Edit: im not actually sure if meson should be giving lld-link the pch object file at all, the clang docs are vague about whether the pch object is required for linking or only compilation.
Tested locally and meson shouldn't be giving the .obj file to lld-link for the final link, will see if i can put a patch together

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -3121,7 +3121,10 @@ def generate_msvc_pch_command(self, target, compiler, pch):
         commands += self._generate_single_compile(target, compiler)
         commands += self.get_compile_debugfile_args(compiler, target, objname)
         dep = dst + '.' + compiler.get_depfile_suffix()
-        return commands, dep, dst, [objname], source
+
+        link_objects = [objname] if compiler.should_link_pch_object() else []
+
+        return commands, dep, dst, link_objects, source
 
     def generate_gcc_pch_command(self, target, compiler, pch):
         commands = self._generate_single_compile(target, compiler)
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -843,6 +843,9 @@ def get_colorout_args(self, colortype: str) -> T.List[str]:
     def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
         return []
 
+    def should_link_pch_object(self) -> bool:
+        return False
+
     def get_link_debugfile_name(self, targetfile: str) -> T.Optional[str]:
         return self.linker.get_debugfile_name(targetfile)
 
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -425,6 +425,10 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st
     def get_pch_base_name(self, header: str) -> str:
         return os.path.basename(header)
 
+    # MSVC requires linking to the generated object file when linking a build target
+    # that uses a precompiled header
+    def should_link_pch_object(self) -> bool:
+        return True
 
 class ClangClCompiler(VisualStudioLikeCompiler):
 


Test patch: