In CMake, how can I find the directory of an included file?
FileCmakeFile Problem Overview
Suppose my project's CMakeLists.txt
includes foo.cmake
:
include(foo)
In foo.cmake
, i want to know the path of foo.cmake
.
How can I do that?
Note that CMAKE_CURRENT_LIST_DIR
gives the directory of the including CMakeLists.txt
, not that of the included foo.cmake
, and is thus not what I want.
Of course, foo.cmake
might be included by several projects (i.e., by several CMakeLists.txt
files).
File Solutions
Solution 1 - File
People have reported seemingly contradictory facts about how CMAKE_CURRENT_LIST_DIR behaves. Now I know the reason for the confusion:
First, in my Linux environment:
$ cd /path/to/home
$ mkdir cmake-test
$ cd cmake-test
$ mkdir source
$ mkdir source/subdirectory
$ mkdir build
I create these two files:
$ cat source/CMakeLists.txt
include(subdirectory/foo.cmake)
$ cat source/subdirectory/foo.cmake
message("CMAKE_CURRENT_LIST_DIR is ${CMAKE_CURRENT_LIST_DIR}")
CMake works as reported by Fraser and Robert Dailey:
$ cd build
$ cmake ../source
CMAKE_CURRENT_LIST_DIR is /path/to/home/cmake-test/source/subdirectory
[...]
However, I add a function to foo.cmake, which I call from CMakeLists.txt:
$ cat ../source/subdirectory/foo.cmake
message("CMAKE_CURRENT_LIST_DIR is ${CMAKE_CURRENT_LIST_DIR}")
function(bar)
message("CMAKE_CURRENT_LIST_DIR in bar() is ${CMAKE_CURRENT_LIST_DIR}")
endfunction()
$ cat ../source/CMakeLists.txt
include(subdirectory/foo.cmake)
bar()
Then:
$ cmake ../source
CMAKE_CURRENT_LIST_DIR is /path/to/home/cmake-test/source/subdirectory
CMAKE_CURRENT_LIST_DIR in bar() is /path/to/home/cmake-test/source
[...]
So, the value of CMAKE_CURRENT_LIST_DIR in foo.cmake is not the same at the time foo.cmake is included and when bar() is called. This is according to the specification of CMAKE_CURRENT_LIST_DIR.
Here is one possible solution for accessing the directory of foo.cmake from within bar():
$ cat ../source/subdirectory/foo.cmake
set(DIR_OF_FOO_CMAKE ${CMAKE_CURRENT_LIST_DIR})
function(bar)
message("DIR_OF_FOO_CMAKE in bar() is ${DIR_OF_FOO_CMAKE}")
endfunction()
after which I get the behavior I was looking for:
$ cmake ../source
DIR_OF_FOO_CMAKE in bar() is /path/to/home/cmake-test/source/subdirectory
[...]
Solution 2 - File
> Full directory of the listfile currently being processed. > > As CMake processes the listfiles in your project this variable will > always be set to the directory where the listfile which is currently > being processed (CMAKE_CURRENT_LIST_FILE) is located. The value has > dynamic scope. When CMake starts processing commands in a source file > it sets this variable to the directory where this file is located. > When CMake finishes processing commands from the file it restores the > previous value. Therefore the value of the variable inside a macro or > function is the directory of the file invoking the bottom-most entry > on the call stack, not the directory of the file containing the macro > or function definition.
Example
I have the following structure:
C:\Work\cmake-test\CMakeLists.txt
C:\Work\cmake-test\subfolder\test.cmake
In my CMakeLists.txt
:
include( subfolder/test.cmake )
In my test.cmake
:
message( "Current dir: ${CMAKE_CURRENT_LIST_DIR}" )
The result I get when I run CMake from C:\Work\cmake-test
is:
> Current dir: C:/Work/cmake-test/subfolder
Solution 3 - File
In CMake 3.17, you have a new variable available, called CMAKE_CURRENT_FUNCTION_LIST_DIR
, which can be used inside a function. It is undefined outside of a function definition.
function(foo)
configure_file(
"${CMAKE_CURRENT_FUNCTION_LIST_DIR}/some.template.in"
some.output
)
endfunction()
Prior to CMake 3.17, CMAKE_CURRENT_FUNCTION_LIST_DIR
functionality has to be approximated with CMAKE_CURRENT_LIST_DIR
by the following workaround, taken from CMake documentation:
set(_THIS_MODULE_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
function(foo)
configure_file(
"${_THIS_MODULE_BASE_DIR}/some.template.in"
some.output
)
endfunction()
Solution 4 - File
The include()
command searches for modules in ${CMAKE_MODULE_PATH}
first and then in CMake Modules
dir.
So you can just check for file presence with if(EXISTS ${CMAKE_MODULE_PATH}/foo.cmake)
and if(EXISTS ${CMAKE_ROOT}/Modules/foo.cmake)
.