cmake:add_custom_command只在第一次调用

时间:2013-10-22 21:34:32

标签: visual-studio cmake

我遇到了自定义目标的add_custom_command问题(使用add_custom_target创建)。

我的一般想法是将静态代码分析工具整合到cmake工具链中。我的解决方案基于此处描述的解决方案:https://github.com/rpavlik/cmake-modules/blob/master/CppcheckTargets.cmake

简而言之,我想运行静态代码分析的每个项目都有以下两行代码:

include(cppcheck)
add_cppcheck(${le_project} STYLE POSSIBLE_ERROR FAIL_ON_WARNINGS)

模块位于文件顶部:

if (NOT TARGET ANALYZE_CODE)

    add_custom_target(ANALYZE_CODE WORKING_DIRECTORY ${LE_LITEN_ROOT})
    set_target_properties(ANALYZE_CODE PROPERTIES EXCLUDE_FROM_ALL TRUE)

  endif ()

以及稍后在函数中添加了custom命令:

 add_custom_command(TARGET
      ANALYZE_CODE
      PRE_BUILD
      COMMAND
      ${CPPCHECK_EXECUTABLE}
      ${CPPCHECK_QUIET_ARG}
      ${CPPCHECK_TEMPLATE_ARG}
      ${_cppcheck_args}
      ${_files}
      WORKING_DIRECTORY
      "${CMAKE_CURRENT_SOURCE_DIR}"
      COMMENT
      "${_name}_cppcheck: Running cppcheck on target ${_name}..."
      VERBATIM)

我看到的问题是该命令仅为包含该文件的项目添加。我不确定为什么以及发生了什么。我使用message()命令验证了以下内容:

  • 目标仅创建一次
  • 对每个调用该函数的项目运行add_custom_command,并使用正确的参数

但是当我实际查看visual studio中的目标时,只会添加第一个include / function call命令。

如果仅在不调用该函数的情况下包含该文件,则根本不会添加任何自定义命令。

所需行为

我想要一个名为“ANALYZE_CODE”的目标来运行通过调用函数添加的所有命令。

即。如果3个项目包含上面的两行,则创建目标ANALYZE_CODE一次,但会为其添加3个自定义命令,每个项目一个。

1 个答案:

答案 0 :(得分:3)

事实证明,你在这里的岩石和硬地之间有点卡住了。我认为这个问题归结为几个因素。

首先,虽然文档没有说清楚,但add_custom_command(TARGET ...)仅适用于在同一目录中创建的目标。因此,调用include(cppcheck)的第一个子项目是唯一可以有效地向目标ANALYZE_CODE添加自定义命令的子项目。

解决方法似乎是将所有对add_cppcheck的调用从各自的子目录移至顶级CMakeLists文件。

include(cppcheck)
add_cppcheck(${le_first_project} STYLE POSSIBLE_ERROR FAIL_ON_WARNINGS)
add_cppcheck(${le_second_project} STYLE POSSIBLE_ERROR FAIL_ON_WARNINGS)
...

这不是一个很好的解决方案,因为它们确实属于它们自己的子目录。但更大的问题是源文件上的属性只存在于添加它们的CMakeLists.txt的范围内。这根本不是显而易见的,而是来自set_source_files_properties的文档:

  

源文件属性仅对添加到同一目录(CMakeLists.txt)中的目标可见。

add_cppcheck的内部有以下代码块:

foreach(_source ${_cppcheck_sources})
  get_source_file_property(_cppcheck_lang "${_source}" LANGUAGE)
  get_source_file_property(_cppcheck_loc "${_source}" LOCATION)
  if("${_cppcheck_lang}" MATCHES "CXX")
    list(APPEND _files "${_cppcheck_loc}")
  endif()
endforeach()

所以这是检查给定目标的每个源文件是否被指定为C ++文件,然后将其添加到要提供给cppcheck的文件列表中。如果从定义目标的CMakeLists.txt(即子目录)中调用此函数,则文件都具有适当的属性并正确添加。

但是,如果从父CMakeLists.txt调用该函数,则文件已丢失其属性,因此没有添加任何文件,并且cppcheck被传递为空列表!


现在可以修复。可能很少有办法摆脱这个漏洞 - 我可以指出一对。

您可以继续选择始终从顶级CMake文件中调用add_cppcheck,并避免使用源文件的属性。因此,上面的问题代码块可以更改为更像:

set(CxxExtensions .cpp .CPP .cc .CC .cxx .CXX)
foreach(_source ${_cppcheck_sources})
  get_filename_component(Extension "${_source}" EXT)
  list(FIND CxxExtensions "${Extension}" IsCxxFile)
  if(IsCxxFile GREATER -1)
    list(APPEND _files "${_source}")
  endif()
endforeach()

你甚至可以通过在函数开头添加类似的东西来强制执行只从顶级CMakeLists.txt调用的函数:

if(NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
  message(FATAL_ERROR "This can only be called from the top-level CMakeLists.txt")
endif()


第二个修复(我个人赞成)是将add_cppcheck调用留在子目录中,并让函数添加自定义目标而不是命令。这些目标可以成功应用为顶级目标ANALYZE_CODE的依赖项。例如,将add_custom_command更改为:

add_custom_target(ANALYZE_${_name}
        ${CPPCHECK_EXECUTABLE}
        ${CPPCHECK_QUIET_ARG}
        ${CPPCHECK_TEMPLATE_ARG}
        ${_cppcheck_args}
        ${_files}
        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
        COMMENT "ANALYZE_${_name}: Running cppcheck on target ${_name}..."
        VERBATIM)
add_dependencies(ANALYZE_CODE ANALYZE_${_name})
set_target_properties(ANALYZE_${_name} PROPERTIES FOLDER "Code Analysis")

这会导致构建ANALYZE_CODE触发构建每个从属ANALYZE_...目标。

它有很多额外目标“污染”解决方案的缺点,但好处是你可能add_test电话中使用这些目标(尽管这可能是太过分了):

# CMake 2.8.0 and newer
add_test(NAME ${_name}_cppcheck_test
         COMMAND ${CMAKE_COMMAND}
             --build ${CMAKE_BINARY_DIR}
             --target ANALYZE_${_name})