[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