From f0eb1974d7e44ee20a3a2f6df34e3c9b6d3bc2e2 Mon Sep 17 00:00:00 2001
From: Florian Uhlig <f.uhlig@gsi.de>
Date: Wed, 8 Jul 2020 11:16:08 +0200
Subject: [PATCH] Implement automatic format checker

Add format check in GitLab CI configuration file.
Add CMake target for format checker .
Add neede CMake files and shell scripts.
Add label in GitLab CI configuration file such that it can be
properly sorted on CDash.
The checker is disabled till we format the complete code base.
---
 .clang-format                       | 74 +++++++++++++++++++++++++++++
 .gitlab-ci.yml                      | 15 ++----
 CMakeLists.txt                      |  8 ++++
 cmake/modules/FindClangFormat.cmake | 47 ++++++++++++++++++
 cmake/scripts/check-format.sh       | 17 +++++++
 cmake/scripts/checkformat.cmake     | 27 +++++++++++
 6 files changed, 178 insertions(+), 10 deletions(-)
 create mode 100644 .clang-format
 create mode 100644 cmake/modules/FindClangFormat.cmake
 create mode 100755 cmake/scripts/check-format.sh
 create mode 100644 cmake/scripts/checkformat.cmake

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..d0ef16a7
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,74 @@
+---
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+#AlignConsecutiveMacros: true
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: true
+AlignTrailingComments: true
+#AllowAllArgumentsOnNextLine: true
+#AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: true
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+#AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+BreakInheritanceList: AfterColon
+ColumnLimit: 80
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+IncludeBlocks: Preserve
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: true
+Language: Cpp
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+PenaltyBreakAssignment: 2
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: true
+#SpaceAfterLogicalNot: true
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+TabWidth: 8
+UseTab: Never
+
+...
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6574d5e5..e14e5737 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,5 @@
 stages:
+  - check
   - build
   - documentation
   
@@ -41,7 +42,6 @@ CbmRoot_Continuous:
     - echo "export FAIRSOFT_VERSION=jun19p1" >> Dart.cfg
     - echo "export FAIRROOT_VERSION=v18.2.0" >> Dart.cfg
     - echo "export SIMPATH=/cvmfs/fairroot.gsi.de/fairsoft/\${FAIRSOFT_VERSION}" >> Dart.cfg
-    - echo "export FAIRROOTPATH=/cvmfs/fairroot.gsi.de/fairroot/\${FAIRROOT_VERSION}_fairsoft-\${FAIRSOFT_VERSION}" >> Dart.cfg 
     - echo "export BUILDDIR=$PWD/build" >> Dart.cfg
     - echo "export SOURCEDIR=$PWD" >> Dart.cfg
     - echo "export NCPU=5" >> Dart.cfg
@@ -57,7 +57,7 @@ CbmRoot_Continuous:
     - cd ..
 
 #FormatCheck:
-#  stage: build
+#  stage: check
 #  tags:
 #    - CbmRoot
 #  only:
@@ -66,31 +66,26 @@ CbmRoot_Continuous:
 #    variables:
 #      - $CI_MERGE_REQUEST_PROJECT_PATH == "computing/cbmroot"
 #  script:
-#    - set -xv
 #    - echo "export FAIRSOFT_VERSION=jun19p1" > env.sh
 #    - echo "export FAIRROOT_VERSION=v18.2.0" >> env.sh
 #    - echo "export SIMPATH=/cvmfs/fairroot.gsi.de/fairsoft/\${FAIRSOFT_VERSION}" >> env.sh
+#    - echo "export FAIRROOTPATH=/cvmfs/fairroot.gsi.de/fairroot/\${FAIRROOT_VERSION}_fairsoft-\${FAIRSOFT_VERSION}" >> env.sh
 #    - echo "export BUILDDIR=$PWD/build" >> env.sh
 #    - echo "export SOURCEDIR=$PWD" >> env.sh
-#    - echo "export PATH=\$SIMPATH/bin:$PATH" >> env.sh
-#    - ls
-#    - pwd
-#    - cat env.sh
+#    - echo "export PATH=/cvmfs/fairroot.gsi.de/clang-format-8.0.1:\$SIMPATH/bin:$PATH:/cvmfs/it.gsi.de/compiler/llvm/6.0.1/bin/" >> env.sh
+#    - echo "export LABEL=format-check_MR-\${CI_MERGE_REQUEST_IID}" >> env.sh
 #    - . ./env.sh && ctest -S cmake/scripts/checkformat.cmake -VV
 
 CbmRoot_Merge:
   stage: build
   tags:
     - CbmRoot
-#  only:
-#    - merge_requests
   only:
     refs:
       - merge_requests
     variables:
       - $CI_MERGE_REQUEST_PROJECT_PATH == "computing/cbmroot"
   script:
-#    - set -xv
     - echo $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH
     - echo $CI_MERGE_REQUEST_PROJECT_PATH
     - if [ "$CI_MERGE_REQUEST_PROJECT_PATH" != "computing/cbmroot" ]; then
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 89fb4b62..b25e8e18 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -357,6 +357,14 @@ add_custom_target(cleantest
                   COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/scripts/cleantest.cmake
                  )
 
+find_package2(PRIVATE ClangFormat)
+if(ClangFormat_FOUND)
+  add_custom_target(FormatCheck
+    ${CMAKE_COMMAND} -E env FAIRROOT_GIT_CLANG_FORMAT_BIN=${GIT_CLANG_FORMAT_BIN} ${CMAKE_SOURCE_DIR}/cmake/scripts/check-format.sh
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  )
+endif()
+
 Option(BUILD_DOXYGEN "Build Doxygen" OFF)
 if(BUILD_DOXYGEN)
   MESSAGE(STATUS "*** Building the Doxygen documentaion ***")
diff --git a/cmake/modules/FindClangFormat.cmake b/cmake/modules/FindClangFormat.cmake
new file mode 100644
index 00000000..3bdbcc26
--- /dev/null
+++ b/cmake/modules/FindClangFormat.cmake
@@ -0,0 +1,47 @@
+# Defines the following variables:
+#
+#   ClangFormat_FOUND - Found clang-format and git-clang-format
+#   CLANG_FORMAT_BIN - clang-format executable
+#   GIT_CLANG_FORMAT_BIN - git-clang-format executable
+
+find_program(CLANG_FORMAT_BIN
+  NAMES clang-format
+        clang-format-9
+        clang-format-8
+        clang-format-7
+        clang-format-6.0
+        clang-format-5.0
+        clang-format-4.0
+        clang-format-3.9
+        clang-format-3.8
+        clang-format-3.7
+        clang-format-3.6
+        clang-format-3.5
+        clang-format-3.4
+        clang-format-3.3
+)
+
+find_program(GIT_CLANG_FORMAT_BIN
+  NAMES git-clang-format
+        git-clang-format-9
+        git-clang-format-8
+        git-clang-format-7
+        git-clang-format-6.0
+        git-clang-format-5.0
+        git-clang-format-4.0
+        git-clang-format-3.9
+        git-clang-format-3.8
+        git-clang-format-3.7
+        git-clang-format-3.6
+        git-clang-format-3.5
+        git-clang-format-3.4
+        git-clang-format-3.3
+)
+
+Message("GIT_CLANG_FORMAT_BIN: ${GIT_CLANG_FORMAT_BIN}")
+Message("CLANG_FORMAT_BIN: ${CLANG_FORMAT_BIN}")
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ClangFormat
+  REQUIRED_VARS CLANG_FORMAT_BIN GIT_CLANG_FORMAT_BIN
+)
diff --git a/cmake/scripts/check-format.sh b/cmake/scripts/check-format.sh
new file mode 100755
index 00000000..73b8a163
--- /dev/null
+++ b/cmake/scripts/check-format.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+BASE_COMMIT=${FAIRROOT_FORMAT_BASE:-HEAD}
+GIT_CLANG_FORMAT_BIN=${FAIRROOT_GIT_CLANG_FORMAT_BIN:-git-clang-format}
+
+FILES=$(git diff --name-only $BASE_COMMIT | grep -E '.*\.(h|hpp|c|C|cpp|cxx|tpl)$' | grep -viE '.*LinkDef.h$')
+RESULT=$($GIT_CLANG_FORMAT_BIN --commit $BASE_COMMIT --diff $FILES --extensions h,hpp,c,C,cpp,cxx,tpl)
+
+if [ "$RESULT" == "no modified files to format" ] || [ "$RESULT" == "clang-format did not modify any files" ]; then
+    echo "format check passed."
+    exit 0
+else
+    echo "ERROR: format check failed. Suggested changes:"
+    echo "$RESULT"
+    exit 1
+fi
+
diff --git a/cmake/scripts/checkformat.cmake b/cmake/scripts/checkformat.cmake
new file mode 100644
index 00000000..61d3814b
--- /dev/null
+++ b/cmake/scripts/checkformat.cmake
@@ -0,0 +1,27 @@
+cmake_host_system_information(RESULT fqdn QUERY FQDN)
+
+set(CTEST_SOURCE_DIRECTORY .)
+set(CTEST_BINARY_DIRECTORY build)
+Set(CTEST_PROJECT_NAME "CbmRoot")
+set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
+set(CTEST_USE_LAUNCHERS ON)
+
+if ("$ENV{CTEST_SITE}" STREQUAL "")
+  set(CTEST_SITE "${fqdn}")
+else()
+  set(CTEST_SITE $ENV{CTEST_SITE})
+endif()
+
+if ("$ENV{LABEL}" STREQUAL "")
+  set(CTEST_BUILD_NAME "format-check")
+else()
+  set(CTEST_BUILD_NAME $ENV{LABEL})
+endif()
+
+ctest_start(Experimental)
+
+ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}")
+
+ctest_build(TARGET FormatCheck FLAGS "")
+
+ctest_submit()
-- 
GitLab