CMake BUILD_SHARED_LIBS: A Guide to Building Shared and Static Libraries


Purpose

  • Determines whether libraries are built as shared libraries (.so or .dll on Unix-like systems, .dll on Windows) or static libraries (.a on all platforms).
  • Controls the default behavior of the add_library() command in CMake.

How it Works

  • You can set BUILD_SHARED_LIBS at the beginning of your top-level CMakeLists.txt file using the option() command:
  • If BUILD_SHARED_LIBS is set to OFF, add_library() creates static libraries by default.
  • When BUILD_SHARED_LIBS is set to ON (the default), add_library() creates shared libraries by default if no explicit library type is specified.
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

This allows users to choose between shared and static libraries when building your project.

Key Points

  • Default Behavior
    With BUILD_SHARED_LIBS on, add_library() creates shared libraries unless you explicitly specify STATIC:
add_library(mylibrary mylibrary.cpp)  # Creates a shared library (default)
add_library(mystaticlib STATIC mystaticlib.cpp)  # Creates a static library
  • Overriding
    You can override the default behavior for individual libraries:
set(BUILD_SHARED_LIBS OFF)  # All libraries will be static by default now
add_library(mysharedlib SHARED mysharedlib.cpp)  # Explicitly create a shared library

Benefits of Shared Libraries

  • Dynamic Linking
    Programs link to shared libraries at runtime, allowing for updates without recompiling the entire program.
  • Code Reusability
    Shared libraries can be used by multiple programs, reducing code duplication and saving disk space.

Benefits of Static Libraries

  • No Dependencies
    Executables don't rely on the presence of shared libraries to run.
  • Smaller Executables
    Static libraries are embedded directly into the executable file, resulting in smaller executables.

Choosing Between Shared and Static Libraries

  • Consider factors like code reusability, dependency management, and deployment complexity when making a decision.
  • Building shared libraries might require additional configuration on some platforms.
  • Shared libraries often require setting linker flags to ensure proper linking.


Example 1: Building Shared and Static Libraries (User Choice)

This example allows users to choose between building shared and static libraries using the option() command:

option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

# Library with default behavior (shared if BUILD_SHARED_LIBS is ON)
add_library(mylibrary mylibrary.cpp)

# Library explicitly built as static
add_library(mystaticlib STATIC mystaticlib.cpp)

# Optionally build a shared library even if BUILD_SHARED_LIBS is OFF
if(NOT BUILD_SHARED_LIBS)
  set(BUILD_SHARED_LIBS ON CACHE INTERNAL "" FORCE)  # Force shared library
  add_library(forcesharedlib SHARED forcesharedlib.cpp)
  set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE)  # Restore original value
endif()

Example 2: Building Shared Library by Default

This example builds a shared library by default, overriding the default behavior for this specific library:

set(BUILD_SHARED_LIBS OFF)  # All libraries will be static by default

# Explicitly build a shared library
add_library(mysharedlib SHARED mysharedlib.cpp)

# Other libraries will be static unless explicitly specified as shared
add_library(myotherlib myotherlib.cpp)  # Static library (default)

Example 3: Conditional Compilation Based on Library Type

This example demonstrates how you can conditionally compile code based on whether a shared or static library is being built:

# Assuming BUILD_SHARED_LIBS is set
if(BUILD_SHARED_LIBS)
  add_definitions(-DPIC)  # Add -fPIC flag for shared library (Position-Independent Code)
endif()

add_library(mylibrary mylibrary.cpp)


Custom CMake Options

Instead of relying on the global BUILD_SHARED_LIBS variable, you can define custom options for each library target:

option(MYLIB_BUILD_SHARED "Build mylibrary as a shared library" ON)

add_library(mylibrary mylibrary.cpp)
if(MYLIB_BUILD_SHARED)
  set_target_properties(mylibrary PROPERTIES POSITION_INDEPENDENT_CODE ON)  # For shared libs
endif()

This approach offers more granular control for individual libraries and avoids potential conflicts with other projects using the same top-level CMakeLists.txt.

Preprocessor Macros

While not as common, you can use preprocessor macros like BUILD_SHARED or BUILD_STATIC defined during the build process. However, this requires modifying source code and can be less flexible compared to CMake options:

# Define macros based on build type (e.g., using CMAKE_BUILD_TYPE)

# In source code
#ifdef BUILD_SHARED
#  // Code specific to shared libraries
#endif
#ifdef BUILD_STATIC
#  // Code specific to static libraries
#endif

This method adds complexity to the code and might not be suitable for large projects.

  • Avoid using preprocessor macros unless absolutely necessary due to potential maintenance issues.
  • For complex projects with multiple libraries and requirements
    Custom CMake options provide better control.
  • For simple projects with clear shared/static preferences
    BUILD_SHARED_LIBS is often sufficient.