CMake uniquifies flags. It also has no abstraction for forced inclusion. Why can you not just add the appropriate #include lines to the files in question?
Let’s get some related things out of the way first:
You should quote your generator expressions when they contain spaces or semicolons. See the following article for a detailed explanation of why (especially the Generator Expressions section): https://crascit.com/2022/01/25/quoting-in-cmake/
Prefer to use target_compile_options() rather than manipulating the COMPILE_OPTIONS target property directly.
Now to the core of your question. The COMPILE_OPTIONS target property is subject to compiler option de-duplication. Because you haven’t put quotes around your generator expression, CMake actually sees your generator expression as two separate arguments to the set_property() command. This results in a semicolon being used to separate the -include and csignal parts of what you tried to set. That in turn makes -include and csignal separately subject to de-duplication rather than the whole expression. Since -include will appear multiple times, CMake de-duplicates that and all but the first -include gets removed.
The SHELL: prefix is designed to handle this situation. It allows you to specify multiple options in a way that keeps them together. See the “Option De-duplication” section of the target_compile_options() docs. I think you’ll get the behavior you want if you replace your two set_property() calls with the following:
The actual use-case is a bit more complicated than the example. The code base uses a PCH, which GCC needs to have specified as -include. Additionally, in debug mode we override the error handling of a 3rd party library (Blaze) with a macro definition (this is the official way to change error handling behavior in Blaze). The goal is to raise a SIGTRAP before throwing an exception (catch throw in GDB isn’t super user-friendly in our use-case). This means we’ve effectively inserted a new include requirement into the 3rd-party header-only library. While we could try to maintain wrapper headers for every interface header that Blaze provides, this is a bit tedious and adds non-negligible overhead. Instead, having the Blaze CMake target specify -include csignal seems more maintainable. I don’t love it, so if you have alternative suggestions they would be appreciated!
I’ve forgotten why this CMake code ended up using set_property instead of target_compile_options, but I’ll see if our CI passes with target_compile_options instead. Looking at the Cake 3.12 documentation (the oldest we support), there doesn’t seem to be any reason to not use target_compile_options.
CI will have the final say in terms of any edge cases with compiler settings, but this seems to be working correctly in the 3 build variations I’ve tried locally. Thank you both so much! I and the rest of the team really appreciate it!
Why would target_compile_definitions(Blaze INTERFACE "$<$<CONFIG:Debug>:SYMBOL_YOU_NEED>") not be sufficient? Arguably, <csignal> can be included in Blaze itself if that is set.
The problem is that SIGTRAP is a preprocessor macro defined in <csignal> so I can’t forward declare that.
I think your second suggestion is that Blaze could include csignal for us. While true, since they don’t need it unless someone changes the error handling in this particular way, I’m not sure I’ll have a good argument for them to include the header in a “just in case” way.