PC-Lint需要包含要扫描的文件的所有包含路径的列表。如何获取CMake中目标递归所需的所有包含路径的列表?

时间:2016-01-28 17:41:22

标签: recursion cmake include pc-lint

我在一个项目中,CMake用于管理构建过程。在项目中有几个可执行文件依赖于组件,这些组件构建为静态库。这些组件之间也存在依赖关系。对于每个可执行文件或组件,仅指定了自己的本地包含,并使用target_link_libraries(<target> <dependencies>)解析包含依赖项的包含。

到目前为止一切顺利。问题是PC-Lint应该集成到环境中。对于每个组件,我们正在设置PC-Lint目标,该目标在该组件的头文件/源文件上运行静态代码分析。问题是PC-Lint需要一个文件作为输入,该文件应包含所分析文件所需的所有包含路径。由于组件依赖于其他组件,我们必须以某种方式递归地检索所有包含路径以获得所需的所有包含。我们希望使用与target_link_libraries相同的机制来解析PC-Lint的包含路径。这可能吗?

由于我们有一个每个组件的依赖项列表,我们可以从这些依赖项中获取包含路径,并将它们包含在PC-Lint的文件中。但真正的问题是,component1中的source_file1.cpp包含来自component2的header_file2.h,而component2又包含来自组件3的header_file3.h。在该示例中,PC-lint会抱怨header_file3.h,因为component3的include路径不包含在提供给PC-lint的文件,原因是组件1中没有依赖组件3(target_link_library()通常解决那些递归依赖项。)

从一开始我们就认为在项目中创建包含所有包含路径的文件就可以了。在这种情况下的问题是有几个具有相同名称的文件(例如:main.h),而PC-Lint将选择错误的文件。

CMake中是否有一种方法可以递归地检索所有包含路径,或者递归地检索所有依赖关系?或者有人知道这个问题的另一种解决方案吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

这里的一般想法是:

GET_DIRECTORY_PROPERTY(_inc_dirs INCLUDE_DIRECTORIES)

foreach(_one_inc_dir ${_inc_dirs})
    list(APPEND PROJECT_INCLUDE_DIRS "-i\"${_one_inc_dir}\"")
endforeach(_one_inc_dir)

然后将PROJECT_INCLUDE_DIRS与PC-Lint调用一起使用。

您可以在此处找到与您的构建项目一起使用的module。 只需在CMakeLists.txt

中添加以下行
find_package(Lint)

... define your targets

add_pc_lint( target_name ${variable with list of your source files to check} )

答案 1 :(得分:0)

我自己找到了解决问题的方法。在CMake使用&#34; NMake Makefiles&#34;生成的文件中作为生成器,每个目标都有一个名为&#34; DependInfo.cmake&#34;的文件。该文件位于每个目标的CMakeFiles / .dir / DependInfo.cmake中。在该文件中有一个名为&#34; CMAKE_CXX_TARGET_INCLUDE_PATH&#34;的变量。其中包含目标所需的所有包含路径。我最终做的是一个powershell脚本解析该文件,然后在该变量中挑选出那些路径并将其输出到PC-Lint想要的格式的文件中。

Powershell脚本:

$projectRoot    = $args[0]
$dependInfoFile = $args[1]
$output         = $args[2]

if (Test-Path $dependInfoFile)
{
    $lines = Get-Content $dependInfoFile

    Remove-Item $output -Force -ErrorAction SilentlyContinue
    foreach ($line in $lines)
    {
        if ($line -eq "set(CMAKE_CXX_TARGET_INCLUDE_PATH")
        {
            $printToFile = $TRUE
        }
        elseif ($printToFile -and ($line -eq "  )"))
        {
            $printToFile = $FALSE
            break
        }
        elseif ($printToFile)
        {
            $line = $line.trim()
            $line = $line.Substring(1,$line.length-2)
            while ($line.Substring(0,3) -eq "../")
            {
                $line = $line.SubString(3)
            }
            $outLine = "-i`"$projectRoot`/$line`""
            Add-Content $output $outLine
        }
    }  
}

对于PC-lint,我添加了一个custom_target,它使用正确的DependInfo.cmake文件和输出文件的名称运行此powershell脚本。此custom_target作为依赖项添加到普通lint目标,这导致在linting之前生成输出文件。 (请注意,可能需要更改某些路径)

FindLint.cmake FindLint.cmake由cmake提供,但已被修改)

# This file contains functions and configurations for generating PC-Lint build
# targets for your CMake projects.

set(PC_LINT_EXECUTABLE "c:/lint/lint-nt.exe" CACHE STRING "full path to the pc-lint executable. NOT the generated lin.bat")
set(PC_LINT_CONFIG_DIR "c:/lint/" CACHE STRING "full path to the directory containing pc-lint configuration files")
set(PC_LINT_USER_FLAGS "-b" CACHE STRING "additional pc-lint command line options -- some flags of pc-lint cannot be set in option files (most notably -b)")

# a phony target which causes all available *_LINT targets to be executed
add_custom_target(ALL_LINT)

# add_pc_lint(target source1 [source2 ...])
#
# Takes a list of source files and generates a build target which can be used
# for linting all files
#
# The generated lint commands assume that a top-level config file named
# 'std.lnt' resides in the configuration directory 'PC_LINT_CONFIG_DIR'. This
# config file must include all other config files. This is standard lint
# behaviour.
#
# Parameters:
#  - target: the name of the target to which the sources belong. You will get a
#            new build target named ${target}_LINT
#  - source1 ... : a list of source files to be linted. Just pass the same list
#            as you passed for add_executable or add_library. Everything except
#            C and CPP files (*.c, *.cpp, *.cxx) will be filtered out.
#
# Example:
#  If you have a CMakeLists.txt which generates an executable like this:
#
#    set(MAIN_SOURCES main.c foo.c bar.c)
#    add_executable(main ${MAIN_SOURCES})
#
#  include this file
#
#    include(/path/to/pc_lint.cmake)
#
#  and add a line to generate the main_LINT target
#
#   if(COMMAND add_pc_lint)
#    add_pc_lint(main ${MAIN_SOURCES})
#   endif(COMMAND add_pc_lint)
#
function(add_pc_lint target)
    get_directory_property(lint_include_directories INCLUDE_DIRECTORIES)
    get_directory_property(lint_defines COMPILE_DEFINITIONS)

    # let's get those elephants across the alps
    # prepend each include directory with "-i"; also quotes the directory
    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/lint_include/${target}.lnt "")
    foreach(include_dir ${lint_include_directories})
        file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/lint_include/${target}.lnt -i"${include_dir}"\n)
    endforeach(include_dir)

    # prepend each definition with "-d"
    set(lint_defines_transformed)
    foreach(definition ${lint_defines})
        list(APPEND lint_defines_transformed -d${definition})
    endforeach(definition)

    # list of all commands, one for each given source file
    set(pc_lint_commands)

    foreach(sourcefile ${ARGN})
        # only include c and cpp files
        if( sourcefile MATCHES \\.c$|\\.cxx$|\\.cpp$ )
            # make filename absolute
            get_filename_component(sourcefile_abs ${sourcefile} ABSOLUTE)
            # create command line for linting one source file and add it to the list of commands
            list(APPEND pc_lint_commands
                COMMAND ${PC_LINT_EXECUTABLE}
                -i"${PC_LINT_CONFIG_DIR}" std.lnt
                "-u" ${PC_LINT_USER_FLAGS}
                "${CMAKE_CURRENT_BINARY_DIR}/lint_include/${target}.lnt"
                ${lint_defines_transformed}
                ${sourcefile_abs})
        endif()
    endforeach(sourcefile)

    add_custom_target(${target}_LINT_INCLUDE
        powershell.exe -File \"${CMAKE_MODULE_PATH}/generate_lint_include.ps1\" ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir/DependInfo.cmake ${CMAKE_CURRENT_BINARY_DIR}/lint_include/${target}.lnt)

    # add a custom target consisting of all the commands generated above
    add_custom_target(${target}_LINT ${pc_lint_commands} VERBATIM)
    add_dependencies(${target}_LINT ${target}_LINT_INCLUDE)
    # make the ALL_LINT target depend on each and every *_LINT target
    add_dependencies(lint ${target}_LINT)

endfunction(add_pc_lint)