CMake:配置深层子文件夹树

时间:2016-01-10 18:36:17

标签: visual-c++ cmake

我仍然不完全了解使用源代码的嵌套文件夹的深层次结构配置CMake项目的正确方法。这里,为MSVC 2013配置

以下是此类项目的示例:

+RootFolder(no code)
    +NestedFolder1(no code)
        +NestedNestedFolder1(has code .h and .cpp)
        +NestedNestedFolder2(has code and nested folder with code)
             +NestedNestedNestedFolder1(has code .h and .cpp)

我在CMake中配置它的方式:

RootFolder CMakeLists:

project(MyDemoCmakeProject)
add_subdirectory(NestedFolder1)

NestedFolder1 CMakeLists:

 add_subdirectory(NestedNestedFolder1)
 add_subdirectory(NestedNestedFolder2)     
 add_definitions(.....)
 include_directories(.....)

NestedNestedFolder1 CMakeLists:

 set(mysources ${sources})
 add_library(MyLib STATIC ${mysources })

NestedNestedFolder2 CMakeLists:

 subdirs(NestedNestedNestedFolder1)
 set(mysources ${sources})
 add_library(MyLib STATIC ${mysources })

NestedNestedNestedFolder1 CMakeLists:

 set(mysources ${sources})
 add_library(MyLib STATIC ${mysources })

运行CMake时使用此配置我得到:

  

add_library无法创建目标" MyLib"因为另一个目标   同名已存在。现有目标是静态库   在源目录NestedNestedFolder1

中创建

现在,如果我删除      add_subdirectory(NestedNestedFolder1)      add_subdirectory(NestedNestedFolder2)

来自NestedFolder1 CMakeLists.txt它运行正常。我不完全理解这一刻。例如在NestedNestedFolder2中我也为嵌套的子目录做了add_subdirectory但是它没有抱怨。我也没有&# 39;了解CMake如何能够揭示' NestedFolder1的子目录,如果我不是add_subdirectory()那些。从不同的例子中我了解到每个具有源子目录的目录必须用subdirs()add_subdirectory()公开它。我想念这里?

2 个答案:

答案 0 :(得分:2)

subdirs是添加子目录的旧方法,已弃用。 CMake docs说要使用add_subdirectory代替。 subdirs的行为与add_subdirectory略有不同,因为前者还会搜索子目录中的CMakeLists.txt个文件并自动添加它们。

有一些方法可用于管理深层次结构,例如您描述的方案。按照我个人的偏好顺序:

方法1:

这需要CMake 3.1或更高版本,因为它使用target_sources命令。您可以在顶层定义可执行目标,然后在每个子目录中调用target_sources以向其添加源。您还可以根据需要在每个子目录中使用target_include_directoriestarget_compile_definitions,而不是在顶层调用add_definitionsinclude_directories,以使这些依赖项附加到目标而不是应用于一切(如果你以后在顶级CMakeLists.txt文件中添加了更多内容,那么这里真的很重要)。对于您的场景,它看起来类似于以下内容(为了说明目的,我插入了虚拟源文件名和编译定义):

RootFolder CMakeLists:

project(MyDemoCmakeProject)

# add_library() requires a source file argument to be
# present, but it can be an empty string if there are
# no source files, headers, etc. that should be added
# from this directory.
add_library(MyLib STATIC "")
add_subdirectory(NestedFolder1)

NestedFolder1 CMakeLists:

add_subdirectory(NestedNestedFolder1)
add_subdirectory(NestedNestedFolder2)

NestedNestedFolder1 CMakeLists:

target_sources(MyLib PRIVATE
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN1a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN1b.cpp"
)
target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

NestedNestedFolder2 CMakeLists:

add_subdirectory(NestedNestedNestedFolder1)
target_sources(MyLib PRIVATE
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN2a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN2b.cpp"
)
target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_definitions(MyLib PUBLIC -DSOME_VAL=42)

NestedNestedNestedFolder1 CMakeLists:

target_sources(MyLib PRIVATE
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNNN1a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNNN1b.cpp"
)
target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

这种方法的优点是每个子目录都是自包含的。上层不必知道较低层正在做什么。此外,信息在该信息最相关/已知的级别添加到目标。一个缺点是你必须添加每个源的完整路径,但这是一个小小的不便。

方法2:

使用变量来累积源文件名,并在包含所有子目录后在顶层添加该变量的内容,而不是使用target_sources。我们也使用include而不是add_subdirectory,以便我们的变量始终在同一范围内进行操作。由于之后拉入子目录时未定义目标,因此您也无法使用target_compile_definitionstarget_include_directories,因此这些也必须通过变量进行处理。

RootFolder CMakeLists:

project(MyDemoCmakeProject)

set(MyLib_SOURCES)
set(MyLib_INCLUDE_DIRS)
set(MyLib_DEFINITIONS)
include(NestedFolder1/CMakeLists.txt)
add_library(MyLib STATIC ${MyLib_SOURCES})
target_include_directories(MyLib PUBLIC ${MyLib_INCLUDE_DIRS})
target_compile_definitions(MyLib PUBLIC ${MyLib_DEFINITIONS})

NestedFolder1 CMakeLists:

include(NestedNestedFolder1/CMakeLists.txt)
include(NestedNestedFolder2/CMakeLists.txt)

NestedNestedFolder1 CMakeLists:

list(APPEND MyLib_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN1a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN1b.cpp"
)
list(APPEND MyLib_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")

NestedNestedFolder2 CMakeLists:

include(NestedNestedNestedFolder1/CMakeLists.txt)
list(APPEND MyLib_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN2a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNN2b.cpp"
)
list(APPEND MyLib_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
list(APPEND MyLib_DEFINITIONS -DSOME_VAL=42)

NestedNestedNestedFolder1 CMakeLists:

list(APPEND MyLib_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNNN1a.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/fileNNN1b.cpp"
)
list(APPEND MyLib_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")

这不如方法1的主要原因是通过变量携带所有源,头路径和编译定义使其更脆弱。所有子目录都必须运行良好,不要使用相同的变量名称来处理其他任何事情(小问题)和处理嵌入空间的路径需要更多的关注(棘手的问题)。另一个缺点是必须更仔细地处理可变范围。任何add_subdirectory调用都会改变范围。这种方法优于方法1的一个优点是它适用于许多旧版本的CMake。

方法3:

另一种方法是在每个子目录级别定义一个库,并对其进行顶级库链接。这些子目录级库可以是静态库或共享库,也可以是对象库(这需要更新的CMake版本)。这种方法的优点是它甚至可以用于旧版本的CMake。缺点取决于您的观点。这将导致定义更多的目标,并且可能降低并行构建的有效性。对于少数子目录,我建议这种方法可能实际上是最好的,但是一旦你超越了几个子目录,事情就会很快开始失控。

答案 1 :(得分:0)

问题是您有多个具有相同名称的目标:" MyLib"。只需给每个库一个唯一的名称,它应该可以工作。