Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cmake clang-tidy (or other script) as custom target

I am trying to create a custom cmake target for clang-tidy, to lint my project. The source folder looks something like this:

src/scripts/run-clang-tidy.py
src/.clang-tidy
src/...

So far my plan was to copy both these files to the build directory with a custom command:

add_custom_command(
    OUTPUT run-clang-tidy.py .clang-tidy
    COMMAND cp ${CMAKE_SOURCE_DIR}/scripts/run-clang-tidy.py ${CMAKE_SOURCE_DIR}/.clang-tidy ${CMAKE_CURRENT_BINARY_DIR})

I now want to call run-clang-tidy.py in the build directory (which should be the working directory), with a custom target, so that I can just call:

make lint

Which should run the checks specified in .clang-tidy.

For this script to work, it also needs the CMAKE_EXPORT_COMPILE_COMMANDS option. I try to set it with the following command, but it does not recognize it:

add_definitions(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON)

How would the call to add_custom_target look like?

like image 512
fuji Avatar asked Aug 28 '15 22:08

fuji


4 Answers

Since CMake 3.6, native integration of clang-tidy is implemented [1, 2]. Mechanics are similar to include-what-you-use integration that was there since CMake 3.3 [3].

like image 143
Alexander Shukaev Avatar answered Sep 24 '22 16:09

Alexander Shukaev


I can suggest another way to do, which do not require an extra Python script.

First of all, I wanted to integrate clang-tidy and clang-format in custom CMake rules, so I first generated .clang-tidy and .clang-format files which were located at the root directory of the project.

Generating the configuration files

To generate .clang-tidy, first find the suitable options for your project and then, just do:

$> clang-tidy <source-files> -dump-config <tidy-options> -- <compile-options> > .clang-tidy

Similarly for clang-format you may start with a default style using the -style=xxx option, and dump it. For example, starting with the LLVM style:

$> clang-format -style=LLVM -dump-config > .clang-format

Then, edit it and configure it properly as you wish. It should looks like that:

---
Language:        Cpp
# BasedOnStyle:  LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: true
AlignEscapedNewlinesLeft: false
AlignOperands:   true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakBeforeMultilineStrings: false
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BinPackParameters: true
BinPackArguments: true
ColumnLimit:     80
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
DerivePointerAlignment: false
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: false
IndentWrappedFunctionNames: false
IndentFunctionDeclarationAfterType: false
MaxEmptyLinesToKeep: 1
KeepEmptyLinesAtTheStartOfBlocks: true
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
SpacesBeforeTrailingComments: 1
Cpp11BracedListStyle: true
Standard:        Cpp11
IndentWidth:     2
TabWidth:        8
UseTab:          Never
BreakBeforeBraces: Attach
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpacesInAngles:  false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterCStyleCast: false
SpacesInContainerLiterals: true
SpaceBeforeAssignmentOperators: true
ContinuationIndentWidth: 4
CommentPragmas:  '^ IWYU pragma:'
ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
SpaceBeforeParens: ControlStatements
DisableFormat:   false
...

Creating the custom CMake rule

CMake allow to define custom rules in a very simple way, you just have to write a set of CMake commands in a file with a call to the add_custom_target() procedure and, then, include it in your CMakeList.txt file. This is what we will do, we first create a cmake/clang-dev-tools.cmake file at the root of your project:

# Additional target to perform clang-format/clang-tidy run
# Requires clang-format and clang-tidy

# Get all project files
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.hpp)

add_custom_target(
        clang-format
        COMMAND /usr/bin/clang-format
        -style=file
        -i
        ${ALL_SOURCE_FILES}
)

add_custom_target(
        clang-tidy
        COMMAND /usr/bin/clang-tidy
        ${ALL_SOURCE_FILES}
        -config=''
        --
        -std=c++11
        ${INCLUDE_DIRECTORIES}
)

Then, edit you CMakeLists.txt and add:

# Including extra cmake rules
include(cmake/clang-dev-tools.cmake)

Then, once the build-system regenerated, you should be able to run make clang-tidy and make clang-format.

like image 30
perror Avatar answered Sep 23 '22 16:09

perror


The documentation mentioned by Alexander Shukaev is a bit short on details, so I'm adding an example. The formatting of the warning strings makes IDEs think the clang-tidy results are compiler warnings and will mark the source code. Also, it runs each file in parallel after its object file has been created.

if ( CMAKE_VERSION GREATER "3.5" )
  set(ENABLE_CLANG_TIDY OFF CACHE BOOL "Add clang-tidy automatically to builds")
  if (ENABLE_CLANG_TIDY)
    find_program (CLANG_TIDY_EXE NAMES "clang-tidy" PATHS /usr/local/opt/llvm/bin )
    if (CLANG_TIDY_EXE)
      message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
      set(CLANG_TIDY_CHECKS "-*,modernize-*")
      set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'"
        CACHE STRING "" FORCE)
    else()
      message(AUTHOR_WARNING "clang-tidy not found!")
      set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE) # delete it
    endif()
  endif()
endif()

The only problems I've had with this is that it still checks automatically generated moc_*.cxx files and the usual annoyances of warnings from code in an ExternalProject.

like image 25
Pete Peterson Avatar answered Sep 22 '22 16:09

Pete Peterson


add_definitions set CMake variable, available only configuration stage. If you want to set environment variable for command being executed at build stage, use appropriate shell mechanism with COMMAND keyword:

add_custom_target(lint
    COMMAND CMAKE_EXPORT_COMPILE_COMMANDS=ON python run-clang-tidy.py
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/run-clang-tidy.py
            ${CMAKE_CURRENT_BINARY_DIR}/.clang-tidy

Everything specified for COMMAND keyword will be interpreted by the shell "as is" (after interpretation of CMake, which is no-op here).

like image 42
Tsyvarev Avatar answered Sep 22 '22 16:09

Tsyvarev