Bug 1535355 - Add --output & --format=json|text to ./mach static-analysis check, r=ahal,firefox-build-system-reviewers,chmanchester
authorBastien Abadie <bastien@mozilla.com>
Tue, 19 Mar 2019 18:26:50 +0000
changeset 465168 d2038c56d1389a5f472d8ee49a88a6af31278d7f
parent 465167 1976c178f2d19b040ca3d092ac2a877c80491b33
child 465169 708979f9c3f33c77fb2e9106054b215c77044a04
push id35732
push useropoprus@mozilla.com
push dateWed, 20 Mar 2019 10:52:37 +0000
treeherdermozilla-central@708979f9c3f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal, firefox-build-system-reviewers, chmanchester
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1535355 - Add --output & --format=json|text to ./mach static-analysis check, r=ahal,firefox-build-system-reviewers,chmanchester Differential Revision: https://phabricator.services.mozilla.com/D23523
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -725,24 +725,27 @@ class StaticAnalysisFooter(Footer):
         if monitor.current_file:
             parts.append(('bold', monitor.current_file))
 class StaticAnalysisOutputManager(OutputManager):
-    """Handles writing static analysis output to a terminal."""
+    """Handles writing static analysis output to a terminal or file."""
     def __init__(self, log_manager, monitor, footer):
         self.monitor = monitor
+        self.raw = ''
         OutputManager.__init__(self, log_manager, footer)
     def on_line(self, line):
         warning, relevant = self.monitor.on_line(line)
+        if relevant:
+            self.raw += line + '\n'
         if warning:
             self.log(logging.INFO, 'compiler_warning', warning,
                 'Warning: {flag} in {filename}: {message}')
         if relevant:
             self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
@@ -750,16 +753,32 @@ class StaticAnalysisOutputManager(Output
             if have_handler:
                 if have_handler:
+    def write(self, path, output_format):
+        assert output_format in ('text', 'json'), \
+            'Invalid output format {}'.format(output_format)
+        path = os.path.realpath(path)
+        if output_format == 'json':
+            self.monitor._warnings_database.save_to_file(path)
+        else:
+            with open(path, 'w') as f:
+                f.write(self.raw)
+        self.log(logging.INFO, 'write_output',
+                 {'path': path, 'format': output_format},
+                 'Wrote {format} output in {path}')
 class CCacheStats(object):
     """Holds statistics from ccache.
     Instances can be subtracted from each other to obtain differences.
     print() or str() the object to show a ``ccache -s`` like output
     of the captured stats.
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1691,18 +1691,22 @@ class StaticAnalysis(MachCommandBase):
     @CommandArgument('--strip', '-p', default='1', metavar='NUM',
                      help='Strip NUM leading components from file names in diff mode.')
     @CommandArgument('--fix', '-f', default=False, action='store_true',
                      help='Try to autofix errors detected by clang-tidy checkers.')
     @CommandArgument('--header-filter', '-h-f', default='', metavar='header_filter',
                      help='Regular expression matching the names of the headers to '
                           'output diagnostics from. Diagnostics from the main file '
                           'of each translation unit are always displayed')
+    @CommandArgument('--output', '-o', default=None,
+                     help='Write clang-tidy output in a file')
+    @CommandArgument('--format', default='text', choices=('text', 'json'),
+                     help='Output format to write in a file')
     def check(self, source=None, jobs=2, strip=1, verbose=False,
-              checks='-*', fix=False, header_filter=''):
+              checks='-*', fix=False, header_filter='', output=None, format='text'):
         from mozbuild.controller.building import (
@@ -1727,22 +1731,27 @@ class StaticAnalysis(MachCommandBase):
         self._compilation_commands_path = self.topobjdir
         self._clang_tidy_config = self._get_clang_tidy_config()
         args = self._get_clang_tidy_command(
             checks=checks, header_filter=header_filter, sources=source, jobs=jobs, fix=fix)
         monitor = StaticAnalysisMonitor(self.topsrcdir, self.topobjdir, total)
         footer = StaticAnalysisFooter(self.log_manager.terminal, monitor)
-        with StaticAnalysisOutputManager(self.log_manager, monitor, footer) as output:
-            rc = self.run_process(args=args, ensure_exit_code=False, line_handler=output.on_line, cwd=cwd)
+        with StaticAnalysisOutputManager(self.log_manager, monitor, footer) as output_manager:
+            rc = self.run_process(args=args, ensure_exit_code=False, line_handler=output_manager.on_line, cwd=cwd)
             self.log(logging.WARNING, 'warning_summary',
                      {'count': len(monitor.warnings_db)},
                      '{count} warnings present.')
+            # Write output file
+            if output is not None:
+                output_manager.write(output, format)
         if rc != 0:
             return rc
         # if we are building firefox for android it might be nice to
         # also analyze the java code base
         if self.substs['MOZ_BUILD_APP'] == 'mobile/android':
             rc = self.check_java(source, jobs, strip, verbose, skip_export=True)
         return rc