# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# Compiler wrapper script that runs a crude linting process on each source file
-# before passing arguments along to the compiler. The linter rules are inspired
-# by The Power of 10 Rules defined by NASA/JPL
-# (http://web.eecs.umich.edu/~imarkov/10rules.pdf)
-#
-# Below are the NASA/JPL rules that this script attempts to check along with
-# descriptions of what the script checks for and ways it differs from the
-# original rule.
-#
-# 1) "Restrict all code to very simple control flow constructs - do not use goto
-# statements, setjmp or longjmp constructs, or direct or indirect recursion."
-#
-# * error on use of goto, setjmp, and longjmp
-# * error on multiple return statements in a single function
-# * recursion is *not* checked for
-#
-# 2) "No function should be longer than what can be printed on a single sheet of
-# paper in a standard format with one line per statement and one line per
-# declaration. Typically, this means no more than about 60 lines of code per
-# function."
-#
-# * warn on functions greater than 60 lines.
-#
-# 3) "The code's assertion density should average to minimally two assertions per
-# function. Assertions must be used to check for anomalous conditions that
-# should never happen in real-life executions. Assertions must be side-effect
-# free and should be defined as Boolean tests. When an assertion fails, an
-# explicit recovery action must be taken such as returning an error condition
-# to the caller of the function that executes the failing assertion. Any
-# assertion for which a static checking tool can prove that it can never
-# fail or never hold violates this rule."
-#
-# * warn when public functions have no assertions (assert, ensure, require)
-#
-# 4) "The use of the preprocessor must be limited to the inclusion of header files
-# and simple macro definitions. Token pasting, variable argument lists
-# (ellipses), and recursive macro calls are not allowed. All macros must
-# expand into complete syntactic units. The use of conditional compilation
-# directives must be kept to a minimum."
-#
-# * error on use of #if or #undef
-# * (TODO) error on use of function like macros
-#
-# 5) "All code must be compiled, from the first day of development, with all
-# compiler warnings enabled at the most pedantic setting available. All code
-# must compile without warnings. All code must also be checked daily with at
-# least one, but preferably more than one, strong static source code analyzer
-# and should pass all analyses with zero warnings."
-#
-# * invokes compiler -Wall -Wextra -Werror -std=c99 -pedantic
-
declare -a sources
declare -a objects
declare -a libpaths
compile=false
-lintonly=false
runtime='
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
-c|-E) # Mark this as compilation/preprocess only run
compile=true ;;
- --lint) # Mark this as lint only run
- lintonly=true ;;
-
esac
done
-script=$(cat <<EOS
- # initialize some state
- BEGIN {
- MAXLINES = 50
- syms["setjmp"] = 1
- syms["longjmp"] = 1
- syms["goto"] = 1
- keyw["if"] = 1
- keyw["for"] = 1
- keyw["while"] = 1
- cpp["undef"] = 1
- cpp["if"] = 1
- asserts["assert"] = 1
- asserts["require"] = 1
- asserts["ensure"] = 1
- }
-
- # define helper functions
- function warn(msg){
- print FILENAME ":" FNR ": warning:", msg
- }
-
- function error(msg){
- print FILENAME ":" FNR ": error:", msg
- code = 1
- }
-
- function get_syms(line, ary){
- gsub(/\W+/, " ")
- split(\$0, ary, " ")
- }
-
- function scan_func(){
- if (M[i] == "return")
- {
- nreturns += 1
- }
- else if (asserts[M[i]])
- {
- nasserts += 1
- }
- }
-
- function process_idents(opening, closing){
- level += opening
- for (i = 0; i < length(M); i++)
- {
- if (paren && (M[i] == "static"))
- {
- privatefn = 1
- }
- else if (keyw[M[i]])
- {
- control = 1
- }
- else if (syms[M[i]])
- {
- error("use of prohibited symbol '" M[i] "'");
- }
- else if (level > 0)
- {
- scan_func()
- }
- }
- level -= closing
- }
-
- function close_function(){
- if (nreturns > 1)
- {
- error("functions should have a maximum of one return statement")
- }
-
- if ((FNR - linenum) > MAXLINES)
- {
- warn("function is " (FNR - linenum) " lines long. blocks larger than 50 lines are considered too complex")
- }
-
- if (level < 0)
- {
- error("too many closing braces detected")
- }
-
- if (!privatefn && (nasserts == 0))
- {
- warn("public function has no assertions")
- }
- }
-
- # if we're in a comment block, look for the end
- (incomment && match(\$0, /\*\//)) {
- incomment = 0
- }
-
- # scan preprocessor statements
- match(\$0, /^\s*#\s*(\w+)/, M) {
- if (cpp[M[1]])
- {
- error("use of #" M[1] " is prohibited")
- }
- }
-
- # scan all identifiers for prohibited symbols
- !incomment && /^\s*[^#]/ {
- control = 0
- gsub(/(TEST|PROPERTY|TEST_SUITE)\(.*\)/, "") # ignore test functions
- paren = match(\$0, /\(/)
- semi = match(\$0, /;\s*$/)
- gsub(/"(\\\\"|[^"])*"/, "") # ignore string literals
- gsub(/'(\\\\'|[^'])'/, "") # ignore char literals
- sub(/\/\/.*$/, "") # ignore line comments
- gsub(/\/\*.*\*\//, "") # ignore block comments on single line
- incomment = sub(/\/\*.*$/, "") # ignore block comment starts
- sub(/^.*\*\//, "") # ignore block comment stops
-
-# print "'" \$1 "'"
-
- opening = gsub(/{/, "")
- closing = gsub(/}/, "")
- get_syms(\$0, M);
-
- if (level == 0)
- {
- linenum = FNR + 1
- }
-
- # reset state when we potentially encounter a new function
- if (level == 0 && paren)
- {
- privatefn = 0
- nasserts = 0
- nreturns = 0
- if (opening)
- {
- error("function braces should be on their own lines")
- }
- }
-
- process_idents(opening, closing)
-
- if (control && opening)
- {
- warn("braces of control constructs should be on their own lines")
- }
-
- if (infunc && level <= 0)
- {
- close_function()
- infunc = 0
- }
-
- if (level == 0 && paren && !semi)
- {
- infunc = 1
- }
- }
-
- # set return code based on errors detected
- END {
- if (level > 0)
- {
- error("reached EOF with open block");
- }
-# exit code
- }
-EOS
-)
-
-# run the linter and bail if it fails
-if [[ ${#sources[@]} -gt 0 ]]; then
- #printf "%s\n" "$script"
- if ! gawk "$script" "${sources[@]}"; then
- exit 1
- fi
- if $lintonly; then
- exit 0;
- fi
-fi
-
# if we're compiling, generate the header, compile, and exit
if $compile; then
genfile=$(mktemp)