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
bugs1535355
milestone68.0a1
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
python/mozbuild/mozbuild/controller/building.py
python/mozbuild/mozbuild/mach_commands.py
--- 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))
 
         self.write(parts)
 
 
 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}')
         else:
@@ -750,16 +753,32 @@ class StaticAnalysisOutputManager(Output
             if have_handler:
                 self.handler.acquire()
             try:
                 self.refresh()
             finally:
                 if have_handler:
                     self.handler.release()
 
+    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 (
             StaticAnalysisFooter,
             StaticAnalysisOutputManager,
         )
 
         self._set_log_level(verbose)
         self.log_manager.enable_all_structured_loggers()
 
@@ -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