Bug 1392504 - clang-format changed files by default, r=franziskus
authorMartin Thomson <martin.thomson@gmail.com>
Tue, 22 Aug 2017 14:05:45 +1000
changeset 13543 181a42a2e523a39305342e60bdc9d80f9f2245cf
parent 13542 c4850aa1c407e7dad4f94d28676d92ac75eeacf6
child 13544 e0dea8f21d3c579e53350779268fab5bbdfa2a9c
push id2334
push usermartin.thomson@gmail.com
push dateTue, 22 Aug 2017 23:45:42 +0000
reviewersfranziskus
bugs1392504
Bug 1392504 - clang-format changed files by default, r=franziskus
mach
--- a/mach
+++ b/mach
@@ -1,14 +1,14 @@
 #!/usr/bin/env python
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
-################################################################################
+##########################################################################
 #
 # This is a collection of helper tools to get stuff done in NSS.
 #
 
 import sys
 import argparse
 import subprocess
 import os
@@ -18,23 +18,25 @@ from hashlib import sha256
 cwd = os.path.dirname(os.path.abspath(__file__))
 
 
 class cfAction(argparse.Action):
     docker_command = ["docker"]
     restorecon = None
 
     def __call__(self, parser, args, values, option_string=None):
-        if "noroot" not in values:
+        if not args.noroot:
             self.setDockerCommand()
+
+        if values:
+            files = [os.path.abspath(x) for x in values]
         else:
-            values.remove("noroot")
-        files = [os.path.join('/home/worker/nss',
-                              os.path.relpath(os.path.abspath(x), start=cwd))
-                     for x in values]
+            files = self.modifiedFiles()
+        files = [os.path.join('/home/worker/nss', os.path.relpath(x, start=cwd))
+                 for x in files]
 
         # First check if we can run docker.
         try:
             with open(os.devnull, "w") as f:
                 subprocess.check_call(
                     self.docker_command + ["images"], stdout=f)
         except:
             print("Please install docker and start the docker daemon.")
@@ -54,19 +56,20 @@ class cfAction(argparse.Action):
             ]
             with open(os.devnull, "w") as f:
                 subprocess.check_call(command, stdout=f)
         except:
             print("I have to build the docker image first.")
             self.buildImage(docker_image, cf_docker_folder)
 
         command = self.docker_command + [
-                'run', '-v', cwd + ':/home/worker/nss:Z', '--rm', '-ti', docker_image
+            'run', '-v', cwd + ':/home/worker/nss:Z', '--rm', '-ti', docker_image
         ]
-        # The clang format script returns 1 if something's to do. We don't care.
+        # The clang format script returns 1 if something's to do. We don't
+        # care.
         subprocess.call(command + files)
         if self.restorecon is not None:
             subprocess.call([self.restorecon, '-R', cwd])
 
     def filesChanged(self, path):
         hash = sha256()
         for dirname, dirnames, files in os.walk(path):
             for file in files:
@@ -92,24 +95,50 @@ class cfAction(argparse.Action):
         return
 
     def setDockerCommand(self):
         if platform.system() == "Linux":
             from distutils.spawn import find_executable
             self.restorecon = find_executable('restorecon')
             self.docker_command = ["sudo"] + self.docker_command
 
+    def modifiedFiles(self):
+        files = []
+        if os.path.exists(os.path.join(cwd, '.hg')):
+            st = subprocess.Popen(['hg', 'status', '-m', '-a'],
+                                  cwd=cwd, stdout=subprocess.PIPE)
+            for line in iter(st.stdout.readline, ''):
+                files += [line[2:].rstrip()]
+        elif os.path.exists(os.path.join(cwd, '.git')):
+            st = subprocess.Popen(['git', 'status', '--porcelain'],
+                                  cwd=cwd, stdout=subprocess.PIPE)
+            for line in iter(st.stdout.readline, ''):
+                if line[1] == 'M' or line[1] != 'D' and \
+                        (line[0] == 'M' or line[0] == 'A' or
+                         line[0] == 'C' or line[0] == 'U'):
+                    files += [line[3:].rstrip()]
+                elif line[0] == 'R':
+                    files += [line[line.index(' -> ', beg=4) + 4:]]
+        else:
+            print('Warning: neither mercurial nor git detected!')
+
+        def isFormatted(x):
+            return x[-2:] == '.c' or x[-3:] == '.cc' or x[-2:] == '.h'
+        return [x for x in files if isFormatted(x)]
+
 
 class buildAction(argparse.Action):
+
     def __call__(self, parser, args, values, option_string=None):
         cwd = os.path.dirname(os.path.abspath(__file__))
         subprocess.check_call([cwd + "/build.sh"] + values)
 
 
 class testAction(argparse.Action):
+
     def runTest(self, test, cycles="standard"):
         cwd = os.path.dirname(os.path.abspath(__file__))
         domsuf = os.getenv('DOMSUF', "localdomain")
         host = os.getenv('HOST', "localhost")
         env = {
             "NSS_TESTS": test,
             "NSS_CYCLES": cycles,
             "DOMSUF": domsuf,
@@ -121,16 +150,17 @@ class testAction(argparse.Action):
         subprocess.check_call(command, env=os_env)
 
     def __call__(self, parser, args, values, option_string=None):
         self.runTest(values)
 
 
 class commandsAction(argparse.Action):
     commands = []
+
     def __call__(self, parser, args, values, option_string=None):
         for c in commandsAction.commands:
             print(c)
 
 
 def parse_arguments():
     parser = argparse.ArgumentParser(
         description='NSS helper script. ' +
@@ -139,21 +169,30 @@ def parse_arguments():
 
     parser_build = subparsers.add_parser(
         'build', help='All arguments are passed to build.sh')
     parser_build.add_argument(
         'build_args', nargs='*', help="build arguments", action=buildAction)
 
     parser_cf = subparsers.add_parser(
         'clang-format',
-        help='Run clang-format on all folders or provide a folder to format.')
+        help="""
+        Run clang-format.
+
+        By default this runs against any files that you have modified.  If
+        there are no modified files, it checks everything.
+        """)
     parser_cf.add_argument(
-        'cf_args',
+        '--noroot',
+        help='On linux, suppress the use of \'sudo\' for running docker.',
+        action='store_true')
+    parser_cf.add_argument(
+        '<file/dir>',
         nargs='*',
-        help="clang-format folders and noroot if you don't want to use sudo",
+        help="Specify files or directories to run clang-format on",
         action=cfAction)
 
     parser_test = subparsers.add_parser(
         'tests', help='Run tests through tests/all.sh.')
     tests = [
         "cipher", "lowhash", "chains", "cert", "dbtests", "tools", "fips",
         "sdr", "crmf", "smime", "ssl", "ocsp", "merge", "pkits", "ec",
         "gtests", "ssl_gtests"