How to find all the include folders for a target for a static analyzer?

We want to add a the cppcheck static analyzer to a product and it has input arguments such as:

cppcheck <files> \
        --xml \
        --force \
        --output-file=results\cppcheck \
        --error-exitcode=1 \
        --enable=performance \
        -I include/ \
        -I internal/ \
        -I ...

For a given target it’s pretty easy to get a list of all the source files.

However, I’m not able to easily get all of the -I folders.

I thought it might be the contents of the INCLUDE_DIRECTORIES property, but that doesn’t seem to have the include folders that come from packages we’ve added using find_package() and target_link_libraries(...package)

I checked the INCLUDE_DIRECTORIES property using message()

I added logic to the end of my cmakefile that used get_property() to get the targets INCLUDE_DIRECTORIES value and then print it using message()

Any suggestions on how to proceed?

Hmmmm… so reading about generator expressions I found a section that uses INCLUDE_DIRECTORIES as the example.

I made a custom command

add_custom_command(TARGET ${TARGET_LIB}
    PRE_BUILD
    COMMAND ECHO "===================================================="
    COMMAND echo "foo -I $<JOIN:$<TARGET_PROPERTY:${TARGET_LIB},INCLUDE_DIRECTORIES>, -I >"
    COMMAND ECHO "===================================================="
)

And it is indeed showing me all of the include directories.

However, if I do

    get_property(propVal TARGET ${TARGET_LIB} PROPERTY INCLUDE_DIRECTORIES)
    if (propVal)
        message("${target}-INCLUDE_DIRECTORIES:   ${propVal}")
    endif()

I only get one include folder. Why would I get different values?

The INCLUDE_DIRECTORIES property at configure time is just the set added explicitly via target_include_directories (and maybe include_directories, though that may just be stored on the directory and not gathered until generate time). At generate time, the set of linked libraries is finally known and usage requirements from the targets that are linked may be looked up and expanded (with, e.g., platform detection genexes expanded too). It’s not something you can query at configure time, so your add_custom_command is one way to do it properly.

You might be able to use CMAKE_EXPORT_COMPILE_COMMANDS to generate compile_commands.json and have cppcheck use that instead.

Thank you Ben!

I didn’t realize there was this sort of difference for properties during configuration time and generation time.

I will need to re-think some of the debugging methods I’ve used in the past.

We were able to make a custom command cppcheck that uses the INCLUDE_DIRECTORIES property and it’s working great!

We’re still experimenting with the command and syntax but we have:

add_custom_target(
        cppcheck
        COMMAND /usr/bin/cppcheck
        "${CMAKE_CURRENT_LIST_DIR}/source/*.cpp"
        "--xml"
        "--force"
        "--output-file=${CMAKE_CURRENT_LIST_DIR}/results/"
        "--error-exitcode=1"
        "--enable=performance"    
        "-I $<JOIN:$<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>, -I >"
) 

Which we can invoke with

cmake --build . -v --target cppcheck

And it’s happily working!