Skip to content

Commit

Permalink
add ability to check clang-format
Browse files Browse the repository at this point in the history
  • Loading branch information
ttroy50 committed Jul 5, 2017
1 parent 077d498 commit b0e13d5
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 0 deletions.
61 changes: 61 additions & 0 deletions 04-static-analysis/clang-format/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 120
CommentPragmas: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 3
NamespaceIndentation: All
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...
14 changes: 14 additions & 0 deletions 04-static-analysis/clang-format/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cmake_minimum_required (VERSION 3.0)

project(cppcheck_analysis)

# Add a custom CMake Modules directory
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules
${CMAKE_MODULE_PATH})

# Add sub directories
add_subdirectory(subproject1)
add_subdirectory(subproject2)

set(CLANG_FORMAT_BIN_NAME clang-format-3.6)
find_package(ClangFormat)
142 changes: 142 additions & 0 deletions 04-static-analysis/clang-format/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
= clang-format
:toc:
:toc-placement!:

toc::[]

# Introduction

This example shows how to call the
https://clang.llvm.org/docs/ClangFormat.html[Clang Format] to check if your source code
matches against your code style guidelines.

The files included in this example are:

```
$ tree
.
├── .clang-format
├── CMakeLists.txt
├── cmake
│   ├── modules
│   │   ├── clang-format.cmake
│   │   └── FindClangFormat.cmake
│   └── scripts
│   └── clang-format-check-changed
├── subproject1
│   ├── CMakeLists.txt
│   └── main1.cpp
└── subproject2
├── CMakeLists.txt
└── main2.cpp
```

* link:CMakeLists.txt[] - Top level CMakeLists.txt
* link:.clang-format - The file describing the stype guide
* link:cmake/modules/FindClangFormat.cmake - Script to find the clang-format binary
* link:cmake/modules/clang-format.cmake - Script to setup the format targets
* link:cmake/scripts/clang-format-check-changed - A helper script to check against changed files in git
* link:subproject1/CMakeLists.txt[] - CMake commands for subproject 1
* link:subproject1/main.cpp[] - source for a subproject with no errors
* link:subproject2/CMakeLists.txt[] - CMake commands for subproject 2
* link:subproject2/main2.cpp[] - source for a subproject that includes errors

# Requirements

To run this example you must have clang format tool installed. This can be installed on Ubuntu using the following command.

[source,bash]
----
$ sudo apt-get install clang-format
----

It will result in the tool being available as:

[source,bash]
----
$ clang-format
----

# Concepts

## clang-format

+clang-format+ can scan a source file then find and optionally format it to match your
companys style guidelines. There are default styles build in but you can also setup a style guide using a custom file called +.clang-format+, for example a snipped from this
repositories +.clang-format+ is below:

[source]
----
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
----

By default this will run the standard compiler for your platform, i.e. `gcc` on linux. However, if you want to override this you can change the command to:

[source,bash]
----
$ scan-build-3.6 --use-cc=clang-3.6 --use-c++=clang++-3.6 -o ./scanbuildout/ make
----

## format style

As mentioned, the style in this example is based on the +.clang-format+ file. This can be changed by editing link:cmake/modules/clang-format.cmake[clang-format.cmake] and changing
the `-style=file` to the required style;

# Targets

This example will setup 3 targets:

* format
* format-check
* format-check-changed

## format

The format target will find any C++ source files and in place modify them to match the
+.clang-format+ style. The source files are found using the following cmake code

[source,cmake]
----
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.cxx *.hxx *.hpp *.cc)
# Don't include some common build folders
set(CLANG_FORMAT_EXCLUDE_PATTERNS ${CLANG_FORMAT_EXCLUDE_PATTERNS} "build/" "/CMakeFiles/")
# get all project files file
foreach (SOURCE_FILE ${ALL_SOURCE_FILES})
foreach (EXCLUDE_PATTERN ${CLANG_FORMAT_EXCLUDE_PATTERNS})
string(FIND ${SOURCE_FILE} ${EXCLUDE_PATTERN} EXCLUDE_FOUND)
if (NOT ${EXCLUDE_FOUND} EQUAL -1)
list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE})
endif ()
endforeach ()
endforeach ()
----

This will find files matching the common C++ suffixes and then remove any that match some
common CMake build directories.

## format-check

This target will work as above but instead of formatting the files it will cause a failure
if any files don't match the clang-format style

## format-check-changed

This target will check the output of `git status` and scan the files to check if they match the style. This can be used by developers to make sure their changed files match the correct style.

In this example the actual check is done with a helper script +clang-format-check-changed+. This calls the following command to check files:

[source,bash]
----
git status --porcelain \
| egrep '*\.cpp|*\.h|*\.cxx|*\.hxx|*\.hpp|*\.cc' \
| awk -F " " '{print $NF}' \
| xargs -r clang-format -style=file -output-replacements-xml \
| grep "replacement offset" 2>&1 > /dev/null
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Find Clang format
#
#
if(NOT CLANG_FORMAT_BIN_NAME)
set(CLANG_FORMAT_BIN_NAME clang-format)
endif()

# if custom path check there first
if(CPPCHECK_ROOT_DIR)
find_program(CLANG_FORMAT_BIN
NAMES
${CLANG_FORMAT_BIN_NAME}
PATHS
"${CLANG_FORMAT_ROOT_DIR}"
NO_DEFAULT_PATH)
endif()

find_program(CLANG_FORMAT_BIN NAMES ${CLANG_FORMAT_BIN_NAME})

include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
CLANG_FORMAT
DEFAULT_MSG
CLANG_FORMAT_BIN)

mark_as_advanced(
CLANG_FORMAT_BIN)

if(CLANG_FORMAT_FOUND)
# A CMake script to find all source files and setup clang-format targets for them
include(clang-format)
else()
message("clang-format not found. Not setting up format targets")
endif()
46 changes: 46 additions & 0 deletions 04-static-analysis/clang-format/cmake/modules/clang-format.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# A CMake script to find all source files and setup clang-format targets for them

# Find all source files
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.cxx *.hxx *.hpp *.cc)

# Don't include some common build folders
set(CLANG_FORMAT_EXCLUDE_PATTERNS ${CLANG_FORMAT_EXCLUDE_PATTERNS} "main2.cpp" "build/" "/CMakeFiles/")

# get all project files file
foreach (SOURCE_FILE ${ALL_SOURCE_FILES})
foreach (EXCLUDE_PATTERN ${CLANG_FORMAT_EXCLUDE_PATTERNS})
string(FIND ${SOURCE_FILE} ${EXCLUDE_PATTERN} EXCLUDE_FOUND)
if (NOT ${EXCLUDE_FOUND} EQUAL -1)
list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE})
endif ()
endforeach ()
endforeach ()

add_custom_target(format
COMMENT "Running clang-format to change files"
COMMAND ${CLANG_FORMAT_BIN}
-style=file
-i
${ALL_SOURCE_FILES}
)

add_custom_target(format-check
COMMENT "Checking clang-format changes"
COMMAND ${CLANG_FORMAT_BIN}
-style=file
-output-replacements-xml
${ALL_SOURCE_FILES}
| grep "replacement offset" 2>&1 > /dev/null
)

# This is a hack because our CMake root dir isn't the same as our git root dir

# Get the path to this file
get_filename_component(_clangcheckpath ${CMAKE_CURRENT_LIST_FILE} PATH)
# call the script to chech changed files in git
add_custom_target(format-check-changed
COMMENT "Checking changed files in git"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${_clangcheckpath}/../scripts/clang-format-check-changed ${CLANG_FORMAT_BIN}
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

# Required because cmake root isn't git root in this example
CLANG_FORMAT_BIN=$1
GIT_ROOT=`git rev-parse --show-toplevel`

pushd ${GIT_ROOT} > /dev/null

git status --porcelain \
| egrep '*\.cpp|*\.h|*\.cxx|*\.hxx|*\.hpp|*\.cc' \
| awk -F " " '{print $NF}' \
| xargs -r ${CLANG_FORMAT_BIN} -style=file -output-replacements-xml \
| grep "replacement offset" 2>&1 > /dev/null

RET=$?
popd > /dev/null

exit ${RET}
10 changes: 10 additions & 0 deletions 04-static-analysis/clang-format/subproject1/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Set the project name
project (subproject1)

# Create a sources variable with a link to all cpp files to compile
set(SOURCES
main1.cpp
)

# Add an executable with the above sources
add_executable(${PROJECT_NAME} ${SOURCES})
7 changes: 7 additions & 0 deletions 04-static-analysis/clang-format/subproject1/main1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <iostream>

int main(int argc, char* argv[])
{
std::cout << "Hello Main1!" << std::endl;
return 0;
}
10 changes: 10 additions & 0 deletions 04-static-analysis/clang-format/subproject2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Set the project name
project (subproject2)

# Create a sources variable with a link to all cpp files to compile
set(SOURCES
main2.cpp
)

# Add an executable with the above sources
add_executable(${PROJECT_NAME} ${SOURCES})
18 changes: 18 additions & 0 deletions 04-static-analysis/clang-format/subproject2/main2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <iostream>

class TestClass {
public:
TestClass();
};

TestClass::TestClass() {

}

int main(int argc, char* argv[])
{
std::cout << "Hello Main2!" << std::endl;
int* x = NULL;
std::cout << *x << std::endl;
return 0;
}

0 comments on commit b0e13d5

Please sign in to comment.