<LANG>_VISIBILITY_PRESET consistency problems with C++20 modules imported target
NOTE: The projects described below are presented in much more detail in the blog article C++20 Modules, CMake, And Shared Libraries.
Let's say you have a project with a shared library called Algo
, and that shared library exports a C++20 module as part of its public API. The Algo
target has CXX_VISIBILITY_PRESET
set to hidden
(that is the critical part for this issue), and it uses the generate_export_header()
command to provide the usual tooling to allow it to only export the specific entities it wants to expose to consumers of the shared library.
Now let's say Algo
gets the usual install logic and is included in an export set. The generated target definition when the export set is installed will end up setting some properties on the imported target that look something like this:
set_target_properties(Algo PROPERTIES
CXX_EXTENSIONS "FALSE"
IMPORTED_CXX_MODULES_COMPILE_FEATURES "cxx_std_20"
IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_COMPILE_FEATURES "cxx_std_20"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
Note how there's no mention of any CXX_VISIBILITY_PRESET
property here, which is business as usual.
Now let's suppose a separate project also defined a shared library Consumer
, and that shared library links to Algo
and uses the module from Algo
privately in its implementation. Let's say Consumer
is also trying to be a good citizen and is also setting CXX_VISIBILITY_PRESET
to hidden as a target property so it can control what symbols are exposed in its public API as well. This arrangement works with the Visual Studio toolchain, but fails with Clang 17 with an error like the following:
error: default visibility for functions and variables [-fvisibility] differs in PCH file vs. current file
error: module file CMakeFiles/Algo@synth_838524ceec9e.dir/163bc42a95ce.bmi cannot be loaded due to a configuration mismatch with the current compilation [-Wmodule-file-config-mismatch]
CMake generates a BMI on-the-fly in the consuming project for Algo
's module. From the above error, it looks like the call to the Clang compiler to create that BMI is using inconsistent visibility settings to what the Consumer
target uses, and Clang complains. In this scenario, I found that you can make the error go away by setting CXX_VISIBILITY_PRESET
to hidden
on the imported target for Algo
(i.e. add this property in the consuming project). That seems to result in using a consistent visibility setting when generating the BMI. This is not a great arrangement, you have a consumer modifying the imported target of something that should be fully defined by itself, but at least it's somewhat of a workaround for now.
The problem I want to capture here in this issue is that it is not obvious what a consuming project should do in this situation, and whether there's anything CMake can do to handle this case automatically. Ideally, CMake should identify that it needs to create a BMI with visibility settings that are compatible with the consumer, but I don't know if CMake generates one BMI for each consumer, or just one BMI across the whole project for the thing being consumed (Algo
). If it's the latter, then it's hard to see how CMake could generate compatible BMIs that work for all consumers unless you were lucky and they all used the same flags. And as I said earlier, it looks like only Clang is choking on this, so it probably is a toolchain-specific fix, if there is one at all.