将CMake与具有钻石依赖性的库

时间:2015-10-12 16:39:22

标签: cmake

假设我有四个独立的项目。其中三个是库CommonFooBar,其中一个是可执行文件AppFooBar都取决于公共库,App取决于FooBar。此外,其中一些项目有一些脚本需要运行以生成一些头文件和源文件。

现在我有很多这样的电话:

if (NOT TARGET common)
  add_subdirectory(${common_lib_directory})
endif()

但这并不是一个合适的解决方案。如果我没有把它包裹在那里,如果后卫,那么就会出现错误,因为它试图不止一次地构建Common。如果每个项目的CMakeLists中的警卫也不正确。我应该为每个库编写Find<Library>脚本吗?我已经尝试过如何设置我的CMakeLists文件的示例或最佳实践,但我能找到的唯一例子要么太简单,要么涵盖完全不同的用例。

评论中要求我发布一些代码。这或多或少是我的CMakeLists.txt文件的样子:

cmake_minimum_required(VERSION 2.8.12)

project(app)

# Temporary files (like object files) created while compiling projects.
set(tmp_dir ${CMAKE_BINARY_DIR}/obj)

# Directory which contains the source for 3rd party libraries.
if(NOT DEFINED dependencies_root)
  get_filename_component(
    dependencies_root "${CMAKE_CURRENT_SOURCE_DIR}/../../../../external"
    REALPATH)
endif()

set(dependencies_foo_dir "${dependencies_root}/foo"
    CACHE PATH "Directory containing the foo library.")
set(dependencies_bar_dir "${dependencies_root}/bar"
    CACHE PATH "Directory containing the bar library.")

if(NOT TARGET foo)
  add_subdirectory("${dependencies_foo_dir}" ${tmp_dir}/foo)
endif()

if(NOT TARGET bar)
  add_subdirectory("${dependencies_bar_dir}" ${tmp_dir}/bar)
endif()

include_directories(${dependencies_foo_dir}/include)
include_directories(${foo_generated_include_dir})

include_directories(${dependencies_bar_dir}/include)
include_directories(${bar_generated_include_dir})

set(app_srcs ...)

add_executable(app ${app_SRCS})

target_link_libraries(app foo bar common)

如前所述,我拥有if (NOT TARGET blah)警卫的原因是因为如果我不这样做,我会收到这样的错误:

CMake Error at /path/to/my/project/CMakeLists.txt:267 (add_library):
  add_library cannot create target "blah" because another target with the
  same name already exists.  The existing target is a static library created
  in source directory
  "/path/to/blah".
  See documentation for policy CMP0002 for more details.

1 个答案:

答案 0 :(得分:1)

如果您有这样紧密相关的项目,那么最佳决策似乎是在每个项目开始时使用 guard来重新包含。可以使用return command轻松实现此类保护,{{3}}从当前执行的add_subdirectory()调用返回:

<强>富/的CMakeLists.txt

if(DEFINED Foo_GUARD)
    if(NOT Foo_GUARD STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
        return() # Project has been already included by someone else
    endif()
else()
    set(Foo_GUARD ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "Foo guard")
endif()

project(Foo)

...

所以任何项目都可以使用不受保护的 add_subdirectory()调用包含给定的一个:

应用/的CMakeLists.txt

...
# Need *Foo* functionality? Simply include it!
add_subdirectory("${dependencies_foo_dir}" ${tmp_dir}/foo)

可以将guard作为宏实现,将其放入库中并在每个项目中使用它:

<强>的cmake / utils.cmake

macro(project_guarded name)
    if(DEFINED ${name}_GUARD)
        if(NOT ${name}_GUARD STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
            return() # return() *doesn't* terminate a macro!
        endif()
    else()
        set(${name}_GUARD ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "${name} guard")
    endif()
    project(${name})
endmacro()

宏用法很简单:

project_guarded(Foo)