检查cmake中可选的目标,这些目标的顺序不正确

时间:2015-09-24 16:10:55

标签: c++ cmake

我目前正在开发一个使用cmake作为构建系统的大型软件项目。但我有一个问题,检查是否存在(或将存在)另一个目标。

例如,有一个根CMakeLists.txt和两个可以选择作为子文件夹添加到软件项目中的模块。

cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
project(root)
add_subdirectory(./A)
add_subdirectory(./B)

在根CMakeLists中,这些模块随add_subdirectory命令一起添加:

cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
project(A)
add_libary(A a.cpp a.hpp)
if (TARGET B)
    target_compile_definitions(A PUBLIC HAVE_B)
endif()

在某些情况下,如果模块B存在,我想检查模块A,并为模块A中的编译选项添加一个define:

if(TARGET target-name)

from A3 import db_access



        allareas = db_access.get_all_areas()
        #print(allareas)


        template = "{:>5}   {:30} {:>15}  {:>15} "
        row = template.format("ID", "Name", "Num Loc", "Avg Value", "Categories")
        print(row)



        for x in allareas:
            area_id = x['area_id']
            name = x['name']
            row = template.format(area_id, name)
            print(row)

命令将返回false,因为仅当模块以正确的顺序添加到根CMakeLists.txt时才会起作用。

cmake中是否有其他检查不依赖于目标的顺序?

问候 佩里

3 个答案:

答案 0 :(得分:1)

这可以使用宏来解决。

这个想法是每个子目录必须有一个其他子目录可以检查的关联顶级变量,因此必须在顶级CMakeLists.txt文件中声明它们。此外,所有变量都必须在add_subdirectory规则开始之前定义。

因此我们的宏将把子目录列表作为参数,首先输出变量声明列表,然后输出子目录列表。

以下是此类宏的代码:

macro(declare_modules)
    foreach(module ${ARGN})
        set(TARGET_${module} yes)
    endforeach()

    foreach(module ${ARGN})
        add_subdirectory(./${module})
    endforeach()
endmacro(declare_modules)

可以像这样使用:

declare_modules(A B C)

它将扩展为以下代码:

foreach(module A B C)
    set(TARGET_${module} yes)
endforeach()

foreach(module A B C)
    add_subdirectory(./${module})
endforeach()

这将导致变量TARGET_ATARGET_BTARGET_C被声明并设置为yes,并且它们将可从所有模块中获得。

添加新模块只需在declare_modules调用中添加新参数。

答案 1 :(得分:1)

因为根CMakeLists.txt文件必须知道项目的子目录,所以我通常会将可选设置放在那里:

<强>的CMakeLists.txt

cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)

project(root CXX)

if (EXISTS "B/CMakeLists.txt")
    set(HAVE_B 1)
endif()

add_subdirectory(A)
if (HAVE_B)
    add_subdirectory(B)
endif()

<强> A /的CMakeLists.txt

add_library(A a.cpp a.hpp)
if (HAVE_B)
    target_link_libraries(A B)
endif()

<强> B /的CMakeLists.txt

add_library(B b.cpp b.hpp)
target_compile_definitions(B PUBLIC HAVE_B)
target_include_directories(B PUBLIC .)

有很多可能的地方可以设置编译器定义和必要的包含目录。在此示例中,我选择使用target_compile_definitions(... PUBLIC ...)target_include_directories(... PUBLIC ...)进行宣传。然后你只需要用target_link_libraries()设置依赖关系,然后CMake处理其余的(target_link_libraries()接受前向声明)。

答案 2 :(得分:0)

没有可用的$<TARGET_EXISTS:...>生成器表达式,您可以在其中写下:

add_libary(A a.cpp a.hpp)
target_compile_definitions(A PUBLIC $<<TARGET_EXISTS:B>:HAVE_B=1>)

虽然我向cmake提议,here。作为解决方法,您可以创建阴影目标以跟踪其他目录中的目标:

# Custom property to check if target exists
define_property(TARGET PROPERTY "INTERFACE_TARGET_EXISTS"
    BRIEF_DOCS "True if target exists"
    FULL_DOCS "True if target exists"
)

# Create shadow target to notify that the target exists
macro(shadow_notify TARGET)
    if(NOT TARGET _shadow_target_${TARGET})
        add_library(_shadow_target_${TARGET} INTERFACE IMPORTED GLOBAL)
    endif()
    set_target_properties(_shadow_target_${TARGET} PROPERTIES INTERFACE_TARGET_EXISTS 1)
endmacro()
# Check if target exists by querying the shadow target
macro(shadow_exists OUT TARGET)
    if(NOT TARGET _shadow_target_${TARGET})
        add_library(_shadow_target_${TARGET} INTERFACE IMPORTED GLOBAL)
        set_target_properties(_shadow_target_${TARGET} PROPERTIES INTERFACE_TARGET_EXISTS 0)
    endif()
    set(${OUT} "$<TARGET_PROPERTY:_shadow_target_${TARGET},INTERFACE_TARGET_EXISTS>")
endmacro()

这会创建两个函数,以及一个自定义属性,用于跟踪目标是否存在。 shadow_notify如果不存在则会创建一个影子目标,然后将INTERFACE_TARGET_EXISTS属性设置为true(如果它存在)。

shadow_exists目标将创建一个生成器表达式来查询目标是否存在。如果在INTERFACE_TARGET_EXISTS属性设置为0的情况下不存在,则通过创建新的阴影目标来执行此操作。然后,它将创建一个生成器表达式,该表达式将查询阴影目标上的INTERFACE_TARGET_EXISTS属性。阴影目标将始终存在,如果实际目标存在,则属性将由shadow_notify设置为1。

那么你可以在你的cmake中写下这个:

add_libary(A a.cpp a.hpp)
# Notify that target A exists
shadow_notify(A)
# Check if target B exists
shadow_exists(HAS_B B)
target_compile_definitions(A PUBLIC $<${HAS_B}:HAVE_B=1>)

创建库B时,您还需要调用shadow_notify(B)

如果您不喜欢这些额外的shadow_notify来电,您还可以覆盖add_library为您执行此操作:

macro(add_library LIB)
    _add_library(${LIB} ${ARGN})
    if(NOT TARGET _shadow_target_${TARGET})
        _add_library(_shadow_target_${TARGET} INTERFACE IMPORTED GLOBAL)
    endif()
    set_target_properties(_shadow_target_${TARGET} PROPERTIES INTERFACE_TARGET_EXISTS 1)
endmacro()