[Pkg-debile-commits] [debile-slave] 82/100: Added support for scan-build.
Sylvestre Ledru
sylvestre at alioth.debian.org
Mon Aug 19 14:53:14 UTC 2013
This is an automated email from the git hooks/post-receive script.
sylvestre pushed a commit to branch master
in repository debile-slave.
commit 98c87a950f622cc523819ec06b2fc0aec9ebc825
Author: Léo Cavaillé <leo at cavaille.net>
Date: Fri Jul 19 10:29:58 2013 +0200
Added support for scan-build.
This works with a patched version of sbuild.
---
ethel/commands/__init__.py | 2 +
ethel/commands/clanganalyzer.py | 10 ++
ethel/runners/clanganalyzer.py | 84 +++++++++++++++++
ethel/wrappers/clanganalyzer.py | 199 +++++++++++++++++++++++++++++++++++++++
4 files changed, 295 insertions(+)
diff --git a/ethel/commands/__init__.py b/ethel/commands/__init__.py
index 4c700f5..e041fd4 100644
--- a/ethel/commands/__init__.py
+++ b/ethel/commands/__init__.py
@@ -3,6 +3,8 @@ import importlib
PLUGINS = {
"build": "ethel.commands.build",
+ "clanganalyzer": "ethel.commands.clanganalyzer",
+
"pep8": "ethel.commands.pep8",
"perlcritic": "ethel.commands.perlcritic",
"cppcheck": "ethel.commands.cppcheck",
diff --git a/ethel/commands/clanganalyzer.py b/ethel/commands/clanganalyzer.py
new file mode 100644
index 0000000..142ba27
--- /dev/null
+++ b/ethel/commands/clanganalyzer.py
@@ -0,0 +1,10 @@
+from ethel.runners.clanganalyzer import clanganalyzer
+
+
+# target package firehose
+def run(dsc, package, job, firehose):
+ suite = job['suite']
+ #FIXME : hardcoded here, but does not really matter
+ arch = 'amd64'
+
+ return clanganalyzer(dsc, suite, arch, firehose)
diff --git a/ethel/runners/clanganalyzer.py b/ethel/runners/clanganalyzer.py
new file mode 100644
index 0000000..8211e6c
--- /dev/null
+++ b/ethel/runners/clanganalyzer.py
@@ -0,0 +1,84 @@
+from ethel.utils import run_command
+from ethel.wrappers.clanganalyzer import parse_scandir
+from schroot import schroot
+
+import os
+import glob
+import shutil
+
+def clanganalyzer(package, suite, arch, analysis):
+ chroot_name = "%s-%s" % (suite, arch)
+ with schroot(chroot_name) as chroot:
+ # We should have the dsc file to bulid
+ dsc = os.path.basename(package)
+ if not dsc.endswith('.dsc'):
+ raise ValueError("clanganalyzer runner must receive a dsc file")
+
+ # Setup the chroot for scan-build run
+ # 1/ install clang
+ # TODO: check the return codes
+ out, err, ret = chroot.run([
+ 'apt-get', 'install', '-y', 'clang', 'wget'
+ ], user='root')
+
+ # 2/ fake dpkg-buildpackage in the schroot
+ # Replace the real dpkg-buildpackage by our script
+ out_, err, ret = chroot.run([
+ 'mv', '/usr/bin/dpkg-buildpackage', '/usr/bin/dpkg-buildpackage.faked'
+ ], user='root')
+ out += out_
+
+ internal_report_dir = "/tmp/scan-build/"
+ # We will output the scan-build plist reports there
+ out_, err, ret = chroot.run([
+ 'mkdir', '-p', internal_report_dir
+ ], user='root')
+ out += out_
+ out_, err, ret = chroot.run([
+ 'chmod', '777', internal_report_dir
+ ], user='root')
+ out += out_
+
+ # Create the script
+ fake_dpkg_url = "http://leo.cavaille.net/public/dpkg-buildpackage"
+ out_, err, ret = chroot.run([
+ 'wget', '-O', '/usr/bin/dpkg-buildpackage', fake_dpkg_url
+ ], user='root')
+ out += out_
+
+ # Make it executable
+ out_, err, ret = chroot.run([
+ 'chmod', '755', '/usr/bin/dpkg-buildpackage'
+ ], user='root')
+ out += out_
+
+
+ # Now run sbuild in this session chroot for the package
+ out_, err, ret = run_command([
+ "sbuild",
+ "-A",
+ "--use-schroot-session", chroot.session,
+ "-v",
+ "-d", suite,
+ "-j", "8",
+ package,
+ ])
+ out += out_
+
+ failed = ret != 0
+
+ # Parse the plist reports into Firehose and return
+ # WARN : if the previous run did not delete the folder, this will fail
+ # worst, if we run several instances of virtual builders, this will fail because
+ # by default /tmp is a bind mount from the physical server
+ reports_dir = glob.glob(internal_report_dir+'*')
+
+ # If the result is empty then scan-build returned nothing because no directory
+ # was created
+ if reports_dir:
+ for reports in parse_scandir(reports_dir[0]):
+ for issue in reports:
+ analysis.results.append(issue)
+ shutil.rmtree(reports_dir[0])
+
+ return analysis, out, failed
diff --git a/ethel/wrappers/clanganalyzer.py b/ethel/wrappers/clanganalyzer.py
new file mode 100644
index 0000000..ada673d
--- /dev/null
+++ b/ethel/wrappers/clanganalyzer.py
@@ -0,0 +1,199 @@
+# Copyright 2013 David Malcolm <dmalcolm at redhat.com>
+# Copyright 2013 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+# USA
+
+# Parser for the .plist files emitted by the clang-static-analyzer,
+# when "-plist" is passed as an option to "scan-build" or "clang"
+
+# This has been taken from the firehose repository and changed to fit the
+# needs here
+
+# Potential FIXME : in Debian we use newer versions of clang
+# Developed against output from clang-3.0-14.fc17
+
+import glob
+import os
+import plistlib
+from pprint import pprint
+import sys
+
+from firehose.model import Message, Function, Point, Range, \
+ File, Location, Generator, Metadata, Analysis, Issue, Sut, Trace, \
+ State, Notes
+
+def parse_scandir(resultdir):
+ """
+ Given a path to a directory of scan-build output, parse it and
+ yield Analysis instances
+ """
+ for filename in glob.glob(os.path.join(resultdir, 'report-*.plist')):
+ yield parse_plist(filename)
+
+def parse_plist(pathOrFile):
+ """
+ Given a .plist file emitted by clang-static-analyzer (e.g. via
+ scan-build), parse it and return an Analysis instance
+ """
+ plist = plistlib.readPlist(pathOrFile)
+ # We now have the .plist file as a hierarchy of dicts, lists, etc
+
+ # Handy debug dump:
+ if 0:
+ pprint(plist)
+
+ # A list of filenames, apparently referenced by index within
+ # diagnostics:
+ files = plist['files']
+
+ for diagnostic in plist['diagnostics']:
+ if 0:
+ pprint(diagnostic)
+
+ cwe = None
+
+ # TODO: we're not yet handling the following:
+ # diagnostic['category']
+ # diagnostic['type']
+
+ message = Message(text=diagnostic['description'])
+
+ loc = diagnostic['location']
+ location = Location(file=File(givenpath=files[loc.file],
+ abspath=None),
+
+ # FIXME: doesn't tell us function name
+ # TODO: can we patch this upstream?
+ function=None,
+
+ point=Point(int(loc.line),
+ int(loc.col)))
+
+ notes = None
+
+ trace = make_trace(files, diagnostic['path'])
+
+ issue = Issue(cwe,
+ None, # FIXME: can we get at the test id?
+ location, message, notes, trace)
+
+ yield issue
+
+def make_point_from_plist_point(loc):
+ # point:
+ # e.g. {'col': 2, 'file': 0, 'line': 130}
+ return Point(int(loc.line),
+ int(loc.col))
+
+def make_location_from_point(files, loc):
+ # loc:
+ # e.g. {'col': 2, 'file': 0, 'line': 130}
+ location = Location(file=File(givenpath=files[loc.file],
+ abspath=None),
+
+ # FIXME: doesn't tell us function name
+ # TODO: can we patch this upstream?
+ function=Function(''),
+
+ point=make_point_from_plist_point(loc))
+ return location
+
+def make_location_from_range(files, range_):
+ # range_:
+ # e.g.:
+ # [{'col': 18, 'file': 0, 'line': 165},
+ # {'col': 21, 'file': 0, 'line': 165}]
+ assert len(range_) == 2
+ start = range_[0]
+ end = range_[1]
+ assert start['file'] == end['file']
+
+ if start == end:
+ point = make_point_from_plist_point(start)
+ range_ = None
+ else:
+ point = None
+ range_ = Range(start=make_point_from_plist_point(start),
+ end=make_point_from_plist_point(end))
+
+ location = Location(file=File(givenpath=files[start['file']],
+ abspath=None),
+
+ # FIXME: doesn't tell us function name
+ # TODO: can we patch this upstream?
+ function=Function(''),
+
+ point=point,
+ range_=range_)
+
+ return location
+
+def make_trace(files, path):
+ """
+ Construct a Trace instance from the .plist's 'path' list
+ """
+ trace = Trace([])
+ lastlocation = None
+ for node in path:
+ if 0:
+ pprint(node)
+
+ kind = node['kind']
+
+ if kind == 'event':
+ # e.g.:
+ # {'extended_message': "Value stored to 'ret' is never read",
+ # 'kind': 'event',
+ # 'location': {'col': 2, 'file': 0, 'line': 130},
+ # 'message': "Value stored to 'ret' is never read",
+ # 'ranges': [[{'col': 8, 'file': 0, 'line': 130},
+ # {'col': 29, 'file': 0, 'line': 130}]]}
+
+ # TODO: we're not yet handling the following:
+ # node['extended_message']
+ # node['ranges']
+
+ loc = node['location']
+ location = make_location_from_point(files, loc)
+
+ notes = Notes(node['message'])
+ trace.add_state(State(location, notes))
+
+ lastlocation = location
+
+ elif kind == 'control':
+ # e.g.:
+ # {'edges': [{'end': [{'col': 9, 'file': 0, 'line': 161},
+ # {'col': 9, 'file': 0, 'line': 161}],
+ # 'start': [{'col': 2, 'file': 0, 'line': 161},
+ # {'col': 2, 'file': 0, 'line': 161}]}],
+ # 'kind': 'control'}
+ edges = node['edges']
+ for edge in edges:
+ edge_start = edge.start
+ edge_end = edge.end
+
+ startloc = make_location_from_range(files, edge_start)
+ endloc = make_location_from_range(files, edge_end)
+
+ if startloc != lastlocation:
+ trace.add_state(State(startloc, None))
+ trace.add_state(State(endloc, None))
+ lastlocation = endloc
+ else:
+ raise ValueError('unknown kind: %r' % kind)
+ return trace
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-debile/debile-slave.git
More information about the Pkg-debile-commits
mailing list