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 164581 a171743fb50ee3bf6dd861b17a7c653b94451169
parent 164580 f97076de7eb0125c42cfbd2974b55ffd9c5d8b42
child 164582 ad626723a359e9b852f8295423f63a6802f02597
push id26052
push usercbook@mozilla.com
push dateWed, 22 Jan 2014 12:55:15 +0000
treeherdermozilla-central@e08be9425388 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs952379
milestone29.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 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
+