In CMake, how can I find the directory of an included file?

FileCmake

File 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

See CMAKE_CURRENT_LIST_DIR:

> 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).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1387866View Question on Stackoverflow
Solution 1 - Fileuser1387866View Answer on Stackoverflow
Solution 2 - Filevoid.pointerView Answer on Stackoverflow
Solution 3 - Fileuser7610View Answer on Stackoverflow
Solution 4 - FilearrowdView Answer on Stackoverflow