Bug 1535355 - Add --output option to ./mach clang-format, r=ahal
authorBastien Abadie <bastien@mozilla.com>
Tue, 19 Mar 2019 20:33:39 +0000
changeset 465167 1976c178f2d19b040ca3d092ac2a877c80491b33
parent 465166 bc864bb5cd2bfa0f6b76eba69ee2cbc07d39643a
child 465168 d2038c56d1389a5f472d8ee49a88a6af31278d7f
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
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 option to ./mach clang-format, r=ahal Differential Revision: https://phabricator.services.mozilla.com/D23522
python/mozbuild/mozbuild/mach_commands.py
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -2328,42 +2328,50 @@ class StaticAnalysis(MachCommandBase):
             return rc
         checkers, _ = self._get_infer_config()
         print('Infer checks:')
         for checker in checkers:
             print(' '*4 + checker)
         return 0
 
     @Command('clang-format',  category='misc', description='Run clang-format on current changes')
-    @CommandArgument('--show', '-s', action='store_true', default=False,
-                     help='Show diff output on instead of applying changes')
+    @CommandArgument('--show', '-s', action='store_const', const='stdout', dest='output_path',
+                     help='Show diff output on stdout instead of applying changes')
     @CommandArgument('--assume-filename', '-a', nargs=1, default=None,
                      help='This option is usually used in the context of hg-formatsource.'
                           'When reading from stdin, clang-format assumes this '
                           'filename to look for a style config file (with '
                           '-style=file) and to determine the language. When '
                           'specifying this option only one file should be used '
                           'as an input and the output will be forwarded to stdin. '
                           'This option also impairs the download of the clang-tools '
                           'and assumes the package is already located in it\'s default '
                           'location')
     @CommandArgument('--path', '-p', nargs='+', default=None,
                      help='Specify the path(s) to reformat')
     @CommandArgument('--commit', '-c', default=None,
                      help='Specify a commit to reformat from.'
                           'For git you can also pass a range of commits (foo..bar)'
                           'to format all of them at the same time.')
-    def clang_format(self, show, assume_filename, path, commit, verbose=False):
+    @CommandArgument('--output', '-o', default=None, dest='output_path',
+                     help='Specify a file handle to write clang-format raw output instead of '
+                          'applying changes. This can be stdout or a file path.')
+    def clang_format(self, assume_filename, path, commit, output_path=None, verbose=False):
         # Run clang-format or clang-format-diff on the local changes
         # or files/directories
         if path is not None:
             path = self._conv_to_abspath(path)
 
         os.chdir(self.topsrcdir)
 
+        # Load output file handle, either stdout or a file handle in write mode
+        output = None
+        if output_path is not None:
+            output = sys.stdout if output_path == 'stdout' else open(output_path, 'w')
+
         # With assume_filename we want to have stdout clean since the result of the
         # format will be redirected to stdout. Only in case of errror we
         # write something to stdout.
         # We don't actually want to get the clang-tools here since we want in some
         # scenarios to do this in parallel so we relay on the fact that the tools
         # have already been downloaded via './mach bootstrap' or directly via
         # './mach static-analysis install'
         if assume_filename:
@@ -2377,22 +2385,22 @@ class StaticAnalysis(MachCommandBase):
                 return 1
         else:
             rc = self._get_clang_tools(verbose=verbose)
             if rc != 0:
                 return rc
 
         if path is None:
             return self._run_clang_format_diff(self._clang_format_diff,
-                                               self._clang_format_path, show, commit)
+                                               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, show, path)
+        return self._run_clang_format_path(self._clang_format_path, path, output)
 
     def _verify_checker(self, item):
         check = item['name']
         test_file_path = mozpath.join(self._clang_tidy_base_path, "test", check)
         test_file_path_cpp = test_file_path + '.cpp'
         test_file_path_json = test_file_path + '.json'
 
         self.log(logging.INFO, 'static-analysis', {},
@@ -2759,31 +2767,32 @@ class StaticAnalysis(MachCommandBase):
                                                        skip_cache=skip_cache,
                                                        from_build=[job],
                                                        no_unpack=False,
                                                        retry=0)
         # Change back the cwd
         os.chdir(currentWorkingDir)
         return rc
 
-    def _run_clang_format_diff(self, clang_format_diff, clang_format, show, commit):
+    def _run_clang_format_diff(self, clang_format_diff, clang_format, commit, output_file):
         # Run clang-format on the diff
         # Note that this will potentially miss a lot things
         from subprocess import Popen, PIPE, check_output, CalledProcessError
 
         diff_process = Popen(self._get_clang_format_diff_command(commit), stdout=PIPE)
         args = [sys.executable, clang_format_diff, "-p1", "-binary=%s" % clang_format]
 
-        if not show:
+        if not output_file:
             args.append("-i")
         try:
             output = check_output(args, stdin=diff_process.stdout)
-            if show:
+            if output_file:
                 # We want to print the diffs
-                print(output)
+                print(output, file=output_file)
+
             return 0
         except CalledProcessError as e:
             # Something wrong happend
             print("clang-format: An error occured while running clang-format-diff.")
             return e.returncode
 
     def _is_ignored_path(self, ignored_dir_re, f):
         # Remove upto topsrcdir in pathname and match
@@ -2846,37 +2855,37 @@ class StaticAnalysis(MachCommandBase):
 
         process = subprocess.Popen(args, stdin=subprocess.PIPE)
         with open(paths[0], 'r') as fin:
             process.stdin.write(fin.read())
             process.stdin.close()
             process.wait();
             return 0
 
-    def _run_clang_format_path(self, clang_format, show, paths):
+    def _run_clang_format_path(self, clang_format, paths, output_file):
 
         # Run clang-format on files or directories directly
         from subprocess import check_output, CalledProcessError
 
         args = [clang_format, "-i"]
 
-        if show:
+        if output_file:
             # We just want to show the diff, we create the directory to copy it
             tmpdir = os.path.join(self.topobjdir, 'tmp')
             if not os.path.exists(tmpdir):
                 os.makedirs(tmpdir)
 
         path_list = self._generate_path_list(paths)
 
         if path_list == []:
             return
 
         print("Processing %d file(s)..." % len(path_list))
 
-        if show:
+        if output_file:
             for i in range(0, len(path_list)):
                 l = path_list[i: (i + 1)]
 
                 # Copy the files into a temp directory
                 # and run clang-format on the temp directory
                 # and show the diff
                 original_path = l[0]
                 local_path = ntpath.basename(original_path)
@@ -2899,17 +2908,17 @@ class StaticAnalysis(MachCommandBase):
                 diff_command = ["diff", "-u", original_path, target_file]
                 try:
                     output = check_output(diff_command)
                 except CalledProcessError as e:
                     # diff -u returns 0 when no change
                     # here, we expect changes. if we are here, this means that
                     # there is a diff to show
                     if e.output:
-                        print(e.output)
+                        print(e.output, file=output_file)
 
             shutil.rmtree(tmpdir)
             return 0
 
         # Run clang-format in parallel trying to saturate all of the available cores.
         import concurrent.futures
         import multiprocessing
         import math
@@ -2945,16 +2954,17 @@ class StaticAnalysis(MachCommandBase):
                 ret_val = future.result()
                 if ret_val is not None:
                     error_code = ret_val
 
             if error_code is not None:
                 return error_code
         return 0
 
+
 @CommandProvider
 class Vendor(MachCommandBase):
     """Vendor third-party dependencies into the source repository."""
 
     @Command('vendor', category='misc',
              description='Vendor third-party dependencies into the source repository.')
     def vendor(self):
         self.parser.print_usage()