Clang Tidy, how to set options?

Hi,
I can run clang-tidy using below, but it only run default checks. How can i configure clang-tidy options?

set_target_properties(${PROJECT_NAME} PROPERTIES
        CXX_CLANG_TIDY clang-tidy
)

Documentation states below but i dont get where/how to define the clang tidy arguments.

"Specify a semicolon-separated list containing a command line for the clang-tidy tool. The Makefile Generators and the Ninja generator will run this tool along with the compiler and report a warning if the tool reports any problems.

The specified clang-tidy command line will be invoked with additional arguments specifying the source file and, after --, the full compiler command line."
https://cmake.org/cmake/help/latest/prop_tgt/LANG_CLANG_TIDY.html

I haven’t tried this, though following the documentation I read it as:

set_target_properties(${PROJECT_NAME} PROPERTIES
        CXX_CLANG_TIDY clang-tidy;--checks=-*,modernize-*;--verbose
)

You’re on the right track, but need a slightly different syntax. set_target_properties() is best suited for single-value properties, because it interprets its arguments as key1 value1 key2 value2… Your command as written would set property CXX_CLANG_TIDY to value clang-tidy and then property named --checks=-*,modernize-* to value --verbose. If you want to use set_target_properties(), you need appropriate quoting:

set_target_properties(${PROJECT_NAME} PROPERTIES
        CXX_CLANG_TIDY "clang-tidy;--checks=-*,modernize-*;--verbose"
)

However, I strongly suggest to use set_property() instead for multi-value properties:

set_property(TARGET ${PROJECT_NAME} PROPERTY
  CXX_CLANG_TIDY
    clang-tidy --checks=-*,modernize-* --verbose
)

I find the latter much more readble and maintainable.

Note that semicolons are not needed in the latter case as the multiple arguments for the property value form a CMake list, and CMake lists are semicolon-seprated internally (which is also the reason why CXX_CLANG_TIDY uses a semicolon-separated list itself).

Thx, this works without verbose option (i assume --verbose is a clang-tidy error not Cmake).

But i want clang-tidy to only run with Clang compiler in a multiconfig. I build/test project using several compilers and clang-tidy gives error with GNU and Intel DPC that is not there when using Clang compiler. How/can i condition clang-tidy using generator expression?
I tried below without success:

set_property(TARGET ${PROJECT_NAME} PROPERTY
        $<$<AND:$<COMPILE_LANG_AND_ID:CXX,Clang>,$<CONFIG:Debug>>:CXX_CLANG_TIDY "clang-tidy;--checks=*,modernize*">)

and:

set_target_properties(${PROJECT_NAME} PROPERTIES
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,Clang>,$<CONFIG:Debug>>:CXX_CLANG_TIDY "clang-tidy;--checks=*,modernize*">)

About the particular arguments, I took them from the earlier post in this thread; I failed to notice it wasn’t yours.

Regarding compiler selection: do you really need to do this using genexes? Those are great for things which are not fully known at configure time, or for things which are exported for consumers of your project (such as library usage requirements).

However, using clang-tidy applies only to building your project itself, and the compiler used is known at configure time. You can therefore wrap the whole setup in a normal if(), which is much easier than working with genexes:

if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  set_property(TARGET ${PROJECT_NAME} PROPERTY
    CXX_CLANG_TIDY
      clang-tidy --checks=*,modernize*
  )
endif()

If you still need a genex there (e.g. to only run cang-tidy in certain configurations), you need to ensure the whole genex remains one configure-time argument. Perhaps like this (not tested):

set_property(TARGET ${PROJECT_NAME} PROPERTY
  CXX_CLANG_TIDY
    "$<$<CONFIG:Debug>:clang-tidy;--checks=*,modernize*>"
)

Of course, then we’re back at needing explicit semicolon separation and quoting. That’s a downside of genexes, and why I feel they shouldn’t be overused.

2 Likes

…do you really need to do this using genexes? …
For single config generators true it does not matter. But multi-config generators ignores compiler and build type. Generator expressions solves that.

This works!

It helped me to come up with below that works perfectly.

set_target_properties(${PROJECT_NAME} PROPERTIES
       CXX_CLANG_TIDY "$<$<AND:$<COMPILE_LANG_AND_ID:CXX,Clang>,$<CONFIG:Debug>>:clang-tidy;--checks=*,modernize*>")

All good super Thx!

If you want to be pedantic, technically the genex that checks for debug configs should get the value of the global property DEBUG_CONFIGURATIONS and check if the build config is one of the ones in that list:

get_cmake_property(debug_configs DEBUG_CONFIGURATIONS)

set (config_is_debug "$<IN_LIST:$<CONFIG>,${debug_configs}>")

set (clang_tidy_args
clang-tidy --checks=*,modernize*)

set_property(TARGET "${PROJECT_NAME}" PROPERTY 
  CXX_CLANG_TIDY 
    "$<${config_is_debug}:${clang_tidy_args}>"
)

I agree that genexes shouldn’t be overused, but when you do need to use them, you can build them up through variables, as shown above

1 Like

Interesting, thx for the depth on the subject.

I am actually doing a separate build just for clang-tidy due to all noise it generates, separating debug build from clang tidy build. I use/trigger builds using Cmake presets that has lots of different configs, i build using tree different compilers, sanitizers plus standard release/debug/relwinfo and now clang-tidy. So this is actually my clang-tidy property.

CXX_CLANG_TIDY "$<$<AND:$<COMPILE_LANG_AND_ID:CXX,Clang>,$<CONFIG:Clang_tidy>>:clang-tidy;--checks=*,modernize*>"

And my Cmake preset is:

    {
      "inherits": [
        "common-values",
        "Clang"
      ],
      "name": "Clang_tidy",
      "displayName": "Clang Tidy",
      "description": "Clang tidy build using Ninja generator",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Clang_tidy",
        "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-O2 -g"
      }
    }

It works but if it has side effects im open to improvements, does it make sense?

1 Like

Yes I think this makes sense, I think that adding custom build configurations is an underutilized feature, but quite appropriate for things like sanitizers, CI/static analysis builds, etc, so I think this use case definitely makes sense.

1 Like