3

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

我的总体想法是将静态代码分析工具合并到 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 ()

稍后在函数中添加自定义命令:

 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中查看目标时,只添加了第一个包含/函数调用命令。

如果只包含文件而不调用函数,则根本不添加任何自定义命令。

期望的行为:

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

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

4

1 回答 1

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()

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

但是,如果从父级 CMakeLists.txt 调用该函数,则文件会丢失其属性,因此不会添加任何内容,并且 cppcheck 会传递一个空列表!


现在进行可能的修复。摆脱这个漏洞的方法可能很少——我可以指出几个。

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

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})
于 2013-10-22T21:46:51.197 回答