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



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

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

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

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

CMake 中有没有办法递归检索所有包含路径,或者递归检索所有依赖项?或者有谁知道这个问题的另一种解决方案?

谢谢!

这里的一般思路:

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 调用一起使用。

在这里,您可以找到要与构建项目一起使用的模块。只需将以下行添加到您的 CMakeList 中.txt

find_package(Lint)
... define your targets
add_pc_lint( target_name ${variable with list of your source files to check} )

我自己找到了解决问题的方法。在使用"NMake Makefiles"作为生成器时,CMake 生成的文件中,每个目标都有一个名为"DependInfo.cmake"的文件。该文件位于每个目标的CMakeFiles/.dir/DependInfo.cmake中。在该文件中,有一个名为"CMAKE_CXX_TARGET_INCLUDE_PATH"的变量,其中包含目标所需的所有包含路径。我最终做的是一个 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)

最新更新