4

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?

swineone
  • 2,296
  • 1
  • 18
  • 32
  • "Eventually, after an investigation that involved running CMake under a debugger"... this is clearly an issue to raise with upstream CMake. – Alex Reinking Aug 11 '21 at 06:32
  • Actually, up until that part of question, I believe what happened speaks more to my inexperience, or at most a lack of proper documentation or clearer error messages from CMake. Now if there are no better suggestions on how to handle the issues pointed out near the end of the question, I may consider filing a feature request. – swineone Aug 11 '21 at 06:46
  • This _is_ a good question, I just think it's one for the CMake developers rather than for the StackOverflow community – Alex Reinking Aug 11 '21 at 06:58
  • It boils down to "how should `REUSE_FROM` work between targets that depend on different sets of languages?" – Alex Reinking Aug 11 '21 at 06:59

1 Answers1

0

Late answer, but you could try disabling precompiled headers usage for the specific C files in your test project:

# inside the CMakeLists.txt of D_tests

set_source_files_properties(my_file.c PROPERTIES SKIP_PRECOMPILE_HEADERS ON)

I was able to make a similar error go away because I had an objective-C file inside a project trying to reuse precompiled headers from another that contains only C++ files.

Duduche
  • 53
  • 6