Bug 952379 - Add a clang-format-diff helper to mach; r=gps
authorAnthony Jones <ajones@mozilla.com>
Wed, 08 Jan 2014 14:51:44 +1300
changeset 168859 a171743fb50ee3bf6dd861b17a7c653b94451169
parent 168858 f97076de7eb0125c42cfbd2974b55ffd9c5d8b42
child 168860 ad626723a359e9b852f8295423f63a6802f02597
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-esr52@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs952379
milestone29.0a1
Bug 952379 - Add a clang-format-diff helper to mach; r=gps
.clang-format-ignore
tools/mach_commands.py
new file mode 100644
--- /dev/null
+++ b/.clang-format-ignore
@@ -0,0 +1,2 @@
+\mfbt/
+\js/
--- a/tools/mach_commands.py
+++ b/tools/mach_commands.py
@@ -1,15 +1,19 @@
 # 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/.
 
 from __future__ import unicode_literals
 
 import sys
+import os
+import stat
+import platform
+import urllib2
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
 from mozbuild.base import MachCommandBase
@@ -211,17 +215,16 @@ class PastebinProvider(object):
                      choices=['d', 'day', 'm', 'month', 'f', 'forever'],
                      help='Keep for specified duration (default: %(default)s)')
     @CommandArgument('file', nargs='?', default=None,
                      help='Specify the file to upload to pastebin.mozilla.org')
 
     def pastebin(self, language, poster, duration, file):
         import sys
         import urllib
-        import urllib2
 
         URL = 'http://pastebin.mozilla.org/'
 
         FILE_TYPES = [{'value': 'text', 'name': 'None', 'extension': 'txt'},
         {'value': 'bash', 'name': 'Bash', 'extension': 'sh'},
         {'value': 'c', 'name': 'C', 'extension': 'c'},
         {'value': 'cpp', 'name': 'C++', 'extension': 'cpp'},
         {'value': 'html4strict', 'name': 'HTML', 'extension': 'html'},
@@ -301,8 +304,74 @@ class ReviewboardToolsProvider(MachComma
         self.virtualenv_manager.install_pip_package('RBTools')
 
         from rbtools.commands.main import main
 
         # main() doesn't accept arguments and instead reads from sys.argv. So,
         # we fake it out.
         sys.argv = ['rbt'] + args
         return main()
+
+@CommandProvider
+class FormatProvider(MachCommandBase):
+    @Command('clang-format', category='devenv', allow_all_args=True,
+        description='Run clang-format on current changes')
+    @CommandArgument('args', nargs='...', help='Arguments to clang-format tool')
+    def clang_format(self, args):
+        if not args:
+            args = ['help']
+
+        fmt = "clang-format-3.5"
+        fmt_diff = "clang-format-diff-3.5"
+
+        # We are currently using a modified verion of clang-format hosted on people.mozilla.org.
+        # This is a temporary work around until we upstream the necessary changes and we can use
+        # a system version of clang-format. See bug 961541.
+        self.prompt = 1
+        plat = platform.system()
+        if plat == "Windows":
+            fmt += ".exe"
+        else:
+            arch = os.uname()[4]
+            if plat != "Linux" or arch != 'x86_64':
+                print("Unsupported platform " + plat + "/" + arch +
+                      ". Supported platforms are Windows/* and Linux/x86_64")
+                return 1
+
+        os.chdir(self.topsrcdir)
+
+        try:
+            if not self.locate_or_fetch(fmt):
+                return 1
+            clang_format_diff = self.locate_or_fetch(fmt_diff)
+            if not clang_format_diff:
+                return 1
+
+        except urllib2.HTTPError as e:
+            print("HTTP error {0}: {1}".format(e.code, e.reason))
+            return 1
+
+        from subprocess import Popen, PIPE
+        p1 = Popen(["hg", "diff", "-U0", "-r", "tip^", "--include", "glob:**.c", "--include", "glob:**.cpp",
+                   "--include", "glob:**.h", "--exclude", "listfile:.clang-format-ignore"], stdout=PIPE)
+        p2 = Popen([sys.executable, clang_format_diff, "-i", "-p1", "-style=Mozilla"], stdin=p1.stdout)
+        return p2.communicate()[0]
+
+    def locate_or_fetch(self, root):
+        target = os.path.join(self._mach_context.state_dir, root)
+        if not os.path.exists(target):
+            site = "http://people.mozilla.org/~ajones/clang-format/"
+            if self.prompt and raw_input("Download clang-format executables from {0} (yN)? ".format(site)).lower() != 'y':
+                print("Download aborted.")
+                return 1
+            self.prompt = 0
+
+            u = site + root
+            print("Downloading {0} to {1}".format(u, target))
+            data = urllib2.urlopen(url=u).read()
+            temp = target + ".tmp"
+            with open(temp, "wb") as fh:
+                fh.write(data)
+                fh.close()
+            os.chmod(temp, os.stat(temp).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+            os.rename(temp, target)
+        return target
+