62

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

palmplam
  • 707
  • 9
  • 20
user1387866
  • 2,834
  • 3
  • 22
  • 28
  • 3
    What version of CMake are you using? In 2.8.9 on Windows 7, [`CMAKE_CURRENT_LIST_DIR`](http://www.cmake.org/cmake/help/v2.8.9/cmake.html#variable:CMAKE_CURRENT_LIST_DIR) behaves as per the documentation. i.e. if `foo.cmake` contains the command `message("foo dir - ${CMAKE_CURRENT_LIST_DIR}")` it outputs the directory containing `foo.cmake`, not that of the parent `CMakeLists.txt`. – Fraser Oct 09 '12 at 20:21
  • Yes, `CMAKE_CURRENT_LIST_DIR` behaves as per the documentation. And no, this is not what I was looking for: I would have liked to get the directory of `foo.cmake`, not the directory of the `CMakeLists.txt` that includes `foo.cmake`. – user1387866 Oct 10 '12 at 14:19
  • 1
    You seem to be contradicting yourself here. If you refer to `CMAKE_CURRENT_LIST_DIR` in CMakeLists.txt, it yields the directory of CMakeLists.txt. If you refer to `CMAKE_CURRENT_LIST_DIR` in foo.cmake, it yields the directory of foo.cmake. – Fraser Oct 10 '12 at 22:38
  • 1
    `CMAKE_CURRENT_LIST_DIR` in `foo.cmake` yields the directory of `CMakeLists.txt`, **not**, as you write, the directory of `foo.cmake` (except of course if those two directories happen to be identical). According to the [documentation](http://www.cmake.org/cmake/help/v2.8.9/cmake.html#variable:CMAKE_CURRENT_LIST_FILE): _Full path to the listfile currently being processed._, and my experiments confirm that this is indeed how `CMAKE_CURRENT_LIST_DIR` behaves. – user1387866 Oct 11 '12 at 08:32
  • [Robert Dailey's answer](http://stackoverflow.com/a/12827082/424459) is correct. There's something far wrong if this is not what you're seeing. Have you tried his solution? If you have, and your results aren't as per his answer, can you post minimal example CMake files, your directory structure, your version of CMake, your platform details, and the commands you're using to invoke CMake? – Fraser Oct 12 '12 at 01:19

4 Answers4

88

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  
[...]  
danijar
  • 32,406
  • 45
  • 166
  • 297
user1387866
  • 2,834
  • 3
  • 22
  • 28
  • OK - That makes sense now. `function` in CMake effectively inlines the calls made within the function, so in your original case the "listfile currently being processed" was CMakeLists.txt even though the definition of the function was in another listfile. – Fraser Oct 12 '12 at 12:34
  • Your design is wrong, IMHO. `CMAKE_CURRENT_LIST_DIR` is working as designed. Instead, put your bar function in the root CMakeLists.txt file and only call common functions in an upward path. In other words, do not call functions defined by lower-level CMake scripts from higher-level ones and you won't run into this problem. – void.pointer Oct 12 '12 at 15:11
  • 4
    "_put your bar function in the root CMakeLists.txt file_" Hmm... As I wrote in my very first post: "_Of course, foo.cmake might be included by several projects_" Are you suggesting that bar() should be duplicated in as many (root) CMakeLists.txt as it happens to be needed?! – user1387866 Oct 12 '12 at 17:40
  • And if bar is in a CMakeLists.txt included with add_subdirectory, you'll have to add PARENT_SCOPE to the declaration of DIR_OF_FOO_CMAKE: `set(DIR_OF_FOO_CMAKE ${CMAKE_CURRENT_LIST_DIR}) PARENT_SCOPE`) – Samuel Peter May 10 '17 at 16:58
  • This worked for me, with one caveat: use a unique variable name for each source file in which you use this technique. Variables are not necessarily exclusively file-scoped! (Tested with cmake 3.6.2) – cxw Nov 18 '18 at 13:50
16

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()
user7610
  • 25,267
  • 15
  • 124
  • 150
11

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

void.pointer
  • 24,859
  • 31
  • 132
  • 243
2

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

arrowd
  • 33,231
  • 8
  • 79
  • 110
  • 1
    From CMake documentation: "CMAKE_MODULE_PATH: List of directories to search for CMake modules." So, one would have to check for all the elements of CMAKE_MODULE_PATH, and then ${CMAKE_ROOT}/Modules. Not impossible, but... isn't there anything more convenient? – user1387866 Oct 09 '12 at 15:11
  • The included .cmake file (`foo.cmake` in my example) refers to a directory. The location of that directory is well defined relatively to the .cmake file, so I would have liked to refer to it with something like `${PATH_OF_THIS_CMAKE_FILE}/`. – user1387866 Oct 09 '12 at 17:54
  • Take a look at `include` documentation, the exceptional case: http://cmake.org/cmake/help/v2.8.9/cmake.html#command:include It seems that you can do `include(relative/path/to/file)` in your foo.cmake and it would work. – arrowd Oct 09 '12 at 17:58
  • I tried the suggested `include(relative/path/to/file)`, but it does not work. I will resort to the solution that you originally proposed, or to some workaround. – user1387866 Oct 10 '12 at 04:55