Environment
cmake version 3.21.1 running on macOS 10.15.7, clang version string Apple clang version 12.0.0 (clang-1200.0.32.29)
.
Introduction
I have a project for a library written in C, however its unit tests are written in C++ using Google Test. The library implements different algorithms, with one target for each different algorithm. For each target, say A
, there is a corresponding A_tests
target. Say there are 5 targets, A
through E
.
Due to ever-increasing build times, I'm trying to add Google Test's "gtest/gtest.h"
header as a precompiled header, evidently only for C++. To avoid repeatedly recompiling the same header, I added the following entry to one of my targets, say A_tests
:
target_precompiled_headers(A_tests PRIVATE [["gtest/gtest.h"]])
Note that A_tests
is composed entirely of C++ files.
For each of the other targets (X
= B
, C
, D
, E
), I added the following:
target_precompiled_headers(X_tests REUSE_FROM A_tests)
The issue
Now this works fine for, say, X
= B
and C
, which are also pure C++ targets. However, D_tests
has a C file in it in addition to the various C++ files. When configuring the project with CMake, I get the following error:
CMake Error in CMakeLists.txt:
Unable to resolve full path of PCH-header
'/Users/.../my-lib/build/CMakeFiles/A_tests.dir/cmake_pch.h'
assigned to target D_tests, although its path is supposed to be
known!
Indeed, at my-lib/build/CMakeFiles/A_tests.dir
, there is a cmake_pch.hxx
file but not a cmake_pch.h
file.
Root cause
Eventually, after an investigation that involved running CMake under a debugger, I found out it had to with the presence of a C file in D_tests
, along with the lack of C files in A_tests
. (Note: the PCH must be compiled inside A_tests
, since A
is the only mandatory target in the library -- B
through E
may all be disabled through CMake options.)
Attempts to fix
My first attempt was to add a dummy C file to A_tests
to ensure that a C PCH is created as well. Although this ensures the error goes away, this is the content of the cmake_pch.h
(note this is the C version of the file, as opposed to the separate C++ version which is cmake_pch.hxx
):
/* generated by CMake */
#pragma clang system_header
#include "gtest/gtest.h"
I can't imagine any good things will come out of force-including a C++ header in C files (even if that's not an error, it will at the very least slow down the compilation by including the PCH in files where it makes no sense to do so).
After some more experimentation, I got an acceptable result by changing the target_precompiled_headers()
entry in A_tests
to the following, using generator expressions:
target_precompile_headers(A_tests PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:\"gtest/gtest.h\">"
"$<$<COMPILE_LANGUAGE:C>:<stddef.h$<ANGLE-R>>")
In principle this solution is acceptable -- having a C PCH with stddef.h
is not really a problem, since it's a small and harmless header, and moreover there are very few C files in the X_tests
targets and anyway C compilation is blazingly-fast.
However, I'm still bothered by the fact that I must add some C header to prevent an error. I even tried changing the relevant part of the statement above to "$<$<COMPILE_LANGUAGE:C>:>"
, but then I get a different error: target_precompile_headers called with invalid arguments
.
The question
Can I modify my script to communicate to CMake that, for target D_tests
, only a C++ PCH should be used, even though there are C files in that target?
Failing that, is it possible to create an empty C PCH, say by a suitable modification of the generator expression above?