Skip to content

Commit 62f1ac0

Browse files
committed
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).
1 parent 330e59f commit 62f1ac0

File tree

5 files changed

+68
-16
lines changed

5 files changed

+68
-16
lines changed

workspace_tools/options.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ def get_default_options_parser():
3535
help="clean the build directory")
3636

3737
parser.add_option("-o", "--options", action="append",
38-
help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information)')
38+
help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information, "analyze": run static code analyzer")')
3939

4040
return parser

workspace_tools/toolchains/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from workspace_tools.settings import BUILD_OPTIONS
2727

2828
import workspace_tools.hooks as hooks
29+
import re
2930

3031
def print_notify(event):
3132
# Default command line notification
@@ -141,7 +142,10 @@ class mbedToolchain:
141142
"Cortex-M0+": ["__CORTEX_M0PLUS", "ARM_MATH_CM0"],
142143
"Cortex-M4" : ["__CORTEX_M4", "ARM_MATH_CM4", "__FPU_PRESENT=1"],
143144
}
144-
145+
146+
GOANNA_FORMAT = "[Goanna] warning [%FILENAME%:%LINENO%] - [%CHECKNAME%(%SEVERITY%)] %MESSAGE%"
147+
GOANNA_DIAGNOSTIC_PATTERN = re.compile(r'"\[Goanna\] (?P<severity>warning) \[(?P<file>[^:]+):(?P<line>\d+)\] \- (?P<message>.*)"')
148+
145149
def __init__(self, target, options=None, notify=None):
146150
self.target = target
147151
self.name = self.__class__.__name__
@@ -168,6 +172,12 @@ def __init__(self, target, options=None, notify=None):
168172
self.labels = None
169173

170174
self.build_all = False
175+
176+
def goanna_parse_line(self, line):
177+
if "analyze" in self.options:
178+
return self.GOANNA_DIAGNOSTIC_PATTERN.match(line)
179+
else:
180+
return None
171181

172182
def get_symbols(self):
173183
if self.symbols is None:

workspace_tools/toolchains/arm.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from workspace_tools.toolchains import mbedToolchain
2121
from workspace_tools.settings import ARM_BIN, ARM_INC, ARM_LIB, MY_ARM_CLIB, ARM_CPPLIB
2222
from workspace_tools.hooks import hook_tool
23+
from workspace_tools.settings import GOANNA_PATH
2324

2425
class ARM(mbedToolchain):
2526
LINKER_EXT = '.sct'
@@ -39,7 +40,8 @@ def __init__(self, target, options=None, notify=None):
3940
else:
4041
cpu = target.core
4142

42-
common = [join(ARM_BIN, "armcc"), "-c",
43+
main_cc = join(ARM_BIN, "armcc")
44+
common = ["-c",
4345
"--cpu=%s" % cpu, "--gnu",
4446
"-Ospace", "--split_sections", "--apcs=interwork",
4547
"--brief_diagnostics", "--restrict"
@@ -56,9 +58,13 @@ def __init__(self, target, options=None, notify=None):
5658
'-I%s' % ARM_INC
5759
]
5860

59-
self.asm = common
60-
self.cc = common + common_c + ["--c99"]
61-
self.cppc = common + common_c + ["--cpp", "--no_rtti"]
61+
self.asm = [main_cc] + common
62+
if not "analyze" in self.options:
63+
self.cc = [main_cc] + common + common_c + ["--c99"]
64+
self.cppc = [main_cc] + common + common_c + ["--cpp", "--no_rtti"]
65+
else:
66+
self.cc = [join(GOANNA_PATH, "goannacc"), "--with-cc=" + main_cc.replace('\\', '/'), "--dialect=armcc", '--output-format="%s"' % self.GOANNA_FORMAT] + common + common_c + ["--c99"]
67+
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"]
6268

6369
self.ld = [join(ARM_BIN, "armlink")]
6470
self.sys_libs = []
@@ -92,7 +98,15 @@ def parse_output(self, output):
9298
match.group('line'),
9399
match.group('message')
94100
)
95-
101+
match = self.goanna_parse_line(line)
102+
if match is not None:
103+
self.cc_info(
104+
match.group('severity').lower(),
105+
match.group('file'),
106+
match.group('line'),
107+
match.group('message')
108+
)
109+
96110
def archive(self, objects, lib_path):
97111
self.default_cmd([self.ar, '-r', lib_path] + objects)
98112

workspace_tools/toolchains/gcc.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from workspace_tools.toolchains import mbedToolchain
2121
from workspace_tools.settings import GCC_ARM_PATH, GCC_CR_PATH, GCC_CS_PATH, CW_EWL_PATH, CW_GCC_PATH
22-
22+
from workspace_tools.settings import GOANNA_PATH
2323

2424
class GCC(mbedToolchain):
2525
LINKER_EXT = '.ld'
@@ -59,11 +59,17 @@ def __init__(self, target, options=None, notify=None, tool_path=""):
5959

6060
if "debug-info" in self.options:
6161
common_flags.append("-g")
62-
62+
6363
self.asm = [join(tool_path, "arm-none-eabi-as")] + self.cpu
64-
65-
self.cc = [join(tool_path, "arm-none-eabi-gcc"), "-std=gnu99"] + common_flags
66-
self.cppc =[join(tool_path, "arm-none-eabi-g++"), "-std=gnu++98"] + common_flags
64+
65+
main_cc = join(tool_path, "arm-none-eabi-gcc")
66+
main_cppc = join(tool_path, "arm-none-eabi-g++")
67+
if not "analyze" in self.options:
68+
self.cc = [main_cc, "-std=gnu99"] + common_flags
69+
self.cppc =[main_cppc, "-std=gnu++98"] + common_flags
70+
else:
71+
self.cc = [join(GOANNA_PATH, "goannacc"), "--with-cc=" + main_cc.replace('\\', '/'), "-std=gnu99", "--dialect=gnu", '--output-format="%s"' % self.GOANNA_FORMAT] + common_flags
72+
self.cppc= [join(GOANNA_PATH, "goannac++"), "--with-cxx=" + main_cppc.replace('\\', '/'), "-std=gnu++98", "--dialect=gnu", '--output-format="%s"' % self.GOANNA_FORMAT] + common_flags
6773

6874
self.ld = [join(tool_path, "arm-none-eabi-gcc"), "-Wl,--gc-sections", "-Wl,--wrap,main"] + self.cpu
6975
self.sys_libs = ["stdc++", "supc++", "m", "c", "gcc"]
@@ -98,6 +104,16 @@ def parse_output(self, output):
98104
WHERE, WHAT = 0, 1
99105
state, file, message = WHERE, None, None
100106
for line in output.splitlines():
107+
match = self.goanna_parse_line(line)
108+
if match is not None:
109+
self.cc_info(
110+
match.group('severity').lower(),
111+
match.group('file'),
112+
match.group('line'),
113+
match.group('message')
114+
)
115+
continue
116+
101117
# Each line should start with the file information: "filepath: ..."
102118
# i should point past the file path ^
103119
# avoid the first column in Windows (C:\)

workspace_tools/toolchains/iar.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from workspace_tools.toolchains import mbedToolchain
2222
from workspace_tools.settings import IAR_PATH
23-
23+
from workspace_tools.settings import GOANNA_PATH
2424

2525
class IAR(mbedToolchain):
2626
LIBRARY_EXT = '.a'
@@ -49,10 +49,14 @@ def __init__(self, target, options=None, notify=None):
4949
c_flags.append("-r")
5050

5151
IAR_BIN = join(IAR_PATH, "bin")
52+
main_cc = join(IAR_BIN, "iccarm")
5253
self.asm = [join(IAR_BIN, "iasmarm")] + ["--cpu", target.core]
53-
self.cc = [join(IAR_BIN, "iccarm")] + c_flags
54-
self.cppc = [join(IAR_BIN, "iccarm"), "--c++", "--no_rtti", "--no_exceptions"] + c_flags
55-
54+
if not "analyze" in self.options:
55+
self.cc = [main_cc] + c_flags
56+
self.cppc = [main_cc, "--c++", "--no_rtti", "--no_exceptions"] + c_flags
57+
else:
58+
self.cc = [join(GOANNA_PATH, "goannacc"), '--with-cc="%s"' % main_cc.replace('\\', '/'), "--dialect=iar-arm", '--output-format="%s"' % self.GOANNA_FORMAT] + c_flags
59+
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
5660
self.ld = join(IAR_BIN, "ilinkarm")
5761
self.ar = join(IAR_BIN, "iarchive")
5862
self.elf2bin = join(IAR_BIN, "ielftool")
@@ -67,6 +71,14 @@ def parse_output(self, output):
6771
match.group('line'),
6872
match.group('message'),
6973
)
74+
match = self.goanna_parse_line(line)
75+
if match is not None:
76+
self.cc_info(
77+
match.group('severity').lower(),
78+
match.group('file'),
79+
match.group('line'),
80+
match.group('message')
81+
)
7082

7183
def get_dep_opt(self, dep_path):
7284
return ["--dependencies", dep_path]

0 commit comments

Comments
 (0)