【问题标题】:In CMake, how can I find the directory of an included file?在 CMake 中,如何找到包含文件的目录?
【发布时间】:2012-09-29 22:08:51
【问题描述】:

假设我的项目的CMakeLists.txt 包含foo.cmake

include(foo)

foo.cmake,我想知道foo.cmake的路径。
我该怎么做?

注意CMAKE_CURRENT_LIST_DIR 给出了包含CMakeLists.txt 的目录,而不是包含foo.cmake 的目录,因此不是我想要的。

当然,foo.cmake 可能包含在多个项目中(即多个CMakeLists.txt 文件)。

【问题讨论】:

  • 您使用的是什么版本的 CMake?在 Windows 7 上的 2.8.9 中,CMAKE_CURRENT_LIST_DIR 的行为与文档一致。即如果foo.cmake 包含命令message("foo dir - ${CMAKE_CURRENT_LIST_DIR}") 它输出包含foo.cmake 的目录,而不是父CMakeLists.txt 的目录。
  • 是的,CMAKE_CURRENT_LIST_DIR 的行为与文档一致。不,这不是我想要的:我希望获得foo.cmake 的目录,而不是包含foo.cmakeCMakeLists.txt 的目录。
  • 你似乎在这里自相矛盾。如果你在 CMakeLists.txt 中引用CMAKE_CURRENT_LIST_DIR,它会产生 CMakeLists.txt 的目录。如果你在 foo.cmake 中引用CMAKE_CURRENT_LIST_DIR,它会产生 foo.cmake 的目录。
  • CMAKE_CURRENT_LIST_DIR in foo.cmake 产生CMakeLists.txt 的目录,不是,正如你所写,foo.cmake 的目录(当然,如果这两个目录除外恰好是相同的)。根据documentation当前正在处理的列表文件的完整路径。,我的实验证实这确实是CMAKE_CURRENT_LIST_DIR 的行为方式。
  • Robert Dailey's answer 是正确的。如果这不是您所看到的,那就大错特错了。你试过他的解决方案吗?如果您有,并且您的结果与他的回答不符,您能否发布最小的示例 CMake 文件、您的目录结构、您的 CMake 版本、您的平台详细信息以及您用于调用 CMake 的命令?

标签: file cmake


【解决方案1】:

人们报告了关于 CMAKE_CURRENT_LIST_DIR 行为方式的看似矛盾的事实。现在我知道了混乱的原因:

首先,在我的 Linux 环境中:

$ cd /path/to/home  
$ mkdir cmake-test  
$ cd cmake-test  
$ mkdir source  
$ mkdir source/subdirectory  
$ mkdir build  

我创建了这两个文件:

$ cat source/CMakeLists.txt  
include(subdirectory/foo.cmake)  

$ cat source/subdirectory/foo.cmake  
message("CMAKE_CURRENT_LIST_DIR is ${CMAKE_CURRENT_LIST_DIR}")  

Fraser 和 Robert Dailey 报告的 CMake 工作原理:

$ cd build  
$ cmake ../source  
CMAKE_CURRENT_LIST_DIR is /path/to/home/cmake-test/source/subdirectory  
[...]  

但是,我在 foo.cmake 中添加了一个函数,我从 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()  

然后:

$ 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  
[...]  

因此,在包含 foo.cmake 和调用 bar() 时, foo.cmake 中的 CMAKE_CURRENT_LIST_DIR 的值是不一样的。这是根据CMAKE_CURRENT_LIST_DIR的规范。

这是从 bar() 中访问 foo.cmake 目录的一种可能解决方案:

$ 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()  

之后我得到了我正在寻找的行为:

$ cmake ../source  
DIR_OF_FOO_CMAKE in bar() is /path/to/home/cmake-test/source/subdirectory  
[...]  

【讨论】:

  • 好的——现在说得通了。 CMake 中的function 有效地内联了在函数内进行的调用,因此在您的原始情况下,“当前正在处理的列表文件”是 CMakeLists.txt,即使函数的定义在另一个列表文件中。
  • 你的设计是错误的,恕我直言。 CMAKE_CURRENT_LIST_DIR 正在按设计工作。相反,将您的 bar 函数放在根 CMakeLists.txt 文件中,并且只在向上的路径中调用常用函数。也就是说,不要从高层调用低层CMake脚本定义的函数,就不会遇到这个问题。
  • "将 bar 函数放在根 CMakeLists.txt 文件中" 嗯...正如我在第一篇文章中所写的那样:“当然,foo。 cmake 可能包含在多个项目中" 您是否建议 bar() 应该在尽可能多的(根)CMakeLists.txt 中复制,因为它恰好需要?!
  • 如果 bar 位于 add_subdirectory 包含的 CMakeLists.txt 中,则必须将 PARENT_SCOPE 添加到 DIR_OF_FOO_CMAKE 的声明中:set(DIR_OF_FOO_CMAKE ${CMAKE_CURRENT_LIST_DIR}) PARENT_SCOPE)
  • 这对我有用,但有一个警告:为使用此技术的每个源文件使用唯一的变量名。变量不一定是文件范围的! (使用 cmake 3.6.2 测试)
【解决方案2】:

CMAKE_CURRENT_LIST_DIR:

当前正在处理的列表文件的完整目录。

当 CMake 处理项目中的列表文件时,此变量将 始终设置为当前列表文件所在的目录 正在处理 (CMAKE_CURRENT_LIST_FILE) 的位置。值有 动态范围。当 CMake 开始处理源文件中的命令时 它将这个变量设置为这个文件所在的目录。 当 CMake 完成处理来自文件的命令时,它会恢复 以前的值。因此,宏内部变量的值或 function 是调用最底部条目的文件的目录 在调用堆栈上,而不是包含宏的文件的目录 或函数定义。

示例

我有以下结构:

C:\Work\cmake-test\CMakeLists.txt
C:\Work\cmake-test\subfolder\test.cmake

在我的CMakeLists.txt:

include( subfolder/test.cmake )

在我的test.cmake

message( "Current dir: ${CMAKE_CURRENT_LIST_DIR}" )

我从C:\Work\cmake-test 运行 CMake 时得到的结果是:

当前目录:C:/Work/cmake-test/subfolder

【讨论】:

  • CMAKE_CURRENT_LIST_DIR 在这种情况下毫无帮助。
  • 我在答案中添加了一个示例,以便您更清楚地了解。
  • 这仅在您直接在 .cmake 中访问时才有效,如果您在函数或宏中访问变量则无效。
【解决方案3】:

在 CMake 3.17 中,have a new variable 可用,称为CMAKE_CURRENT_FUNCTION_LIST_DIR,可以在函数内部使用。它在函数定义之外是未定义的。

function(foo)
  configure_file(
    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/some.template.in"
    some.output
  )
endfunction()

在 CMake 3.17 之前,CMAKE_CURRENT_FUNCTION_LIST_DIR 功能必须通过以下解决方法近似于 CMAKE_CURRENT_LIST_DIR,取自 CMake 文档:

set(_THIS_MODULE_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")

function(foo)
  configure_file(
    "${_THIS_MODULE_BASE_DIR}/some.template.in"
    some.output
  )
endfunction()

【讨论】:

    【解决方案4】:

    include() 命令首先在 ${CMAKE_MODULE_PATH} 中搜索模块,然后在 CMake Modules 目录中搜索。

    因此您可以使用if(EXISTS ${CMAKE_MODULE_PATH}/foo.cmake)if(EXISTS ${CMAKE_ROOT}/Modules/foo.cmake) 检查文件是否存在。

    【讨论】:

    • 来自 CMake 文档:“CMAKE_MODULE_PATH:用于搜索 CMake 模块的目录列表。”因此,必须检查 CMAKE_MODULE_PATH 的所有元素,然后检查 ${CMAKE_ROOT}/Modules。不是不可能,但是……没有更方便的吗?
    • 包含的 .cmake 文件(在我的示例中为foo.cmake)指的是一个目录。该目录的位置相对于 .cmake 文件定义得很好,所以我想用${PATH_OF_THIS_CMAKE_FILE}/<path relative to the .cmake file> 之类的东西来引用它。
    • 查看include 文档,例外情况:cmake.org/cmake/help/v2.8.9/cmake.html#command:include 看来您可以在 foo.cmake 中执行include(relative/path/to/file),它会起作用。
    • 我尝试了建议的include(relative/path/to/file),但它不起作用。我将求助于您最初提出的解决方案,或一些解决方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-11
    相关资源
    最近更新 更多