CMake Include Search Paths: Controlling Header Discovery


Purpose

  • Determines the order in which include directories are searched by the compiler during the build process.
  • Controls the default behavior of the include_directories() command in CMake.

Behavior

  • When set to OFF:
    • include_directories() directories are appended to the search path.
  • When set to ON (default in newer CMake versions):
    • Directories specified using include_directories() are prepended to the compiler's search path.
    • This prioritizes your project's include directories over system-wide or third-party ones (unless explicitly prepended using BEFORE).

Impact on Build Process

  • By controlling the search order, CMAKE_INCLUDE_DIRECTORIES_BEFORE helps ensure that the compiler finds the correct header files required by your project's source code.
    • Prepending your project's directories guarantees that your custom headers are found first, preventing conflicts with system-wide headers that might have the same names.

Common Usages

  • Targeted Prepending
    For individual include_directories() calls, you can still use the BEFORE option to prepend directories even if the global variable is OFF.
  • Overriding Default
    If you have specific reasons to append directories (e.g., integrating with system-wide libraries that require their headers to be found first), you can set CMAKE_INCLUDE_DIRECTORIES_BEFORE to OFF in your CMakeLists.txt file.
  • Default Behavior
    In most cases, you don't need to explicitly set CMAKE_INCLUDE_DIRECTORIES_BEFORE. The default ON value ensures your project's headers take precedence.

Example

# Project's include directory (assuming it's in the same directory as CMakeLists.txt)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

# External library with a non-standard include directory structure
set(EXTERNAL_LIB_INCLUDE_DIR "/path/to/external/lib/nonstandard/include")
include_directories(BEFORE ${EXTERNAL_LIB_INCLUDE_DIR})

In this example:

  • The external library's directory is explicitly prepended with BEFORE to ensure its headers are found first if there are potential conflicts.
  • The project's include directory is prepended using the default behavior.
  • If unsure about the default behavior in your CMake version, consult the documentation for your specific version.
  • Consider using project-specific header search paths for better organization and modularity.


Default Behavior - Project Headers Take Precedence (No Need to Set Variable)

# Project's include directory (assuming it's in the same directory)
include_directories(include)

# Source code using project-specific headers
add_executable(my_program main.cpp)

In this basic example, the include_directories command adds the local include directory to the compiler's search path. Since CMAKE_INCLUDE_DIRECTORIES_BEFORE is likely ON by default, this directory will be searched first, ensuring your project's headers are found before any system-wide ones with the same names.

Overriding Default - Appending Directories

# Set variable to OFF to append directories
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE OFF)

# External library include directory
include_directories(${EXTERNAL_LIB_INCLUDE_DIR})

# Project's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

This example explicitly sets CMAKE_INCLUDE_DIRECTORIES_BEFORE to OFF to change the default behavior. Now, include_directories calls will append directories to the search path. In this case, the external library's directory is added first (potentially containing system-wide headers), followed by your project's directory. Use this only if you have a specific reason to prioritize external headers first.

Combining Default and Targeted Prepending

# Project's include directory (default behavior prepend)
include_directories(include)

# External library with a potential name conflict (prepend explicitly)
set(EXTERNAL_LIB_INCLUDE_DIR "/path/to/external/headers")
include_directories(BEFORE ${EXTERNAL_LIB_INCLUDE_DIR})

# Another external library (default behavior will prepend)
include_directories(thirdparty/library/include)

# Source code using project and external headers
add_executable(my_program main.cpp)

Here, the project's directory is prepended by default. The first external library's directory uses BEFORE to ensure it's searched before the project's directory (potential conflict resolution). Finally, the second external library's directory will be prepended by default, placing it after the project's and the first external library's directories in the search path.



  1. Targeted Prepending with BEFORE Option
  • You can still achieve prepending behavior for individual include_directories() calls, regardless of the global CMAKE_INCLUDE_DIRECTORIES_BEFORE setting. Use the BEFORE option within the include_directories() command:
include_directories(BEFORE ${EXTERNAL_LIB_INCLUDE_DIR})

This ensures the specified directory is searched before the remaining directories in the search path.

  1. Explicit Relative Paths in Source Code
  • For well-defined project structures, you can use relative paths in your source code's #include directives to explicitly reference project headers:
#include "../include/my_header.h"  // Assuming include dir is one level above

This avoids relying on compiler search paths and ensures your project's headers are found as long as the relative paths are correct. However, this approach requires more discipline in maintaining relative paths consistently throughout your codebase.

  1. Header Search Paths in Project Configuration
  • CMake allows setting project-specific header search paths for different build configurations. This can be useful if you have configurations with varying dependencies. You can utilize commands like target_include_directories() or compiler flags to define search paths specific to each configuration.

While not a direct replacement for CMAKE_INCLUDE_DIRECTORIES_BEFORE, it provides another layer of control over header search behavior based on build requirements. Refer to the CMake documentation for details on these commands and flags.