changeset 54:0edaba9986ba

CMake: add vera++, closes 602
author David Demelier <markand@malikania.fr>
date Fri, 16 Dec 2016 13:59:55 +0100
parents fe7e3524e571
children 576c35ccdff6
files CMakeLists.txt cmake/MalikaniaOptions.cmake cmake/function/MalikaniaDefineExecutable.cmake cmake/function/MalikaniaDefineLibrary.cmake cmake/function/MalikaniaDefineTest.cmake cmake/function/MalikaniaVeraCheck.cmake 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 vera/rules/T019.tcl
diffstat 20 files changed, 425 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Dec 16 13:07:44 2016 +0100
+++ b/CMakeLists.txt	Fri Dec 16 13:59:55 2016 +0100
@@ -35,6 +35,7 @@
 include(cmake/function/MalikaniaDefineExecutable.cmake)
 include(cmake/function/MalikaniaDefineTest.cmake)
 include(cmake/function/MalikaniaSetg.cmake)
+include(cmake/function/MalikaniaVeraCheck.cmake)
 include(cmake/MalikaniaVersion.cmake)
 include(cmake/MalikaniaOptions.cmake)
 include(cmake/MalikaniaSystem.cmake)
@@ -71,3 +72,4 @@
 message("")
 message("Documentation:")
 message("      Doxygen:         ${WITH_DOXYGEN_MSG}")
+message("      Vera:            ${WITH_VERA_MSG}")
--- a/cmake/MalikaniaOptions.cmake	Fri Dec 16 13:07:44 2016 +0100
+++ b/cmake/MalikaniaOptions.cmake	Fri Dec 16 13:59:55 2016 +0100
@@ -68,6 +68,26 @@
 endif ()
 
 #
+# Options for development.
+# -------------------------------------------------------------------
+#
+
+option(WITH_VERA "Enable vera++ analyzer" On)
+
+find_program(VERA_EXECUTABLE vera++)
+
+if (VERA_EXECUTABLE)
+    if (WITH_VERA)
+        malikania_setg(WITH_VERA_MSG "Yes")
+    else ()
+        malikania_setg(WITH_MSG Off)
+        malikania_setg(WITH_VERA_MSG "No (vera++ not found)")
+    endif ()
+else ()
+    malikania_setg(WITH_VERA_MSG "No (disabled by user)")
+endif ()
+
+#
 # Options for unit tests
 # -------------------------------------------------------------------
 #
--- a/cmake/function/MalikaniaDefineExecutable.cmake	Fri Dec 16 13:07:44 2016 +0100
+++ b/cmake/function/MalikaniaDefineExecutable.cmake	Fri Dec 16 13:59:55 2016 +0100
@@ -34,6 +34,8 @@
 
 include(CMakeParseArguments)
 
+include(${CMAKE_CURRENT_LIST_DIR}/MalikaniaVeraCheck.cmake)
+
 function(malikania_define_executable)
     set(singleArgs TARGET)
     set(multiArgs SOURCES FLAGS INCLUDES LIBRARIES)
@@ -64,4 +66,6 @@
                 RUNTIME_OUTPUT_DIRECTORY_${c} ${WITH_FAKEROOT_DIR}/bin
         )
     endforeach()
+
+    malikania_vera_check(${EXE_TARGET} ${EXE_SOURCES})
 endfunction()
--- a/cmake/function/MalikaniaDefineLibrary.cmake	Fri Dec 16 13:07:44 2016 +0100
+++ b/cmake/function/MalikaniaDefineLibrary.cmake	Fri Dec 16 13:59:55 2016 +0100
@@ -37,6 +37,7 @@
 include(CMakeParseArguments)
 
 include(${CMAKE_CURRENT_LIST_DIR}/MalikaniaBuildAssets.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/MalikaniaVeraCheck.cmake)
 
 function(malikania_define_library)
     set(singleArgs TARGET)
@@ -69,4 +70,6 @@
             ${LIB_PUBLIC_INCLUDES}
     )
     target_compile_definitions(${LIB_TARGET} PRIVATE ${LIB_FLAGS})
+
+    malikania_vera_check(${LIB_TARGET} ${LIB_SOURCES})
 endfunction()
--- a/cmake/function/MalikaniaDefineTest.cmake	Fri Dec 16 13:07:44 2016 +0100
+++ b/cmake/function/MalikaniaDefineTest.cmake	Fri Dec 16 13:59:55 2016 +0100
@@ -33,6 +33,8 @@
 
 include(CMakeParseArguments)
 
+include(${CMAKE_CURRENT_LIST_DIR}/MalikaniaVeraCheck.cmake)
+
 function(malikania_create_test)
     set(singleArgs NAME)
     set(multiArgs LIBRARIES SOURCES RESOURCES)
@@ -98,4 +100,6 @@
     )
 
     add_dependencies(tests test-${TEST_NAME})
+
+    malikania_vera_check(test-${TEST_NAME} ${TEST_SOURCES})
 endfunction()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/function/MalikaniaVeraCheck.cmake	Fri Dec 16 13:59:55 2016 +0100
@@ -0,0 +1,29 @@
+#
+# MalikaniaVeraCheck.cmake -- CMake build system for malikania
+#
+# Copyright (c) 2013-2016 Malikania Authors
+#
+# 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.
+#
+
+function(malikania_vera_check target sources)
+    if (WITH_VERA)
+        add_custom_command(
+            TARGET ${target}
+            COMMAND
+                ${VERA_EXECUTABLE} -w --root ${CMAKE_SOURCE_DIR}/vera ${sources}
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+            VERBATIM
+        )
+    endif ()
+endfunction()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/profiles/default	Fri Dec 16 13:59:55 2016 +0100
@@ -0,0 +1,15 @@
+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
+rule=T019
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/L001.tcl	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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	Fri Dec 16 13:59:55 2016 +0100
@@ -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
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vera/rules/T019.tcl	Fri Dec 16 13:59:55 2016 +0100
@@ -0,0 +1,48 @@
+#!/usr/bin/tclsh
+# control structures should have complete curly-braced block of code
+
+foreach fileName [getSourceFileNames] {
+
+    set state "start"
+    set prev ""
+    set pp_pragma_line -1
+    foreach token [getTokens $fileName 1 0 -1 -1 {for if while do else leftparen rightparen leftbrace rightbrace semicolon pp_pragma}] {
+        set type [lindex $token 3]
+        set line [lindex $token 1]
+
+        if {$state == "control"} {
+            if {$type == "leftparen"} {
+                incr parenCount				
+            } elseif {$type == "rightparen"} {
+                incr parenCount -1
+                if {$parenCount == 0} {
+                    set state "expectedblock"
+                }
+            }
+
+        } elseif {$state == "expectedblock"} {
+            if {$prev == "else" && $type == "if" } {
+              # skip
+            } elseif {$type != "leftbrace"} {
+                set line [lindex $token 1]
+                report $fileName $line "full block {} expected in the control structure"
+            }
+            set state "block"
+        }
+
+        if {$type == "pp_pragma"} {
+          set pp_pragma_line $line
+        } elseif {$pp_pragma_line != $line} {
+            if {$type == "for" || $type == "if"} {
+                set parenCount 0
+                set state "control"
+            } elseif {$type == "do" || $type == "else"} {
+                set state "expectedblock"
+            } elseif {$type == "while" && $prev != "rightbrace"} {
+                set parenCount 0
+                set state "control"
+            }
+        }
+        set prev $type
+    }
+}