Understanding CMAKE_LINK_WHAT_YOU_USE for Smaller Binaries in CMake


Understanding CMAKE_LINK_WHAT_YOU_USE

In CMake, CMAKE_LINK_WHAT_YOU_USE isn't a variable in the traditional sense. It's a target property introduced in CMake version 3.7. Target properties are attributes associated with build targets (executables, libraries) that control various aspects of their creation.

Purpose

While the name might suggest it directly controls linker behavior, CMAKE_LINK_WHAT_YOU_USE doesn't actually affect linking by itself. Its primary purpose is to identify unused libraries during the linking process.

Mechanism

  1. CMake's Default Behavior
    By default, CMake links all specified libraries with an executable or shared library, even if the target doesn't use all of them. This can lead to larger binary sizes.
  2. CMAKE_LINK_WHAT_YOU_USE and Unused Library Detection
    When you set CMAKE_LINK_WHAT_YOU_USE to TRUE on a target, CMake employs a special technique to determine which libraries are actually referenced by the target's code.
    • It leverages the linker's --no-as-needed flag (if supported) to force the linker to include all libraries in the linking process.
    • This step might involve creating a temporary executable or library just for dependency analysis.
    • Afterward, CMake examines the linker's output to identify libraries that weren't truly needed.

Benefits

  • Reduced Binary Size
    By identifying unused libraries, CMAKE_LINK_WHAT_YOU_USE can help create smaller executables and libraries, which is especially beneficial for resource-constrained environments or embedded systems.

Limitations

  • Overhead
    The process of identifying unused libraries can add some overhead to the linking stage.
  • Not a Universal Solution
    --no-as-needed isn't universally supported by all linkers. CMake might not be able to accurately detect unused libraries on systems with unsupported linkers.

Usage

To enable unused library detection for a target, you can use the target_link_libraries command with the LINK_WHAT_YOU_USE property:

target_link_libraries(my_executable MY_LIBRARY PUBLIC LINK_WHAT_YOU_USE ON)
  • Be aware of potential compatibility issues with certain linkers.
  • While CMAKE_LINK_WHAT_YOU_USE can be helpful, it's not a guaranteed solution for minimizing binary size. Other optimization techniques like stripping symbols and using compiler optimization flags might be necessary for further reduction.


Example 1: Basic Usage

This example shows how to enable unused library detection for an executable:

# Define a library
add_library(my_library my_library.cpp)

# Define an executable
add_executable(my_executable my_executable.cpp)

# Link the executable with the library and enable unused library detection
target_link_libraries(my_executable my_library PUBLIC LINK_WHAT_YOU_USE ON)

Example 2: Conditional Usage

This example demonstrates enabling CMAKE_LINK_WHAT_YOU_USE only when a specific flag is defined:

if (DEFINED BUILD_FOR_EMBEDDED)
  # Enable for embedded builds where size is critical
  set(CMAKE_LINK_WHAT_YOU_USE ON)
endif()

# Define a library and an executable
add_library(my_library my_library.cpp)
add_executable(my_executable my_executable.cpp)

# Link the executable with the library (unused detection depends on BUILD_FOR_EMBEDDED)
target_link_libraries(my_executable my_library PUBLIC)
# Enable unused library detection by default
set(CMAKE_LINK_WHAT_YOU_USE ON)

# Define a library and an executable
add_library(special_library special_library.cpp)
add_executable(my_executable my_executable.cpp)

# Link the executable with the special library, but disable unused detection for it
target_link_libraries(my_executable special_library PUBLIC LINK_WHAT_YOU_USE OFF)


Manual Library Linking

  • Cons
    Can be time-consuming and error-prone for larger projects.
  • Pros
    Offers the most granular control over linked libraries.
  • This approach involves carefully analyzing your code dependencies and only linking the libraries that are truly used. It requires a good understanding of your project's codebase.

Compiler Optimization Flags

  • Cons
    Might introduce performance trade-offs due to aggressive optimizations.
  • Pros
    Relatively easy to implement and can have significant size reductions.
  • Many compilers provide optimization flags that can help reduce binary size. These flags often focus on removing unused code and data. Common examples include -O2, -Os, and -flto (Link-Time Optimization).

Precompiled Headers

  • Cons
    Requires additional setup and might not be suitable for all projects.
  • Pros
    Can improve build times and sometimes reduce binary size.
  • Precompiled headers involve pre-compiling common header files once and including the precompiled object file instead. This can reduce compile times and potentially lead to smaller final executables.

Stripping Symbols

  • Cons
    Makes debugging more difficult or impossible.
  • Pros
    Simple and effective way to reduce binary size.
  • Stripping symbols removes debugging information from the final executable or library. This information isn't necessary for normal execution but can significantly increase binary size.

Linker Optimization Flags

  • Cons
    Requires knowledge of your specific linker and its flags.
  • Pros
    Can be effective in conjunction with other techniques.
  • Some linkers offer flags to optimize the linking process and reduce binary size. These flags can be project-specific and may require experimentation to find the best combination.

Choosing the Right Approach

  • Stripping symbols can be applied in most cases unless debugging information is crucial.
  • For larger projects, consider precompiled headers or linker optimization flags.
  • For small and medium-sized projects, manual linking with compiler optimization might be sufficient.
  • Profiling
    Use profiling tools to identify code sections that contribute most to binary size. This can help you target your optimization efforts effectively.
  • Third-Party Libraries
    Be aware of how third-party libraries are linked. Some might come with pre-built configurations that may or may not remove unused parts. Consider building them from source with proper flags if size is critical.