changeset 510:6ec510722582

CMake: enable vera++ as code checking, closes #603
author David Demelier <markand@malikania.fr>
date Thu, 19 Oct 2017 13:01:39 +0200
parents c9a6d9df083f
children f912379cc1a3
files CMakeLists.txt cmake/IrccdOptions.cmake cmake/function/IrccdDefineExecutable.cmake cmake/function/IrccdDefineLibrary.cmake cmake/function/IrccdDefineTest.cmake cmake/function/IrccdVeraCheck.cmake extern/libircclient/CMakeLists.txt vera/profiles/default vera/rules/L001.tcl vera/rules/L002.tcl vera/rules/L003.tcl vera/rules/L004.tcl vera/rules/L005.tcl vera/rules/T003.tcl vera/rules/T004.tcl vera/rules/T005.tcl vera/rules/T006.tcl vera/rules/T008.tcl vera/rules/T017.tcl vera/rules/T018.tcl
diffstat 20 files changed, 415 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Oct 16 13:47:05 2017 +0200
+++ b/CMakeLists.txt	Thu Oct 19 13:01:39 2017 +0200
@@ -119,6 +119,7 @@
 message("    User docs:        ${WITH_HTML_MSG}")
 message("    Doxygen:          ${WITH_DOXYGEN_MSG}")
 message("    Package:          ${IRCCD_PACKAGE_MSG}")
+message("    Vera:             ${WITH_VERA_MSG}")
 message("")
 
 message("Installing plugins:")
--- a/cmake/IrccdOptions.cmake	Mon Oct 16 13:47:05 2017 +0200
+++ b/cmake/IrccdOptions.cmake	Thu Oct 19 13:01:39 2017 +0200
@@ -29,6 +29,7 @@
 # WITH_MAN              Install manpages (default: on, off for Windows)
 # WITH_PKGCONFIG        Install pkg-config files (default: on, off for Windows (except MinGW))
 # WITH_PLUGIN_<NAME>    Enable or disable the specified plugin (default: on)
+# WITH_VERA             Enable style checking using vera (default: on)
 #
 # Note: the option() commands for WITH_PLUGIN_<name> variables are defined automatically from the IRCCD_PLUGINS
 # list.
@@ -79,6 +80,7 @@
 option(WITH_DOXYGEN "Enable doxygen" Off)
 option(WITH_MAN "Install man pages" ${DEFAULT_MAN})
 option(WITH_PKGCONFIG "Enable pkg-config file" ${DEFAULT_PKGCONFIG})
+option(WITH_VERA "Enable vera++" On)
 
 #
 # Installation paths.
@@ -164,6 +166,30 @@
     set(WITH_HTML FALSE)
 endif ()
 
+find_program(VERA_EXECUTABLE vera++)
+
+if (VERA_EXECUTABLE)
+    if (WITH_VERA)
+        execute_process(
+            COMMAND ${VERA_EXECUTABLE} --version
+            OUTPUT_VARIABLE VERA_VERSION
+        )
+
+        if (${VERA_VERSION} VERSION_LESS "1.3.0")
+            set(WITH_VERA Off)
+            set(WITH_VERA_MSG "No (1.3.0 or greater required)")
+        else ()
+            set(WITH_VERA_MSG "Yes")
+        endif ()
+    else ()
+        set(WITH_VERA Off)
+        set(WITH_VERA_MSG "No (disabled by user)")
+    endif ()
+else ()
+    set(WITH_VERA Off)
+    set(WITH_VERA_MSG "No (vera++ not found)")
+endif ()
+
 #
 # Determine if allowed to package.
 # -------------------------------------------------------------------
--- a/cmake/function/IrccdDefineExecutable.cmake	Mon Oct 16 13:47:05 2017 +0200
+++ b/cmake/function/IrccdDefineExecutable.cmake	Thu Oct 19 13:01:39 2017 +0200
@@ -32,6 +32,8 @@
 # Create an executable that can be installed or not.
 #
 
+include(${CMAKE_CURRENT_LIST_DIR}/IrccdVeraCheck.cmake)
+
 function(irccd_define_executable)
     set(options "")
     set(oneValueArgs DESCRIPTION TARGET)
@@ -79,4 +81,6 @@
     setg(CPACK_COMPONENT_${CMP}_DISPLAY_NAME "${EXE_TARGET} executable")
     setg(CPACK_COMPONENT_${CMP}_DESCRIPTION ${EXE_DESCRIPTION})
     setg(CPACK_COMPONENT_${CMP}_GROUP "Applications")
+
+    irccd_vera_check(${EXE_TARGET} "${EXE_SOURCES}")
 endfunction()
--- a/cmake/function/IrccdDefineLibrary.cmake	Mon Oct 16 13:47:05 2017 +0200
+++ b/cmake/function/IrccdDefineLibrary.cmake	Thu Oct 19 13:01:39 2017 +0200
@@ -24,6 +24,7 @@
 #    TARGET target name
 #    SOURCES src1, src2, srcn
 #    LOCAL (Optional) set to true to build a static library
+#    EXTERNAL (Optional) set to true if library is third party
 #    FLAGS (Optional) C/C++ flags (without -D)
 #    LIBRARIES (Optional) libraries to link
 #    LOCAL_INCLUDES (Optional) local includes for the target only
@@ -33,8 +34,10 @@
 # Create a static library for internal use.
 #
 
+include(${CMAKE_CURRENT_LIST_DIR}/IrccdVeraCheck.cmake)
+
 function(irccd_define_library)
-    set(options LOCAL)
+    set(options EXTERNAL LOCAL)
     set(oneValueArgs TARGET)
     set(multiValueArgs SOURCES FLAGS LIBRARIES LOCAL_INCLUDES PUBLIC_INCLUDES)
     set(mandatory TARGET SOURCES)
@@ -69,4 +72,8 @@
                 RUNTIME_OUTPUT_DIRECTORY_${cu} ${CMAKE_BINARY_DIR}/bin/${c}
         )
     endforeach()
+
+    if (NOT ${LIB_EXTERNAL})
+        irccd_vera_check(${LIB_TARGET} "${LIB_SOURCES}")
+    endif ()
 endfunction()
--- a/cmake/function/IrccdDefineTest.cmake	Mon Oct 16 13:47:05 2017 +0200
+++ b/cmake/function/IrccdDefineTest.cmake	Thu Oct 19 13:01:39 2017 +0200
@@ -34,6 +34,8 @@
 
 find_package(Boost REQUIRED COMPONENTS unit_test_framework)
 
+include(${CMAKE_CURRENT_LIST_DIR}/IrccdVeraCheck.cmake)
+
 function(irccd_define_test)
     set(oneValueArgs NAME)
     set(multiValueArgs SOURCES LIBRARIES FLAGS)
@@ -104,4 +106,6 @@
         COMMAND test-${TEST_NAME}
         WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests
     )
+
+    irccd_vera_check(test-${TEST_NAME} "${TEST_SOURCES}")
 endfunction()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/function/IrccdVeraCheck.cmake	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,56 @@
+#
+# IrccdVeraCheck.cmake -- CMake build system for irccd
+#
+# Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+#
+# irccd_vera_check
+# ----------------
+#
+# irccd_vera_check(target sources)
+#
+# Check the style source code using vera++.
+#
+# No-op if WITH_VERA is Off.
+#
+# This macro add a post-build command to call vera++ program on the specified
+# sources file for the given target.
+#
+
+function(irccd_vera_check target sources)
+    if (WITH_VERA)
+        set(valid ".cpp;.c;.hpp;.h")
+
+        # Cleanup non relevant files.
+        foreach (s ${sources})
+            get_filename_component(ext ${s} EXT)
+
+            foreach (e ${valid})
+                if (${ext} STREQUAL ${e})
+                    list(APPEND newsources ${s})
+                endif ()
+            endforeach ()
+        endforeach ()
+
+        add_custom_command(
+            TARGET ${target}
+            COMMAND
+                ${VERA_EXECUTABLE} -w --root ${CMAKE_SOURCE_DIR}/vera ${newsources}
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+            VERBATIM
+        )
+    endif ()
+endfunction()
--- a/extern/libircclient/CMakeLists.txt	Mon Oct 16 13:47:05 2017 +0200
+++ b/extern/libircclient/CMakeLists.txt	Thu Oct 19 13:01:39 2017 +0200
@@ -51,7 +51,7 @@
 endif ()
 
 irccd_define_library(
-    LOCAL
+    LOCAL EXTERNAL
     TARGET extern-ircclient
     SOURCES src/libircclient.c
     FLAGS ${FLAGS}
@@ -65,4 +65,4 @@
     PROPERTIES
         PROJECT_LABEL libircclient
         FOLDER extern
-)
\ No newline at end of file
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/profiles/default	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,14 @@
+rule=L001
+rule=L002
+rule=L003
+rule=L004
+parameter=max-line-length=120
+rule=L005
+parameter=max-consecutive-empty-lines=1
+rule=T003
+rule=T004
+rule=T005
+rule=T006
+rule=T008
+rule=T017
+rule=T018
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L001.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,24 @@
+#!/usr/bin/tclsh
+# No trailing whitespace
+
+set strictMode [getParameter "strict-trailing-space" 0]
+
+foreach f [getSourceFileNames] {
+    set lineNumber 1
+    set previousIndent ""
+    foreach line [getAllLines $f] {
+
+        if [regexp {^.*\r$} $line] {
+          report $f $lineNumber "CRLF line ending"
+          set line [string range $line 0 end-1]
+        }
+        if [regexp {^.*[[:space:]]+$} $line] {
+            if {$strictMode || [string trim $line] != "" || $line != $previousIndent} {
+                report $f $lineNumber "trailing whitespace"
+            }
+        }
+
+        regexp {^([[:space:]]*).*$} $line dummy previousIndent
+        incr lineNumber
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L002.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,14 @@
+#!/usr/bin/tclsh
+# Don't use tab characters
+
+foreach f [getSourceFileNames] {
+    set lineNumber 1
+    foreach line [getAllLines $f] {
+
+        if [regexp {\t} $line] {
+            report $f $lineNumber "horizontal tab used"
+        }
+
+        incr lineNumber
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L003.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,17 @@
+#!/usr/bin/tclsh
+# No leading and no trailing empty lines
+
+foreach f [getSourceFileNames] {
+    set lineCount [getLineCount $f]
+    if {$lineCount > 0} {
+        set firstLine [getLine $f 1]
+        if {[string trim $firstLine] == ""} {
+            report $f 1 "leading empty line(s)"
+        }
+
+        set lastLine [getLine $f $lineCount]
+        if {[string trim $lastLine] == ""} {
+            report $f $lineCount "trailing empty line(s)"
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L004.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,14 @@
+#!/usr/bin/tclsh
+# Line cannot be too long
+
+set maxLength [getParameter "max-line-length" 100]
+
+foreach f [getSourceFileNames] {
+    set lineNumber 1
+    foreach line [getAllLines $f] {
+        if {[string length $line] > $maxLength} {
+            report $f $lineNumber "line is longer than ${maxLength} characters"
+        }
+        incr lineNumber
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L005.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,23 @@
+#!/usr/bin/tclsh
+# There should not be too many consecutive empty lines
+
+set maxEmptyLines [getParameter "max-consecutive-empty-lines" 2]
+
+foreach f [getSourceFileNames] {
+    set lineNumber 1
+    set emptyCount 0
+    set reported false
+    foreach line [getAllLines $f] {
+        if {[string trim $line] == ""} {
+            incr emptyCount
+            if {$emptyCount > $maxEmptyLines && $reported == "false"} {
+                report $f $lineNumber "too many consecutive empty lines"
+                set reported true
+            }
+        } else {
+            set emptyCount 0
+            set reported false
+        }
+        incr lineNumber
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T003.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,47 @@
+#!/usr/bin/tclsh
+# Some keywords should be followed by a single space
+
+set keywords {
+    case
+    class
+    enum
+    explicit
+    extern
+    goto
+    new
+    struct
+    union
+    using
+}
+
+proc isKeyword {s} {
+    global keywords
+    return [expr [lsearch $keywords $s] != -1]
+}
+
+set state "other"
+foreach f [getSourceFileNames] {
+    foreach t [getTokens $f 1 0 -1 -1 {}] {
+        set tokenValue [lindex $t 0]
+        set tokenName [lindex $t 3]
+        if {$state == "keyword"} {
+            if {$tokenName == "space" && $tokenValue == " "} {
+                set state "space"
+            } else {
+                report $f $lineNumber "keyword \'${keywordValue}\' not followed by a single space"
+                set state "other"
+            }
+        } elseif {$state == "space"} {
+            if {$tokenName == "newline"} {
+                report $f $lineNumber "keyword \'${keywordValue}\' not followed by a single space"
+            }
+            set state "other"
+        } else {
+            if [isKeyword $tokenName] {
+                set state "keyword"
+                set lineNumber [lindex $t 1]
+                set keywordValue [lindex $t 0]
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T004.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,51 @@
+#!/usr/bin/tclsh
+# Some keywords should be immediately followed by a colon
+
+set keywords {
+    default
+    private
+    protected
+    public
+}
+
+proc isKeyword {s} {
+    global keywords
+    return [expr [lsearch $keywords $s] != -1]
+}
+
+foreach f [getSourceFileNames] {
+    set lastKeywordLine 0
+    set lastKeywordColumn 0
+    set lastKeywordValue ""
+    set last ""
+    foreach t [getTokens $f 1 0 -1 -1 [concat $keywords colon]] {
+        set tokenValue [lindex $t 0]
+        set tokenName [lindex $t 3]
+        if {$tokenName == "colon"} {
+            if {$last == "keyword" && $lastKeywordLine != 0} {
+                set line [lindex $t 1]
+                set column [lindex $t 2]
+                if {$line != $lastKeywordLine ||
+                    $column != [expr $lastKeywordColumn + [string length $lastKeywordValue]]} {
+                    set nonWhiteFound "false"
+                    foreach tb [getTokens $f $lastKeywordLine [expr $lastKeywordColumn + 1] $line $column {}] {
+                        set tbName [lindex $tb 3]
+                        if {[lsearch {space newline ccomment cppcomment} $tbName] == -1} {
+                            set nonWhiteFound "true"
+                            break
+                        }
+                    }
+                    if {$nonWhiteFound == "false"} {
+                        report $f $line "colon not immediately after the \'$lastKeywordValue\' keyword"
+                    }
+                }
+            }
+            set last "colon"
+        } else {
+            set lastKeywordLine [lindex $t 1]
+            set lastKeywordColumn [lindex $t 2]
+            set lastKeywordValue $tokenValue
+            set last "keyword"
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T005.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,19 @@
+#!/usr/bin/tclsh
+# Keywords break and continue should be immediately followed by a semicolon
+
+foreach f [getSourceFileNames] {
+    foreach t [getTokens $f 1 0 -1 -1 {break continue}] {
+        set keyword [lindex $t 0]
+        set line [lindex $t 1]
+        set column [lindex $t 2]
+        set semicolons [getTokens $f $line [expr $column + [string length $keyword]] [expr $line + 1] 0 {semicolon}]
+        if {$semicolons == {}} {
+            report $f $line "keyword '${keyword}' not immediately followed by a semicolon"
+        } else {
+            set semColumn [lindex [lindex $semicolons 0] 2]
+            if {$semColumn != $column + [string length $keyword]} {
+                report $f $line "keyword '${keyword}' not immediately followed by a semicolon"
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T006.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,21 @@
+#!/usr/bin/tclsh
+# Keywords return and throw should be immediately followed by a semicolon or a single space
+
+foreach f [getSourceFileNames] {
+    foreach t [getTokens $f 1 0 -1 -1 {return throw}] {
+        set keyword [lindex $t 0]
+        set line [lindex $t 1]
+        set column [lindex $t 2]
+        set followingTokens [getTokens $f $line [expr $column + [string length $keyword]] [expr $line + 1] 0 {}]
+        if {$followingTokens == {}} {
+            report $f $line "keyword '${keyword}' not immediately followed by a semicolon or a single space"
+        } else {
+            set first [lindex [lindex $followingTokens 0] 0]
+            if {$first != ";" && $first != " "} {
+                if {!([llength $followingTokens] >= 2 && $keyword == "throw" && $first == "(" && [lindex [lindex $followingTokens 1] 0] == ")")} {
+                    report $f $line "keyword '${keyword}' not immediately followed by a semicolon or a single space"
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T008.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,24 @@
+#!/usr/bin/tclsh
+# Keywords catch, for, if and while should be followed by a single space
+
+foreach f [getSourceFileNames] {
+    set pp_pragma_line -1
+    foreach t [getTokens $f 1 0 -1 -1 {catch for if switch while pp_pragma}] {
+        set keyword [lindex $t 0]
+        set line [lindex $t 1]
+        set column [lindex $t 2]
+        set type [lindex $t 3]
+        if {$type == "pp_pragma"} {
+          set pp_pragma_line $line
+        } elseif {$pp_pragma_line != $line} {
+            set followingTokens [getTokens $f $line [expr $column + [string length $keyword]] [expr $line + 1] -1 {}]
+            if {[llength $followingTokens] < 2} {
+                report $f $line "keyword '${keyword}' not followed by a single space"
+            } else {
+                if {[list [lindex [lindex $followingTokens 0] 0] [lindex [lindex $followingTokens 1] 0]] != [list " " "("]} {
+                    report $f $line "keyword '${keyword}' not followed by a single space"
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T017.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,23 @@
+#!/usr/bin/tclsh
+# Unnamed namespaces are not allowed in header files
+
+foreach fileName [getSourceFileNames] {
+    set extension [file extension $fileName]
+    if {[lsearch {.h .hh .hpp .hxx .ipp} $extension] != -1} {
+
+        set state "start"
+        foreach token [getTokens $fileName 1 0 -1 -1 {namespace identifier leftbrace}] {
+            set type [lindex $token 3]
+
+            if {$state == "namespace" && $type == "leftbrace"} {
+                report $fileName $namespaceLine "unnamed namespace not allowed in header file"
+            }
+
+            if {$type == "namespace"} {
+                set namespaceLine [lindex $token 1]
+            }
+
+            set state $type
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T018.tcl	Thu Oct 19 13:01:39 2017 +0200
@@ -0,0 +1,23 @@
+#!/usr/bin/tclsh
+# using namespace are not allowed in header files
+
+foreach fileName [getSourceFileNames] {
+    set extension [file extension $fileName]
+    if {[lsearch {.h .hh .hpp .hxx .ipp} $extension] != -1} {
+
+        set state "start"
+        foreach token [getTokens $fileName 1 0 -1 -1 {using namespace identifier}] {
+            set type [lindex $token 3]
+
+            if {$state == "using" && $type == "namespace"} {
+                report $fileName $usingLine "using namespace not allowed in header file"
+            }
+
+            if {$type == "using"} {
+                set usingLine [lindex $token 1]
+            }
+
+            set state $type
+        }
+    }
+}