Understanding CMake Policy CMP0009: Controlling Symlink Handling in FILE(GLOB_RECURSE)
CMP0009: Controlling Symlink Handling in FILE(GLOB_RECURSE)
In CMake, the file(GLOB_RECURSE)
command is used to find files within a directory and its subdirectories that match a specific pattern. Prior to CMake version 2.6.2, this command would automatically follow symbolic links (symlinks) encountered during the search. This could lead to unexpected behavior, especially if symlinks pointed to directories containing a large number of files.
Introducing CMP0009
CMake Policy CMP0009 was introduced to address this issue and provide more control over symlink handling. It determines whether or not file(GLOB_RECURSE)
should follow symlinks by default:
- NEW Behavior (CMake 2.6.2 and Later)
Symlinks are not followed by default. - OLD Behavior (Pre-CMake 2.6.2)
Symlinks are always followed during the file search.
Setting the Policy
You can explicitly set the CMP0009 policy using the cmake_policy
command in your CMakeLists.txt file:
if(COMMAND cmake_policy)
cmake_policy(SET CMP0009 NEW) # Enable the new behavior (not following symlinks by default)
# OR
cmake_policy(SET CMP0009 OLD) # Revert to the old behavior (following symlinks)
endif(COMMAND cmake_policy)
Specifying FOLLOW_SYMLINKS Option
Even with the NEW behavior set, you can still instruct file(GLOB_RECURSE)
to follow a specific symlink by adding the FOLLOW_SYMLINKS
option after the glob pattern and before the RELATIVE
keyword (if used):
file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" FOLLOW_SYMLINKS "*.cpp")
In this example, file(GLOB_RECURSE)
will follow the symlink and include any .cpp
files found within the linked directory.
- Use
FOLLOW_SYMLINKS
to explicitly include files or directories reached through symlinks. - The NEW behavior (not following symlinks by default) is generally recommended to avoid unexpected results.
- CMP0009 provides finer control over symlink handling in
file(GLOB_RECURSE)
.
Scenario 1: New Behavior (Not Following Symlinks by Default)
This example assumes CMP0009 is set to NEW (the default behavior in CMake 2.6.2 and later).
# Directory structure (simplified)
src/
main.cpp
header/
header.h
linked_dir/ # Symbolic link to another directory
linked_file.cpp
file(GLOB_RECURSE source_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.cpp")
# source_files will only contain:
# main.cpp
# To include linked_file.cpp, explicitly follow the symlink:
file(GLOB_RECURSE linked_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" FOLLOW_SYMLINKS "linked_dir/*.cpp")
# linked_files will contain:
# linked_file.cpp
In this case, file(GLOB_RECURSE)
won't follow the linked_dir
symlink by default (due to CMP0009 being NEW). You need to use the FOLLOW_SYMLINKS
option to include files within the linked directory.
Scenario 2: Old Behavior (Following Symlinks)
Imagine you want the old behavior (following symlinks) for this project. You can achieve this by setting CMP0009 to OLD:
if(COMMAND cmake_policy)
cmake_policy(SET CMP0009 OLD)
endif(COMMAND cmake_policy)
file(GLOB_RECURSE source_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.cpp")
# source_files will now contain:
# main.cpp
# linked_dir/linked_file.cpp # Because we're following symlinks
With CMP0009 set to OLD, file(GLOB_RECURSE)
will automatically follow the linked_dir
symlink and include linked_file.cpp
in the results.
- Use
FOLLOW_SYMLINKS
explicitly when necessary to include files or directories reached through symlinks, regardless of the default behavior. - If you're unsure, the NEW behavior is generally recommended to avoid unintended consequences.
- Choose the behavior (NEW or OLD) based on your project's requirements and potential issues with following symlinks.
Manual Directory Iteration
# Assuming Bash-like shell
find "${CMAKE_CURRENT_SOURCE_DIR}" -type f -name "*.cpp" > source_files.txt
# Read the generated file list
file(READ source_files.txt source_file_list)
# Remove the temporary file (optional)
file(REMOVE source_files.txt)
# Use source_file_list for further processing
This approach offers complete control over symlink handling, but it's less concise and potentially less portable compared to file(GLOB_RECURSE)
.
Custom Script or Function
If you need more complex logic for filtering or processing files during the search, consider creating a custom script or function that encapsulates your desired behavior. You can call this script/function from your CMakeLists.txt.
- If you need to explicitly include files or directories reached through symlinks, use the
FOLLOW_SYMLINKS
option withinfile(GLOB_RECURSE)
. - If your primary concern is avoiding unintended symlink traversal, the NEW behavior of CMP0009 (not following symlinks by default) is generally recommended.