在CMake中保持跨子目录的文件层次结构
Keeping file hierarchy across subdirectories in CMake
到目前为止,我仍然没有真正理解什么是"最佳实践",为一个CMake项目做这么多子目录。
假设我有一个项目层次结构,每个子目录中都有源文件…
--CMake Project Source dir
|-- SubD1
|-- SubSubD1
|-- SubD2
我通常会做的是在根目录的CMakeLists.txt中分别为D2和递归地为SubD1目录的CMakeLists.txt中的子目录做add_subdirectory(SubD1)
,同时在每个子目录中声明变量,并使它们在根目录中可见PARENT_SCOPE
。
这意味着如果文件Source2.cpp
存在于' SubSubD1'中,我只需执行
set(SUBSUBD1_SOURCES Source2.cpp PARENT_SCOPE)
并期望能够在我的SubD1
目录中使用subsub1_source。随后,假设Source.cpp
存在于SubD1中,我将执行
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} Source.cpp PARENT_SCOPE)
这样所有的源文件都可以在根目录中看到。
问题当然是当变量到达根目录时,文件路径不会被保留。我目前所做的是为所有的源文件,我set
,我包括一个${CMAKE_CURRENT_LIST_DIR}
,使其
set(SUBSUBD1_SOURCES ${CMAKE_CURRENT_LIST_DIR}/Source2.cpp PARENT_SCOPE)
和
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/Source.cpp PARENT_SCOPE)
在这种情况下,我可以说,在我的CMake项目的根目录中执行add_executable(myProg SUBSUBD1_SOURCES)
。
是否有更好的方法做到这一点,然后必须始终包括一个CMake变量在所有源文件的前面?
如果你使用的是较新版本的CMake,还有第四种方法。
看看CMake的target_sources()命令
似乎你在CMakeLists.txt中声明了你的目标
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
你可以依赖你在根目录CMakeLists.txt中定义的目标文件,而不是将你的源文件传播到根目录。这意味着subd1/CMakeLists.txt可能看起来像:
target_sources(my_target PRIVATE "subd1/Source.cpp" "subd1/Source2.cpp")
[编辑]如注释中所述,必须将源文件的相对路径提供给target_sources()。我使用target_sources()是因为我不希望显式的源文件列表污染目标CMakeLists.txt。另一个用例是可以用PUBLIC或INTERFACE关键字调用target_sources(),以将源文件传播到依赖的目标。我从来没有这样使用过target_sources()。
(/编辑)如果你使用像Visual Studio这样支持文件夹的ide,你也想在CMakeLists.txt中声明一个source_group(),其中包含你的目标。所以根目录CMakeLists.txt可能看起来像:
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
...
source_group(subd1 REGULAR_EXPRESSION "subd1/*")
source_group(subd2 REGULAR_EXPRESSION "subd2/*")
我使用这种方法,因为它导致更干净的CMakeLists.txt文件,它的工作量更少,我认为不需要的变量的引入只会提高你的CMakeLists.txt文件的复杂性。
CMakeLists.txt作为目标源
我目前使用子文件夹的CMakeLists.txt作为目标的源文件,否则CMake会抱怨add_executable命令没有给定的源文件。
我以前用过3种方法。我通常更喜欢第一种方式,但根据用例已经使用了所有3种方式:
1。直接在根CMakeLists.txt
文件中命名源码
set(
SUBD1_SOURCES
"SubD1/SubSubD1/Source2.cpp"
"SubD1/Source.cpp"
)
set(
SUBD2_SOURCES
"SubD2/Source3.cpp"
)
add_executable(myProg ${SUBD1_SOURCES} ${SUBD2_SOURCES})
2。您使用OBJECT
中间库来收集/分组您的源
SubD1/SubSubD1/CMakeLists.txt:
add_library(SubSubD1Objs OBJECT Source2.cpp)
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
add_library(SubD1Objs OBJECT Source.cpp)
CMakeLists.txt:
add_executable(myProg $<TARGET_OBJECTS:SubSubD1Objs> $<TARGET_OBJECTS:SubD1Objs>)
3。您编写自己的function()
来收集数据(并添加前缀)
CMakeLists.txt:
function(my_collect_sources)
foreach(_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
set_property(GLOBAL APPEND PROPERTY GlobalSourceList "${_source_abs}")
endforeach()
endfunction(my_collect_sources)
add_subdirectory(SubD1)
#add_subdirectory(SubD2)
get_property(MY_SOURCES GLOBAL PROPERTY GlobalSourceList)
add_executable(myProg ${MY_SOURCES})
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
my_collect_sources(Source.cpp)
SubD1/SubSubD1/CMakeLists.txt:
my_collect_sources(Source2.cpp)
在您的情况下,没有必要使用add_subdirectory
,因为您只有一个目标,它是在根CMakeLists.txt
中创建的。您可以简单地这样写:
add_executable(myProg
SubD1/Source.cpp
SubD1/SubSubD1/Source2.cpp)
使用add_subdirectory
为子目录创建自己的目标,所以没有信息向上传递。
- 使文件夹中的所有文件可供所有项目(子目录)访问
- 从所有列出的文件和子目录中更改目录
- 获取每个目录或子目录中第一个文件的路径
- Qt创建者:加载位于子目录中的项目文件
- 如何在子目录中创建文本文件?
- Makefile在Linux中工作,但在Windows下不起作用,在子目录中找不到文件
- 生成文件:子目录的模式规则
- CMake 子目录 - 编译测试子项目时,包含的头文件没有此类文件或目录
- 节点构建失败。无法在包含目录的子目录中找到库头文件
- 这是在生成文件中包含子目录的正确方法吗?
- 使用C 列出目录中的文件,而不是递归,只有文件和无子目录
- CMAKE:将静态库从子目录链接到另一个子目录中的可执行文件
- 在目录和子目录C 中计数具有特定扩展名的文件
- CMake 从子目录和父目录链接文件
- 创建目录中所有.cpp文件和 android.mk 子目录中的FILE_LIST
- 一种使用 boost 和 c++ 列出目录和子目录中所有文件的方法
- 如何在子目录中生成包含源的生成文件
- 用于列出子目录中所有文件的递归功能
- 扫描目录和子目录中的.txt文件
- 如何在cllion中编译子目录文件