Bug 1543492 - for clang based static-analysis and formating add a package version that represents a baseline if we continue the analysis or prompt for an update. r=sylvestre
authorAndi-Bogdan Postelnicu <bpostelnicu@mozilla.com>
Thu, 11 Apr 2019 09:58:59 +0000
changeset 469323 bc17715cc318a32b92e818ff3bfb666baf0c893f
parent 469322 1c603eed6ce480b38bf8ac26aa706415d12f6eb8
child 469324 4b27c94d46b842d903da1fc388ca3f7e17817b22
push id83077
push userbpostelnicu@mozilla.com
push dateFri, 12 Apr 2019 18:06:34 +0000
treeherderautoland@bc17715cc318 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssylvestre
bugs1543492
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 1543492 - for clang based static-analysis and formating add a package version that represents a baseline if we continue the analysis or prompt for an update. r=sylvestre Differential Revision: https://phabricator.services.mozilla.com/D27042
python/mozbuild/mozbuild/mach_commands.py
tools/clang-tidy/config.yaml
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1672,16 +1672,18 @@ class StaticAnalysisMonitor(object):
 class StaticAnalysis(MachCommandBase):
     """Utilities for running C++ static analysis checks and format."""
 
     # List of file extension to consider (should start with dot)
     _format_include_extensions = ('.cpp', '.c', '.cc', '.h', '.m', '.mm')
     # File contaning all paths to exclude from formatting
     _format_ignore_file = '.clang-format-ignore'
 
+    _clang_tidy_config = None
+
     @Command('static-analysis', category='testing',
              description='Run C++ static analysis checks')
     def static_analysis(self):
         # If not arguments are provided, just print a help message.
         mach = Mach(os.getcwd())
         mach.run(['static-analysis', '--help'])
 
     @StaticAnalysisSubCommand('static-analysis', 'check',
@@ -1719,19 +1721,27 @@ class StaticAnalysis(MachCommandBase):
         from mozbuild.controller.building import (
             StaticAnalysisFooter,
             StaticAnalysisOutputManager,
         )
 
         self._set_log_level(verbose)
         self.log_manager.enable_all_structured_loggers()
 
+        rc = self._get_clang_tools(verbose=verbose)
+        if rc != 0:
+            return rc
+
+        if self._is_version_eligible() is False:
+            self.log(logging.ERROR, 'static-analysis', {}, "You're using an old version of clang-format binary."
+                            " Please update to a more recent one by running: './mach bootstrap'")
+            return 1
+
         rc = self._build_compile_db(verbose=verbose)
         rc = rc or self._build_export(jobs=jobs, verbose=verbose)
-        rc = rc or self._get_clang_tools(verbose=verbose)
         if rc != 0:
             return rc
 
         # Use outgoing files instead of source files
         if outgoing:
             repo = get_repository_object(self.topsrcdir)
             files = repo.get_outgoing_files()
             source = map(os.path.abspath, files)
@@ -1748,17 +1758,18 @@ class StaticAnalysis(MachCommandBase):
                 if name_re.search(f['file']):
                     total = total + 1
 
         if not total:
             return 0
 
         cwd = self.topobjdir
         self._compilation_commands_path = self.topobjdir
-        self._clang_tidy_config = self._get_clang_tidy_config()
+        if self._clang_tidy_config is None:
+            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_manager:
             rc = self.run_process(args=args, ensure_exit_code=False, line_handler=output_manager.on_line, cwd=cwd)
@@ -2198,16 +2209,44 @@ class StaticAnalysis(MachCommandBase):
             file_handler = open(mozpath.join(self.topsrcdir, "tools", "clang-tidy", "config.yaml"))
             config = yaml.safe_load(file_handler)
         except Exception:
             print('Looks like config.yaml is not valid, we are going to use default'
                   ' values for the rest of the analysis.')
             return None
         return config
 
+    def _is_version_eligible(self):
+        # make sure that we've cached self._clang_tidy_config
+        if self._clang_tidy_config is None:
+            self._clang_tidy_config = self._get_clang_tidy_config()
+
+        version = None
+        if 'package_version' in self._clang_tidy_config:
+            version = self._clang_tidy_config['package_version']
+        else:
+            self.log(logging.ERROR, 'static-analysis', {}, "Unable to find 'package_version' in the config.yml")
+            return False
+
+        # Because the fact that we ship together clang-tidy and clang-format
+        # we are sure that these two will always share the same version.
+        # Thus in order to determine that the version is compatible we only
+        # need to check one of them, going with clang-format
+        cmd = [self._clang_format_path, '--version']
+        try:
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('utf-8')
+            version_string = 'clang-format version ' + version
+            if output.startswith(version_string):
+                return True
+        except subprocess.CalledProcessError as e:
+            self.log(logging.ERROR, 'static-analysis', {},
+                     "Error determining the version clang-tidy/format binary, please see the attached exception: \n{}".format(e.output))
+
+        return False
+
     def _get_clang_tidy_command(self, checks, header_filter, sources, jobs, fix):
 
         if checks == '-*':
             checks = self._get_checks()
 
         common_args = ['-clang-tidy-binary', self._clang_tidy_path,
                        '-clang-apply-replacements-binary', self._clang_apply_replacements,
                        '-checks=%s' % checks,
@@ -2750,16 +2789,21 @@ class StaticAnalysis(MachCommandBase):
             if not self._do_clang_tools_exist():
                 print("clang-format: Unable to set locate clang-format tools.")
                 return 1
         else:
             rc = self._get_clang_tools(verbose=verbose)
             if rc != 0:
                 return rc
 
+        if self._is_version_eligible() is False:
+            self.log(logging.ERROR, 'static-analysis', {}, "You're using an old version of clang-format binary."
+                            " Please update to a more recent one by running: './mach bootstrap'")
+            return 1
+
         if path is None:
             return self._run_clang_format_diff(self._clang_format_diff,
                                                self._clang_format_path, commit, output)
 
         if assume_filename:
             return self._run_clang_format_in_console(self._clang_format_path, path, assume_filename)
 
         return self._run_clang_format_path(self._clang_format_path, path, output, output_format)
--- a/tools/clang-tidy/config.yaml
+++ b/tools/clang-tidy/config.yaml
@@ -13,17 +13,17 @@ target: obj-x86_64-pc-linux-gnu
 # 5. Commit this file + the .cpp test case + the json result
 platforms:
   - linux64
   - macosx64
   - win32
   - win64
 # Minimum clang-tidy version that is required for all the following checkers
 # to work properly.
-min_clang_tidy: "8.0.0"
+package_version: "8.0.0"
 clang_checkers:
   - name: -*
     publish: !!bool no
   - name: bugprone-argument-comment
   - name: bugprone-assert-side-effect
   - name: bugprone-bool-pointer-implicit-conversion
   - name: bugprone-forward-declaration-namespace
   - name: bugprone-incorrect-roundings