到目前为止,我仍然不太了解最佳实践'是为具有许多子目录的CMake项目执行此操作。
假设我有一个项目层次结构,每个子目录中都有源文件......
--CMake Project Source dir
|-- SubD1
|-- SubSubD1
|-- SubD2
我通常要做的是分别对根目录的CMakeLists.txt中的add_subdirectory(SubD1)
和D2进行处理,并递归查找SubD1目录的CMakeLists.txt中的子目录,同时在每个子目录中声明变量并使用PARENT_SCOPE
在根目录中显示它们。
这意味着如果`SubSubD1'中存在Source2.cpp
文件,我只需要
set(SUBSUBD1_SOURCES Source2.cpp PARENT_SCOPE)
并希望能够在我的SubD1
目录中使用SUBSUBD1_SOURCE。
随后,说SubD1中存在Source.cpp
,我会做
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变量?
答案 0 :(得分:6)
我之前使用过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
中间libraries来收集/分组您的来源
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)
答案 1 :(得分:6)
如果您使用较新版本的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命令没有给出源文件。
答案 2 :(得分:1)
在您的情况下,不需要使用add_subdirectory
,因为您只有一个在根CMakeLists.txt
中创建的目标。你可以写下这个:
add_executable(myProg
SubD1/Source.cpp
SubD1/SubSubD1/Source2.cpp)
使用add_subdirectory
为子目录创建自己的目标,这样就没有向上传递的信息。