From 62f1ac097dcf96c9cbf9f96f503e8c8f0e7acf0f Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Mon, 14 Oct 2013 17:32:41 +0300 Subject: [PATCH] Integrated support for Red Lizzard's "goanna" static analysis tool Initial support (activate with "-o analyze"). Not working well with IAR for now (partially because of a bug in goannac++ which was reported to Red Lizzard). --- workspace_tools/options.py | 2 +- workspace_tools/toolchains/__init__.py | 12 +++++++++++- workspace_tools/toolchains/arm.py | 24 +++++++++++++++++++----- workspace_tools/toolchains/gcc.py | 26 +++++++++++++++++++++----- workspace_tools/toolchains/iar.py | 20 ++++++++++++++++---- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/workspace_tools/options.py b/workspace_tools/options.py index 3ffabe8891..8ba8aff9d6 100644 --- a/workspace_tools/options.py +++ b/workspace_tools/options.py @@ -35,6 +35,6 @@ def get_default_options_parser(): help="clean the build directory") parser.add_option("-o", "--options", action="append", - help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information)') + help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information, "analyze": run static code analyzer")') return parser diff --git a/workspace_tools/toolchains/__init__.py b/workspace_tools/toolchains/__init__.py index 352aa1f048..29fbf34f15 100644 --- a/workspace_tools/toolchains/__init__.py +++ b/workspace_tools/toolchains/__init__.py @@ -26,6 +26,7 @@ from workspace_tools.patch import patch from workspace_tools.settings import BUILD_OPTIONS import workspace_tools.hooks as hooks +import re def print_notify(event): # Default command line notification @@ -141,7 +142,10 @@ class mbedToolchain: "Cortex-M0+": ["__CORTEX_M0PLUS", "ARM_MATH_CM0"], "Cortex-M4" : ["__CORTEX_M4", "ARM_MATH_CM4", "__FPU_PRESENT=1"], } - + + GOANNA_FORMAT = "[Goanna] warning [%FILENAME%:%LINENO%] - [%CHECKNAME%(%SEVERITY%)] %MESSAGE%" + GOANNA_DIAGNOSTIC_PATTERN = re.compile(r'"\[Goanna\] (?Pwarning) \[(?P[^:]+):(?P\d+)\] \- (?P.*)"') + def __init__(self, target, options=None, notify=None): self.target = target self.name = self.__class__.__name__ @@ -168,6 +172,12 @@ class mbedToolchain: self.labels = None self.build_all = False + + def goanna_parse_line(self, line): + if "analyze" in self.options: + return self.GOANNA_DIAGNOSTIC_PATTERN.match(line) + else: + return None def get_symbols(self): if self.symbols is None: diff --git a/workspace_tools/toolchains/arm.py b/workspace_tools/toolchains/arm.py index df265d0f76..902d84edef 100644 --- a/workspace_tools/toolchains/arm.py +++ b/workspace_tools/toolchains/arm.py @@ -20,6 +20,7 @@ from os.path import join from workspace_tools.toolchains import mbedToolchain from workspace_tools.settings import ARM_BIN, ARM_INC, ARM_LIB, MY_ARM_CLIB, ARM_CPPLIB from workspace_tools.hooks import hook_tool +from workspace_tools.settings import GOANNA_PATH class ARM(mbedToolchain): LINKER_EXT = '.sct' @@ -39,7 +40,8 @@ class ARM(mbedToolchain): else: cpu = target.core - common = [join(ARM_BIN, "armcc"), "-c", + main_cc = join(ARM_BIN, "armcc") + common = ["-c", "--cpu=%s" % cpu, "--gnu", "-Ospace", "--split_sections", "--apcs=interwork", "--brief_diagnostics", "--restrict" @@ -56,9 +58,13 @@ class ARM(mbedToolchain): '-I%s' % ARM_INC ] - self.asm = common - self.cc = common + common_c + ["--c99"] - self.cppc = common + common_c + ["--cpp", "--no_rtti"] + self.asm = [main_cc] + common + if not "analyze" in self.options: + self.cc = [main_cc] + common + common_c + ["--c99"] + self.cppc = [main_cc] + common + common_c + ["--cpp", "--no_rtti"] + else: + self.cc = [join(GOANNA_PATH, "goannacc"), "--with-cc=" + main_cc.replace('\\', '/'), "--dialect=armcc", '--output-format="%s"' % self.GOANNA_FORMAT] + common + common_c + ["--c99"] + self.cppc= [join(GOANNA_PATH, "goannac++"), "--with-cxx=" + main_cc.replace('\\', '/'), "--dialect=armcc", '--output-format="%s"' % self.GOANNA_FORMAT] + common + common_c + ["--cpp", "--no_rtti"] self.ld = [join(ARM_BIN, "armlink")] self.sys_libs = [] @@ -92,7 +98,15 @@ class ARM(mbedToolchain): match.group('line'), match.group('message') ) - + match = self.goanna_parse_line(line) + if match is not None: + self.cc_info( + match.group('severity').lower(), + match.group('file'), + match.group('line'), + match.group('message') + ) + def archive(self, objects, lib_path): self.default_cmd([self.ar, '-r', lib_path] + objects) diff --git a/workspace_tools/toolchains/gcc.py b/workspace_tools/toolchains/gcc.py index 014a8ff9e2..fef4c03fe7 100644 --- a/workspace_tools/toolchains/gcc.py +++ b/workspace_tools/toolchains/gcc.py @@ -19,7 +19,7 @@ from os.path import join, basename, splitext from workspace_tools.toolchains import mbedToolchain from workspace_tools.settings import GCC_ARM_PATH, GCC_CR_PATH, GCC_CS_PATH, CW_EWL_PATH, CW_GCC_PATH - +from workspace_tools.settings import GOANNA_PATH class GCC(mbedToolchain): LINKER_EXT = '.ld' @@ -59,11 +59,17 @@ class GCC(mbedToolchain): if "debug-info" in self.options: common_flags.append("-g") - + self.asm = [join(tool_path, "arm-none-eabi-as")] + self.cpu - - self.cc = [join(tool_path, "arm-none-eabi-gcc"), "-std=gnu99"] + common_flags - self.cppc =[join(tool_path, "arm-none-eabi-g++"), "-std=gnu++98"] + common_flags + + main_cc = join(tool_path, "arm-none-eabi-gcc") + main_cppc = join(tool_path, "arm-none-eabi-g++") + if not "analyze" in self.options: + self.cc = [main_cc, "-std=gnu99"] + common_flags + self.cppc =[main_cppc, "-std=gnu++98"] + common_flags + else: + self.cc = [join(GOANNA_PATH, "goannacc"), "--with-cc=" + main_cc.replace('\\', '/'), "-std=gnu99", "--dialect=gnu", '--output-format="%s"' % self.GOANNA_FORMAT] + common_flags + self.cppc= [join(GOANNA_PATH, "goannac++"), "--with-cxx=" + main_cppc.replace('\\', '/'), "-std=gnu++98", "--dialect=gnu", '--output-format="%s"' % self.GOANNA_FORMAT] + common_flags self.ld = [join(tool_path, "arm-none-eabi-gcc"), "-Wl,--gc-sections", "-Wl,--wrap,main"] + self.cpu self.sys_libs = ["stdc++", "supc++", "m", "c", "gcc"] @@ -98,6 +104,16 @@ class GCC(mbedToolchain): WHERE, WHAT = 0, 1 state, file, message = WHERE, None, None for line in output.splitlines(): + match = self.goanna_parse_line(line) + if match is not None: + self.cc_info( + match.group('severity').lower(), + match.group('file'), + match.group('line'), + match.group('message') + ) + continue + # Each line should start with the file information: "filepath: ..." # i should point past the file path ^ # avoid the first column in Windows (C:\) diff --git a/workspace_tools/toolchains/iar.py b/workspace_tools/toolchains/iar.py index e4816d2f72..8f935b6f3f 100644 --- a/workspace_tools/toolchains/iar.py +++ b/workspace_tools/toolchains/iar.py @@ -20,7 +20,7 @@ from os.path import join, exists from workspace_tools.toolchains import mbedToolchain from workspace_tools.settings import IAR_PATH - +from workspace_tools.settings import GOANNA_PATH class IAR(mbedToolchain): LIBRARY_EXT = '.a' @@ -49,10 +49,14 @@ class IAR(mbedToolchain): c_flags.append("-r") IAR_BIN = join(IAR_PATH, "bin") + main_cc = join(IAR_BIN, "iccarm") self.asm = [join(IAR_BIN, "iasmarm")] + ["--cpu", target.core] - self.cc = [join(IAR_BIN, "iccarm")] + c_flags - self.cppc = [join(IAR_BIN, "iccarm"), "--c++", "--no_rtti", "--no_exceptions"] + c_flags - + if not "analyze" in self.options: + self.cc = [main_cc] + c_flags + self.cppc = [main_cc, "--c++", "--no_rtti", "--no_exceptions"] + c_flags + else: + self.cc = [join(GOANNA_PATH, "goannacc"), '--with-cc="%s"' % main_cc.replace('\\', '/'), "--dialect=iar-arm", '--output-format="%s"' % self.GOANNA_FORMAT] + c_flags + self.cppc = [join(GOANNA_PATH, "goannac++"), '--with-cxx="%s"' % main_cc.replace('\\', '/'), "--dialect=iar-arm", '--output-format="%s"' % self.GOANNA_FORMAT] + ["--c++", "--no_rtti", "--no_exceptions"] + c_flags self.ld = join(IAR_BIN, "ilinkarm") self.ar = join(IAR_BIN, "iarchive") self.elf2bin = join(IAR_BIN, "ielftool") @@ -67,6 +71,14 @@ class IAR(mbedToolchain): match.group('line'), match.group('message'), ) + match = self.goanna_parse_line(line) + if match is not None: + self.cc_info( + match.group('severity').lower(), + match.group('file'), + match.group('line'), + match.group('message') + ) def get_dep_opt(self, dep_path): return ["--dependencies", dep_path]