I am trying to use precompiled headers in my project, but I’m finding a strange issue. I put together a simple reproducer, where I have a header which I want to precompile when building a shared library (lib1) and then reuse when building another library (lib2). The compiler issues a warning when compiling the latter, informing that the PCH cannot be reused due to the -Dlib1_EXPORTS flag being missing.
According to my investigations the -Dlib1_EXPORTS compiler flag is automatically added by CMake when compiling lib1 and also the PCH, while for lib2 it just adds -Dlib2_EXPORTS. These different flags make the reusage of the PCH impossible.
I didn’t find any hint about the meaning of these flags, why they are added, and eventually how to remove them; I only saw that they are not added to executable targets, but this says not much to me. The documentation about target_precompile_headers does not prescribe any particular care to be taken to avoid this issue in the Reusing Precompile Headers section; it just says that the flags must be identical. It seems really strange to me that the PCH reusage machinery fails like this in this very simple case, so I must be doing something terribly wrong, but I cannot figure out what.
You are correct to say that this one is added automatically by CMake. It happens when the library is SHARED (so that one could do #ifdef SomeLibrary_EXPORTS in their sources).
From what I see in the documentation, you’d need to unset (or set to FALSE?) ENABLE_EXPORTS for the target(s) or CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS for the entire project (to affect all the targets).
Why does that happen, and what one should do in this case (as removing the *_EXPORTS compile definitions doesn’t seem to be the way to go) - that I don’t know, and I’d be curious to see what others have to say on the matter.
From what I understood from the ENABLE_EXPORTS documentation you linked, disabling exports for a shared library would prevent the possibility to link it to an executable, actually making it almost useless. If this is correct then I’d say this is not a solution that would work for me…
You’re not going to like this answer… While what you’re trying to do seems reasonable, it can’t get past the fundamental restriction that to re-use a PCH, all options have to be identical. Because you have shared libraries involved with symbol visibility enabled, you can’t satisfy that requirement. When building the shared library whose PCH you want to re-use, a compiler define has to be set to a certain value to make symbols get exported. For literally all other code using that shared library, that same compiler define has to have a different value, which is how all that other code imports rather than exports those symbols.
What does this mean for you? Well, if you’re building shared libraries, you won’t be able to re-use PCH between them. Static libraries, yes that would be fine, but not shared libraries. Or you could throw away all the goodness that symbol visibility gives you, which would remove the need for symbol exporting and importing, but please don’t do that!
So is the -Dlib1_EXPORTS needed by the compiler to export symbols? I’m not an expert of build tools, but I’m pretty sure I worked with makefile projects which produced working shared libraries also without this flag.
Eventually, just to give it a try: how do I manually remove the compiler flag? I checked the CMake documentation but I found no command to remove compiler flags, only to add.
As a thought experiment, when building the shared library, the compiler has to interpret the headers one way (because it has to build that code exporting the symbols from there). When building code that uses the shared library, it has to interpret the headers a different way (importing the symbols from there). A PCH can’t support both scenarios at once, so you can’t use a single PCH for both cases. The lib1_EXPORTS symbol is just the way you’re seeing that manifested in the build. You can probably come up with other ways to avoid the compiler definition and tell the compiler what to do another way, but the underlying limitation will still apply and you still won’t be able to use a single PCH for both scenarios.
Thanks for the explanation, but I still don’t understand. The lib1_EXPORTS flag is never explicitly used in my headers, and as far as I can see it’s not even in the involved automatically generated headers by CMake. Moreover, according to what I have been able to understand searching the web, a compiler flag with a name following the *_EXPORTS pattern is not a special GCC flag which might determine a special behavior. Plus, as I wrote above, I obtained working shared libraries in makefile projects without that flag, so I still don’t understand why it’s necessary for CMake (it’s present even when a not using PCHs so I guess it’s not strictly related to those).
I found a bit of info here about a possible meaning of this flag and it sort of matches what you write about the ability to import/export symbols from/to libraries, but it’s a Windows-specific trick which I’ve never seen under Linux, so from it I wouldn’t say that the flag is absolutely necessary under Linux (which is my target environment).
To prove that the flag is not strictly needed I manually tuned the reproducer linked in my first post. First, I compiled as usual with the VERBOSE=1 command line flag for make to print the compiler invocation commands, then I cleaned the build and issued all the commands manually removing the *_EXPORTS flag:
In this way the compiler does not complain about any missing flag when reusing the PCH, everything compiles and links fine, and I even added a simple main calling functions from the two libraries to be sure that absolutely everything works.
So, coming back to the begin of this long post, I still don’t understand…
Under Linux, by default all symbols are exported from a shared library. You can change this to symbols being hidden by default (through the flag -fvisibility=hidden, and see e.g. here for reasons why this is a good idea: Exported Symbols of Shared Libraries (GNU Gnulib) ). To selectively export symbols, you need to add a marker (__attribute__((__visibility__("default")))) to them.
Typically you do this with a macro - and this macro then adds the marker, but only if currently compiling the shared library itself (based on a define such as the _EXPORTS).
So if you are (as is done by default) just exposing all symbols (with all the downsides as mentioned in the article linked above, then yes, technically you don’t need the macro.
Windows actually has the opposite default behavior - there all symbols are hidden by default and must be explicitly marked as visible; so if you are developing cross-platform code, having the default behavior to be the same on all can actually also make things easier through consistency
Thanks for the additional insight. I’m not developing for cross-platform, just for Linux, and exposing the full API of my libraries is not an issue for me (it’s what I am doing currently and it creates no problem). So I’d really like to remove the _EXPORTS flag to be able to reuse PCHs across libraries and see if it brings any benefit to build time, how can this be done?
The thing you’re probably looking for is the DEFINE_SYMBOL target property. I’m not sure if setting it to an empty string will achieve what you want. The CMake code might treat that the same as unset, or it might lead to an invalid symbol name, etc.
If it turns out impossible to disable DEFINE_SYMBOL, it should be possible to work around this by setting it to the same thing for all of your projects which want to share PCHs. The set of defines would then be the same and the compiler shouldn’t complain.