2013-10-22 77 views
3

我遇到了自定义目标(使用add_custom_target创建)的add_custom_command问题。cmake:只有第一次调用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) 

我看到的问题是该命令仅添加了包含该文件的项目。我不知道为什么和发生了什么。我核实了以下使用消息()命令:

  • 目标只创建一次
  • 的add_custom_command运行对每个调用该函数的项目,在适当的参数

但是当我真正在Visual Studio中查看目标,只添加第一个包含/函数调用命令。

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

期望的行为:

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

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

回答

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}) 
+0

我想我可能一直不清楚是什么其实我是想发生,并且更新了我原来的职位。但我打算只有一个目标,但是在构建目标时会运行多个自定义命令。 – Max

+0

啊 - 好的。我会删除这个。 – Fraser

+0

@Max我重构了我的答案一点点:-) – Fraser