CMake Mastery (5): Comprehensive Guide to add_library – Static and Dynamic Libraries

Master the Core Logic of Library Building from Compilation Principles to Industrial Practices

1. Essential Differences Between Static and Dynamic Libraries

Core Feature Comparison

Feature Static Library (<span>.a</span>/<span>.lib</span>) Dynamic Library (<span>.so</span>/<span>.dll</span>)
Linking Method Compiled into the executable file at build time Loaded dynamically at runtime
Memory Usage Multiple loads of the same library will consume memory repeatedly Loaded only once in memory, shared usage
Update Maintenance Requires recompilation of the main program Replacing the library file takes effect
Applicable Scenarios Small toolchains, embedded development Large frameworks, plugin systems

1.2 Dependency Management

  • Static Library:
    • All dependencies are directly embedded in the final executable file.
    • Advantages: Simple deployment (no external dependencies).
    • Disadvantages: Large executable file size, requires full recompilation for updates.
  • Dynamic Library:
    • Dependencies are resolved at runtime.
    • Advantages: Modular updates, saves memory.
    • Disadvantages: Must ensure correct runtime paths (<span>LD_LIBRARY_PATH</span> or <span>RPATH</span>).

2. Detailed Explanation of Core Function: <span>add_library()</span>

2.1 Basic Syntax and Parameters

add_library(&lt;name&gt; [STATIC | SHARED | OBJECT]  
            [EXCLUDE_FROM_ALL]  
            [source1] [source2 ...])  
  • Type Parameter:
    • <span>STATIC</span>: Static library (default type).
    • <span>SHARED</span>: Dynamic library (must enable<span>-fPIC</span>).
    • <span>OBJECT</span>: Collection of intermediate object files (speeds up compilation).
  • Key Options:
    • <span>EXCLUDE_FROM_ALL</span>: Does not participate in the default build (needs manual compilation).

2.2 Practical Example: Building a Mathematical Static Library and a Network Dynamic Library

# Static Library Example  
add_library(math_utils STATIC  
    src/matrix.cpp  
    src/vector.cpp  
)  

# Dynamic Library Example (requires -fPIC)  
add_library(network SHARED  
    src/socket.cpp  
    src/http.cpp  
)  

# Enable position-independent code (required for dynamic libraries)  
set_target_properties(network PROPERTIES  
    POSITION_INDEPENDENT_CODE ON  
)  

3. Symbol Hiding and Position Independent Code (fPIC)

3.1 Symbol Hiding

  • Problem: By default, dynamic libraries export all symbols (functions/variables), leading to naming conflicts.
  • Solution:
    • Compiler Option: <span>-fvisibility=hidden</span> (GCC/Clang).
    • Code Marking: Explicitly declare exported symbols.
// Export symbol  
__attribute__((visibility("default"))) void public_api();  
// Hide symbol (default)  
void internal_impl();  
    • CMake Configuration:
set(CMAKE_CXX_VISIBILITY_PRESET hidden)  # Globally hide symbols  
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Hide inline functions  

3.2 Position Independent Code (-fPIC)

  • Function: Generates code that is independent of memory location, required for dynamic libraries.
  • CMake Configuration:
# Globally enable (recommended)  
set(CMAKE_POSITION_INDEPENDENT_CODE ON)  

# Or for specific targets  
set_target_properties(network PROPERTIES  
  POSITION_INDEPENDENT_CODE ON  
)  

4. Compilation Parameters and Optimization Techniques

4.1 Common Compilation Options

Parameter Function Example
<span>-Wall -Wextra</span> Enable all warnings <span>target_compile_options(<target> PRIVATE -Wall -Wextra)</span>
<span>-O3</span> Highest optimization level <span>target_compile_options(<target> PRIVATE -O3)</span>
<span>-g</span> Generate debug symbols <span>target_compile_options(<target> PRIVATE -g)</span>
<span>-march=native</span> Optimize for local CPU <span>target_compile_options(<target> PRIVATE -march=native)</span>

4.2 Separate Build Type Configuration

# Debug mode configuration  
target_compile_options(math_utils PRIVATE  
    $&lt;$&lt;CONFIG:Debug&gt;:-O0 -g3&gt;  
)  

# Release mode configuration  
target_compile_options(math_utils PRIVATE  
    $&lt;$&lt;CONFIG:Release&gt;:-O3 -DNDEBUG&gt;  
)  

5. Practical Example: Multi-Library Dependencies and Link Control

5.1 Project Structure

Project/  
├── CMakeLists.txt  
├── src/  
│   ├── main.cpp  
│   ├── math/  
│   │   ├── matrix.cpp  
│   │   └── vector.cpp  
│   └── network/  
│       ├── socket.cpp  
│       └── http.cpp  
└── include/  
    ├── math_utils.h  
    └── network_utils.h  

5.2 Configuring Dependencies

# Main program links library  
add_executable(my_app src/main.cpp)  
# Static Library: Math Library  
add_library(math STATIC src/math/matrix.cpp src/math/vector.cpp)  
target_include_directories(math PUBLIC include/)  
# Dynamic Library: Network Library (depends on Math Library)  
add_library(network SHARED   src/network/socket.cpp   src/network/http.cpp)  
target_include_directories(network PUBLIC include/)  
target_link_libraries(network PRIVATE math)  # Private dependency  
# Main program links Network Library (automatically gets Math header files)  
target_link_libraries(my_app PRIVATE network)  

5.3 Key Validation Points

  1. Symbol Visibility:
nm -D build/libnetwork.so | grep public_api  # Should only show exported symbols  
  1. Dependency Propagation:
// In main.cpp, can directly use math_utils.h (due to network's PUBLIC dependency)  
#include "math_utils.h"  

6. Common Issues and Solutions

6.1 Dynamic Library Load Failure

  • Phenomenon: Runtime prompts <span>error while loading shared libraries: libnetwork.so</span>
  • Solution:
# Set RPATH (relative path)  
set(CMAKE_BUILD_RPATH "$ORIGIN")  # Same directory as executable  
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")  

6.2 Mixing Static and Dynamic Library Linking

  • Rule: If a dynamic library depends on a static library, ensure the static library is compiled with <span>-fPIC</span>.
  • Configuration:
# Enable -fPIC for static library (if it will be depended on by dynamic library)  
set_target_properties(math PROPERTIES  
  POSITION_INDEPENDENT_CODE ON  
)  

6.3 Cross-Platform Symbol Export (Windows)

  • Code Marking:
#ifdef _WIN32  
#  define API_EXPORT __declspec(dllexport)  
#else  
#  define API_EXPORT __attribute__((visibility("default")))  
#endif  

API_EXPORT void public_api();  

7. Summary and Next Article Preview

Key Points of This Article:

  • Master the core differences and building methods of static and dynamic libraries.
  • Deeply understand the compilation principles of <span>-fPIC</span> and symbol hiding.
  • Implement multi-library dependency control and cross-platform configuration.

Next Article Preview:

  • “Part 6: Header Files and Linkers – Modern<span>target_*</span> Command Family”
    • <span>target_include_directories</span> vs <span>include_directories</span>
    • <span>target_link_libraries</span> dependency propagation rules
    • Completely eliminate traditional commands (such as <span>link_directories</span> etc.)

CMake Mastery (1): CMake Simplified Introduction – Environment Configuration and First ProjectCMake Mastery (2): Variables and ScopesCMake Mastery (3): Functions and Parameter PassingCMake Mastery (4): Multi-Directory Project Management – The Philosophy of add_subdirectory

Leave a Comment