cmake项目相关的变量
在cmake项目中,通常需要对路径进行操作,比如我们需要知道源码的顶级目录,源码的构建目录和某个project名字相关的一些目录等。
CMAKE_SOURCE_DIR,这个变量的值代表的是源码的顶级目录。但是这个变量的值可能会发生变化。
比如我们现在有个项目B,他的顶级目录是/root/workspace/code/b。这时候我们在项目B中使用CMAKE_SOURCE_DIR变量的值,毫无疑问,它的值是:/root/workspace/code/b。
现在我们又有一个项目A,它的顶级目录是/root/workspace/code/a,同时我们的项目A依赖项目B,所以我们通过某种方式将项目B作为项目A的依赖。假设这个时候项目A依赖的项目B的源码在/root/workspace/code/b/3rd/b目录中。那这个时候我们在项目B中获取到的CMAKE_SOURCE_DIR的值就不是我们期望的/root/workspace/code/a/3rd/b,而变成了/root/workspace/code/a。所以我们的项目如果可能被作为第三方项目使用,那CMAKE_SOURCE_DIR的值可能是不可靠的。
同样CMAKE_BINARY_DIR变量同样有这样的问题。
那cmake是怎么解决这些问题的了?当我们调用project()命令的时候cmake会同时设置一些和project相关的变量的值。
- PROJECT_SOURCE_DIR
- 该变量的值是在当前作用域或者父作用域中最近的一处调用project()命令的那个CMakeLists.txt所在的目录。
- PROJECT_BINARY_DIR
- PROJECT_SOURCE_DIR 目录对应的构建目录。
- projectName_SOURCE_DIR
- 前面的projectName是在调用project()命令时传入的名字,加上——SOURCE_DIR后缀可以特指某个项目的CMakeLists.txt所在的目录。
- projectName_BINARY_DIR
- projectName_SOURCE_DIR 目录对应的构建目录
demo
目录结构:
顶层目录下,有一个child文件夹,child文件夹有个grandchild文件夹
top cmake文件:
cmake_minimum_required(VERSION 3.26 FATAL_ERROR)
project(topLevel)
message("Top level : \n")
message(" PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")
message(" topLevel_SOURCE_DIR = ${topLevel_SOURCE_DIR}")
add_subdirectory(child)
child cmake文件
cmake_minimum_required(VERSION 3.26 FATAL_ERROR)
message("Child \n")
message(" PROJECT_SOURCE_DIR (before) = ${PROJECT_SOURCE_DIR}")
project(child)
message(" PROJECT_SOURCE_DIR (after) = ${PROJECT_SOURCE_DIR}")
message(" child_SOURCE_DIR = ${child_SOURCE_DIR}")
add_subdirectory(grandchild)
grandchild cmake文件
cmake_minimum_required(VERSION 3.26 FATAL_ERROR)
message("GrandChild \n")
message(" PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")
message(" child_SOURCE_DIR = ${child_SOURCE_DIR}")
message(" topLevel_SOURCE_DIR = ${topLevel_SOURCE_DIR}")
结果:
总结:
通过上面的例子,我们可以看到,使用project()命令相关的变量去获取相关的路径是非常可靠的,我们的项目中也建议使用这种方式.
举个例子,我们再使用${CMAKE_SOURCE_DIR}/someFile的时候最好使用${PROJECT_SOURCE_DIR}/someFile 或者${projctName_SOURCE_DIR}/someFile代替.这样无论我们的项目是单独构建的还是作为一个子项目构建都能保证我们期望的目录是对的.我们经常看到一些开源项目的顶级目录的CMakeLists.txt中有类似下面的代码:
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# do something
endif()
这里通过比较CMAKE_CURRENT_SOURCE和CMAKE_SOURCE_DIR两个变量的值来判断当前项目是否单独构建.如果相等,表明这个项目是单独构建的,没有作为任何其他项目的子模块.
上面这种方式还需要我们自己判断,cmake3.21版本开始,cmake提供了一个变量:PROJECT_IS_TOP_LEVEL,如果这个变量为真,就代表当前项目是单独构建的,或者是项目中顶级project.
同理,也有proName_IS_TOP_LEVEL变量.每当我们调用project()命令的时候,就会创建对应的proName_IS_TOP_LEVEL缓存变量.