It is good practice to hide internal symbols when building a shared library. This means that the library shrinks in size, because what you expose to the user is less than what you have in the library. This defines the Application Binary Interface (ABI), which most of the time should coincide with the Application Programming Interface (API). This is done in two stages:
- We use the appropriate compiler flags.
- We mark symbols to be exported with a preprocessor variable (message_EXPORT, in our example). When compiling, the hiding will be lifted for these symbols (such as classes and functions).
Static libraries are just archives of object files. Thus one compiles sources into object files and then the archiver bundles them into an archive. There is no notion of ABI: all symbols are visible by default and the visibility flags for the compiler do not affect static archiving. However, if you are going to build a shared and static library from the same source files, you need a way to give meaning to the message_EXPORT preprocessor variable that now appears in the code in both cases. This is where the GenerateExportHeader.cmake module comes in. It will define a header with all the logic for giving the proper definition of this preprocessor variable. For shared libraries, it will be what is needed by the given combination of platform and compiler. Note that the meaning will also change based on whether we are building or using the shared library. Fortunately, CMake takes care of this for us without further intervention. For static libraries, it will expand to an empty string doing what we expect: nothing.
The attentive reader will have noticed that building the static and shared libraries as shown here will actually require to compile the sources twice. This was not an expensive operation for our simple example, but it can clearly become quite onerous, even for projects that are only slightly bigger than our example. Why did we choose this approach over the one using OBJECT libraries shown in Recipe 3, Building and linking static and shared libraries, in Chapter 1, From a Simple Executable to Libraries? OBJECT libraries take care of the first step in compiling the library: from sources to object files. In that step, the preprocessor intervenes and will evaluate message_EXPORT. Since the compilation of OBJECT libraries happens once, message_EXPORT is either evaluated to a value compatible with building the shared or the static library. Thus to avoid ambiguities, we chose the more robust approach of compiling twice, letting the preprocessor evaluate the visibility variable correctly.