PC-Lint

From KitwarePublic
Jump to: navigation, search

CMake script for generating PC-Lint targets

add_pc_lint is a CMake macro for generating build targets for PC-Lint.

If you use it, you will get a build target invoking pc-lint for each source file with the same include directories and preprocessor defines as for the compiler. The lint build target will have the same name as your original build target, but with _LINT appended. You will also get the build target ALL_LINT which invokes all *_LINT targets. The *_LINT targets will not be built with ALL_BUILD. Only if they are directly built or if ALL_LINT is built.

This setup integrates PC-Lint into your existing setup. It has been tested with Visual Studio 8 and NMake Makefiles.

Usage

If you want to use this macro, you need:

  • an existing CMake setup for your project
  • PC-Lint installed
  • configuration files for PC-Lint

Synopsis

add_pc_lint(target source1 [source2 ...])

target
the name of the target to which the sources belong. You will get a new build target named ${target}_LINT
source1 source2 ...
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 (or library) like this:

 set(MAIN_SOURCES main.c foo.c bar.c)
 add_executable(main ${MAIN_SOURCES})

include this file (only once, preferrably at the top level CMakeLists.txt)

 include(/path/to/pc_lint.cmake)

and add a command to generate the main_LINT target

if(COMMAND add_pc_lint)
 add_pc_lint(main ${MAIN_SOURCES})
endif(COMMAND add_pc_lint)

Configuration

The pc_lint.cmake script exports the following CMake cache variables, which you have to adjust using the CMake tools to fit your environment.

PC_LINT_EXECUTABLE
Full path to the pc-lint executable. Not the generated lin.bat. On a standard PC-Lint installation, this path is c:/lint/lint-nt.exe
PC_LINT_CONFIG_DIR
Full path to the directory containing pc-lint configuration files. For each Platform/Compiler/Toolchain, you may want to have a different set of options. If you keep these options in separate directories, you can point this variable to the appropriate one. add_pc_lint assumes that there is a file called std.lnt in the configuration directory which includes all further lint option files.
PC_LINT_USER_FLAGS
Additional pc-lint command line options. Some flags of pc-lint cannot be set in option files (most notably -b).

Source

# 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
    set(lint_include_directories_transformed)
    foreach(include_dir ${lint_include_directories})
        list(APPEND lint_include_directories_transformed -i"${include_dir}")
    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}
                ${lint_include_directories_transformed}
                ${lint_defines_transformed}
                ${sourcefile_abs})
        endif()
    endforeach(sourcefile)

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

endfunction(add_pc_lint)