Skip to content

friendlyanon/cxx-static-shared-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CMake Example: support building as either shared or static

Continuous Integration

This example project shows you how to build a C++ project that can be built as static or shared, by using the corresponding CMake functionality.

The master branch uses CMake 3.14 as minimum. For older minimum requirements, see the cmake-3.12 and cmake-3.8 branches. Only the install(TARGETS) command is different in older versions of CMake, but for the purpose of being a proper educational example they are also included.

Anything older than CMake 3.8 is not practical to use.

How?

Below are what a developer has to take into account to successfully aid users of the library in making the decision on how to build the library. Users in this case can be other developers depending on the library or package maintainers who distribute your library.

BUILD_SHARED_LIBS

CMake provides a way to let the user choose the type of libraries to build if those do not specify their type explicitly. add_library will check for the value of BUILD_SHARED_LIBS when called without an type and honor its value.

This variable should never be set anywhere in your CMakeLists.txt files and there should be no "special" ways to achieve the same thing CMake provides by default.
Making package maintainers' life more difficult for no reason benefits noone.

add_library(your_target source/main.cpp)

SONAME and REAL LIBRARY

On Linux platforms, a shared library consists of a single REAL LIBRARY and symlinks to it, one of which is the SONAME. The REAL LIBRARY has a filename similar to libyour_target.so.1.2.3, where the full version number is embedded as a suffix. This does not affect runtime loading, the REAL LIBRARY is for humans.

The SONAME on the other hand is what the runtime loader will go looking for. The corresponding SONAME of the above shared library would be libyour_target.so.1, where 1 is the library's SOVERSION. Different libraries may choose different versioning strategies and it doesn't necessarily have to be related to the suffix in the REAL LIBRARY name.
In this example, the major version of the project will be assumed to mean SOVERSION as well.

set_target_properties(
    your_target PROPERTIES
    VERSION "${PROJECT_VERSION}"
    SOVERSION "${PROJECT_VERSION_MAJOR}"
)

Visibility

On the Windows platform, symbols must be explicitly exported from a DLL and must be marked as DLL imports in the header files in the consuming end.
This is the desired behavior on all platforms, however the defaults are wrong on other platforms like Linux.

To hide symbols on platforms, where the default is not hiding symbols, you must add some properties to your (possibly) shared targets:

set_target_properties(
    your_target PROPERTIES
    CXX_VISIBILITY_PRESET hidden
    VISIBILITY_INLINES_HIDDEN YES
)

Export macros

CMake supports generating cross-platform export macros via the GenerateExportHeader module. A library must annotate every symbol that the user is intended to or might be able to observe. Additionally, when the library is being built as a static library, then these macros should expand to nothing. This can be achieved by conditionally setting the YOUR_TARGET_STATIC_DEFINE definition.

include(GenerateExportHeader)

generate_export_header(
    your_target
    EXPORT_FILE_NAME include/your_project/your_target_export.h
)

if(NOT BUILD_SHARED_LIBS)
  target_compile_definitions(your_target PUBLIC YOUR_TARGET_STATIC_DEFINE)
endif()

target_include_directories(
    your_target ${your_project_warning_guard}
    PUBLIC
    "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
)
#include <your_project/your_target_export.h>

class YOUR_TARGET_EXPORT your_class {};

NAME LINK

On Linux, besides the SONAME, there is another symlink pointing to the REAL LIBRARY, which is the NAME LINK. While the SONAME and REAL LIBRARY are part of the runtime component, this symlink is part of the development component of a library, as it is used at build time for linking by dependent projects.

Depending on your CMake version, this is installed in different ways. Here it is for CMake 3.14+ using the default install locations from GNUInstallDirs:

include(GNUInstallDirs)

install(
    TARGETS your_target
    RUNTIME #
    COMPONENT your_project_Runtime
    LIBRARY #
    COMPONENT your_project_Runtime
    NAMELINK_COMPONENT your_project_Development
    ARCHIVE #
    COMPONENT your_project_Development
)

The big picture

The above points were focusing on single aspects that must be taken into account.
To see the big picture and how the pieces fit together, please take a look at the CMakeLists.txt file.

References

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published